1
0
Fork 0
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:
Zackhasacat 2024-03-12 06:54:15 -05:00
commit 8b75932f50
300 changed files with 4597 additions and 2004 deletions

View file

@ -210,6 +210,7 @@ Ubuntu_GCC_Debug:
CCACHE_SIZE: 3G
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:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,49 @@
#include <components/esm3/loadbsgn.hpp>
#include <components/lua/luastate.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "birthsignbindings.hpp"
#include "idcollectionbindings.hpp"
#include "luamanagerimp.hpp"
#include "types/types.hpp"
namespace sol
{
template <>
struct is_automagical<ESM::BirthSign> : std::false_type
{
};
}
namespace MWLua
{
sol::table initBirthSignRecordBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::table birthSigns(context.mLua->sol(), sol::create);
addRecordFunctionBinding<ESM::BirthSign>(birthSigns, context);
auto signT = lua.new_usertype<ESM::BirthSign>("ESM3_BirthSign");
signT[sol::meta_function::to_string] = [](const ESM::BirthSign& rec) -> std::string {
return "ESM3_BirthSign[" + rec.mId.toDebugString() + "]";
};
signT["id"] = sol::readonly_property([](const ESM::BirthSign& rec) { return rec.mId.serializeText(); });
signT["name"] = sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mName; });
signT["description"]
= sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; });
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string {
return Misc::ResourceHelpers::correctTexturePath(rec.mTexture, vfs);
});
signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table {
return createReadOnlyRefIdTable(lua, rec.mPowers.mList);
});
return LuaUtil::makeReadOnly(birthSigns);
}
}

View file

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

View file

@ -6,6 +6,7 @@
#include "../mwworld/esmstore.hpp"
#include "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 {

View file

@ -7,7 +7,7 @@
namespace MWLua
{
sol::table initCoreClassBindings(const Context& context);
sol::table initClassRecordBindings(const Context& context);
}
#endif // MWLUA_CLASSBINDINGS_H

View file

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

View file

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

View file

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

View file

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

View file

@ -18,7 +18,7 @@ namespace MWLua
{
mScriptsContainer->registerEngineHandlers({ &mKeyPressHandlers, &mKeyReleaseHandlers,
&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" };
};
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -174,12 +174,12 @@ namespace MWLua
api["say"] = sol::overload(
[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);

View file

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

View file

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

View file

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

View file

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