From 9cae7882dd53480bc75fa00de0954d00eeef20ce Mon Sep 17 00:00:00 2001 From: fredzio Date: Wed, 31 Mar 2021 19:34:06 +0200 Subject: [PATCH 01/44] Fix a bug that was triggered with multi mark mod. When a script calls SetPos for x,y,z in sequence on an actor, we need to make sure that the actor will not spawn under ground at x,y coordinates. Now that change of coordinates are cumulated and applied all at once, we need to account for the whole offset. To this end move the terrain height check inside of Actor class. --- apps/openmw/mwphysics/actor.cpp | 11 +++++++++++ apps/openmw/mwscript/transformationextensions.cpp | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index a2454345b..776212ede 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -195,6 +197,15 @@ void Actor::applyOffsetChange() { if (mPositionOffset.length() == 0) return; + if (mPositionOffset.z() != 0) + { + // Often, offset are set in sequence x, y, z + // We don't want actors to be moved under the ground + // Check terrain height at new coordinate and update z offset if necessary + const auto pos = mWorldPosition + mPositionOffset; + const auto terrainHeight = mPtr.getCell()->isExterior() ? MWBase::Environment::get().getWorld()->getTerrainHeightAt(pos) : -std::numeric_limits::max(); + mPositionOffset.z() = std::max(pos.z(), terrainHeight) - mWorldPosition.z(); + } mWorldPosition += mPositionOffset; mPosition += mPositionOffset; mPreviousPosition += mPositionOffset; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index e8b406977..6b92378c7 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -284,17 +284,6 @@ namespace MWScript } else if(axis == "z") { - // We should not place actors under ground - if (ptr.getClass().isActor()) - { - float terrainHeight = -std::numeric_limits::max(); - if (ptr.getCell()->isExterior()) - terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos); - - if (pos < terrainHeight) - pos = terrainHeight; - } - newPos[2] = pos; } else From b8089680705c0008d5a4a08dcf5c185dea4950b2 Mon Sep 17 00:00:00 2001 From: "Hristos N. Triantafillou" Date: Wed, 24 Mar 2021 18:19:05 -0500 Subject: [PATCH 02/44] Use the new macOS image for the CI runner open beta Also add a second build for wider version coverage. --- .gitlab-ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 36c8a6ab7..713cc2601 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -94,6 +94,7 @@ Debian_Clang_tests: BUILD_TESTS_ONLY: 1 MacOS: + image: macos-11-xcode-12 tags: - macos stage: build @@ -111,6 +112,10 @@ MacOS: - build/OpenMW-*.dmg - "build/**/*.log" +macOS10.15_Xcode11: + extends: MacOS + image: macos-10.15-xcode-11 + variables: &engine-targets targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard" package: "Engine" From 6c1844930432831e52c1d4aab7e362c81d213cb8 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 3 Apr 2021 02:15:00 +0200 Subject: [PATCH 03/44] Remove GTest and ExternalProject from FindGMock.cmake --- cmake/FindGMock.cmake | 320 +----------------------------------------- 1 file changed, 1 insertion(+), 319 deletions(-) diff --git a/cmake/FindGMock.cmake b/cmake/FindGMock.cmake index 8d7324242..502910a9d 100644 --- a/cmake/FindGMock.cmake +++ b/cmake/FindGMock.cmake @@ -1,15 +1,11 @@ # Get the Google C++ Mocking Framework. -# (This file is almost an copy of the original FindGTest.cmake file, -# altered to download and compile GMock and GTest if not found -# in GMOCK_ROOT or GTEST_ROOT respectively, +# (This file is almost an copy of the original FindGTest.cmake file for GMock, # feel free to use it as it is or modify it for your own needs.) # # Defines the following variables: # # GMOCK_FOUND - Found or got the Google Mocking framework -# GTEST_FOUND - Found or got the Google Testing framework # GMOCK_INCLUDE_DIRS - GMock include directory -# GTEST_INCLUDE_DIRS - GTest include direcotry # # Also defines the library variables below as normal variables # @@ -17,14 +13,8 @@ # GMOCK_LIBRARIES - libgmock # GMOCK_MAIN_LIBRARIES - libgmock-main # -# GTEST_BOTH_LIBRARIES - Both libgtest & libgtest_main -# GTEST_LIBRARIES - libgtest -# GTEST_MAIN_LIBRARIES - libgtest_main -# # Accepts the following variables as input: # -# GMOCK_ROOT - The root directory of the gmock install prefix -# GTEST_ROOT - The root directory of the gtest install prefix # GMOCK_SRC_DIR -The directory of the gmock sources # GMOCK_VER - The version of the gmock sources to be downloaded # @@ -101,48 +91,6 @@ # # * Kitware, Inc. #============================================================================= -# Thanks to Daniel Blezek for the GTEST_ADD_TESTS code - -function(gtest_add_tests executable extra_args) - if(NOT ARGN) - message(FATAL_ERROR "Missing ARGN: Read the documentation for GTEST_ADD_TESTS") - endif() - if(ARGN STREQUAL "AUTO") - # obtain sources used for building that executable - get_property(ARGN TARGET ${executable} PROPERTY SOURCES) - endif() - set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*") - set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)") - foreach(source ${ARGN}) - file(READ "${source}" contents) - string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents}) - foreach(hit ${found_tests}) - string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit}) - - # Parameterized tests have a different signature for the filter - if("x${test_type}" STREQUAL "xTEST_P") - string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1.\\2/*" test_name ${hit}) - elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST") - string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" test_name ${hit}) - elseif("x${test_type}" STREQUAL "xTYPED_TEST") - string(REGEX REPLACE ${gtest_case_name_regex} "\\1/*.\\2" test_name ${hit}) - else() - message(WARNING "Could not parse GTest ${hit} for adding to CTest.") - continue() - endif() - add_test(NAME ${test_name} COMMAND ${executable} --gtest_filter=${test_name} ${extra_args}) - endforeach() - endforeach() -endfunction() - -function(_append_debugs _endvar _library) - if(${_library} AND ${_library}_DEBUG) - set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) - else() - set(_output ${${_library}}) - endif() - set(${_endvar} ${_output} PARENT_SCOPE) -endfunction() function(_gmock_find_library _name) find_library(${_name} @@ -155,38 +103,20 @@ function(_gmock_find_library _name) mark_as_advanced(${_name}) endfunction() -function(_gtest_find_library _name) - find_library(${_name} - NAMES ${ARGN} - HINTS - ENV GTEST_ROOT - ${GTEST_ROOT} - PATH_SUFFIXES ${_gtest_libpath_suffixes} - ) - mark_as_advanced(${_name}) -endfunction() - if(NOT DEFINED GMOCK_MSVC_SEARCH) set(GMOCK_MSVC_SEARCH MD) endif() set(_gmock_libpath_suffixes lib) -set(_gtest_libpath_suffixes lib) if(MSVC) if(GMOCK_MSVC_SEARCH STREQUAL "MD") list(APPEND _gmock_libpath_suffixes msvc/gmock-md/Debug msvc/gmock-md/Release) - list(APPEND _gtest_libpath_suffixes - msvc/gtest-md/Debug - msvc/gtest-md/Release) elseif(GMOCK_MSVC_SEARCH STREQUAL "MT") list(APPEND _gmock_libpath_suffixes msvc/gmock/Debug msvc/gmock/Release) - list(APPEND _gtest_libpath_suffixes - msvc/gtest/Debug - msvc/gtest/Release) endif() endif() @@ -197,13 +127,6 @@ find_path(GMOCK_INCLUDE_DIR gmock/gmock.h ) mark_as_advanced(GMOCK_INCLUDE_DIR) -find_path(GTEST_INCLUDE_DIR gtest/gtest.h - HINTS - $ENV{GTEST_ROOT}/include - ${GTEST_ROOT}/include - ) -mark_as_advanced(GTEST_INCLUDE_DIR) - if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD") # The provided /MD project files for Google Mock add -md suffixes to the # library names. @@ -211,28 +134,12 @@ if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD") _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd) _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main) _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind) - - _gtest_find_library(GTEST_LIBRARY gtest-md gtest) - _gtest_find_library(GTEST_LIBRARY_DEBUG gtest-mdd gtestd) - _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main-md gtest_main) - _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_main-mdd gtest_maind) else() _gmock_find_library(GMOCK_LIBRARY gmock) _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd) _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main) _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind) - _gtest_find_library(GTEST_LIBRARY gtest) - _gtest_find_library(GTEST_LIBRARY_DEBUG gtestd) - _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main) - _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_maind) -endif() - -if(NOT TARGET GTest::GTest) - add_library(GTest::GTest UNKNOWN IMPORTED) -endif() -if(NOT TARGET GTest::Main) - add_library(GTest::Main UNKNOWN IMPORTED) endif() if(NOT TARGET GMock::GMock) @@ -244,224 +151,17 @@ if(NOT TARGET GMock::Main) endif() set(GMOCK_LIBRARY_EXISTS OFF) -set(GTEST_LIBRARY_EXISTS OFF) if(EXISTS "${GMOCK_LIBRARY}" OR EXISTS "${GMOCK_LIBRARY_DEBUG}" AND GMOCK_INCLUDE_DIR) set(GMOCK_LIBRARY_EXISTS ON) endif() -if(EXISTS "${GTEST_LIBRARY}" OR EXISTS "${GTEST_LIBRARY_DEBUG}" AND GTEST_INCLUDE_DIR) - set(GTEST_LIBRARY_EXISTS ON) -endif() - -if(NOT (${GMOCK_LIBRARY_EXISTS} AND ${GTEST_LIBRARY_EXISTS})) - - include(ExternalProject) - - if(GTEST_USE_STATIC_LIBS) - set(GTEST_CMAKE_ARGS -Dgtest_force_shared_crt:BOOL=ON -DBUILD_SHARED_LIBS=OFF) - if(BUILD_SHARED_LIBS) - list(APPEND GTEST_CMAKE_ARGS - -DCMAKE_POSITION_INDEPENDENT_CODE=ON - -Dgtest_hide_internal_symbols=ON - -DCMAKE_CXX_VISIBILITY_PRESET=hidden - -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON - -DCMAKE_POLICY_DEFAULT_CMP0063=NEW - ) - endif() - set(GTEST_LIBRARY_PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX}) - else() - set(GTEST_CMAKE_ARGS -DBUILD_SHARED_LIBS=ON) - set(GTEST_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) - endif() - if(WIN32) - list(APPEND GTEST_CMAKE_ARGS -Dgtest_disable_pthreads=ON) - endif() - - if("${GMOCK_SRC_DIR}" STREQUAL "") - message(STATUS "Downloading GMock / GTest version ${GMOCK_VER} from git") - if("${GMOCK_VER}" STREQUAL "1.6.0" OR "${GMOCK_VER}" STREQUAL "1.7.0") - set(GTEST_BIN_DIR "${GMOCK_ROOT}/src/gtest-build") - set(GTEST_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GTEST_MAIN_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - mark_as_advanced(GTEST_LIBRARY) - mark_as_advanced(GTEST_MAIN_LIBRARY) - - externalproject_add( - gtest - GIT_REPOSITORY "https://github.com/google/googletest.git" - GIT_TAG "release-${GMOCK_VER}" - PREFIX ${GMOCK_ROOT} - INSTALL_COMMAND "" - LOG_DOWNLOAD ON - LOG_CONFIGURE ON - LOG_BUILD ON - CMAKE_ARGS - ${GTEST_CMAKE_ARGS} - BINARY_DIR ${GTEST_BIN_DIR} - BUILD_BYPRODUCTS - "${GTEST_LIBRARY}" - "${GTEST_MAIN_LIBRARY}" - ) - - set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") - set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - mark_as_advanced(GMOCK_LIBRARY) - mark_as_advanced(GMOCK_MAIN_LIBRARY) - - externalproject_add( - gmock - GIT_REPOSITORY "https://github.com/google/googlemock.git" - GIT_TAG "release-${GMOCK_VER}" - PREFIX ${GMOCK_ROOT} - INSTALL_COMMAND "" - LOG_DOWNLOAD ON - LOG_CONFIGURE ON - LOG_BUILD ON - CMAKE_ARGS - ${GTEST_CMAKE_ARGS} - BINARY_DIR ${GMOCK_BIN_DIR} - BUILD_BYPRODUCTS - "${GMOCK_LIBRARY}" - "${GMOCK_MAIN_LIBRARY}" - ) - - add_dependencies(gmock gtest) - - add_dependencies(GTest::GTest gtest) - add_dependencies(GTest::Main gtest) - add_dependencies(GMock::GMock gmock) - add_dependencies(GMock::Main gmock) - - externalproject_get_property(gtest source_dir) - set(GTEST_INCLUDE_DIR "${source_dir}/include") - mark_as_advanced(GTEST_INCLUDE_DIR) - externalproject_get_property(gmock source_dir) - set(GMOCK_INCLUDE_DIR "${source_dir}/include") - mark_as_advanced(GMOCK_INCLUDE_DIR) - else() #1.8.0 - set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") - set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - mark_as_advanced(GTEST_LIBRARY) - mark_as_advanced(GTEST_MAIN_LIBRARY) - mark_as_advanced(GMOCK_LIBRARY) - mark_as_advanced(GMOCK_MAIN_LIBRARY) - - externalproject_add( - gmock - GIT_REPOSITORY "https://github.com/google/googletest.git" - GIT_TAG "release-${GMOCK_VER}" - PREFIX ${GMOCK_ROOT} - INSTALL_COMMAND "" - LOG_DOWNLOAD ON - LOG_CONFIGURE ON - LOG_BUILD ON - CMAKE_ARGS - ${GTEST_CMAKE_ARGS} - BINARY_DIR "${GMOCK_BIN_DIR}" - BUILD_BYPRODUCTS - "${GTEST_LIBRARY}" - "${GTEST_MAIN_LIBRARY}" - "${GMOCK_LIBRARY}" - "${GMOCK_MAIN_LIBRARY}" - ) - - add_dependencies(GTest::GTest gmock) - add_dependencies(GTest::Main gmock) - add_dependencies(GMock::GMock gmock) - add_dependencies(GMock::Main gmock) - - externalproject_get_property(gmock source_dir) - set(GTEST_INCLUDE_DIR "${source_dir}/googletest/include") - set(GMOCK_INCLUDE_DIR "${source_dir}/googlemock/include") - mark_as_advanced(GMOCK_INCLUDE_DIR) - mark_as_advanced(GTEST_INCLUDE_DIR) - endif() - - # Prevent CMake from complaining about these directories missing when the libgtest/libgmock targets get used as dependencies - file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR}) - else() - message(STATUS "Building Gmock / Gtest from dir ${GMOCK_SRC_DIR}") - - set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") - set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - mark_as_advanced(GTEST_LIBRARY) - mark_as_advanced(GTEST_MAIN_LIBRARY) - mark_as_advanced(GMOCK_LIBRARY) - mark_as_advanced(GMOCK_MAIN_LIBRARY) - - if(EXISTS "${GMOCK_SRC_DIR}/gtest/include/gtest/gtest.h") - set(GTEST_INCLUDE_DIR "${GMOCK_SRC_DIR}/gtest/include") - mark_as_advanced(GTEST_INCLUDE_DIR) - endif() - if(EXISTS "${GMOCK_SRC_DIR}/include/gmock/gmock.h") - set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/include") - mark_as_advanced(GMOCK_INCLUDE_DIR) - elseif(EXISTS "${GMOCK_SRC_DIR}/../../include/gmock/gmock.h") - set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/../../include") - if(IS_ABSOLUTE "${GMOCK_INCLUDE_DIR}") - get_filename_component(GMOCK_INCLUDE_DIR "${GMOCK_INCLUDE_DIR}" ABSOLUTE) - endif() - mark_as_advanced(GMOCK_INCLUDE_DIR) - endif() - - externalproject_add( - gmock - SOURCE_DIR ${GMOCK_SRC_DIR} - PREFIX ${GMOCK_ROOT} - INSTALL_COMMAND "" - LOG_DOWNLOAD ON - LOG_CONFIGURE ON - LOG_BUILD ON - CMAKE_ARGS - ${GTEST_CMAKE_ARGS} - BINARY_DIR "${GMOCK_BIN_DIR}" - BUILD_BYPRODUCTS - "${GTEST_LIBRARY}" - "${GTEST_MAIN_LIBRARY}" - "${GMOCK_LIBRARY}" - "${GMOCK_MAIN_LIBRARY}" - ) - - add_dependencies(GTest::GTest gmock) - add_dependencies(GTest::Main gmock) - add_dependencies(GMock::GMock gmock) - add_dependencies(GMock::Main gmock) - endif() -endif() - include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GTest DEFAULT_MSG GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY) find_package_handle_standard_args(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY) include(CMakeFindDependencyMacro) find_dependency(Threads) -set_target_properties(GTest::GTest PROPERTIES - INTERFACE_LINK_LIBRARIES "Threads::Threads" - IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" - IMPORTED_LOCATION "${GTEST_LIBRARY}" - ) - -if(GTEST_INCLUDE_DIR) - set_target_properties(GTest::GTest PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}" - INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}" - ) -endif() - -set_target_properties(GTest::Main PROPERTIES - INTERFACE_LINK_LIBRARIES "GTest::GTest" - IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" - IMPORTED_LOCATION "${GTEST_MAIN_LIBRARY}") - set_target_properties(GMock::GMock PROPERTIES INTERFACE_LINK_LIBRARIES "Threads::Threads" IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" @@ -477,13 +177,6 @@ if(GMOCK_INCLUDE_DIR) # so just specify it on the link interface. set_property(TARGET GMock::GMock APPEND PROPERTY INTERFACE_LINK_LIBRARIES GTest::GTest) - elseif(GTEST_INCLUDE_DIR) - # GMock 1.7 and beyond doesn't have it as a link-time dependency anymore, - # so merge it's compile-time interface (include dirs) with ours. - set_property(TARGET GMock::GMock APPEND PROPERTY - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") - set_property(TARGET GMock::GMock APPEND PROPERTY - INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") endif() endif() @@ -492,17 +185,6 @@ set_target_properties(GMock::Main PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" IMPORTED_LOCATION "${GMOCK_MAIN_LIBRARY}") -if(GTEST_FOUND) - set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIR}) - set(GTEST_LIBRARIES GTest::GTest) - set(GTEST_MAIN_LIBRARIES GTest::Main) - set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) - if(VERBOSE) - message(STATUS "GTest includes: ${GTEST_INCLUDE_DIRS}") - message(STATUS "GTest libs: ${GTEST_BOTH_LIBRARIES}") - endif() -endif() - if(GMOCK_FOUND) set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) set(GMOCK_LIBRARIES GMock::GMock) From db32b8de917b2290c679d8ded4b0057ffdd8b61e Mon Sep 17 00:00:00 2001 From: Alexander Olofsson Date: Sat, 3 Apr 2021 13:41:41 +0200 Subject: [PATCH 04/44] Return to CC0-licensed appdata metadata --- files/openmw.appdata.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/openmw.appdata.xml b/files/openmw.appdata.xml index a9906aae6..8f3865f09 100644 --- a/files/openmw.appdata.xml +++ b/files/openmw.appdata.xml @@ -5,7 +5,7 @@ Copyright 2020 Bret Curtis --> org.openmw.launcher.desktop - GPL-3.0-or-later + CC0-1.0 GPL-3.0-or-later OpenMW Unofficial open source engine re-implementation of the game Morrowind From 03fc3ec80351c4edef43fb2a34ce96e2a3d1562e Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 4 Apr 2021 22:51:21 +0200 Subject: [PATCH 05/44] Do not allow write variant of string as local variable To be consitent with read where it's not allowed. --- components/esm/variantimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index aeea5017e..2b69923d1 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -98,6 +98,9 @@ void ESM::VariantStringData::write (ESMWriter& esm, Variant::Format format, VarT if (format==Variant::Format_Info) throw std::runtime_error ("info variables of type string not supported"); + if (format==Variant::Format_Local) + throw std::runtime_error ("local variables of type string not supported"); + // GMST esm.writeHNString ("STRV", mValue); } From dae3f022baf4f33372176a2b784213d1750ddbd6 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 4 Apr 2021 20:15:58 +0200 Subject: [PATCH 06/44] Add tests for ESM::Variant --- apps/openmw_test_suite/CMakeLists.txt | 1 + apps/openmw_test_suite/esm/variant.cpp | 515 +++++++++++++++++++++++++ 2 files changed, 516 insertions(+) create mode 100644 apps/openmw_test_suite/esm/variant.cpp diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index a3bb0c6f8..d78cb6955 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -13,6 +13,7 @@ if (GTEST_FOUND AND GMOCK_FOUND) mwdialogue/test_keywordsearch.cpp esm/test_fixed_string.cpp + esm/variant.cpp misc/test_stringops.cpp misc/test_endianness.cpp diff --git a/apps/openmw_test_suite/esm/variant.cpp b/apps/openmw_test_suite/esm/variant.cpp new file mode 100644 index 000000000..c00f73d73 --- /dev/null +++ b/apps/openmw_test_suite/esm/variant.cpp @@ -0,0 +1,515 @@ +#include +#include +#include +#include + +#include +#include + +#include + +namespace +{ + using namespace testing; + using namespace ESM; + + Variant makeVariant(VarType type) + { + Variant v; + v.setType(type); + return v; + } + + Variant makeVariant(VarType type, int value) + { + Variant v; + v.setType(type); + v.setInteger(value); + return v; + } + + TEST(ESMVariantTest, move_constructed_should_have_data) + { + Variant a(int{42}); + const Variant b(std::move(a)); + ASSERT_EQ(b.getInteger(), 42); + } + + TEST(ESMVariantTest, copy_constructed_is_equal_to_source) + { + const Variant a(int{42}); + const Variant b(a); + ASSERT_EQ(a, b); + } + + TEST(ESMVariantTest, copy_constructed_does_not_share_data_with_source) + { + const Variant a(int{42}); + Variant b(a); + b.setInteger(13); + ASSERT_EQ(a.getInteger(), 42); + ASSERT_EQ(b.getInteger(), 13); + } + + TEST(ESMVariantTest, move_assigned_should_have_data) + { + Variant b; + { + Variant a(int{42}); + b = std::move(a); + } + ASSERT_EQ(b.getInteger(), 42); + } + + TEST(ESMVariantTest, copy_assigned_is_equal_to_source) + { + const Variant a(int{42}); + Variant b; + b = a; + ASSERT_EQ(a, b); + } + + TEST(ESMVariantTest, not_equal_is_negation_of_equal) + { + const Variant a(int{42}); + Variant b; + b = a; + ASSERT_TRUE(!(a != b)); + } + + TEST(ESMVariantTest, different_types_are_not_equal) + { + ASSERT_NE(Variant(int{42}), Variant(float{2.7f})); + } + + struct ESMVariantWriteToOStreamTest : TestWithParam> {}; + + TEST_P(ESMVariantWriteToOStreamTest, should_write) + { + const auto [variant, result] = GetParam(); + std::ostringstream s; + s << variant; + ASSERT_EQ(s.str(), result); + } + + INSTANTIATE_TEST_SUITE_P(VariantAsString, ESMVariantWriteToOStreamTest, Values( + std::make_tuple(Variant(), "variant none"), + std::make_tuple(Variant(int{42}), "variant long: 42"), + std::make_tuple(Variant(float{2.7f}), "variant float: 2.7"), + std::make_tuple(Variant(std::string("foo")), "variant string: \"foo\""), + std::make_tuple(makeVariant(VT_Unknown), "variant unknown"), + std::make_tuple(makeVariant(VT_Short, 42), "variant short: 42"), + std::make_tuple(makeVariant(VT_Int, 42), "variant int: 42") + )); + + struct ESMVariantGetTypeTest : Test {}; + + TEST(ESMVariantGetTypeTest, default_constructed_should_return_none) + { + ASSERT_EQ(Variant().getType(), VT_None); + } + + TEST(ESMVariantGetTypeTest, for_constructed_from_int_should_return_long) + { + ASSERT_EQ(Variant(int{}).getType(), VT_Long); + } + + TEST(ESMVariantGetTypeTest, for_constructed_from_float_should_return_float) + { + ASSERT_EQ(Variant(float{}).getType(), VT_Float); + } + + TEST(ESMVariantGetTypeTest, for_constructed_from_lvalue_string_should_return_string) + { + const std::string string; + ASSERT_EQ(Variant(string).getType(), VT_String); + } + + TEST(ESMVariantGetTypeTest, for_constructed_from_rvalue_string_should_return_string) + { + ASSERT_EQ(Variant(std::string{}).getType(), VT_String); + } + + struct ESMVariantGetIntegerTest : Test {}; + + TEST(ESMVariantGetIntegerTest, for_default_constructed_should_throw_exception) + { + ASSERT_THROW(Variant().getInteger(), std::runtime_error); + } + + TEST(ESMVariantGetIntegerTest, for_constructed_from_int_should_return_same_value) + { + const Variant variant(int{42}); + ASSERT_EQ(variant.getInteger(), 42); + } + + TEST(ESMVariantGetIntegerTest, for_constructed_from_float_should_return_casted_to_int) + { + const Variant variant(float{2.7}); + ASSERT_EQ(variant.getInteger(), 2); + } + + TEST(ESMVariantGetIntegerTest, for_constructed_from_string_should_throw_exception) + { + const Variant variant(std::string("foo")); + ASSERT_THROW(variant.getInteger(), std::runtime_error); + } + + TEST(ESMVariantGetFloatTest, for_default_constructed_should_throw_exception) + { + ASSERT_THROW(Variant().getFloat(), std::runtime_error); + } + + TEST(ESMVariantGetFloatTest, for_constructed_from_int_should_return_casted_to_float) + { + const Variant variant(int{42}); + ASSERT_EQ(variant.getFloat(), 42); + } + + TEST(ESMVariantGetFloatTest, for_constructed_from_float_should_return_same_value) + { + const Variant variant(float{2.7f}); + ASSERT_EQ(variant.getFloat(), 2.7f); + } + + TEST(ESMVariantGetFloatTest, for_constructed_from_string_should_throw_exception) + { + const Variant variant(std::string("foo")); + ASSERT_THROW(variant.getFloat(), std::runtime_error); + } + + TEST(ESMVariantGetStringTest, for_default_constructed_should_throw_exception) + { + ASSERT_THROW(Variant().getString(), std::runtime_error); + } + + TEST(ESMVariantGetStringTest, for_constructed_from_int_should_throw_exception) + { + const Variant variant(int{42}); + ASSERT_THROW(variant.getString(), std::runtime_error); + } + + TEST(ESMVariantGetStringTest, for_constructed_from_float_should_throw_exception) + { + const Variant variant(float{2.7}); + ASSERT_THROW(variant.getString(), std::runtime_error); + } + + TEST(ESMVariantGetStringTest, for_constructed_from_string_should_return_same_value) + { + const Variant variant(std::string("foo")); + ASSERT_EQ(variant.getString(), "foo"); + } + + TEST(ESMVariantSetTypeTest, for_unknown_should_reset_data) + { + Variant variant(int{42}); + variant.setType(VT_Unknown); + ASSERT_THROW(variant.getInteger(), std::runtime_error); + } + + TEST(ESMVariantSetTypeTest, for_none_should_reset_data) + { + Variant variant(int{42}); + variant.setType(VT_None); + ASSERT_THROW(variant.getInteger(), std::runtime_error); + } + + TEST(ESMVariantSetTypeTest, for_same_type_should_not_change_value) + { + Variant variant(int{42}); + variant.setType(VT_Long); + ASSERT_EQ(variant.getInteger(), 42); + } + + TEST(ESMVariantSetTypeTest, for_float_replaced_by_int_should_cast_float_to_int) + { + Variant variant(float{2.7f}); + variant.setType(VT_Int); + ASSERT_EQ(variant.getInteger(), 2); + } + + TEST(ESMVariantSetTypeTest, for_string_replaced_by_int_should_set_default_initialized_data) + { + Variant variant(std::string("foo")); + variant.setType(VT_Int); + ASSERT_EQ(variant.getInteger(), 0); + } + + TEST(ESMVariantSetTypeTest, for_default_constructed_replaced_by_float_should_set_default_initialized_value) + { + Variant variant; + variant.setType(VT_Float); + ASSERT_EQ(variant.getInteger(), 0.0f); + } + + TEST(ESMVariantSetTypeTest, for_float_replaced_by_short_should_cast_data_to_int) + { + Variant variant(float{2.7f}); + variant.setType(VT_Short); + ASSERT_EQ(variant.getInteger(), 2); + } + + TEST(ESMVariantSetTypeTest, for_float_replaced_by_long_should_cast_data_to_int) + { + Variant variant(float{2.7f}); + variant.setType(VT_Long); + ASSERT_EQ(variant.getInteger(), 2); + } + + TEST(ESMVariantSetTypeTest, for_int_replaced_by_float_should_cast_data_to_float) + { + Variant variant(int{42}); + variant.setType(VT_Float); + ASSERT_EQ(variant.getFloat(), 42.0f); + } + + TEST(ESMVariantSetTypeTest, for_int_replaced_by_string_should_set_default_initialized_data) + { + Variant variant(int{42}); + variant.setType(VT_String); + ASSERT_EQ(variant.getString(), ""); + } + + TEST(ESMVariantSetIntegerTest, for_default_constructed_should_throw_exception) + { + Variant variant; + ASSERT_THROW(variant.setInteger(42), std::runtime_error); + } + + TEST(ESMVariantSetIntegerTest, for_unknown_should_throw_exception) + { + Variant variant; + variant.setType(VT_Unknown); + ASSERT_THROW(variant.setInteger(42), std::runtime_error); + } + + TEST(ESMVariantSetIntegerTest, for_default_int_should_change_value) + { + Variant variant(int{13}); + variant.setInteger(42); + ASSERT_EQ(variant.getInteger(), 42); + } + + TEST(ESMVariantSetIntegerTest, for_int_should_change_value) + { + Variant variant; + variant.setType(VT_Int); + variant.setInteger(42); + ASSERT_EQ(variant.getInteger(), 42); + } + + TEST(ESMVariantSetIntegerTest, for_short_should_change_value) + { + Variant variant; + variant.setType(VT_Short); + variant.setInteger(42); + ASSERT_EQ(variant.getInteger(), 42); + } + + TEST(ESMVariantSetIntegerTest, for_float_should_change_value) + { + Variant variant(float{2.7f}); + variant.setInteger(42); + ASSERT_EQ(variant.getFloat(), 42.0f); + } + + TEST(ESMVariantSetIntegerTest, for_string_should_throw_exception) + { + Variant variant(std::string{}); + ASSERT_THROW(variant.setInteger(42), std::runtime_error); + } + + TEST(ESMVariantSetFloatTest, for_default_constructed_should_throw_exception) + { + Variant variant; + ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error); + } + + TEST(ESMVariantSetFloatTest, for_unknown_should_throw_exception) + { + Variant variant; + variant.setType(VT_Unknown); + ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error); + } + + TEST(ESMVariantSetFloatTest, for_default_int_should_change_value) + { + Variant variant(int{13}); + variant.setFloat(2.7f); + ASSERT_EQ(variant.getInteger(), 2); + } + + TEST(ESMVariantSetFloatTest, for_int_should_change_value) + { + Variant variant; + variant.setType(VT_Int); + variant.setFloat(2.7f); + ASSERT_EQ(variant.getInteger(), 2); + } + + TEST(ESMVariantSetFloatTest, for_short_should_change_value) + { + Variant variant; + variant.setType(VT_Short); + variant.setFloat(2.7f); + ASSERT_EQ(variant.getInteger(), 2); + } + + TEST(ESMVariantSetFloatTest, for_float_should_change_value) + { + Variant variant(float{2.7f}); + variant.setFloat(3.14f); + ASSERT_EQ(variant.getFloat(), 3.14f); + } + + TEST(ESMVariantSetFloatTest, for_string_should_throw_exception) + { + Variant variant(std::string{}); + ASSERT_THROW(variant.setFloat(2.7f), std::runtime_error); + } + + TEST(ESMVariantSetStringTest, for_default_constructed_should_throw_exception) + { + Variant variant; + ASSERT_THROW(variant.setString("foo"), std::runtime_error); + } + + TEST(ESMVariantSetStringTest, for_unknown_should_throw_exception) + { + Variant variant; + variant.setType(VT_Unknown); + ASSERT_THROW(variant.setString("foo"), std::runtime_error); + } + + TEST(ESMVariantSetStringTest, for_default_int_should_throw_exception) + { + Variant variant(int{13}); + ASSERT_THROW(variant.setString("foo"), std::runtime_error); + } + + TEST(ESMVariantSetStringTest, for_int_should_throw_exception) + { + Variant variant; + variant.setType(VT_Int); + ASSERT_THROW(variant.setString("foo"), std::runtime_error); + } + + TEST(ESMVariantSetStringTest, for_short_should_throw_exception) + { + Variant variant; + variant.setType(VT_Short); + ASSERT_THROW(variant.setString("foo"), std::runtime_error); + } + + TEST(ESMVariantSetStringTest, for_float_should_throw_exception) + { + Variant variant(float{2.7f}); + ASSERT_THROW(variant.setString("foo"), std::runtime_error); + } + + TEST(ESMVariantSetStringTest, for_string_should_change_value) + { + Variant variant(std::string("foo")); + variant.setString("bar"); + ASSERT_EQ(variant.getString(), "bar"); + } + + struct WriteToESMTestCase + { + Variant mVariant; + Variant::Format mFormat; + std::size_t mDataSize {}; + std::size_t mDataHash {}; + }; + + std::string write(const Variant& variant, const Variant::Format format) + { + std::ostringstream out; + ESM::ESMWriter writer; + writer.save(out); + variant.write(writer, format); + writer.close(); + return out.str(); + } + + Variant read(const Variant::Format format, const std::string& data) + { + Variant result; + ESM::ESMReader reader; + reader.open(std::make_shared(data), ""); + result.read(reader, format); + return result; + } + + Variant writeAndRead(const Variant& variant, const Variant::Format format, std::size_t dataSize, std::size_t dataHash) + { + const std::string data = write(variant, format); + EXPECT_EQ(data.size(), dataSize); + EXPECT_EQ(std::hash{}(data), dataHash); + return read(format, data); + } + + struct ESMVariantToESMTest : TestWithParam {}; + + TEST_P(ESMVariantToESMTest, deserialized_is_equal_to_serialized) + { + const auto param = GetParam(); + const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize, param.mDataHash); + ASSERT_EQ(param.mVariant, result); + } + + INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMTest, Values( + WriteToESMTestCase {Variant(), Variant::Format_Gmst, 324, 10398667754238537314ul}, + WriteToESMTestCase {Variant(int{42}), Variant::Format_Global, 345, 2440845426097842853ul}, + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Global, 345, 8428720798053904009ul}, + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Info, 336, 11930997575130354755ul}, + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Local, 336, 11930997575130354755ul}, + WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Global, 345, 7812065815960720679ul}, + WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Local, 334, 5017869102981712080ul}, + WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Info, 336, 12560431547347287906ul}, + WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Local, 336, 12560431547347287906ul} + )); + + struct ESMVariantToESMNoneTest : TestWithParam {}; + + TEST_P(ESMVariantToESMNoneTest, deserialized_is_none) + { + const auto param = GetParam(); + const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize, param.mDataHash); + ASSERT_EQ(Variant(), result); + } + + INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMNoneTest, Values( + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Gmst, 336, 11930997575130354755ul}, + WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Gmst, 335, 7604528240659685057ul}, + WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Gmst, 336, 12560431547347287906ul} + )); + + struct ESMVariantWriteToESMFailTest : TestWithParam {}; + + TEST_P(ESMVariantWriteToESMFailTest, write_is_not_supported) + { + const auto param = GetParam(); + std::ostringstream out; + ESM::ESMWriter writer; + writer.save(out); + ASSERT_THROW(param.mVariant.write(writer, param.mFormat), std::runtime_error); + } + + INSTANTIATE_TEST_SUITE_P(VariantAndFormat, ESMVariantWriteToESMFailTest, Values( + WriteToESMTestCase {Variant(), Variant::Format_Global}, + WriteToESMTestCase {Variant(), Variant::Format_Info}, + WriteToESMTestCase {Variant(), Variant::Format_Local}, + WriteToESMTestCase {Variant(int{42}), Variant::Format_Gmst}, + WriteToESMTestCase {Variant(int{42}), Variant::Format_Info}, + WriteToESMTestCase {Variant(int{42}), Variant::Format_Local}, + WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Global}, + WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Info}, + WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Local}, + WriteToESMTestCase {makeVariant(VT_Unknown), Variant::Format_Global}, + WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Global}, + WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Gmst}, + WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Info} + )); +} From ecde3932e273f34fb6f07ff7396a3072245540db Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 5 Apr 2021 09:43:37 +0200 Subject: [PATCH 07/44] Make AddItem's quantity overflow on negative numbers --- CHANGELOG.md | 1 + apps/openmw/mwscript/containerextensions.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1fea773e..f8cb55d36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs Bug #5912: ImprovedBound mod doesn't work Bug #5914: BM: The Swimmer can't reach destination + Bug #5934: AddItem command doesn't accept negative values Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references Feature #1536: Show more information about level on menu diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 186940dd9..375242f17 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -93,7 +93,7 @@ namespace MWScript runtime.pop(); if (count<0) - throw std::runtime_error ("second argument for AddItem must be non-negative"); + count = static_cast(count); // no-op if (count == 0) From 045bb7cbd7d206f9de9e4b624aa6dd6dc0d4d55a Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 3 Apr 2021 00:48:35 +0200 Subject: [PATCH 08/44] Store CustomData and ContainerStore as unique_ptr --- apps/openmw/mwclass/container.cpp | 8 +++--- apps/openmw/mwclass/container.hpp | 2 +- apps/openmw/mwclass/creature.cpp | 36 ++++++++++++++----------- apps/openmw/mwclass/creaturelevlist.cpp | 10 +++---- apps/openmw/mwclass/door.cpp | 9 +++---- apps/openmw/mwclass/npc.cpp | 11 ++++---- apps/openmw/mwworld/containerstore.hpp | 2 +- apps/openmw/mwworld/customdata.hpp | 4 ++- apps/openmw/mwworld/inventorystore.hpp | 2 +- apps/openmw/mwworld/refdata.cpp | 11 +++----- apps/openmw/mwworld/refdata.hpp | 5 ++-- 11 files changed, 52 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 28305c394..2d7f30047 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -46,9 +46,9 @@ namespace MWClass mStore.readState(inventory); } - MWWorld::CustomData *ContainerCustomData::clone() const + std::unique_ptr ContainerCustomData::clone() const { - return new ContainerCustomData (*this); + return std::make_unique(*this); } ContainerCustomData& ContainerCustomData::asContainerCustomData() @@ -72,7 +72,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); // store - ptr.getRefData().setCustomData (std::make_unique(*ref->mBase, ptr.getCell()).release()); + ptr.getRefData().setCustomData (std::make_unique(*ref->mBase, ptr.getCell())); MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell()); } @@ -317,7 +317,7 @@ namespace MWClass return; const ESM::ContainerState& containerState = state.asContainerState(); - ptr.getRefData().setCustomData(std::make_unique(containerState.mInventory).release()); + ptr.getRefData().setCustomData(std::make_unique(containerState.mInventory)); } void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 2dc0c06ca..fc3e046f1 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -20,7 +20,7 @@ namespace MWClass ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell); ContainerCustomData(const ESM::InventoryState& inventory); - MWWorld::CustomData *clone() const override; + std::unique_ptr clone() const override; ContainerCustomData& asContainerCustomData() override; const ContainerCustomData& asContainerCustomData() const override; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 31341db73..2a55fb9fd 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -55,10 +55,14 @@ namespace MWClass { public: MWMechanics::CreatureStats mCreatureStats; - MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures + std::unique_ptr mContainerStore; // may be InventoryStore for some creatures MWMechanics::Movement mMovement; - MWWorld::CustomData *clone() const override; + CreatureCustomData() = default; + CreatureCustomData(const CreatureCustomData& other); + CreatureCustomData(CreatureCustomData&& other) noexcept = default; + + std::unique_ptr clone() const override; CreatureCustomData& asCreatureCustomData() override { @@ -68,16 +72,18 @@ namespace MWClass { return *this; } - - CreatureCustomData() : mContainerStore(nullptr) {} - virtual ~CreatureCustomData() { delete mContainerStore; } }; - MWWorld::CustomData *CreatureCustomData::clone() const + CreatureCustomData::CreatureCustomData(const CreatureCustomData& other) + : mCreatureStats(other.mCreatureStats), + mContainerStore(other.mContainerStore->clone()), + mMovement(other.mMovement) { - CreatureCustomData* cloned = new CreatureCustomData (*this); - cloned->mContainerStore = mContainerStore->clone(); - return cloned; + } + + std::unique_ptr CreatureCustomData::clone() const + { + return std::make_unique(*this); } const Creature::GMST& Creature::getGmst() @@ -148,16 +154,16 @@ namespace MWClass // inventory bool hasInventory = hasInventoryStore(ptr); if (hasInventory) - data->mContainerStore = new MWWorld::InventoryStore(); + data->mContainerStore = std::make_unique(); else - data->mContainerStore = new MWWorld::ContainerStore(); + data->mContainerStore = std::make_unique(); data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold); data->mCreatureStats.setNeedRecalcDynamicStats(false); // store - ptr.getRefData().setCustomData(data.release()); + ptr.getRefData().setCustomData(std::move(data)); getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); @@ -758,11 +764,11 @@ namespace MWClass std::unique_ptr data (new CreatureCustomData); if (hasInventoryStore(ptr)) - data->mContainerStore = new MWWorld::InventoryStore(); + data->mContainerStore = std::make_unique(); else - data->mContainerStore = new MWWorld::ContainerStore(); + data->mContainerStore = std::make_unique(); - ptr.getRefData().setCustomData (data.release()); + ptr.getRefData().setCustomData (std::move(data)); } } else diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index e3e52901e..3b401f1a3 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -17,7 +17,7 @@ namespace MWClass int mSpawnActorId; bool mSpawn; // Should a new creature be spawned? - MWWorld::CustomData *clone() const override; + std::unique_ptr clone() const override; CreatureLevListCustomData& asCreatureLevListCustomData() override { @@ -29,9 +29,9 @@ namespace MWClass } }; - MWWorld::CustomData *CreatureLevListCustomData::clone() const + std::unique_ptr CreatureLevListCustomData::clone() const { - return new CreatureLevListCustomData (*this); + return std::make_unique(*this); } std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const @@ -138,11 +138,11 @@ namespace MWClass { if (!ptr.getRefData().getCustomData()) { - std::unique_ptr data (new CreatureLevListCustomData); + std::unique_ptr data = std::make_unique(); data->mSpawnActorId = -1; data->mSpawn = true; - ptr.getRefData().setCustomData(data.release()); + ptr.getRefData().setCustomData(std::move(data)); } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 25f7fc456..ae8085586 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -36,7 +36,7 @@ namespace MWClass public: MWWorld::DoorState mDoorState = MWWorld::DoorState::Idle; - MWWorld::CustomData *clone() const override; + std::unique_ptr clone() const override; DoorCustomData& asDoorCustomData() override { @@ -48,9 +48,9 @@ namespace MWClass } }; - MWWorld::CustomData *DoorCustomData::clone() const + std::unique_ptr DoorCustomData::clone() const { - return new DoorCustomData (*this); + return std::make_unique(*this); } void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const @@ -327,8 +327,7 @@ namespace MWClass { if (!ptr.getRefData().getCustomData()) { - std::unique_ptr data(new DoorCustomData); - ptr.getRefData().setCustomData(data.release()); + ptr.getRefData().setCustomData(std::make_unique()); } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5de4c197e..248ef5d01 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -253,7 +253,7 @@ namespace MWClass MWMechanics::Movement mMovement; MWWorld::InventoryStore mInventoryStore; - MWWorld::CustomData *clone() const override; + std::unique_ptr clone() const override; NpcCustomData& asNpcCustomData() override { @@ -265,9 +265,9 @@ namespace MWClass } }; - MWWorld::CustomData *NpcCustomData::clone() const + std::unique_ptr NpcCustomData::clone() const { - return new NpcCustomData (*this); + return std::make_unique(*this); } const Npc::GMST& Npc::getGmst() @@ -397,7 +397,7 @@ namespace MWClass data->mNpcStats.setGoldPool(gold); // store - ptr.getRefData().setCustomData (data.release()); + ptr.getRefData().setCustomData(std::move(data)); getInventoryStore(ptr).autoEquip(ptr); } @@ -1302,8 +1302,7 @@ namespace MWClass if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) - std::unique_ptr data (new NpcCustomData); - ptr.getRefData().setCustomData (data.release()); + ptr.getRefData().setCustomData(std::make_unique()); } } else diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 882f5efc4..bb8ca5273 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -153,7 +153,7 @@ namespace MWWorld virtual ~ContainerStore(); - virtual ContainerStore* clone() { return new ContainerStore(*this); } + virtual std::unique_ptr clone() { return std::make_unique(*this); } ConstContainerStoreIterator cbegin (int mask = Type_All) const; ConstContainerStoreIterator cend() const; diff --git a/apps/openmw/mwworld/customdata.hpp b/apps/openmw/mwworld/customdata.hpp index 8af45e36a..ee89dd2a8 100644 --- a/apps/openmw/mwworld/customdata.hpp +++ b/apps/openmw/mwworld/customdata.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWWORLD_CUSTOMDATA_H #define GAME_MWWORLD_CUSTOMDATA_H +#include + namespace MWClass { class CreatureCustomData; @@ -19,7 +21,7 @@ namespace MWWorld virtual ~CustomData() {} - virtual CustomData *clone() const = 0; + virtual std::unique_ptr clone() const = 0; // Fast version of dynamic_cast. Needs to be overridden in the respective class. diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 6809e63b2..32dc0d2e9 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -123,7 +123,7 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); - InventoryStore* clone() override { return new InventoryStore(*this); } + std::unique_ptr clone() override { return std::make_unique(*this); } ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool allowAutoEquip = true, bool resolve = true) override; ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 71ff6d040..f6a445d61 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -40,8 +40,6 @@ namespace MWWorld void RefData::cleanup() { mBaseNode = nullptr; - - delete mCustomData; mCustomData = nullptr; } @@ -223,21 +221,20 @@ namespace MWWorld return mPosition; } - void RefData::setCustomData (CustomData *data) + void RefData::setCustomData(std::unique_ptr&& value) noexcept { mChanged = true; // We do not currently track CustomData, so assume anything with a CustomData is changed - delete mCustomData; - mCustomData = data; + mCustomData = std::move(value); } CustomData *RefData::getCustomData() { - return mCustomData; + return mCustomData.get(); } const CustomData *RefData::getCustomData() const { - return mCustomData; + return mCustomData.get(); } bool RefData::hasChanged() const diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 738a6d53a..5419a261b 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -7,6 +7,7 @@ #include "../mwscript/locals.hpp" #include +#include namespace SceneUtil { @@ -44,7 +45,7 @@ namespace MWWorld ESM::AnimationState mAnimationState; - CustomData *mCustomData; + std::unique_ptr mCustomData; void copy (const RefData& refData); @@ -117,7 +118,7 @@ namespace MWWorld void setPosition (const ESM::Position& pos); const ESM::Position& getPosition() const; - void setCustomData (CustomData *data); + void setCustomData(std::unique_ptr&& value) noexcept; ///< Set custom data (potentially replacing old custom data). The ownership of \a data is /// transferred to this. From e380470558a1d810d7e5cd51c09a31ace376c1e6 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 3 Apr 2021 00:49:14 +0200 Subject: [PATCH 09/44] Add move ctor and assignment operator to RefData --- apps/openmw/mwworld/refdata.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 5419a261b..8979c8505 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -5,6 +5,7 @@ #include #include "../mwscript/locals.hpp" +#include "../mwworld/customdata.hpp" #include #include @@ -69,6 +70,7 @@ namespace MWWorld /// perform these operations). RefData (const RefData& refData); + RefData (RefData&& other) noexcept = default; ~RefData(); @@ -77,6 +79,7 @@ namespace MWWorld /// perform this operations). RefData& operator= (const RefData& refData); + RefData& operator= (RefData&& other) noexcept = default; /// Return base node (can be a null pointer). SceneUtil::PositionAttitudeTransform* getBaseNode(); From bd33fa76b63536398e00ba416d3289a44beaf461 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 3 Apr 2021 12:59:44 +0200 Subject: [PATCH 10/44] Use CRTP to define CustomData clone function --- apps/openmw/mwclass/container.cpp | 5 ----- apps/openmw/mwclass/container.hpp | 4 +--- apps/openmw/mwclass/creature.cpp | 9 +-------- apps/openmw/mwclass/creaturelevlist.cpp | 9 +-------- apps/openmw/mwclass/door.cpp | 9 +-------- apps/openmw/mwclass/npc.cpp | 9 +-------- apps/openmw/mwworld/customdata.hpp | 9 +++++++++ 7 files changed, 14 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 2d7f30047..de560608c 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -46,11 +46,6 @@ namespace MWClass mStore.readState(inventory); } - std::unique_ptr ContainerCustomData::clone() const - { - return std::make_unique(*this); - } - ContainerCustomData& ContainerCustomData::asContainerCustomData() { return *this; diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index fc3e046f1..1c8937006 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -13,15 +13,13 @@ namespace ESM namespace MWClass { - class ContainerCustomData : public MWWorld::CustomData + class ContainerCustomData : public MWWorld::TypedCustomData { MWWorld::ContainerStore mStore; public: ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell); ContainerCustomData(const ESM::InventoryState& inventory); - std::unique_ptr clone() const override; - ContainerCustomData& asContainerCustomData() override; const ContainerCustomData& asContainerCustomData() const override; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2a55fb9fd..6b8d2f3f2 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -51,7 +51,7 @@ namespace namespace MWClass { - class CreatureCustomData : public MWWorld::CustomData + class CreatureCustomData : public MWWorld::TypedCustomData { public: MWMechanics::CreatureStats mCreatureStats; @@ -62,8 +62,6 @@ namespace MWClass CreatureCustomData(const CreatureCustomData& other); CreatureCustomData(CreatureCustomData&& other) noexcept = default; - std::unique_ptr clone() const override; - CreatureCustomData& asCreatureCustomData() override { return *this; @@ -81,11 +79,6 @@ namespace MWClass { } - std::unique_ptr CreatureCustomData::clone() const - { - return std::make_unique(*this); - } - const Creature::GMST& Creature::getGmst() { static GMST gmst; diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 3b401f1a3..f86004c61 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -10,15 +10,13 @@ namespace MWClass { - class CreatureLevListCustomData : public MWWorld::CustomData + class CreatureLevListCustomData : public MWWorld::TypedCustomData { public: // actorId of the creature we spawned int mSpawnActorId; bool mSpawn; // Should a new creature be spawned? - std::unique_ptr clone() const override; - CreatureLevListCustomData& asCreatureLevListCustomData() override { return *this; @@ -29,11 +27,6 @@ namespace MWClass } }; - std::unique_ptr CreatureLevListCustomData::clone() const - { - return std::make_unique(*this); - } - std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const { return ""; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index ae8085586..3a5ff0d9a 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -31,13 +31,11 @@ namespace MWClass { - class DoorCustomData : public MWWorld::CustomData + class DoorCustomData : public MWWorld::TypedCustomData { public: MWWorld::DoorState mDoorState = MWWorld::DoorState::Idle; - std::unique_ptr clone() const override; - DoorCustomData& asDoorCustomData() override { return *this; @@ -48,11 +46,6 @@ namespace MWClass } }; - std::unique_ptr DoorCustomData::clone() const - { - return std::make_unique(*this); - } - void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 248ef5d01..c5b352cb4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -246,15 +246,13 @@ namespace namespace MWClass { - class NpcCustomData : public MWWorld::CustomData + class NpcCustomData : public MWWorld::TypedCustomData { public: MWMechanics::NpcStats mNpcStats; MWMechanics::Movement mMovement; MWWorld::InventoryStore mInventoryStore; - std::unique_ptr clone() const override; - NpcCustomData& asNpcCustomData() override { return *this; @@ -265,11 +263,6 @@ namespace MWClass } }; - std::unique_ptr NpcCustomData::clone() const - { - return std::make_unique(*this); - } - const Npc::GMST& Npc::getGmst() { static GMST gmst; diff --git a/apps/openmw/mwworld/customdata.hpp b/apps/openmw/mwworld/customdata.hpp index ee89dd2a8..7200e7684 100644 --- a/apps/openmw/mwworld/customdata.hpp +++ b/apps/openmw/mwworld/customdata.hpp @@ -40,6 +40,15 @@ namespace MWWorld virtual MWClass::CreatureLevListCustomData& asCreatureLevListCustomData(); virtual const MWClass::CreatureLevListCustomData& asCreatureLevListCustomData() const; }; + + template + struct TypedCustomData : CustomData + { + std::unique_ptr clone() const final + { + return std::make_unique(*static_cast(this)); + } + }; } #endif From 59720aea9a2eeed93e47974016a11012fd01643b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 7 Apr 2021 12:07:03 +0400 Subject: [PATCH 11/44] Restore old aiming for melee combat --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 6 +++--- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 10 ++++------ apps/openmw/mwworld/worldimp.hpp | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 5e844ffae..3d55ad987 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -607,7 +607,7 @@ namespace MWBase /// Return a vector aiming the actor's weapon towards a target. /// @note The length of the vector is the distance between actor and target. - virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0; + virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) = 0; /// Return the distance between actor's weapon and target's collision box. virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 51fcb92c1..5edd59ded 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -228,7 +228,6 @@ namespace MWMechanics const osg::Vec3f vActorPos(pos.asVec3()); const osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3()); - osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target); storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS); @@ -236,13 +235,14 @@ namespace MWMechanics if (isRangedCombat) { // rotate actor taking into account target movement direction and projectile speed - vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength); + osg::Vec3f vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength); storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir); storage.mMovement.mRotation[2] = getZAngleToDir(vAimDir); } else { + osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, false); storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir); storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated } @@ -698,7 +698,7 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same osg::Vec3f vTargetPos = target.getRefData().getPosition().asVec3(); - osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target); + osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, true); float distToTarget = vDirToTarget.length(); osg::Vec3f vTargetMoveDir = vTargetPos - vLastTargetPos; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8c58fd943..b83d313ce 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2955,7 +2955,7 @@ void CharacterController::updateHeadTracking(float duration) } else // no head node to look at, fall back to look at center of collision box - direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget); + direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget, false); } direction.normalize(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5e0cdbb2b..3b50dd6aa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3885,14 +3885,12 @@ namespace MWWorld return false; } - osg::Vec3f World::aimToTarget(const ConstPtr &actor, const ConstPtr &target) + osg::Vec3f World::aimToTarget(const ConstPtr &actor, const ConstPtr &target, bool isRangedCombat) { osg::Vec3f weaponPos = actor.getRefData().getPosition().asVec3(); - osg::Vec3f weaponHalfExtents = mPhysics->getHalfExtents(actor); - osg::Vec3f targetPos = target.getRefData().getPosition().asVec3(); - osg::Vec3f targetHalfExtents = mPhysics->getHalfExtents(target); - weaponPos.z() += weaponHalfExtents.z() * 2 * Constants::TorsoHeight; - targetPos.z() += targetHalfExtents.z(); + float heightRatio = isRangedCombat ? 2.f * Constants::TorsoHeight : 1.f; + weaponPos.z() += mPhysics->getHalfExtents(actor).z() * heightRatio; + osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target); return (targetPos - weaponPos); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e2d344930..929e035e9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -705,7 +705,7 @@ namespace MWWorld /// Return a vector aiming the actor's weapon towards a target. /// @note The length of the vector is the distance between actor and target. - osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) override; + osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) override; /// Return the distance between actor's weapon and target's collision box. float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) override; From 44f2cb092346e9588e4e9d943548afaa65c90ee4 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 7 Apr 2021 17:30:21 +0200 Subject: [PATCH 12/44] Fix targeted scripts losing their targets when rearranging your load order --- apps/openmw/mwscript/globalscripts.cpp | 9 ++++++++- apps/openmw/mwscript/globalscripts.hpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 1a7e3ebbc..0d579abdc 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -235,13 +235,20 @@ namespace MWScript } } - bool GlobalScripts::readRecord (ESM::ESMReader& reader, uint32_t type) + bool GlobalScripts::readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap) { if (type==ESM::REC_GSCR) { ESM::GlobalScript script; script.load (reader); + if (script.mTargetRef.hasContentFile()) + { + auto iter = contentFileMap.find(script.mTargetRef.mContentFile); + if (iter != contentFileMap.end()) + script.mTargetRef.mContentFile = iter->second; + } + auto iter = mScripts.find (script.mId); if (iter==mScripts.end()) diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index c5c5a9a45..049e78804 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -73,7 +73,7 @@ namespace MWScript void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; - bool readRecord (ESM::ESMReader& reader, uint32_t type); + bool readRecord (ESM::ESMReader& reader, uint32_t type, const std::map& contentFileMap); ///< Records for variables that do not exist are dropped silently. /// /// \return Known type? diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 93f75c6f1..fb418b94a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -461,7 +461,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str case ESM::REC_GSCR: - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.intval); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.intval, contentFileMap); break; case ESM::REC_GMAP: From 3ad2335d115c7a4baa2290554ed998651c072cb9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 7 Apr 2021 20:23:39 +0000 Subject: [PATCH 13/44] Fix FetchContent Bullet with MSVC --- extern/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index fc00ae254..3842b8357 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -35,9 +35,13 @@ if(NOT OPENMW_USE_SYSTEM_BULLET) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) else() set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE) - if(MSVC) - set(USE_MSVC_RUNTIME_LIBRARY_DLL ON CACHE BOOL "" FORCE) - endif() + endif() + + if(MSVC) + # this setting is badly named - having it off forces the static runtime library, + # but having it on does nothing, letting the defaults get used. + # OpenMW uses the defaults, and you can't mix and match. + set(USE_MSVC_RUNTIME_LIBRARY_DLL ON CACHE BOOL "" FORCE) endif() # master on 12 Mar 2021 From d617d66a87fb58524f6a330b2c9e73559af192a0 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Wed, 7 Apr 2021 18:58:46 +0200 Subject: [PATCH 14/44] Add file to BSA --- apps/bsatool/bsatool.cpp | 43 +++++++- components/bsa/bsa_file.cpp | 156 ++++++++++++++++++++++++++- components/bsa/bsa_file.hpp | 41 +++++-- components/bsa/compressedbsafile.cpp | 19 ++-- components/bsa/compressedbsafile.hpp | 2 +- components/vfs/bsaarchive.cpp | 4 +- 6 files changed, 238 insertions(+), 27 deletions(-) diff --git a/apps/bsatool/bsatool.cpp b/apps/bsatool/bsatool.cpp index 3afbd777f..8e8cf8918 100644 --- a/apps/bsatool/bsatool.cpp +++ b/apps/bsatool/bsatool.cpp @@ -20,6 +20,7 @@ struct Arguments std::string mode; std::string filename; std::string extractfile; + std::string addfile; std::string outdir; bool longformat; @@ -36,6 +37,10 @@ bool parseOptions (int argc, char** argv, Arguments &info) " Extract a file from the input archive.\n\n" " bsatool extractall archivefile [output_directory]\n" " Extract all files from the input archive.\n\n" + " bsatool add [-a] archivefile file_to_add\n" + " Add a file to the input archive.\n\n" + " bsatool create [-c] archivefile\n" + " Create an archive.\n\n" "Allowed options"); desc.add_options() @@ -95,7 +100,7 @@ bool parseOptions (int argc, char** argv, Arguments &info) } info.mode = variables["mode"].as(); - if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall")) + if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall" || info.mode == "add" || info.mode == "create")) { std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n" << desc << std::endl; @@ -126,6 +131,17 @@ bool parseOptions (int argc, char** argv, Arguments &info) if (variables["input-file"].as< std::vector >().size() > 2) info.outdir = variables["input-file"].as< std::vector >()[2]; } + else if (info.mode == "add") + { + if (variables["input-file"].as< std::vector >().size() < 1) + { + std::cout << "\nERROR: file to add unspecified\n\n" + << desc << std::endl; + return false; + } + if (variables["input-file"].as< std::vector >().size() > 1) + info.addfile = variables["input-file"].as< std::vector >()[1]; + } else if (variables["input-file"].as< std::vector >().size() > 1) info.outdir = variables["input-file"].as< std::vector >()[1]; @@ -138,6 +154,7 @@ bool parseOptions (int argc, char** argv, Arguments &info) int list(std::unique_ptr& bsa, Arguments& info); int extract(std::unique_ptr& bsa, Arguments& info); int extractAll(std::unique_ptr& bsa, Arguments& info); +int add(std::unique_ptr& bsa, Arguments& info); int main(int argc, char** argv) { @@ -157,6 +174,12 @@ int main(int argc, char** argv) else bsa = std::make_unique(Bsa::BSAFile()); + if (info.mode == "create") + { + bsa->open(info.filename); + return 0; + } + bsa->open(info.filename); if (info.mode == "list") @@ -165,6 +188,8 @@ int main(int argc, char** argv) return extract(bsa, info); else if (info.mode == "extractall") return extractAll(bsa, info); + else if (info.mode == "add") + return add(bsa, info); else { std::cout << "Unsupported mode. That is not supposed to happen." << std::endl; @@ -188,13 +213,13 @@ int list(std::unique_ptr& bsa, Arguments& info) { // Long format std::ios::fmtflags f(std::cout.flags()); - std::cout << std::setw(50) << std::left << file.name; + std::cout << std::setw(50) << std::left << file.name(); std::cout << std::setw(8) << std::left << std::dec << file.fileSize; std::cout << "@ 0x" << std::hex << file.offset << std::endl; std::cout.flags(f); } else - std::cout << file.name << std::endl; + std::cout << file.name() << std::endl; } return 0; @@ -253,7 +278,7 @@ int extractAll(std::unique_ptr& bsa, Arguments& info) { for (const auto &file : bsa->getList()) { - std::string extractPath(file.name); + std::string extractPath(file.name()); Misc::StringUtils::replaceAll(extractPath, "\\", "/"); // Get the target path (the path the file will be extracted to) @@ -272,7 +297,7 @@ int extractAll(std::unique_ptr& bsa, Arguments& info) // Get a stream for the file to extract // (inefficient because getFile iter on the list again) - Files::IStreamPtr data = bsa->getFile(file.name); + Files::IStreamPtr data = bsa->getFile(file.name()); bfs::ofstream out(target, std::ios::binary); // Write the file to disk @@ -283,3 +308,11 @@ int extractAll(std::unique_ptr& bsa, Arguments& info) return 0; } + +int add(std::unique_ptr& bsa, Arguments& info) +{ + boost::filesystem::fstream stream(info.addfile, std::ios_base::binary | std::ios_base::out | std::ios_base::in); + bsa->addFile(info.addfile, stream); + + return 0; +} diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index f6220b7ce..ef49a60d2 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -27,6 +27,7 @@ #include #include +#include using namespace Bsa; @@ -37,6 +38,31 @@ void BSAFile::fail(const std::string &msg) throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + mFilename); } +//the getHash code is from bsapack from ghostwheel +//the code is also the same as in https://github.com/arviceblot/bsatool_rs/commit/67cb59ec3aaeedc0849222ea387f031c33e48c81 +BSAFile::Hash getHash(const std::string& name) +{ + BSAFile::Hash hash; + unsigned l = (name.size() >> 1); + unsigned sum, off, temp, i, n; + + for (sum = off = i = 0; i < l; i++) { + sum ^= (((unsigned)(name[i])) << (off & 0x1F)); + off += 8; + } + hash.low = sum; + + for (sum = off = 0; i < name.size(); i++) { + temp = (((unsigned)(name[i])) << (off & 0x1F)); + sum ^= temp; + n = temp & 0x1F; + sum = (sum << (32 - n)) | (sum >> n); // binary "rotate right" + off += 8; + } + hash.high = sum; + return hash; +} + /// Read header information from the input source void BSAFile::readHeader() { @@ -113,14 +139,17 @@ void BSAFile::readHeader() // Read the offset info into a temporary buffer std::vector offsets(3*filenum); - input.read(reinterpret_cast(&offsets[0]), 12*filenum); + input.read(reinterpret_cast(offsets.data()), 12*filenum); // Read the string table mStringBuf.resize(dirsize-12*filenum); - input.read(&mStringBuf[0], mStringBuf.size()); + input.read(mStringBuf.data(), mStringBuf.size()); // Check our position assert(input.tellg() == std::streampos(12+dirsize)); + std::vector hashes(filenum); + static_assert(sizeof(Hash) == 8); + input.read(reinterpret_cast(hashes.data()), 8*filenum); // Calculate the offset of the data buffer. All file offsets are // relative to this. 12 header bytes + directory + hash table @@ -129,23 +158,72 @@ void BSAFile::readHeader() // Set up the the FileStruct table mFiles.resize(filenum); + size_t endOfNameBuffer = 0; for(size_t i=0;i fsize) fail("Archive contains offsets outside itself"); // Add the file name to the lookup - mLookup[fs.name] = i; + mLookup[fs.name()] = i; } + mStringBuf.resize(endOfNameBuffer); + + std::sort(mFiles.begin(), mFiles.end(), [](const FileStruct& left, const FileStruct& right) { + return left.offset < right.offset; + }); mIsLoaded = true; } +/// Write header information to the output sink +void Bsa::BSAFile::writeHeader() +{ + namespace bfs = boost::filesystem; + bfs::fstream output(mFilename, std::ios::binary | std::ios::in | std::ios::out); + + uint32_t head[3]; + head[0] = 0x100; + auto fileDataOffset = mFiles.empty() ? 12 : mFiles.front().offset; + head[1] = fileDataOffset - 12 - 8*mFiles.size(); + + output.seekp(0, std::ios_base::end); + + head[2] = mFiles.size(); + output.seekp(0); + output.write(reinterpret_cast(head), 12); + + std::sort(mFiles.begin(), mFiles.end(), [](const FileStruct& left, const FileStruct& right) { + return std::make_pair(left.hash.low, left.hash.high) < std::make_pair(right.hash.low, right.hash.high); + }); + + size_t filenum = mFiles.size(); + std::vector offsets(3* filenum); + std::vector hashes(filenum); + for(size_t i=0;i(offsets.data()), sizeof(uint32_t)*offsets.size()); + output.write(reinterpret_cast(mStringBuf.data()), mStringBuf.size()); + output.seekp(fileDataOffset - 8*mFiles.size(), std::ios_base::beg); + output.write(reinterpret_cast(hashes.data()), sizeof(Hash)*hashes.size()); +} + /// Get the index of a given file name, or -1 if not found int BSAFile::getIndex(const char *str) const { @@ -162,7 +240,22 @@ int BSAFile::getIndex(const char *str) const void BSAFile::open(const std::string &file) { mFilename = file; - readHeader(); + if(boost::filesystem::exists(file)) + readHeader(); + else + { + { boost::filesystem::fstream(mFilename, std::ios::binary | std::ios::out); } + writeHeader(); + } +} + +/// Close the archive, write the updated headers to the file +void Bsa::BSAFile::close() +{ + if (!mHasChanged) + return; + + writeHeader(); } Files::IStreamPtr BSAFile::getFile(const char *file) @@ -181,3 +274,56 @@ Files::IStreamPtr BSAFile::getFile(const FileStruct *file) { return Files::openConstrainedFileStream (mFilename.c_str (), file->offset, file->fileSize); } + +void Bsa::BSAFile::addFile(const std::string& filename, std::istream& file) +{ + namespace bfs = boost::filesystem; + + auto newStartOfDataBuffer = 12 + (12 + 8) * (mFiles.size() + 1) + mStringBuf.size() + filename.size() + 1; + if (mFiles.empty()) + bfs::resize_file(mFilename, newStartOfDataBuffer); + + bfs::fstream stream(mFilename, std::ios::binary | std::ios::in | std::ios::out); + + FileStruct newFile; + file.seekg(0, std::ios::end); + newFile.fileSize = file.tellg(); + newFile.setNameInfos(mStringBuf.size(), &mStringBuf); + newFile.hash = getHash(filename); + + if(mFiles.empty()) + newFile.offset = newStartOfDataBuffer; + else + { + std::vector buffer; + while (mFiles.front().offset < newStartOfDataBuffer) { + FileStruct& firstFile = mFiles.front(); + buffer.resize(firstFile.fileSize); + + stream.seekg(firstFile.offset, std::ios::beg); + stream.read(buffer.data(), firstFile.fileSize); + + stream.seekp(0, std::ios::end); + firstFile.offset = stream.tellp(); + + stream.write(buffer.data(), firstFile.fileSize); + + //ensure sort order is preserved + std::rotate(mFiles.begin(), mFiles.begin() + 1, mFiles.end()); + } + stream.seekp(0, std::ios::end); + newFile.offset = stream.tellp(); + } + + mStringBuf.insert(mStringBuf.end(), filename.begin(), filename.end()); + mStringBuf.push_back('\0'); + mFiles.push_back(newFile); + + mHasChanged = true; + + mLookup[filename.c_str()] = mFiles.size() - 1; + + stream.seekp(0, std::ios::end); + file.seekg(0, std::ios::beg); + stream << file.rdbuf(); +} diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index 3e7538401..fa6e5fc1c 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -43,20 +43,42 @@ namespace Bsa class BSAFile { public: + + #pragma pack(push) + #pragma pack(1) + struct Hash + { + uint32_t low, high; + }; + #pragma pack(pop) + /// Represents one file entry in the archive struct FileStruct { + void setNameInfos(size_t index, + std::vector* stringBuf + ) { + namesOffset = index; + namesBuffer = stringBuf; + } + // File size and offset in file. We store the offset from the // beginning of the file, not the offset into the data buffer // (which is what is stored in the archive.) uint32_t fileSize, offset; + Hash hash; // Zero-terminated file name - const char *name; + const char* name() const { return &(*namesBuffer)[namesOffset]; }; + + uint32_t namesOffset = 0; + std::vector* namesBuffer = nullptr; }; typedef std::vector FileList; protected: + bool mHasChanged = false; + /// Table of files in this archive FileList mFiles; @@ -72,7 +94,7 @@ protected: /// Case insensitive string comparison struct iltstr { - bool operator()(const char *s1, const char *s2) const + bool operator()(const std::string& s1, const std::string& s2) const { return Misc::StringUtils::ciLess(s1, s2); } }; @@ -80,7 +102,7 @@ protected: the files[] vector above. The iltstr ensures that file name checks are case insensitive. */ - typedef std::map Lookup; + typedef std::map Lookup; Lookup mLookup; /// Error handling @@ -88,9 +110,7 @@ protected: /// Read header information from the input source virtual void readHeader(); - - /// Read header information from the input source - + virtual void writeHeader(); /// Get the index of a given file name, or -1 if not found /// @note Thread safe. @@ -106,11 +126,16 @@ public: : mIsLoaded(false) { } - virtual ~BSAFile() = default; + virtual ~BSAFile() + { + close(); + } /// Open an archive file. void open(const std::string &file); + void close(); + /* ----------------------------------- * Archive file routines * ----------------------------------- @@ -131,6 +156,8 @@ public: */ virtual Files::IStreamPtr getFile(const FileStruct* file); + virtual void addFile(const std::string& filename, std::istream& file); + /// Get a list of all files /// @note Thread safe. const FileList &getList() const diff --git a/components/bsa/compressedbsafile.cpp b/components/bsa/compressedbsafile.cpp index 77e477ac5..aaeb5bffa 100644 --- a/components/bsa/compressedbsafile.cpp +++ b/components/bsa/compressedbsafile.cpp @@ -226,7 +226,6 @@ void CompressedBSAFile::readHeader() FileStruct fileStruct{}; fileStruct.fileSize = file.getSizeWithoutCompressionFlag(); fileStruct.offset = file.offset; - fileStruct.name = nullptr; mFiles.push_back(fileStruct); fullPaths.push_back(folder); @@ -249,7 +248,7 @@ void CompressedBSAFile::readHeader() } //The vector guarantees that its elements occupy contiguous memory - mFiles[fileIndex].name = reinterpret_cast(mStringBuf.data() + mStringBuffOffset); + mFiles[fileIndex].setNameInfos(mStringBuffOffset, &mStringBuf); fullPaths.at(fileIndex) += "\\" + std::string(mStringBuf.data() + mStringBuffOffset); @@ -276,7 +275,7 @@ void CompressedBSAFile::readHeader() fullPaths.at(fileIndex).c_str() + stringLength + 1u, mStringBuf.data() + mStringBuffOffset); - mFiles[fileIndex].name = reinterpret_cast(mStringBuf.data() + mStringBuffOffset); + mFiles[fileIndex].setNameInfos(mStringBuffOffset, &mStringBuf); mLookup[reinterpret_cast(mStringBuf.data() + mStringBuffOffset)] = fileIndex; mStringBuffOffset += stringLength + 1u; @@ -320,13 +319,19 @@ CompressedBSAFile::FileRecord CompressedBSAFile::getFileRecord(const std::string Files::IStreamPtr CompressedBSAFile::getFile(const FileStruct* file) { - FileRecord fileRec = getFileRecord(file->name); + FileRecord fileRec = getFileRecord(file->name()); if (!fileRec.isValid()) { - fail("File not found: " + std::string(file->name)); + fail("File not found: " + std::string(file->name())); } return getFile(fileRec); } +void CompressedBSAFile::addFile(const std::string& filename, std::istream& file) +{ + assert(false); //not implemented yet + fail("Add file is not implemented for compressed BSA: " + filename); +} + Files::IStreamPtr CompressedBSAFile::getFile(const char* file) { FileRecord fileRec = getFileRecord(file); @@ -430,10 +435,10 @@ void CompressedBSAFile::convertCompressedSizesToUncompressed() { for (auto & mFile : mFiles) { - const FileRecord& fileRecord = getFileRecord(mFile.name); + const FileRecord& fileRecord = getFileRecord(mFile.name()); if (!fileRecord.isValid()) { - fail("Could not find file " + std::string(mFile.name) + " in BSA"); + fail("Could not find file " + std::string(mFile.name()) + " in BSA"); } if (!fileRecord.isCompressed(mCompressedByDefault)) diff --git a/components/bsa/compressedbsafile.hpp b/components/bsa/compressedbsafile.hpp index deddfae38..215a1fc49 100644 --- a/components/bsa/compressedbsafile.hpp +++ b/components/bsa/compressedbsafile.hpp @@ -94,7 +94,7 @@ namespace Bsa Files::IStreamPtr getFile(const char* filePath) override; Files::IStreamPtr getFile(const FileStruct* fileStruct) override; - + void addFile(const std::string& filename, std::istream& file) override; }; } diff --git a/components/vfs/bsaarchive.cpp b/components/vfs/bsaarchive.cpp index e6d779aab..90899ac61 100644 --- a/components/vfs/bsaarchive.cpp +++ b/components/vfs/bsaarchive.cpp @@ -32,7 +32,7 @@ void BsaArchive::listResources(std::map &out, char (*normal { for (std::vector::iterator it = mResources.begin(); it != mResources.end(); ++it) { - std::string ent = it->mInfo->name; + std::string ent = it->mInfo->name(); std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function); out[ent] = &*it; @@ -43,7 +43,7 @@ bool BsaArchive::contains(const std::string& file, char (*normalize_function)(ch { for (const auto& it : mResources) { - std::string ent = it.mInfo->name; + std::string ent = it.mInfo->name(); std::transform(ent.begin(), ent.end(), ent.begin(), normalize_function); if(file == ent) return true; From 2cb4b62b837353addec6bd48b220b9ccd1d4e232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Lamut?= Date: Thu, 8 Apr 2021 16:32:38 +0000 Subject: [PATCH 15/44] De-hardcode references to mesh files used by the sky. --- apps/openmw/mwrender/sky.cpp | 35 +++++++++++++++++++---------------- files/settings-default.cfg | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 5a6ec06e5..2f2882368 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -45,6 +45,9 @@ #include #include +#include +#include + #include #include "../mwbase/environment.hpp" @@ -1164,7 +1167,7 @@ void SkyManager::create() { assert(!mCreated); - mAtmosphereDay = mSceneManager->getInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot); + mAtmosphereDay = mSceneManager->getInstance(Settings::Manager::getString("skyatmosphere", "Models"), mEarlyRenderBinRoot); ModVertexAlphaVisitor modAtmosphere(0); mAtmosphereDay->accept(modAtmosphere); @@ -1176,10 +1179,10 @@ void SkyManager::create() mEarlyRenderBinRoot->addChild(mAtmosphereNightNode); osg::ref_ptr atmosphereNight; - if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) - atmosphereNight = mSceneManager->getInstance("meshes/sky_night_02.nif", mAtmosphereNightNode); + if (mSceneManager->getVFS()->exists(Settings::Manager::getString("skynight02", "Models"))) + atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString("skynight02", "Models"), mAtmosphereNightNode); else - atmosphereNight = mSceneManager->getInstance("meshes/sky_night_01.nif", mAtmosphereNightNode); + atmosphereNight = mSceneManager->getInstance(Settings::Manager::getString("skynight01", "Models"), mAtmosphereNightNode); atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); ModVertexAlphaVisitor modStars(2); atmosphereNight->accept(modStars); @@ -1193,14 +1196,14 @@ void SkyManager::create() mCloudNode = new osg::PositionAttitudeTransform; mEarlyRenderBinRoot->addChild(mCloudNode); - mCloudMesh = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode); + mCloudMesh = mSceneManager->getInstance(Settings::Manager::getString("skyclouds", "Models"), mCloudNode); ModVertexAlphaVisitor modClouds(1); mCloudMesh->accept(modClouds); mCloudUpdater = new CloudUpdater; mCloudUpdater->setOpacity(1.f); mCloudMesh->addUpdateCallback(mCloudUpdater); - mCloudMesh2 = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode); + mCloudMesh2 = mSceneManager->getInstance(Settings::Manager::getString("skyclouds", "Models"), mCloudNode); mCloudMesh2->accept(modClouds); mCloudUpdater2 = new CloudUpdater; mCloudUpdater2->setOpacity(0.f); @@ -1597,7 +1600,7 @@ void SkyManager::update(float duration) if (mParticleNode) { // Morrowind deliberately rotates the blizzard mesh, so so should we. - if (mCurrentParticleEffect == "meshes\\blizzard.nif") + if (mCurrentParticleEffect == Settings::Manager::getString("weatherblizzard", "Models")) quat.makeRotate(osg::Vec3f(-1,0,0), mStormDirection); mParticleNode->setAttitude(quat); } @@ -1897,16 +1900,16 @@ void SkyManager::setWaterHeight(float height) void SkyManager::listAssetsToPreload(std::vector& models, std::vector& textures) { - models.emplace_back("meshes/sky_atmosphere.nif"); - if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) - models.emplace_back("meshes/sky_night_02.nif"); - models.emplace_back("meshes/sky_night_01.nif"); - models.emplace_back("meshes/sky_clouds_01.nif"); + models.emplace_back(Settings::Manager::getString("skyatmosphere", "Models")); + if (mSceneManager->getVFS()->exists(Settings::Manager::getString("skynight02", "Models"))) + models.emplace_back(Settings::Manager::getString("skynight02", "Models")); + models.emplace_back(Settings::Manager::getString("skynight01", "Models")); + models.emplace_back(Settings::Manager::getString("skyclouds", "Models")); - models.emplace_back("meshes\\ashcloud.nif"); - models.emplace_back("meshes\\blightcloud.nif"); - models.emplace_back("meshes\\snow.nif"); - models.emplace_back("meshes\\blizzard.nif"); + models.emplace_back(Settings::Manager::getString("weatherashcloud", "Models")); + models.emplace_back(Settings::Manager::getString("weatherblightcloud", "Models")); + models.emplace_back(Settings::Manager::getString("weathersnow", "Models")); + models.emplace_back(Settings::Manager::getString("weatherblizzard", "Models")); textures.emplace_back("textures/tx_mooncircle_full_s.dds"); textures.emplace_back("textures/tx_mooncircle_full_m.dds"); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index d439c4635..78487b173 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1005,6 +1005,30 @@ xbaseanimfemalekf = meshes/xbase_anim_female.kf # File to load xargonianswimkna animations from xargonianswimknakf = meshes/xargonian_swimkna.kf +# Sky atmosphere mesh +skyatmosphere = meshes/sky_atmosphere.nif + +# Sky clouds mesh +skyclouds = meshes/sky_clouds_01.nif + +# Sky stars mesh 01 +skynight01 = meshes/sky_night_01.nif + +# Sky stars mesh 02 +skynight02 = meshes/sky_night_02.nif + +# Ash clouds weather effect +weatherashcloud = meshes/ashcloud.nif + +# Blight clouds weather effect +weatherblightcloud = meshes/blightcloud.nif + +# Snow falling weather effect +weathersnow = meshes/snow.nif + +# Blizzard weather effect +weatherblizzard = meshes/blizzard.nif + [Groundcover] # enable separate groundcover handling From 7f577f5f08306058a434aa1fc90a54b0260b5391 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 8 Apr 2021 19:38:42 +0200 Subject: [PATCH 16/44] Do not compare hash in tests Different std libraries have different implementation that produce different results for the same values. --- apps/openmw_test_suite/esm/variant.cpp | 32 ++++++++++++-------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/apps/openmw_test_suite/esm/variant.cpp b/apps/openmw_test_suite/esm/variant.cpp index c00f73d73..9615222fe 100644 --- a/apps/openmw_test_suite/esm/variant.cpp +++ b/apps/openmw_test_suite/esm/variant.cpp @@ -420,7 +420,6 @@ namespace Variant mVariant; Variant::Format mFormat; std::size_t mDataSize {}; - std::size_t mDataHash {}; }; std::string write(const Variant& variant, const Variant::Format format) @@ -442,11 +441,10 @@ namespace return result; } - Variant writeAndRead(const Variant& variant, const Variant::Format format, std::size_t dataSize, std::size_t dataHash) + Variant writeAndRead(const Variant& variant, const Variant::Format format, std::size_t dataSize) { const std::string data = write(variant, format); EXPECT_EQ(data.size(), dataSize); - EXPECT_EQ(std::hash{}(data), dataHash); return read(format, data); } @@ -455,20 +453,20 @@ namespace TEST_P(ESMVariantToESMTest, deserialized_is_equal_to_serialized) { const auto param = GetParam(); - const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize, param.mDataHash); + const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize); ASSERT_EQ(param.mVariant, result); } INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMTest, Values( - WriteToESMTestCase {Variant(), Variant::Format_Gmst, 324, 10398667754238537314ul}, - WriteToESMTestCase {Variant(int{42}), Variant::Format_Global, 345, 2440845426097842853ul}, - WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Global, 345, 8428720798053904009ul}, - WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Info, 336, 11930997575130354755ul}, - WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Local, 336, 11930997575130354755ul}, - WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Global, 345, 7812065815960720679ul}, - WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Local, 334, 5017869102981712080ul}, - WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Info, 336, 12560431547347287906ul}, - WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Local, 336, 12560431547347287906ul} + WriteToESMTestCase {Variant(), Variant::Format_Gmst, 324}, + WriteToESMTestCase {Variant(int{42}), Variant::Format_Global, 345}, + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Global, 345}, + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Info, 336}, + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Local, 336}, + WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Global, 345}, + WriteToESMTestCase {makeVariant(VT_Short, 42), Variant::Format_Local, 334}, + WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Info, 336}, + WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Local, 336} )); struct ESMVariantToESMNoneTest : TestWithParam {}; @@ -476,14 +474,14 @@ namespace TEST_P(ESMVariantToESMNoneTest, deserialized_is_none) { const auto param = GetParam(); - const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize, param.mDataHash); + const auto result = writeAndRead(param.mVariant, param.mFormat, param.mDataSize); ASSERT_EQ(Variant(), result); } INSTANTIATE_TEST_SUITE_P(VariantAndData, ESMVariantToESMNoneTest, Values( - WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Gmst, 336, 11930997575130354755ul}, - WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Gmst, 335, 7604528240659685057ul}, - WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Gmst, 336, 12560431547347287906ul} + WriteToESMTestCase {Variant(float{2.7f}), Variant::Format_Gmst, 336}, + WriteToESMTestCase {Variant(std::string("foo")), Variant::Format_Gmst, 335}, + WriteToESMTestCase {makeVariant(VT_Int, 42), Variant::Format_Gmst, 336} )); struct ESMVariantWriteToESMFailTest : TestWithParam {}; From 8e1c92d9afa951e1dd4195e81a6161266fbef6e0 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 4 Apr 2021 19:17:10 +0200 Subject: [PATCH 17/44] Use std::variant for ESM::Variant implementation --- apps/openmw_test_suite/esm/variant.cpp | 18 +-- components/esm/variant.cpp | 177 ++++++----------------- components/esm/variant.hpp | 38 ++--- components/esm/variantimp.cpp | 188 +++--------------------- components/esm/variantimp.hpp | 193 +++++-------------------- 5 files changed, 133 insertions(+), 481 deletions(-) diff --git a/apps/openmw_test_suite/esm/variant.cpp b/apps/openmw_test_suite/esm/variant.cpp index 9615222fe..10d35e486 100644 --- a/apps/openmw_test_suite/esm/variant.cpp +++ b/apps/openmw_test_suite/esm/variant.cpp @@ -180,19 +180,19 @@ namespace TEST(ESMVariantGetStringTest, for_default_constructed_should_throw_exception) { - ASSERT_THROW(Variant().getString(), std::runtime_error); + ASSERT_THROW(Variant().getString(), std::bad_variant_access); } TEST(ESMVariantGetStringTest, for_constructed_from_int_should_throw_exception) { const Variant variant(int{42}); - ASSERT_THROW(variant.getString(), std::runtime_error); + ASSERT_THROW(variant.getString(), std::bad_variant_access); } TEST(ESMVariantGetStringTest, for_constructed_from_float_should_throw_exception) { const Variant variant(float{2.7}); - ASSERT_THROW(variant.getString(), std::runtime_error); + ASSERT_THROW(variant.getString(), std::bad_variant_access); } TEST(ESMVariantGetStringTest, for_constructed_from_string_should_return_same_value) @@ -372,40 +372,40 @@ namespace TEST(ESMVariantSetStringTest, for_default_constructed_should_throw_exception) { Variant variant; - ASSERT_THROW(variant.setString("foo"), std::runtime_error); + ASSERT_THROW(variant.setString("foo"), std::bad_variant_access); } TEST(ESMVariantSetStringTest, for_unknown_should_throw_exception) { Variant variant; variant.setType(VT_Unknown); - ASSERT_THROW(variant.setString("foo"), std::runtime_error); + ASSERT_THROW(variant.setString("foo"), std::bad_variant_access); } TEST(ESMVariantSetStringTest, for_default_int_should_throw_exception) { Variant variant(int{13}); - ASSERT_THROW(variant.setString("foo"), std::runtime_error); + ASSERT_THROW(variant.setString("foo"), std::bad_variant_access); } TEST(ESMVariantSetStringTest, for_int_should_throw_exception) { Variant variant; variant.setType(VT_Int); - ASSERT_THROW(variant.setString("foo"), std::runtime_error); + ASSERT_THROW(variant.setString("foo"), std::bad_variant_access); } TEST(ESMVariantSetStringTest, for_short_should_throw_exception) { Variant variant; variant.setType(VT_Short); - ASSERT_THROW(variant.setString("foo"), std::runtime_error); + ASSERT_THROW(variant.setString("foo"), std::bad_variant_access); } TEST(ESMVariantSetStringTest, for_float_should_throw_exception) { Variant variant(float{2.7f}); - ASSERT_THROW(variant.setString("foo"), std::runtime_error); + ASSERT_THROW(variant.setString("foo"), std::bad_variant_access); } TEST(ESMVariantSetStringTest, for_string_should_change_value) diff --git a/components/esm/variant.cpp b/components/esm/variant.cpp index cbcb6e002..3e0417ef7 100644 --- a/components/esm/variant.cpp +++ b/components/esm/variant.cpp @@ -14,106 +14,53 @@ namespace const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value; const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value; const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value; -} -ESM::Variant::Variant() : mType (VT_None), mData (nullptr) {} - -ESM::Variant::Variant(const std::string &value) -{ - mData = nullptr; - mType = VT_None; - setType(VT_String); - setString(value); -} - -ESM::Variant::Variant(int value) -{ - mData = nullptr; - mType = VT_None; - setType(VT_Long); - setInteger(value); -} - -ESM::Variant::Variant(float value) -{ - mData = nullptr; - mType = VT_None; - setType(VT_Float); - setFloat(value); -} - -ESM::Variant::~Variant() -{ - delete mData; -} - -ESM::Variant& ESM::Variant::operator= (const Variant& variant) -{ - if (&variant!=this) + template + struct GetValue { - VariantDataBase *newData = variant.mData ? variant.mData->clone() : nullptr; + T operator()(int value) const { return static_cast(value); } - delete mData; + T operator()(float value) const { return static_cast(value); } - mType = variant.mType; - mData = newData; - } + template + T operator()(const V&) const + { + if constexpr (orDefault) + return T {}; + else + throw std::runtime_error("cannot convert variant"); + } + }; - return *this; -} - -ESM::Variant& ESM::Variant::operator= (Variant&& variant) -{ - if (&variant!=this) + template + struct SetValue { - delete mData; + T mValue; - mType = variant.mType; - mData = variant.mData; + explicit SetValue(T value) : mValue(value) {} - variant.mData = nullptr; - } + void operator()(int& value) const { value = static_cast(mValue); } - return *this; -} + void operator()(float& value) const { value = static_cast(mValue); } -ESM::Variant::Variant (const Variant& variant) -: mType (variant.mType), mData (variant.mData ? variant.mData->clone() : nullptr) -{} - -ESM::Variant::Variant(Variant&& variant) -: mType (variant.mType), mData (variant.mData) -{ - variant.mData = nullptr; -} - -ESM::VarType ESM::Variant::getType() const -{ - return mType; + template + void operator()(V&) const { throw std::runtime_error("cannot convert variant"); } + }; } std::string ESM::Variant::getString() const { - if (!mData) - throw std::runtime_error ("can not convert empty variant to string"); - - return mData->getString(); + return std::get(mData); } int ESM::Variant::getInteger() const { - if (!mData) - throw std::runtime_error ("can not convert empty variant to integer"); - - return mData->getInteger(); + return std::visit(GetValue{}, mData); } float ESM::Variant::getFloat() const { - if (!mData) - throw std::runtime_error ("can not convert empty variant to float"); - - return mData->getFloat(); + return std::visit(GetValue{}, mData); } void ESM::Variant::read (ESMReader& esm, Format format) @@ -202,9 +149,7 @@ void ESM::Variant::read (ESMReader& esm, Format format) setType (type); - // data - if (mData) - mData->read (esm, format, mType); + std::visit(ReadESMVariantValue {esm, format, mType}, mData); } void ESM::Variant::write (ESMWriter& esm, Format format) const @@ -227,7 +172,7 @@ void ESM::Variant::write (ESMWriter& esm, Format format) const // nothing to do here for GMST format } else - mData->write (esm, format, mType); + std::visit(WriteESMVariantValue {esm, format, mType}, mData); } void ESM::Variant::write (std::ostream& stream) const @@ -246,27 +191,27 @@ void ESM::Variant::write (std::ostream& stream) const case VT_Short: - stream << "variant short: " << mData->getInteger(); + stream << "variant short: " << std::get(mData); break; case VT_Int: - stream << "variant int: " << mData->getInteger(); + stream << "variant int: " << std::get(mData); break; case VT_Long: - stream << "variant long: " << mData->getInteger(); + stream << "variant long: " << std::get(mData); break; case VT_Float: - stream << "variant float: " << mData->getFloat(); + stream << "variant float: " << std::get(mData); break; case VT_String: - stream << "variant string: \"" << mData->getString() << "\""; + stream << "variant string: \"" << std::get(mData) << "\""; break; } } @@ -275,74 +220,50 @@ void ESM::Variant::setType (VarType type) { if (type!=mType) { - VariantDataBase *newData = nullptr; - switch (type) { case VT_Unknown: case VT_None: - - break; // no data + mData = std::monostate {}; + break; case VT_Short: case VT_Int: case VT_Long: - - newData = new VariantIntegerData (mData); + mData = std::visit(GetValue{}, mData); break; case VT_Float: - - newData = new VariantFloatData (mData); + mData = std::visit(GetValue{}, mData); break; case VT_String: - - newData = new VariantStringData (mData); + mData = std::string {}; break; } - delete mData; - mData = newData; mType = type; } } void ESM::Variant::setString (const std::string& value) { - if (!mData) - throw std::runtime_error ("can not assign string to empty variant"); + std::get(mData) = value; +} - mData->setString (value); +void ESM::Variant::setString (std::string&& value) +{ + std::get(mData) = std::move(value); } void ESM::Variant::setInteger (int value) { - if (!mData) - throw std::runtime_error ("can not assign integer to empty variant"); - - mData->setInteger (value); + std::visit(SetValue(value), mData); } void ESM::Variant::setFloat (float value) { - if (!mData) - throw std::runtime_error ("can not assign float to empty variant"); - - mData->setFloat (value); -} - -bool ESM::Variant::isEqual (const Variant& value) const -{ - if (mType!=value.mType) - return false; - - if (!mData) - return true; - - assert (value.mData); - - return mData->isEqual (*value.mData); + std::visit(SetValue(value), mData); } std::ostream& ESM::operator<< (std::ostream& stream, const Variant& value) @@ -350,13 +271,3 @@ std::ostream& ESM::operator<< (std::ostream& stream, const Variant& value) value.write (stream); return stream; } - -bool ESM::operator== (const Variant& left, const Variant& right) -{ - return left.isEqual (right); -} - -bool ESM::operator!= (const Variant& left, const Variant& right) -{ - return !(left==right); -} diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index 20b3aa76e..f0a16d4d5 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include namespace ESM { @@ -20,12 +22,10 @@ namespace ESM VT_String }; - class VariantDataBase; - class Variant { VarType mType; - VariantDataBase *mData; + std::variant mData; public: @@ -37,21 +37,17 @@ namespace ESM Format_Local // local script variables in save game files }; - Variant(); + Variant() : mType (VT_None), mData (std::monostate{}) {} - Variant (const std::string& value); - Variant (int value); - Variant (float value); + explicit Variant(const std::string& value) : mType(VT_String), mData(value) {} - ~Variant(); + explicit Variant(std::string&& value) : mType(VT_String), mData(std::move(value)) {} - Variant& operator= (const Variant& variant); - Variant& operator= (Variant && variant); + explicit Variant(int value) : mType(VT_Long), mData(value) {} - Variant (const Variant& variant); - Variant (Variant&& variant); + explicit Variant(float value) : mType(VT_Float), mData(value) {} - VarType getType() const; + VarType getType() const { return mType; } std::string getString() const; ///< Will throw an exception, if value can not be represented as a string. @@ -75,19 +71,27 @@ namespace ESM void setString (const std::string& value); ///< Will throw an exception, if type is not compatible with string. + void setString (std::string&& value); + ///< Will throw an exception, if type is not compatible with string. + void setInteger (int value); ///< Will throw an exception, if type is not compatible with integer. void setFloat (float value); ///< Will throw an exception, if type is not compatible with float. - bool isEqual (const Variant& value) const; + friend bool operator==(const Variant& left, const Variant& right) + { + return std::tie(left.mType, left.mData) == std::tie(right.mType, right.mData); + } + + friend bool operator!=(const Variant& left, const Variant& right) + { + return !(left == right); + } }; std::ostream& operator<<(std::ostream& stream, const Variant& value); - - bool operator== (const Variant& left, const Variant& right); - bool operator!= (const Variant& left, const Variant& right); } #endif diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index 2b69923d1..74d1351ec 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -5,71 +5,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -ESM::VariantDataBase::~VariantDataBase() {} - -std::string ESM::VariantDataBase::getString (bool default_) const -{ - if (default_) - return ""; - - throw std::runtime_error ("can not convert variant to string"); -} - -int ESM::VariantDataBase::getInteger (bool default_) const -{ - if (default_) - return 0; - - throw std::runtime_error ("can not convert variant to integer"); -} - -float ESM::VariantDataBase::getFloat (bool default_) const -{ - if (default_) - return 0; - - throw std::runtime_error ("can not convert variant to float"); -} - -void ESM::VariantDataBase::setString (const std::string& value) -{ - throw std::runtime_error ("conversion of string to variant not possible"); -} - -void ESM::VariantDataBase::setInteger (int value) -{ - throw std::runtime_error ("conversion of integer to variant not possible"); -} - -void ESM::VariantDataBase::setFloat (float value) -{ - throw std::runtime_error ("conversion of float to variant not possible"); -} - - - -ESM::VariantStringData::VariantStringData (const VariantDataBase *data) -{ - if (data) - mValue = data->getString (true); -} - -ESM::VariantDataBase *ESM::VariantStringData::clone() const -{ - return new VariantStringData (*this); -} - -std::string ESM::VariantStringData::getString (bool default_) const -{ - return mValue; -} - -void ESM::VariantStringData::setString (const std::string& value) -{ - mValue = value; -} - -void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarType type) +void ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, std::string& out) { if (type!=VT_String) throw std::logic_error ("not a string type"); @@ -84,10 +20,10 @@ void ESM::VariantStringData::read (ESMReader& esm, Variant::Format format, VarTy esm.fail ("local variables of type string not supported"); // GMST - mValue = esm.getHString(); + out = esm.getHString(); } -void ESM::VariantStringData::write (ESMWriter& esm, Variant::Format format, VarType type) const +void ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, const std::string& in) { if (type!=VT_String) throw std::logic_error ("not a string type"); @@ -102,48 +38,10 @@ void ESM::VariantStringData::write (ESMWriter& esm, Variant::Format format, VarT throw std::runtime_error ("local variables of type string not supported"); // GMST - esm.writeHNString ("STRV", mValue); + esm.writeHNString("STRV", in); } -bool ESM::VariantStringData::isEqual (const VariantDataBase& value) const -{ - return dynamic_cast (value).mValue==mValue; -} - - - -ESM::VariantIntegerData::VariantIntegerData (const VariantDataBase *data) : mValue (0) -{ - if (data) - mValue = data->getInteger (true); -} - -ESM::VariantDataBase *ESM::VariantIntegerData::clone() const -{ - return new VariantIntegerData (*this); -} - -int ESM::VariantIntegerData::getInteger (bool default_) const -{ - return mValue; -} - -float ESM::VariantIntegerData::getFloat (bool default_) const -{ - return static_cast(mValue); -} - -void ESM::VariantIntegerData::setInteger (int value) -{ - mValue = value; -} - -void ESM::VariantIntegerData::setFloat (float value) -{ - mValue = static_cast (value); -} - -void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarType type) +void ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, int& out) { if (type!=VT_Short && type!=VT_Long && type!=VT_Int) throw std::logic_error ("not an integer type"); @@ -156,12 +54,12 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT if (type==VT_Short) { if (value!=value) - mValue = 0; // nan + out = 0; // nan else - mValue = static_cast (value); + out = static_cast (value); } else if (type==VT_Long) - mValue = static_cast (value); + out = static_cast (value); else esm.fail ("unsupported global variable integer type"); } @@ -176,7 +74,7 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT esm.fail (stream.str()); } - esm.getHT (mValue); + esm.getHT(out); } else if (format==Variant::Format_Local) { @@ -184,18 +82,18 @@ void ESM::VariantIntegerData::read (ESMReader& esm, Variant::Format format, VarT { short value; esm.getHT(value); - mValue = value; + out = value; } else if (type==VT_Int) { - esm.getHT(mValue); + esm.getHT(out); } else esm.fail("unsupported local variable integer type"); } } -void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, VarType type) const +void ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, int in) { if (type!=VT_Short && type!=VT_Long && type!=VT_Int) throw std::logic_error ("not an integer type"); @@ -204,7 +102,7 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var { if (type==VT_Short || type==VT_Long) { - float value = static_cast(mValue); + float value = static_cast(in); esm.writeHNString ("FNAM", type==VT_Short ? "s" : "l"); esm.writeHNT ("FLTV", value); } @@ -222,72 +120,35 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var throw std::runtime_error (stream.str()); } - esm.writeHNT ("INTV", mValue); + esm.writeHNT("INTV", in); } else if (format==Variant::Format_Local) { if (type==VT_Short) - esm.writeHNT ("STTV", (short)mValue); + esm.writeHNT("STTV", static_cast(in)); else if (type == VT_Int) - esm.writeHNT ("INTV", mValue); + esm.writeHNT("INTV", in); else throw std::runtime_error("unsupported local variable integer type"); } } -bool ESM::VariantIntegerData::isEqual (const VariantDataBase& value) const -{ - return dynamic_cast (value).mValue==mValue; -} - - -ESM::VariantFloatData::VariantFloatData (const VariantDataBase *data) : mValue (0) -{ - if (data) - mValue = data->getFloat (true); -} - -ESM::VariantDataBase *ESM::VariantFloatData::clone() const -{ - return new VariantFloatData (*this); -} - -int ESM::VariantFloatData::getInteger (bool default_) const -{ - return static_cast (mValue); -} - -float ESM::VariantFloatData::getFloat (bool default_) const -{ - return mValue; -} - -void ESM::VariantFloatData::setInteger (int value) -{ - mValue = static_cast(value); -} - -void ESM::VariantFloatData::setFloat (float value) -{ - mValue = value; -} - -void ESM::VariantFloatData::read (ESMReader& esm, Variant::Format format, VarType type) +void ESM::readESMVariantValue(ESMReader& esm, Variant::Format format, VarType type, float& out) { if (type!=VT_Float) throw std::logic_error ("not a float type"); if (format==Variant::Format_Global) { - esm.getHNT (mValue, "FLTV"); + esm.getHNT(out, "FLTV"); } else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { - esm.getHT (mValue); + esm.getHT(out); } } -void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarType type) const +void ESM::writeESMVariantValue(ESMWriter& esm, Variant::Format format, VarType type, float in) { if (type!=VT_Float) throw std::logic_error ("not a float type"); @@ -295,15 +156,10 @@ void ESM::VariantFloatData::write (ESMWriter& esm, Variant::Format format, VarTy if (format==Variant::Format_Global) { esm.writeHNString ("FNAM", "f"); - esm.writeHNT ("FLTV", mValue); + esm.writeHNT("FLTV", in); } else if (format==Variant::Format_Gmst || format==Variant::Format_Info || format==Variant::Format_Local) { - esm.writeHNT ("FLTV", mValue); + esm.writeHNT("FLTV", in); } } - -bool ESM::VariantFloatData::isEqual (const VariantDataBase& value) const -{ - return dynamic_cast (value).mValue==mValue; -} diff --git a/components/esm/variantimp.hpp b/components/esm/variantimp.hpp index c1203ddf8..945872811 100644 --- a/components/esm/variantimp.hpp +++ b/components/esm/variantimp.hpp @@ -2,177 +2,58 @@ #define OPENMW_ESM_VARIANTIMP_H #include +#include #include "variant.hpp" namespace ESM { - class VariantDataBase + void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, std::string& value); + + void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, float& value); + + void readESMVariantValue(ESMReader& reader, Variant::Format format, VarType type, int& value); + + void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, const std::string& value); + + void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, float value); + + void writeESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type, int value); + + struct ReadESMVariantValue { - public: + std::reference_wrapper mReader; + Variant::Format mFormat; + VarType mType; - virtual ~VariantDataBase(); + ReadESMVariantValue(ESMReader& reader, Variant::Format format, VarType type) + : mReader(reader), mFormat(format), mType(type) {} - virtual VariantDataBase *clone() const = 0; - - virtual std::string getString (bool default_ = false) const; - ///< Will throw an exception, if value can not be represented as a string. - /// - /// \note Numeric values are not converted to strings. - /// - /// \param default_ Return a default value instead of throwing an exception. - /// - /// Default-implementation: throw an exception. - - virtual int getInteger (bool default_ = false) const; - ///< Will throw an exception, if value can not be represented as an integer (implicit - /// casting of float values is permitted). - /// - /// \param default_ Return a default value instead of throwing an exception. - /// - /// Default-implementation: throw an exception. - - virtual float getFloat (bool default_ = false) const; - ///< Will throw an exception, if value can not be represented as a float value. - /// - /// \param default_ Return a default value instead of throwing an exception. - /// - /// Default-implementation: throw an exception. - - virtual void setString (const std::string& value); - ///< Will throw an exception, if type is not compatible with string. - /// - /// Default-implementation: throw an exception. - - virtual void setInteger (int value); - ///< Will throw an exception, if type is not compatible with integer. - /// - /// Default-implementation: throw an exception. - - virtual void setFloat (float value); - ///< Will throw an exception, if type is not compatible with float. - /// - /// Default-implementation: throw an exception. - - virtual void read (ESMReader& esm, Variant::Format format, VarType type) = 0; - ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail - - virtual void write (ESMWriter& esm, Variant::Format format, VarType type) const = 0; - ///< If \a type is not supported by \a format, an exception is thrown. - - virtual bool isEqual (const VariantDataBase& value) const = 0; - ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. + void operator()(std::monostate) const {} + template + void operator()(T& value) const + { + readESMVariantValue(mReader.get(), mFormat, mType, value); + } }; - class VariantStringData : public VariantDataBase + struct WriteESMVariantValue { - std::string mValue; + std::reference_wrapper mWriter; + Variant::Format mFormat; + VarType mType; - public: + WriteESMVariantValue(ESMWriter& writer, Variant::Format format, VarType type) + : mWriter(writer), mFormat(format), mType(type) {} - VariantStringData (const VariantDataBase *data = nullptr); - ///< Calling the constructor with an incompatible data type will result in a silent - /// default initialisation. + void operator()(std::monostate) const {} - VariantDataBase *clone() const override; - - std::string getString (bool default_ = false) const override; - ///< Will throw an exception, if value can not be represented as a string. - /// - /// \note Numeric values are not converted to strings. - /// - /// \param default_ Return a default value instead of throwing an exception. - - void setString (const std::string& value) override; - ///< Will throw an exception, if type is not compatible with string. - - void read (ESMReader& esm, Variant::Format format, VarType type) override; - ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail - - void write (ESMWriter& esm, Variant::Format format, VarType type) const override; - ///< If \a type is not supported by \a format, an exception is thrown. - - bool isEqual (const VariantDataBase& value) const override; - ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. - }; - - class VariantIntegerData : public VariantDataBase - { - int mValue; - - public: - - VariantIntegerData (const VariantDataBase *data = nullptr); - ///< Calling the constructor with an incompatible data type will result in a silent - /// default initialisation. - - VariantDataBase *clone() const override; - - int getInteger (bool default_ = false) const override; - ///< Will throw an exception, if value can not be represented as an integer (implicit - /// casting of float values is permitted). - /// - /// \param default_ Return a default value instead of throwing an exception. - - float getFloat (bool default_ = false) const override; - ///< Will throw an exception, if value can not be represented as a float value. - /// - /// \param default_ Return a default value instead of throwing an exception. - - void setInteger (int value) override; - ///< Will throw an exception, if type is not compatible with integer. - - void setFloat (float value) override; - ///< Will throw an exception, if type is not compatible with float. - - void read (ESMReader& esm, Variant::Format format, VarType type) override; - ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail - - void write (ESMWriter& esm, Variant::Format format, VarType type) const override; - ///< If \a type is not supported by \a format, an exception is thrown. - - bool isEqual (const VariantDataBase& value) const override; - ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. - }; - - class VariantFloatData : public VariantDataBase - { - float mValue; - - public: - - VariantFloatData (const VariantDataBase *data = nullptr); - ///< Calling the constructor with an incompatible data type will result in a silent - /// default initialisation. - - VariantDataBase *clone() const override; - - int getInteger (bool default_ = false) const override; - ///< Will throw an exception, if value can not be represented as an integer (implicit - /// casting of float values is permitted). - /// - /// \param default_ Return a default value instead of throwing an exception. - - float getFloat (bool default_ = false) const override; - ///< Will throw an exception, if value can not be represented as a float value. - /// - /// \param default_ Return a default value instead of throwing an exception. - - void setInteger (int value) override; - ///< Will throw an exception, if type is not compatible with integer. - - void setFloat (float value) override; - ///< Will throw an exception, if type is not compatible with float. - - void read (ESMReader& esm, Variant::Format format, VarType type) override; - ///< If \a type is not supported by \a format, an exception is thrown via ESMReader::fail - - void write (ESMWriter& esm, Variant::Format format, VarType type) const override; - ///< If \a type is not supported by \a format, an exception is thrown. - - bool isEqual (const VariantDataBase& value) const override; - ///< If the (C++) type of \a value does not match the type of *this, an exception is thrown. + template + void operator()(const T& value) const + { + writeESMVariantValue(mWriter.get(), mFormat, mType, value); + } }; } From 9a6f0691b6fec2ca575b9f1f4629e9d890d358c8 Mon Sep 17 00:00:00 2001 From: CedricMocquillon Date: Thu, 8 Apr 2021 20:13:50 +0200 Subject: [PATCH 18/44] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8cb55d36..81ee0d9b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -148,6 +148,7 @@ Feature #5730: Add graphic herbalism option to the launcher and documents Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used. Feature #5813: Instanced groundcover support + Feature #5814: Bsatool should be able to create BSA archives, not only to extract it Feature #5910: Fall back to delta time when physics can't keep up Task #5480: Drop Qt4 support Task #5520: Improve cell name autocompleter implementation From 6f7e8d9f5905cccd550ad7c40195a192c3b9848c Mon Sep 17 00:00:00 2001 From: tess <2687892-TescoShoppah@users.noreply.gitlab.com> Date: Thu, 8 Apr 2021 20:57:50 +0000 Subject: [PATCH 19/44] Implement #3983 - Add page to the wizard with links to buy morrowind --- AUTHORS.md | 1 + CHANGELOG.md | 1 + apps/wizard/methodselectionpage.cpp | 13 +++- apps/wizard/methodselectionpage.hpp | 3 + files/ui/wizard/methodselectionpage.ui | 77 ++++++++++++++++++++++ files/wizard/icons/tango/48x48/dollar.png | Bin 0 -> 7395 bytes files/wizard/wizard.qrc | 1 + 7 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 files/wizard/icons/tango/48x48/dollar.png diff --git a/AUTHORS.md b/AUTHORS.md index d2de85747..611d6b9f6 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -197,6 +197,7 @@ Programmers Sylvain Thesnieres (Garvek) t6 terrorfisch + Tess (tescoShoppah) thegriglat Thomas Luppi (Digmaster) tlmullis diff --git a/CHANGELOG.md b/CHANGELOG.md index f8cb55d36..25bcc18e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,6 +123,7 @@ Feature #2404: Levelled List can not be placed into a container Feature #2686: Timestamps in openmw.log Feature #3171: OpenMW-CS: Instance drag selection + Feature #3983: Wizard: Add link to buy Morrowind Feature #4894: Consider actors as obstacles for pathfinding Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing Feature #4977: Use the "default icon.tga" when an item's icon is not found diff --git a/apps/wizard/methodselectionpage.cpp b/apps/wizard/methodselectionpage.cpp index e00344af9..37234468b 100644 --- a/apps/wizard/methodselectionpage.cpp +++ b/apps/wizard/methodselectionpage.cpp @@ -1,6 +1,9 @@ #include "methodselectionpage.hpp" #include "mainwizard.hpp" +#include +#include + Wizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) : QWizardPage(parent) { @@ -11,9 +14,12 @@ Wizard::MethodSelectionPage::MethodSelectionPage(QWidget *parent) : #ifndef OPENMW_USE_UNSHIELD retailDiscRadioButton->setEnabled(false); existingLocationRadioButton->setChecked(true); + buyLinkButton->released(); #endif - + registerField(QLatin1String("installation.retailDisc"), retailDiscRadioButton); + + connect(buyLinkButton, SIGNAL(released()), this, SLOT(handleBuyButton())); } int Wizard::MethodSelectionPage::nextId() const @@ -24,3 +30,8 @@ int Wizard::MethodSelectionPage::nextId() const return MainWizard::Page_ExistingInstallation; } } + +void Wizard::MethodSelectionPage::handleBuyButton() +{ + QDesktopServices::openUrl(QUrl("https://openmw.org/faq/#do_i_need_morrowind")); +} diff --git a/apps/wizard/methodselectionpage.hpp b/apps/wizard/methodselectionpage.hpp index c189ea171..57d551d27 100644 --- a/apps/wizard/methodselectionpage.hpp +++ b/apps/wizard/methodselectionpage.hpp @@ -17,6 +17,9 @@ namespace Wizard int nextId() const override; + private slots: + void handleBuyButton(); + private: MainWizard *mWizard; diff --git a/files/ui/wizard/methodselectionpage.ui b/files/ui/wizard/methodselectionpage.ui index 4d4d66bad..c2dd26052 100644 --- a/files/ui/wizard/methodselectionpage.ui +++ b/files/ui/wizard/methodselectionpage.ui @@ -147,6 +147,83 @@ + + + + Qt::Horizontal + + + + + + + + + Don't have a copy? + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + <html><head/><body><p><img src=":/icons/tango/48x48/dollar.png"/></p></body></html> + + + + + + + Buy the game + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + diff --git a/files/wizard/icons/tango/48x48/dollar.png b/files/wizard/icons/tango/48x48/dollar.png new file mode 100644 index 0000000000000000000000000000000000000000..a14ba2505d1d09b17fc3572af93b15ad7de7abd4 GIT binary patch literal 7395 zcmeHKc|4SB`yXWq$;B%?bwBqLN3gY!5K|BX zfj|;?OH+H`AI`s42?M{*D}IjwfmZ#o$I*prPvn7FY$lB!Km~KdSX3}IluiSILc7LX z+%q;7ubKCetrAp|iXEQRdo$jzb-N4vnI=O zF6`{fKJ}>_FO|(WIWd~_ASq{UI_J#II}*4pMKSI>WFJ;*T?(5S9Eel)^0CPcXV|^* zOIl2lACYMA_C6)h=!=~s8eV|a5Q3yz*;0Czs)KHiQn+p_3{SlpVwSO!$~?}G-2Svu z!zCbT`|6%USrrRyL8)*0YbB-vb7n^G@dmH&e-$oXx$n8cyN>J_p3Y{&Y}-Puir+N# zIzeHb=Qr&|-iREnGZ0b)kzkx5c=@bsD_`w#Dtf@u`+k zSkh*kGpp9w9h7oAj!Bi69G?E`*jiXF<_RG3rsI<-{z$kuFB zDGur;emr7GeN(!}rDOJ*>&i202FBt=`~rK76Kvf4YUrBfA&2dfJomh-DIG0%-mr1? zP+W?+GVWec+Gb;VvxfFVz2FR2Ipe@6+2duG-ap;#biLAiJa4x9&2!hY;{J76l_JyW zI(6C8RIy4fq*;FbpryB-;>uONx{vDY6Msv@4L*=-u8@jOCk?GeHsAElX{sE#_fn3- ztE*YR7>m9Ef0KBvz9C2Z-LLSEC#$!VNo)_iCdtsIbhjGL3Uo_KT9=o#uet0s{?g9t z6Z4GZ!Uha?RnW8dZstcEJrdie+5X5;x2?#n%A@}IB*}x`UDy3QpKi79Fa^K&D{f7k zllM}LyjO9L9V}Q@xw(sq#l3E?d6WXr_DKk@j4rx(!z7~}p^{wn*eGrREEVf7p6Fqg?WB$s_ z%vwv8Qv-hCejm@Y3Tg5LMx`~gZr*&OJL3B$miTN9VWgNko^V3Z(Dpod@`~4m*V3u+ zt~zLhFuG?v<5sTrRr|skh+E8>wR01`231y)CAo;P3dj1!`Et)_n?S}~@SdpM)n1|f zE4%u}*!3!rA46+{jpnj;qkZdR&uaFLkDMA@NE-<`zOYdEWI^}newml!JErGDxC!Cb zg!=Sc(`zS`CLQw{ROHX_60WUH*btRsm69nOzO&VX?2N*(j-Ef7BNUW!<*I=8q(weu z93lJmNWa33ng@b($S0quzKPlKc8OQU$@1M9U26TSpG3fmWt~2yXc~FV7)ezqyozj` z0u8GfWnsYEc1;xcMX*v@6R-$U)I9=cg{o-kB{-J=6`KWJzGZ{m{>vt8VZPZ6=6bvV z{3%*LKPgJ0$@VZv<{7q^i1%V$JyzV4Dt7upTttdU{6M0MqD**>KFVvSSg0iWtVr=i zW66e~gY5H(LrG_o9CdXgUYprT`D`8#Ms4iL-WeGsDZ8q|Xh4iDt-mbe3(8mW6fx&S-;h;&uB?;|`2af_!-#gl1anRno~nM@Hhvj8 z%<4HQ6MV(w%oXUaEmzf?-}wnn(Zt?5x*uAzPxrl9Zh=YeU3j_BB}pt)4y)gFT2&Ng zNV;^oH4E9?^S+}iq-eP9SeukX`j}(71FZKddOYun>3*wQ0zQUL4!O10*GX4myB@YLI; zMz!Y~#*^Po`u zXXb>u#*0$=+-FrS(OngF7IT48z0G;yiD%H6^_TBIDa7nK^r75y{7v`*w)fGkIi_iK z)#PWzCu8HJ5%IV0+kP85bv4ofzsotq@kQ;Uy7@C7bUS)tbP&{Dho>5XtWJG>fgTkl zrA3>WQvEZa;tyUL$)knWV^ByCNFa)CVnV>1nEZJP0?s};`%?5R8x1$y_HrnI$ciKs zVJJnm>+<7c)0|GPk~J=GEje_zz^gT16(1Dzi--7{{o9}pbMayW6NAuJcuBvxs)*H(}AR47iRrXqB{89PyV zQ`>~L6>GNvvThn$b_uSTsW7D$F7~T)N8qsL-*~Mj;FR2PSLXHPODxg& z%p-1U5zgZteY`mM%nr1u%_=}I3@gAF@Ov&7Mc`zS0dfIjN7dlIDV zwt+yB33T9C>SAM!BQpcFh!iG?sudc@0*-{|h`|mqy-*xL5J=?`!J&Zx3=S?-AF{-Y1Md0V zFbH@F!u8jOxY!WDCQLRJjM74B!GR5g9)g7I5CiM6DKwnDsrgq5prsG-<#Jg#7>vi` zY4NnRm~0;y0*l4M;7Aw}2?Y>PP8fqr423c{8~GGpI83P=GMmog(wPh}pOZ*p26Ocx z5MUhql^hr)n}q_MUmftn@|hGdNr#gG}X90pwbAfCoW^lXS>r zG!#oipr9xn9U_#70(dBBB%Fpu=xCGC%PH*Gbf6N50n4M}Q&9jaB8sSsM4&KGw6=~m z6ou6#K}lrb4vr*K;W|X%poP>~iiW=qI3ogHAA;0^fA1j#5V&#zY_3Qd1B1ci?|xa`0vH82>k(@6c~7MreNmon2mdOrV=HG!D^riJ>LkgfTNd*QkZ2~wV`HTDopkVG^UiPR;N z$mk#0IZPUtM`Tlte1IGP))^?RrF90YFJ+&`k7%AR703}NTn7h-L%u98SP#bEQ2(5s z9)HWRvB51DK##vA;qd&-a|mX!0_arskHY+!DE|v?nf=$K{7>e~VPC9Gn5;0MXnnbM zJjUO;{|n$325UN*%HS~nF7)M)FS0Cq?f@}==>wi;z~d42?fLjME%=r5U;KPcxBsFC zfcmGCf28kExqiy^j}-Vv;Gfy`Q?7rcz&`^2%&z}8xx~J|VN)5vL5~N#KdT{FV~A)oR$OppwuKTau%^YtE2`wU^#?>%wEtlKQ?V zR>=aPIQ94E`BG8(qwQ4=D&EsiQO50Mj4 zSaIM$ly`5p@Xgp^;$ z;p3j(Q!0{4bsalPHs3DU07@-Vef;a@6U8NXjgs~!I)a-XTRqJ`b<-w&_aBifx^arG zP3V%hr1-$2Ij*N5$GUUQ?cB^P#S1t_kxt~!-4Zgr=y`gh?Z9fo?%OV-`##lPR<=>B z6>puHDxB6_5jX+A$xzA5z0yfctO9lHU%-_%dqzx@b7MSP_L8fR&DX@Ggll2vT>6?Z zVp2kbaZ`GwcZj1}hBDPEKFR`*U8)6owI9#=?KVx<)7)%#Ev=RyqtgSupI|Ek_R3(! zq|%31KJ$!>t@Nm~9gv}WNXI~5YVCDbY-@V^ao^D8M@1)Q1oO5!jzyl^>C&e!9Xi)t zRV(jey|+?PIQI_eh#O~h)SZn5CEo29G@ciURp57rYE4}9(~=pxonkNh;gH3G9DD8| zzB=iJL_>RfynI#d6`o|?+tepY%_{e|daAs4tT?%1g!gJhc|zPxy%IMkSP~!NIW*sX nX6yLN#Ru6s{y+*Wrhf*#?j9b?dCi;y5)6blvo)icons/tango/index.theme icons/tango/48x48/folder.png icons/tango/48x48/system-installer.png + icons/tango/48x48/dollar.png images/intropage-background.png From 3e200007784285e57c64fd5c1a86baf5df732572 Mon Sep 17 00:00:00 2001 From: jvoisin Date: Fri, 9 Apr 2021 16:51:44 +0200 Subject: [PATCH 20/44] Add a coverity scan --- .gitlab-ci.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 713cc2601..850407938 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,6 +30,29 @@ stages: paths: - build/install/ +Coverity: + extends: .Debian + only: + - schedules + before_script: + - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic + - curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN + - tar xfz /tmp/cov-analysis-linux64.tgz + script: + - CI/before_script.linux.sh + - cd build + - cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build . -- -j $(nproc) + after_script: + - tar cfz cov-int.tar.gz cov-int + - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME \ + --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL \ + --form file=@cov-int.tar.gz --form version="`git describe --tags`" \ + --form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID + variables: + CC: gcc + CXX: g++ + timeout: 2h + Debian_GCC: extends: .Debian cache: From 799cf16f3161fe7bfa39c20d17d9c780010de3da Mon Sep 17 00:00:00 2001 From: Jonas Tobias Hopusch Date: Fri, 9 Apr 2021 18:16:05 +0200 Subject: [PATCH 21/44] Attempt to fix #5942 Closes OpenMW/openmw#5942 This is an attempt to apply the fix suggested by @Capostrophic --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fb6721853..f1e40ef7f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -681,7 +681,7 @@ namespace MWMechanics // Deviating from Morrowind here: it doesn't increase disposition on marginal wins, // which seems to be a bug (MCP fixes it too). // Original logic: x = 0, y = -iPerMinChange - x = -iPerMinChange; + x = iPerMinChange; y = x; // This goes unused. } else From d661a3103e446a22dd79160ec6813ea88b0a05f7 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 8 Apr 2021 00:05:02 +0200 Subject: [PATCH 22/44] Use clang 9 for linux builds on travis --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e84a9ad71..debb2e3dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ addons: - sourceline: 'ppa:openmw/openmw' packages: [ # Dev - build-essential, cmake, clang-tools, ccache, + build-essential, cmake, clang-tools-9, ccache, # Boost libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg @@ -37,8 +37,8 @@ matrix: os: linux dist: focal env: - - MATRIX_EVAL="CC=clang && CXX=clang++" - - ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++" + - MATRIX_EVAL="CC=clang-9 && CXX=clang++-9" + - ANALYZE="scan-build-9 --force-analyze-debug-code --use-cc clang-9 --use-c++ clang++-9" compiler: clang before_install: From aaf975ea351157450164fca18657002435961be9 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 8 Apr 2021 00:24:15 +0200 Subject: [PATCH 23/44] Send travis notifications only for main repo master --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index debb2e3dd..d019b5526 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,11 +66,13 @@ deploy: repo: OpenMW/openmw notifications: email: + if: repository_slug = OpenMW/openmw AND branch = master recipients: - corrmage+travis-ci@gmail.com on_success: change on_failure: always irc: + if: repository_slug = OpenMW/openmw AND branch = master channels: - "chat.freenode.net#openmw" on_success: change From 75b4871bab239b9f3c92ff9441ae93b488e49574 Mon Sep 17 00:00:00 2001 From: Simon Meulenbeek Date: Fri, 9 Apr 2021 19:28:08 +0000 Subject: [PATCH 24/44] Add Audio settings to openmw-launcher --- AUTHORS.md | 1 + CHANGELOG.md | 1 + apps/launcher/CMakeLists.txt | 4 + apps/launcher/advancedpage.cpp | 68 +++++++++ apps/launcher/utils/openalutil.cpp | 55 +++++++ apps/launcher/utils/openalutil.hpp | 7 + .../reference/modding/settings/sound.rst | 10 +- files/ui/advancedpage.ui | 144 ++++++++++++++++++ 8 files changed, 285 insertions(+), 5 deletions(-) create mode 100644 apps/launcher/utils/openalutil.cpp create mode 100644 apps/launcher/utils/openalutil.hpp diff --git a/AUTHORS.md b/AUTHORS.md index d2de85747..41313b8aa 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -184,6 +184,7 @@ Programmers sergoz ShadowRadiance Siimacore + Simon Meulenbeek (simonmb) sir_herrbatka smbas Sophie Kirschner (pineapplemachine) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8cb55d36..69a467b8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Feature #5456: Basic collada animation support Feature #5457: Realistic diagonal movement Feature #5486: Fixes trainers to choose their training skills based on their base skill points + Feature #5511: Add in game option to toggle HRTF support in OpenMW Feature #5519: Code Patch tab in launcher Feature #5524: Resume failed script execution after reload Feature #5545: Option to allow stealing from an unconscious NPC during combat diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 329d06a57..301823770 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -13,6 +13,7 @@ set(LAUNCHER utils/profilescombobox.cpp utils/textinputdialog.cpp utils/lineedit.cpp + utils/openalutil.cpp ${CMAKE_SOURCE_DIR}/files/windows/launcher.rc ) @@ -31,6 +32,7 @@ set(LAUNCHER_HEADER utils/profilescombobox.hpp utils/textinputdialog.hpp utils/lineedit.hpp + utils/openalutil.hpp ) # Headers that must be pre-processed @@ -47,6 +49,7 @@ set(LAUNCHER_HEADER_MOC utils/textinputdialog.hpp utils/profilescombobox.hpp utils/lineedit.hpp + utils/openalutil.hpp ) @@ -95,6 +98,7 @@ endif (WIN32) target_link_libraries(openmw-launcher ${SDL2_LIBRARY_ONLY} + ${OPENAL_LIBRARY} components ) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index d82dd1be2..b7bf1a249 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -5,11 +5,14 @@ #include #include #include +#include #include #include #include +#include "utils/openalutil.hpp" + Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, Settings::Manager &engineSettings, QWidget *parent) : QWidget(parent) @@ -19,7 +22,17 @@ Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, setObjectName ("AdvancedPage"); setupUi(this); + for(const char * name : Launcher::enumerateOpenALDevices()) + { + audioDeviceSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name)); + } + for(const char * name : Launcher::enumerateOpenALDevicesHrtf()) + { + hrtfProfileSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name)); + } + loadSettings(); + mCellNameCompleter.setModel(&mCellNameCompleterModel); startDefaultCharacterAtField->setCompleter(&mCellNameCompleter); } @@ -126,6 +139,34 @@ bool Launcher::AdvancedPage::loadSettings() viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera"))); } + // Audio + { + std::string selectedAudioDevice = mEngineSettings.getString("device", "Sound"); + if (selectedAudioDevice.empty() == false) + { + int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice)); + if (audioDeviceIndex != -1) + { + audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex); + } + } + int hrtfEnabledIndex = mEngineSettings.getInt("hrtf enable", "Sound"); + if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1) + { + enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1); + } + std::string selectedHRTFProfile = mEngineSettings.getString("hrtf", "Sound"); + if (selectedHRTFProfile.empty() == false) + { + int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile)); + if (hrtfProfileIndex != -1) + { + hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex); + } + } + } + + // Camera { loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera"); @@ -247,6 +288,33 @@ void Launcher::AdvancedPage::saveSettings() mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance)); } } + + // Audio + { + int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex(); + if (audioDeviceIndex != 0) + { + mEngineSettings.setString("device", "Sound", audioDeviceSelectorComboBox->currentText().toUtf8().constData()); + } + else + { + mEngineSettings.setString("device", "Sound", ""); + } + int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1; + if (hrtfEnabledIndex != mEngineSettings.getInt("hrtf enable", "Sound")) + { + mEngineSettings.setInt("hrtf enable", "Sound", hrtfEnabledIndex); + } + int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex(); + if (selectedHRTFProfileIndex != 0) + { + mEngineSettings.setString("hrtf", "Sound", hrtfProfileSelectorComboBox->currentText().toUtf8().constData()); + } + else + { + mEngineSettings.setString("hrtf", "Sound", ""); + } + } // Camera { diff --git a/apps/launcher/utils/openalutil.cpp b/apps/launcher/utils/openalutil.cpp new file mode 100644 index 000000000..6f76b7130 --- /dev/null +++ b/apps/launcher/utils/openalutil.cpp @@ -0,0 +1,55 @@ +#include +#include +#include + +#include + +#include "openalutil.hpp" + +#ifndef ALC_ALL_DEVICES_SPECIFIER +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 +#endif + +std::vector Launcher::enumerateOpenALDevices() +{ + std::vector devlist; + const ALCchar *devnames; + + if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) + { + devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); + } + else + { + devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); + } + + while(devnames && *devnames) + { + devlist.emplace_back(devnames); + devnames += strlen(devnames)+1; + } + return devlist; +} + +std::vector Launcher::enumerateOpenALDevicesHrtf() +{ + std::vector ret; + + ALCdevice *device = alcOpenDevice(nullptr); + if(device && alcIsExtensionPresent(device, "ALC_SOFT_HRTF")) + { + LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr; + void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT"); + memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr)); + ALCint num_hrtf; + alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf); + ret.reserve(num_hrtf); + for(ALCint i = 0;i < num_hrtf && i < 20;++i) + { + const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i); + ret.emplace_back(entry); + } + } + return ret; +} \ No newline at end of file diff --git a/apps/launcher/utils/openalutil.hpp b/apps/launcher/utils/openalutil.hpp new file mode 100644 index 000000000..4a84fbae7 --- /dev/null +++ b/apps/launcher/utils/openalutil.hpp @@ -0,0 +1,7 @@ +#include + +namespace Launcher +{ + std::vector enumerateOpenALDevices(); + std::vector enumerateOpenALDevicesHrtf(); +} \ No newline at end of file diff --git a/docs/source/reference/modding/settings/sound.rst b/docs/source/reference/modding/settings/sound.rst index 895b919fb..4cc665582 100644 --- a/docs/source/reference/modding/settings/sound.rst +++ b/docs/source/reference/modding/settings/sound.rst @@ -5,7 +5,7 @@ device ------ :Type: string -:Range: +:Range: :Default: "" This setting determines which audio device to use. A blank or missing setting means to use the default device, @@ -13,7 +13,7 @@ which should usually be sufficient, but if you need to explicitly specify a devi The names of detected devices can be found in the openmw.log file in your configuration directory. -This setting can only be configured by editing the settings configuration file. +This setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher. master volume ------------- @@ -111,13 +111,13 @@ Enabling HRTF may also require an OpenAL Soft version greater than 1.17.0, and possibly some operating system configuration. A value of 0 disables HRTF processing, while a value of 1 explicitly enables HRTF processing. The default value is -1, which should enable the feature automatically for most users when possible. -This setting can only be configured by editing the settings configuration file. +This setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher. hrtf ---- :Type: string -:Range: +:Range: :Default: "" This setting specifies which HRTF profile to use when HRTF is enabled. Blank means use the default. @@ -125,4 +125,4 @@ This setting has no effect if HRTF is not enabled based on the hrtf enable setti Allowed values for this field are enumerated in openmw.log file is an HRTF enabled audio system is installed. The default value is empty, which uses the default profile. -This setting can only be configured by editing the settings configuration file. +This setting can be configured by editing the settings configuration file, or in the Audio tab of the OpenMW Launcher. diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index a3601ce94..a990e9172 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -467,6 +467,150 @@ This setting makes the fog use the actual eye point distance (or so called Eucli + + + Audio + + + + + + + + Audio Device + + + Select your preferred audio device. + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Default + + + + + + + + + + + + HRTF + + + This setting controls HRTF, which simulates 3D sound on stereo systems. + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Automatic + + + + + Off + + + + + On + + + + + + + + + + + + HRTF Profile + + + Select your preferred HRTF profile. + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Default + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + Camera From 6e1c67a9aea864df8dff68ecb0fa3eb5c73c8ed0 Mon Sep 17 00:00:00 2001 From: fredzio Date: Fri, 9 Apr 2021 19:11:26 +0200 Subject: [PATCH 25/44] Account for waterwalking when updating position. Otherwise we might trace down the actor at waterlevel at the wrong coordinate. Triggered by multimark mod with waterwalking effect. --- apps/openmw/mwphysics/mtphysics.cpp | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 47 +++++++++++++------------ apps/openmw/mwphysics/physicssystem.hpp | 4 +-- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 4be8b2396..4957ef422 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -317,7 +317,7 @@ namespace MWPhysics // init for (auto& data : actorsData) - data.updatePosition(); + data.updatePosition(mCollisionWorld); mPrevStepCount = numSteps; mRemainingSteps = numSteps; mTimeAccum = timeAccum; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index ac8dd92f8..4087ba7e1 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -60,6 +60,22 @@ #include "movementsolver.hpp" #include "mtphysics.hpp" +namespace +{ + bool canMoveToWaterSurface(const MWPhysics::Actor* physicActor, const float waterlevel, btCollisionWorld* world) + { + if (!physicActor) + return false; + const float halfZ = physicActor->getHalfExtents().z(); + const osg::Vec3f actorPosition = physicActor->getPosition(); + const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ); + const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ); + MWPhysics::ActorTracer tracer; + tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, world); + return (tracer.mFraction >= 1.0f); + } +} + namespace MWPhysics { PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode) @@ -347,16 +363,7 @@ namespace MWPhysics bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel) { - const Actor* physicActor = getActor(actor); - if (!physicActor) - return false; - const float halfZ = physicActor->getHalfExtents().z(); - const osg::Vec3f actorPosition = physicActor->getPosition(); - const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ); - const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ); - ActorTracer tracer; - tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld.get()); - return (tracer.mFraction >= 1.0f); + return ::canMoveToWaterSurface(getActor(actor), waterlevel, mCollisionWorld.get()); } osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const @@ -772,16 +779,10 @@ namespace MWPhysics const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects(); bool waterCollision = false; - bool moveToWaterSurface = false; if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude()) { - if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3()))) + if (physicActor->getCollisionMode() || !world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3()))) waterCollision = true; - else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel)) - { - moveToWaterSurface = true; - waterCollision = true; - } } physicActor->setCanWaterWalk(waterCollision); @@ -794,7 +795,7 @@ namespace MWPhysics if (!willSimulate) standingOn = physicActor->getStandingOnPtr(); - actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel); + actorsFrameData.emplace_back(std::move(physicActor), standingOn, waterCollision, movement, slowFall, waterlevel); } mMovementQueue.clear(); return actorsFrameData; @@ -937,9 +938,9 @@ namespace MWPhysics } ActorFrameData::ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr standingOn, - bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel) + bool waterCollision, osg::Vec3f movement, float slowFall, float waterlevel) : mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn), - mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface), + mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision), mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos() { const MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -953,7 +954,7 @@ namespace MWPhysics mWasOnGround = actor->getOnGround(); } - void ActorFrameData::updatePosition() + void ActorFrameData::updatePosition(btCollisionWorld* world) { mActorRaw->updateWorldPosition(); // If physics runs "fast enough", position are interpolated without simulation @@ -961,10 +962,10 @@ namespace MWPhysics // regardless of simulation speed. mActorRaw->applyOffsetChange(); mPosition = mActorRaw->getPosition(); - if (mMoveToWaterSurface) + if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world)) { mPosition.z() = mWaterlevel; - MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z()); + MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z(), false); } mOldHeight = mPosition.z(); mRefpos = mActorRaw->getPtr().getRefData().getPosition(); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 5ccbf1a38..ce10b4246 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -79,7 +79,7 @@ namespace MWPhysics struct ActorFrameData { ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel); - void updatePosition(); + void updatePosition(btCollisionWorld* world); std::weak_ptr mActor; Actor* mActorRaw; MWWorld::Ptr mStandingOn; @@ -90,7 +90,7 @@ namespace MWPhysics bool mDidJump; bool mFloatToSurface; bool mNeedLand; - bool mMoveToWaterSurface; + bool mWaterCollision; float mWaterlevel; float mSlowFall; float mOldHeight; From 8874a5be22710c06b26f25c83051dddf373d6f67 Mon Sep 17 00:00:00 2001 From: fredzio Date: Fri, 9 Apr 2021 23:06:36 +0200 Subject: [PATCH 26/44] Change (again) the way SetPos behave. Instead of registering the desired change of position and rely on physics simulation to apply it to the world, immediately change the position in the world without reset the simulation. --- apps/openmw/mwbase/world.hpp | 4 +-- apps/openmw/mwphysics/actor.cpp | 12 +------ .../mwscript/transformationextensions.cpp | 21 +++++++++--- apps/openmw/mwworld/worldimp.cpp | 32 ++++--------------- apps/openmw/mwworld/worldimp.hpp | 7 ++-- 5 files changed, 27 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3d55ad987..6cee36b40 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -281,13 +281,13 @@ namespace MWBase virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0; - virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool moveToActive=false) = 0; + virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) = 0; ///< @return an updated Ptr in case the Ptr's cell changes virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0; ///< @return an updated Ptr - virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec) = 0; + virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec, bool moveToActive) = 0; ///< @return an updated Ptr virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 776212ede..f9adc9bc6 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -3,8 +3,6 @@ #include #include -#include -#include #include #include #include @@ -181,6 +179,7 @@ bool Actor::setPosition(const osg::Vec3f& position) if (mSkipSimulation) return false; bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged; + updateWorldPosition(); applyOffsetChange(); mPreviousPosition = mPosition; mPosition = position; @@ -197,15 +196,6 @@ void Actor::applyOffsetChange() { if (mPositionOffset.length() == 0) return; - if (mPositionOffset.z() != 0) - { - // Often, offset are set in sequence x, y, z - // We don't want actors to be moved under the ground - // Check terrain height at new coordinate and update z offset if necessary - const auto pos = mWorldPosition + mPositionOffset; - const auto terrainHeight = mPtr.getCell()->isExterior() ? MWBase::Environment::get().getWorld()->getTerrainHeightAt(pos) : -std::numeric_limits::max(); - mPositionOffset.z() = std::max(pos.z(), terrainHeight) - mWorldPosition.z(); - } mWorldPosition += mPositionOffset; mPosition += mPositionOffset; mPreviousPosition += mPositionOffset; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 6b92378c7..ba211503e 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -32,7 +32,7 @@ namespace MWScript std::vector actors; MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); for (auto& actor : actors) - MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff); + MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff, false); } template @@ -284,6 +284,17 @@ namespace MWScript } else if(axis == "z") { + // We should not place actors under ground + if (ptr.getClass().isActor()) + { + float terrainHeight = -std::numeric_limits::max(); + if (ptr.getCell()->isExterior()) + terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos); + + if (pos < terrainHeight) + pos = terrainHeight; + } + newPos[2] = pos; } else @@ -292,7 +303,7 @@ namespace MWScript } dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true)); } }; @@ -428,7 +439,7 @@ namespace MWScript } else { - ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true); + ptr = MWBase::Environment::get().getWorld()->moveObject(ptr, x, y, z, true, true); } dynamic_cast(runtime.getContext()).updatePtr(base,ptr); @@ -715,7 +726,7 @@ namespace MWScript // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); } }; @@ -751,7 +762,7 @@ namespace MWScript // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3b50dd6aa..5b9afa4fe 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -862,19 +862,6 @@ namespace MWWorld if (reference == getPlayerPtr()) throw std::runtime_error("can not disable player object"); - // A common pattern to teleport NPC in scripts is a sequence of SetPos/Disable/Enable - // Disable/Enable create a new physics actor, and so the SetPos call is lost - // Call moveObject so that the newly created physics actor will have up-to-date position - if (reference.getClass().isActor()) - { - auto* physactor = mPhysics->getActor(reference); - if (physactor) - { - physactor->applyOffsetChange(); - const auto position = physactor->getSimulationPosition(); - moveObject(reference, position.x(), position.y(), position.z(), true); - } - } reference.getRefData().disable(); if (reference.getCellRef().getRefNum().hasContentFile()) @@ -1251,7 +1238,7 @@ namespace MWWorld return newPtr; } - MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive) + MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics, bool moveToActive) { int cellX, cellY; positionToIndex(x, y, cellX, cellY); @@ -1266,21 +1253,14 @@ namespace MWWorld return moveObject(ptr, cell, x, y, z, movePhysics); } - MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive) - { - return moveObjectImp(ptr, x, y, z, true, moveToActive); - } - - MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec) + MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive) { auto* actor = mPhysics->getActor(ptr); if (actor) - { actor->adjustPosition(vec); - return ptr; - } + osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec; - return moveObject(ptr, newpos.x(), newpos.y(), newpos.z()); + return moveObject(ptr, newpos.x(), newpos.y(), newpos.z(), false, moveToActive && ptr != getPlayerPtr()); } void World::scaleObject (const Ptr& ptr, float scale) @@ -1546,7 +1526,7 @@ namespace MWWorld auto* physactor = mPhysics->getActor(actor); assert(physactor); const auto position = physactor->getSimulationPosition(); - moveObjectImp(actor, position.x(), position.y(), position.z(), false); + moveObject(actor, position.x(), position.y(), position.z(), false, false); } } @@ -1556,7 +1536,7 @@ namespace MWWorld auto* physactor = mPhysics->getActor(*player); assert(physactor); const auto position = physactor->getSimulationPosition(); - moveObjectImp(*player, position.x(), position.y(), position.z(), false); + moveObject(*player, position.x(), position.y(), position.z(), false, false); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 929e035e9..33b23e065 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -138,9 +138,6 @@ namespace MWWorld void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags); - Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false); - ///< @return an updated Ptr in case the Ptr's cell changes - Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); void updateSoundListener(); @@ -376,13 +373,13 @@ namespace MWWorld void undeleteObject (const Ptr& ptr) override; - MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool moveToActive=false) override; + MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) override; ///< @return an updated Ptr in case the Ptr's cell changes MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true) override; ///< @return an updated Ptr - MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec) override; + MWWorld::Ptr moveObjectBy(const Ptr& ptr, osg::Vec3f vec, bool moveToActive) override; ///< @return an updated Ptr void scaleObject (const Ptr& ptr, float scale) override; From 8ff4f731fb6bcba2fa85ef0c742a4d2ad2bd74c1 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 9 Apr 2021 23:08:51 +0000 Subject: [PATCH 27/44] Make Coverity happy about A2C Initialise member variable --- components/shader/shadervisitor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 612c9011d..b0013538f 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -50,6 +50,7 @@ namespace Shader , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) , mApplyLightingToEnvMaps(false) + , mConvertAlphaTestToAlphaToCoverage(false) , mTranslucentFramebuffer(false) , mShaderManager(shaderManager) , mImageManager(imageManager) From 93954a961c7511946e4b25b46c597eaeaa18cdc7 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 09:30:58 +0400 Subject: [PATCH 28/44] Unlock mutex on return to avoid hang --- extern/osg-ffmpeg-videoplayer/videostate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index 7c4bddb01..a35c845db 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -349,7 +349,10 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) vp->pts = pts; if (vp->set_dimensions(w, h) < 0) + { + this->pictq_mutex.unlock(); return -1; + } sws_scale(this->sws_context, pFrame->data, pFrame->linesize, 0, this->video_ctx->height, vp->rgbaFrame->data, vp->rgbaFrame->linesize); From 41c78a889a54ff0e13efba637f151fdf3c992857 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 09:35:31 +0400 Subject: [PATCH 29/44] Check for decompression error code --- components/bsa/compressedbsafile.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/bsa/compressedbsafile.cpp b/components/bsa/compressedbsafile.cpp index aaeb5bffa..0f3a26e15 100644 --- a/components/bsa/compressedbsafile.cpp +++ b/components/bsa/compressedbsafile.cpp @@ -381,8 +381,10 @@ Files::IStreamPtr CompressedBSAFile::getFile(const FileRecord& fileRecord) LZ4F_decompressionContext_t context = nullptr; LZ4F_createDecompressionContext(&context, LZ4F_VERSION); LZ4F_decompressOptions_t options = {}; - LZ4F_decompress(context, memoryStreamPtr->getRawData(), &uncompressedSize, buffer.get(), &size, &options); - LZ4F_errorCode_t errorCode = LZ4F_freeDecompressionContext(context); + LZ4F_errorCode_t errorCode = LZ4F_decompress(context, memoryStreamPtr->getRawData(), &uncompressedSize, buffer.get(), &size, &options); + if (LZ4F_isError(errorCode)) + fail("LZ4 decompression error (file " + mFilename + "): " + LZ4F_getErrorName(errorCode)); + errorCode = LZ4F_freeDecompressionContext(context); if (LZ4F_isError(errorCode)) fail("LZ4 decompression error (file " + mFilename + "): " + LZ4F_getErrorName(errorCode)); } From b96929f3fc94ad0a0ae54438cd4cf1541e606967 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 09:52:46 +0400 Subject: [PATCH 30/44] Avoid division by zero --- apps/launcher/graphicspage.cpp | 5 ++++- apps/openmw/mwgui/settingswindow.cpp | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 01205043e..c6e74573c 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -20,6 +20,9 @@ QString getAspect(int x, int y) { int gcd = std::gcd (x, y); + if (gcd == 0) + return QString(); + int xaspect = x / gcd; int yaspect = y / gcd; // special case: 8 : 5 is usually referred to as 16:10 @@ -298,9 +301,9 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) return result; } - QString aspect = getAspect(mode.w, mode.h); QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h); + QString aspect = getAspect(mode.w, mode.h); if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { resolution.append(tr("\t(Wide ") + aspect + ")"); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 538b3db5e..6342433c4 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -61,6 +61,9 @@ namespace std::string getAspect (int x, int y) { int gcd = std::gcd (x, y); + if (gcd == 0) + return std::string(); + int xaspect = x / gcd; int yaspect = y / gcd; // special case: 8 : 5 is usually referred to as 16:10 @@ -249,8 +252,10 @@ namespace MWGui std::sort(resolutions.begin(), resolutions.end(), sortResolutions); for (std::pair& resolution : resolutions) { - std::string str = MyGUI::utility::toString(resolution.first) + " x " + MyGUI::utility::toString(resolution.second) - + " (" + getAspect(resolution.first, resolution.second) + ")"; + std::string str = MyGUI::utility::toString(resolution.first) + " x " + MyGUI::utility::toString(resolution.second); + std::string aspect = getAspect(resolution.first, resolution.second); + if (!aspect.empty()) + str = str + " (" + aspect + ")"; if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE) mResolutionList->addItem(str); From 124a33d8a314924c6df5095622ca1a597dfdf889 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 10:58:00 +0400 Subject: [PATCH 31/44] Fix uninitialized variables --- apps/opencs/model/world/refidadapterimp.cpp | 22 ++++++++-- apps/opencs/model/world/refidadapterimp.hpp | 43 +++++++++++++++++--- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/esm/loadtes3.cpp | 1 + components/misc/frameratelimiter.hpp | 1 + components/resource/scenemanager.cpp | 1 + components/sdlutil/sdlinputwrapper.cpp | 2 + components/shader/shadervisitor.cpp | 1 + extern/osg-ffmpeg-videoplayer/videostate.cpp | 5 ++- 9 files changed, 66 insertions(+), 11 deletions(-) diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index d944adc23..644092f16 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -56,7 +56,9 @@ void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData CSMWorld::IngredientColumns::IngredientColumns (const InventoryColumns& columns) -: InventoryColumns (columns) {} +: InventoryColumns (columns) +, mEffects(nullptr) +{} CSMWorld::IngredientRefIdAdapter::IngredientRefIdAdapter (const IngredientColumns& columns) : InventoryRefIdAdapter (UniversalId::Type_Ingredient, columns), @@ -585,7 +587,13 @@ void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& } CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns) -: InventoryColumns (columns) {} +: InventoryColumns (columns) +, mTime(nullptr) +, mRadius(nullptr) +, mColor(nullptr) +, mSound(nullptr) +, mEmitterType(nullptr) +{} CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns) : InventoryRefIdAdapter (UniversalId::Type_Light, columns), mColumns (columns) @@ -1454,7 +1462,15 @@ int CSMWorld::CreatureMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *co } CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) -: EnchantableColumns (columns) {} +: EnchantableColumns (columns) +, mType(nullptr) +, mHealth(nullptr) +, mSpeed(nullptr) +, mReach(nullptr) +, mChop{nullptr} +, mSlash{nullptr} +, mThrust{nullptr} +{} CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns) : EnchantableRefIdAdapter (UniversalId::Type_Weapon, columns), mColumns (columns) diff --git a/apps/opencs/model/world/refidadapterimp.hpp b/apps/opencs/model/world/refidadapterimp.hpp index 0a29afcad..95d1a09a2 100644 --- a/apps/opencs/model/world/refidadapterimp.hpp +++ b/apps/opencs/model/world/refidadapterimp.hpp @@ -178,7 +178,11 @@ namespace CSMWorld const RefIdColumn *mName; const RefIdColumn *mScript; - NameColumns (const ModelColumns& base) : ModelColumns (base) {} + NameColumns (const ModelColumns& base) + : ModelColumns (base) + , mName(nullptr) + , mScript(nullptr) + {} }; /// \brief Adapter for IDs with names (all but levelled lists and statics) @@ -247,7 +251,12 @@ namespace CSMWorld const RefIdColumn *mWeight; const RefIdColumn *mValue; - InventoryColumns (const NameColumns& base) : NameColumns (base) {} + InventoryColumns (const NameColumns& base) + : NameColumns (base) + , mIcon(nullptr) + , mWeight(nullptr) + , mValue(nullptr) + {} }; /// \brief Adapter for IDs that can go into an inventory @@ -405,7 +414,11 @@ namespace CSMWorld const RefIdColumn *mEnchantment; const RefIdColumn *mEnchantmentPoints; - EnchantableColumns (const InventoryColumns& base) : InventoryColumns (base) {} + EnchantableColumns (const InventoryColumns& base) + : InventoryColumns (base) + , mEnchantment(nullptr) + , mEnchantmentPoints(nullptr) + {} }; /// \brief Adapter for enchantable IDs @@ -474,7 +487,11 @@ namespace CSMWorld const RefIdColumn *mQuality; const RefIdColumn *mUses; - ToolColumns (const InventoryColumns& base) : InventoryColumns (base) {} + ToolColumns (const InventoryColumns& base) + : InventoryColumns (base) + , mQuality(nullptr) + , mUses(nullptr) + {} }; /// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes) @@ -549,7 +566,17 @@ namespace CSMWorld const RefIdColumn *mAiPackages; std::map mServices; - ActorColumns (const NameColumns& base) : NameColumns (base) {} + ActorColumns (const NameColumns& base) + : NameColumns (base) + , mHello(nullptr) + , mFlee(nullptr) + , mFight(nullptr) + , mAlarm(nullptr) + , mInventory(nullptr) + , mSpells(nullptr) + , mDestinations(nullptr) + , mAiPackages(nullptr) + {} }; /// \brief Adapter for actor IDs (handles common AI functionality) @@ -2054,7 +2081,11 @@ namespace CSMWorld const RefIdColumn *mLevList; const RefIdColumn *mNestedListLevList; - LevListColumns (const BaseColumns& base) : BaseColumns (base) {} + LevListColumns (const BaseColumns& base) + : BaseColumns (base) + , mLevList(nullptr) + , mNestedListLevList(nullptr) + {} }; template diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 331927155..b6fe05d0f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -815,6 +815,7 @@ namespace MWRender RenderingManager::RayResult result; result.mHit = false; result.mHitRefnum.mContentFile = -1; + result.mHitRefnum.mIndex = -1; result.mRatio = 0; if (intersector->containsIntersections()) { diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index d953f1dc2..84a31b3bd 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -42,6 +42,7 @@ void ESM::Header::load (ESMReader &esm) MasterData m; m.name = esm.getHString(); m.size = esm.getHNLong ("DATA"); + m.index = -1; mMaster.push_back (m); } diff --git a/components/misc/frameratelimiter.hpp b/components/misc/frameratelimiter.hpp index b8e210165..b727074d2 100644 --- a/components/misc/frameratelimiter.hpp +++ b/components/misc/frameratelimiter.hpp @@ -13,6 +13,7 @@ namespace Misc explicit FrameRateLimiter(std::chrono::duration maxFrameDuration, std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now()) : mMaxFrameDuration(std::chrono::duration_cast(maxFrameDuration)) + , mLastFrameDuration(0) , mLastMeasurement(now) {} diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 66d48f971..287365a83 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -226,6 +226,7 @@ namespace Resource , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) , mApplyLightingToEnvMaps(false) + , mConvertAlphaTestToAlphaToCoverage(false) , mInstanceCache(new MultiObjectCache) , mSharedStateManager(new SharedStateManager) , mImageManager(imageManager) diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 8d6a124e2..ca223ae3b 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -375,6 +375,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v pack_evt.y = mMouseY = evt.motion.y; pack_evt.xrel = evt.motion.xrel; pack_evt.yrel = evt.motion.yrel; + pack_evt.type = SDL_MOUSEMOTION; if (mFirstMouseMove) { // first event should be treated as non-relative, since there's no point of reference @@ -387,6 +388,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v { mMouseZ += pack_evt.zrel = (evt.wheel.y * 120); pack_evt.z = mMouseZ; + pack_evt.type = SDL_MOUSEWHEEL; } else { diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 612c9011d..b0013538f 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -50,6 +50,7 @@ namespace Shader , mAutoUseNormalMaps(false) , mAutoUseSpecularMaps(false) , mApplyLightingToEnvMaps(false) + , mConvertAlphaTestToAlphaToCoverage(false) , mTranslucentFramebuffer(false) , mShaderManager(shaderManager) , mImageManager(imageManager) diff --git a/extern/osg-ffmpeg-videoplayer/videostate.cpp b/extern/osg-ffmpeg-videoplayer/videostate.cpp index a35c845db..c153aa14c 100644 --- a/extern/osg-ffmpeg-videoplayer/videostate.cpp +++ b/extern/osg-ffmpeg-videoplayer/videostate.cpp @@ -50,8 +50,9 @@ VideoState::VideoState() , av_sync_type(AV_SYNC_DEFAULT) , audio_st(nullptr) , video_st(nullptr), frame_last_pts(0.0) - , video_clock(0.0), sws_context(nullptr), pictq_size(0) - , pictq_rindex(0), pictq_windex(0) + , video_clock(0.0), sws_context(nullptr) + , sws_context_w(0), sws_context_h(0) + , pictq_size(0), pictq_rindex(0), pictq_windex(0) , mSeekRequested(false) , mSeekPos(0) , mVideoEnded(false) From c989fac67b6de8e6df9676fbf34a6f06d1b053e6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 11:20:12 +0400 Subject: [PATCH 32/44] Add bound for pointers cache size, as it specified in docs --- apps/openmw/mwworld/cells.cpp | 9 +++++---- apps/openmw/mwworld/cells.hpp | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 15c1b46ba..40ad62a40 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -132,9 +132,11 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), - mIdCache (Settings::Manager::getInt("pointers cache size", "Cells"), std::pair ("", (CellStore*)nullptr)), mIdCacheIndex (0) -{} +{ + int cacheSize = std::max(Settings::Manager::getInt("pointers cache size", "Cells"), 0); + mIdCache = IdCache(cacheSize, std::pair ("", (CellStore*)nullptr)); +} MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y) { @@ -259,8 +261,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) { // First check the cache - for (std::vector >::iterator iter (mIdCache.begin()); - iter!=mIdCache.end(); ++iter) + for (IdCache::iterator iter (mIdCache.begin()); iter!=mIdCache.end(); ++iter) if (iter->first==name && iter->second) { Ptr ptr = getPtr (name, *iter->second); diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 90ede409b..654d9a14b 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -28,11 +28,12 @@ namespace MWWorld /// \brief Cell container class Cells { + typedef std::vector > IdCache; const MWWorld::ESMStore& mStore; std::vector& mReader; mutable std::map mInteriors; mutable std::map, CellStore> mExteriors; - std::vector > mIdCache; + IdCache mIdCache; std::size_t mIdCacheIndex; Cells (const Cells&); From 903b89a0ffafed6f92bfc0c6265d37aeedcdc70e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 11:21:53 +0400 Subject: [PATCH 33/44] Add bound for UI scale factor, as it specified in docs --- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwinput/controllermanager.cpp | 2 +- apps/openmw/mwinput/mousemanager.cpp | 2 +- apps/openmw/mwrender/localmap.cpp | 2 +- components/fontloader/fontloader.cpp | 3 ++- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index b0749d4bd..6152efaa9 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -71,7 +71,7 @@ namespace MWGui , mUpdateTimer(0.f) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - if (uiScale > 1.0) + if (uiScale > 0.f) mScaleFactor = uiScale; mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 48091541c..d17a4bd95 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -70,7 +70,7 @@ namespace MWInput } float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - if (uiScale != 0.f) + if (uiScale > 0.f) mInvUiScalingFactor = 1.f / uiScale; float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input"); diff --git a/apps/openmw/mwinput/mousemanager.cpp b/apps/openmw/mwinput/mousemanager.cpp index 4816470ff..8df116baa 100644 --- a/apps/openmw/mwinput/mousemanager.cpp +++ b/apps/openmw/mwinput/mousemanager.cpp @@ -37,7 +37,7 @@ namespace MWInput , mGuiCursorEnabled(true) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - if (uiScale != 0.f) + if (uiScale > 0.f) mInvUiScalingFactor = 1.f / uiScale; int w,h; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 64931aa88..25d859e54 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -90,7 +90,7 @@ LocalMap::LocalMap(osg::Group* root) { // Increase map resolution, if use UI scaling float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - if (uiScale > 1.0) + if (uiScale > 0.f) mMapResolution *= uiScale; SceneUtil::FindByNameVisitor find("Scene Root"); diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 2bed079e1..98fce32d2 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -569,7 +569,8 @@ namespace Gui resolution = std::min(960, std::max(48, resolution)); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); - resolution *= uiScale; + if (uiScale > 0.f) + resolution *= uiScale; MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property"); resolutionNode->addAttribute("key", "Resolution"); From 1db369f4184a083ed6c57be8f3d4341ebc3da1d6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 11:26:54 +0400 Subject: [PATCH 34/44] Do not use unchecked value in calculations --- apps/openmw/mwgui/spellcreationdialog.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index f9de469e2..5a5dec60f 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -393,7 +393,8 @@ namespace MWGui MWWorld::Ptr player = MWMechanics::getPlayer(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if (MyGUI::utility::parseInt(mPriceLabel->getCaption()) > playerGold) + int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); + if (price > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; @@ -401,8 +402,6 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - int price = MyGUI::utility::parseInt(mPriceLabel->getCaption()); - player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool From f984e96b347730469da4d49f22a489bfef316522 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 12:23:03 +0400 Subject: [PATCH 35/44] Use conventional names for atan2 arguments --- apps/openmw/mwmechanics/aiavoiddoor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 73a638563..6a59ae2bf 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -45,13 +45,13 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont return true; //Door is no longer opening ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door - float x = pos.pos[0] - tPos.pos[0]; - float y = pos.pos[1] - tPos.pos[1]; + float x = pos.pos[1] - tPos.pos[1]; + float y = pos.pos[0] - tPos.pos[0]; actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); // Turn away from the door and move when turn completed - if (zTurn(actor, std::atan2(x,y) + getAdjustedAngle(), osg::DegreesToRadians(5.f))) + if (zTurn(actor, std::atan2(y,x) + getAdjustedAngle(), osg::DegreesToRadians(5.f))) actor.getClass().getMovementSettings(actor).mPosition[1] = 1; else actor.getClass().getMovementSettings(actor).mPosition[1] = 0; From 45b1c68af437ea26b193fd823ffea84f79eb2864 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 10 Apr 2021 12:32:12 +0400 Subject: [PATCH 36/44] Remove annotation which does not work --- components/crashcatcher/crashcatcher.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 4ad856548..b4b2a4a0c 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -146,11 +146,10 @@ static void gdb_info(pid_t pid) /* * Create a temp file to put gdb commands into. * Note: POSIX.1-2008 declares that the file should be already created with mode 0600 by default. - * Modern systems implement it and and suggest to do not touch masks in multithreaded applications. + * Modern systems implement it and suggest to do not touch masks in multithreaded applications. * So CoverityScan warning is valid only for ancient versions of stdlib. */ strcpy(respfile, "/tmp/gdb-respfile-XXXXXX"); - // coverity[secure_temp] if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr) { fprintf(f, "attach %d\n" From 400cae58e56a17df3ec8d4a35e5615618ba8d064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Lamut?= Date: Sat, 10 Apr 2021 12:52:42 +0000 Subject: [PATCH 37/44] Collada user documentation --- .../reference/modding/custom-models/index.rst | 19 +++++ .../pipeline-blender-collada.rst | 84 +++++++++++++++++++ .../custom-models/pipeline-blender-nif.rst | 17 ++++ .../pipeline-blender-osgnative.rst} | 27 ++---- docs/source/reference/modding/index.rst | 1 + .../modding/texture-modding/index.rst | 1 - 6 files changed, 127 insertions(+), 22 deletions(-) create mode 100644 docs/source/reference/modding/custom-models/index.rst create mode 100644 docs/source/reference/modding/custom-models/pipeline-blender-collada.rst create mode 100644 docs/source/reference/modding/custom-models/pipeline-blender-nif.rst rename docs/source/reference/modding/{texture-modding/native-mesh-format.rst => custom-models/pipeline-blender-osgnative.rst} (77%) diff --git a/docs/source/reference/modding/custom-models/index.rst b/docs/source/reference/modding/custom-models/index.rst new file mode 100644 index 000000000..8f575bf3c --- /dev/null +++ b/docs/source/reference/modding/custom-models/index.rst @@ -0,0 +1,19 @@ +############# +Custom Models +############# + +Custom models can be imported into OpenMW using a variety of formats. Below is a quick overview of supported formats, followed by separate articles with further look at the pipelines. + +* **COLLADA** has no license restrictions and is suitable for modding as well as standalone games based on the OpenMW engine. It supports static and animated models. While it doesn't yet work in all parts of the engine, work is being done to resolve the remaining limitations. + +* **OSG native** has no license restrictions, but currently supports only static, non-animated models. + +* **NIF** is the proprietary format used in the original Morrowind game. It supports static and animated models and everything else the format included in the original game. + +.. toctree:: + :caption: Table of Contents + :maxdepth: 1 + + pipeline-blender-collada + pipeline-blender-osgnative + pipeline-blender-nif diff --git a/docs/source/reference/modding/custom-models/pipeline-blender-collada.rst b/docs/source/reference/modding/custom-models/pipeline-blender-collada.rst new file mode 100644 index 000000000..48a19cb7c --- /dev/null +++ b/docs/source/reference/modding/custom-models/pipeline-blender-collada.rst @@ -0,0 +1,84 @@ +############################## +Blender to OpenMW with Collada +############################## + +First, let's take a look at the pipeline requirements and how the fundamental properties of a scene translate from Blender to OpenMW. + +Requirements +------------ +* `OpenMW 0.47 `_ or later +* `Blender 2.81 `_ or later. Latest confirmed, working version is Blender 2.91 +* `Better COLLADA Exporter `_ tuned for OpenMW +* A model you would like to export + +In addition, OpenMW needs to be configured to read COLLADA (dae) files instead of the default format (nif). In settings.cfg under [Models] section... TODO + +Location +-------- + +Objects keep their visual location and origin they had in the original scene. + + +Rotation +-------- + +* Blender’s +Z axis is up axis in OpenMW +* Blender’s +Y axis is front axis in OpenMW +* Blender’s X axis is left-right axis in OpenMW + + +Scale +----- + +Scale ratio between Blender and OpenMW is 70 to 1. This means 70 blender units translate to 1 m in OpenMW. + +However, a scale factor like this is impractical to work with. A better approach is to work with a scale of 1 Blender unit = 1m and apply the 70 scale factor in the Better COLLADA Exporter. The exporter will automatically scale all object, mesh, armature and animation data. + + +Exporter settings - static models +--------------------------------- + +Better COLLADA Exporter offers various options which are rather straightforward for static models. The important one is last in the list, to apply a scaling factor of 70 to the whole scene, so 1 blender unit equals 1 m in OpenMW. The following settings should be good for general use. +It's also very important to have "export selected" box checked, as otherwise the exporter may just fail with an error message. It's also important to have the correct window open, and the models selected before exporting. + + +Animated models to OpenMW +------------------------- + +Animated models are those where a hierarchy of bones, known as armature, deforms the mesh and makes things move. Besides the topics covered above, the following requirements apply. + +Armature +-------- + +* For animated models, a single armature per COLLADA file is advised to avoid any potential problems. +* There needs to be a single top-most bone in the armature’s hierarchy, where both the deformation and control bones fall under it. +* Not all bones need to be exported. By disabing the bone’s “Deform” property and using the corresponding option in the exporter, it is possible to export only the bones needed for animation. + + +Animations +---------- + +Every action in Blender is exported as its own animation clip in COLLADA. Actions you don't wish to export need to have "-noexp" added to their name, with the corresponding option enabled in the exporter. + +Due to current limitations of the format / exporter, the keyframes of any action must not overlap the keyframes of any other action. Thus in practice, the keyframes for each action need to be manually offset to their unique range on the timeline. + +An animated .dae file needs a corresponding animation definition file, or textkeys, for OpenMW to understand. Textkeys are set in .txt file with the same name as the model. E.g. OpenMWDude.dae -> OpenMWDude.txt , each line having a textkey and a double number for timesignature. E.g. idle: start 0.03333333333333333. + +Root Motion +----------- + +OpenMW can read the movement of the root (top-most) bone and use it to move objects in the game world. For this to work, the root bone must be animated to move through space. The root bone must, in its default pose, be alligned with the world. + + +Exporter Settings +----------------- + +For animated models, use the following exporter settings. Before export, select all objects you wish to include in the exported file. TODO + + + + + + + + diff --git a/docs/source/reference/modding/custom-models/pipeline-blender-nif.rst b/docs/source/reference/modding/custom-models/pipeline-blender-nif.rst new file mode 100644 index 000000000..a1b7b0f70 --- /dev/null +++ b/docs/source/reference/modding/custom-models/pipeline-blender-nif.rst @@ -0,0 +1,17 @@ +########################## +Blender to OpenMW with NIF +########################## + +There is a lot of information available around the Internet on how to work with NIF files. We recommend you refer to https://www.niftools.org/ for more information. + +For Blender specifically, you will need the following requirements. + +Requirements +------------ +* `OpenMW `_ +* `Blender 2.8+ `_ +* Either `Niftools addon `_ +* Or `Morrowind Blender Plugin `_ +* A model you would like to export + + diff --git a/docs/source/reference/modding/texture-modding/native-mesh-format.rst b/docs/source/reference/modding/custom-models/pipeline-blender-osgnative.rst similarity index 77% rename from docs/source/reference/modding/texture-modding/native-mesh-format.rst rename to docs/source/reference/modding/custom-models/pipeline-blender-osgnative.rst index c2ddd26ba..95bc7e5ac 100644 --- a/docs/source/reference/modding/texture-modding/native-mesh-format.rst +++ b/docs/source/reference/modding/custom-models/pipeline-blender-osgnative.rst @@ -1,17 +1,9 @@ -################## -Native Mesh Format -################## +################################# +Blender to OpenMW with OSG native +################################# -This article explains how to export a model from Blender to OpenMW using the OSG model format. -Starting with OpenMW version 0.38 we can utilize the OSG native model format. -The OSG model format doesn't yet support all the features that NIF's support, -but works for basic models. For more details on the format, refer to -`this forum post `_. - -Previously, NIF files were the only way to get models into the game. -Unfortunately, the NIF format is proprietary, bloated, -and the available exporters are not in great shape. -For example, the Blender NIF exporter currently only works with the very old Blender 2.49. +This article explains how to export a model from Blender to OpenMW using the OSG model format. It supports only basic, static models. +For more details on the format, refer to `this forum post `_. Prerequisites ############# @@ -103,12 +95,5 @@ Using shaders/normal maps ######################### See :ref:`OSG Native Files` + -Conclusion -########## - -These are the basics of getting a textured, static model from Blender into the game. -In the future, we will want a way to add texture animations, -skeletal animations, separate collision shapes, -and some other features that are currently only available via NIF files. -We will likely add these features to the native OSG format after OpenMW 1.0. \ No newline at end of file diff --git a/docs/source/reference/modding/index.rst b/docs/source/reference/modding/index.rst index 69ec0a56a..df98137d8 100644 --- a/docs/source/reference/modding/index.rst +++ b/docs/source/reference/modding/index.rst @@ -22,6 +22,7 @@ about creating new content for OpenMW, please refer to mod-install settings/index texture-modding/index + custom-models/index font extended paths diff --git a/docs/source/reference/modding/texture-modding/index.rst b/docs/source/reference/modding/texture-modding/index.rst index 3e0b359ee..aa2802af5 100644 --- a/docs/source/reference/modding/texture-modding/index.rst +++ b/docs/source/reference/modding/texture-modding/index.rst @@ -13,4 +13,3 @@ to texture modding in OpenMW. texture-basics convert-bump-mapped-mods - native-mesh-format From 010f290fd525f4b71eacb63a9d2a590b9f34dc11 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 11 Apr 2021 14:07:12 +0200 Subject: [PATCH 38/44] Update OSX deployment target to 10.14 To support std::variant --- CI/before_script.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh index 36fa79299..f9191eb89 100755 --- a/CI/before_script.osx.sh +++ b/CI/before_script.osx.sh @@ -16,7 +16,7 @@ cmake \ -D CMAKE_CXX_FLAGS="-stdlib=libc++" \ -D CMAKE_C_FLAGS_RELEASE="-g -O0" \ -D CMAKE_CXX_FLAGS_RELEASE="-g -O0" \ --D CMAKE_OSX_DEPLOYMENT_TARGET="10.12" \ +-D CMAKE_OSX_DEPLOYMENT_TARGET="10.14" \ -D CMAKE_BUILD_TYPE=RELEASE \ -D OPENMW_OSX_DEPLOYMENT=TRUE \ -D BUILD_OPENMW=TRUE \ From b91be1e8035f36fdfd5cee44b706bafd82a2a047 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sun, 11 Apr 2021 14:12:31 +0200 Subject: [PATCH 39/44] Catch exceptions in ResolutionListener --- apps/openmw/mwworld/containerstore.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 635485dde..86c5ec331 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -77,10 +77,24 @@ MWWorld::ResolutionListener::~ResolutionListener() { if(!mStore.mModified && mStore.mResolved && !mStore.mPtr.isEmpty()) { - for(const auto&& ptr : mStore) - ptr.getRefData().setCount(0); + try + { + for(const auto&& ptr : mStore) + ptr.getRefData().setCount(0); + } + catch(const std::exception& e) + { + Log(Debug::Warning) << "Failed to clear temporary container contents of " << mStore.mPtr.get()->mBase->mId << ": " << e.what(); + } mStore.fillNonRandom(mStore.mPtr.get()->mBase->mInventory, "", mStore.mSeed); - addScripts(mStore, mStore.mPtr.mCell); + try + { + addScripts(mStore, mStore.mPtr.mCell); + } + catch(const std::exception& e) + { + Log(Debug::Warning) << "Failed to restart item scripts inside " << mStore.mPtr.get()->mBase->mId << ": " << e.what(); + } mStore.mResolved = false; } } From fda639eb57f3180a4a87b8d2879ffbb381f7eb32 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sun, 14 Mar 2021 16:21:33 +0100 Subject: [PATCH 40/44] Remove unused forward declarations --- apps/openmw/engine.hpp | 20 -------------------- apps/openmw/mwbase/windowmanager.hpp | 1 - apps/openmw/mwgui/container.hpp | 6 ------ apps/openmw/mwgui/dialogue.hpp | 5 ----- apps/openmw/mwgui/race.hpp | 5 ----- apps/openmw/mwgui/review.hpp | 5 ----- apps/openmw/mwgui/settingswindow.hpp | 5 ----- apps/openmw/mwgui/spellbuyingwindow.hpp | 5 ----- apps/openmw/mwgui/statswindow.hpp | 2 -- apps/openmw/mwgui/textinput.hpp | 5 ----- apps/openmw/mwgui/travelwindow.hpp | 6 ------ apps/openmw/mwgui/windowbase.hpp | 6 ------ apps/openmw/mwgui/windowmanagerimp.hpp | 1 - apps/openmw/mwgui/windowpinnablebase.hpp | 2 -- apps/openmw/mwscript/interpretercontext.hpp | 10 ---------- apps/openmw/mwworld/scene.hpp | 1 - apps/openmw/mwworld/worldimp.hpp | 2 -- components/nifosg/controller.hpp | 5 ----- 18 files changed, 92 deletions(-) diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index ff362f4b6..1aef62df5 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -33,26 +33,6 @@ namespace Compiler class Context; } -namespace MWScript -{ - class ScriptManager; -} - -namespace MWSound -{ - class SoundManager; -} - -namespace MWWorld -{ - class World; -} - -namespace MWGui -{ - class WindowManager; -} - namespace Files { struct ConfigurationManager; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 29d404777..9bbea9c51 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -32,7 +32,6 @@ namespace MyGUI namespace ESM { - struct Class; class ESMReader; class ESMWriter; struct CellId; diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index feda123fb..85c0dddc6 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -6,11 +6,6 @@ #include "itemmodel.hpp" -namespace MWWorld -{ - class Environment; -} - namespace MyGUI { class Gui; @@ -19,7 +14,6 @@ namespace MyGUI namespace MWGui { - class WindowManager; class ContainerWindow; class ItemView; class SortFilterItemModel; diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 02401f2e1..ac6303e20 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -15,11 +15,6 @@ namespace Gui class MWList; } -namespace MWGui -{ - class WindowManager; -} - namespace MWGui { class ResponseCallback; diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 0299c2a1a..170c1dbce 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -7,11 +7,6 @@ #include -namespace MWGui -{ - class WindowManager; -} - namespace MWRender { class RaceSelectionPreview; diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index bd17c7afb..cb847536d 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -11,11 +11,6 @@ namespace ESM struct Spell; } -namespace MWGui -{ - class WindowManager; -} - namespace MWGui { class ReviewDialog : public WindowModal diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 6f25dd114..c268514dc 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -3,11 +3,6 @@ #include "windowbase.hpp" -namespace MWGui -{ - class WindowManager; -} - namespace MWGui { class SettingsWindow : public WindowBase diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 622548c95..f46c43796 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -15,11 +15,6 @@ namespace MyGUI class Widget; } -namespace MWGui -{ - class WindowManager; -} - namespace MWGui { class SpellBuyingWindow : public ReferenceInterface, public WindowBase diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 24f302580..bf78cde34 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -6,8 +6,6 @@ namespace MWGui { - class WindowManager; - class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener { public: diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index 84d9d032d..4d365eb44 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -3,11 +3,6 @@ #include "windowbase.hpp" -namespace MWGui -{ - class WindowManager; -} - namespace MWGui { class TextInputDialog : public WindowModal diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 962d17161..00b7db730 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -11,12 +11,6 @@ namespace MyGUI class Widget; } -namespace MWGui -{ - class WindowManager; -} - - namespace MWGui { class TravelWindow : public ReferenceInterface, public WindowBase diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index 8afb2321e..90ef2118d 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -3,11 +3,6 @@ #include "layout.hpp" -namespace MWBase -{ - class WindowManager; -} - namespace MWWorld { class Ptr; @@ -15,7 +10,6 @@ namespace MWWorld namespace MWGui { - class WindowManager; class DragAndDrop; class WindowBase: public Layout diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index cc1a1b694..3fd8b132f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -114,7 +114,6 @@ namespace MWGui class TrainingWindow; class SpellIcons; class MerchantRepair; - class Repair; class SoulgemDialog; class Recharge; class CompanionWindow; diff --git a/apps/openmw/mwgui/windowpinnablebase.hpp b/apps/openmw/mwgui/windowpinnablebase.hpp index a94212819..c91f0a148 100644 --- a/apps/openmw/mwgui/windowpinnablebase.hpp +++ b/apps/openmw/mwgui/windowpinnablebase.hpp @@ -5,8 +5,6 @@ namespace MWGui { - class WindowManager; - class WindowPinnableBase: public WindowBase { public: diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index c1481d6d0..298454bcd 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -10,16 +10,6 @@ #include "../mwworld/ptr.hpp" -namespace MWSound -{ - class SoundManager; -} - -namespace MWInput -{ - struct MWInputManager; -} - namespace MWScript { class Locals; diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index a70d3ccdd..f87a0ca73 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -36,7 +36,6 @@ namespace Loading namespace DetourNavigator { struct Navigator; - class Water; } namespace MWRender diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 33b23e065..5447e20c3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -60,8 +60,6 @@ namespace ToUTF8 class Utf8Encoder; } -struct ContentLoader; - namespace MWPhysics { class Object; diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 96beafcbb..b45916693 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -24,11 +24,6 @@ namespace osg class Material; } -namespace osgParticle -{ - class Emitter; -} - namespace NifOsg { From efb241f1de280987a79526c1d817c1f9ebe5feaa Mon Sep 17 00:00:00 2001 From: fredzio Date: Mon, 29 Mar 2021 19:44:10 +0200 Subject: [PATCH 41/44] Use override instead of virtual --- apps/openmw/mwsound/soundmanagerimp.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 6b9de800f..934402cd4 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -141,7 +141,7 @@ namespace MWSound public: SoundManager(const VFS::Manager* vfs, bool useSound); - virtual ~SoundManager(); + ~SoundManager() override; void processChangedSettings(const Settings::CategorySettingVector& settings) override; From 32981bcd88d411090d324e410e0084297b8c3f97 Mon Sep 17 00:00:00 2001 From: fredzio Date: Mon, 29 Mar 2021 19:44:23 +0200 Subject: [PATCH 42/44] Constify a few things --- apps/openmw/mwgui/timeadvancer.cpp | 4 ++-- apps/openmw/mwgui/timeadvancer.hpp | 4 ++-- apps/openmw/mwrender/objectpaging.hpp | 2 +- apps/openmw/mwstate/quicksavemanager.cpp | 4 ++-- apps/openmw/mwstate/quicksavemanager.hpp | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/timeadvancer.cpp b/apps/openmw/mwgui/timeadvancer.cpp index a07da1682..c38094ae4 100644 --- a/apps/openmw/mwgui/timeadvancer.cpp +++ b/apps/openmw/mwgui/timeadvancer.cpp @@ -58,12 +58,12 @@ namespace MWGui } } - int TimeAdvancer::getHours() + int TimeAdvancer::getHours() const { return mHours; } - bool TimeAdvancer::isRunning() + bool TimeAdvancer::isRunning() const { return mRunning; } diff --git a/apps/openmw/mwgui/timeadvancer.hpp b/apps/openmw/mwgui/timeadvancer.hpp index 8367b5a8b..b8456f376 100644 --- a/apps/openmw/mwgui/timeadvancer.hpp +++ b/apps/openmw/mwgui/timeadvancer.hpp @@ -14,8 +14,8 @@ namespace MWGui void stop(); void onFrame(float dt); - int getHours(); - bool isRunning(); + int getHours() const; + bool isRunning() const; // signals typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 65f53d530..c24cdf4f8 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -63,7 +63,7 @@ namespace MWRender { std::set mDisabled; std::set mBlacklist; - bool operator==(const RefTracker&other) { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; } + bool operator==(const RefTracker&other) const { return mDisabled == other.mDisabled && mBlacklist == other.mBlacklist; } }; RefTracker mRefTracker; RefTracker mRefTrackerNew; diff --git a/apps/openmw/mwstate/quicksavemanager.cpp b/apps/openmw/mwstate/quicksavemanager.cpp index df078e026..bf1781520 100644 --- a/apps/openmw/mwstate/quicksavemanager.cpp +++ b/apps/openmw/mwstate/quicksavemanager.cpp @@ -18,14 +18,14 @@ void MWState::QuickSaveManager::visitSave(const Slot *saveSlot) } } -bool MWState::QuickSaveManager::isOldestSave(const Slot *compare) +bool MWState::QuickSaveManager::isOldestSave(const Slot *compare) const { if(mOldestSlotVisited == nullptr) return true; return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp); } -bool MWState::QuickSaveManager::shouldCreateNewSlot() +bool MWState::QuickSaveManager::shouldCreateNewSlot() const { return (mSlotsVisited < mMaxSaves); } diff --git a/apps/openmw/mwstate/quicksavemanager.hpp b/apps/openmw/mwstate/quicksavemanager.hpp index a5237d7c3..cdeff42c2 100644 --- a/apps/openmw/mwstate/quicksavemanager.hpp +++ b/apps/openmw/mwstate/quicksavemanager.hpp @@ -13,8 +13,8 @@ namespace MWState{ unsigned int mSlotsVisited; const Slot *mOldestSlotVisited; private: - bool shouldCreateNewSlot(); - bool isOldestSave(const Slot *compare); + bool shouldCreateNewSlot() const; + bool isOldestSave(const Slot *compare) const; public: QuickSaveManager(std::string &saveName, unsigned int maxSaves); ///< A utility class to manage multiple quicksave slots From 95042a2a688e674fae259df8a23616d97780cdee Mon Sep 17 00:00:00 2001 From: jvoisin Date: Sun, 11 Apr 2021 20:31:24 +0200 Subject: [PATCH 43/44] Use the number of logical cores on the CI on OSX --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 713cc2601..c26235d0a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -105,7 +105,7 @@ MacOS: - rm -fr build/* # remove anything in the build directory - CI/before_install.osx.sh - CI/before_script.osx.sh - - cd build; make -j2 package + - cd build; make -j $(sysctl -n hw.logicalcpu) package - for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done artifacts: paths: From 56ede535b5e2f8bf818c4ade2c9456fb399190cf Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Mon, 12 Apr 2021 08:31:45 +0200 Subject: [PATCH 44/44] Don't perform a hit test outside the page's bounds --- CHANGELOG.md | 1 + apps/openmw/mwgui/bookpage.cpp | 76 +++++++++++++++------------------- 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c7834566..a506079e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs Bug #5912: ImprovedBound mod doesn't work Bug #5914: BM: The Swimmer can't reach destination + Bug #5923: Clicking on empty spaces between journal entries might show random topics Bug #5934: AddItem command doesn't accept negative values Feature #390: 3rd person look "over the shoulder" Feature #832: OpenMW-CS: Handle deleted references diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index c70783f39..fba136f88 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -1,5 +1,7 @@ #include "bookpage.hpp" +#include + #include "MyGUI_RenderItem.h" #include "MyGUI_RenderManager.h" #include "MyGUI_TextureUtility.h" @@ -894,6 +896,27 @@ protected: return mIsPageReset || (mPage != page); } + std::optional getAdjustedPos(int left, int top, bool move = false) + { + if (!mBook) + return {}; + + if (mPage >= mBook->mPages.size()) + return {}; + + MyGUI::IntPoint pos (left, top); +#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3) + // work around inconsistency in MyGUI where the mouse press coordinates aren't + // transformed by the current Layer (even though mouse *move* events are). + if(!move) + pos = mNode->getLayer()->getPosition(left, top); +#endif + pos.left -= mCroppedParent->getAbsoluteLeft (); + pos.top -= mCroppedParent->getAbsoluteTop (); + pos.top += mViewTop; + return pos; + } + public: typedef TypesetBookImpl::StyleImpl Style; @@ -952,16 +975,10 @@ public: void onMouseMove (int left, int top) { - if (!mBook) - return; - - if (mPage >= mBook->mPages.size()) - return; - - left -= mCroppedParent->getAbsoluteLeft (); - top -= mCroppedParent->getAbsoluteTop (); - - Style * hit = mBook->hitTestWithMargin (left, mViewTop + top); + Style * hit = nullptr; + if(auto pos = getAdjustedPos(left, top, true)) + if(pos->top <= mViewBottom) + hit = mBook->hitTestWithMargin (pos->left, pos->top); if (mLastDown == MyGUI::MouseButton::None) { @@ -991,24 +1008,11 @@ public: void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id) { - if (!mBook) - return; + auto pos = getAdjustedPos(left, top); - if (mPage >= mBook->mPages.size()) - return; - - // work around inconsistency in MyGUI where the mouse press coordinates aren't - // transformed by the current Layer (even though mouse *move* events are). - MyGUI::IntPoint pos (left, top); -#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3) - pos = mNode->getLayer()->getPosition(left, top); -#endif - pos.left -= mCroppedParent->getAbsoluteLeft (); - pos.top -= mCroppedParent->getAbsoluteTop (); - - if (mLastDown == MyGUI::MouseButton::None) + if (pos && mLastDown == MyGUI::MouseButton::None) { - mFocusItem = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top); + mFocusItem = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr; mItemActive = true; dirtyFocusItem (); @@ -1019,25 +1023,11 @@ public: void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id) { - if (!mBook) - return; + auto pos = getAdjustedPos(left, top); - if (mPage >= mBook->mPages.size()) - return; - - // work around inconsistency in MyGUI where the mouse release coordinates aren't - // transformed by the current Layer (even though mouse *move* events are). - MyGUI::IntPoint pos (left, top); -#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3) - pos = mNode->getLayer()->getPosition(left, top); -#endif - - pos.left -= mCroppedParent->getAbsoluteLeft (); - pos.top -= mCroppedParent->getAbsoluteTop (); - - if (mLastDown == id) + if (pos && mLastDown == id) { - Style * item = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top); + Style * item = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr; bool clicked = mFocusItem == item;