1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-07 01:11:32 +00:00

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

This commit is contained in:
Zackhasacat 2024-03-12 06:54:15 -05:00
commit 8b75932f50
300 changed files with 4597 additions and 2004 deletions

View file

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

View file

@ -15,6 +15,7 @@ Programmers
Nicolay Korslund - Project leader 2008-2010 Nicolay Korslund - Project leader 2008-2010
scrawl - Top contributor scrawl - Top contributor
AbduSharif
Adam Hogan (aurix) Adam Hogan (aurix)
Aesylwinn Aesylwinn
aegis aegis
@ -79,6 +80,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 +189,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)

View file

@ -26,12 +26,15 @@
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 #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
Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load
Bug #6025: Subrecords cannot overlap records Bug #6025: Subrecords cannot overlap records
Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex
Bug #6146: Lua command `actor:setEquipment` doesn't trigger mwscripts when equipping or unequipping a scripted item
Bug #6156: 1ft Charm or Sound magic effect vfx doesn't work properly
Bug #6190: Unintuitive sun specularity time of day dependence Bug #6190: Unintuitive sun specularity time of day dependence
Bug #6222: global map cell size can crash openmw if set to too high a value Bug #6222: global map cell size can crash openmw if set to too high a value
Bug #6313: Followers with high Fight can turn hostile Bug #6313: Followers with high Fight can turn hostile
@ -55,6 +58,7 @@
Bug #6973: Fade in happens after the scene load and is shown Bug #6973: Fade in happens after the scene load and is shown
Bug #6974: Only harmful effects are reflected Bug #6974: Only harmful effects are reflected
Bug #6977: Sun damage implementation does not match research Bug #6977: Sun damage implementation does not match research
Bug #6985: Issues with Magic Cards numbers readability
Bug #6986: Sound magic effect does not make noise Bug #6986: Sound magic effect does not make noise
Bug #6987: Set/Mod Blindness should not darken the screen Bug #6987: Set/Mod Blindness should not darken the screen
Bug #6992: Crossbow reloading doesn't look the same as in Morrowind Bug #6992: Crossbow reloading doesn't look the same as in Morrowind
@ -70,12 +74,15 @@
Bug #7084: Resurrecting an actor doesn't take into account base record changes Bug #7084: Resurrecting an actor doesn't take into account base record changes
Bug #7088: Deleting last save game of last character doesn't clear character name/details Bug #7088: Deleting last save game of last character doesn't clear character name/details
Bug #7092: BSA archives from higher priority directories don't take priority Bug #7092: BSA archives from higher priority directories don't take priority
Bug #7102: Some HQ Creatures mod models can hit the 8 texture slots limit with 0.48
Bug #7103: Multiple paths pointing to the same plugin but with different cases lead to automatically removed config entries Bug #7103: Multiple paths pointing to the same plugin but with different cases lead to automatically removed config entries
Bug #7122: Teleportation to underwater should cancel active water walking effect Bug #7122: Teleportation to underwater should cancel active water walking effect
Bug #7131: MyGUI log spam when post processing HUD is open Bug #7131: MyGUI log spam when post processing HUD is open
Bug #7134: Saves with an invalid last generated RefNum can be loaded Bug #7134: Saves with an invalid last generated RefNum can be loaded
Bug #7163: Myar Aranath: Wheat breaks the GUI Bug #7163: Myar Aranath: Wheat breaks the GUI
Bug #7168: Fix average scene luminance
Bug #7172: Current music playlist continues playing indefinitely if next playlist is empty Bug #7172: Current music playlist continues playing indefinitely if next playlist is empty
Bug #7202: Post-processing normals for terrain, water randomly stop rendering
Bug #7204: Missing actor scripts freeze the game Bug #7204: Missing actor scripts freeze the game
Bug #7229: Error marker loading failure is not handled Bug #7229: Error marker loading failure is not handled
Bug #7243: Supporting loading external files from VFS from esm files Bug #7243: Supporting loading external files from VFS from esm files
@ -98,6 +105,7 @@
Bug #7475: Equipping a constant effect item doesn't update the magic menu Bug #7475: Equipping a constant effect item doesn't update the magic menu
Bug #7502: Data directories dialog (0.48.0) forces adding subdirectory instead of intended directory Bug #7502: Data directories dialog (0.48.0) forces adding subdirectory instead of intended directory
Bug #7505: Distant terrain does not support sample size greater than cell size Bug #7505: Distant terrain does not support sample size greater than cell size
Bug #7535: Bookart paths for textures in OpenMW vs vanilla Morrowind
Bug #7553: Faction reaction loading is incorrect Bug #7553: Faction reaction loading is incorrect
Bug #7557: Terrain::ChunkManager::createChunk is called twice for the same position, lod on initial loading Bug #7557: Terrain::ChunkManager::createChunk is called twice for the same position, lod on initial loading
Bug #7573: Drain Fatigue can't bring fatigue below zero by default Bug #7573: Drain Fatigue can't bring fatigue below zero by default
@ -108,8 +116,10 @@
Bug #7611: Beast races' idle animations slide after turning or jumping in place Bug #7611: Beast races' idle animations slide after turning or jumping in place
Bug #7617: The death prompt asks the player if they wanted to load the character's last created save Bug #7617: The death prompt asks the player if they wanted to load the character's last created save
Bug #7619: Long map notes may get cut off Bug #7619: Long map notes may get cut off
Bug #7623: Incorrect placement of the script info in the engraved ring of healing tooltip
Bug #7630: Charm can be cast on creatures Bug #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
@ -127,30 +137,41 @@
Bug #7679: Scene luminance value flashes when toggling shaders Bug #7679: Scene luminance value flashes when toggling shaders
Bug #7685: Corky sometimes doesn't follow Llovyn Andus Bug #7685: Corky sometimes doesn't follow Llovyn Andus
Bug #7712: Casting doesn't support spells and enchantments with no effects Bug #7712: Casting doesn't support spells and enchantments with no effects
Bug #7721: CS: Special Chars Not Allowed in IDs
Bug #7723: Assaulting vampires and werewolves shouldn't be a crime Bug #7723: Assaulting vampires and werewolves shouldn't be a crime
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 #7763: Bullet shape loading problems, assorted
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 #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 #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 #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
Bug #7841: Editor: "Dirty" water heights are saved in modified CELLs
Bug #7859: AutoCalc flag is not used to calculate potion 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 #6149: Dehardcode Lua API_REVISION Feature #5926: Refraction based on water depth
Feature #5944: Option to use camera as sound listener
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 #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
Feature #6679: Design a custom Input Action API
Feature #6726: Lua API for creating new objects Feature #6726: Lua API for creating new objects
Feature #6727: Lua API for records of all object types
Feature #6864: Lua file access API Feature #6864: Lua file access API
Feature #6922: Improve launcher appearance Feature #6922: Improve launcher appearance
Feature #6933: Support high-resolution cursor textures Feature #6933: Support high-resolution cursor textures
@ -163,10 +184,12 @@
Feature #7125: Remembering console commands between sessions Feature #7125: Remembering console commands between sessions
Feature #7129: Add support for non-adaptive VSync Feature #7129: Add support for non-adaptive VSync
Feature #7130: Ability to set MyGUI logging verbosity Feature #7130: Ability to set MyGUI logging verbosity
Feature #7142: MWScript Lua API
Feature #7148: Optimize string literal lookup in mwscript Feature #7148: Optimize string literal lookup in mwscript
Feature #7161: OpenMW-CS: Make adding and filtering TopicInfos easier
Feature #7194: Ori to show texture paths Feature #7194: Ori to show texture paths
Feature #7214: Searching in the in-game console Feature #7214: Searching in the in-game console
Feature #7284: Searching in the console with regex and toggleable case-sensitivity Feature #7248: Searching in the console with regex and toggleable case-sensitivity
Feature #7468: Factions API for Lua Feature #7468: Factions API for Lua
Feature #7477: NegativeLight Magic Effect flag Feature #7477: NegativeLight Magic Effect flag
Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field
@ -186,7 +209,10 @@
Feature #7795: Support MaxNumberRipples INI setting Feature #7795: Support MaxNumberRipples INI setting
Feature #7805: Lua Menu context Feature #7805: Lua Menu context
Task #5896: Do not use deprecated MyGUI properties Task #5896: Do not use deprecated MyGUI properties
Task #6085: Replace boost::filesystem with std::filesystem
Task #6149: Dehardcode Lua API_REVISION
Task #6624: Drop support for saves made prior to 0.45 Task #6624: Drop support for saves made prior to 0.45
Task #7048: Get rid of std::bind
Task #7113: Move from std::atoi to std::from_char Task #7113: Move from std::atoi to std::from_char
Task #7117: Replace boost::scoped_array with std::vector Task #7117: Replace boost::scoped_array with std::vector
Task #7151: Do not use std::strerror to get errno error message Task #7151: Do not use std::strerror to get errno error message

View file

@ -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

View file

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

View file

@ -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)
@ -36,6 +44,7 @@ option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
option(BUILD_NAVMESHTOOL "Build navmesh tool" ON) option(BUILD_NAVMESHTOOL "Build navmesh tool" ON)
option(BUILD_BULLETOBJECTTOOL "Build Bullet object tool" ON) option(BUILD_BULLETOBJECTTOOL "Build Bullet object tool" ON)
option(BUILD_OPENCS_TESTS "Build OpenMW Construction Set tests" OFF) option(BUILD_OPENCS_TESTS "Build OpenMW Construction Set tests" OFF)
option(PRECOMPILE_HEADERS_WITH_MSVC "Precompile most common used headers with MSVC (alternative to ccache)" ON)
set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up. set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up.
@ -72,7 +81,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 53) set(OPENMW_LUA_API_REVISION 56)
set(OPENMW_POSTPROCESSING_API_REVISION 1) set(OPENMW_POSTPROCESSING_API_REVISION 1)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
@ -81,7 +90,7 @@ set(OPENMW_VERSION_COMMITDATE "")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(OPENMW_DOC_BASEURL "https://openmw.readthedocs.io/en/stable/") set(OPENMW_DOC_BASEURL "https://openmw.readthedocs.io/en/")
set(GIT_CHECKOUT FALSE) set(GIT_CHECKOUT FALSE)
if(EXISTS ${PROJECT_SOURCE_DIR}/.git) if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
@ -182,6 +191,22 @@ if (MSVC)
add_compile_options(/bigobj) add_compile_options(/bigobj)
add_compile_options(/Zc:__cplusplus) add_compile_options(/Zc:__cplusplus)
if (CMAKE_CXX_COMPILER_LAUNCHER OR CMAKE_C_COMPILER_LAUNCHER)
if (CMAKE_GENERATOR MATCHES "Visual Studio")
message(STATUS "A compiler launcher was specified, but will be unused by the current generator (${CMAKE_GENERATOR})")
else()
foreach (config_lower ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER "${config_lower}" config)
if (CMAKE_C_COMPILER_LAUNCHER STREQUAL "ccache")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_${config} "${CMAKE_C_FLAGS_${config}}")
endif()
if (CMAKE_CXX_COMPILER_LAUNCHER STREQUAL "ccache")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_${config} "${CMAKE_CXX_FLAGS_${config}}")
endif()
endforeach()
endif()
endif()
endif() endif()
# Set up common paths # Set up common paths
@ -710,67 +735,66 @@ if (WIN32)
) )
foreach(d ${WARNINGS_DISABLE}) foreach(d ${WARNINGS_DISABLE})
set(WARNINGS "${WARNINGS} /wd${d}") list(APPEND WARNINGS "/wd${d}")
endforeach(d) endforeach(d)
if(OPENMW_MSVC_WERROR) if(OPENMW_MSVC_WERROR)
set(WARNINGS "${WARNINGS} /WX") list(APPEND WARNINGS "/WX")
endif() endif()
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(components PRIVATE ${WARNINGS})
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS})
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920) if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE) target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
endif() endif()
if (BUILD_BSATOOL) if (BUILD_BSATOOL)
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(bsatool PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_ESMTOOL) if (BUILD_ESMTOOL)
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(esmtool PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_ESSIMPORTER) if (BUILD_ESSIMPORTER)
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw-essimporter PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_LAUNCHER) if (BUILD_LAUNCHER)
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw-launcher PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_MWINIIMPORTER) if (BUILD_MWINIIMPORTER)
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_OPENCS) if (BUILD_OPENCS)
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw-cs PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_OPENMW) if (BUILD_OPENMW)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_WIZARD) if (BUILD_WIZARD)
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw-wizard PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_UNITTESTS) if (BUILD_UNITTESTS)
set_target_properties(openmw_test_suite PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw_test_suite PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_BENCHMARKS) if (BUILD_BENCHMARKS)
set_target_properties(openmw_detournavigator_navmeshtilescache_benchmark PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_NAVMESHTOOL) if (BUILD_NAVMESHTOOL)
set_target_properties(openmw-navmeshtool PROPERTIES COMPILE_FLAGS "${WARNINGS}") target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS})
endif() endif()
if (BUILD_BULLETOBJECTTOOL) if (BUILD_BULLETOBJECTTOOL)
set(WARNINGS "${WARNINGS} ${MT_BUILD}") target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD})
set_target_properties(openmw-bulletobjecttool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
endif() endif()
endif(MSVC) endif(MSVC)
@ -1099,13 +1123,13 @@ if (USE_QT)
if (BUILD_LAUNCHER OR BUILD_WIZARD) if (BUILD_LAUNCHER OR BUILD_WIZARD)
if (APPLE) if (APPLE)
set(QT_TRANSLATIONS_PATH "${APP_BUNDLE_DIR}/Contents/Resources/resources/translations") set(QT_OPENMW_TRANSLATIONS_PATH "${APP_BUNDLE_DIR}/Contents/Resources/resources/translations")
else () else ()
get_generator_is_multi_config(multi_config) get_generator_is_multi_config(multi_config)
if (multi_config) if (multi_config)
set(QT_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/$<CONFIG>/resources/translations") set(QT_OPENMW_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/$<CONFIG>/resources/translations")
else () else ()
set(QT_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/resources/translations") set(QT_OPENMW_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/resources/translations")
endif () endif ()
endif () endif ()
@ -1121,9 +1145,30 @@ if (USE_QT)
qt_add_translation(QM_FILES ${TS_FILES} OPTIONS -silent) 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 add_custom_target(qm-files
COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_TRANSLATIONS_PATH} COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_OPENMW_TRANSLATIONS_PATH}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QM_FILES} ${QT_TRANSLATIONS_PATH} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QM_FILES} ${QT_OPENMW_TRANSLATIONS_PATH}
DEPENDS ${QM_FILES} DEPENDS ${QM_FILES}
COMMENT "Copy *.qm files to resources folder") COMMENT "Copy *.qm files to resources folder")
endif () endif ()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -479,7 +479,7 @@ namespace EsmTool
std::cout << " Script: " << mData.mScript << std::endl; std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl; std::cout << " Flags: " << potionFlags(mData.mData.mFlags) << std::endl;
printEffectList(mData.mEffects); printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl;
} }
@ -612,7 +612,6 @@ namespace EsmTool
} }
else else
std::cout << " Map Color: " << Misc::StringUtils::format("0x%08X", mData.mMapColor) << std::endl; std::cout << " Map Color: " << Misc::StringUtils::format("0x%08X", mData.mMapColor) << std::endl;
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl; std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl; std::cout << " Deleted: " << mIsDeleted << std::endl;
} }
@ -722,9 +721,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)
@ -1115,9 +1111,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)

View file

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

View file

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

View file

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

View file

@ -1,7 +1,6 @@
#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>
@ -9,6 +8,7 @@
#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/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
@ -41,16 +41,7 @@ int runLauncher(int argc, char* argv[])
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string()); resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
} }
// Internationalization l10n::installQtTranslations(app, "launcher", resourcesPath);
QString locale = QLocale::system().name().section('_', 0, 0);
QTranslator appTranslator;
appTranslator.load(resourcesPath + "/translations/launcher_" + locale + ".qm");
app.installTranslator(&appTranslator);
QTranslator componentsAppTranslator;
componentsAppTranslator.load(resourcesPath + "/translations/components_" + locale + ".qm");
app.installTranslator(&componentsAppTranslator);
Launcher::MainDialog mainWin(configurationManager); Launcher::MainDialog mainWin(configurationManager);

View file

@ -193,8 +193,10 @@ bool Launcher::SettingsPage::loadSettings()
loadSettingBool(Settings::game().mSmoothMovement, *smoothMovementCheckBox); loadSettingBool(Settings::game().mSmoothMovement, *smoothMovementCheckBox);
loadSettingBool(Settings::game().mPlayerMovementIgnoresAnimation, *playerMovementIgnoresAnimationCheckBox); loadSettingBool(Settings::game().mPlayerMovementIgnoresAnimation, *playerMovementIgnoresAnimationCheckBox);
distantLandCheckBox->setCheckState( connect(distantLandCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotDistantLandToggled);
Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging ? Qt::Checked : Qt::Unchecked); bool distantLandEnabled = Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging;
distantLandCheckBox->setCheckState(distantLandEnabled ? Qt::Checked : Qt::Unchecked);
slotDistantLandToggled(distantLandEnabled);
loadSettingBool(Settings::terrain().mObjectPagingActiveGrid, *activeGridObjectPagingCheckBox); loadSettingBool(Settings::terrain().mObjectPagingActiveGrid, *activeGridObjectPagingCheckBox);
viewingDistanceComboBox->setValue(convertToCells(Settings::camera().mViewingDistance)); viewingDistanceComboBox->setValue(convertToCells(Settings::camera().mViewingDistance));
@ -294,6 +296,7 @@ bool Launcher::SettingsPage::loadSettings()
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex); hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
} }
} }
loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox);
} }
// Interface Changes // Interface Changes
@ -490,6 +493,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
@ -579,9 +585,16 @@ void Launcher::SettingsPage::slotShadowDistLimitToggled(bool checked)
fadeStartSpinBox->setEnabled(checked); fadeStartSpinBox->setEnabled(checked);
} }
void Launcher::SettingsPage::slotDistantLandToggled(bool checked)
{
activeGridObjectPagingCheckBox->setEnabled(checked);
objectPagingMinSizeComboBox->setEnabled(checked);
}
void Launcher::SettingsPage::slotLightTypeCurrentIndexChanged(int index) void Launcher::SettingsPage::slotLightTypeCurrentIndexChanged(int index)
{ {
lightsMaximumDistanceSpinBox->setEnabled(index != 0); lightsMaximumDistanceSpinBox->setEnabled(index != 0);
lightFadeMultiplierSpinBox->setEnabled(index != 0);
lightsMaxLightsSpinBox->setEnabled(index != 0); lightsMaxLightsSpinBox->setEnabled(index != 0);
lightsBoundingSphereMultiplierSpinBox->setEnabled(index != 0); lightsBoundingSphereMultiplierSpinBox->setEnabled(index != 0);
lightsMinimumInteriorBrightnessSpinBox->setEnabled(index != 0); lightsMinimumInteriorBrightnessSpinBox->setEnabled(index != 0);

View file

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

View file

@ -524,7 +524,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
@ -652,42 +652,6 @@
<item> <item>
<layout class="QGridLayout" name="terrainLayout" columnstretch="0,0"> <layout class="QGridLayout" name="terrainLayout" columnstretch="0,0">
<item row="4" column="0"> <item row="4" column="0">
<widget class="QCheckBox" name="distantLandCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Distant land</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="viewingDistanceComboBox">
<property name="suffix">
<string> cells</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
</widget>
</item>
<item row="6" column="1">
<spacer name="verticalSpacer_15">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="objectPagingMinSizeLabel"> <widget class="QLabel" name="objectPagingMinSizeLabel">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls how large an object must be to be visible in the scene. The objects size is divided by its distance to the camera and the result of the division is compared with this value. The smaller this value is, the more objects you will see in the scene.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls how large an object must be to be visible in the scene. The objects size is divided by its distance to the camera and the result of the division is compared with this value. The smaller this value is, the more objects you will see in the scene.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -697,14 +661,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="4" column="1">
<widget class="QLabel" name="viewingDistanceLabel">
<property name="text">
<string>Viewing distance</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="objectPagingMinSizeComboBox"> <widget class="QDoubleSpinBox" name="objectPagingMinSizeComboBox">
<property name="decimals"> <property name="decimals">
<number>3</number> <number>3</number>
@ -720,7 +677,53 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="2" column="0">
<widget class="QLabel" name="viewingDistanceLabel">
<property name="text">
<string>Viewing distance</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="viewingDistanceComboBox">
<property name="suffix">
<string> cells</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.125000000000000</double>
</property>
</widget>
</item>
<item row="7" column="1">
<spacer name="verticalSpacer_15">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="distantLandCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Distant land</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="activeGridObjectPagingCheckBox"> <widget class="QCheckBox" name="activeGridObjectPagingCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use object paging for active cells grid.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use object paging for active cells grid.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -866,6 +869,9 @@
<property name="maximum"> <property name="maximum">
<number>81920</number> <number>81920</number>
</property> </property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value"> <property name="value">
<number>8192</number> <number>8192</number>
</property> </property>
@ -969,6 +975,9 @@
<property name="maximum"> <property name="maximum">
<double>1.000000000000000</double> <double>1.000000000000000</double>
</property> </property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value"> <property name="value">
<double>0.900000000000000</double> <double>0.900000000000000</double>
</property> </property>
@ -1024,7 +1033,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum distance at which lights will appear (measured in units).&lt;/p&gt;&lt;p&gt;Set this to 0 to use an unlimited distance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum distance at which lights will appear (measured in units).&lt;/p&gt;&lt;p&gt;Set this to 0 to use an unlimited distance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Lights maximum distance</string> <string>Maximum light distance</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -1047,7 +1056,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum number of lights per object.&lt;/p&gt;&lt;p&gt;A low number near default will cause light popping similar to what you would see with legacy lighting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum number of lights per object.&lt;/p&gt;&lt;p&gt;A low number near default will cause light popping similar to what you would see with legacy lighting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Max light sources</string> <string>Max lights</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -1057,7 +1066,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Fraction of maximum distance at which lights will start to fade.&lt;/p&gt;&lt;p&gt;Set this to a low value for slower transitions or a high value for quicker transitions.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Fraction of maximum distance at which lights will start to fade.&lt;/p&gt;&lt;p&gt;Set this to a low value for slower transitions or a high value for quicker transitions.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Lights fade multiplier</string> <string>Fade start multiplier</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -1099,7 +1108,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Multipler for bounding sphere of lights.&lt;/p&gt;&lt;p&gt;Higher numbers allows for smooth falloff but require an increase in number of max lights.&lt;/p&gt;&lt;p&gt;Does not effect the illumination or strength of lights.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Multipler for bounding sphere of lights.&lt;/p&gt;&lt;p&gt;Higher numbers allows for smooth falloff but require an increase in number of max lights.&lt;/p&gt;&lt;p&gt;Does not effect the illumination or strength of lights.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Lights bounding sphere multiplier</string> <string>Bounding sphere multiplier</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -1109,7 +1118,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Minimum ambient interior brightness.&lt;/p&gt;&lt;p&gt;Increase this if you feel interiors are too dark.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Minimum ambient interior brightness.&lt;/p&gt;&lt;p&gt;Increase this if you feel interiors are too dark.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Lights minimum interior brightness</string> <string>Minimum interior brightness</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -1320,6 +1329,16 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QCheckBox" name="cameraListenerCheckBox">
<property name="toolTip">
<string>In third-person view, use the camera as the sound listener instead of the player character.</string>
</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">

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)

View file

@ -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;

View file

@ -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);
} }

View file

@ -996,7 +996,10 @@ namespace CSMWorld
case 5: case 5:
{ {
if (isInterior && interiorWater) if (isInterior && interiorWater)
{
cell.mWater = value.toFloat(); cell.mWater = value.toFloat();
cell.setHasWaterHeightSub(true);
}
else else
return; // return without saving return; // return without saving
break; break;

View file

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

View file

@ -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
{ {

View file

@ -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;
}; };

View file

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

View file

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

View file

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

View file

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

View file

@ -64,7 +64,7 @@ add_openmw_dir (mwlua
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings
classbindings itemdata inputprocessor animationbindings classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc 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/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/potion types/ingredient types/misc types/repair types/armor types/light types/static
@ -88,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
) )
@ -161,7 +161,7 @@ target_link_libraries(openmw
components components
) )
if (MSVC) if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw PRIVATE target_precompile_headers(openmw PRIVATE
<boost/program_options/options_description.hpp> <boost/program_options/options_description.hpp>

View file

@ -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>
@ -30,6 +31,7 @@
#include <components/stereo/multiview.hpp> #include <components/stereo/multiview.hpp>
#include <components/stereo/stereomanager.hpp> #include <components/stereo/stereomanager.hpp>
#include <components/sceneutil/glextensions.hpp>
#include <components/sceneutil/workqueue.hpp> #include <components/sceneutil/workqueue.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
@ -109,10 +111,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);
} }
@ -586,6 +601,7 @@ void OMW::Engine::createWindow()
mViewer->setRealizeOperation(realizeOperations); mViewer->setRealizeOperation(realizeOperations);
osg::ref_ptr<IdentifyOpenGLOperation> identifyOp = new IdentifyOpenGLOperation(); osg::ref_ptr<IdentifyOpenGLOperation> identifyOp = new IdentifyOpenGLOperation();
realizeOperations->add(identifyOp); realizeOperations->add(identifyOp);
realizeOperations->add(new SceneUtil::GetGLExtensionsOperation());
if (Debug::shouldDebugOpenGL()) if (Debug::shouldDebugOpenGL())
realizeOperations->add(new Debug::EnableGLDebugOperation()); realizeOperations->add(new Debug::EnableGLDebugOperation());
@ -717,8 +733,7 @@ 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);
@ -767,13 +782,13 @@ void OMW::Engine::prepareEngine()
// gui needs our shaders path before everything else // gui needs our shaders path before everything else
mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders"); mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders");
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false); osg::GLExtensions& exts = SceneUtil::getGLExtensions();
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f); bool shadersSupported = exts.glslLanguageVersion >= 1.2f;
#if OSG_VERSION_LESS_THAN(3, 6, 6) #if OSG_VERSION_LESS_THAN(3, 6, 6)
// hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028 // hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028
if (exts) if (!osg::isGLExtensionSupported(exts.contextID, "NV_framebuffer_multisample_coverage"))
exts->glRenderbufferStorageMultisampleCoverageNV = nullptr; exts.glRenderbufferStorageMultisampleCoverageNV = nullptr;
#endif #endif
osg::ref_ptr<osg::Group> guiRoot = new osg::Group; osg::ref_ptr<osg::Group> guiRoot = new osg::Group;

View file

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

View file

@ -68,6 +68,8 @@ namespace MWBase
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, uint32_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;
@ -81,6 +83,12 @@ namespace MWBase
struct InputEvent struct InputEvent
{ {
struct WheelChange
{
int x;
int y;
};
enum enum
{ {
KeyPressed, KeyPressed,
@ -91,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;

View file

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

View file

@ -202,9 +202,6 @@ namespace MWBase
virtual bool getFullHelp() const = 0; virtual bool getFullHelp() const = 0;
virtual void setActiveMap(int x, int y, bool interior) = 0;
///< set the indices of the map texture that should be used
/// sets the visibility of the drowning bar /// sets the visibility of the drowning bar
virtual void setDrowningBarVisibility(bool visible) = 0; virtual void setDrowningBarVisibility(bool visible) = 0;

View file

@ -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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -103,7 +103,7 @@ namespace MWClass
// 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 {};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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));

View file

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

View file

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

View file

@ -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;
} }

View file

@ -410,10 +410,13 @@ namespace MWGui
const std::string& image = info.icon; const std::string& image = info.icon;
int imageSize = (!image.empty()) ? info.imageSize : 0; int imageSize = (!image.empty()) ? info.imageSize : 0;
std::string text = info.text; std::string text = info.text;
std::string_view extra = info.extra;
// remove the first newline (easier this way) // remove the first newline (easier this way)
if (text.size() > 0 && text[0] == '\n') if (!text.empty() && text[0] == '\n')
text.erase(0, 1); text.erase(0, 1);
if (!extra.empty() && extra[0] == '\n')
extra = extra.substr(1);
const ESM::Enchantment* enchant = nullptr; const ESM::Enchantment* enchant = nullptr;
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
@ -572,6 +575,24 @@ namespace MWGui
} }
} }
if (!extra.empty())
{
Gui::EditBox* extraWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>("SandText",
MyGUI::IntCoord(padding.left, totalSize.height + 12, 300 - padding.left, 300 - totalSize.height),
MyGUI::Align::Stretch, "ToolTipExtraText");
extraWidget->setEditStatic(true);
extraWidget->setEditMultiLine(true);
extraWidget->setEditWordWrap(info.wordWrap);
extraWidget->setCaptionWithReplacing(extra);
extraWidget->setTextAlign(MyGUI::Align::HCenter | MyGUI::Align::Top);
extraWidget->setNeedKeyFocus(false);
MyGUI::IntSize extraTextSize = extraWidget->getTextSize();
totalSize.height += extraTextSize.height + 4;
totalSize.width = std::max(totalSize.width, extraTextSize.width);
}
captionWidget->setCoord((totalSize.width - captionSize.width) / 2 + imageSize, captionWidget->setCoord((totalSize.width - captionSize.width) / 2 + imageSize,
(captionHeight - captionSize.height) / 2, captionSize.width - imageSize, captionSize.height); (captionHeight - captionSize.height) / 2, captionSize.width - imageSize, captionSize.height);

View file

@ -29,6 +29,7 @@ namespace MWGui
std::string caption; std::string caption;
std::string text; std::string text;
std::string extra;
std::string icon; std::string icon;
int imageSize; int imageSize;

View file

@ -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);

View file

@ -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)

View file

@ -844,7 +844,7 @@ namespace MWGui
if (!player.getCell()->isExterior()) if (!player.getCell()->isExterior())
{ {
setActiveMap(x, y, true); setActiveMap(*player.getCell()->getCell());
} }
// else: need to know the current grid center, call setActiveMap from changeCell // else: need to know the current grid center, call setActiveMap from changeCell
@ -982,29 +982,23 @@ namespace MWGui
mMap->addVisitedLocation(name, cellCommon->getGridX(), cellCommon->getGridY()); mMap->addVisitedLocation(name, cellCommon->getGridX(), cellCommon->getGridY());
mMap->cellExplored(cellCommon->getGridX(), cellCommon->getGridY()); mMap->cellExplored(cellCommon->getGridX(), cellCommon->getGridY());
setActiveMap(cellCommon->getGridX(), cellCommon->getGridY(), false);
} }
else else
{ {
mMap->setCellPrefix(std::string(cellCommon->getNameId()));
mHud->setCellPrefix(std::string(cellCommon->getNameId()));
osg::Vec3f worldPos; osg::Vec3f worldPos;
if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos))
worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();
else else
MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos); MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos);
mMap->setGlobalMapPlayerPosition(worldPos.x(), worldPos.y()); mMap->setGlobalMapPlayerPosition(worldPos.x(), worldPos.y());
setActiveMap(0, 0, true);
} }
setActiveMap(*cellCommon);
} }
void WindowManager::setActiveMap(int x, int y, bool interior) void WindowManager::setActiveMap(const MWWorld::Cell& cell)
{ {
mMap->setActiveCell(x, y, interior); mMap->setActiveCell(cell);
mHud->setActiveCell(x, y, interior); mHud->setActiveCell(cell);
} }
void WindowManager::setDrowningBarVisibility(bool visible) void WindowManager::setDrowningBarVisibility(bool visible)

View file

@ -50,6 +50,7 @@ namespace MyGUI
namespace MWWorld namespace MWWorld
{ {
class Cell;
class ESMStore; class ESMStore;
} }
@ -216,9 +217,6 @@ namespace MWGui
bool toggleFullHelp() override; ///< show extra info in item tooltips (owner, script) bool toggleFullHelp() override; ///< show extra info in item tooltips (owner, script)
bool getFullHelp() const override; bool getFullHelp() const override;
void setActiveMap(int x, int y, bool interior) override;
///< set the indices of the map texture that should be used
/// sets the visibility of the drowning bar /// sets the visibility of the drowning bar
void setDrowningBarVisibility(bool visible) override; void setDrowningBarVisibility(bool visible) override;
@ -589,6 +587,9 @@ namespace MWGui
void setCullMask(uint32_t mask) override; void setCullMask(uint32_t mask) override;
uint32_t getCullMask() override; uint32_t getCullMask() override;
void setActiveMap(const MWWorld::Cell& cell);
///< set the indices of the map texture that should be used
Files::ConfigurationManager& mCfgMgr; Files::ConfigurationManager& mCfgMgr;
}; };
} }

View file

@ -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,8 +169,12 @@ 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()
{ {
@ -208,14 +220,14 @@ namespace MWInput
}; };
// Only actually turn player when we're not in vanity mode // Only actually turn player when we're not in vanity mode
bool controls = MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"); bool playerLooking = MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking");
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && controls) if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && playerLooking)
{ {
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer(); MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
player.yaw(-rot[2]); player.yaw(-rot[2]);
player.pitch(-rot[0]); player.pitch(-rot[0]);
} }
else if (!controls) else if (!playerLooking)
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation(); MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
MWBase::Environment::get().getInputManager()->resetIdleTime(); MWBase::Environment::get().getInputManager()->resetIdleTime();

View file

@ -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;

View file

@ -0,0 +1,49 @@
#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 "idcollectionbindings.hpp"
#include "luamanagerimp.hpp"
#include "types/types.hpp"
namespace sol
{
template <>
struct is_automagical<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 {
return createReadOnlyRefIdTable(lua, rec.mPowers.mList);
});
return LuaUtil::makeReadOnly(birthSigns);
}
}

View 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

View file

@ -6,6 +6,7 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "classbindings.hpp" #include "classbindings.hpp"
#include "idcollectionbindings.hpp"
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
#include "stats.hpp" #include "stats.hpp"
#include "types/types.hpp" #include "types/types.hpp"
@ -16,16 +17,12 @@ namespace sol
struct is_automagical<ESM::Class> : std::false_type struct is_automagical<ESM::Class> : std::false_type
{ {
}; };
template <>
struct is_automagical<MWWorld::Store<ESM::Class>> : std::false_type
{
};
} }
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);
@ -40,34 +37,15 @@ namespace MWLua
= sol::readonly_property([](const ESM::Class& rec) -> std::string_view { return rec.mDescription; }); = sol::readonly_property([](const ESM::Class& rec) -> std::string_view { return rec.mDescription; });
classT["attributes"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table { classT["attributes"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table {
sol::table res(lua, sol::create); return createReadOnlyRefIdTable(lua, rec.mData.mAttribute, ESM::Attribute::indexToRefId);
auto attribute = rec.mData.mAttribute;
for (size_t i = 0; i < attribute.size(); ++i)
{
ESM::RefId attributeId = ESM::Attribute::indexToRefId(attribute[i]);
res[i + 1] = attributeId.serializeText();
}
return res;
}); });
classT["majorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table { classT["majorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table {
sol::table res(lua, sol::create); return createReadOnlyRefIdTable(
auto skills = rec.mData.mSkills; lua, rec.mData.mSkills, [](const auto& pair) { return ESM::Skill::indexToRefId(pair[1]); });
for (size_t i = 0; i < skills.size(); ++i)
{
ESM::RefId skillId = ESM::Skill::indexToRefId(skills[i][1]);
res[i + 1] = skillId.serializeText();
}
return res;
}); });
classT["minorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table { classT["minorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table {
sol::table res(lua, sol::create); return createReadOnlyRefIdTable(
auto skills = rec.mData.mSkills; lua, rec.mData.mSkills, [](const auto& pair) { return ESM::Skill::indexToRefId(pair[0]); });
for (size_t i = 0; i < skills.size(); ++i)
{
ESM::RefId skillId = ESM::Skill::indexToRefId(skills[i][0]);
res[i + 1] = skillId.serializeText();
}
return res;
}); });
classT["specialization"] = sol::readonly_property([](const ESM::Class& rec) -> std::string_view { classT["specialization"] = sol::readonly_property([](const ESM::Class& rec) -> std::string_view {

View file

@ -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

View file

@ -95,6 +95,24 @@ namespace MWLua
scripts->onAnimationTextKey(event.mGroupname, event.mKey); scripts->onAnimationTextKey(event.mGroupname, event.mKey);
} }
void operator()(const OnSkillUse& event) const
{
MWWorld::Ptr actor = getPtr(event.mActor);
if (actor.isEmpty())
return;
if (auto* scripts = getLocalScripts(actor))
scripts->onSkillUse(event.mSkill, event.useType, event.scale);
}
void operator()(const OnSkillLevelUp& event) const
{
MWWorld::Ptr actor = getPtr(event.mActor);
if (actor.isEmpty())
return;
if (auto* scripts = getLocalScripts(actor))
scripts->onSkillLevelUp(event.mSkill, event.mSource);
}
private: private:
MWWorld::Ptr getPtr(ESM::RefNum id) const MWWorld::Ptr getPtr(ESM::RefNum id) const
{ {

View file

@ -57,8 +57,21 @@ namespace MWLua
std::string mGroupname; std::string mGroupname;
std::string mKey; std::string mKey;
}; };
struct OnSkillUse
{
ESM::RefNum mActor;
std::string mSkill;
int useType;
float scale;
};
struct OnSkillLevelUp
{
ESM::RefNum mActor;
std::string mSkill;
std::string mSource;
};
using Event = std::variant<OnActive, OnInactive, OnConsume, OnActivate, OnUseItem, OnNewExterior, OnTeleported, using Event = std::variant<OnActive, OnInactive, OnConsume, OnActivate, OnUseItem, OnNewExterior, OnTeleported,
OnAnimationTextKey>; OnAnimationTextKey, OnSkillUse, OnSkillLevelUp>;
void clear() { mQueue.clear(); } void clear() { mQueue.clear(); }
void addToQueue(Event e) { mQueue.push_back(std::move(e)); } void addToQueue(Event e) { mQueue.push_back(std::move(e)); }

View file

@ -9,6 +9,7 @@
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "idcollectionbindings.hpp"
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
namespace namespace
@ -96,26 +97,10 @@ namespace MWLua
return res; return res;
}); });
factionT["attributes"] = sol::readonly_property([&lua](const ESM::Faction& rec) { factionT["attributes"] = sol::readonly_property([&lua](const ESM::Faction& rec) {
sol::table res(lua, sol::create); return createReadOnlyRefIdTable(lua, rec.mData.mAttribute, ESM::Attribute::indexToRefId);
for (auto attributeIndex : rec.mData.mAttribute)
{
ESM::RefId id = ESM::Attribute::indexToRefId(attributeIndex);
if (!id.empty())
res.add(id.serializeText());
}
return res;
}); });
factionT["skills"] = sol::readonly_property([&lua](const ESM::Faction& rec) { factionT["skills"] = sol::readonly_property([&lua](const ESM::Faction& rec) {
sol::table res(lua, sol::create); return createReadOnlyRefIdTable(lua, rec.mData.mSkills, ESM::Skill::indexToRefId);
for (auto skillIndex : rec.mData.mSkills)
{
ESM::RefId id = ESM::Skill::indexToRefId(skillIndex);
if (!id.empty())
res.add(id.serializeText());
}
return res;
}); });
auto rankT = lua.new_usertype<FactionRank>("ESM3_FactionRank"); auto rankT = lua.new_usertype<FactionRank>("ESM3_FactionRank");
rankT[sol::meta_function::to_string] = [](const FactionRank& rec) -> std::string { rankT[sol::meta_function::to_string] = [](const FactionRank& rec) -> std::string {

View file

@ -0,0 +1,25 @@
#ifndef MWLUA_IDCOLLECTIONBINDINGS_H
#define MWLUA_IDCOLLECTIONBINDINGS_H
#include <functional>
#include <components/esm/refid.hpp>
#include <components/lua/luastate.hpp>
namespace MWLua
{
template <class C, class P = std::identity>
sol::table createReadOnlyRefIdTable(const sol::state_view& lua, const C& container, P projection = {})
{
sol::table res(lua, sol::create);
for (const auto& element : container)
{
ESM::RefId id = projection(element);
if (!id.empty())
res.add(id.serializeText());
}
return LuaUtil::makeReadOnly(res);
}
}
#endif

View file

@ -18,7 +18,7 @@ namespace MWLua
{ {
mScriptsContainer->registerEngineHandlers({ &mKeyPressHandlers, &mKeyReleaseHandlers, mScriptsContainer->registerEngineHandlers({ &mKeyPressHandlers, &mKeyReleaseHandlers,
&mControllerButtonPressHandlers, &mControllerButtonReleaseHandlers, &mActionHandlers, &mTouchpadPressed, &mControllerButtonPressHandlers, &mControllerButtonReleaseHandlers, &mActionHandlers, &mTouchpadPressed,
&mTouchpadReleased, &mTouchpadMoved }); &mTouchpadReleased, &mTouchpadMoved, &mMouseButtonPress, &mMouseButtonRelease, &mMouseWheel });
} }
void processInputEvent(const MWBase::LuaManager::InputEvent& event) void processInputEvent(const MWBase::LuaManager::InputEvent& event)
@ -53,6 +53,16 @@ namespace MWLua
case InputEvent::TouchMoved: case InputEvent::TouchMoved:
mScriptsContainer->callEngineHandlers(mTouchpadMoved, std::get<SDLUtil::TouchEvent>(event.mValue)); mScriptsContainer->callEngineHandlers(mTouchpadMoved, std::get<SDLUtil::TouchEvent>(event.mValue));
break; break;
case InputEvent::MouseButtonPressed:
mScriptsContainer->callEngineHandlers(mMouseButtonPress, std::get<int>(event.mValue));
break;
case InputEvent::MouseButtonReleased:
mScriptsContainer->callEngineHandlers(mMouseButtonRelease, std::get<int>(event.mValue));
break;
case InputEvent::MouseWheel:
auto wheelEvent = std::get<MWBase::LuaManager::InputEvent::WheelChange>(event.mValue);
mScriptsContainer->callEngineHandlers(mMouseWheel, wheelEvent.y, wheelEvent.x);
break;
} }
} }
@ -66,6 +76,9 @@ namespace MWLua
typename Container::EngineHandlerList mTouchpadPressed{ "onTouchPress" }; typename Container::EngineHandlerList mTouchpadPressed{ "onTouchPress" };
typename Container::EngineHandlerList mTouchpadReleased{ "onTouchRelease" }; typename Container::EngineHandlerList mTouchpadReleased{ "onTouchRelease" };
typename Container::EngineHandlerList mTouchpadMoved{ "onTouchMove" }; typename Container::EngineHandlerList mTouchpadMoved{ "onTouchMove" };
typename Container::EngineHandlerList mMouseButtonPress{ "onMouseButtonPress" };
typename Container::EngineHandlerList mMouseButtonRelease{ "onMouseButtonRelease" };
typename Container::EngineHandlerList mMouseWheel{ "onMouseWheel" };
}; };
} }

View file

@ -176,7 +176,8 @@ namespace MWLua
{ {
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData)); this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers, registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
&mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers }); &mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse,
&mOnSkillLevelUp });
} }
void LocalScripts::setActive(bool active) void LocalScripts::setActive(bool active)

View file

@ -79,6 +79,14 @@ namespace MWLua
{ {
callEngineHandlers(mOnPlayAnimationHandlers, groupname, options); callEngineHandlers(mOnPlayAnimationHandlers, groupname, options);
} }
void onSkillUse(std::string_view skillId, int useType, float scale)
{
callEngineHandlers(mOnSkillUse, skillId, useType, scale);
}
void onSkillLevelUp(std::string_view skillId, std::string_view source)
{
callEngineHandlers(mOnSkillLevelUp, skillId, source);
}
void applyStatsCache(); void applyStatsCache();
@ -93,6 +101,8 @@ namespace MWLua
EngineHandlerList mOnTeleportedHandlers{ "onTeleported" }; EngineHandlerList mOnTeleportedHandlers{ "onTeleported" };
EngineHandlerList mOnAnimationTextKeyHandlers{ "_onAnimationTextKey" }; EngineHandlerList mOnAnimationTextKeyHandlers{ "_onAnimationTextKey" };
EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" }; EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" };
EngineHandlerList mOnSkillUse{ "_onSkillUse" };
EngineHandlerList mOnSkillLevelUp{ "_onSkillLevelUp" };
}; };
} }

View file

@ -112,13 +112,12 @@ namespace MWLua
mPlayerPackages.insert(mLocalPackages.begin(), mLocalPackages.end()); mPlayerPackages.insert(mLocalPackages.begin(), mLocalPackages.end());
LuaUtil::LuaStorage::initLuaBindings(mLua.sol()); LuaUtil::LuaStorage::initLuaBindings(mLua.sol());
mGlobalScripts.addPackage( mGlobalScripts.addPackage("openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua, &mGlobalStorage));
"openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua.sol(), &mGlobalStorage));
mMenuScripts.addPackage( mMenuScripts.addPackage(
"openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage)); "openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua, &mGlobalStorage, &mPlayerStorage));
mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua.sol(), &mGlobalStorage); mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua, &mGlobalStorage);
mPlayerPackages["openmw.storage"] mPlayerPackages["openmw.storage"]
= LuaUtil::LuaStorage::initPlayerPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage); = LuaUtil::LuaStorage::initPlayerPackage(mLua, &mGlobalStorage, &mPlayerStorage);
mPlayerStorage.setActive(true); mPlayerStorage.setActive(true);
mGlobalStorage.setActive(false); mGlobalStorage.setActive(false);
@ -456,6 +455,17 @@ namespace MWLua
scripts->onPlayAnimation(groupname, options); scripts->onPlayAnimation(groupname, options);
} }
void LuaManager::skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale)
{
mEngineEvents.addToQueue(EngineEvents::OnSkillUse{ getId(actor), skillId.serializeText(), useType, scale });
}
void LuaManager::skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source)
{
mEngineEvents.addToQueue(
EngineEvents::OnSkillLevelUp{ getId(actor), skillId.serializeText(), std::string(source) });
}
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr) void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
{ {
mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet. mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.

View file

@ -88,6 +88,8 @@ namespace MWLua
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, uint32_t loops, std::string_view start, std::string_view stop, float startpoint, uint32_t loops,
bool loopfallback) override; bool loopfallback) override;
void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) override;
void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) override;
void exteriorCreated(MWWorld::CellStore& cell) override void exteriorCreated(MWWorld::CellStore& cell) override
{ {
mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell }); mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });

View file

@ -16,6 +16,31 @@
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
#include "objectlists.hpp" #include "objectlists.hpp"
namespace
{
template <class T = MWWorld::Ptr>
std::vector<T> parseIgnoreList(const sol::table& options)
{
std::vector<T> ignore;
if (const auto& ignoreObj = options.get<sol::optional<MWLua::LObject>>("ignore"))
{
ignore.push_back(ignoreObj->ptr());
}
else if (const auto& ignoreTable = options.get<sol::optional<sol::table>>("ignore"))
{
ignoreTable->for_each([&](const auto& _, const sol::object& value) {
if (value.is<MWLua::LObject>())
{
ignore.push_back(value.as<MWLua::LObject>().ptr());
}
});
}
return ignore;
}
}
namespace sol namespace sol
{ {
template <> template <>
@ -71,24 +96,27 @@ namespace MWLua
})); }));
api["castRay"] = [](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options) { api["castRay"] = [](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options) {
MWWorld::Ptr ignore; std::vector<MWWorld::ConstPtr> ignore;
int collisionType = MWPhysics::CollisionType_Default; int collisionType = MWPhysics::CollisionType_Default;
float radius = 0; float radius = 0;
if (options) if (options)
{ {
sol::optional<LObject> ignoreObj = options->get<sol::optional<LObject>>("ignore"); ignore = parseIgnoreList<MWWorld::ConstPtr>(*options);
if (ignoreObj)
ignore = ignoreObj->ptr();
collisionType = options->get<sol::optional<int>>("collisionType").value_or(collisionType); collisionType = options->get<sol::optional<int>>("collisionType").value_or(collisionType);
radius = options->get<sol::optional<float>>("radius").value_or(0); radius = options->get<sol::optional<float>>("radius").value_or(0);
} }
const MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting(); const MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();
if (radius <= 0) if (radius <= 0)
return rayCasting->castRay(from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType); {
return rayCasting->castRay(from, to, ignore, {}, collisionType);
}
else else
{ {
if (!ignore.isEmpty()) for (const auto& ptr : ignore)
{
if (!ptr.isEmpty())
throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0"); throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0");
}
return rayCasting->castSphere(from, to, radius, collisionType); return rayCasting->castSphere(from, to, radius, collisionType);
} }
}; };
@ -108,22 +136,37 @@ namespace MWLua
// and use this callback from the main thread at the beginning of the next frame processing. // and use this callback from the main thread at the beginning of the next frame processing.
rayCasting->asyncCastRay(callback, from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType); rayCasting->asyncCastRay(callback, from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType);
};*/ };*/
api["castRenderingRay"] = [manager = context.mLuaManager](const osg::Vec3f& from, const osg::Vec3f& to) { api["castRenderingRay"] = [manager = context.mLuaManager](const osg::Vec3f& from, const osg::Vec3f& to,
const sol::optional<sol::table>& options) {
if (!manager->isProcessingInputEvents()) if (!manager->isProcessingInputEvents())
{ {
throw std::logic_error( throw std::logic_error(
"castRenderingRay can be used only in player scripts during processing of input events; " "castRenderingRay can be used only in player scripts during processing of input events; "
"use asyncCastRenderingRay instead."); "use asyncCastRenderingRay instead.");
} }
std::vector<MWWorld::Ptr> ignore;
if (options.has_value())
{
ignore = parseIgnoreList(*options);
}
MWPhysics::RayCastingResult res; MWPhysics::RayCastingResult res;
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false, ignore);
return res; return res;
}; };
api["asyncCastRenderingRay"] = [context]( api["asyncCastRenderingRay"] = [context](const sol::table& callback, const osg::Vec3f& from,
const sol::table& callback, const osg::Vec3f& from, const osg::Vec3f& to) { const osg::Vec3f& to, const sol::optional<sol::table>& options) {
context.mLuaManager->addAction([context, callback = LuaUtil::Callback::fromLua(callback), from, to] { std::vector<MWWorld::Ptr> ignore;
if (options.has_value())
{
ignore = parseIgnoreList(*options);
}
context.mLuaManager->addAction([context, ignore = std::move(ignore),
callback = LuaUtil::Callback::fromLua(callback), from, to] {
MWPhysics::RayCastingResult res; MWPhysics::RayCastingResult res;
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false, ignore);
context.mLuaManager->queueCallback(callback, sol::main_object(context.mLua->sol(), sol::in_place, res)); context.mLuaManager->queueCallback(callback, sol::main_object(context.mLua->sol(), sol::in_place, res));
}); });
}; };

View file

@ -0,0 +1,117 @@
#include <components/esm/attr.hpp>
#include <components/esm3/loadrace.hpp>
#include <components/lua/luastate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "idcollectionbindings.hpp"
#include "luamanagerimp.hpp"
#include "racebindings.hpp"
#include "types/types.hpp"
namespace
{
struct RaceAttributes
{
const ESM::Race& mRace;
const sol::state_view mLua;
sol::table getAttribute(ESM::RefId id) const
{
sol::table res(mLua, sol::create);
res["male"] = mRace.mData.getAttribute(id, true);
res["female"] = mRace.mData.getAttribute(id, false);
return LuaUtil::makeReadOnly(res);
}
};
}
namespace sol
{
template <>
struct is_automagical<ESM::Race> : std::false_type
{
};
template <>
struct is_automagical<RaceAttributes> : std::false_type
{
};
}
namespace MWLua
{
sol::table initRaceRecordBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::table races(context.mLua->sol(), sol::create);
addRecordFunctionBinding<ESM::Race>(races, context);
auto raceT = lua.new_usertype<ESM::Race>("ESM3_Race");
raceT[sol::meta_function::to_string]
= [](const ESM::Race& rec) -> std::string { return "ESM3_Race[" + rec.mId.toDebugString() + "]"; };
raceT["id"] = sol::readonly_property([](const ESM::Race& rec) { return rec.mId.serializeText(); });
raceT["name"] = sol::readonly_property([](const ESM::Race& rec) -> std::string_view { return rec.mName; });
raceT["description"]
= sol::readonly_property([](const ESM::Race& rec) -> std::string_view { return rec.mDescription; });
raceT["spells"] = sol::readonly_property(
[lua](const ESM::Race& rec) -> sol::table { return createReadOnlyRefIdTable(lua, rec.mPowers.mList); });
raceT["skills"] = sol::readonly_property([lua](const ESM::Race& rec) -> sol::table {
sol::table res(lua, sol::create);
for (const auto& skillBonus : rec.mData.mBonus)
{
ESM::RefId skill = ESM::Skill::indexToRefId(skillBonus.mSkill);
if (!skill.empty())
res[skill.serializeText()] = skillBonus.mBonus;
}
return res;
});
raceT["isPlayable"] = sol::readonly_property(
[](const ESM::Race& rec) -> bool { return rec.mData.mFlags & ESM::Race::Playable; });
raceT["isBeast"]
= sol::readonly_property([](const ESM::Race& rec) -> bool { return rec.mData.mFlags & ESM::Race::Beast; });
raceT["height"] = sol::readonly_property([lua](const ESM::Race& rec) -> sol::table {
sol::table res(lua, sol::create);
res["male"] = rec.mData.mMaleHeight;
res["female"] = rec.mData.mFemaleHeight;
return LuaUtil::makeReadOnly(res);
});
raceT["weight"] = sol::readonly_property([lua](const ESM::Race& rec) -> sol::table {
sol::table res(lua, sol::create);
res["male"] = rec.mData.mMaleWeight;
res["female"] = rec.mData.mFemaleWeight;
return LuaUtil::makeReadOnly(res);
});
raceT["attributes"] = sol::readonly_property([lua](const ESM::Race& rec) -> RaceAttributes {
return { rec, lua };
});
auto attributesT = lua.new_usertype<RaceAttributes>("ESM3_RaceAttributes");
const auto& store = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>();
attributesT[sol::meta_function::index]
= [&](const RaceAttributes& attributes, std::string_view stringId) -> sol::optional<sol::table> {
ESM::RefId id = ESM::RefId::deserializeText(stringId);
if (!store.search(id))
return sol::nullopt;
return attributes.getAttribute(id);
};
attributesT[sol::meta_function::pairs] = [&](sol::this_state ts, RaceAttributes& attributes) {
auto iterator = store.begin();
return sol::as_function(
[iterator, attributes,
&store]() mutable -> std::pair<sol::optional<std::string>, sol::optional<sol::table>> {
if (iterator != store.end())
{
ESM::RefId id = iterator->mId;
++iterator;
return { id.serializeText(), attributes.getAttribute(id) };
}
return { sol::nullopt, sol::nullopt };
});
};
return LuaUtil::makeReadOnly(races);
}
}

View file

@ -0,0 +1,13 @@
#ifndef MWLUA_RACEBINDINGS_H
#define MWLUA_RACEBINDINGS_H
#include <sol/forward.hpp>
#include "context.hpp"
namespace MWLua
{
sol::table initRaceRecordBindings(const Context& context);
}
#endif // MWLUA_RACEBINDINGS_H

View file

@ -174,12 +174,12 @@ namespace MWLua
api["say"] = sol::overload( api["say"] = sol::overload(
[luaManager = context.mLuaManager]( [luaManager = context.mLuaManager](
std::string_view fileName, const Object& object, sol::optional<std::string_view> text) { std::string_view fileName, const Object& object, sol::optional<std::string_view> text) {
MWBase::Environment::get().getSoundManager()->say(object.ptr(), std::string(fileName)); MWBase::Environment::get().getSoundManager()->say(object.ptr(), VFS::Path::Normalized(fileName));
if (text) if (text)
luaManager->addUIMessage(*text); luaManager->addUIMessage(*text);
}, },
[luaManager = context.mLuaManager](std::string_view fileName, sol::optional<std::string_view> text) { [luaManager = context.mLuaManager](std::string_view fileName, sol::optional<std::string_view> text) {
MWBase::Environment::get().getSoundManager()->say(std::string(fileName)); MWBase::Environment::get().getSoundManager()->say(VFS::Path::Normalized(fileName));
if (text) if (text)
luaManager->addUIMessage(*text); luaManager->addUIMessage(*text);
}); });
@ -227,7 +227,7 @@ namespace MWLua
soundT["maxRange"] soundT["maxRange"]
= sol::readonly_property([](const ESM::Sound& rec) -> unsigned char { return rec.mData.mMaxRange; }); = sol::readonly_property([](const ESM::Sound& rec) -> unsigned char { return rec.mData.mMaxRange; });
soundT["fileName"] = sol::readonly_property([](const ESM::Sound& rec) -> std::string { soundT["fileName"] = sol::readonly_property([](const ESM::Sound& rec) -> std::string {
return VFS::Path::normalizeFilename(Misc::ResourceHelpers::correctSoundPath(rec.mSound)); return Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(rec.mSound)).value();
}); });
return LuaUtil::makeReadOnly(api); return LuaUtil::makeReadOnly(api);

View file

@ -73,6 +73,96 @@ namespace MWLua
"StatUpdateAction"); "StatUpdateAction");
} }
static void setCreatureValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
auto& stats = ptr.getClass().getCreatureStats(ptr);
if (prop == "current")
stats.setLevel(LuaUtil::cast<int>(value));
}
static void setNpcValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
auto& stats = ptr.getClass().getNpcStats(ptr);
if (prop == "progress")
stats.setLevelProgress(LuaUtil::cast<int>(value));
else if (prop == "skillIncreasesForAttribute")
stats.setSkillIncreasesForAttribute(
*std::get<ESM::RefId>(index).getIf<ESM::StringRefId>(), LuaUtil::cast<int>(value));
else if (prop == "skillIncreasesForSpecialization")
stats.setSkillIncreasesForSpecialization(
static_cast<ESM::Class::Specialization>(std::get<int>(index)), LuaUtil::cast<int>(value));
}
class SkillIncreasesForAttributeStats
{
ObjectVariant mObject;
public:
SkillIncreasesForAttributeStats(ObjectVariant object)
: mObject(std::move(object))
{
}
sol::object get(const Context& context, ESM::StringRefId attributeId) const
{
const auto& ptr = mObject.ptr();
if (!ptr.getClass().isNpc())
return sol::nil;
return getValue(context, mObject, &setNpcValue, attributeId, "skillIncreasesForAttribute",
[attributeId](const MWWorld::Ptr& ptr) {
return ptr.getClass().getNpcStats(ptr).getSkillIncreasesForAttribute(attributeId);
});
}
void set(const Context& context, ESM::StringRefId attributeId, const sol::object& value) const
{
const auto& ptr = mObject.ptr();
if (!ptr.getClass().isNpc())
return;
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }] = value;
}
};
class SkillIncreasesForSpecializationStats
{
ObjectVariant mObject;
public:
SkillIncreasesForSpecializationStats(ObjectVariant object)
: mObject(std::move(object))
{
}
sol::object get(const Context& context, int specialization) const
{
const auto& ptr = mObject.ptr();
if (!ptr.getClass().isNpc())
return sol::nil;
return getValue(context, mObject, &setNpcValue, specialization, "skillIncreasesForSpecialization",
[specialization](const MWWorld::Ptr& ptr) {
return ptr.getClass().getNpcStats(ptr).getSkillIncreasesForSpecialization(
static_cast<ESM::Class::Specialization>(specialization));
});
}
void set(const Context& context, int specialization, const sol::object& value) const
{
const auto& ptr = mObject.ptr();
if (!ptr.getClass().isNpc())
return;
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, specialization, "skillIncreasesForSpecialization" }]
= value;
}
};
class LevelStat class LevelStat
{ {
ObjectVariant mObject; ObjectVariant mObject;
@ -85,7 +175,7 @@ namespace MWLua
public: public:
sol::object getCurrent(const Context& context) const sol::object getCurrent(const Context& context) const
{ {
return getValue(context, mObject, &LevelStat::setValue, std::monostate{}, "current", return getValue(context, mObject, &setCreatureValue, std::monostate{}, "current",
[](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); }); [](const MWWorld::Ptr& ptr) { return ptr.getClass().getCreatureStats(ptr).getLevel(); });
} }
@ -93,7 +183,7 @@ namespace MWLua
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, std::monostate{}, "current" }] = value; obj->mStatsCache[SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }] = value;
} }
sol::object getProgress(const Context& context) const sol::object getProgress(const Context& context) const
@ -101,7 +191,30 @@ namespace MWLua
const auto& ptr = mObject.ptr(); const auto& ptr = mObject.ptr();
if (!ptr.getClass().isNpc()) if (!ptr.getClass().isNpc())
return sol::nil; return sol::nil;
return sol::make_object(context.mLua->sol(), ptr.getClass().getNpcStats(ptr).getLevelProgress());
return getValue(context, mObject, &setNpcValue, std::monostate{}, "progress",
[](const MWWorld::Ptr& ptr) { return ptr.getClass().getNpcStats(ptr).getLevelProgress(); });
}
void setProgress(const Context& context, const sol::object& value) const
{
const auto& ptr = mObject.ptr();
if (!ptr.getClass().isNpc())
return;
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }] = value;
}
SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const
{
return SkillIncreasesForAttributeStats{ mObject };
}
SkillIncreasesForSpecializationStats getSkillIncreasesForSpecializationStats() const
{
return SkillIncreasesForSpecializationStats{ mObject };
} }
static std::optional<LevelStat> create(ObjectVariant object, Index) static std::optional<LevelStat> create(ObjectVariant object, Index)
@ -110,13 +223,6 @@ namespace MWLua
return {}; return {};
return LevelStat{ std::move(object) }; return LevelStat{ std::move(object) };
} }
static void setValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
auto& stats = ptr.getClass().getCreatureStats(ptr);
if (prop == "current")
stats.setLevel(LuaUtil::cast<int>(value));
}
}; };
class DynamicStat class DynamicStat
@ -323,6 +429,14 @@ namespace MWLua
namespace sol namespace sol
{ {
template <>
struct is_automagical<MWLua::SkillIncreasesForAttributeStats> : std::false_type
{
};
template <>
struct is_automagical<MWLua::SkillIncreasesForSpecializationStats> : std::false_type
{
};
template <> template <>
struct is_automagical<MWLua::LevelStat> : std::false_type struct is_automagical<MWLua::LevelStat> : std::false_type
{ {
@ -360,10 +474,39 @@ namespace MWLua
sol::table stats(context.mLua->sol(), sol::create); sol::table stats(context.mLua->sol(), sol::create);
actor["stats"] = LuaUtil::makeReadOnly(stats); actor["stats"] = LuaUtil::makeReadOnly(stats);
auto skillIncreasesForAttributeStatsT
= context.mLua->sol().new_usertype<SkillIncreasesForAttributeStats>("SkillIncreasesForAttributeStats");
for (const auto& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>())
{
skillIncreasesForAttributeStatsT[ESM::RefId(attribute.mId).serializeText()] = sol::property(
[=](const SkillIncreasesForAttributeStats& stat) { return stat.get(context, attribute.mId); },
[=](const SkillIncreasesForAttributeStats& stat, const sol::object& value) {
stat.set(context, attribute.mId, value);
});
}
// ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization)
auto skillIncreasesForSpecializationStatsT
= context.mLua->sol().new_usertype<SkillIncreasesForSpecializationStats>(
"skillIncreasesForSpecializationStats");
for (int i = 0; i < 3; i++)
{
std::string_view index = ESM::Class::specializationIndexToLuaId.at(i);
skillIncreasesForSpecializationStatsT[index]
= sol::property([=](const SkillIncreasesForSpecializationStats& stat) { return stat.get(context, i); },
[=](const SkillIncreasesForSpecializationStats& stat, const sol::object& value) {
stat.set(context, i, value);
});
}
auto levelStatT = context.mLua->sol().new_usertype<LevelStat>("LevelStat"); auto levelStatT = context.mLua->sol().new_usertype<LevelStat>("LevelStat");
levelStatT["current"] = sol::property([context](const LevelStat& stat) { return stat.getCurrent(context); }, levelStatT["current"] = sol::property([context](const LevelStat& stat) { return stat.getCurrent(context); },
[context](const LevelStat& stat, const sol::object& value) { stat.setCurrent(context, value); }); [context](const LevelStat& stat, const sol::object& value) { stat.setCurrent(context, value); });
levelStatT["progress"] = sol::property([context](const LevelStat& stat) { return stat.getProgress(context); }); levelStatT["progress"] = sol::property([context](const LevelStat& stat) { return stat.getProgress(context); },
[context](const LevelStat& stat, const sol::object& value) { stat.setProgress(context, value); });
levelStatT["skillIncreasesForAttribute"]
= sol::readonly_property([](const LevelStat& stat) { return stat.getSkillIncreasesForAttributeStats(); });
levelStatT["skillIncreasesForSpecialization"] = sol::readonly_property(
[](const LevelStat& stat) { return stat.getSkillIncreasesForSpecializationStats(); });
stats["level"] = addIndexedAccessor<LevelStat>(0); stats["level"] = addIndexedAccessor<LevelStat>(0);
auto dynamicStatT = context.mLua->sol().new_usertype<DynamicStat>("DynamicStat"); auto dynamicStatT = context.mLua->sol().new_usertype<DynamicStat>("DynamicStat");
@ -461,6 +604,13 @@ namespace MWLua
skillT["attribute"] = sol::readonly_property([](const ESM::Skill& rec) -> std::string { skillT["attribute"] = sol::readonly_property([](const ESM::Skill& rec) -> std::string {
return ESM::Attribute::indexToRefId(rec.mData.mAttribute).serializeText(); return ESM::Attribute::indexToRefId(rec.mData.mAttribute).serializeText();
}); });
skillT["skillGain"] = sol::readonly_property([lua](const ESM::Skill& rec) -> sol::table {
sol::table res(lua, sol::create);
int index = 1;
for (auto skillGain : rec.mData.mUseValue)
res[index++] = skillGain;
return res;
});
auto schoolT = context.mLua->sol().new_usertype<ESM::MagicSchool>("MagicSchool"); auto schoolT = context.mLua->sol().new_usertype<ESM::MagicSchool>("MagicSchool");
schoolT[sol::meta_function::to_string] schoolT[sol::meta_function::to_string]

View file

@ -403,6 +403,11 @@ namespace MWLua
return target.getClass().getCreatureStats(target).isDead(); return target.getClass().getCreatureStats(target).isDead();
}; };
actor["isDeathFinished"] = [](const Object& o) {
const auto& target = o.ptr();
return target.getClass().getCreatureStats(target).isDeathAnimationFinished();
};
actor["getEncumbrance"] = [](const Object& actor) -> float { actor["getEncumbrance"] = [](const Object& actor) -> float {
const MWWorld::Ptr ptr = actor.ptr(); const MWWorld::Ptr ptr = actor.ptr();
return ptr.getClass().getEncumbrance(ptr); return ptr.getClass().getEncumbrance(ptr);

View file

@ -1,5 +1,6 @@
#include <sol/sol.hpp> #include <sol/sol.hpp>
#include "../../mwmechanics/spellutil.hpp"
#include "../../mwworld/class.hpp" #include "../../mwworld/class.hpp"
#include "../itemdata.hpp" #include "../itemdata.hpp"
@ -10,13 +11,21 @@ namespace MWLua
{ {
void addItemBindings(sol::table item, const Context& context) void addItemBindings(sol::table item, const Context& context)
{ {
item["getEnchantmentCharge"] item["getEnchantmentCharge"] = [](const Object& object) -> sol::optional<float> {
= [](const Object& object) { return object.ptr().getCellRef().getEnchantmentCharge(); }; float charge = object.ptr().getCellRef().getEnchantmentCharge();
item["setEnchantmentCharge"] if (charge == -1)
= [](const GObject& object, float charge) { object.ptr().getCellRef().setEnchantmentCharge(charge); }; return sol::nullopt;
else
return charge;
};
item["setEnchantmentCharge"] = [](const GObject& object, sol::optional<float> charge) {
object.ptr().getCellRef().setEnchantmentCharge(charge.value_or(-1));
};
item["isRestocking"] item["isRestocking"]
= [](const Object& object) -> bool { return object.ptr().getCellRef().getCount(false) < 0; }; = [](const Object& object) -> bool { return object.ptr().getCellRef().getCount(false) < 0; };
item["isCarriable"] = [](const Object& object) -> bool { return object.ptr().getClass().isItem(object.ptr()); };
addItemDataBindings(item, context); addItemDataBindings(item, context);
} }
} }

View file

@ -15,6 +15,7 @@
#include "../classbindings.hpp" #include "../classbindings.hpp"
#include "../localscripts.hpp" #include "../localscripts.hpp"
#include "../racebindings.hpp"
#include "../stats.hpp" #include "../stats.hpp"
namespace sol namespace sol
@ -85,7 +86,8 @@ namespace MWLua
record["baseGold"] = sol::readonly_property([](const ESM::NPC& rec) -> int { return rec.mNpdt.mGold; }); record["baseGold"] = sol::readonly_property([](const ESM::NPC& rec) -> int { return rec.mNpdt.mGold; });
addActorServicesBindings<ESM::NPC>(record, context); addActorServicesBindings<ESM::NPC>(record, context);
npc["classes"] = initCoreClassBindings(context); npc["classes"] = initClassRecordBindings(context);
npc["races"] = initRaceRecordBindings(context);
// This function is game-specific, in future we should replace it with something more universal. // This function is game-specific, in future we should replace it with something more universal.
npc["isWerewolf"] = [](const Object& o) { npc["isWerewolf"] = [](const Object& o) {

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