mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-21 06:09:42 +00:00
Merge branch 'master' of gitlab.com:openmw/openmw into fix_global_iteration
This commit is contained in:
commit
8b75932f50
300 changed files with 4597 additions and 2004 deletions
|
@ -210,6 +210,7 @@ Ubuntu_GCC_Debug:
|
|||
CCACHE_SIZE: 3G
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
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.
|
||||
timeout: 2h
|
||||
|
||||
|
@ -520,13 +521,13 @@ Ubuntu_GCC_integration_tests_asan:
|
|||
- build/OpenMW-*.dmg
|
||||
- "build/**/*.log"
|
||||
|
||||
macOS13_Xcode14_arm64:
|
||||
macOS14_Xcode15_arm64:
|
||||
extends: .MacOS
|
||||
image: macos-12-xcode-14
|
||||
image: macos-14-xcode-15
|
||||
tags:
|
||||
- saas-macos-medium-m1
|
||||
cache:
|
||||
key: macOS12_Xcode14_arm64.v4
|
||||
key: macOS14_Xcode15_arm64.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 3G
|
||||
|
||||
|
@ -575,7 +576,7 @@ macOS13_Xcode14_arm64:
|
|||
- cd MSVC2019_64_Ninja
|
||||
- .\ActivateMSVC.ps1
|
||||
- cmake --build . --config $config
|
||||
- ccache --show-stats
|
||||
- ccache --show-stats -v
|
||||
- cd $config
|
||||
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
|
||||
- $artifactDirectory = "$(Make-SafeFileName("${CI_PROJECT_NAMESPACE}"))/$(Make-SafeFileName("${CI_COMMIT_REF_NAME}"))/$(Make-SafeFileName("${CI_COMMIT_SHORT_SHA}-${CI_JOB_ID}"))/"
|
||||
|
@ -665,7 +666,6 @@ macOS13_Xcode14_arm64:
|
|||
- choco source disable -n=chocolatey
|
||||
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
|
||||
- choco install 7zip -y
|
||||
- choco install ccache -y
|
||||
- choco install vswhere -y
|
||||
- choco install python -y
|
||||
- choco install awscli -y
|
||||
|
@ -688,15 +688,11 @@ macOS13_Xcode14_arm64:
|
|||
- $time = (Get-Date -Format "HH:mm:ss")
|
||||
- echo ${time}
|
||||
- echo "started by ${GITLAB_USER_NAME}"
|
||||
- $env:CCACHE_BASEDIR = Get-Location
|
||||
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
||||
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
|
||||
- New-Item -Type File -Force -Path MSVC2019_64\.cmake\api\v1\query\codemodel-v2
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -b -t -C $multiview -E
|
||||
- cd MSVC2019_64
|
||||
- Get-Volume
|
||||
- cmake --build . --config $config
|
||||
- ccache --show-stats
|
||||
- cd $config
|
||||
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
|
||||
- $artifactDirectory = "$(Make-SafeFileName("${CI_PROJECT_NAMESPACE}"))/$(Make-SafeFileName("${CI_COMMIT_REF_NAME}"))/$(Make-SafeFileName("${CI_COMMIT_SHORT_SHA}-${CI_JOB_ID}"))/"
|
||||
|
@ -728,7 +724,6 @@ macOS13_Xcode14_arm64:
|
|||
cache:
|
||||
key: msbuild-v8
|
||||
paths:
|
||||
- ccache
|
||||
- deps
|
||||
- MSVC2019_64/deps/Qt
|
||||
artifacts:
|
||||
|
|
|
@ -15,6 +15,7 @@ Programmers
|
|||
Nicolay Korslund - Project leader 2008-2010
|
||||
scrawl - Top contributor
|
||||
|
||||
AbduSharif
|
||||
Adam Hogan (aurix)
|
||||
Aesylwinn
|
||||
aegis
|
||||
|
@ -79,6 +80,7 @@ Programmers
|
|||
Eduard Cot (trombonecot)
|
||||
Eli2
|
||||
Emanuel Guével (potatoesmaster)
|
||||
Epoch
|
||||
Eris Caffee (eris)
|
||||
eroen
|
||||
escondida
|
||||
|
@ -187,6 +189,7 @@ Programmers
|
|||
pkubik
|
||||
PLkolek
|
||||
PlutonicOverkill
|
||||
Qlonever
|
||||
Radu-Marius Popovici (rpopovici)
|
||||
Rafael Moura (dhustkoder)
|
||||
Randy Davin (Kindi)
|
||||
|
|
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -26,12 +26,15 @@
|
|||
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
|
||||
Bug #5413: Enemies do a battlecry everytime the player summons a creature
|
||||
Bug #5714: Touch spells cast using ExplodeSpell don't always explode
|
||||
Bug #5755: Reset friendly hit counter
|
||||
Bug #5849: Paralysis breaks landing
|
||||
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 #5977: Fatigueless NPCs' corpse underwater changes animation on game load
|
||||
Bug #6025: Subrecords cannot overlap records
|
||||
Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex
|
||||
Bug #6146: Lua command `actor:setEquipment` doesn't trigger mwscripts when equipping or unequipping a scripted item
|
||||
Bug #6156: 1ft Charm or Sound magic effect vfx doesn't work properly
|
||||
Bug #6190: Unintuitive sun specularity time of day dependence
|
||||
Bug #6222: global map cell size can crash openmw if set to too high a value
|
||||
Bug #6313: Followers with high Fight can turn hostile
|
||||
|
@ -55,6 +58,7 @@
|
|||
Bug #6973: Fade in happens after the scene load and is shown
|
||||
Bug #6974: Only harmful effects are reflected
|
||||
Bug #6977: Sun damage implementation does not match research
|
||||
Bug #6985: Issues with Magic Cards numbers readability
|
||||
Bug #6986: Sound magic effect does not make noise
|
||||
Bug #6987: Set/Mod Blindness should not darken the screen
|
||||
Bug #6992: Crossbow reloading doesn't look the same as in Morrowind
|
||||
|
@ -70,12 +74,15 @@
|
|||
Bug #7084: Resurrecting an actor doesn't take into account base record changes
|
||||
Bug #7088: Deleting last save game of last character doesn't clear character name/details
|
||||
Bug #7092: BSA archives from higher priority directories don't take priority
|
||||
Bug #7102: Some HQ Creatures mod models can hit the 8 texture slots limit with 0.48
|
||||
Bug #7103: Multiple paths pointing to the same plugin but with different cases lead to automatically removed config entries
|
||||
Bug #7122: Teleportation to underwater should cancel active water walking effect
|
||||
Bug #7131: MyGUI log spam when post processing HUD is open
|
||||
Bug #7134: Saves with an invalid last generated RefNum can be loaded
|
||||
Bug #7163: Myar Aranath: Wheat breaks the GUI
|
||||
Bug #7168: Fix average scene luminance
|
||||
Bug #7172: Current music playlist continues playing indefinitely if next playlist is empty
|
||||
Bug #7202: Post-processing normals for terrain, water randomly stop rendering
|
||||
Bug #7204: Missing actor scripts freeze the game
|
||||
Bug #7229: Error marker loading failure is not handled
|
||||
Bug #7243: Supporting loading external files from VFS from esm files
|
||||
|
@ -98,6 +105,7 @@
|
|||
Bug #7475: Equipping a constant effect item doesn't update the magic menu
|
||||
Bug #7502: Data directories dialog (0.48.0) forces adding subdirectory instead of intended directory
|
||||
Bug #7505: Distant terrain does not support sample size greater than cell size
|
||||
Bug #7535: Bookart paths for textures in OpenMW vs vanilla Morrowind
|
||||
Bug #7553: Faction reaction loading is incorrect
|
||||
Bug #7557: Terrain::ChunkManager::createChunk is called twice for the same position, lod on initial loading
|
||||
Bug #7573: Drain Fatigue can't bring fatigue below zero by default
|
||||
|
@ -108,8 +116,10 @@
|
|||
Bug #7611: Beast races' idle animations slide after turning or jumping in place
|
||||
Bug #7617: The death prompt asks the player if they wanted to load the character's last created save
|
||||
Bug #7619: Long map notes may get cut off
|
||||
Bug #7623: Incorrect placement of the script info in the engraved ring of healing tooltip
|
||||
Bug #7630: Charm can be cast on creatures
|
||||
Bug #7631: Cannot trade with/talk to Creeper or Mudcrab Merchant when they're fleeing
|
||||
Bug #7633: Groundcover should ignore non-geometry Drawables
|
||||
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 #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 #7685: Corky sometimes doesn't follow Llovyn Andus
|
||||
Bug #7712: Casting doesn't support spells and enchantments with no effects
|
||||
Bug #7721: CS: Special Chars Not Allowed in IDs
|
||||
Bug #7723: Assaulting vampires and werewolves shouldn't be a crime
|
||||
Bug #7724: Guards don't help vs werewolves
|
||||
Bug #7733: Launcher shows incorrect data paths when there's two plugins with the same name
|
||||
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 #7761: Rain and ambient loop sounds are mutually exclusive
|
||||
Bug #7763: Bullet shape loading problems, assorted
|
||||
Bug #7765: OpenMW-CS: Touch Record option is broken
|
||||
Bug #7769: Sword of the Perithia: Broken NPCs
|
||||
Bug #7770: Sword of the Perithia: Script execution failure
|
||||
Bug #7780: Non-ASCII texture paths in NIF files don't work
|
||||
Bug #7785: OpenMW-CS initialising Skill and Attribute fields to 0 instead of -1 on non-FortifyStat spells
|
||||
Bug #7794: Fleeing NPCs name tooltip doesn't appear
|
||||
Bug #7796: Absorbed enchantments don't restore magicka
|
||||
Bug #7832: Ingredient tooltips show magnitude for Fortify Maximum Magicka effect
|
||||
Bug #7840: First run of the launcher doesn't save viewing distance as the default value
|
||||
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 #3537: Shader-based water ripples
|
||||
Feature #5173: Support for NiFogProperty
|
||||
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 #6188: Specular lighting from point light sources
|
||||
Feature #6411: Support translations in openmw-launcher
|
||||
Feature #6447: Add LOD support to Object Paging
|
||||
Feature #6491: Add support for Qt6
|
||||
Feature #6556: Lua API for sounds
|
||||
Feature #6679: Design a custom Input Action API
|
||||
Feature #6726: Lua API for creating new objects
|
||||
Feature #6727: Lua API for records of all object types
|
||||
Feature #6864: Lua file access API
|
||||
Feature #6922: Improve launcher appearance
|
||||
Feature #6933: Support high-resolution cursor textures
|
||||
|
@ -163,10 +184,12 @@
|
|||
Feature #7125: Remembering console commands between sessions
|
||||
Feature #7129: Add support for non-adaptive VSync
|
||||
Feature #7130: Ability to set MyGUI logging verbosity
|
||||
Feature #7142: MWScript Lua API
|
||||
Feature #7148: Optimize string literal lookup in mwscript
|
||||
Feature #7161: OpenMW-CS: Make adding and filtering TopicInfos easier
|
||||
Feature #7194: Ori to show texture paths
|
||||
Feature #7214: Searching in the in-game console
|
||||
Feature #7284: Searching in the console with regex and toggleable case-sensitivity
|
||||
Feature #7248: Searching in the console with regex and toggleable case-sensitivity
|
||||
Feature #7468: Factions API for Lua
|
||||
Feature #7477: NegativeLight Magic Effect flag
|
||||
Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field
|
||||
|
@ -186,7 +209,10 @@
|
|||
Feature #7795: Support MaxNumberRipples INI setting
|
||||
Feature #7805: Lua Menu context
|
||||
Task #5896: Do not use deprecated MyGUI properties
|
||||
Task #6085: Replace boost::filesystem with std::filesystem
|
||||
Task #6149: Dehardcode Lua API_REVISION
|
||||
Task #6624: Drop support for saves made prior to 0.45
|
||||
Task #7048: Get rid of std::bind
|
||||
Task #7113: Move from std::atoi to std::from_char
|
||||
Task #7117: Replace boost::scoped_array with std::vector
|
||||
Task #7151: Do not use std::strerror to get errno error message
|
||||
|
|
|
@ -22,7 +22,7 @@ declare -a CMAKE_CONF_OPTS=(
|
|||
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_INSTALL_PREFIX=install
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DBUILD_SHARED_LIBS="${BUILD_SHARED_LIBS:-OFF}"
|
||||
-DUSE_SYSTEM_TINYXML=ON
|
||||
-DOPENMW_USE_SYSTEM_RECASTNAVIGATION=ON
|
||||
-DOPENMW_CXX_FLAGS="-Werror -Werror=implicit-fallthrough" # flags specific to OpenMW project
|
||||
|
|
|
@ -528,8 +528,12 @@ if ! [ -z $UNITY_BUILD ]; then
|
|||
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
|
||||
fi
|
||||
|
||||
if ! [ -z $USE_CCACHE ]; then
|
||||
add_cmake_opts "-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
|
||||
if [ -n "$USE_CCACHE" ]; then
|
||||
if [ -n "$NMAKE" ] || [ -n "$NINJA" ]; then
|
||||
add_cmake_opts "-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPRECOMPILE_HEADERS_WITH_MSVC=OFF"
|
||||
else
|
||||
echo "Ignoring -C (CCache) as it is incompatible with Visual Studio CMake generators"
|
||||
fi
|
||||
fi
|
||||
|
||||
# turn on LTO by default
|
||||
|
|
|
@ -19,6 +19,14 @@ if(OPENMW_GL4ES_MANUAL_INIT)
|
|||
add_definitions(-DOPENMW_GL4ES_MANUAL_INIT)
|
||||
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
|
||||
option(BUILD_OPENMW "Build OpenMW" 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_BULLETOBJECTTOOL "Build Bullet object tool" ON)
|
||||
option(BUILD_OPENCS_TESTS "Build OpenMW Construction Set tests" OFF)
|
||||
option(PRECOMPILE_HEADERS_WITH_MSVC "Precompile most common used headers with MSVC (alternative to ccache)" ON)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up.
|
||||
|
||||
|
@ -72,7 +81,7 @@ message(STATUS "Configuring OpenMW...")
|
|||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 49)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
set(OPENMW_LUA_API_REVISION 53)
|
||||
set(OPENMW_LUA_API_REVISION 56)
|
||||
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
||||
|
||||
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_DOC_BASEURL "https://openmw.readthedocs.io/en/stable/")
|
||||
set(OPENMW_DOC_BASEURL "https://openmw.readthedocs.io/en/")
|
||||
|
||||
set(GIT_CHECKOUT FALSE)
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
||||
|
@ -182,6 +191,22 @@ if (MSVC)
|
|||
add_compile_options(/bigobj)
|
||||
|
||||
add_compile_options(/Zc:__cplusplus)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_LAUNCHER OR CMAKE_C_COMPILER_LAUNCHER)
|
||||
if (CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
message(STATUS "A compiler launcher was specified, but will be unused by the current generator (${CMAKE_GENERATOR})")
|
||||
else()
|
||||
foreach (config_lower ${CMAKE_CONFIGURATION_TYPES})
|
||||
string(TOUPPER "${config_lower}" config)
|
||||
if (CMAKE_C_COMPILER_LAUNCHER STREQUAL "ccache")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_${config} "${CMAKE_C_FLAGS_${config}}")
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_LAUNCHER STREQUAL "ccache")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_${config} "${CMAKE_CXX_FLAGS_${config}}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set up common paths
|
||||
|
@ -710,67 +735,66 @@ if (WIN32)
|
|||
)
|
||||
|
||||
foreach(d ${WARNINGS_DISABLE})
|
||||
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||
list(APPEND WARNINGS "/wd${d}")
|
||||
endforeach(d)
|
||||
|
||||
if(OPENMW_MSVC_WERROR)
|
||||
set(WARNINGS "${WARNINGS} /WX")
|
||||
list(APPEND WARNINGS "/WX")
|
||||
endif()
|
||||
|
||||
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(components PRIVATE ${WARNINGS})
|
||||
target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS})
|
||||
|
||||
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
|
||||
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||
endif()
|
||||
|
||||
if (BUILD_BSATOOL)
|
||||
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(bsatool PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_ESMTOOL)
|
||||
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(esmtool PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_ESSIMPORTER)
|
||||
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw-essimporter PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_LAUNCHER)
|
||||
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw-launcher PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_MWINIIMPORTER)
|
||||
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENCS)
|
||||
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw-cs PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENMW)
|
||||
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_WIZARD)
|
||||
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw-wizard PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_UNITTESTS)
|
||||
set_target_properties(openmw_test_suite PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw_test_suite PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_BENCHMARKS)
|
||||
set_target_properties(openmw_detournavigator_navmeshtilescache_benchmark PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_NAVMESHTOOL)
|
||||
set_target_properties(openmw-navmeshtool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS})
|
||||
endif()
|
||||
|
||||
if (BUILD_BULLETOBJECTTOOL)
|
||||
set(WARNINGS "${WARNINGS} ${MT_BUILD}")
|
||||
set_target_properties(openmw-bulletobjecttool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
|
||||
target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD})
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
|
@ -1099,13 +1123,13 @@ if (USE_QT)
|
|||
|
||||
if (BUILD_LAUNCHER OR BUILD_WIZARD)
|
||||
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 ()
|
||||
get_generator_is_multi_config(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 ()
|
||||
set(QT_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/resources/translations")
|
||||
set(QT_OPENMW_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/resources/translations")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
@ -1121,9 +1145,30 @@ if (USE_QT)
|
|||
|
||||
qt_add_translation(QM_FILES ${TS_FILES} OPTIONS -silent)
|
||||
|
||||
if (DEPLOY_QT_TRANSLATIONS)
|
||||
# Once we set a Qt 6.2.0 as a minimum required version, we may use "qtpaths --qt-query" instead.
|
||||
get_target_property(QT_QMAKE_EXECUTABLE Qt::qmake IMPORTED_LOCATION)
|
||||
execute_process(COMMAND "${QT_QMAKE_EXECUTABLE}" -query QT_INSTALL_TRANSLATIONS
|
||||
OUTPUT_VARIABLE QT_TRANSLATIONS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
foreach(QM_FILE ${QM_FILES})
|
||||
get_filename_component(QM_BASENAME ${QM_FILE} NAME)
|
||||
string(REGEX REPLACE "[^_]+_(.*)\\.qm" "\\1" LANG_NAME ${QM_BASENAME})
|
||||
if (EXISTS "${QT_TRANSLATIONS_DIR}/qtbase_${LANG_NAME}.qm")
|
||||
set(QM_FILES ${QM_FILES} "${QT_TRANSLATIONS_DIR}/qtbase_${LANG_NAME}.qm")
|
||||
elseif (EXISTS "${QT_TRANSLATIONS_DIR}/qt_${LANG_NAME}.qm")
|
||||
set(QM_FILES ${QM_FILES} "${QT_TRANSLATIONS_DIR}/qt_${LANG_NAME}.qm")
|
||||
else ()
|
||||
message(FATAL_ERROR "Qt translations for '${LANG_NAME}' locale are not found in the '${QT_TRANSLATIONS_DIR}' folder.")
|
||||
endif ()
|
||||
endforeach(QM_FILE)
|
||||
|
||||
list(REMOVE_DUPLICATES QM_FILES)
|
||||
endif ()
|
||||
|
||||
add_custom_target(qm-files
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_TRANSLATIONS_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QM_FILES} ${QT_TRANSLATIONS_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_OPENMW_TRANSLATIONS_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QM_FILES} ${QT_OPENMW_TRANSLATIONS_PATH}
|
||||
DEPENDS ${QM_FILES}
|
||||
COMMENT "Copy *.qm files to resources folder")
|
||||
endif ()
|
||||
|
|
|
@ -5,7 +5,7 @@ if (UNIX AND NOT APPLE)
|
|||
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE <algorithm>)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ namespace
|
|||
for (auto _ : state)
|
||||
{
|
||||
const auto& key = keys[n++ % keys.size()];
|
||||
const auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);
|
||||
auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ namespace
|
|||
while (state.KeepRunning())
|
||||
{
|
||||
const auto& key = keys[n++ % keys.size()];
|
||||
const auto result = cache.set(
|
||||
auto result = cache.set(
|
||||
key.mAgentBounds, key.mTilePosition, key.mRecastMesh, std::make_unique<PreparedNavMeshData>());
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ if (UNIX AND NOT APPLE)
|
|||
target_link_libraries(openmw_esm_refid_benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw_esm_refid_benchmark PRIVATE <algorithm>)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ if (UNIX AND NOT APPLE)
|
|||
target_link_libraries(openmw_settings_access_benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw_settings_access_benchmark PRIVATE <algorithm>)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(bsatool gcov)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(bsatool PRIVATE
|
||||
<filesystem>
|
||||
<fstream>
|
||||
|
|
|
@ -329,17 +329,19 @@ int main(int argc, char** argv)
|
|||
|
||||
switch (bsaVersion)
|
||||
{
|
||||
case Bsa::BSAVER_COMPRESSED:
|
||||
return call<Bsa::CompressedBSAFile>(info);
|
||||
case Bsa::BSAVER_BA2_GNRL:
|
||||
return call<Bsa::BA2GNRLFile>(info);
|
||||
case Bsa::BSAVER_BA2_DX10:
|
||||
return call<Bsa::BA2DX10File>(info);
|
||||
case Bsa::BSAVER_UNCOMPRESSED:
|
||||
case Bsa::BsaVersion::Unknown:
|
||||
break;
|
||||
case Bsa::BsaVersion::Uncompressed:
|
||||
return call<Bsa::BSAFile>(info);
|
||||
default:
|
||||
throw std::runtime_error("Unrecognised BSA archive");
|
||||
case Bsa::BsaVersion::Compressed:
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ if (WIN32)
|
|||
install(TARGETS openmw-bulletobjecttool RUNTIME DESTINATION ".")
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw-bulletobjecttool PRIVATE
|
||||
<string>
|
||||
<vector>
|
||||
|
|
|
@ -25,7 +25,7 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(esmtool gcov)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(esmtool PRIVATE
|
||||
<fstream>
|
||||
<string>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "labels.hpp"
|
||||
|
||||
#include <components/esm3/loadalch.hpp>
|
||||
#include <components/esm3/loadbody.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/esm3/loadcont.hpp>
|
||||
|
@ -987,3 +988,16 @@ std::string recordFlags(uint32_t flags)
|
|||
properties += Misc::StringUtils::format("(0x%08X)", flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string potionFlags(int flags)
|
||||
{
|
||||
std::string properties;
|
||||
if (flags == 0)
|
||||
properties += "[None] ";
|
||||
if (flags & ESM::Potion::Autocalc)
|
||||
properties += "Autocalc ";
|
||||
if (flags & (0xFFFFFFFF ^ ESM::Enchantment::Autocalc))
|
||||
properties += "Invalid ";
|
||||
properties += Misc::StringUtils::format("(0x%08X)", flags);
|
||||
return properties;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ std::string itemListFlags(int flags);
|
|||
std::string lightFlags(int flags);
|
||||
std::string magicEffectFlags(int flags);
|
||||
std::string npcFlags(int flags);
|
||||
std::string potionFlags(int flags);
|
||||
std::string raceFlags(int flags);
|
||||
std::string spellFlags(int flags);
|
||||
std::string weaponFlags(int flags);
|
||||
|
|
|
@ -479,7 +479,7 @@ namespace EsmTool
|
|||
std::cout << " Script: " << mData.mScript << std::endl;
|
||||
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||
std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl;
|
||||
std::cout << " Flags: " << potionFlags(mData.mData.mFlags) << std::endl;
|
||||
printEffectList(mData.mEffects);
|
||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||
}
|
||||
|
@ -612,7 +612,6 @@ namespace EsmTool
|
|||
}
|
||||
else
|
||||
std::cout << " Map Color: " << Misc::StringUtils::format("0x%08X", mData.mMapColor) << std::endl;
|
||||
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
|
||||
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
|
||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||
}
|
||||
|
@ -722,9 +721,6 @@ namespace EsmTool
|
|||
std::cout << " AI Fight:" << (int)mData.mAiData.mFight << 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 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;
|
||||
|
||||
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 Flee:" << (int)mData.mAiData.mFlee << 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;
|
||||
|
||||
for (const ESM::AIPackage& package : mData.mAiPackage.mList)
|
||||
|
|
|
@ -47,7 +47,7 @@ if (WIN32)
|
|||
INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".")
|
||||
endif(WIN32)
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw-essimporter PRIVATE
|
||||
<algorithm>
|
||||
<filesystem>
|
||||
|
|
|
@ -94,7 +94,7 @@ if(USE_QT)
|
|||
set_property(TARGET openmw-launcher PROPERTY AUTOMOC ON)
|
||||
endif(USE_QT)
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw-launcher PRIVATE
|
||||
<boost/program_options/options_description.hpp>
|
||||
|
||||
|
|
|
@ -819,7 +819,7 @@ void Launcher::DataFilesPage::addArchivesFromDir(const QString& path)
|
|||
for (const auto& fileinfo : dir.entryInfoList(archiveFilter))
|
||||
{
|
||||
const auto absPath = fileinfo.absoluteFilePath();
|
||||
if (Bsa::BSAFile::detectVersion(Files::pathFromQString(absPath)) == Bsa::BSAVER_UNKNOWN)
|
||||
if (Bsa::BSAFile::detectVersion(Files::pathFromQString(absPath)) == Bsa::BsaVersion::Unknown)
|
||||
continue;
|
||||
|
||||
const auto fileName = fileinfo.fileName();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <QDir>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
@ -9,6 +8,7 @@
|
|||
#include <components/debug/debugging.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/files/qtconversion.hpp>
|
||||
#include <components/l10n/qttranslations.hpp>
|
||||
#include <components/platform/platform.hpp>
|
||||
|
||||
#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());
|
||||
}
|
||||
|
||||
// Internationalization
|
||||
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);
|
||||
l10n::installQtTranslations(app, "launcher", resourcesPath);
|
||||
|
||||
Launcher::MainDialog mainWin(configurationManager);
|
||||
|
||||
|
|
|
@ -193,8 +193,10 @@ bool Launcher::SettingsPage::loadSettings()
|
|||
loadSettingBool(Settings::game().mSmoothMovement, *smoothMovementCheckBox);
|
||||
loadSettingBool(Settings::game().mPlayerMovementIgnoresAnimation, *playerMovementIgnoresAnimationCheckBox);
|
||||
|
||||
distantLandCheckBox->setCheckState(
|
||||
Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging ? Qt::Checked : Qt::Unchecked);
|
||||
connect(distantLandCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotDistantLandToggled);
|
||||
bool distantLandEnabled = Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging;
|
||||
distantLandCheckBox->setCheckState(distantLandEnabled ? Qt::Checked : Qt::Unchecked);
|
||||
slotDistantLandToggled(distantLandEnabled);
|
||||
|
||||
loadSettingBool(Settings::terrain().mObjectPagingActiveGrid, *activeGridObjectPagingCheckBox);
|
||||
viewingDistanceComboBox->setValue(convertToCells(Settings::camera().mViewingDistance));
|
||||
|
@ -294,6 +296,7 @@ bool Launcher::SettingsPage::loadSettings()
|
|||
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
|
||||
}
|
||||
}
|
||||
loadSettingBool(Settings::sound().mCameraListener, *cameraListenerCheckBox);
|
||||
}
|
||||
|
||||
// Interface Changes
|
||||
|
@ -490,6 +493,9 @@ void Launcher::SettingsPage::saveSettings()
|
|||
Settings::sound().mHrtf.set(hrtfProfileSelectorComboBox->currentText().toStdString());
|
||||
else
|
||||
Settings::sound().mHrtf.set({});
|
||||
|
||||
const bool cCameraListener = cameraListenerCheckBox->checkState() != Qt::Unchecked;
|
||||
Settings::sound().mCameraListener.set(cCameraListener);
|
||||
}
|
||||
|
||||
// Interface Changes
|
||||
|
@ -579,9 +585,16 @@ void Launcher::SettingsPage::slotShadowDistLimitToggled(bool checked)
|
|||
fadeStartSpinBox->setEnabled(checked);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::slotDistantLandToggled(bool checked)
|
||||
{
|
||||
activeGridObjectPagingCheckBox->setEnabled(checked);
|
||||
objectPagingMinSizeComboBox->setEnabled(checked);
|
||||
}
|
||||
|
||||
void Launcher::SettingsPage::slotLightTypeCurrentIndexChanged(int index)
|
||||
{
|
||||
lightsMaximumDistanceSpinBox->setEnabled(index != 0);
|
||||
lightFadeMultiplierSpinBox->setEnabled(index != 0);
|
||||
lightsMaxLightsSpinBox->setEnabled(index != 0);
|
||||
lightsBoundingSphereMultiplierSpinBox->setEnabled(index != 0);
|
||||
lightsMinimumInteriorBrightnessSpinBox->setEnabled(index != 0);
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace Launcher
|
|||
void slotPostProcessToggled(bool checked);
|
||||
void slotSkyBlendingToggled(bool checked);
|
||||
void slotShadowDistLimitToggled(bool checked);
|
||||
void slotDistantLandToggled(bool checked);
|
||||
void slotLightTypeCurrentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
|
|
|
@ -524,7 +524,7 @@
|
|||
<string><html><head/><body><p>Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use anti-alias alpha testing</string>
|
||||
<string>Use anti-aliased alpha testing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -652,42 +652,6 @@
|
|||
<item>
|
||||
<layout class="QGridLayout" name="terrainLayout" columnstretch="0,0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="distantLandCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.</p></body></html></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">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Controls how large an object must be to be visible in the scene. The object’s 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.</p></body></html></string>
|
||||
|
@ -697,14 +661,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="viewingDistanceLabel">
|
||||
<property name="text">
|
||||
<string>Viewing distance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QDoubleSpinBox" name="objectPagingMinSizeComboBox">
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
|
@ -720,7 +677,53 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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><html><head/><body><p>If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Distant land</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="activeGridObjectPagingCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use object paging for active cells grid.</p></body></html></string>
|
||||
|
@ -866,6 +869,9 @@
|
|||
<property name="maximum">
|
||||
<number>81920</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>128</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>8192</number>
|
||||
</property>
|
||||
|
@ -969,6 +975,9 @@
|
|||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.900000000000000</double>
|
||||
</property>
|
||||
|
@ -1024,7 +1033,7 @@
|
|||
<string><html><head/><body><p>Maximum distance at which lights will appear (measured in units).</p><p>Set this to 0 to use an unlimited distance.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lights maximum distance</string>
|
||||
<string>Maximum light distance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1047,7 +1056,7 @@
|
|||
<string><html><head/><body><p>Maximum number of lights per object.</p><p>A low number near default will cause light popping similar to what you would see with legacy lighting.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Max light sources</string>
|
||||
<string>Max lights</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1057,7 +1066,7 @@
|
|||
<string><html><head/><body><p>Fraction of maximum distance at which lights will start to fade.</p><p>Set this to a low value for slower transitions or a high value for quicker transitions.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lights fade multiplier</string>
|
||||
<string>Fade start multiplier</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1099,7 +1108,7 @@
|
|||
<string><html><head/><body><p>Multipler for bounding sphere of lights.</p><p>Higher numbers allows for smooth falloff but require an increase in number of max lights.</p><p>Does not effect the illumination or strength of lights.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lights bounding sphere multiplier</string>
|
||||
<string>Bounding sphere multiplier</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1109,7 +1118,7 @@
|
|||
<string><html><head/><body><p>Minimum ambient interior brightness.</p><p>Increase this if you feel interiors are too dark.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lights minimum interior brightness</string>
|
||||
<string>Minimum interior brightness</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1320,6 +1329,16 @@
|
|||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
|
|
|
@ -33,7 +33,7 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(openmw-iniimporter gcov)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw-iniimporter PRIVATE
|
||||
<string>
|
||||
<vector>
|
||||
|
|
|
@ -21,7 +21,7 @@ if (WIN32)
|
|||
install(TARGETS openmw-navmeshtool RUNTIME DESTINATION ".")
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw-navmeshtool PRIVATE
|
||||
<algorithm>
|
||||
<memory>
|
||||
|
|
|
@ -17,6 +17,6 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(niftest gcov)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(niftest PRIVATE <filesystem>)
|
||||
endif()
|
||||
|
|
|
@ -42,29 +42,10 @@ bool isBSA(const std::filesystem::path& filename)
|
|||
return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2");
|
||||
}
|
||||
|
||||
std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
|
||||
{
|
||||
switch (Bsa::BSAFile::detectVersion(path))
|
||||
{
|
||||
case Bsa::BSAVER_COMPRESSED:
|
||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_COMPRESSED>::type>(path);
|
||||
case Bsa::BSAVER_BA2_GNRL:
|
||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_BA2_GNRL>::type>(path);
|
||||
case Bsa::BSAVER_BA2_DX10:
|
||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_BA2_DX10>::type>(path);
|
||||
case Bsa::BSAVER_UNCOMPRESSED:
|
||||
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_UNCOMPRESSED>::type>(path);
|
||||
case Bsa::BSAVER_UNKNOWN:
|
||||
default:
|
||||
std::cerr << "'" << Files::pathToUnicodeString(path) << "' is not a recognized BSA archive" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
||||
{
|
||||
if (isBSA(path))
|
||||
return makeBsaArchive(path);
|
||||
return VFS::makeBsaArchive(path);
|
||||
if (std::filesystem::is_directory(path))
|
||||
return std::make_unique<VFS::FileSystemArchive>(path);
|
||||
return nullptr;
|
||||
|
@ -124,17 +105,23 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
|||
|
||||
if (!archivePath.empty() && !isBSA(archivePath))
|
||||
{
|
||||
Files::PathContainer dataDirs = { archivePath };
|
||||
const Files::Collections fileCollections = Files::Collections(dataDirs);
|
||||
const Files::Collections fileCollections({ archivePath });
|
||||
const Files::MultiDirCollection& bsaCol = fileCollections.getCollection(".bsa");
|
||||
const Files::MultiDirCollection& ba2Col = fileCollections.getCollection(".ba2");
|
||||
for (auto& file : bsaCol)
|
||||
for (const Files::MultiDirCollection& collection : { bsaCol, ba2Col })
|
||||
{
|
||||
readVFS(makeBsaArchive(file.second), file.second, quiet);
|
||||
}
|
||||
for (auto& file : ba2Col)
|
||||
{
|
||||
readVFS(makeBsaArchive(file.second), file.second, quiet);
|
||||
for (auto& file : collection)
|
||||
{
|
||||
try
|
||||
{
|
||||
readVFS(VFS::makeBsaArchive(file.second), file.second, quiet);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Failed to read archive file '" << Files::pathToUnicodeString(file.second)
|
||||
<< "': " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ else()
|
|||
set (OPENCS_OPENMW_CFG "")
|
||||
endif(APPLE)
|
||||
|
||||
add_library(openmw-cs-lib
|
||||
add_library(openmw-cs-lib STATIC
|
||||
${OPENCS_SRC}
|
||||
${OPENCS_UI_HDR}
|
||||
${OPENCS_MOC_SRC}
|
||||
|
@ -292,7 +292,7 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(openmw-cs-lib gcov)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw-cs-lib PRIVATE
|
||||
<boost/program_options/options_description.hpp>
|
||||
|
||||
|
|
|
@ -67,6 +67,11 @@ namespace CSMWorld
|
|||
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
|
||||
{
|
||||
return mDependencies.find(id) != mDependencies.end();
|
||||
|
@ -90,10 +95,11 @@ namespace CSMWorld
|
|||
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;
|
||||
mIsBeast = isBeast;
|
||||
mWeightsHeights = raceStats;
|
||||
for (auto& str : mFemaleParts)
|
||||
str = ESM::RefId();
|
||||
for (auto& str : mMaleParts)
|
||||
|
@ -163,6 +169,11 @@ namespace CSMWorld
|
|||
return it->second.first;
|
||||
}
|
||||
|
||||
const osg::Vec2f& ActorAdapter::ActorData::getRaceWeightHeight() const
|
||||
{
|
||||
return mRaceData->getGenderWeightHeight(isFemale());
|
||||
}
|
||||
|
||||
bool ActorAdapter::ActorData::hasDependency(const ESM::RefId& id) const
|
||||
{
|
||||
return mDependencies.find(id) != mDependencies.end();
|
||||
|
@ -504,7 +515,11 @@ namespace CSMWorld
|
|||
}
|
||||
|
||||
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
|
||||
for (int i = 0; i < mBodyParts.getSize(); ++i)
|
||||
|
|
|
@ -41,6 +41,12 @@ namespace CSMWorld
|
|||
/// Tracks unique strings
|
||||
using RefIdSet = std::unordered_set<ESM::RefId>;
|
||||
|
||||
struct WeightsHeights
|
||||
{
|
||||
osg::Vec2f mMaleWeightHeight;
|
||||
osg::Vec2f mFemaleWeightHeight;
|
||||
};
|
||||
|
||||
/// Contains base race data shared between actors
|
||||
class RaceData
|
||||
{
|
||||
|
@ -57,6 +63,8 @@ namespace CSMWorld
|
|||
const ESM::RefId& getFemalePart(ESM::PartReferenceType index) const;
|
||||
/// Retrieves the associated body part
|
||||
const ESM::RefId& getMalePart(ESM::PartReferenceType index) const;
|
||||
|
||||
const osg::Vec2f& getGenderWeightHeight(bool isFemale);
|
||||
/// Checks if the race has a data dependency
|
||||
bool hasDependency(const ESM::RefId& id) const;
|
||||
|
||||
|
@ -67,7 +75,8 @@ namespace CSMWorld
|
|||
/// Marks an additional dependency
|
||||
void addOtherDependency(const ESM::RefId& id);
|
||||
/// 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:
|
||||
bool handles(ESM::PartReferenceType type) const;
|
||||
|
@ -75,6 +84,7 @@ namespace CSMWorld
|
|||
bool mIsBeast;
|
||||
RacePartList mFemaleParts;
|
||||
RacePartList mMaleParts;
|
||||
WeightsHeights mWeightsHeights;
|
||||
RefIdSet mDependencies;
|
||||
};
|
||||
using RaceDataPtr = std::shared_ptr<RaceData>;
|
||||
|
@ -96,6 +106,8 @@ namespace CSMWorld
|
|||
std::string getSkeleton() const;
|
||||
/// Retrieves the associated actor part
|
||||
ESM::RefId getPart(ESM::PartReferenceType index) const;
|
||||
|
||||
const osg::Vec2f& getRaceWeightHeight() const;
|
||||
/// Checks if the actor has a data dependency
|
||||
bool hasDependency(const ESM::RefId& id) const;
|
||||
|
||||
|
|
|
@ -585,19 +585,22 @@ namespace CSMWorld
|
|||
void set(Record<ESXRecordT>& record, const QVariant& data) override
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
float bodyAttr = std::clamp(data.toFloat(), 0.5f, 2.0f);
|
||||
|
||||
if (mWeight)
|
||||
{
|
||||
if (mMale)
|
||||
record2.mData.mMaleWeight = data.toFloat();
|
||||
record2.mData.mMaleWeight = bodyAttr;
|
||||
else
|
||||
record2.mData.mFemaleWeight = data.toFloat();
|
||||
record2.mData.mFemaleWeight = bodyAttr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mMale)
|
||||
record2.mData.mMaleHeight = data.toFloat();
|
||||
record2.mData.mMaleHeight = bodyAttr;
|
||||
else
|
||||
record2.mData.mFemaleHeight = data.toFloat();
|
||||
record2.mData.mFemaleHeight = bodyAttr;
|
||||
}
|
||||
record.setModified(record2);
|
||||
}
|
||||
|
|
|
@ -996,7 +996,10 @@ namespace CSMWorld
|
|||
case 5:
|
||||
{
|
||||
if (isInterior && interiorWater)
|
||||
{
|
||||
cell.mWater = value.toFloat();
|
||||
cell.setHasWaterHeightSub(true);
|
||||
}
|
||||
else
|
||||
return; // return without saving
|
||||
break;
|
||||
|
|
|
@ -33,7 +33,7 @@ QVariant CSMWorld::PotionRefIdAdapter::getData(const RefIdColumn* column, const
|
|||
data.getRecord(RefIdData::LocalIndex(index, UniversalId::Type_Potion)));
|
||||
|
||||
if (column == mAutoCalc)
|
||||
return record.get().mData.mAutoCalc != 0;
|
||||
return record.get().mData.mFlags & ESM::Potion::Autocalc;
|
||||
|
||||
// to show nested tables in dialogue subview, see IdTree::hasChildren()
|
||||
if (column == mColumns.mEffects)
|
||||
|
@ -51,7 +51,7 @@ void CSMWorld::PotionRefIdAdapter::setData(
|
|||
ESM::Potion potion = record.get();
|
||||
|
||||
if (column == mAutoCalc)
|
||||
potion.mData.mAutoCalc = value.toInt();
|
||||
potion.mData.mFlags = value.toBool();
|
||||
else
|
||||
{
|
||||
InventoryRefIdAdapter<ESM::Potion>::setData(column, data, index, value);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <osg/Group>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Node>
|
||||
#include <osg/Vec3d>
|
||||
|
||||
#include <apps/opencs/model/world/actoradapter.hpp>
|
||||
#include <apps/opencs/model/world/idcollection.hpp>
|
||||
|
@ -29,7 +30,7 @@ namespace CSVRender
|
|||
Actor::Actor(const ESM::RefId& id, CSMWorld::Data& data)
|
||||
: mId(id)
|
||||
, mData(data)
|
||||
, mBaseNode(new osg::Group())
|
||||
, mBaseNode(new osg::PositionAttitudeTransform())
|
||||
, mSkeleton(nullptr)
|
||||
{
|
||||
mActorData = mData.getActorAdapter()->getActorData(mId);
|
||||
|
@ -60,6 +61,10 @@ namespace CSVRender
|
|||
|
||||
// Attach parts to skeleton
|
||||
loadBodyParts();
|
||||
|
||||
const osg::Vec2f& attributes = mActorData->getRaceWeightHeight();
|
||||
|
||||
mBaseNode->setScale(osg::Vec3d(attributes.x(), attributes.x(), attributes.y()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <string_view>
|
||||
|
||||
#include <osg/Group>
|
||||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <QObject>
|
||||
|
@ -59,7 +60,7 @@ namespace CSVRender
|
|||
CSMWorld::Data& mData;
|
||||
CSMWorld::ActorAdapter::ActorDataPtr mActorData;
|
||||
|
||||
osg::ref_ptr<osg::Group> mBaseNode;
|
||||
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||
SceneUtil::Skeleton* mSkeleton;
|
||||
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
|
||||
};
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/sceneutil/glextensions.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
#include "../widget/scenetoolmode.hpp"
|
||||
|
@ -76,6 +77,8 @@ namespace CSVRender
|
|||
= new osgViewer::GraphicsWindowEmbedded(0, 0, width(), height());
|
||||
mWidget->setGraphicsWindowEmbedded(window);
|
||||
|
||||
mRenderer->setRealizeOperation(new SceneUtil::GetGLExtensionsOperation());
|
||||
|
||||
int frameRateLimit = CSMPrefs::get()["Rendering"]["framerate-limit"].toInt();
|
||||
mRenderer->setRunMaxFrameRate(frameRateLimit);
|
||||
mRenderer->setUseConfigureAffinity(false);
|
||||
|
|
|
@ -2,17 +2,6 @@
|
|||
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
|
||||
bool CSVWorld::IdValidator::isValid(const QChar& c, bool first) const
|
||||
{
|
||||
if (c.isLetter() || c == '_')
|
||||
return true;
|
||||
|
||||
if (!first && (c.isDigit() || c.isSpace()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CSVWorld::IdValidator::IdValidator(bool relaxed, QObject* parent)
|
||||
: QValidator(parent)
|
||||
, mRelaxed(relaxed)
|
||||
|
@ -92,7 +81,7 @@ QValidator::State CSVWorld::IdValidator::validate(QString& input, int& pos) cons
|
|||
{
|
||||
prevScope = false;
|
||||
|
||||
if (!isValid(*iter, first))
|
||||
if (!iter->isPrint())
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,6 @@ namespace CSVWorld
|
|||
std::string mNamespace;
|
||||
mutable std::string mError;
|
||||
|
||||
private:
|
||||
bool isValid(const QChar& c, bool first) const;
|
||||
|
||||
public:
|
||||
IdValidator(bool relaxed = false, QObject* parent = nullptr);
|
||||
///< \param relaxed Relaxed rules for IDs that also functino as user visible text
|
||||
|
|
|
@ -26,7 +26,7 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(openmw-cs-tests PRIVATE gcov)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw-cs-tests PRIVATE
|
||||
<gtest/gtest.h>
|
||||
)
|
||||
|
|
|
@ -64,7 +64,7 @@ add_openmw_dir (mwlua
|
|||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
|
||||
postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings
|
||||
classbindings itemdata inputprocessor animationbindings
|
||||
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings
|
||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
||||
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
||||
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
|
||||
|
@ -88,7 +88,7 @@ add_openmw_dir (mwworld
|
|||
|
||||
add_openmw_dir (mwphysics
|
||||
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
||||
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
|
||||
contacttestresultcallback stepper movementsolver projectile
|
||||
actorconvexcallback raycasting mtphysics contacttestwrapper projectileconvexcallback
|
||||
)
|
||||
|
||||
|
@ -161,7 +161,7 @@ target_link_libraries(openmw
|
|||
components
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
|
||||
target_precompile_headers(openmw PRIVATE
|
||||
<boost/program_options/options_description.hpp>
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <components/debug/gldebug.hpp>
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/misc/strings/format.hpp>
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
@ -30,6 +31,7 @@
|
|||
#include <components/stereo/multiview.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include <components/sceneutil/glextensions.hpp>
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
@ -109,10 +111,23 @@ namespace
|
|||
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(
|
||||
std::move(message), MWGui::ShowInDialogueMode_Never);
|
||||
}
|
||||
|
@ -586,6 +601,7 @@ void OMW::Engine::createWindow()
|
|||
mViewer->setRealizeOperation(realizeOperations);
|
||||
osg::ref_ptr<IdentifyOpenGLOperation> identifyOp = new IdentifyOpenGLOperation();
|
||||
realizeOperations->add(identifyOp);
|
||||
realizeOperations->add(new SceneUtil::GetGLExtensionsOperation());
|
||||
|
||||
if (Debug::shouldDebugOpenGL())
|
||||
realizeOperations->add(new Debug::EnableGLDebugOperation());
|
||||
|
@ -717,9 +733,8 @@ void OMW::Engine::prepareEngine()
|
|||
mScreenCaptureOperation = new SceneUtil::AsyncScreenCaptureOperation(mWorkQueue,
|
||||
new SceneUtil::WriteScreenshotToFileOperation(mCfgMgr.getScreenshotPath(),
|
||||
Settings::general().mScreenshotFormat,
|
||||
Settings::general().mNotifyOnSavedScreenshot
|
||||
? std::function<void(std::string)>(ScheduleNonDialogMessageBox{})
|
||||
: std::function<void(std::string)>(IgnoreString{})));
|
||||
Settings::general().mNotifyOnSavedScreenshot ? std::function<void(std::string)>(ScreenCaptureMessageBox{})
|
||||
: std::function<void(std::string)>(IgnoreString{})));
|
||||
|
||||
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
|
||||
|
||||
|
@ -767,13 +782,13 @@ void OMW::Engine::prepareEngine()
|
|||
// gui needs our shaders path before everything else
|
||||
mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders");
|
||||
|
||||
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
|
||||
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f);
|
||||
osg::GLExtensions& exts = SceneUtil::getGLExtensions();
|
||||
bool shadersSupported = exts.glslLanguageVersion >= 1.2f;
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3, 6, 6)
|
||||
// hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028
|
||||
if (exts)
|
||||
exts->glRenderbufferStorageMultisampleCoverageNV = nullptr;
|
||||
if (!osg::isGLExtensionSupported(exts.contextID, "NV_framebuffer_multisample_coverage"))
|
||||
exts.glRenderbufferStorageMultisampleCoverageNV = nullptr;
|
||||
#endif
|
||||
|
||||
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <components/fallback/fallback.hpp>
|
||||
#include <components/fallback/validate.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/misc/osgpluginchecker.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/platform/platform.hpp>
|
||||
#include <components/version/version.hpp>
|
||||
|
@ -228,6 +229,9 @@ int runApplication(int argc, char* argv[])
|
|||
|
||||
if (parseOptions(argc, argv, *engine, cfgMgr))
|
||||
{
|
||||
if (!Misc::checkRequiredOSGPluginsArePresent())
|
||||
return 1;
|
||||
|
||||
engine->go();
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,8 @@ namespace MWBase
|
|||
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)
|
||||
= 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 actorDied(const MWWorld::Ptr& actor) = 0;
|
||||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||
|
@ -81,6 +83,12 @@ namespace MWBase
|
|||
|
||||
struct InputEvent
|
||||
{
|
||||
struct WheelChange
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
KeyPressed,
|
||||
|
@ -91,8 +99,11 @@ namespace MWBase
|
|||
TouchPressed,
|
||||
TouchReleased,
|
||||
TouchMoved,
|
||||
MouseButtonPressed,
|
||||
MouseButtonReleased,
|
||||
MouseWheel,
|
||||
} 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;
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
|
||||
#include "../mwsound/type.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
|
@ -129,11 +131,11 @@ namespace MWBase
|
|||
/// \param name of the folder that contains the playlist
|
||||
/// Title music playlist is predefined
|
||||
|
||||
virtual void say(const MWWorld::ConstPtr& reference, const std::string& filename) = 0;
|
||||
virtual void say(const MWWorld::ConstPtr& reference, VFS::Path::NormalizedView filename) = 0;
|
||||
///< Make an actor say some text.
|
||||
/// \param filename name of a sound file in the VFS
|
||||
|
||||
virtual void say(const std::string& filename) = 0;
|
||||
virtual void say(VFS::Path::NormalizedView filename) = 0;
|
||||
///< Say some text, without an actor ref
|
||||
/// \param filename name of a sound file in the VFS
|
||||
|
||||
|
|
|
@ -202,9 +202,6 @@ namespace MWBase
|
|||
|
||||
virtual bool getFullHelp() const = 0;
|
||||
|
||||
virtual void setActiveMap(int x, int y, bool interior) = 0;
|
||||
///< set the indices of the map texture that should be used
|
||||
|
||||
/// sets the visibility of the drowning bar
|
||||
virtual void setDrowningBarVisibility(bool visible) = 0;
|
||||
|
||||
|
|
|
@ -304,7 +304,7 @@ namespace MWBase
|
|||
virtual const MWPhysics::RayCastingInterface* getRayCasting() const = 0;
|
||||
|
||||
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;
|
||||
|
||||
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool internal, bool external) = 0;
|
||||
|
|
|
@ -104,13 +104,11 @@ namespace MWClass
|
|||
std::string_view name = getName(ptr);
|
||||
info.caption = MyGUI::TextIterator::toTagsString(MyGUI::UString(name)) + MWGui::ToolTips::getCountString(count);
|
||||
|
||||
std::string text;
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
info.text = std::move(text);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
|
|
@ -102,8 +102,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
info.text = std::move(text);
|
||||
|
||||
|
|
|
@ -257,8 +257,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.enchant = ref->mBase->mEnchant;
|
||||
|
|
|
@ -121,8 +121,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.enchant = ref->mBase->mEnchant;
|
||||
|
|
|
@ -164,8 +164,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.enchant = ref->mBase->mEnchant;
|
||||
|
|
|
@ -265,10 +265,10 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
if (ptr.getCellRef().getRefId() == "stolen_goods")
|
||||
text += "\nYou can not use evidence chests";
|
||||
info.extra += "\nYou cannot use evidence chests";
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -316,11 +316,11 @@ namespace MWClass
|
|||
{
|
||||
const unsigned char* attack = nullptr;
|
||||
if (type == ESM::Weapon::AT_Chop)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop.data();
|
||||
else if (type == ESM::Weapon::AT_Slash)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash.data();
|
||||
else if (type == ESM::Weapon::AT_Thrust)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust.data();
|
||||
if (attack)
|
||||
{
|
||||
damage = attack[0] + ((attack[1] - attack[0]) * attackStrength);
|
||||
|
@ -474,20 +474,17 @@ namespace MWClass
|
|||
}
|
||||
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
const MWMechanics::AiSequence& aiSequence = stats.getAiSequence();
|
||||
|
||||
const bool isInCombat = aiSequence.isInCombat();
|
||||
if (stats.isDead())
|
||||
{
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation && !isInCombat)
|
||||
// by default user can loot non-fighting actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation)
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
|
||||
// otherwise wait until death animation
|
||||
if (stats.isDeathAnimationFinished())
|
||||
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);
|
||||
|
||||
// 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);
|
||||
info.caption = MyGUI::TextIterator::toTagsString(MyGUI::UString(name));
|
||||
|
||||
std::string text;
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.text = std::move(text);
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
|
|
@ -290,8 +290,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
info.text = std::move(text);
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ namespace MWClass
|
|||
// 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.
|
||||
// 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"))
|
||||
return {};
|
||||
|
||||
|
|
|
@ -117,8 +117,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
|
|
@ -173,8 +173,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -118,8 +118,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -163,8 +163,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -634,11 +635,11 @@ namespace MWClass
|
|||
{
|
||||
const unsigned char* attack = nullptr;
|
||||
if (type == ESM::Weapon::AT_Chop)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop.data();
|
||||
else if (type == ESM::Weapon::AT_Slash)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash.data();
|
||||
else if (type == ESM::Weapon::AT_Thrust)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust.data();
|
||||
if (attack)
|
||||
{
|
||||
damage = attack[0] + ((attack[1] - attack[0]) * attackStrength);
|
||||
|
@ -662,7 +663,7 @@ namespace MWClass
|
|||
ESM::RefId weapskill = ESM::Skill::HandToHand;
|
||||
if (!weapon.isEmpty())
|
||||
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();
|
||||
|
||||
|
@ -852,7 +853,7 @@ namespace MWClass
|
|||
|
||||
ESM::RefId skill = armor.getClass().getEquipmentSkill(armor);
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
skillUsageSucceeded(ptr, skill, 0);
|
||||
skillUsageSucceeded(ptr, skill, ESM::Skill::Armor_HitByOpponent);
|
||||
|
||||
if (skill == ESM::Skill::LightArmor)
|
||||
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);
|
||||
}
|
||||
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::AiSequence& aiSequence = stats.getAiSequence();
|
||||
const bool isPursuing = aiSequence.isInPursuit() && actor == MWMechanics::getPlayer();
|
||||
const bool inCombatWithActor = aiSequence.isInCombat(actor) || isPursuing;
|
||||
|
||||
if (stats.isDead())
|
||||
{
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation && !stats.getAiSequence().isInCombat())
|
||||
// by default user can loot non-fighting actors during death animation
|
||||
if (Settings::game().mCanLootDuringDeathAnimation)
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
|
||||
// otherwise wait until death animation
|
||||
if (stats.isDeathAnimationFinished())
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
}
|
||||
else if (!stats.getAiSequence().isInCombat())
|
||||
else
|
||||
{
|
||||
if (stats.getKnockedDown() || MWBase::Environment::get().getMechanicsManager()->isSneaking(actor))
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr); // stealing
|
||||
const bool allowStealingFromKO
|
||||
= Settings::game().mAlwaysAllowStealingFromKnockedOutActors || !inCombatWithActor;
|
||||
if (stats.getKnockedDown() && allowStealingFromKO)
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
|
||||
// Can't talk to werewolves
|
||||
if (!getNpcStats(ptr).isWerewolf())
|
||||
const bool allowStealingWhileSneaking = !inCombatWithActor;
|
||||
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);
|
||||
}
|
||||
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 (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
|
||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||
if (inCombatWithActor)
|
||||
return std::make_unique<MWWorld::FailedAction>("#{sActorInCombat}");
|
||||
|
||||
return std::make_unique<MWWorld::FailedAction>();
|
||||
}
|
||||
|
@ -1086,7 +1090,8 @@ namespace MWClass
|
|||
if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())
|
||||
return true;
|
||||
|
||||
if (!customData.mNpcStats.getAiSequence().isInCombat())
|
||||
const MWMechanics::AiSequence& aiSeq = customData.mNpcStats.getAiSequence();
|
||||
if (!aiSeq.isInCombat() || aiSeq.isFleeing())
|
||||
return true;
|
||||
|
||||
if (Settings::game().mAlwaysAllowStealingFromKnockedOutActors && customData.mNpcStats.getKnockedDown())
|
||||
|
@ -1113,7 +1118,7 @@ namespace MWClass
|
|||
}
|
||||
|
||||
if (fullHelp)
|
||||
info.text = MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra = MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
@ -1138,16 +1143,7 @@ namespace MWClass
|
|||
|
||||
void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const
|
||||
{
|
||||
MWMechanics::NpcStats& stats = getNpcStats(ptr);
|
||||
|
||||
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);
|
||||
MWBase::Environment::get().getLuaManager()->skillUse(ptr, skill, usageType, extraFactor);
|
||||
}
|
||||
|
||||
float Npc::getArmorRating(const MWWorld::Ptr& ptr) const
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "../mwrender/renderinginterface.hpp"
|
||||
|
||||
#include "../mwmechanics/alchemy.hpp"
|
||||
#include "../mwmechanics/spellutil.hpp"
|
||||
|
||||
#include "classmodel.hpp"
|
||||
|
||||
|
@ -65,9 +66,7 @@ namespace MWClass
|
|||
|
||||
int Potion::getValue(const MWWorld::ConstPtr& ptr) const
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Potion>* ref = ptr.get<ESM::Potion>();
|
||||
|
||||
return ref->mBase->mData.mValue;
|
||||
return MWMechanics::getPotionValue(*ptr.get<ESM::Potion>()->mBase);
|
||||
}
|
||||
|
||||
const ESM::RefId& Potion::getUpSoundId(const MWWorld::ConstPtr& ptr) const
|
||||
|
@ -101,7 +100,7 @@ namespace MWClass
|
|||
std::string text;
|
||||
|
||||
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
|
||||
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
|
||||
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}");
|
||||
|
||||
info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects);
|
||||
|
||||
|
@ -114,8 +113,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -117,8 +117,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -119,8 +119,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -239,8 +239,8 @@ namespace MWClass
|
|||
|
||||
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
|
||||
{
|
||||
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
|
||||
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
|
||||
}
|
||||
|
||||
info.text = std::move(text);
|
||||
|
|
|
@ -543,7 +543,8 @@ namespace MWDialogue
|
|||
mPermanentDispositionChange += perm;
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -652,7 +653,7 @@ namespace MWDialogue
|
|||
if (Settings::gui().mSubtitles)
|
||||
winMgr->messageBox(info->mResponse);
|
||||
if (!info->mSound.empty())
|
||||
sndMgr->say(actor, Misc::ResourceHelpers::correctSoundPath(info->mSound));
|
||||
sndMgr->say(actor, Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(info->mSound)));
|
||||
if (!info->mResultScript.empty())
|
||||
executeScript(info->mResultScript, actor);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace
|
|||
{
|
||||
const std::string mText;
|
||||
const Response mResponses[3];
|
||||
const std::string mSound;
|
||||
const VFS::Path::Normalized mSound;
|
||||
};
|
||||
|
||||
Step sGenerateClassSteps(int number)
|
||||
|
|
|
@ -164,8 +164,10 @@ namespace MWGui
|
|||
const MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
|
||||
|
||||
setClassImage(mClassImage,
|
||||
ESM::RefId::stringRefId(getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(0),
|
||||
pcStats.getSkillIncreasesForSpecialization(1), pcStats.getSkillIncreasesForSpecialization(2))));
|
||||
ESM::RefId::stringRefId(
|
||||
getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Combat),
|
||||
pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Magic),
|
||||
pcStats.getSkillIncreasesForSpecialization(ESM::Class::Specialization::Stealth))));
|
||||
|
||||
int level = creatureStats.getLevel() + 1;
|
||||
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level));
|
||||
|
|
|
@ -95,6 +95,13 @@ namespace
|
|||
return std::clamp(
|
||||
viewingDistanceInCells, Constants::CellGridRadius, Settings::map().mMaxLocalViewingDistance.get());
|
||||
}
|
||||
|
||||
ESM::RefId getCellIdInWorldSpace(const MWWorld::Cell& cell, int x, int y)
|
||||
{
|
||||
if (cell.isExterior())
|
||||
return ESM::Cell::generateIdForCell(true, {}, x, y);
|
||||
return cell.getId();
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
|
@ -170,12 +177,9 @@ namespace MWGui
|
|||
LocalMapBase::LocalMapBase(
|
||||
CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled)
|
||||
: mLocalMapRender(localMapRender)
|
||||
, mCurX(0)
|
||||
, mCurY(0)
|
||||
, mInterior(false)
|
||||
, mActiveCell(nullptr)
|
||||
, mLocalMap(nullptr)
|
||||
, mCompass(nullptr)
|
||||
, mChanged(true)
|
||||
, mFogOfWarToggled(true)
|
||||
, mFogOfWarEnabled(fogOfWarEnabled)
|
||||
, mNumCells(1)
|
||||
|
@ -231,12 +235,6 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void LocalMapBase::setCellPrefix(const std::string& prefix)
|
||||
{
|
||||
mPrefix = prefix;
|
||||
mChanged = true;
|
||||
}
|
||||
|
||||
bool LocalMapBase::toggleFogOfWar()
|
||||
{
|
||||
mFogOfWarToggled = !mFogOfWarToggled;
|
||||
|
@ -262,8 +260,8 @@ namespace MWGui
|
|||
{
|
||||
// normalized cell coordinates
|
||||
auto mapWidgetSize = getWidgetSize();
|
||||
return MyGUI::IntPoint(std::round(nX * mapWidgetSize + (mCellDistance + (cellX - mCurX)) * mapWidgetSize),
|
||||
std::round(nY * mapWidgetSize + (mCellDistance - (cellY - mCurY)) * mapWidgetSize));
|
||||
return MyGUI::IntPoint(std::round((nX + mCellDistance + cellX - mActiveCell->getGridX()) * mapWidgetSize),
|
||||
std::round((nY + mCellDistance - cellY + mActiveCell->getGridY()) * mapWidgetSize));
|
||||
}
|
||||
|
||||
MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) const
|
||||
|
@ -272,7 +270,7 @@ namespace MWGui
|
|||
// normalized cell coordinates
|
||||
float nX, nY;
|
||||
|
||||
if (!mInterior)
|
||||
if (mActiveCell->isExterior())
|
||||
{
|
||||
ESM::ExteriorCellLocation cellPos = ESM::positionToExteriorCellLocation(worldX, worldY);
|
||||
cellIndex.x() = cellPos.mX;
|
||||
|
@ -336,7 +334,7 @@ namespace MWGui
|
|||
|
||||
std::vector<MyGUI::Widget*>& LocalMapBase::currentDoorMarkersWidgets()
|
||||
{
|
||||
return mInterior ? mInteriorDoorMarkerWidgets : mExteriorDoorMarkerWidgets;
|
||||
return mActiveCell->isExterior() ? mExteriorDoorMarkerWidgets : mInteriorDoorMarkerWidgets;
|
||||
}
|
||||
|
||||
void LocalMapBase::updateCustomMarkers()
|
||||
|
@ -344,12 +342,14 @@ namespace MWGui
|
|||
for (MyGUI::Widget* widget : mCustomMarkerWidgets)
|
||||
MyGUI::Gui::getInstance().destroyWidget(widget);
|
||||
mCustomMarkerWidgets.clear();
|
||||
|
||||
if (!mActiveCell)
|
||||
return;
|
||||
for (int dX = -mCellDistance; dX <= mCellDistance; ++dX)
|
||||
{
|
||||
for (int dY = -mCellDistance; dY <= mCellDistance; ++dY)
|
||||
{
|
||||
ESM::RefId cellRefId = ESM::Cell::generateIdForCell(!mInterior, mPrefix, mCurX + dX, mCurY + dY);
|
||||
ESM::RefId cellRefId
|
||||
= getCellIdInWorldSpace(*mActiveCell, mActiveCell->getGridX() + dX, mActiveCell->getGridY() + dY);
|
||||
|
||||
CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellRefId);
|
||||
for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second;
|
||||
|
@ -377,16 +377,25 @@ namespace MWGui
|
|||
redraw();
|
||||
}
|
||||
|
||||
void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
|
||||
void LocalMapBase::setActiveCell(const MWWorld::Cell& cell)
|
||||
{
|
||||
if (x == mCurX && y == mCurY && mInterior == interior && !mChanged)
|
||||
if (&cell == mActiveCell)
|
||||
return; // don't do anything if we're still in the same cell
|
||||
|
||||
if (!interior && !(x == mCurX && y == mCurY))
|
||||
const int x = cell.getGridX();
|
||||
const int y = cell.getGridY();
|
||||
|
||||
if (cell.isExterior())
|
||||
{
|
||||
const MyGUI::IntRect intersection
|
||||
= { std::max(x, mCurX) - mCellDistance, std::max(y, mCurY) - mCellDistance,
|
||||
std::min(x, mCurX) + mCellDistance, std::min(y, mCurY) + mCellDistance };
|
||||
int curX = 0;
|
||||
int curY = 0;
|
||||
if (mActiveCell)
|
||||
{
|
||||
curX = mActiveCell->getGridX();
|
||||
curY = mActiveCell->getGridY();
|
||||
}
|
||||
const MyGUI::IntRect intersection = { std::max(x, curX) - mCellDistance, std::max(y, curY) - mCellDistance,
|
||||
std::min(x, curX) + mCellDistance, std::min(y, curY) + mCellDistance };
|
||||
|
||||
const MyGUI::IntRect activeGrid = createRect({ x, y }, Constants::CellGridRadius);
|
||||
const MyGUI::IntRect currentView = createRect({ x, y }, mCellDistance);
|
||||
|
@ -407,17 +416,14 @@ namespace MWGui
|
|||
for (auto& widget : mDoorMarkersToRecycle)
|
||||
widget->setVisible(false);
|
||||
|
||||
for (auto const& cell : mMaps)
|
||||
for (auto const& entry : mMaps)
|
||||
{
|
||||
if (mHasALastActiveCell && !intersection.inside({ cell.mCellX, cell.mCellY }))
|
||||
mLocalMapRender->removeExteriorCell(cell.mCellX, cell.mCellY);
|
||||
if (mHasALastActiveCell && !intersection.inside({ entry.mCellX, entry.mCellY }))
|
||||
mLocalMapRender->removeExteriorCell(entry.mCellX, entry.mCellY);
|
||||
}
|
||||
}
|
||||
|
||||
mCurX = x;
|
||||
mCurY = y;
|
||||
mInterior = interior;
|
||||
mChanged = false;
|
||||
mActiveCell = &cell;
|
||||
|
||||
for (int mx = 0; mx < mNumCells; ++mx)
|
||||
{
|
||||
|
@ -441,7 +447,7 @@ namespace MWGui
|
|||
for (MyGUI::Widget* widget : currentDoorMarkersWidgets())
|
||||
widget->setCoord(getMarkerCoordinates(widget, 8));
|
||||
|
||||
if (!mInterior)
|
||||
if (mActiveCell->isExterior())
|
||||
mHasALastActiveCell = true;
|
||||
|
||||
updateMagicMarkers();
|
||||
|
@ -580,7 +586,7 @@ namespace MWGui
|
|||
|
||||
if (!entry.mMapTexture)
|
||||
{
|
||||
if (!mInterior)
|
||||
if (mActiveCell->isExterior())
|
||||
requestMapRender(&MWBase::Environment::get().getWorldModel()->getExterior(
|
||||
ESM::ExteriorCellLocation(entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId)));
|
||||
|
||||
|
@ -626,12 +632,12 @@ namespace MWGui
|
|||
mDoorMarkersToRecycle.end(), mInteriorDoorMarkerWidgets.begin(), mInteriorDoorMarkerWidgets.end());
|
||||
mInteriorDoorMarkerWidgets.clear();
|
||||
|
||||
if (mInterior)
|
||||
if (!mActiveCell->isExterior())
|
||||
{
|
||||
for (MyGUI::Widget* widget : mExteriorDoorMarkerWidgets)
|
||||
widget->setVisible(false);
|
||||
|
||||
MWWorld::CellStore& cell = worldModel->getInterior(mPrefix);
|
||||
MWWorld::CellStore& cell = worldModel->getInterior(mActiveCell->getNameId());
|
||||
world->getDoorMarkers(cell, doors);
|
||||
}
|
||||
else
|
||||
|
@ -678,7 +684,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
currentDoorMarkersWidgets().push_back(markerWidget);
|
||||
if (!mInterior)
|
||||
if (mActiveCell->isExterior())
|
||||
mExteriorDoorsByCell[{ data->cellX, data->cellY }].push_back(markerWidget);
|
||||
}
|
||||
|
||||
|
@ -701,8 +707,7 @@ namespace MWGui
|
|||
MWWorld::CellStore* markedCell = nullptr;
|
||||
ESM::Position markedPosition;
|
||||
MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition);
|
||||
if (markedCell && markedCell->isExterior() == !mInterior
|
||||
&& (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->getNameId(), mPrefix)))
|
||||
if (markedCell && markedCell->getCell()->getWorldSpace() == mActiveCell->getWorldSpace())
|
||||
{
|
||||
MarkerUserData markerPos(mLocalMapRender);
|
||||
MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
|
||||
|
@ -870,11 +875,11 @@ namespace MWGui
|
|||
int y = (int(widgetPos.top / float(mapWidgetSize)) - mCellDistance) * -1;
|
||||
float nX = widgetPos.left / float(mapWidgetSize) - int(widgetPos.left / float(mapWidgetSize));
|
||||
float nY = widgetPos.top / float(mapWidgetSize) - int(widgetPos.top / float(mapWidgetSize));
|
||||
x += mCurX;
|
||||
y += mCurY;
|
||||
x += mActiveCell->getGridX();
|
||||
y += mActiveCell->getGridY();
|
||||
|
||||
osg::Vec2f worldPos;
|
||||
if (mInterior)
|
||||
if (!mActiveCell->isExterior())
|
||||
{
|
||||
worldPos = mLocalMapRender->interiorMapToWorldPosition(nX, nY, x, y);
|
||||
}
|
||||
|
@ -886,7 +891,7 @@ namespace MWGui
|
|||
|
||||
mEditingMarker.mWorldX = worldPos.x();
|
||||
mEditingMarker.mWorldY = worldPos.y();
|
||||
ESM::RefId clickedId = ESM::Cell::generateIdForCell(!mInterior, LocalMapBase::mPrefix, x, y);
|
||||
ESM::RefId clickedId = getCellIdInWorldSpace(*mActiveCell, x, y);
|
||||
|
||||
mEditingMarker.mCell = clickedId;
|
||||
|
||||
|
@ -977,7 +982,7 @@ namespace MWGui
|
|||
resizeGlobalMap();
|
||||
|
||||
float x = mCurPos.x(), y = mCurPos.y();
|
||||
if (mInterior)
|
||||
if (!mActiveCell->isExterior())
|
||||
{
|
||||
auto pos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();
|
||||
x = pos.x();
|
||||
|
@ -1020,7 +1025,7 @@ namespace MWGui
|
|||
resizeGlobalMap();
|
||||
}
|
||||
|
||||
MapWindow::~MapWindow() {}
|
||||
MapWindow::~MapWindow() = default;
|
||||
|
||||
void MapWindow::setCellName(const std::string& cellName)
|
||||
{
|
||||
|
@ -1289,7 +1294,7 @@ namespace MWGui
|
|||
mMarkers.clear();
|
||||
|
||||
mGlobalMapRender->clear();
|
||||
mChanged = true;
|
||||
mActiveCell = nullptr;
|
||||
|
||||
for (auto& widgetPair : mGlobalMapMarkers)
|
||||
MyGUI::Gui::getInstance().destroyWidget(widgetPair.first.widget);
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace ESM
|
|||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Cell;
|
||||
class CellStore;
|
||||
}
|
||||
|
||||
|
@ -77,8 +78,7 @@ namespace MWGui
|
|||
virtual ~LocalMapBase();
|
||||
void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int cellDistance = Constants::CellGridRadius);
|
||||
|
||||
void setCellPrefix(const std::string& prefix);
|
||||
void setActiveCell(const int x, const int y, bool interior = false);
|
||||
void setActiveCell(const MWWorld::Cell& cell);
|
||||
void requestMapRender(const MWWorld::CellStore* cell);
|
||||
void setPlayerDir(const float x, const float y);
|
||||
void setPlayerPos(int cellX, int cellY, const float nx, const float ny);
|
||||
|
@ -115,15 +115,12 @@ namespace MWGui
|
|||
float mLocalMapZoom = 1.f;
|
||||
MWRender::LocalMap* mLocalMapRender;
|
||||
|
||||
int mCurX, mCurY; // the position of the active cell on the global map (in cell coords)
|
||||
const MWWorld::Cell* mActiveCell;
|
||||
bool mHasALastActiveCell = false;
|
||||
osg::Vec2f mCurPos; // the position of the player in the world (in cell coords)
|
||||
|
||||
bool mInterior;
|
||||
MyGUI::ScrollView* mLocalMap;
|
||||
MyGUI::ImageBox* mCompass;
|
||||
std::string mPrefix;
|
||||
bool mChanged;
|
||||
bool mFogOfWarToggled;
|
||||
bool mFogOfWarEnabled;
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace MWGui
|
|||
return false;
|
||||
}
|
||||
else
|
||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1);
|
||||
player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, ESM::Skill::Sneak_PickPocket);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -410,10 +410,13 @@ namespace MWGui
|
|||
const std::string& image = info.icon;
|
||||
int imageSize = (!image.empty()) ? info.imageSize : 0;
|
||||
std::string text = info.text;
|
||||
std::string_view extra = info.extra;
|
||||
|
||||
// remove the first newline (easier this way)
|
||||
if (text.size() > 0 && text[0] == '\n')
|
||||
if (!text.empty() && text[0] == '\n')
|
||||
text.erase(0, 1);
|
||||
if (!extra.empty() && extra[0] == '\n')
|
||||
extra = extra.substr(1);
|
||||
|
||||
const ESM::Enchantment* enchant = nullptr;
|
||||
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,
|
||||
(captionHeight - captionSize.height) / 2, captionSize.width - imageSize, captionSize.height);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace MWGui
|
|||
|
||||
std::string caption;
|
||||
std::string text;
|
||||
std::string extra;
|
||||
std::string icon;
|
||||
int imageSize;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <MyGUI_TextIterator.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -174,10 +175,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
// increase skill
|
||||
MWWorld::LiveCellRef<ESM::NPC>* playerRef = player.get<ESM::NPC>();
|
||||
|
||||
const ESM::Class* class_ = store.get<ESM::Class>().find(playerRef->mBase->mClass);
|
||||
pcStats.increaseSkill(skill->mId, *class_, true);
|
||||
MWBase::Environment::get().getLuaManager()->skillLevelUp(player, skill->mId, "trainer");
|
||||
|
||||
// remove gold
|
||||
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
|
||||
|
|
|
@ -371,7 +371,7 @@ namespace MWGui::Widgets
|
|||
|
||||
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();
|
||||
if (displayType == ESM::MagicEffect::MDT_TimesInt)
|
||||
|
@ -386,7 +386,7 @@ namespace MWGui::Widgets
|
|||
|
||||
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);
|
||||
if (mEffectParams.mMagnMin != mEffectParams.mMagnMax)
|
||||
|
|
|
@ -844,7 +844,7 @@ namespace MWGui
|
|||
|
||||
if (!player.getCell()->isExterior())
|
||||
{
|
||||
setActiveMap(x, y, true);
|
||||
setActiveMap(*player.getCell()->getCell());
|
||||
}
|
||||
// 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->cellExplored(cellCommon->getGridX(), cellCommon->getGridY());
|
||||
|
||||
setActiveMap(cellCommon->getGridX(), cellCommon->getGridY(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mMap->setCellPrefix(std::string(cellCommon->getNameId()));
|
||||
mHud->setCellPrefix(std::string(cellCommon->getNameId()));
|
||||
|
||||
osg::Vec3f worldPos;
|
||||
if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos))
|
||||
worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();
|
||||
else
|
||||
MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos);
|
||||
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);
|
||||
mHud->setActiveCell(x, y, interior);
|
||||
mMap->setActiveCell(cell);
|
||||
mHud->setActiveCell(cell);
|
||||
}
|
||||
|
||||
void WindowManager::setDrowningBarVisibility(bool visible)
|
||||
|
|
|
@ -50,6 +50,7 @@ namespace MyGUI
|
|||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Cell;
|
||||
class ESMStore;
|
||||
}
|
||||
|
||||
|
@ -216,9 +217,6 @@ namespace MWGui
|
|||
bool toggleFullHelp() override; ///< show extra info in item tooltips (owner, script)
|
||||
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
|
||||
void setDrowningBarVisibility(bool visible) override;
|
||||
|
||||
|
@ -589,6 +587,9 @@ namespace MWGui
|
|||
void setCullMask(uint32_t mask) 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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
|
@ -119,15 +120,22 @@ namespace MWInput
|
|||
mBindingsManager->setPlayerControlsEnabled(!guiMode);
|
||||
mBindingsManager->mouseReleased(arg, id);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getLuaManager()->inputEvent(
|
||||
{ MWBase::LuaManager::InputEvent::MouseButtonReleased, arg.button });
|
||||
}
|
||||
|
||||
void MouseManager::mouseWheelMoved(const SDL_MouseWheelEvent& arg)
|
||||
{
|
||||
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
||||
if (mBindingsManager->isDetectingBindingState() || !input->controlsDisabled())
|
||||
{
|
||||
mBindingsManager->mouseWheelMoved(arg);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -161,7 +169,11 @@ namespace MWInput
|
|||
const MWGui::SettingsWindow* settingsWindow
|
||||
= MWBase::Environment::get().getWindowManager()->getSettingsWindow();
|
||||
if ((!settingsWindow || !settingsWindow->isVisible()) && !input->controlsDisabled())
|
||||
{
|
||||
mBindingsManager->mousePressed(arg, id);
|
||||
}
|
||||
MWBase::Environment::get().getLuaManager()->inputEvent(
|
||||
{ MWBase::LuaManager::InputEvent::MouseButtonPressed, arg.button });
|
||||
}
|
||||
|
||||
void MouseManager::updateCursorMode()
|
||||
|
@ -208,14 +220,14 @@ namespace MWInput
|
|||
};
|
||||
|
||||
// Only actually turn player when we're not in vanity mode
|
||||
bool controls = MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols");
|
||||
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && controls)
|
||||
bool playerLooking = MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking");
|
||||
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && playerLooking)
|
||||
{
|
||||
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
||||
player.yaw(-rot[2]);
|
||||
player.pitch(-rot[0]);
|
||||
}
|
||||
else if (!controls)
|
||||
else if (!playerLooking)
|
||||
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
|
||||
|
||||
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
||||
|
|
|
@ -21,24 +21,6 @@
|
|||
#include "animationbindings.hpp"
|
||||
#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
|
||||
{
|
||||
using BlendMask = MWRender::Animation::BlendMask;
|
||||
|
|
49
apps/openmw/mwlua/birthsignbindings.cpp
Normal file
49
apps/openmw/mwlua/birthsignbindings.cpp
Normal 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);
|
||||
}
|
||||
}
|
13
apps/openmw/mwlua/birthsignbindings.hpp
Normal file
13
apps/openmw/mwlua/birthsignbindings.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef MWLUA_BIRTHSIGNBINDINGS_H
|
||||
#define MWLUA_BIRTHSIGNBINDINGS_H
|
||||
|
||||
#include <sol/forward.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
sol::table initBirthSignRecordBindings(const Context& context);
|
||||
}
|
||||
|
||||
#endif // MWLUA_BIRTHSIGNBINDINGS_H
|
|
@ -6,6 +6,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "classbindings.hpp"
|
||||
#include "idcollectionbindings.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
#include "stats.hpp"
|
||||
#include "types/types.hpp"
|
||||
|
@ -16,16 +17,12 @@ namespace sol
|
|||
struct is_automagical<ESM::Class> : std::false_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_automagical<MWWorld::Store<ESM::Class>> : std::false_type
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
sol::table initCoreClassBindings(const Context& context)
|
||||
sol::table initClassRecordBindings(const Context& context)
|
||||
{
|
||||
sol::state_view& lua = context.mLua->sol();
|
||||
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; });
|
||||
|
||||
classT["attributes"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table {
|
||||
sol::table res(lua, sol::create);
|
||||
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;
|
||||
return createReadOnlyRefIdTable(lua, rec.mData.mAttribute, ESM::Attribute::indexToRefId);
|
||||
});
|
||||
classT["majorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table {
|
||||
sol::table res(lua, sol::create);
|
||||
auto skills = rec.mData.mSkills;
|
||||
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;
|
||||
return createReadOnlyRefIdTable(
|
||||
lua, rec.mData.mSkills, [](const auto& pair) { return ESM::Skill::indexToRefId(pair[1]); });
|
||||
});
|
||||
classT["minorSkills"] = sol::readonly_property([lua](const ESM::Class& rec) -> sol::table {
|
||||
sol::table res(lua, sol::create);
|
||||
auto skills = rec.mData.mSkills;
|
||||
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;
|
||||
return createReadOnlyRefIdTable(
|
||||
lua, rec.mData.mSkills, [](const auto& pair) { return ESM::Skill::indexToRefId(pair[0]); });
|
||||
});
|
||||
|
||||
classT["specialization"] = sol::readonly_property([](const ESM::Class& rec) -> std::string_view {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace MWLua
|
||||
{
|
||||
sol::table initCoreClassBindings(const Context& context);
|
||||
sol::table initClassRecordBindings(const Context& context);
|
||||
}
|
||||
|
||||
#endif // MWLUA_CLASSBINDINGS_H
|
||||
|
|
|
@ -95,6 +95,24 @@ namespace MWLua
|
|||
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:
|
||||
MWWorld::Ptr getPtr(ESM::RefNum id) const
|
||||
{
|
||||
|
|
|
@ -57,8 +57,21 @@ namespace MWLua
|
|||
std::string mGroupname;
|
||||
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,
|
||||
OnAnimationTextKey>;
|
||||
OnAnimationTextKey, OnSkillUse, OnSkillLevelUp>;
|
||||
|
||||
void clear() { mQueue.clear(); }
|
||||
void addToQueue(Event e) { mQueue.push_back(std::move(e)); }
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
#include "idcollectionbindings.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace
|
||||
|
@ -96,26 +97,10 @@ namespace MWLua
|
|||
return res;
|
||||
});
|
||||
factionT["attributes"] = sol::readonly_property([&lua](const ESM::Faction& rec) {
|
||||
sol::table res(lua, sol::create);
|
||||
for (auto attributeIndex : rec.mData.mAttribute)
|
||||
{
|
||||
ESM::RefId id = ESM::Attribute::indexToRefId(attributeIndex);
|
||||
if (!id.empty())
|
||||
res.add(id.serializeText());
|
||||
}
|
||||
|
||||
return res;
|
||||
return createReadOnlyRefIdTable(lua, rec.mData.mAttribute, ESM::Attribute::indexToRefId);
|
||||
});
|
||||
factionT["skills"] = sol::readonly_property([&lua](const ESM::Faction& rec) {
|
||||
sol::table res(lua, sol::create);
|
||||
for (auto skillIndex : rec.mData.mSkills)
|
||||
{
|
||||
ESM::RefId id = ESM::Skill::indexToRefId(skillIndex);
|
||||
if (!id.empty())
|
||||
res.add(id.serializeText());
|
||||
}
|
||||
|
||||
return res;
|
||||
return createReadOnlyRefIdTable(lua, rec.mData.mSkills, ESM::Skill::indexToRefId);
|
||||
});
|
||||
auto rankT = lua.new_usertype<FactionRank>("ESM3_FactionRank");
|
||||
rankT[sol::meta_function::to_string] = [](const FactionRank& rec) -> std::string {
|
||||
|
|
25
apps/openmw/mwlua/idcollectionbindings.hpp
Normal file
25
apps/openmw/mwlua/idcollectionbindings.hpp
Normal 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
|
|
@ -18,7 +18,7 @@ namespace MWLua
|
|||
{
|
||||
mScriptsContainer->registerEngineHandlers({ &mKeyPressHandlers, &mKeyReleaseHandlers,
|
||||
&mControllerButtonPressHandlers, &mControllerButtonReleaseHandlers, &mActionHandlers, &mTouchpadPressed,
|
||||
&mTouchpadReleased, &mTouchpadMoved });
|
||||
&mTouchpadReleased, &mTouchpadMoved, &mMouseButtonPress, &mMouseButtonRelease, &mMouseWheel });
|
||||
}
|
||||
|
||||
void processInputEvent(const MWBase::LuaManager::InputEvent& event)
|
||||
|
@ -53,6 +53,16 @@ namespace MWLua
|
|||
case InputEvent::TouchMoved:
|
||||
mScriptsContainer->callEngineHandlers(mTouchpadMoved, std::get<SDLUtil::TouchEvent>(event.mValue));
|
||||
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 mTouchpadReleased{ "onTouchRelease" };
|
||||
typename Container::EngineHandlerList mTouchpadMoved{ "onTouchMove" };
|
||||
typename Container::EngineHandlerList mMouseButtonPress{ "onMouseButtonPress" };
|
||||
typename Container::EngineHandlerList mMouseButtonRelease{ "onMouseButtonRelease" };
|
||||
typename Container::EngineHandlerList mMouseWheel{ "onMouseWheel" };
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,8 @@ namespace MWLua
|
|||
{
|
||||
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
|
||||
registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
|
||||
&mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers });
|
||||
&mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse,
|
||||
&mOnSkillLevelUp });
|
||||
}
|
||||
|
||||
void LocalScripts::setActive(bool active)
|
||||
|
|
|
@ -79,6 +79,14 @@ namespace MWLua
|
|||
{
|
||||
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();
|
||||
|
||||
|
@ -93,6 +101,8 @@ namespace MWLua
|
|||
EngineHandlerList mOnTeleportedHandlers{ "onTeleported" };
|
||||
EngineHandlerList mOnAnimationTextKeyHandlers{ "_onAnimationTextKey" };
|
||||
EngineHandlerList mOnPlayAnimationHandlers{ "_onPlayAnimation" };
|
||||
EngineHandlerList mOnSkillUse{ "_onSkillUse" };
|
||||
EngineHandlerList mOnSkillLevelUp{ "_onSkillLevelUp" };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -112,13 +112,12 @@ namespace MWLua
|
|||
mPlayerPackages.insert(mLocalPackages.begin(), mLocalPackages.end());
|
||||
|
||||
LuaUtil::LuaStorage::initLuaBindings(mLua.sol());
|
||||
mGlobalScripts.addPackage(
|
||||
"openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua.sol(), &mGlobalStorage));
|
||||
mGlobalScripts.addPackage("openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua, &mGlobalStorage));
|
||||
mMenuScripts.addPackage(
|
||||
"openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage));
|
||||
mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua.sol(), &mGlobalStorage);
|
||||
"openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua, &mGlobalStorage, &mPlayerStorage));
|
||||
mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua, &mGlobalStorage);
|
||||
mPlayerPackages["openmw.storage"]
|
||||
= LuaUtil::LuaStorage::initPlayerPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage);
|
||||
= LuaUtil::LuaStorage::initPlayerPackage(mLua, &mGlobalStorage, &mPlayerStorage);
|
||||
|
||||
mPlayerStorage.setActive(true);
|
||||
mGlobalStorage.setActive(false);
|
||||
|
@ -456,6 +455,17 @@ namespace MWLua
|
|||
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)
|
||||
{
|
||||
mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
||||
|
|
|
@ -88,6 +88,8 @@ namespace MWLua
|
|||
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) 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
|
||||
{
|
||||
mEngineEvents.addToQueue(EngineEvents::OnNewExterior{ cell });
|
||||
|
|
|
@ -16,6 +16,31 @@
|
|||
#include "luamanagerimp.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
|
||||
{
|
||||
template <>
|
||||
|
@ -71,24 +96,27 @@ namespace MWLua
|
|||
}));
|
||||
|
||||
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;
|
||||
float radius = 0;
|
||||
if (options)
|
||||
{
|
||||
sol::optional<LObject> ignoreObj = options->get<sol::optional<LObject>>("ignore");
|
||||
if (ignoreObj)
|
||||
ignore = ignoreObj->ptr();
|
||||
ignore = parseIgnoreList<MWWorld::ConstPtr>(*options);
|
||||
collisionType = options->get<sol::optional<int>>("collisionType").value_or(collisionType);
|
||||
radius = options->get<sol::optional<float>>("radius").value_or(0);
|
||||
}
|
||||
const MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();
|
||||
if (radius <= 0)
|
||||
return rayCasting->castRay(from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType);
|
||||
{
|
||||
return rayCasting->castRay(from, to, ignore, {}, collisionType);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ignore.isEmpty())
|
||||
throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0");
|
||||
for (const auto& ptr : ignore)
|
||||
{
|
||||
if (!ptr.isEmpty())
|
||||
throw std::logic_error("Currently castRay doesn't support `ignore` when radius > 0");
|
||||
}
|
||||
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.
|
||||
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())
|
||||
{
|
||||
throw std::logic_error(
|
||||
"castRenderingRay can be used only in player scripts during processing of input events; "
|
||||
"use asyncCastRenderingRay instead.");
|
||||
}
|
||||
|
||||
std::vector<MWWorld::Ptr> ignore;
|
||||
if (options.has_value())
|
||||
{
|
||||
ignore = parseIgnoreList(*options);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
api["asyncCastRenderingRay"] = [context](
|
||||
const sol::table& callback, const osg::Vec3f& from, const osg::Vec3f& to) {
|
||||
context.mLuaManager->addAction([context, callback = LuaUtil::Callback::fromLua(callback), from, to] {
|
||||
api["asyncCastRenderingRay"] = [context](const sol::table& callback, const osg::Vec3f& from,
|
||||
const osg::Vec3f& to, const sol::optional<sol::table>& options) {
|
||||
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;
|
||||
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));
|
||||
});
|
||||
};
|
||||
|
|
117
apps/openmw/mwlua/racebindings.cpp
Normal file
117
apps/openmw/mwlua/racebindings.cpp
Normal 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);
|
||||
}
|
||||
}
|
13
apps/openmw/mwlua/racebindings.hpp
Normal file
13
apps/openmw/mwlua/racebindings.hpp
Normal 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
|
|
@ -174,12 +174,12 @@ namespace MWLua
|
|||
api["say"] = sol::overload(
|
||||
[luaManager = context.mLuaManager](
|
||||
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)
|
||||
luaManager->addUIMessage(*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)
|
||||
luaManager->addUIMessage(*text);
|
||||
});
|
||||
|
@ -227,7 +227,7 @@ namespace MWLua
|
|||
soundT["maxRange"]
|
||||
= sol::readonly_property([](const ESM::Sound& rec) -> unsigned char { return rec.mData.mMaxRange; });
|
||||
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);
|
||||
|
|
|
@ -73,6 +73,96 @@ namespace MWLua
|
|||
"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
|
||||
{
|
||||
ObjectVariant mObject;
|
||||
|
@ -85,7 +175,7 @@ namespace MWLua
|
|||
public:
|
||||
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(); });
|
||||
}
|
||||
|
||||
|
@ -93,7 +183,7 @@ namespace MWLua
|
|||
{
|
||||
SelfObject* obj = mObject.asSelfObject();
|
||||
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
|
||||
|
@ -101,7 +191,30 @@ namespace MWLua
|
|||
const auto& ptr = mObject.ptr();
|
||||
if (!ptr.getClass().isNpc())
|
||||
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)
|
||||
|
@ -110,13 +223,6 @@ namespace MWLua
|
|||
return {};
|
||||
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
|
||||
|
@ -323,6 +429,14 @@ namespace MWLua
|
|||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<MWLua::SkillIncreasesForAttributeStats> : std::false_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_automagical<MWLua::SkillIncreasesForSpecializationStats> : std::false_type
|
||||
{
|
||||
};
|
||||
template <>
|
||||
struct is_automagical<MWLua::LevelStat> : std::false_type
|
||||
{
|
||||
|
@ -360,10 +474,39 @@ namespace MWLua
|
|||
sol::table stats(context.mLua->sol(), sol::create);
|
||||
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");
|
||||
levelStatT["current"] = sol::property([context](const LevelStat& stat) { return stat.getCurrent(context); },
|
||||
[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);
|
||||
|
||||
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 {
|
||||
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");
|
||||
schoolT[sol::meta_function::to_string]
|
||||
|
|
|
@ -403,6 +403,11 @@ namespace MWLua
|
|||
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 {
|
||||
const MWWorld::Ptr ptr = actor.ptr();
|
||||
return ptr.getClass().getEncumbrance(ptr);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <sol/sol.hpp>
|
||||
|
||||
#include "../../mwmechanics/spellutil.hpp"
|
||||
#include "../../mwworld/class.hpp"
|
||||
|
||||
#include "../itemdata.hpp"
|
||||
|
@ -10,13 +11,21 @@ namespace MWLua
|
|||
{
|
||||
void addItemBindings(sol::table item, const Context& context)
|
||||
{
|
||||
item["getEnchantmentCharge"]
|
||||
= [](const Object& object) { return object.ptr().getCellRef().getEnchantmentCharge(); };
|
||||
item["setEnchantmentCharge"]
|
||||
= [](const GObject& object, float charge) { object.ptr().getCellRef().setEnchantmentCharge(charge); };
|
||||
item["getEnchantmentCharge"] = [](const Object& object) -> sol::optional<float> {
|
||||
float charge = object.ptr().getCellRef().getEnchantmentCharge();
|
||||
if (charge == -1)
|
||||
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"]
|
||||
= [](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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "../classbindings.hpp"
|
||||
#include "../localscripts.hpp"
|
||||
#include "../racebindings.hpp"
|
||||
#include "../stats.hpp"
|
||||
|
||||
namespace sol
|
||||
|
@ -85,7 +86,8 @@ namespace MWLua
|
|||
record["baseGold"] = sol::readonly_property([](const ESM::NPC& rec) -> int { return rec.mNpdt.mGold; });
|
||||
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.
|
||||
npc["isWerewolf"] = [](const Object& o) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue