mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-03 14:41:32 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor
This commit is contained in:
commit
d73c1c8590
349 changed files with 12858 additions and 2571 deletions
|
@ -172,6 +172,20 @@ Clang_Format:
|
||||||
- CI/check_file_names.sh
|
- CI/check_file_names.sh
|
||||||
- CI/check_clang_format.sh
|
- CI/check_clang_format.sh
|
||||||
|
|
||||||
|
Lupdate:
|
||||||
|
extends: .Ubuntu_Image
|
||||||
|
stage: checks
|
||||||
|
cache:
|
||||||
|
key: Ubuntu_lupdate.ubuntu_22.04.v1
|
||||||
|
paths:
|
||||||
|
- apt-cache/
|
||||||
|
variables:
|
||||||
|
LUPDATE: lupdate
|
||||||
|
before_script:
|
||||||
|
- CI/install_debian_deps.sh openmw-qt-translations
|
||||||
|
script:
|
||||||
|
- CI/check_qt_translations.sh
|
||||||
|
|
||||||
Teal:
|
Teal:
|
||||||
stage: checks
|
stage: checks
|
||||||
extends: .Ubuntu_Image
|
extends: .Ubuntu_Image
|
||||||
|
@ -196,6 +210,7 @@ Ubuntu_GCC_Debug:
|
||||||
CCACHE_SIZE: 3G
|
CCACHE_SIZE: 3G
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_CXX_FLAGS_DEBUG: -O0
|
CMAKE_CXX_FLAGS_DEBUG: -O0
|
||||||
|
BUILD_SHARED_LIBS: 1
|
||||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||||
timeout: 2h
|
timeout: 2h
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ Programmers
|
||||||
Eduard Cot (trombonecot)
|
Eduard Cot (trombonecot)
|
||||||
Eli2
|
Eli2
|
||||||
Emanuel Guével (potatoesmaster)
|
Emanuel Guével (potatoesmaster)
|
||||||
|
Epoch
|
||||||
Eris Caffee (eris)
|
Eris Caffee (eris)
|
||||||
eroen
|
eroen
|
||||||
escondida
|
escondida
|
||||||
|
@ -187,6 +188,7 @@ Programmers
|
||||||
pkubik
|
pkubik
|
||||||
PLkolek
|
PLkolek
|
||||||
PlutonicOverkill
|
PlutonicOverkill
|
||||||
|
Qlonever
|
||||||
Radu-Marius Popovici (rpopovici)
|
Radu-Marius Popovici (rpopovici)
|
||||||
Rafael Moura (dhustkoder)
|
Rafael Moura (dhustkoder)
|
||||||
Randy Davin (Kindi)
|
Randy Davin (Kindi)
|
||||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -24,7 +24,9 @@
|
||||||
Bug #5129: Stuttering animation on Centurion Archer
|
Bug #5129: Stuttering animation on Centurion Archer
|
||||||
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
|
||||||
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
|
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
|
||||||
|
Bug #5413: Enemies do a battlecry everytime the player summons a creature
|
||||||
Bug #5714: Touch spells cast using ExplodeSpell don't always explode
|
Bug #5714: Touch spells cast using ExplodeSpell don't always explode
|
||||||
|
Bug #5755: Reset friendly hit counter
|
||||||
Bug #5849: Paralysis breaks landing
|
Bug #5849: Paralysis breaks landing
|
||||||
Bug #5870: Disposing of actors who were selected in the console doesn't deselect them like vanilla
|
Bug #5870: Disposing of actors who were selected in the console doesn't deselect them like vanilla
|
||||||
Bug #5883: Immobile creatures don't cause water ripples
|
Bug #5883: Immobile creatures don't cause water ripples
|
||||||
|
@ -37,6 +39,7 @@
|
||||||
Bug #6402: The sound of a thunderstorm does not stop playing after entering the premises
|
Bug #6402: The sound of a thunderstorm does not stop playing after entering the premises
|
||||||
Bug #6427: Enemy health bar disappears before damaging effect ends
|
Bug #6427: Enemy health bar disappears before damaging effect ends
|
||||||
Bug #6550: Cloned body parts don't inherit texture effects
|
Bug #6550: Cloned body parts don't inherit texture effects
|
||||||
|
Bug #6574: Crash at far away from world origin coordinates
|
||||||
Bug #6645: Enemy block sounds align with animation instead of blocked hits
|
Bug #6645: Enemy block sounds align with animation instead of blocked hits
|
||||||
Bug #6657: Distant terrain tiles become black when using FWIW mod
|
Bug #6657: Distant terrain tiles become black when using FWIW mod
|
||||||
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
|
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
|
||||||
|
@ -61,6 +64,7 @@
|
||||||
Bug #7034: Misc items defined in one content file are not treated as keys if another content file uses them as such
|
Bug #7034: Misc items defined in one content file are not treated as keys if another content file uses them as such
|
||||||
Bug #7042: Weapon follow animations that immediately follow the hit animations cause multiple hits
|
Bug #7042: Weapon follow animations that immediately follow the hit animations cause multiple hits
|
||||||
Bug #7044: Changing a class' services does not affect autocalculated NPCs
|
Bug #7044: Changing a class' services does not affect autocalculated NPCs
|
||||||
|
Bug #7053: Running into objects doesn't trigger GetCollidingPC
|
||||||
Bug #7054: Quests aren't sorted by name
|
Bug #7054: Quests aren't sorted by name
|
||||||
Bug #7064: NPCs don't report crime if the player is casting offensive spells on them while sneaking
|
Bug #7064: NPCs don't report crime if the player is casting offensive spells on them while sneaking
|
||||||
Bug #7077: OpenMW fails to load certain particle effects in .osgt format
|
Bug #7077: OpenMW fails to load certain particle effects in .osgt format
|
||||||
|
@ -107,6 +111,7 @@
|
||||||
Bug #7619: Long map notes may get cut off
|
Bug #7619: Long map notes may get cut off
|
||||||
Bug #7630: Charm can be cast on creatures
|
Bug #7630: Charm can be cast on creatures
|
||||||
Bug #7631: Cannot trade with/talk to Creeper or Mudcrab Merchant when they're fleeing
|
Bug #7631: Cannot trade with/talk to Creeper or Mudcrab Merchant when they're fleeing
|
||||||
|
Bug #7633: Groundcover should ignore non-geometry Drawables
|
||||||
Bug #7636: Animations bug out when switching between 1st and 3rd person, while playing a scripted animation
|
Bug #7636: Animations bug out when switching between 1st and 3rd person, while playing a scripted animation
|
||||||
Bug #7637: Actors can sometimes move while playing scripted animations
|
Bug #7637: Actors can sometimes move while playing scripted animations
|
||||||
Bug #7639: NPCs don't use hand-to-hand if their other melee skills were damaged during combat
|
Bug #7639: NPCs don't use hand-to-hand if their other melee skills were damaged during combat
|
||||||
|
@ -128,18 +133,28 @@
|
||||||
Bug #7724: Guards don't help vs werewolves
|
Bug #7724: Guards don't help vs werewolves
|
||||||
Bug #7733: Launcher shows incorrect data paths when there's two plugins with the same name
|
Bug #7733: Launcher shows incorrect data paths when there's two plugins with the same name
|
||||||
Bug #7742: Governing attribute training limit should use the modified attribute
|
Bug #7742: Governing attribute training limit should use the modified attribute
|
||||||
|
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 #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 #7761: Rain and ambient loop sounds are mutually exclusive
|
||||||
Bug #7765: OpenMW-CS: Touch Record option is broken
|
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
|
Bug #7770: Sword of the Perithia: Script execution failure
|
||||||
Bug #7780: Non-ASCII texture paths in NIF files don't work
|
Bug #7780: Non-ASCII texture paths in NIF files don't work
|
||||||
|
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 #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
|
||||||
Feature #2566: Handle NAM9 records for manual cell references
|
Feature #2566: Handle NAM9 records for manual cell references
|
||||||
Feature #3537: Shader-based water ripples
|
Feature #3537: Shader-based water ripples
|
||||||
Feature #5173: Support for NiFogProperty
|
Feature #5173: Support for NiFogProperty
|
||||||
Feature #5492: Let rain and snow collide with statics
|
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 #6149: Dehardcode Lua API_REVISION
|
||||||
Feature #6152: Playing music via lua scripts
|
Feature #6152: Playing music via lua scripts
|
||||||
Feature #6188: Specular lighting from point light sources
|
Feature #6188: Specular lighting from point light sources
|
||||||
|
Feature #6411: Support translations in openmw-launcher
|
||||||
Feature #6447: Add LOD support to Object Paging
|
Feature #6447: Add LOD support to Object Paging
|
||||||
Feature #6491: Add support for Qt6
|
Feature #6491: Add support for Qt6
|
||||||
Feature #6556: Lua API for sounds
|
Feature #6556: Lua API for sounds
|
||||||
|
@ -166,13 +181,18 @@
|
||||||
Feature #7546: Start the game on Fredas
|
Feature #7546: Start the game on Fredas
|
||||||
Feature #7554: Controller binding for tab for menu navigation
|
Feature #7554: Controller binding for tab for menu navigation
|
||||||
Feature #7568: Uninterruptable scripted music
|
Feature #7568: Uninterruptable scripted music
|
||||||
|
Feature #7606: Launcher: allow Shift-select in Archives tab
|
||||||
Feature #7608: Make the missing dependencies warning when loading a savegame more helpful
|
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
|
Feature #7618: Show the player character's health in the save details
|
||||||
Feature #7625: Add some missing console error outputs
|
Feature #7625: Add some missing console error outputs
|
||||||
Feature #7634: Support NiParticleBomb
|
Feature #7634: Support NiParticleBomb
|
||||||
|
Feature #7648: Lua Save game API
|
||||||
Feature #7652: Sort inactive post processing shaders list properly
|
Feature #7652: Sort inactive post processing shaders list properly
|
||||||
Feature #7698: Implement sAbsorb, sDamage, sDrain, sFortify and sRestore
|
Feature #7698: Implement sAbsorb, sDamage, sDrain, sFortify and sRestore
|
||||||
Feature #7709: Improve resolution selection in Launcher
|
Feature #7709: Improve resolution selection in Launcher
|
||||||
|
Feature #7792: Support Timescale Clouds
|
||||||
|
Feature #7795: Support MaxNumberRipples INI setting
|
||||||
|
Feature #7805: Lua Menu context
|
||||||
Task #5896: Do not use deprecated MyGUI properties
|
Task #5896: Do not use deprecated MyGUI properties
|
||||||
Task #6624: Drop support for saves made prior to 0.45
|
Task #6624: Drop support for saves made prior to 0.45
|
||||||
Task #7113: Move from std::atoi to std::from_char
|
Task #7113: Move from std::atoi to std::from_char
|
||||||
|
|
|
@ -22,7 +22,7 @@ declare -a CMAKE_CONF_OPTS=(
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||||
-DCMAKE_INSTALL_PREFIX=install
|
-DCMAKE_INSTALL_PREFIX=install
|
||||||
-DBUILD_SHARED_LIBS=OFF
|
-DBUILD_SHARED_LIBS="${BUILD_SHARED_LIBS:-OFF}"
|
||||||
-DUSE_SYSTEM_TINYXML=ON
|
-DUSE_SYSTEM_TINYXML=ON
|
||||||
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON
|
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON
|
||||||
-DOPENMW_CXX_FLAGS="-Werror -Werror=implicit-fallthrough" # flags specific to OpenMW project
|
-DOPENMW_CXX_FLAGS="-Werror -Werror=implicit-fallthrough" # flags specific to OpenMW project
|
||||||
|
|
11
CI/check_qt_translations.sh
Executable file
11
CI/check_qt_translations.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
! (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)
|
|
@ -33,9 +33,10 @@ declare -rA GROUPED_DEPS=(
|
||||||
libboost-system-dev libboost-iostreams-dev
|
libboost-system-dev libboost-iostreams-dev
|
||||||
|
|
||||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||||
libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev
|
libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev
|
||||||
libbullet-dev liblz4-dev libpng-dev libjpeg-dev libluajit-5.1-dev
|
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev
|
||||||
librecast-dev libsqlite3-dev ca-certificates libicu-dev libyaml-cpp-dev
|
libluajit-5.1-dev librecast-dev libsqlite3-dev ca-certificates libicu-dev
|
||||||
|
libyaml-cpp-dev
|
||||||
"
|
"
|
||||||
|
|
||||||
# These dependencies can alternatively be built and linked statically.
|
# These dependencies can alternatively be built and linked statically.
|
||||||
|
@ -99,6 +100,12 @@ declare -rA GROUPED_DEPS=(
|
||||||
clang-format-14
|
clang-format-14
|
||||||
git-core
|
git-core
|
||||||
"
|
"
|
||||||
|
|
||||||
|
[openmw-qt-translations]="
|
||||||
|
qttools5-dev
|
||||||
|
qttools5-dev-tools
|
||||||
|
git-core
|
||||||
|
"
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ $# -eq 0 ]]; then
|
if [[ $# -eq 0 ]]; then
|
||||||
|
|
|
@ -19,6 +19,14 @@ if(OPENMW_GL4ES_MANUAL_INIT)
|
||||||
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
|
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (APPLE OR WIN32)
|
||||||
|
set(DEPLOY_QT_TRANSLATIONS_DEFAULT ON)
|
||||||
|
else ()
|
||||||
|
set(DEPLOY_QT_TRANSLATIONS_DEFAULT OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
option(DEPLOY_QT_TRANSLATIONS "Deploy standard Qt translations to resources folder. Needed when OpenMW applications are deployed with Qt libraries" ${DEPLOY_QT_TRANSLATIONS_DEFAULT})
|
||||||
|
|
||||||
# Apps and tools
|
# Apps and tools
|
||||||
option(BUILD_OPENMW "Build OpenMW" ON)
|
option(BUILD_OPENMW "Build OpenMW" ON)
|
||||||
option(BUILD_LAUNCHER "Build Launcher" ON)
|
option(BUILD_LAUNCHER "Build Launcher" ON)
|
||||||
|
@ -72,7 +80,7 @@ message(STATUS "Configuring OpenMW...")
|
||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 49)
|
set(OPENMW_VERSION_MINOR 49)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
set(OPENMW_LUA_API_REVISION 51)
|
set(OPENMW_LUA_API_REVISION 54)
|
||||||
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
|
@ -224,9 +232,9 @@ find_package(LZ4 REQUIRED)
|
||||||
if (USE_QT)
|
if (USE_QT)
|
||||||
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
|
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
|
||||||
if (QT_VERSION_MAJOR VERSION_EQUAL 5)
|
if (QT_VERSION_MAJOR VERSION_EQUAL 5)
|
||||||
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL REQUIRED)
|
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL LinguistTools REQUIRED)
|
||||||
else()
|
else()
|
||||||
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets REQUIRED)
|
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Using Qt${QT_VERSION}")
|
message(STATUS "Using Qt${QT_VERSION}")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1074,3 +1082,78 @@ if (DOXYGEN_FOUND)
|
||||||
WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
|
WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
|
||||||
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
|
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
# Qt localization
|
||||||
|
if (USE_QT)
|
||||||
|
file(GLOB LAUNCHER_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/launcher_*.ts)
|
||||||
|
file(GLOB WIZARD_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/wizard_*.ts)
|
||||||
|
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}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/components
|
||||||
|
VERBATIM
|
||||||
|
COMMAND_EXPAND_LISTS
|
||||||
|
|
||||||
|
COMMAND ${QT_LUPDATE_EXECUTABLE} ${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}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/launcher
|
||||||
|
VERBATIM
|
||||||
|
COMMAND_EXPAND_LISTS)
|
||||||
|
|
||||||
|
if (BUILD_LAUNCHER OR BUILD_WIZARD)
|
||||||
|
if (APPLE)
|
||||||
|
set(QT_OPENMW_TRANSLATIONS_PATH "${APP_BUNDLE_DIR}/Contents/Resources/resources/translations")
|
||||||
|
else ()
|
||||||
|
get_generator_is_multi_config(multi_config)
|
||||||
|
if (multi_config)
|
||||||
|
set(QT_OPENMW_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/$<CONFIG>/resources/translations")
|
||||||
|
else ()
|
||||||
|
set(QT_OPENMW_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/resources/translations")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
file(GLOB TS_FILES ${COMPONENTS_TS_FILES})
|
||||||
|
|
||||||
|
if (BUILD_LAUNCHER)
|
||||||
|
set(TS_FILES ${TS_FILES} ${LAUNCHER_TS_FILES})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (BUILD_WIZARD)
|
||||||
|
set(TS_FILES ${TS_FILES} ${WIZARD_TS_FILES})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
qt_add_translation(QM_FILES ${TS_FILES} OPTIONS -silent)
|
||||||
|
|
||||||
|
if (DEPLOY_QT_TRANSLATIONS)
|
||||||
|
# Once we set a Qt 6.2.0 as a minimum required version, we may use "qtpaths --qt-query" instead.
|
||||||
|
get_target_property(QT_QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION)
|
||||||
|
execute_process(COMMAND "${QT_QMAKE_EXECUTABLE}" -query QT_INSTALL_TRANSLATIONS
|
||||||
|
OUTPUT_VARIABLE QT_TRANSLATIONS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
|
||||||
|
foreach(QM_FILE ${QM_FILES})
|
||||||
|
get_filename_component(QM_BASENAME ${QM_FILE} NAME)
|
||||||
|
string(REGEX REPLACE "[^_]+_(.*)\\.qm" "\\1" LANG_NAME ${QM_BASENAME})
|
||||||
|
if (EXISTS "${QT_TRANSLATIONS_DIR}/qtbase_${LANG_NAME}.qm")
|
||||||
|
set(QM_FILES ${QM_FILES} "${QT_TRANSLATIONS_DIR}/qtbase_${LANG_NAME}.qm")
|
||||||
|
elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qt_${LANG_NAME}.qm")
|
||||||
|
set(QM_FILES ${QM_FILES} "${QT_TRANSLATIONS_DIR}/qt_${LANG_NAME}.qm")
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR "Qt translations for '${LANG_NAME}' locale are not found in the '${QT_TRANSLATIONS_DIR}' folder.")
|
||||||
|
endif ()
|
||||||
|
endforeach(QM_FILE)
|
||||||
|
|
||||||
|
list(REMOVE_DUPLICATES QM_FILES)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_custom_target(qm-files
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_OPENMW_TRANSLATIONS_PATH}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QM_FILES} ${QT_OPENMW_TRANSLATIONS_PATH}
|
||||||
|
DEPENDS ${QM_FILES}
|
||||||
|
COMMENT "Copy *.qm files to resources folder")
|
||||||
|
endif ()
|
||||||
|
endif()
|
||||||
|
|
|
@ -76,10 +76,6 @@ namespace
|
||||||
bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")->multitoken()->composing(),
|
bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")->multitoken()->composing(),
|
||||||
"set fallback BSA archives (later archives have higher priority)");
|
"set fallback BSA archives (later archives have higher priority)");
|
||||||
|
|
||||||
addOption("resources",
|
|
||||||
bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
|
|
||||||
"set resources directory");
|
|
||||||
|
|
||||||
addOption("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
|
addOption("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
|
||||||
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");
|
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ Allowed options)");
|
||||||
addOption("name,n", bpo::value<std::string>(), "Show only the record with this name. Only affects dump mode.");
|
addOption("name,n", bpo::value<std::string>(), "Show only the record with this name. Only affects dump mode.");
|
||||||
addOption("plain,p",
|
addOption("plain,p",
|
||||||
"Print contents of dialogs, books and scripts. "
|
"Print contents of dialogs, books and scripts. "
|
||||||
"(skipped by default)"
|
"(skipped by default) "
|
||||||
"Only affects dump mode.");
|
"Only affects dump mode.");
|
||||||
addOption("quiet,q", "Suppress all record information. Useful for speed tests.");
|
addOption("quiet,q", "Suppress all record information. Useful for speed tests.");
|
||||||
addOption("loadcells,C", "Browse through contents of all cells.");
|
addOption("loadcells,C", "Browse through contents of all cells.");
|
||||||
|
@ -390,7 +390,7 @@ namespace
|
||||||
|
|
||||||
if (!quiet && interested)
|
if (!quiet && interested)
|
||||||
{
|
{
|
||||||
std::cout << "\nRecord: " << n.toStringView() << " '" << record->getId() << "'\n"
|
std::cout << "\nRecord: " << n.toStringView() << " " << record->getId() << "\n"
|
||||||
<< "Record flags: " << recordFlags(record->getFlags()) << '\n';
|
<< "Record flags: " << recordFlags(record->getFlags()) << '\n';
|
||||||
record->print();
|
record->print();
|
||||||
}
|
}
|
||||||
|
|
|
@ -464,7 +464,8 @@ namespace EsmTool
|
||||||
{
|
{
|
||||||
std::cout << " Name: " << mData.mName << std::endl;
|
std::cout << " Name: " << mData.mName << std::endl;
|
||||||
std::cout << " Model: " << mData.mModel << std::endl;
|
std::cout << " Model: " << mData.mModel << std::endl;
|
||||||
std::cout << " Script: " << mData.mScript << std::endl;
|
if (!mData.mScript.empty())
|
||||||
|
std::cout << " Script: " << mData.mScript << std::endl;
|
||||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,7 +517,8 @@ namespace EsmTool
|
||||||
std::cout << " Name: " << mData.mName << std::endl;
|
std::cout << " Name: " << mData.mName << std::endl;
|
||||||
std::cout << " Model: " << mData.mModel << std::endl;
|
std::cout << " Model: " << mData.mModel << std::endl;
|
||||||
std::cout << " Icon: " << mData.mIcon << std::endl;
|
std::cout << " Icon: " << mData.mIcon << std::endl;
|
||||||
std::cout << " Script: " << mData.mScript << std::endl;
|
if (!mData.mScript.empty())
|
||||||
|
std::cout << " Script: " << mData.mScript << std::endl;
|
||||||
std::cout << " Type: " << apparatusTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")"
|
std::cout << " Type: " << apparatusTypeLabel(mData.mData.mType) << " (" << mData.mData.mType << ")"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
||||||
|
@ -679,7 +681,8 @@ namespace EsmTool
|
||||||
{
|
{
|
||||||
std::cout << " Name: " << mData.mName << std::endl;
|
std::cout << " Name: " << mData.mName << std::endl;
|
||||||
std::cout << " Model: " << mData.mModel << std::endl;
|
std::cout << " Model: " << mData.mModel << std::endl;
|
||||||
std::cout << " Script: " << mData.mScript << std::endl;
|
if (!mData.mScript.empty())
|
||||||
|
std::cout << " Script: " << mData.mScript << std::endl;
|
||||||
std::cout << " Flags: " << creatureFlags((int)mData.mFlags) << std::endl;
|
std::cout << " Flags: " << creatureFlags((int)mData.mFlags) << std::endl;
|
||||||
std::cout << " Blood Type: " << mData.mBloodType + 1 << std::endl;
|
std::cout << " Blood Type: " << mData.mBloodType + 1 << std::endl;
|
||||||
std::cout << " Original: " << mData.mOriginal << std::endl;
|
std::cout << " Original: " << mData.mOriginal << std::endl;
|
||||||
|
@ -719,9 +722,6 @@ namespace EsmTool
|
||||||
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
|
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
|
||||||
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
|
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
|
||||||
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
|
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
|
||||||
std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl;
|
|
||||||
std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl;
|
|
||||||
std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl;
|
|
||||||
std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
|
std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
|
||||||
|
|
||||||
for (const ESM::AIPackage& package : mData.mAiPackage.mList)
|
for (const ESM::AIPackage& package : mData.mAiPackage.mList)
|
||||||
|
@ -747,7 +747,8 @@ namespace EsmTool
|
||||||
{
|
{
|
||||||
std::cout << " Name: " << mData.mName << std::endl;
|
std::cout << " Name: " << mData.mName << std::endl;
|
||||||
std::cout << " Model: " << mData.mModel << std::endl;
|
std::cout << " Model: " << mData.mModel << std::endl;
|
||||||
std::cout << " Script: " << mData.mScript << std::endl;
|
if (!mData.mScript.empty())
|
||||||
|
std::cout << " Script: " << mData.mScript << std::endl;
|
||||||
std::cout << " OpenSound: " << mData.mOpenSound << std::endl;
|
std::cout << " OpenSound: " << mData.mOpenSound << std::endl;
|
||||||
std::cout << " CloseSound: " << mData.mCloseSound << std::endl;
|
std::cout << " CloseSound: " << mData.mCloseSound << std::endl;
|
||||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
|
@ -1111,9 +1112,6 @@ namespace EsmTool
|
||||||
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
|
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << std::endl;
|
||||||
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
|
std::cout << " AI Flee:" << (int)mData.mAiData.mFlee << std::endl;
|
||||||
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
|
std::cout << " AI Alarm:" << (int)mData.mAiData.mAlarm << std::endl;
|
||||||
std::cout << " AI U1:" << (int)mData.mAiData.mU1 << std::endl;
|
|
||||||
std::cout << " AI U2:" << (int)mData.mAiData.mU2 << std::endl;
|
|
||||||
std::cout << " AI U3:" << (int)mData.mAiData.mU3 << std::endl;
|
|
||||||
std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
|
std::cout << " AI Services:" << Misc::StringUtils::format("0x%08X", mData.mAiData.mServices) << std::endl;
|
||||||
|
|
||||||
for (const ESM::AIPackage& package : mData.mAiPackage.mList)
|
for (const ESM::AIPackage& package : mData.mAiPackage.mList)
|
||||||
|
@ -1338,28 +1336,26 @@ namespace EsmTool
|
||||||
template <>
|
template <>
|
||||||
void Record<CellState>::print()
|
void Record<CellState>::print()
|
||||||
{
|
{
|
||||||
std::cout << " Id:" << std::endl;
|
std::cout << " Cell Id: \"" << mData.mCellState.mId.toString() << "\"" << std::endl;
|
||||||
std::cout << " CellId: " << mData.mCellState.mId << std::endl;
|
std::cout << " Water Level: " << mData.mCellState.mWaterLevel << std::endl;
|
||||||
std::cout << " Index:" << std::endl;
|
std::cout << " Has Fog Of War: " << mData.mCellState.mHasFogOfWar << std::endl;
|
||||||
std::cout << " WaterLevel: " << mData.mCellState.mWaterLevel << std::endl;
|
std::cout << " Last Respawn:" << std::endl;
|
||||||
std::cout << " HasFogOfWar: " << mData.mCellState.mHasFogOfWar << std::endl;
|
|
||||||
std::cout << " LastRespawn:" << std::endl;
|
|
||||||
std::cout << " Day:" << mData.mCellState.mLastRespawn.mDay << std::endl;
|
std::cout << " Day:" << mData.mCellState.mLastRespawn.mDay << std::endl;
|
||||||
std::cout << " Hour:" << mData.mCellState.mLastRespawn.mHour << std::endl;
|
std::cout << " Hour:" << mData.mCellState.mLastRespawn.mHour << std::endl;
|
||||||
if (mData.mCellState.mHasFogOfWar)
|
if (mData.mCellState.mHasFogOfWar)
|
||||||
{
|
{
|
||||||
std::cout << " NorthMarkerAngle: " << mData.mFogState.mNorthMarkerAngle << std::endl;
|
std::cout << " North Marker Angle: " << mData.mFogState.mNorthMarkerAngle << std::endl;
|
||||||
std::cout << " Bounds:" << std::endl;
|
std::cout << " Bounds:" << std::endl;
|
||||||
std::cout << " MinX: " << mData.mFogState.mBounds.mMinX << std::endl;
|
std::cout << " Min X: " << mData.mFogState.mBounds.mMinX << std::endl;
|
||||||
std::cout << " MinY: " << mData.mFogState.mBounds.mMinY << std::endl;
|
std::cout << " Min Y: " << mData.mFogState.mBounds.mMinY << std::endl;
|
||||||
std::cout << " MaxX: " << mData.mFogState.mBounds.mMaxX << std::endl;
|
std::cout << " Max X: " << mData.mFogState.mBounds.mMaxX << std::endl;
|
||||||
std::cout << " MaxY: " << mData.mFogState.mBounds.mMaxY << std::endl;
|
std::cout << " Max Y: " << mData.mFogState.mBounds.mMaxY << std::endl;
|
||||||
for (const ESM::FogTexture& fogTexture : mData.mFogState.mFogTextures)
|
for (const ESM::FogTexture& fogTexture : mData.mFogState.mFogTextures)
|
||||||
{
|
{
|
||||||
std::cout << " FogTexture:" << std::endl;
|
std::cout << " Fog Texture:" << std::endl;
|
||||||
std::cout << " X: " << fogTexture.mX << std::endl;
|
std::cout << " X: " << fogTexture.mX << std::endl;
|
||||||
std::cout << " Y: " << fogTexture.mY << std::endl;
|
std::cout << " Y: " << fogTexture.mY << std::endl;
|
||||||
std::cout << " ImageData: (" << fogTexture.mImageData.size() << ")" << std::endl;
|
std::cout << " Image Data: (" << fogTexture.mImageData.size() << ")" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1367,7 +1363,7 @@ namespace EsmTool
|
||||||
template <>
|
template <>
|
||||||
std::string Record<ESM::Cell>::getId() const
|
std::string Record<ESM::Cell>::getId() const
|
||||||
{
|
{
|
||||||
return mData.mName;
|
return std::string(); // No ID for Cell record
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -1397,9 +1393,7 @@ namespace EsmTool
|
||||||
template <>
|
template <>
|
||||||
std::string Record<CellState>::getId() const
|
std::string Record<CellState>::getId() const
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
return std::string(); // No ID for CellState record
|
||||||
stream << mData.mCellState.mId;
|
|
||||||
return stream.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
|
@ -71,6 +71,8 @@ openmw_add_executable(openmw-launcher
|
||||||
${UI_HDRS}
|
${UI_HDRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_dependencies(openmw-launcher qm-files)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".")
|
INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".")
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QList>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QPair>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -162,8 +164,8 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C
|
||||||
connect(ui.directoryUpButton, &QPushButton::released, this, [this]() { this->moveDirectory(-1); });
|
connect(ui.directoryUpButton, &QPushButton::released, this, [this]() { this->moveDirectory(-1); });
|
||||||
connect(ui.directoryDownButton, &QPushButton::released, this, [this]() { this->moveDirectory(1); });
|
connect(ui.directoryDownButton, &QPushButton::released, this, [this]() { this->moveDirectory(1); });
|
||||||
connect(ui.directoryRemoveButton, &QPushButton::released, this, [this]() { this->removeDirectory(); });
|
connect(ui.directoryRemoveButton, &QPushButton::released, this, [this]() { this->removeDirectory(); });
|
||||||
connect(ui.archiveUpButton, &QPushButton::released, this, [this]() { this->moveArchive(-1); });
|
connect(ui.archiveUpButton, &QPushButton::released, this, [this]() { this->moveArchives(-1); });
|
||||||
connect(ui.archiveDownButton, &QPushButton::released, this, [this]() { this->moveArchive(1); });
|
connect(ui.archiveDownButton, &QPushButton::released, this, [this]() { this->moveArchives(1); });
|
||||||
connect(
|
connect(
|
||||||
ui.directoryListWidget->model(), &QAbstractItemModel::rowsMoved, this, [this]() { this->sortDirectories(); });
|
ui.directoryListWidget->model(), &QAbstractItemModel::rowsMoved, this, [this]() { this->sortDirectories(); });
|
||||||
|
|
||||||
|
@ -218,6 +220,18 @@ void Launcher::DataFilesPage::buildView()
|
||||||
&DataFilesPage::readNavMeshToolStderr);
|
&DataFilesPage::readNavMeshToolStderr);
|
||||||
connect(mNavMeshToolInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
|
connect(mNavMeshToolInvoker->getProcess(), qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
|
||||||
&DataFilesPage::navMeshToolFinished);
|
&DataFilesPage::navMeshToolFinished);
|
||||||
|
|
||||||
|
buildArchiveContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::DataFilesPage::buildArchiveContextMenu()
|
||||||
|
{
|
||||||
|
connect(ui.archiveListWidget, &QListWidget::customContextMenuRequested, this,
|
||||||
|
&DataFilesPage::slotShowArchiveContextMenu);
|
||||||
|
|
||||||
|
mArchiveContextMenu = new QMenu(ui.archiveListWidget);
|
||||||
|
mArchiveContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems()));
|
||||||
|
mArchiveContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::DataFilesPage::loadSettings()
|
bool Launcher::DataFilesPage::loadSettings()
|
||||||
|
@ -294,7 +308,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
||||||
// Display new content with custom formatting
|
// Display new content with custom formatting
|
||||||
if (mNewDataDirs.contains(canonicalDirPath))
|
if (mNewDataDirs.contains(canonicalDirPath))
|
||||||
{
|
{
|
||||||
tooltip += "Will be added to the current profile\n";
|
tooltip += tr("Will be added to the current profile");
|
||||||
QFont font = item->font();
|
QFont font = item->font();
|
||||||
font.setBold(true);
|
font.setBold(true);
|
||||||
font.setItalic(true);
|
font.setItalic(true);
|
||||||
|
@ -312,7 +326,10 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
||||||
if (mSelector->containsDataFiles(currentDir))
|
if (mSelector->containsDataFiles(currentDir))
|
||||||
{
|
{
|
||||||
item->setIcon(QIcon(":/images/openmw-plugin.png"));
|
item->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||||
tooltip += "Contains content file(s)";
|
if (!tooltip.isEmpty())
|
||||||
|
tooltip += "\n";
|
||||||
|
|
||||||
|
tooltip += tr("Contains content file(s)");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -707,17 +724,71 @@ void Launcher::DataFilesPage::removeDirectory()
|
||||||
refreshDataFilesView();
|
refreshDataFilesView();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::DataFilesPage::moveArchive(int step)
|
void Launcher::DataFilesPage::slotShowArchiveContextMenu(const QPoint& pos)
|
||||||
{
|
{
|
||||||
int selectedRow = ui.archiveListWidget->currentRow();
|
QPoint globalPos = ui.archiveListWidget->viewport()->mapToGlobal(pos);
|
||||||
|
mArchiveContextMenu->exec(globalPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::DataFilesPage::setCheckStateForMultiSelectedItems(bool checked)
|
||||||
|
{
|
||||||
|
Qt::CheckState checkState = checked ? Qt::Checked : Qt::Unchecked;
|
||||||
|
|
||||||
|
for (QListWidgetItem* selectedItem : ui.archiveListWidget->selectedItems())
|
||||||
|
{
|
||||||
|
selectedItem->setCheckState(checkState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::DataFilesPage::slotUncheckMultiSelectedItems()
|
||||||
|
{
|
||||||
|
setCheckStateForMultiSelectedItems(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::DataFilesPage::slotCheckMultiSelectedItems()
|
||||||
|
{
|
||||||
|
setCheckStateForMultiSelectedItems(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Launcher::DataFilesPage::moveArchives(int step)
|
||||||
|
{
|
||||||
|
QList<QListWidgetItem*> selectedItems = ui.archiveListWidget->selectedItems();
|
||||||
|
QList<QPair<int, QListWidgetItem*>> sortedItems;
|
||||||
|
|
||||||
|
for (QListWidgetItem* selectedItem : selectedItems)
|
||||||
|
{
|
||||||
|
int selectedRow = ui.archiveListWidget->row(selectedItem);
|
||||||
|
sortedItems.append(qMakePair(selectedRow, selectedItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step > 0)
|
||||||
|
{
|
||||||
|
std::sort(sortedItems.begin(), sortedItems.end(), [](auto a, auto b) { return a.first > b.first; });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::sort(sortedItems.begin(), sortedItems.end(), [](auto a, auto b) { return a.first < b.first; });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i : sortedItems)
|
||||||
|
{
|
||||||
|
if (!moveArchive(i.second, step))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Launcher::DataFilesPage::moveArchive(QListWidgetItem* listItem, int step)
|
||||||
|
{
|
||||||
|
int selectedRow = ui.archiveListWidget->row(listItem);
|
||||||
int newRow = selectedRow + step;
|
int newRow = selectedRow + step;
|
||||||
if (selectedRow == -1 || newRow < 0 || newRow > ui.archiveListWidget->count() - 1)
|
if (selectedRow == -1 || newRow < 0 || newRow > ui.archiveListWidget->count() - 1)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
const auto* item = ui.archiveListWidget->takeItem(selectedRow);
|
const QListWidgetItem* item = ui.archiveListWidget->takeItem(selectedRow);
|
||||||
|
|
||||||
addArchive(item->text(), item->checkState(), newRow);
|
addArchive(item->text(), item->checkState(), newRow);
|
||||||
ui.archiveListWidget->setCurrentRow(newRow);
|
ui.archiveListWidget->setCurrentRow(newRow);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::DataFilesPage::addArchive(const QString& name, Qt::CheckState selected, int row)
|
void Launcher::DataFilesPage::addArchive(const QString& name, Qt::CheckState selected, int row)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <components/process/processinvoker.hpp>
|
#include <components/process/processinvoker.hpp>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QMenu>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ namespace Launcher
|
||||||
|
|
||||||
ContentSelectorView::ContentSelector* mSelector;
|
ContentSelectorView::ContentSelector* mSelector;
|
||||||
Ui::DataFilesPage ui;
|
Ui::DataFilesPage ui;
|
||||||
|
QMenu* mArchiveContextMenu;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
|
explicit DataFilesPage(const Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings,
|
||||||
|
@ -72,9 +74,13 @@ namespace Launcher
|
||||||
void addSubdirectories(bool append);
|
void addSubdirectories(bool append);
|
||||||
void sortDirectories();
|
void sortDirectories();
|
||||||
void removeDirectory();
|
void removeDirectory();
|
||||||
void moveArchive(int step);
|
void moveArchives(int step);
|
||||||
void moveDirectory(int step);
|
void moveDirectory(int step);
|
||||||
|
|
||||||
|
void slotShowArchiveContextMenu(const QPoint& pos);
|
||||||
|
void slotCheckMultiSelectedItems();
|
||||||
|
void slotUncheckMultiSelectedItems();
|
||||||
|
|
||||||
void on_newProfileAction_triggered();
|
void on_newProfileAction_triggered();
|
||||||
void on_cloneProfileAction_triggered();
|
void on_cloneProfileAction_triggered();
|
||||||
void on_deleteProfileAction_triggered();
|
void on_deleteProfileAction_triggered();
|
||||||
|
@ -120,7 +126,10 @@ namespace Launcher
|
||||||
|
|
||||||
void addArchive(const QString& name, Qt::CheckState selected, int row = -1);
|
void addArchive(const QString& name, Qt::CheckState selected, int row = -1);
|
||||||
void addArchivesFromDir(const QString& dir);
|
void addArchivesFromDir(const QString& dir);
|
||||||
|
bool moveArchive(QListWidgetItem* listItem, int step);
|
||||||
void buildView();
|
void buildView();
|
||||||
|
void buildArchiveContextMenu();
|
||||||
|
void setCheckStateForMultiSelectedItems(bool checked);
|
||||||
void setProfile(int index, bool savePrevious);
|
void setProfile(int index, bool savePrevious);
|
||||||
void setProfile(const QString& previous, const QString& current, bool savePrevious);
|
void setProfile(const QString& previous, const QString& current, bool savePrevious);
|
||||||
void removeProfile(const QString& profile);
|
void removeProfile(const QString& profile);
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QTranslator>
|
|
||||||
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
#include <boost/program_options/options_description.hpp>
|
||||||
#include <boost/program_options/variables_map.hpp>
|
#include <boost/program_options/variables_map.hpp>
|
||||||
|
|
||||||
#include <components/debug/debugging.hpp>
|
#include <components/debug/debugging.hpp>
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
#include <components/files/qtconversion.hpp>
|
||||||
|
#include <components/l10n/qttranslations.hpp>
|
||||||
#include <components/platform/platform.hpp>
|
#include <components/platform/platform.hpp>
|
||||||
|
|
||||||
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||||
|
@ -34,12 +35,13 @@ int runLauncher(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
// Internationalization
|
QString resourcesPath(".");
|
||||||
QString locale = QLocale::system().name().section('_', 0, 0);
|
if (!variables["resources"].empty())
|
||||||
|
{
|
||||||
|
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
|
||||||
|
}
|
||||||
|
|
||||||
QTranslator appTranslator;
|
l10n::installQtTranslations(app, "launcher", resourcesPath);
|
||||||
appTranslator.load(":/translations/" + locale + ".qm");
|
|
||||||
app.installTranslator(&appTranslator);
|
|
||||||
|
|
||||||
Launcher::MainDialog mainWin(configurationManager);
|
Launcher::MainDialog mainWin(configurationManager);
|
||||||
|
|
||||||
|
|
|
@ -294,6 +294,7 @@ bool Launcher::SettingsPage::loadSettings()
|
||||||
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
|
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface Changes
|
// Interface Changes
|
||||||
|
@ -490,6 +491,9 @@ void Launcher::SettingsPage::saveSettings()
|
||||||
Settings::sound().mHrtf.set(hrtfProfileSelectorComboBox->currentText().toStdString());
|
Settings::sound().mHrtf.set(hrtfProfileSelectorComboBox->currentText().toStdString());
|
||||||
else
|
else
|
||||||
Settings::sound().mHrtf.set({});
|
Settings::sound().mHrtf.set({});
|
||||||
|
|
||||||
|
const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked;
|
||||||
|
Settings::sound().mCameraListener.set(cCameraListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface Changes
|
// Interface Changes
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>573</width>
|
<width>573</width>
|
||||||
<height>384</height>
|
<height>557</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
|
@ -29,6 +29,12 @@
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="dataNoteLabel">
|
<widget class="QLabel" name="dataNoteLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>note: content files that are not part of current Content List are <span style=" font-style:italic;font-weight: bold">highlighted</span></p></body></html></string>
|
<string><html><head/><body><p>note: content files that are not part of current Content List are <span style=" font-style:italic;font-weight: bold">highlighted</span></p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -41,14 +47,111 @@
|
||||||
<string>Data Directories</string>
|
<string>Data Directories</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="dirTabLayout">
|
<layout class="QGridLayout" name="dirTabLayout">
|
||||||
<item row="0" column="0" rowspan="26">
|
<item row="0" column="0">
|
||||||
<widget class="QListWidget" name="directoryListWidget">
|
<widget class="QListWidget" name="directoryListWidget">
|
||||||
<property name="dragDropMode">
|
<property name="dragDropMode">
|
||||||
<enum>QAbstractItemView::InternalMove</enum>
|
<enum>QAbstractItemView::InternalMove</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="32" column="0" colspan="2">
|
<item row="0" column="1">
|
||||||
|
<layout class="QVBoxLayout" name="directoryButtons">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="directoryAddSubdirsButton">
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>33</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Scan directories for likely data directories and append them at the end of the list.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Append</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="directoryInsertButton">
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>33</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Scan directories for likely data directories and insert them above the selected position</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Insert Above</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="directoryUpButton">
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>33</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Move selected directory one position up</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Move Up</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="directoryDownButton">
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>33</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Move selected directory one position down</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Move Down</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="directoryRemoveButton">
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>33</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Remove selected directory</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="directoryButtonsSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="directoryNoteLabel">
|
<widget class="QLabel" name="directoryNoteLabel">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||||
|
@ -61,116 +164,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QPushButton" name="directoryAddSubdirsButton">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="baseSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Scan directories for likely data directories and append them at the end of the list.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Append</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QPushButton" name="directoryInsertButton">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="baseSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Scan directories for likely data directories and insert them above the selected position</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Insert Above</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="1">
|
|
||||||
<widget class="QPushButton" name="directoryUpButton">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="baseSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Move selected directory one position up</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Move Up</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="12" column="1">
|
|
||||||
<widget class="QPushButton" name="directoryDownButton">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="baseSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Move selected directory one position down</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Move Down</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="16" column="1">
|
|
||||||
<widget class="QPushButton" name="directoryRemoveButton">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="baseSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Remove selected directory</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="archiveTab">
|
<widget class="QWidget" name="archiveTab">
|
||||||
|
@ -178,64 +171,90 @@
|
||||||
<string>Archive Files</string>
|
<string>Archive Files</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="archiveTabLayout">
|
<layout class="QGridLayout" name="archiveTabLayout">
|
||||||
<item row="0" column="0" rowspan="26">
|
<item row="0" column="0">
|
||||||
<widget class="QListWidget" name="archiveListWidget">
|
<widget class="QListWidget" name="archiveListWidget">
|
||||||
|
<property name="contextMenuPolicy">
|
||||||
|
<enum>Qt::CustomContextMenu</enum>
|
||||||
|
</property>
|
||||||
<property name="dragDropMode">
|
<property name="dragDropMode">
|
||||||
<enum>QAbstractItemView::InternalMove</enum>
|
<enum>QAbstractItemView::InternalMove</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="defaultDropAction">
|
||||||
|
<enum>Qt::CopyAction</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QPushButton" name="archiveUpButton">
|
<layout class="QVBoxLayout" name="archiveButtons">
|
||||||
<property name="sizePolicy">
|
<item>
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
<widget class="QPushButton" name="archiveUpButton">
|
||||||
<horstretch>0</horstretch>
|
<property name="sizePolicy">
|
||||||
<verstretch>33</verstretch>
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
</sizepolicy>
|
<horstretch>0</horstretch>
|
||||||
</property>
|
<verstretch>33</verstretch>
|
||||||
<property name="baseSize">
|
</sizepolicy>
|
||||||
<size>
|
</property>
|
||||||
<width>0</width>
|
<property name="baseSize">
|
||||||
<height>33</height>
|
<size>
|
||||||
</size>
|
<width>0</width>
|
||||||
</property>
|
<height>33</height>
|
||||||
<property name="toolTip">
|
</size>
|
||||||
<string>Move selected archive one position up</string>
|
</property>
|
||||||
</property>
|
<property name="toolTip">
|
||||||
<property name="text">
|
<string>Move selected archive one position up</string>
|
||||||
<string>Move Up</string>
|
</property>
|
||||||
</property>
|
<property name="text">
|
||||||
</widget>
|
<string>Move Up</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="archiveDownButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>33</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>33</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Move selected archive one position down</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Move Down</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="archiveButtonsSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="27" column="0" colspan="2">
|
<item row="1" column="0" colspan="2">
|
||||||
<widget class="QLabel" name="archiveNoteLabel">
|
<widget class="QLabel" name="archiveNoteLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>note: archives that are not part of current Content List are <span style=" font-style:italic;font-weight: bold">highlighted</span></p></body></html></string>
|
<string><html><head/><body><p>note: archives that are not part of current Content List are <span style=" font-style:italic;font-weight: bold">highlighted</span></p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QPushButton" name="archiveDownButton">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>33</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="baseSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>33</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Move selected archive one position down</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Move Down</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="navigationMeshCacheTab">
|
<widget class="QWidget" name="navigationMeshCacheTab">
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>720</width>
|
<width>750</width>
|
||||||
<height>635</height>
|
<height>635</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>720</width>
|
<width>750</width>
|
||||||
<height>635</height>
|
<height>635</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
@ -148,7 +148,7 @@ QToolButton {
|
||||||
<string>Display</string>
|
<string>Display</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Allows to change graphics settings</string>
|
<string>Allows to change display settings</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="settingsAction">
|
<action name="settingsAction">
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<item row="6" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QCheckBox" name="allowNPCToFollowOverWaterSurfaceCheckBox">
|
<widget class="QCheckBox" name="allowNPCToFollowOverWaterSurfaceCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Give actors an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.</string>
|
<string><html><head/><body><p>Give actors an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Always allow actors to follow over water</string>
|
<string>Always allow actors to follow over water</string>
|
||||||
|
@ -206,7 +206,7 @@
|
||||||
<item row="5" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox">
|
<widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html></string>
|
<string><html><head/><body><p>Effects of reflected Absorb spells are not mirrored - like in Morrowind.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Classic reflected Absorb spells behavior</string>
|
<string>Classic reflected Absorb spells behavior</string>
|
||||||
|
@ -524,7 +524,7 @@
|
||||||
<string><html><head/><body><p>Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.</p></body></html></string>
|
<string><html><head/><body><p>Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use anti-alias alpha testing</string>
|
<string>Use anti-aliased alpha testing</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -663,6 +663,9 @@
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QDoubleSpinBox" name="viewingDistanceComboBox">
|
<widget class="QDoubleSpinBox" name="viewingDistanceComboBox">
|
||||||
|
<property name="decimals">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> cells</string>
|
<string> cells</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -670,7 +673,7 @@
|
||||||
<double>0.000000000000000</double>
|
<double>0.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="singleStep">
|
<property name="singleStep">
|
||||||
<double>0.500000000000000</double>
|
<double>0.125000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -1320,6 +1323,16 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<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>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Use the camera as the sound listener</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer>
|
<spacer>
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
|
@ -584,7 +584,7 @@ void MwIniImporter::importGameFiles(
|
||||||
reader.close();
|
reader.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sortedFiles = dependencySort(unsortedFiles);
|
auto sortedFiles = dependencySort(std::move(unsortedFiles));
|
||||||
|
|
||||||
// hard-coded dependency Morrowind - Tribunal - Bloodmoon
|
// hard-coded dependency Morrowind - Tribunal - Bloodmoon
|
||||||
if (findString(sortedFiles, "Morrowind.esm") != sortedFiles.end())
|
if (findString(sortedFiles, "Morrowind.esm") != sortedFiles.end())
|
||||||
|
|
|
@ -88,10 +88,6 @@ namespace NavMeshTool
|
||||||
->composing(),
|
->composing(),
|
||||||
"set fallback BSA archives (later archives have higher priority)");
|
"set fallback BSA archives (later archives have higher priority)");
|
||||||
|
|
||||||
addOption("resources",
|
|
||||||
bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
|
|
||||||
"set resources directory");
|
|
||||||
|
|
||||||
addOption("content",
|
addOption("content",
|
||||||
bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
|
bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
|
||||||
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");
|
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");
|
||||||
|
|
|
@ -116,9 +116,9 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
||||||
|
|
||||||
for (const auto& name : vfs.getRecursiveDirectoryIterator(""))
|
for (const auto& name : vfs.getRecursiveDirectoryIterator(""))
|
||||||
{
|
{
|
||||||
if (isNIF(name))
|
if (isNIF(name.value()))
|
||||||
{
|
{
|
||||||
readNIF(archivePath, name, &vfs, quiet);
|
readNIF(archivePath, name.value(), &vfs, quiet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ else()
|
||||||
set (OPENCS_OPENMW_CFG "")
|
set (OPENCS_OPENMW_CFG "")
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
add_library(openmw-cs-lib
|
add_library(openmw-cs-lib STATIC
|
||||||
${OPENCS_SRC}
|
${OPENCS_SRC}
|
||||||
${OPENCS_UI_HDR}
|
${OPENCS_UI_HDR}
|
||||||
${OPENCS_MOC_SRC}
|
${OPENCS_MOC_SRC}
|
||||||
|
|
|
@ -119,8 +119,6 @@ boost::program_options::variables_map CS::Editor::readConfiguration()
|
||||||
boost::program_options::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(
|
boost::program_options::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(
|
||||||
Files::MaybeQuotedPathContainer::value_type(), ""));
|
Files::MaybeQuotedPathContainer::value_type(), ""));
|
||||||
addOption("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
addOption("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
||||||
addOption("resources",
|
|
||||||
boost::program_options::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"));
|
|
||||||
addOption("fallback-archive",
|
addOption("fallback-archive",
|
||||||
boost::program_options::value<std::vector<std::string>>()
|
boost::program_options::value<std::vector<std::string>>()
|
||||||
->default_value(std::vector<std::string>(), "fallback-archive")
|
->default_value(std::vector<std::string>(), "fallback-archive")
|
||||||
|
|
|
@ -691,15 +691,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (npc.mNpdt.mHealth != 0)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < npc.mNpdt.mAttributes.size(); ++i)
|
|
||||||
{
|
|
||||||
if (npc.mNpdt.mAttributes[i] == 0)
|
|
||||||
messages.add(id, ESM::Attribute::indexToRefId(i).getRefIdString() + " is equal to zero", {},
|
|
||||||
CSMDoc::Message::Severity_Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level <= 0)
|
if (level <= 0)
|
||||||
messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
|
messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning);
|
||||||
|
|
|
@ -18,16 +18,18 @@
|
||||||
#include <apps/opencs/model/world/universalid.hpp>
|
#include <apps/opencs/model/world/universalid.hpp>
|
||||||
|
|
||||||
#include <components/esm3/cellref.hpp>
|
#include <components/esm3/cellref.hpp>
|
||||||
|
#include <components/esm3/loadbody.hpp>
|
||||||
#include <components/esm3/loadfact.hpp>
|
#include <components/esm3/loadfact.hpp>
|
||||||
|
|
||||||
CSMTools::ReferenceCheckStage::ReferenceCheckStage(const CSMWorld::RefCollection& references,
|
CSMTools::ReferenceCheckStage::ReferenceCheckStage(const CSMWorld::RefCollection& references,
|
||||||
const CSMWorld::RefIdCollection& referencables, const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
const CSMWorld::RefIdCollection& referencables, const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||||
const CSMWorld::IdCollection<ESM::Faction>& factions)
|
const CSMWorld::IdCollection<ESM::Faction>& factions, const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts)
|
||||||
: mReferences(references)
|
: mReferences(references)
|
||||||
, mObjects(referencables)
|
, mObjects(referencables)
|
||||||
, mDataSet(referencables.getDataSet())
|
, mDataSet(referencables.getDataSet())
|
||||||
, mCells(cells)
|
, mCells(cells)
|
||||||
, mFactions(factions)
|
, mFactions(factions)
|
||||||
|
, mBodyParts(bodyparts)
|
||||||
{
|
{
|
||||||
mIgnoreBaseRecords = false;
|
mIgnoreBaseRecords = false;
|
||||||
}
|
}
|
||||||
|
@ -49,9 +51,11 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages& message
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check for non existing referenced object
|
// Check for non existing referenced object
|
||||||
if (mObjects.searchId(cellRef.mRefID) == -1)
|
if (mObjects.searchId(cellRef.mRefID) == -1 && mBodyParts.searchId(cellRef.mRefID) == -1)
|
||||||
|
{
|
||||||
messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID.getRefIdString() + "'", "",
|
messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID.getRefIdString() + "'", "",
|
||||||
CSMDoc::Message::Severity_Error);
|
CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check if reference charge is valid for it's proper referenced type
|
// Check if reference charge is valid for it's proper referenced type
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
struct BodyPart;
|
||||||
struct Faction;
|
struct Faction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +30,8 @@ namespace CSMTools
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ReferenceCheckStage(const CSMWorld::RefCollection& references, const CSMWorld::RefIdCollection& referencables,
|
ReferenceCheckStage(const CSMWorld::RefCollection& references, const CSMWorld::RefIdCollection& referencables,
|
||||||
const CSMWorld::IdCollection<CSMWorld::Cell>& cells, const CSMWorld::IdCollection<ESM::Faction>& factions);
|
const CSMWorld::IdCollection<CSMWorld::Cell>& cells, const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||||
|
const CSMWorld::IdCollection<ESM::BodyPart>& bodyparts);
|
||||||
|
|
||||||
void perform(int stage, CSMDoc::Messages& messages) override;
|
void perform(int stage, CSMDoc::Messages& messages) override;
|
||||||
int setup() override;
|
int setup() override;
|
||||||
|
@ -40,6 +42,7 @@ namespace CSMTools
|
||||||
const CSMWorld::RefIdData& mDataSet;
|
const CSMWorld::RefIdData& mDataSet;
|
||||||
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
|
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
|
||||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||||
|
const CSMWorld::IdCollection<ESM::BodyPart>& mBodyParts;
|
||||||
bool mIgnoreBaseRecords;
|
bool mIgnoreBaseRecords;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,8 +105,8 @@ CSMDoc::OperationHolder* CSMTools::Tools::getVerifier()
|
||||||
mData.getFactions(), mData.getScripts(), mData.getResources(CSMWorld::UniversalId::Type_Meshes),
|
mData.getFactions(), mData.getScripts(), mData.getResources(CSMWorld::UniversalId::Type_Meshes),
|
||||||
mData.getResources(CSMWorld::UniversalId::Type_Icons), mData.getBodyParts()));
|
mData.getResources(CSMWorld::UniversalId::Type_Icons), mData.getBodyParts()));
|
||||||
|
|
||||||
mVerifierOperation->appendStage(new ReferenceCheckStage(
|
mVerifierOperation->appendStage(new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(),
|
||||||
mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
|
mData.getCells(), mData.getFactions(), mData.getBodyParts()));
|
||||||
|
|
||||||
mVerifierOperation->appendStage(new ScriptCheckStage(mDocument));
|
mVerifierOperation->appendStage(new ScriptCheckStage(mDocument));
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,11 @@ namespace CSMWorld
|
||||||
return mMaleParts[ESM::getMeshPart(index)];
|
return mMaleParts[ESM::getMeshPart(index)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const osg::Vec2f& ActorAdapter::RaceData::getGenderWeightHeight(bool isFemale)
|
||||||
|
{
|
||||||
|
return isFemale ? mWeightsHeights.mFemaleWeightHeight : mWeightsHeights.mMaleWeightHeight;
|
||||||
|
}
|
||||||
|
|
||||||
bool ActorAdapter::RaceData::hasDependency(const ESM::RefId& id) const
|
bool ActorAdapter::RaceData::hasDependency(const ESM::RefId& id) const
|
||||||
{
|
{
|
||||||
return mDependencies.find(id) != mDependencies.end();
|
return mDependencies.find(id) != mDependencies.end();
|
||||||
|
@ -90,10 +95,11 @@ namespace CSMWorld
|
||||||
mDependencies.emplace(id);
|
mDependencies.emplace(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActorAdapter::RaceData::reset_data(const ESM::RefId& id, bool isBeast)
|
void ActorAdapter::RaceData::reset_data(const ESM::RefId& id, const WeightsHeights& raceStats, bool isBeast)
|
||||||
{
|
{
|
||||||
mId = id;
|
mId = id;
|
||||||
mIsBeast = isBeast;
|
mIsBeast = isBeast;
|
||||||
|
mWeightsHeights = raceStats;
|
||||||
for (auto& str : mFemaleParts)
|
for (auto& str : mFemaleParts)
|
||||||
str = ESM::RefId();
|
str = ESM::RefId();
|
||||||
for (auto& str : mMaleParts)
|
for (auto& str : mMaleParts)
|
||||||
|
@ -163,6 +169,11 @@ namespace CSMWorld
|
||||||
return it->second.first;
|
return it->second.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const osg::Vec2f& ActorAdapter::ActorData::getRaceWeightHeight() const
|
||||||
|
{
|
||||||
|
return mRaceData->getGenderWeightHeight(isFemale());
|
||||||
|
}
|
||||||
|
|
||||||
bool ActorAdapter::ActorData::hasDependency(const ESM::RefId& id) const
|
bool ActorAdapter::ActorData::hasDependency(const ESM::RefId& id) const
|
||||||
{
|
{
|
||||||
return mDependencies.find(id) != mDependencies.end();
|
return mDependencies.find(id) != mDependencies.end();
|
||||||
|
@ -504,7 +515,11 @@ namespace CSMWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& race = raceRecord.get();
|
auto& race = raceRecord.get();
|
||||||
data->reset_data(id, race.mData.mFlags & ESM::Race::Beast);
|
|
||||||
|
WeightsHeights scaleStats = { osg::Vec2f(race.mData.mMaleWeight, race.mData.mMaleHeight),
|
||||||
|
osg::Vec2f(race.mData.mFemaleWeight, race.mData.mFemaleHeight) };
|
||||||
|
|
||||||
|
data->reset_data(id, scaleStats, race.mData.mFlags & ESM::Race::Beast);
|
||||||
|
|
||||||
// Setup body parts
|
// Setup body parts
|
||||||
for (int i = 0; i < mBodyParts.getSize(); ++i)
|
for (int i = 0; i < mBodyParts.getSize(); ++i)
|
||||||
|
|
|
@ -41,6 +41,12 @@ namespace CSMWorld
|
||||||
/// Tracks unique strings
|
/// Tracks unique strings
|
||||||
using RefIdSet = std::unordered_set<ESM::RefId>;
|
using RefIdSet = std::unordered_set<ESM::RefId>;
|
||||||
|
|
||||||
|
struct WeightsHeights
|
||||||
|
{
|
||||||
|
osg::Vec2f mMaleWeightHeight;
|
||||||
|
osg::Vec2f mFemaleWeightHeight;
|
||||||
|
};
|
||||||
|
|
||||||
/// Contains base race data shared between actors
|
/// Contains base race data shared between actors
|
||||||
class RaceData
|
class RaceData
|
||||||
{
|
{
|
||||||
|
@ -57,6 +63,8 @@ namespace CSMWorld
|
||||||
const ESM::RefId& getFemalePart(ESM::PartReferenceType index) const;
|
const ESM::RefId& getFemalePart(ESM::PartReferenceType index) const;
|
||||||
/// Retrieves the associated body part
|
/// Retrieves the associated body part
|
||||||
const ESM::RefId& getMalePart(ESM::PartReferenceType index) const;
|
const ESM::RefId& getMalePart(ESM::PartReferenceType index) const;
|
||||||
|
|
||||||
|
const osg::Vec2f& getGenderWeightHeight(bool isFemale);
|
||||||
/// Checks if the race has a data dependency
|
/// Checks if the race has a data dependency
|
||||||
bool hasDependency(const ESM::RefId& id) const;
|
bool hasDependency(const ESM::RefId& id) const;
|
||||||
|
|
||||||
|
@ -67,7 +75,8 @@ namespace CSMWorld
|
||||||
/// Marks an additional dependency
|
/// Marks an additional dependency
|
||||||
void addOtherDependency(const ESM::RefId& id);
|
void addOtherDependency(const ESM::RefId& id);
|
||||||
/// Clears parts and dependencies
|
/// Clears parts and dependencies
|
||||||
void reset_data(const ESM::RefId& raceId, bool isBeast = false);
|
void reset_data(const ESM::RefId& raceId,
|
||||||
|
const WeightsHeights& raceStats = { osg::Vec2f(1.f, 1.f), osg::Vec2f(1.f, 1.f) }, bool isBeast = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handles(ESM::PartReferenceType type) const;
|
bool handles(ESM::PartReferenceType type) const;
|
||||||
|
@ -75,6 +84,7 @@ namespace CSMWorld
|
||||||
bool mIsBeast;
|
bool mIsBeast;
|
||||||
RacePartList mFemaleParts;
|
RacePartList mFemaleParts;
|
||||||
RacePartList mMaleParts;
|
RacePartList mMaleParts;
|
||||||
|
WeightsHeights mWeightsHeights;
|
||||||
RefIdSet mDependencies;
|
RefIdSet mDependencies;
|
||||||
};
|
};
|
||||||
using RaceDataPtr = std::shared_ptr<RaceData>;
|
using RaceDataPtr = std::shared_ptr<RaceData>;
|
||||||
|
@ -96,6 +106,8 @@ namespace CSMWorld
|
||||||
std::string getSkeleton() const;
|
std::string getSkeleton() const;
|
||||||
/// Retrieves the associated actor part
|
/// Retrieves the associated actor part
|
||||||
ESM::RefId getPart(ESM::PartReferenceType index) const;
|
ESM::RefId getPart(ESM::PartReferenceType index) const;
|
||||||
|
|
||||||
|
const osg::Vec2f& getRaceWeightHeight() const;
|
||||||
/// Checks if the actor has a data dependency
|
/// Checks if the actor has a data dependency
|
||||||
bool hasDependency(const ESM::RefId& id) const;
|
bool hasDependency(const ESM::RefId& id) const;
|
||||||
|
|
||||||
|
|
|
@ -585,19 +585,22 @@ namespace CSMWorld
|
||||||
void set(Record<ESXRecordT>& record, const QVariant& data) override
|
void set(Record<ESXRecordT>& record, const QVariant& data) override
|
||||||
{
|
{
|
||||||
ESXRecordT record2 = record.get();
|
ESXRecordT record2 = record.get();
|
||||||
|
|
||||||
|
float bodyAttr = std::clamp(data.toFloat(), 0.5f, 2.0f);
|
||||||
|
|
||||||
if (mWeight)
|
if (mWeight)
|
||||||
{
|
{
|
||||||
if (mMale)
|
if (mMale)
|
||||||
record2.mData.mMaleWeight = data.toFloat();
|
record2.mData.mMaleWeight = bodyAttr;
|
||||||
else
|
else
|
||||||
record2.mData.mFemaleWeight = data.toFloat();
|
record2.mData.mFemaleWeight = bodyAttr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mMale)
|
if (mMale)
|
||||||
record2.mData.mMaleHeight = data.toFloat();
|
record2.mData.mMaleHeight = bodyAttr;
|
||||||
else
|
else
|
||||||
record2.mData.mFemaleHeight = data.toFloat();
|
record2.mData.mFemaleHeight = bodyAttr;
|
||||||
}
|
}
|
||||||
record.setModified(record2);
|
record.setModified(record2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,6 +385,26 @@ namespace CSMWorld
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
effect.mEffectID = static_cast<short>(value.toInt());
|
effect.mEffectID = static_cast<short>(value.toInt());
|
||||||
|
switch (effect.mEffectID)
|
||||||
|
{
|
||||||
|
case ESM::MagicEffect::DrainSkill:
|
||||||
|
case ESM::MagicEffect::DamageSkill:
|
||||||
|
case ESM::MagicEffect::RestoreSkill:
|
||||||
|
case ESM::MagicEffect::FortifySkill:
|
||||||
|
case ESM::MagicEffect::AbsorbSkill:
|
||||||
|
effect.mAttribute = -1;
|
||||||
|
break;
|
||||||
|
case ESM::MagicEffect::DrainAttribute:
|
||||||
|
case ESM::MagicEffect::DamageAttribute:
|
||||||
|
case ESM::MagicEffect::RestoreAttribute:
|
||||||
|
case ESM::MagicEffect::FortifyAttribute:
|
||||||
|
case ESM::MagicEffect::AbsorbAttribute:
|
||||||
|
effect.mSkill = -1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
effect.mSkill = -1;
|
||||||
|
effect.mAttribute = -1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
@ -30,18 +30,18 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char* const* e
|
||||||
|
|
||||||
for (const auto& filepath : vfs->getRecursiveDirectoryIterator(""))
|
for (const auto& filepath : vfs->getRecursiveDirectoryIterator(""))
|
||||||
{
|
{
|
||||||
if (filepath.size() < baseSize + 1 || filepath.substr(0, baseSize) != mBaseDirectory
|
const std::string_view view = filepath.view();
|
||||||
|| (filepath[baseSize] != '/' && filepath[baseSize] != '\\'))
|
if (view.size() < baseSize + 1 || !view.starts_with(mBaseDirectory) || view[baseSize] != '/')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (extensions)
|
if (extensions)
|
||||||
{
|
{
|
||||||
std::string::size_type extensionIndex = filepath.find_last_of('.');
|
const auto extensionIndex = view.find_last_of('.');
|
||||||
|
|
||||||
if (extensionIndex == std::string::npos)
|
if (extensionIndex == std::string_view::npos)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string extension = filepath.substr(extensionIndex + 1);
|
std::string_view extension = view.substr(extensionIndex + 1);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
@ -53,10 +53,9 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char* const* e
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string file = filepath.substr(baseSize + 1);
|
std::string file(view.substr(baseSize + 1));
|
||||||
mFiles.push_back(file);
|
mFiles.push_back(file);
|
||||||
std::replace(file.begin(), file.end(), '\\', '/');
|
mIndex.emplace(std::move(file), static_cast<int>(mFiles.size()) - 1);
|
||||||
mIndex.insert(std::make_pair(Misc::StringUtils::lowerCase(file), static_cast<int>(mFiles.size()) - 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
|
#include <osg/Vec3d>
|
||||||
|
|
||||||
#include <apps/opencs/model/world/actoradapter.hpp>
|
#include <apps/opencs/model/world/actoradapter.hpp>
|
||||||
#include <apps/opencs/model/world/idcollection.hpp>
|
#include <apps/opencs/model/world/idcollection.hpp>
|
||||||
|
@ -29,7 +30,7 @@ namespace CSVRender
|
||||||
Actor::Actor(const ESM::RefId& id, CSMWorld::Data& data)
|
Actor::Actor(const ESM::RefId& id, CSMWorld::Data& data)
|
||||||
: mId(id)
|
: mId(id)
|
||||||
, mData(data)
|
, mData(data)
|
||||||
, mBaseNode(new osg::Group())
|
, mBaseNode(new osg::PositionAttitudeTransform())
|
||||||
, mSkeleton(nullptr)
|
, mSkeleton(nullptr)
|
||||||
{
|
{
|
||||||
mActorData = mData.getActorAdapter()->getActorData(mId);
|
mActorData = mData.getActorAdapter()->getActorData(mId);
|
||||||
|
@ -60,6 +61,10 @@ namespace CSVRender
|
||||||
|
|
||||||
// Attach parts to skeleton
|
// Attach parts to skeleton
|
||||||
loadBodyParts();
|
loadBodyParts();
|
||||||
|
|
||||||
|
const osg::Vec2f& attributes = mActorData->getRaceWeightHeight();
|
||||||
|
|
||||||
|
mBaseNode->setScale(osg::Vec3d(attributes.x(), attributes.x(), attributes.y()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
@ -59,7 +60,7 @@ namespace CSVRender
|
||||||
CSMWorld::Data& mData;
|
CSMWorld::Data& mData;
|
||||||
CSMWorld::ActorAdapter::ActorDataPtr mActorData;
|
CSMWorld::ActorAdapter::ActorDataPtr mActorData;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> mBaseNode;
|
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||||
SceneUtil::Skeleton* mSkeleton;
|
SceneUtil::Skeleton* mSkeleton;
|
||||||
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
|
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,10 +61,14 @@ add_openmw_dir (mwscript
|
||||||
|
|
||||||
add_openmw_dir (mwlua
|
add_openmw_dir (mwlua
|
||||||
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
||||||
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
|
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||||
camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings itemdata
|
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
|
||||||
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 types/clothing types/levelledlist types/terminal
|
postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings
|
||||||
worker magicbindings factionbindings classbindings animationbindings
|
classbindings itemdata inputprocessor animationbindings birthsignbindings
|
||||||
|
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
|
||||||
|
types/clothing types/levelledlist types/terminal
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwsound
|
add_openmw_dir (mwsound
|
||||||
|
@ -84,7 +88,7 @@ add_openmw_dir (mwworld
|
||||||
|
|
||||||
add_openmw_dir (mwphysics
|
add_openmw_dir (mwphysics
|
||||||
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
||||||
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
|
contacttestresultcallback stepper movementsolver projectile
|
||||||
actorconvexcallback raycasting mtphysics contacttestwrapper projectileconvexcallback
|
actorconvexcallback raycasting mtphysics contacttestwrapper projectileconvexcallback
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <components/debug/gldebug.hpp>
|
#include <components/debug/gldebug.hpp>
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
#include <components/misc/strings/format.hpp>
|
||||||
|
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
#include <components/vfs/registerarchives.hpp>
|
#include <components/vfs/registerarchives.hpp>
|
||||||
|
@ -109,10 +110,23 @@ namespace
|
||||||
profiler.removeUserStatsLine(" -Async");
|
profiler.removeUserStatsLine(" -Async");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScheduleNonDialogMessageBox
|
struct ScreenCaptureMessageBox
|
||||||
{
|
{
|
||||||
void operator()(std::string message) const
|
void operator()(std::string filePath) const
|
||||||
{
|
{
|
||||||
|
if (filePath.empty())
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->scheduleMessageBox(
|
||||||
|
"#{OMWEngine:ScreenshotFailed}", MWGui::ShowInDialogueMode_Never);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string messageFormat
|
||||||
|
= MWBase::Environment::get().getL10nManager()->getMessage("OMWEngine", "ScreenshotMade");
|
||||||
|
|
||||||
|
std::string message = Misc::StringUtils::format(messageFormat, filePath);
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->scheduleMessageBox(
|
MWBase::Environment::get().getWindowManager()->scheduleMessageBox(
|
||||||
std::move(message), MWGui::ShowInDialogueMode_Never);
|
std::move(message), MWGui::ShowInDialogueMode_Never);
|
||||||
}
|
}
|
||||||
|
@ -717,9 +731,8 @@ void OMW::Engine::prepareEngine()
|
||||||
mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation(mWorkQueue,
|
mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation(mWorkQueue,
|
||||||
new SceneUtil::WriteScreenshotToFileOperation(mCfgMgr.getScreenshotPath(),
|
new SceneUtil::WriteScreenshotToFileOperation(mCfgMgr.getScreenshotPath(),
|
||||||
Settings::general().mScreenshotFormat,
|
Settings::general().mScreenshotFormat,
|
||||||
Settings::general().mNotifyOnSavedScreenshot
|
Settings::general().mNotifyOnSavedScreenshot ? std::function<void(std::string)>(ScreenCaptureMessageBox{})
|
||||||
? std::function<void(std::string)>(ScheduleNonDialogMessageBox{})
|
: std::function<void(std::string)>(IgnoreString{})));
|
||||||
: std::function<void(std::string)>(IgnoreString{})));
|
|
||||||
|
|
||||||
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
|
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
|
||||||
|
|
||||||
|
@ -890,8 +903,8 @@ void OMW::Engine::prepareEngine()
|
||||||
<< 100 * static_cast<double>(result.second) / result.first << "%)";
|
<< 100 * static_cast<double>(result.second) / result.first << "%)";
|
||||||
}
|
}
|
||||||
|
|
||||||
mLuaManager->init();
|
|
||||||
mLuaManager->loadPermanentStorage(mCfgMgr.getUserConfigPath());
|
mLuaManager->loadPermanentStorage(mCfgMgr.getUserConfigPath());
|
||||||
|
mLuaManager->init();
|
||||||
|
|
||||||
// starts a separate lua thread if "lua num threads" > 0
|
// starts a separate lua thread if "lua num threads" > 0
|
||||||
mLuaWorker = std::make_unique<MWLua::Worker>(*mLuaManager, *mViewer);
|
mLuaWorker = std::make_unique<MWLua::Worker>(*mLuaManager, *mViewer);
|
||||||
|
|
|
@ -55,6 +55,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void newGameStarted() = 0;
|
virtual void newGameStarted() = 0;
|
||||||
virtual void gameLoaded() = 0;
|
virtual void gameLoaded() = 0;
|
||||||
|
virtual void gameEnded() = 0;
|
||||||
|
virtual void noGame() = 0;
|
||||||
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
|
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
|
||||||
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
|
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
|
||||||
virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0;
|
virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0;
|
||||||
|
@ -64,8 +66,10 @@ namespace MWBase
|
||||||
virtual void animationTextKey(const MWWorld::Ptr& actor, const std::string& key) = 0;
|
virtual void animationTextKey(const MWWorld::Ptr& actor, const std::string& key) = 0;
|
||||||
virtual void playAnimation(const MWWorld::Ptr& object, const std::string& groupname,
|
virtual void playAnimation(const MWWorld::Ptr& object, const std::string& groupname,
|
||||||
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
||||||
std::string_view start, std::string_view stop, float startpoint, size_t loops, bool loopfallback)
|
std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback)
|
||||||
= 0;
|
= 0;
|
||||||
|
virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0;
|
||||||
|
virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0;
|
||||||
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
||||||
virtual void actorDied(const MWWorld::Ptr& actor) = 0;
|
virtual void actorDied(const MWWorld::Ptr& actor) = 0;
|
||||||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||||
|
@ -79,6 +83,12 @@ namespace MWBase
|
||||||
|
|
||||||
struct InputEvent
|
struct InputEvent
|
||||||
{
|
{
|
||||||
|
struct WheelChange
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
KeyPressed,
|
KeyPressed,
|
||||||
|
@ -89,8 +99,11 @@ namespace MWBase
|
||||||
TouchPressed,
|
TouchPressed,
|
||||||
TouchReleased,
|
TouchReleased,
|
||||||
TouchMoved,
|
TouchMoved,
|
||||||
|
MouseButtonPressed,
|
||||||
|
MouseButtonReleased,
|
||||||
|
MouseWheel,
|
||||||
} mType;
|
} mType;
|
||||||
std::variant<SDL_Keysym, int, SDLUtil::TouchEvent> mValue;
|
std::variant<SDL_Keysym, int, SDLUtil::TouchEvent, WheelChange> mValue;
|
||||||
};
|
};
|
||||||
virtual void inputEvent(const InputEvent& event) = 0;
|
virtual void inputEvent(const InputEvent& event) = 0;
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,9 @@ namespace MWBase
|
||||||
virtual bool awarenessCheck(const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;
|
virtual bool awarenessCheck(const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;
|
||||||
|
|
||||||
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
|
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
|
||||||
virtual void startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
|
virtual void startCombat(
|
||||||
|
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies)
|
||||||
|
= 0;
|
||||||
|
|
||||||
/// Removes an actor and its allies from combat with the actor's targets.
|
/// Removes an actor and its allies from combat with the actor's targets.
|
||||||
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;
|
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;
|
||||||
|
@ -171,7 +173,7 @@ namespace MWBase
|
||||||
///< Forces an object to refresh its animation state.
|
///< Forces an object to refresh its animation state.
|
||||||
|
|
||||||
virtual bool playAnimationGroup(
|
virtual bool playAnimationGroup(
|
||||||
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, int number = 1, bool scripted = false)
|
const MWWorld::Ptr& ptr, std::string_view groupName, int mode, uint32_t number = 1, bool scripted = false)
|
||||||
= 0;
|
= 0;
|
||||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||||
/// in the scene should be ignored.
|
/// in the scene should be ignored.
|
||||||
|
@ -180,8 +182,8 @@ namespace MWBase
|
||||||
/// \param number How many times the animation should be run
|
/// \param number How many times the animation should be run
|
||||||
/// \param scripted Whether the animation should be treated as a scripted animation.
|
/// \param scripted Whether the animation should be treated as a scripted animation.
|
||||||
/// \return Success or error
|
/// \return Success or error
|
||||||
virtual bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, int loops, float speed,
|
virtual bool playAnimationGroupLua(const MWWorld::Ptr& ptr, std::string_view groupName, uint32_t loops,
|
||||||
std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
float speed, std::string_view startKey, std::string_view stopKey, bool forceLoop)
|
||||||
= 0;
|
= 0;
|
||||||
///< Lua variant of playAnimationGroup. The mode parameter is omitted
|
///< Lua variant of playAnimationGroup. The mode parameter is omitted
|
||||||
/// and forced to 0. modes 1 and 2 can be emulated by doing clearAnimationQueue() and
|
/// and forced to 0. modes 1 and 2 can be emulated by doing clearAnimationQueue() and
|
||||||
|
|
|
@ -44,6 +44,9 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void askLoadRecent() = 0;
|
virtual void askLoadRecent() = 0;
|
||||||
|
|
||||||
|
virtual void requestNewGame() = 0;
|
||||||
|
virtual void requestLoad(const std::filesystem::path& filepath) = 0;
|
||||||
|
|
||||||
virtual State getState() const = 0;
|
virtual State getState() const = 0;
|
||||||
|
|
||||||
virtual void newGame(bool bypass = false) = 0;
|
virtual void newGame(bool bypass = false) = 0;
|
||||||
|
|
|
@ -166,7 +166,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0;
|
virtual void setConsoleSelectedObject(const MWWorld::Ptr& object) = 0;
|
||||||
virtual MWWorld::Ptr getConsoleSelectedObject() const = 0;
|
virtual MWWorld::Ptr getConsoleSelectedObject() const = 0;
|
||||||
virtual void setConsoleMode(const std::string& mode) = 0;
|
virtual void setConsoleMode(std::string_view mode) = 0;
|
||||||
|
virtual const std::string& getConsoleMode() = 0;
|
||||||
|
|
||||||
static constexpr std::string_view sConsoleColor_Default = "#FFFFFF";
|
static constexpr std::string_view sConsoleColor_Default = "#FFFFFF";
|
||||||
static constexpr std::string_view sConsoleColor_Error = "#FF2222";
|
static constexpr std::string_view sConsoleColor_Error = "#FF2222";
|
||||||
|
|
|
@ -304,7 +304,7 @@ namespace MWBase
|
||||||
virtual const MWPhysics::RayCastingInterface* getRayCasting() const = 0;
|
virtual const MWPhysics::RayCastingInterface* getRayCasting() const = 0;
|
||||||
|
|
||||||
virtual bool castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to,
|
virtual bool castRenderingRay(MWPhysics::RayCastingResult& res, const osg::Vec3f& from, const osg::Vec3f& to,
|
||||||
bool ignorePlayer, bool ignoreActors)
|
bool ignorePlayer, bool ignoreActors, std::span<const MWWorld::Ptr> ignoreList = {})
|
||||||
= 0;
|
= 0;
|
||||||
|
|
||||||
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;
|
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace MWClass
|
||||||
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Activator::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Activator::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Activator>(ptr);
|
return getClassModel<ESM::Activator>(ptr);
|
||||||
}
|
}
|
||||||
|
@ -141,15 +141,14 @@ namespace MWClass
|
||||||
|
|
||||||
ESM::RefId Activator::getSoundIdFromSndGen(const MWWorld::Ptr& ptr, std::string_view name) const
|
ESM::RefId Activator::getSoundIdFromSndGen(const MWWorld::Ptr& ptr, std::string_view name) const
|
||||||
{
|
{
|
||||||
const std::string model
|
// Assume it's not empty, since we wouldn't have gotten the soundgen otherwise
|
||||||
= getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise
|
const std::string_view model = getModel(ptr);
|
||||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||||
const ESM::RefId* creatureId = nullptr;
|
const ESM::RefId* creatureId = nullptr;
|
||||||
|
|
||||||
for (const ESM::Creature& iter : store.get<ESM::Creature>())
|
for (const ESM::Creature& iter : store.get<ESM::Creature>())
|
||||||
{
|
{
|
||||||
if (!iter.mModel.empty()
|
if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, iter.mModel))
|
||||||
&& Misc::StringUtils::ciEqual(model, Misc::ResourceHelpers::correctMeshPath(iter.mModel)))
|
|
||||||
{
|
{
|
||||||
creatureId = !iter.mOriginal.empty() ? &iter.mOriginal : &iter.mId;
|
creatureId = !iter.mOriginal.empty() ? &iter.mOriginal : &iter.mId;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
|
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
|
||||||
///< Generate action for activation
|
///< Generate action for activation
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
bool useAnim() const override;
|
bool useAnim() const override;
|
||||||
///< Whether or not to use animated variant of model (default false)
|
///< Whether or not to use animated variant of model (default false)
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Apparatus::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Apparatus::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Apparatus>(ptr);
|
return getClassModel<ESM::Apparatus>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Armor::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Armor::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Armor>(ptr);
|
return getClassModel<ESM::Armor>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace MWClass
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BodyPart::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view BodyPart::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::BodyPart>(ptr);
|
return getClassModel<ESM::BodyPart>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace MWClass
|
||||||
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< @return true if this object has a tooltip when focused (default implementation: true)
|
///< @return true if this object has a tooltip when focused (default implementation: true)
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Book::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Book::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Book>(ptr);
|
return getClassModel<ESM::Book>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
|
|
|
@ -4,22 +4,16 @@
|
||||||
#include "../mwworld/livecellref.hpp"
|
#include "../mwworld/livecellref.hpp"
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
template <class Class>
|
template <class Class>
|
||||||
std::string getClassModel(const MWWorld::ConstPtr& ptr)
|
std::string_view getClassModel(const MWWorld::ConstPtr& ptr)
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<Class>* ref = ptr.get<Class>();
|
const MWWorld::LiveCellRef<Class>* ref = ptr.get<Class>();
|
||||||
|
return ref->mBase->mModel;
|
||||||
if (!ref->mBase->mModel.empty())
|
|
||||||
return Misc::ResourceHelpers::correctMeshPath(ref->mBase->mModel);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Clothing::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Clothing::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Clothing>(ptr);
|
return getClassModel<ESM::Clothing>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
int getEnchantmentPoints(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ namespace MWClass
|
||||||
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Container::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Container::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Container>(ptr);
|
return getClassModel<ESM::Container>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace MWClass
|
||||||
|
|
||||||
void respawn(const MWWorld::Ptr& ptr) const override;
|
void respawn(const MWWorld::Ptr& ptr) const override;
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
bool useAnim() const override;
|
bool useAnim() const override;
|
||||||
|
|
||||||
|
|
|
@ -178,14 +178,14 @@ namespace MWClass
|
||||||
objects.insertCreature(ptr, model, hasInventoryStore(ptr));
|
objects.insertCreature(ptr, model, hasInventoryStore(ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Creature::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Creature::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Creature>(ptr);
|
return getClassModel<ESM::Creature>(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
void Creature::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const
|
||||||
{
|
{
|
||||||
std::string model = getModel(ptr);
|
std::string_view model = getModel(ptr);
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
models.push_back(model);
|
models.push_back(model);
|
||||||
|
|
||||||
|
@ -474,20 +474,17 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
const MWMechanics::AiSequence& aiSequence = stats.getAiSequence();
|
|
||||||
|
|
||||||
const bool isInCombat = aiSequence.isInCombat();
|
|
||||||
if (stats.isDead())
|
if (stats.isDead())
|
||||||
{
|
{
|
||||||
// by default user can loot friendly actors during death animation
|
// by default user can loot non-fighting actors during death animation
|
||||||
if (Settings::game().mCanLootDuringDeathAnimation && !isInCombat)
|
if (Settings::game().mCanLootDuringDeathAnimation)
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||||
|
|
||||||
// otherwise wait until death animation
|
// otherwise wait until death animation
|
||||||
if (stats.isDeathAnimationFinished())
|
if (stats.isDeathAnimationFinished())
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||||
}
|
}
|
||||||
else if ((!isInCombat || aiSequence.isFleeing()) && !stats.getKnockedDown())
|
else if (!stats.getKnockedDown())
|
||||||
return std::make_unique<MWWorld::ActionTalk>(ptr);
|
return std::make_unique<MWWorld::ActionTalk>(ptr);
|
||||||
|
|
||||||
// Tribunal and some mod companions oddly enough must use open action as fallback
|
// Tribunal and some mod companions oddly enough must use open action as fallback
|
||||||
|
@ -651,13 +648,13 @@ namespace MWClass
|
||||||
|
|
||||||
if (sounds.empty())
|
if (sounds.empty())
|
||||||
{
|
{
|
||||||
const std::string model = getModel(ptr);
|
const std::string_view model = getModel(ptr);
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
{
|
{
|
||||||
for (const ESM::Creature& creature : store.get<ESM::Creature>())
|
for (const ESM::Creature& creature : store.get<ESM::Creature>())
|
||||||
{
|
{
|
||||||
if (creature.mId != ourId && creature.mOriginal != ourId && !creature.mModel.empty()
|
if (creature.mId != ourId && creature.mOriginal != ourId && !creature.mModel.empty()
|
||||||
&& Misc::StringUtils::ciEqual(model, Misc::ResourceHelpers::correctMeshPath(creature.mModel)))
|
&& Misc::StringUtils::ciEqual(model, creature.mModel))
|
||||||
{
|
{
|
||||||
const ESM::RefId& fallbackId = !creature.mOriginal.empty() ? creature.mOriginal : creature.mId;
|
const ESM::RefId& fallbackId = !creature.mOriginal.empty() ? creature.mOriginal : creature.mId;
|
||||||
sound = store.get<ESM::SoundGenerator>().begin();
|
sound = store.get<ESM::SoundGenerator>().begin();
|
||||||
|
|
|
@ -105,9 +105,9 @@ namespace MWClass
|
||||||
|
|
||||||
float getMaxSpeed(const MWWorld::Ptr& ptr) const override;
|
float getMaxSpeed(const MWWorld::Ptr& ptr) const override;
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const override;
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||||
///< list getModel().
|
///< list getModel().
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace MWClass
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Door::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Door::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Door>(ptr);
|
return getClassModel<ESM::Door>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace MWClass
|
||||||
ESM::RefId getScript(const MWWorld::ConstPtr& ptr) const override;
|
ESM::RefId getScript(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Return name of the script attached to ptr
|
///< Return name of the script attached to ptr
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
MWWorld::DoorState getDoorState(const MWWorld::ConstPtr& ptr) const override;
|
MWWorld::DoorState getDoorState(const MWWorld::ConstPtr& ptr) const override;
|
||||||
/// This does not actually cause the door to move. Use World::activateDoor instead.
|
/// This does not actually cause the door to move. Use World::activateDoor instead.
|
||||||
|
|
|
@ -96,14 +96,14 @@ namespace MWClass
|
||||||
|
|
||||||
std::string_view getName(const MWWorld::ConstPtr& ptr) const override { return {}; }
|
std::string_view getName(const MWWorld::ConstPtr& ptr) const override { return {}; }
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override
|
||||||
{
|
{
|
||||||
std::string model = getClassModel<Record>(ptr);
|
std::string_view model = getClassModel<Record>(ptr);
|
||||||
|
|
||||||
// Hide meshes meshes/marker/* and *LOD.nif in ESM4 cells. It is a temporarty hack.
|
// Hide meshes meshes/marker/* and *LOD.nif in ESM4 cells. It is a temporarty hack.
|
||||||
// Needed because otherwise LOD meshes are rendered on top of normal meshes.
|
// Needed because otherwise LOD meshes are rendered on top of normal meshes.
|
||||||
// TODO: Figure out a better way find markers and LOD meshes; show LOD only outside of active grid.
|
// TODO: Figure out a better way find markers and LOD meshes; show LOD only outside of active grid.
|
||||||
if (model.empty() || Misc::StringUtils::ciStartsWith(model, "meshes\\marker")
|
if (model.empty() || Misc::StringUtils::ciStartsWith(model, "marker")
|
||||||
|| Misc::StringUtils::ciEndsWith(model, "lod.nif"))
|
|| Misc::StringUtils::ciEndsWith(model, "lod.nif"))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
|
@ -175,17 +175,14 @@ namespace MWClass
|
||||||
return getCustomData(ptr).mIsFemale;
|
return getCustomData(ptr).mIsFemale;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view ESM4Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
const ESM4NpcCustomData& data = getCustomData(ptr);
|
const ESM4NpcCustomData& data = getCustomData(ptr);
|
||||||
if (data.mTraits == nullptr)
|
if (data.mTraits == nullptr)
|
||||||
return {};
|
return {};
|
||||||
std::string_view model;
|
|
||||||
if (data.mTraits->mIsTES4)
|
if (data.mTraits->mIsTES4)
|
||||||
model = data.mTraits->mModel;
|
return data.mTraits->mModel;
|
||||||
else
|
return data.mIsFemale ? data.mRace->mModelFemale : data.mRace->mModelMale;
|
||||||
model = data.mIsFemale ? data.mRace->mModelFemale : data.mRace->mModelMale;
|
|
||||||
return Misc::ResourceHelpers::correctMeshPath(model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view ESM4Npc::getName(const MWWorld::ConstPtr& ptr) const
|
std::string_view ESM4Npc::getName(const MWWorld::ConstPtr& ptr) const
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace MWClass
|
||||||
return ESM4Impl::getToolTipInfo(getName(ptr), count);
|
return ESM4Impl::getToolTipInfo(getName(ptr), count);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
static const ESM4::Npc* getTraitsRecord(const MWWorld::Ptr& ptr);
|
static const ESM4::Npc* getTraitsRecord(const MWWorld::Ptr& ptr);
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Ingredient::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Ingredient::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Ingredient>(ptr);
|
return getClassModel<ESM::Ingredient>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace MWClass
|
||||||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Return name of inventory icon.
|
///< Return name of inventory icon.
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ namespace MWClass
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Light::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Light::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Light>(ptr);
|
return getClassModel<ESM::Light>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace MWClass
|
||||||
float getRemainingUsageTime(const MWWorld::ConstPtr& ptr) const override;
|
float getRemainingUsageTime(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Returns the remaining duration of the object.
|
///< Returns the remaining duration of the object.
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Lockpick::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Lockpick::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Lockpick>(ptr);
|
return getClassModel<ESM::Lockpick>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Miscellaneous::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Miscellaneous::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Miscellaneous>(ptr);
|
return getClassModel<ESM::Miscellaneous>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace MWClass
|
||||||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Return name of inventory icon.
|
///< Return name of inventory icon.
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
|
@ -19,9 +19,11 @@
|
||||||
#include <components/esm3/loadsoun.hpp>
|
#include <components/esm3/loadsoun.hpp>
|
||||||
#include <components/esm3/npcstate.hpp>
|
#include <components/esm3/npcstate.hpp>
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "../mwbase/dialoguemanager.hpp"
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
@ -424,45 +426,51 @@ namespace MWClass
|
||||||
return (ref->mBase->mRecordFlags & ESM::FLAG_Persistent) != 0;
|
return (ref->mBase->mRecordFlags & ESM::FLAG_Persistent) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Npc::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
|
{
|
||||||
|
const MWWorld::LiveCellRef<ESM::NPC>* ref = ptr.get<ESM::NPC>();
|
||||||
|
std::string_view model = Settings::models().mBaseanim.get();
|
||||||
|
const ESM::Race* race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(ref->mBase->mRace);
|
||||||
|
if (race->mData.mFlags & ESM::Race::Beast)
|
||||||
|
model = Settings::models().mBaseanimkna.get();
|
||||||
|
// Base animations should be in the meshes dir
|
||||||
|
constexpr std::string_view prefix = "meshes/";
|
||||||
|
assert(VFS::Path::pathEqual(prefix, model.substr(0, prefix.size())));
|
||||||
|
return model.substr(prefix.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Npc::getCorrectedModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::NPC>* ref = ptr.get<ESM::NPC>();
|
const MWWorld::LiveCellRef<ESM::NPC>* ref = ptr.get<ESM::NPC>();
|
||||||
|
|
||||||
std::string model = Settings::models().mBaseanim;
|
const std::string& model = Settings::models().mBaseanim;
|
||||||
const ESM::Race* race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(ref->mBase->mRace);
|
const ESM::Race* race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(ref->mBase->mRace);
|
||||||
if (race->mData.mFlags & ESM::Race::Beast)
|
if (race->mData.mFlags & ESM::Race::Beast)
|
||||||
model = Settings::models().mBaseanimkna;
|
return Settings::models().mBaseanimkna;
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Npc::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const
|
void Npc::getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();
|
const MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();
|
||||||
const auto& esmStore = MWBase::Environment::get().getESMStore();
|
const auto& esmStore = MWBase::Environment::get().getESMStore();
|
||||||
const ESM::Race* race = esmStore->get<ESM::Race>().search(npc->mBase->mRace);
|
models.push_back(getModel(ptr));
|
||||||
if (race && race->mData.mFlags & ESM::Race::Beast)
|
|
||||||
models.push_back(Settings::models().mBaseanimkna);
|
|
||||||
|
|
||||||
// keep these always loaded just in case
|
|
||||||
models.push_back(Settings::models().mXargonianswimkna);
|
|
||||||
models.push_back(Settings::models().mXbaseanimfemale);
|
|
||||||
models.push_back(Settings::models().mXbaseanim);
|
|
||||||
|
|
||||||
if (!npc->mBase->mModel.empty())
|
if (!npc->mBase->mModel.empty())
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(npc->mBase->mModel));
|
models.push_back(npc->mBase->mModel);
|
||||||
|
|
||||||
if (!npc->mBase->mHead.empty())
|
if (!npc->mBase->mHead.empty())
|
||||||
{
|
{
|
||||||
const ESM::BodyPart* head = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
const ESM::BodyPart* head = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHead);
|
||||||
if (head)
|
if (head)
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(head->mModel));
|
models.push_back(head->mModel);
|
||||||
}
|
}
|
||||||
if (!npc->mBase->mHair.empty())
|
if (!npc->mBase->mHair.empty())
|
||||||
{
|
{
|
||||||
const ESM::BodyPart* hair = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
const ESM::BodyPart* hair = esmStore->get<ESM::BodyPart>().search(npc->mBase->mHair);
|
||||||
if (hair)
|
if (hair)
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(hair->mModel));
|
models.push_back(hair->mModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
|
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
|
||||||
|
@ -486,7 +494,7 @@ namespace MWClass
|
||||||
|
|
||||||
const ESM::BodyPart* part = esmStore->get<ESM::BodyPart>().search(partname);
|
const ESM::BodyPart* part = esmStore->get<ESM::BodyPart>().search(partname);
|
||||||
if (part && !part->mModel.empty())
|
if (part && !part->mModel.empty())
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
models.push_back(part->mModel);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (equipped->getType() == ESM::Clothing::sRecordId)
|
if (equipped->getType() == ESM::Clothing::sRecordId)
|
||||||
|
@ -501,7 +509,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string model = equipped->getClass().getModel(*equipped);
|
std::string_view model = equipped->getClass().getModel(*equipped);
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
models.push_back(model);
|
models.push_back(model);
|
||||||
}
|
}
|
||||||
|
@ -510,14 +518,14 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
// preload body parts
|
// preload body parts
|
||||||
if (race)
|
if (const ESM::Race* race = esmStore->get<ESM::Race>().search(npc->mBase->mRace))
|
||||||
{
|
{
|
||||||
const std::vector<const ESM::BodyPart*>& parts
|
const std::vector<const ESM::BodyPart*>& parts
|
||||||
= MWRender::NpcAnimation::getBodyParts(race->mId, female, false, false);
|
= MWRender::NpcAnimation::getBodyParts(race->mId, female, false, false);
|
||||||
for (const ESM::BodyPart* part : parts)
|
for (const ESM::BodyPart* part : parts)
|
||||||
{
|
{
|
||||||
if (part && !part->mModel.empty())
|
if (part && !part->mModel.empty())
|
||||||
models.push_back(Misc::ResourceHelpers::correctMeshPath(part->mModel));
|
models.push_back(part->mModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -655,7 +663,7 @@ namespace MWClass
|
||||||
ESM::RefId weapskill = ESM::Skill::HandToHand;
|
ESM::RefId weapskill = ESM::Skill::HandToHand;
|
||||||
if (!weapon.isEmpty())
|
if (!weapon.isEmpty())
|
||||||
weapskill = weapon.getClass().getEquipmentSkill(weapon);
|
weapskill = weapon.getClass().getEquipmentSkill(weapon);
|
||||||
skillUsageSucceeded(ptr, weapskill, 0);
|
skillUsageSucceeded(ptr, weapskill, ESM::Skill::Weapon_SuccessfulHit);
|
||||||
|
|
||||||
const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence();
|
const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence();
|
||||||
|
|
||||||
|
@ -845,7 +853,7 @@ namespace MWClass
|
||||||
|
|
||||||
ESM::RefId skill = armor.getClass().getEquipmentSkill(armor);
|
ESM::RefId skill = armor.getClass().getEquipmentSkill(armor);
|
||||||
if (ptr == MWMechanics::getPlayer())
|
if (ptr == MWMechanics::getPlayer())
|
||||||
skillUsageSucceeded(ptr, skill, 0);
|
skillUsageSucceeded(ptr, skill, ESM::Skill::Armor_HitByOpponent);
|
||||||
|
|
||||||
if (skill == ESM::Skill::LightArmor)
|
if (skill == ESM::Skill::LightArmor)
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
||||||
|
@ -855,7 +863,7 @@ namespace MWClass
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
else if (ptr == MWMechanics::getPlayer())
|
else if (ptr == MWMechanics::getPlayer())
|
||||||
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0);
|
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, ESM::Skill::Armor_HitByOpponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,35 +925,38 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
|
const MWMechanics::AiSequence& aiSequence = stats.getAiSequence();
|
||||||
|
const bool isPursuing = aiSequence.isInPursuit() && actor == MWMechanics::getPlayer();
|
||||||
|
const bool inCombatWithActor = aiSequence.isInCombat(actor) || isPursuing;
|
||||||
|
|
||||||
if (stats.isDead())
|
if (stats.isDead())
|
||||||
{
|
{
|
||||||
// by default user can loot friendly actors during death animation
|
// by default user can loot non-fighting actors during death animation
|
||||||
if (Settings::game().mCanLootDuringDeathAnimation && !stats.getAiSequence().isInCombat())
|
if (Settings::game().mCanLootDuringDeathAnimation)
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||||
|
|
||||||
// otherwise wait until death animation
|
// otherwise wait until death animation
|
||||||
if (stats.isDeathAnimationFinished())
|
if (stats.isDeathAnimationFinished())
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||||
}
|
}
|
||||||
else if (!stats.getAiSequence().isInCombat())
|
else
|
||||||
{
|
{
|
||||||
if (stats.getKnockedDown() || MWBase::Environment::get().getMechanicsManager()->isSneaking(actor))
|
const bool allowStealingFromKO
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr); // stealing
|
= Settings::game().mAlwaysAllowStealingFromKnockedOutActors || !inCombatWithActor;
|
||||||
|
if (stats.getKnockedDown() && allowStealingFromKO)
|
||||||
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||||
|
|
||||||
// Can't talk to werewolves
|
const bool allowStealingWhileSneaking = !inCombatWithActor;
|
||||||
if (!getNpcStats(ptr).isWerewolf())
|
if (MWBase::Environment::get().getMechanicsManager()->isSneaking(actor) && allowStealingWhileSneaking)
|
||||||
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||||
|
|
||||||
|
const bool allowTalking = !inCombatWithActor && !getNpcStats(ptr).isWerewolf();
|
||||||
|
if (allowTalking)
|
||||||
return std::make_unique<MWWorld::ActionTalk>(ptr);
|
return std::make_unique<MWWorld::ActionTalk>(ptr);
|
||||||
}
|
}
|
||||||
else // In combat
|
|
||||||
{
|
|
||||||
if (Settings::game().mAlwaysAllowStealingFromKnockedOutActors && stats.getKnockedDown())
|
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr); // stealing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tribunal and some mod companions oddly enough must use open action as fallback
|
if (inCombatWithActor)
|
||||||
if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
|
return std::make_unique<MWWorld::FailedAction>("#{sActorInCombat}");
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
|
||||||
|
|
||||||
return std::make_unique<MWWorld::FailedAction>();
|
return std::make_unique<MWWorld::FailedAction>();
|
||||||
}
|
}
|
||||||
|
@ -1079,7 +1090,8 @@ namespace MWClass
|
||||||
if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())
|
if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!customData.mNpcStats.getAiSequence().isInCombat())
|
const MWMechanics::AiSequence& aiSeq = customData.mNpcStats.getAiSequence();
|
||||||
|
if (!aiSeq.isInCombat() || aiSeq.isFleeing())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (Settings::game().mAlwaysAllowStealingFromKnockedOutActors && customData.mNpcStats.getKnockedDown())
|
if (Settings::game().mAlwaysAllowStealingFromKnockedOutActors && customData.mNpcStats.getKnockedDown())
|
||||||
|
@ -1131,16 +1143,7 @@ namespace MWClass
|
||||||
|
|
||||||
void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const
|
void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const
|
||||||
{
|
{
|
||||||
MWMechanics::NpcStats& stats = getNpcStats(ptr);
|
MWBase::Environment::get().getLuaManager()->skillUse(ptr, skill, usageType, extraFactor);
|
||||||
|
|
||||||
if (stats.isWerewolf())
|
|
||||||
return;
|
|
||||||
|
|
||||||
MWWorld::LiveCellRef<ESM::NPC>* ref = ptr.get<ESM::NPC>();
|
|
||||||
|
|
||||||
const ESM::Class* class_ = MWBase::Environment::get().getESMStore()->get<ESM::Class>().find(ref->mBase->mClass);
|
|
||||||
|
|
||||||
stats.useSkill(skill, *class_, usageType, extraFactor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Npc::getArmorRating(const MWWorld::Ptr& ptr) const
|
float Npc::getArmorRating(const MWWorld::Ptr& ptr) const
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace MWClass
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
||||||
const MWMechanics::DamageSourceType sourceType) const override;
|
const MWMechanics::DamageSourceType sourceType) const override;
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string>& models) const override;
|
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const override;
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation:
|
||||||
///< list getModel().
|
///< list getModel().
|
||||||
|
|
||||||
|
@ -131,7 +131,9 @@ namespace MWClass
|
||||||
|
|
||||||
ESM::RefId getSoundIdFromSndGen(const MWWorld::Ptr& ptr, std::string_view name) const override;
|
ESM::RefId getSoundIdFromSndGen(const MWWorld::Ptr& ptr, std::string_view name) const override;
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
|
std::string getCorrectedModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Potion::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Potion::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Potion>(ptr);
|
return getClassModel<ESM::Potion>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace MWClass
|
||||||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Return name of inventory icon.
|
///< Return name of inventory icon.
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
float getWeight(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Probe::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Probe::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Probe>(ptr);
|
return getClassModel<ESM::Probe>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Repair::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Repair::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Repair>(ptr);
|
return getClassModel<ESM::Repair>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace MWClass
|
||||||
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
const std::string& getInventoryIcon(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Return name of inventory icon.
|
///< Return name of inventory icon.
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu (default implementation: return a
|
///< Generate action for using via inventory menu (default implementation: return a
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace MWClass
|
||||||
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Static::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Static::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Static>(ptr);
|
return getClassModel<ESM::Static>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace MWClass
|
||||||
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
bool hasToolTip(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< @return true if this object has a tooltip when focused (default implementation: true)
|
///< @return true if this object has a tooltip when focused (default implementation: true)
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Weapon::getModel(const MWWorld::ConstPtr& ptr) const
|
std::string_view Weapon::getModel(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
return getClassModel<ESM::Weapon>(ptr);
|
return getClassModel<ESM::Weapon>(ptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace MWClass
|
||||||
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
std::unique_ptr<MWWorld::Action> use(const MWWorld::Ptr& ptr, bool force = false) const override;
|
||||||
///< Generate action for using via inventory menu
|
///< Generate action for using via inventory menu
|
||||||
|
|
||||||
std::string getModel(const MWWorld::ConstPtr& ptr) const override;
|
std::string_view getModel(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
||||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||||
|
|
||||||
|
|
|
@ -543,7 +543,8 @@ namespace MWDialogue
|
||||||
mPermanentDispositionChange += perm;
|
mPermanentDispositionChange += perm;
|
||||||
|
|
||||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1);
|
player.getClass().skillUsageSucceeded(
|
||||||
|
player, ESM::Skill::Speechcraft, success ? ESM::Skill::Speechcraft_Success : ESM::Skill::Speechcraft_Fail);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -375,12 +375,18 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
|
||||||
return mChoice;
|
return mChoice;
|
||||||
|
|
||||||
case SelectWrapper::Function_AiSetting:
|
case SelectWrapper::Function_AiSetting:
|
||||||
|
{
|
||||||
|
int argument = select.getArgument();
|
||||||
|
if (argument < 0 || argument > 3)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("AiSetting index is out of range");
|
||||||
|
}
|
||||||
|
|
||||||
return mActor.getClass()
|
return mActor.getClass()
|
||||||
.getCreatureStats(mActor)
|
.getCreatureStats(mActor)
|
||||||
.getAiSetting((MWMechanics::AiSetting)select.getArgument())
|
.getAiSetting(static_cast<MWMechanics::AiSetting>(argument))
|
||||||
.getModified(false);
|
.getModified(false);
|
||||||
|
}
|
||||||
case SelectWrapper::Function_PcAttribute:
|
case SelectWrapper::Function_PcAttribute:
|
||||||
{
|
{
|
||||||
ESM::RefId attribute = ESM::Attribute::indexToRefId(select.getArgument());
|
ESM::RefId attribute = ESM::Attribute::indexToRefId(select.getArgument());
|
||||||
|
|
|
@ -126,7 +126,7 @@ namespace
|
||||||
MWGui::BookPage::ClickCallback callback = [this](intptr_t linkId) { notifyTopicClicked(linkId); };
|
MWGui::BookPage::ClickCallback callback = [this](intptr_t linkId) { notifyTopicClicked(linkId); };
|
||||||
|
|
||||||
getPage(LeftBookPage)->adviseLinkClicked(callback);
|
getPage(LeftBookPage)->adviseLinkClicked(callback);
|
||||||
getPage(RightBookPage)->adviseLinkClicked(callback);
|
getPage(RightBookPage)->adviseLinkClicked(std::move(callback));
|
||||||
|
|
||||||
getPage(LeftBookPage)->eventMouseWheel
|
getPage(LeftBookPage)->eventMouseWheel
|
||||||
+= MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);
|
+= MyGUI::newDelegate(this, &JournalWindowImpl::notifyMouseWheel);
|
||||||
|
@ -140,7 +140,7 @@ namespace
|
||||||
|
|
||||||
getPage(LeftTopicIndex)->adviseLinkClicked(callback);
|
getPage(LeftTopicIndex)->adviseLinkClicked(callback);
|
||||||
getPage(CenterTopicIndex)->adviseLinkClicked(callback);
|
getPage(CenterTopicIndex)->adviseLinkClicked(callback);
|
||||||
getPage(RightTopicIndex)->adviseLinkClicked(callback);
|
getPage(RightTopicIndex)->adviseLinkClicked(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustButton(PrevPageBTN);
|
adjustButton(PrevPageBTN);
|
||||||
|
@ -376,7 +376,7 @@ namespace
|
||||||
setVisible(PageTwoNum, relPages > 1);
|
setVisible(PageTwoNum, relPages > 1);
|
||||||
|
|
||||||
getPage(LeftBookPage)->showPage((relPages > 0) ? book : Book(), page + 0);
|
getPage(LeftBookPage)->showPage((relPages > 0) ? book : Book(), page + 0);
|
||||||
getPage(RightBookPage)->showPage((relPages > 0) ? book : Book(), page + 1);
|
getPage(RightBookPage)->showPage((relPages > 0) ? std::move(book) : Book(), page + 1);
|
||||||
|
|
||||||
setText(PageOneNum, page + 1);
|
setText(PageOneNum, page + 1);
|
||||||
setText(PageTwoNum, page + 2);
|
setText(PageTwoNum, page + 2);
|
||||||
|
|
|
@ -164,8 +164,10 @@ namespace MWGui
|
||||||
const MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
|
const MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
|
||||||
|
|
||||||
setClassImage(mClassImage,
|
setClassImage(mClassImage,
|
||||||
ESM::RefId::stringRefId(getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(0),
|
ESM::RefId::stringRefId(
|
||||||
pcStats.getSkillIncreasesForSpecialization(1), pcStats.getSkillIncreasesForSpecialization(2))));
|
getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Combat),
|
||||||
|
pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Magic),
|
||||||
|
pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Stealth))));
|
||||||
|
|
||||||
int level = creatureStats.getLevel() + 1;
|
int level = creatureStats.getLevel() + 1;
|
||||||
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level));
|
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level));
|
||||||
|
|
|
@ -134,7 +134,7 @@ namespace MWGui
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1);
|
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, ESM::Skill::Sneak_PickPocket);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <MyGUI_TextIterator.h>
|
#include <MyGUI_TextIterator.h>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
@ -174,10 +175,7 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
// increase skill
|
// increase skill
|
||||||
MWWorld::LiveCellRef<ESM::NPC>* playerRef = player.get<ESM::NPC>();
|
MWBase::Environment::get().getLuaManager()->skillLevelUp(player, skill->mId, "trainer");
|
||||||
|
|
||||||
const ESM::Class* class_ = store.get<ESM::Class>().find(playerRef->mBase->mClass);
|
|
||||||
pcStats.increaseSkill(skill->mId, *class_, true);
|
|
||||||
|
|
||||||
// remove gold
|
// remove gold
|
||||||
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
|
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
|
||||||
|
|
|
@ -371,7 +371,7 @@ namespace MWGui::Widgets
|
||||||
|
|
||||||
std::string spellLine = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill);
|
std::string spellLine = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill);
|
||||||
|
|
||||||
if (mEffectParams.mMagnMin || mEffectParams.mMagnMax)
|
if ((mEffectParams.mMagnMin || mEffectParams.mMagnMax) && !mEffectParams.mNoMagnitude)
|
||||||
{
|
{
|
||||||
ESM::MagicEffect::MagnitudeDisplayType displayType = magicEffect->getMagnitudeDisplayType();
|
ESM::MagicEffect::MagnitudeDisplayType displayType = magicEffect->getMagnitudeDisplayType();
|
||||||
if (displayType == ESM::MagicEffect::MDT_TimesInt)
|
if (displayType == ESM::MagicEffect::MDT_TimesInt)
|
||||||
|
@ -386,7 +386,7 @@ namespace MWGui::Widgets
|
||||||
|
|
||||||
spellLine += formatter.str();
|
spellLine += formatter.str();
|
||||||
}
|
}
|
||||||
else if (displayType != ESM::MagicEffect::MDT_None && !mEffectParams.mNoMagnitude)
|
else if (displayType != ESM::MagicEffect::MDT_None)
|
||||||
{
|
{
|
||||||
spellLine += " " + MyGUI::utility::toString(mEffectParams.mMagnMin);
|
spellLine += " " + MyGUI::utility::toString(mEffectParams.mMagnMin);
|
||||||
if (mEffectParams.mMagnMin != mEffectParams.mMagnMax)
|
if (mEffectParams.mMagnMin != mEffectParams.mMagnMax)
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include <components/l10n/manager.hpp>
|
#include <components/l10n/manager.hpp>
|
||||||
|
|
||||||
#include <components/lua_ui/util.hpp>
|
#include <components/lua_ui/util.hpp>
|
||||||
|
#include <components/lua_ui/widget.hpp>
|
||||||
|
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
|
@ -546,7 +547,8 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LuaUi::clearUserInterface();
|
LuaUi::clearGameInterface();
|
||||||
|
LuaUi::clearMenuInterface();
|
||||||
|
|
||||||
mStatsWatcher.reset();
|
mStatsWatcher.reset();
|
||||||
|
|
||||||
|
@ -1675,7 +1677,10 @@ namespace MWGui
|
||||||
|
|
||||||
void WindowManager::onKeyFocusChanged(MyGUI::Widget* widget)
|
void WindowManager::onKeyFocusChanged(MyGUI::Widget* widget)
|
||||||
{
|
{
|
||||||
if (widget && widget->castType<MyGUI::EditBox>(false))
|
bool isEditBox = widget && widget->castType<MyGUI::EditBox>(false);
|
||||||
|
LuaUi::WidgetExtension* luaWidget = dynamic_cast<LuaUi::WidgetExtension*>(widget);
|
||||||
|
bool capturesInput = luaWidget ? luaWidget->isTextInput() : isEditBox;
|
||||||
|
if (widget && capturesInput)
|
||||||
SDL_StartTextInput();
|
SDL_StartTextInput();
|
||||||
else
|
else
|
||||||
SDL_StopTextInput();
|
SDL_StopTextInput();
|
||||||
|
@ -2173,11 +2178,16 @@ namespace MWGui
|
||||||
mConsole->print(msg, color);
|
mConsole->print(msg, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::setConsoleMode(const std::string& mode)
|
void WindowManager::setConsoleMode(std::string_view mode)
|
||||||
{
|
{
|
||||||
mConsole->setConsoleMode(mode);
|
mConsole->setConsoleMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& WindowManager::getConsoleMode()
|
||||||
|
{
|
||||||
|
return mConsole->getConsoleMode();
|
||||||
|
}
|
||||||
|
|
||||||
void WindowManager::createCursors()
|
void WindowManager::createCursors()
|
||||||
{
|
{
|
||||||
MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator();
|
MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator();
|
||||||
|
|
|
@ -192,7 +192,8 @@ namespace MWGui
|
||||||
void setConsoleSelectedObject(const MWWorld::Ptr& object) override;
|
void setConsoleSelectedObject(const MWWorld::Ptr& object) override;
|
||||||
MWWorld::Ptr getConsoleSelectedObject() const override;
|
MWWorld::Ptr getConsoleSelectedObject() const override;
|
||||||
void printToConsole(const std::string& msg, std::string_view color) override;
|
void printToConsole(const std::string& msg, std::string_view color) override;
|
||||||
void setConsoleMode(const std::string& mode) override;
|
void setConsoleMode(std::string_view mode) override;
|
||||||
|
const std::string& getConsoleMode() override;
|
||||||
|
|
||||||
/// Set time left for the player to start drowning (update the drowning bar)
|
/// Set time left for the player to start drowning (update the drowning bar)
|
||||||
/// @param time time left to start drowning
|
/// @param time time left to start drowning
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/inputmanager.hpp"
|
#include "../mwbase/inputmanager.hpp"
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
@ -119,15 +120,22 @@ namespace MWInput
|
||||||
mBindingsManager->setPlayerControlsEnabled(!guiMode);
|
mBindingsManager->setPlayerControlsEnabled(!guiMode);
|
||||||
mBindingsManager->mouseReleased(arg, id);
|
mBindingsManager->mouseReleased(arg, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWBase::Environment::get().getLuaManager()->inputEvent(
|
||||||
|
{ MWBase::LuaManager::InputEvent::MouseButtonReleased, arg.button });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent& arg)
|
void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent& arg)
|
||||||
{
|
{
|
||||||
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
||||||
if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled())
|
if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled())
|
||||||
|
{
|
||||||
mBindingsManager->mouseWheelMoved(arg);
|
mBindingsManager->mouseWheelMoved(arg);
|
||||||
|
}
|
||||||
|
|
||||||
input->setJoystickLastUsed(false);
|
input->setJoystickLastUsed(false);
|
||||||
|
MWBase::Environment::get().getLuaManager()->inputEvent({ MWBase::LuaManager::InputEvent::MouseWheel,
|
||||||
|
MWBase::LuaManager::InputEvent::WheelChange{ arg.x, arg.y } });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MouseManager::mousePressed(const SDL_MouseButtonEvent& arg, Uint8 id)
|
void MouseManager::mousePressed(const SDL_MouseButtonEvent& arg, Uint8 id)
|
||||||
|
@ -161,7 +169,11 @@ namespace MWInput
|
||||||
const MWGui::SettingsWindow* settingsWindow
|
const MWGui::SettingsWindow* settingsWindow
|
||||||
= MWBase::Environment::get().getWindowManager()->getSettingsWindow();
|
= MWBase::Environment::get().getWindowManager()->getSettingsWindow();
|
||||||
if ((!settingsWindow || !settingsWindow->isVisible()) && !input->controlsDisabled())
|
if ((!settingsWindow || !settingsWindow->isVisible()) && !input->controlsDisabled())
|
||||||
|
{
|
||||||
mBindingsManager->mousePressed(arg, id);
|
mBindingsManager->mousePressed(arg, id);
|
||||||
|
}
|
||||||
|
MWBase::Environment::get().getLuaManager()->inputEvent(
|
||||||
|
{ MWBase::LuaManager::InputEvent::MouseButtonPressed, arg.button });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MouseManager::updateCursorMode()
|
void MouseManager::updateCursorMode()
|
||||||
|
|
|
@ -21,24 +21,6 @@
|
||||||
#include "animationbindings.hpp"
|
#include "animationbindings.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
namespace MWLua
|
|
||||||
{
|
|
||||||
struct AnimationGroup;
|
|
||||||
struct TextKeyCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace sol
|
|
||||||
{
|
|
||||||
template <>
|
|
||||||
struct is_automagical<MWLua::AnimationGroup> : std::false_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
struct is_automagical<std::shared_ptr<MWLua::TextKeyCallback>> : std::false_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWLua
|
namespace MWLua
|
||||||
{
|
{
|
||||||
using BlendMask = MWRender::Animation::BlendMask;
|
using BlendMask = MWRender::Animation::BlendMask;
|
||||||
|
@ -119,7 +101,7 @@ namespace MWLua
|
||||||
if (asTable)
|
if (asTable)
|
||||||
{
|
{
|
||||||
AnimationPriorities priorities = AnimationPriorities(Priority::Priority_Default);
|
AnimationPriorities priorities = AnimationPriorities(Priority::Priority_Default);
|
||||||
for (auto entry : asTable.value())
|
for (const auto& entry : asTable.value())
|
||||||
{
|
{
|
||||||
if (!entry.first.is<BoneGroup>() || !entry.second.is<Priority>())
|
if (!entry.first.is<BoneGroup>() || !entry.second.is<Priority>())
|
||||||
throw std::runtime_error("Priority table must consist of BoneGroup-Priority pairs only");
|
throw std::runtime_error("Priority table must consist of BoneGroup-Priority pairs only");
|
||||||
|
@ -249,7 +231,7 @@ namespace MWLua
|
||||||
// Extended variant of MWScript's PlayGroup and LoopGroup
|
// Extended variant of MWScript's PlayGroup and LoopGroup
|
||||||
api["playQueued"] = sol::overload(
|
api["playQueued"] = sol::overload(
|
||||||
[mechanics](const sol::object& object, const std::string& groupname, const sol::table& options) {
|
[mechanics](const sol::object& object, const std::string& groupname, const sol::table& options) {
|
||||||
int numberOfLoops = options.get_or("loops", std::numeric_limits<int>::max());
|
uint32_t numberOfLoops = options.get_or("loops", std::numeric_limits<uint32_t>::max());
|
||||||
float speed = options.get_or("speed", 1.f);
|
float speed = options.get_or("speed", 1.f);
|
||||||
std::string startKey = options.get_or<std::string>("startkey", "start");
|
std::string startKey = options.get_or<std::string>("startkey", "start");
|
||||||
std::string stopKey = options.get_or<std::string>("stopkey", "stop");
|
std::string stopKey = options.get_or<std::string>("stopkey", "stop");
|
||||||
|
@ -265,7 +247,7 @@ namespace MWLua
|
||||||
});
|
});
|
||||||
|
|
||||||
api["playBlended"] = [](const sol::object& object, std::string_view groupname, const sol::table& options) {
|
api["playBlended"] = [](const sol::object& object, std::string_view groupname, const sol::table& options) {
|
||||||
int loops = options.get_or("loops", 0);
|
uint32_t loops = options.get_or("loops", 0u);
|
||||||
MWRender::Animation::AnimPriority priority = getPriorityArgument(options);
|
MWRender::Animation::AnimPriority priority = getPriorityArgument(options);
|
||||||
BlendMask blendMask = options.get_or("blendmask", BlendMask::BlendMask_All);
|
BlendMask blendMask = options.get_or("blendmask", BlendMask::BlendMask_All);
|
||||||
bool autoDisable = options.get_or("autodisable", true);
|
bool autoDisable = options.get_or("autodisable", true);
|
||||||
|
@ -344,18 +326,19 @@ namespace MWLua
|
||||||
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos) {
|
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos) {
|
||||||
auto model = getStaticModelOrThrow(staticOrID);
|
auto model = getStaticModelOrThrow(staticOrID);
|
||||||
context.mLuaManager->addAction(
|
context.mLuaManager->addAction(
|
||||||
[world, model, worldPos]() { world->spawnEffect(model, "", worldPos); }, "openmw.vfx.spawn");
|
[world, model = std::move(model), worldPos]() { world->spawnEffect(model, "", worldPos); },
|
||||||
|
"openmw.vfx.spawn");
|
||||||
},
|
},
|
||||||
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos, const sol::table& options) {
|
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos, const sol::table& options) {
|
||||||
auto model = getStaticModelOrThrow(staticOrID);
|
auto model = getStaticModelOrThrow(staticOrID);
|
||||||
|
|
||||||
bool magicVfx = options.get_or("mwMagicVfx", true);
|
bool magicVfx = options.get_or("mwMagicVfx", true);
|
||||||
std::string textureOverride = options.get_or<std::string>("particleTextureOverride", "");
|
std::string texture = options.get_or<std::string>("particleTextureOverride", "");
|
||||||
float scale = options.get_or("scale", 1.f);
|
float scale = options.get_or("scale", 1.f);
|
||||||
|
|
||||||
context.mLuaManager->addAction(
|
context.mLuaManager->addAction(
|
||||||
[world, model, textureOverride, worldPos, scale, magicVfx]() {
|
[world, model = std::move(model), texture = std::move(texture), worldPos, scale, magicVfx]() {
|
||||||
world->spawnEffect(model, textureOverride, worldPos, scale, magicVfx);
|
world->spawnEffect(model, texture, worldPos, scale, magicVfx);
|
||||||
},
|
},
|
||||||
"openmw.vfx.spawn");
|
"openmw.vfx.spawn");
|
||||||
});
|
});
|
||||||
|
|
55
apps/openmw/mwlua/birthsignbindings.cpp
Normal file
55
apps/openmw/mwlua/birthsignbindings.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include <components/esm3/loadbsgn.hpp>
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include "birthsignbindings.hpp"
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
#include "types/types.hpp"
|
||||||
|
|
||||||
|
namespace sol
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct is_automagical<ESM::BirthSign> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<MWWorld::Store<ESM::BirthSign>> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initBirthSignRecordBindings(const Context& context)
|
||||||
|
{
|
||||||
|
sol::state_view& lua = context.mLua->sol();
|
||||||
|
sol::table birthSigns(context.mLua->sol(), sol::create);
|
||||||
|
addRecordFunctionBinding<ESM::BirthSign>(birthSigns, context);
|
||||||
|
|
||||||
|
auto signT = lua.new_usertype<ESM::BirthSign>("ESM3_BirthSign");
|
||||||
|
signT[sol::meta_function::to_string] = [](const ESM::BirthSign& rec) -> std::string {
|
||||||
|
return "ESM3_BirthSign[" + rec.mId.toDebugString() + "]";
|
||||||
|
};
|
||||||
|
signT["id"] = sol::readonly_property([](const ESM::BirthSign& rec) { return rec.mId.serializeText(); });
|
||||||
|
signT["name"] = sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mName; });
|
||||||
|
signT["description"]
|
||||||
|
= sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; });
|
||||||
|
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||||
|
signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string {
|
||||||
|
return Misc::ResourceHelpers::correctTexturePath(rec.mTexture, vfs);
|
||||||
|
});
|
||||||
|
signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table {
|
||||||
|
sol::table res(lua, sol::create);
|
||||||
|
for (size_t i = 0; i < rec.mPowers.mList.size(); ++i)
|
||||||
|
res[i + 1] = rec.mPowers.mList[i].serializeText();
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(birthSigns);
|
||||||
|
}
|
||||||
|
}
|
13
apps/openmw/mwlua/birthsignbindings.hpp
Normal file
13
apps/openmw/mwlua/birthsignbindings.hpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef MWLUA_BIRTHSIGNBINDINGS_H
|
||||||
|
#define MWLUA_BIRTHSIGNBINDINGS_H
|
||||||
|
|
||||||
|
#include <sol/forward.hpp>
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initBirthSignRecordBindings(const Context& context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_BIRTHSIGNBINDINGS_H
|
|
@ -25,7 +25,7 @@ namespace sol
|
||||||
namespace MWLua
|
namespace MWLua
|
||||||
{
|
{
|
||||||
|
|
||||||
sol::table initCoreClassBindings(const Context& context)
|
sol::table initClassRecordBindings(const Context& context)
|
||||||
{
|
{
|
||||||
sol::state_view& lua = context.mLua->sol();
|
sol::state_view& lua = context.mLua->sol();
|
||||||
sol::table classes(context.mLua->sol(), sol::create);
|
sol::table classes(context.mLua->sol(), sol::create);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
namespace MWLua
|
namespace MWLua
|
||||||
{
|
{
|
||||||
sol::table initCoreClassBindings(const Context& context);
|
sol::table initClassRecordBindings(const Context& context);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MWLUA_CLASSBINDINGS_H
|
#endif // MWLUA_CLASSBINDINGS_H
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace MWLua
|
||||||
|
|
||||||
struct Context
|
struct Context
|
||||||
{
|
{
|
||||||
|
bool mIsMenu;
|
||||||
bool mIsGlobal;
|
bool mIsGlobal;
|
||||||
LuaManager* mLuaManager;
|
LuaManager* mLuaManager;
|
||||||
LuaUtil::LuaState* mLua;
|
LuaUtil::LuaState* mLua;
|
||||||
|
|
149
apps/openmw/mwlua/corebindings.cpp
Normal file
149
apps/openmw/mwlua/corebindings.cpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
#include "corebindings.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/esm3/loadfact.hpp>
|
||||||
|
#include <components/lua/l10n.hpp>
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/lua/serialization.hpp>
|
||||||
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
|
#include <components/misc/strings/lower.hpp>
|
||||||
|
#include <components/version/version.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/statemanager.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwworld/datetimemanager.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include "animationbindings.hpp"
|
||||||
|
#include "factionbindings.hpp"
|
||||||
|
#include "luaevents.hpp"
|
||||||
|
#include "magicbindings.hpp"
|
||||||
|
#include "soundbindings.hpp"
|
||||||
|
#include "stats.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
static sol::table initContentFilesBindings(sol::state_view& lua)
|
||||||
|
{
|
||||||
|
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||||
|
sol::table list(lua, sol::create);
|
||||||
|
for (size_t i = 0; i < contentList.size(); ++i)
|
||||||
|
list[i + 1] = Misc::StringUtils::lowerCase(contentList[i]);
|
||||||
|
sol::table res(lua, sol::create);
|
||||||
|
res["list"] = LuaUtil::makeReadOnly(list);
|
||||||
|
res["indexOf"] = [&contentList](std::string_view contentFile) -> sol::optional<int> {
|
||||||
|
for (size_t i = 0; i < contentList.size(); ++i)
|
||||||
|
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
|
||||||
|
return i + 1;
|
||||||
|
return sol::nullopt;
|
||||||
|
};
|
||||||
|
res["has"] = [&contentList](std::string_view contentFile) -> bool {
|
||||||
|
for (size_t i = 0; i < contentList.size(); ++i)
|
||||||
|
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
return LuaUtil::makeReadOnly(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addCoreTimeBindings(sol::table& api, const Context& context)
|
||||||
|
{
|
||||||
|
MWWorld::DateTimeManager* timeManager = MWBase::Environment::get().getWorld()->getTimeManager();
|
||||||
|
|
||||||
|
api["getSimulationTime"] = [timeManager]() { return timeManager->getSimulationTime(); };
|
||||||
|
api["getSimulationTimeScale"] = [timeManager]() { return timeManager->getSimulationTimeScale(); };
|
||||||
|
api["getGameTime"] = [timeManager]() { return timeManager->getGameTime(); };
|
||||||
|
api["getGameTimeScale"] = [timeManager]() { return timeManager->getGameTimeScale(); };
|
||||||
|
api["isWorldPaused"] = [timeManager]() { return timeManager->isPaused(); };
|
||||||
|
api["getRealTime"] = []() {
|
||||||
|
return std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||||
|
};
|
||||||
|
// TODO: remove in global context?
|
||||||
|
api["getRealFrameDuration"] = []() { return MWBase::Environment::get().getFrameDuration(); };
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initCorePackage(const Context& context)
|
||||||
|
{
|
||||||
|
auto* lua = context.mLua;
|
||||||
|
|
||||||
|
if (lua->sol()["openmw_core"] != sol::nil)
|
||||||
|
return lua->sol()["openmw_core"];
|
||||||
|
|
||||||
|
sol::table api(lua->sol(), sol::create);
|
||||||
|
api["API_REVISION"] = Version::getLuaApiRevision(); // specified in CMakeLists.txt
|
||||||
|
api["quit"] = [lua]() {
|
||||||
|
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||||
|
MWBase::Environment::get().getStateManager()->requestQuit();
|
||||||
|
};
|
||||||
|
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) {
|
||||||
|
context.mLuaEvents->addGlobalEvent(
|
||||||
|
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
||||||
|
};
|
||||||
|
api["contentFiles"] = initContentFilesBindings(lua->sol());
|
||||||
|
api["sound"] = initCoreSoundBindings(context);
|
||||||
|
api["vfx"] = initCoreVfxBindings(context);
|
||||||
|
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
|
||||||
|
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||||
|
for (size_t i = 0; i < contentList.size(); ++i)
|
||||||
|
if (Misc::StringUtils::ciEqual(contentList[i], contentFile))
|
||||||
|
return ESM::RefId(ESM::FormId{ index, int(i) }).serializeText();
|
||||||
|
throw std::runtime_error("Content file not found: " + std::string(contentFile));
|
||||||
|
};
|
||||||
|
addCoreTimeBindings(api, context);
|
||||||
|
api["magic"] = initCoreMagicBindings(context);
|
||||||
|
api["stats"] = initCoreStatsBindings(context);
|
||||||
|
|
||||||
|
initCoreFactionBindings(context);
|
||||||
|
api["factions"] = &MWBase::Environment::get().getESMStore()->get<ESM::Faction>();
|
||||||
|
|
||||||
|
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
|
||||||
|
const MWWorld::Store<ESM::GameSetting>* gmstStore
|
||||||
|
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||||
|
api["getGMST"] = [lua = context.mLua, gmstStore](const std::string& setting) -> sol::object {
|
||||||
|
const ESM::GameSetting* gmst = gmstStore->search(setting);
|
||||||
|
if (gmst == nullptr)
|
||||||
|
return sol::nil;
|
||||||
|
const ESM::Variant& value = gmst->mValue;
|
||||||
|
switch (value.getType())
|
||||||
|
{
|
||||||
|
case ESM::VT_Float:
|
||||||
|
return sol::make_object<float>(lua->sol(), value.getFloat());
|
||||||
|
case ESM::VT_Short:
|
||||||
|
case ESM::VT_Long:
|
||||||
|
case ESM::VT_Int:
|
||||||
|
return sol::make_object<int>(lua->sol(), value.getInteger());
|
||||||
|
case ESM::VT_String:
|
||||||
|
return sol::make_object<std::string>(lua->sol(), value.getString());
|
||||||
|
case ESM::VT_Unknown:
|
||||||
|
case ESM::VT_None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return sol::nil;
|
||||||
|
};
|
||||||
|
|
||||||
|
lua->sol()["openmw_core"] = LuaUtil::makeReadOnly(api);
|
||||||
|
return lua->sol()["openmw_core"];
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initCorePackageForMenuScripts(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
for (auto& [k, v] : LuaUtil::getMutableFromReadOnly(initCorePackage(context)))
|
||||||
|
api[k] = v;
|
||||||
|
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) {
|
||||||
|
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
|
||||||
|
{
|
||||||
|
throw std::logic_error("Can't send global events when no game is loaded");
|
||||||
|
}
|
||||||
|
context.mLuaEvents->addGlobalEvent(
|
||||||
|
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
||||||
|
};
|
||||||
|
api["sound"] = sol::nil;
|
||||||
|
api["vfx"] = sol::nil;
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue