mirror of
https://github.com/OpenMW/openmw.git
synced 2025-11-29 14:34:29 +00:00
Merge remote-tracking branch 'origin/master' into spellgc
This commit is contained in:
commit
ae9c42e0a0
516 changed files with 43541 additions and 8884 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
Checks: >
|
Checks: >
|
||||||
-*,
|
-*,
|
||||||
portability-*,
|
portability-*,
|
||||||
|
-portability-template-virtual-member-function,
|
||||||
clang-analyzer-*,
|
clang-analyzer-*,
|
||||||
-clang-analyzer-optin.*,
|
-clang-analyzer-optin.*,
|
||||||
-clang-analyzer-cplusplus.NewDeleteLeaks,
|
-clang-analyzer-cplusplus.NewDeleteLeaks,
|
||||||
|
|
@ -13,3 +14,7 @@ HeaderFilterRegex: '(apps|components)/'
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- key: readability-identifier-naming.ConceptCase
|
- key: readability-identifier-naming.ConceptCase
|
||||||
value: CamelCase
|
value: CamelCase
|
||||||
|
- key: readability-identifier-naming.NamespaceCase
|
||||||
|
value: CamelCase
|
||||||
|
- key: readability-identifier-naming.NamespaceIgnoredRegexp
|
||||||
|
value: 'bpo|osg(DB|FX|Particle|Shadow|Viewer|Util)?'
|
||||||
|
|
|
||||||
122
.gitlab-ci.yml
122
.gitlab-ci.yml
|
|
@ -27,14 +27,14 @@ variables:
|
||||||
.Ubuntu_Image:
|
.Ubuntu_Image:
|
||||||
tags:
|
tags:
|
||||||
- saas-linux-medium-amd64
|
- saas-linux-medium-amd64
|
||||||
image: ubuntu:22.04
|
image: ubuntu:24.04
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"
|
- if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
|
||||||
Ubuntu_GCC_preprocess:
|
Ubuntu_GCC_preprocess:
|
||||||
extends: .Ubuntu_Image
|
extends: .Ubuntu_Image
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_preprocess.ubuntu_22.04.v1
|
key: Ubuntu_GCC_preprocess.ubuntu_24.04.v1
|
||||||
paths:
|
paths:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
- .cache/pip/
|
- .cache/pip/
|
||||||
|
|
@ -43,9 +43,12 @@ Ubuntu_GCC_preprocess:
|
||||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh openmw-deps openmw-deps-dynamic gcc_preprocess
|
- CI/install_debian_deps.sh openmw-deps openmw-deps-dynamic gcc_preprocess
|
||||||
- pip3 install --user click termtables
|
- pip3 install --user --break-system-packages click termtables
|
||||||
script:
|
script:
|
||||||
- CI/ubuntu_gcc_preprocess.sh
|
- CI/ubuntu_gcc_preprocess.sh
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||||
|
|
||||||
.Ubuntu:
|
.Ubuntu:
|
||||||
extends: .Ubuntu_Image
|
extends: .Ubuntu_Image
|
||||||
|
|
@ -77,9 +80,9 @@ Ubuntu_GCC_preprocess:
|
||||||
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi
|
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi
|
||||||
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_esm_refid_benchmark; fi
|
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_esm_refid_benchmark; fi
|
||||||
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_settings_access_benchmark; fi
|
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_settings_access_benchmark; fi
|
||||||
- ccache -s
|
- ccache -svv
|
||||||
- df -h
|
- df -h
|
||||||
- if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then gcovr --xml-pretty --exclude-unreachable-branches --print-summary --root "${CI_PROJECT_DIR}" -j $(nproc) -o ../coverage.xml; fi
|
- if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then ~/.local/bin/gcovr --xml-pretty --exclude-unreachable-branches --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --print-summary --root "${CI_PROJECT_DIR}" -j $(nproc) -o ../coverage.xml; fi
|
||||||
- ls | grep -v -e '^extern$' -e '^install$' -e '^components-tests.xml$' -e '^openmw-tests.xml$' -e '^openmw-cs-tests.xml$' | xargs -I '{}' rm -rf './{}'
|
- ls | grep -v -e '^extern$' -e '^install$' -e '^components-tests.xml$' -e '^openmw-tests.xml$' -e '^openmw-cs-tests.xml$' | xargs -I '{}' rm -rf './{}'
|
||||||
- cd ..
|
- cd ..
|
||||||
- df -h
|
- df -h
|
||||||
|
|
@ -94,12 +97,12 @@ Ubuntu_GCC_preprocess:
|
||||||
Coverity:
|
Coverity:
|
||||||
tags:
|
tags:
|
||||||
- saas-linux-medium-amd64
|
- saas-linux-medium-amd64
|
||||||
image: ubuntu:22.04
|
image: ubuntu:24.04
|
||||||
stage: build
|
stage: build
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||||
cache:
|
cache:
|
||||||
key: Coverity.ubuntu_22.04.v1
|
key: Coverity.ubuntu_24.04.v1
|
||||||
paths:
|
paths:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
- ccache/
|
- ccache/
|
||||||
|
|
@ -124,7 +127,7 @@ Coverity:
|
||||||
- cov-analysis-linux64-*/bin/cov-configure --template --comptype prefix --compiler ccache
|
- cov-analysis-linux64-*/bin/cov-configure --template --comptype prefix --compiler ccache
|
||||||
# Remove the specific targets and build everything once we can do it under 3h
|
# Remove the specific targets and build everything once we can do it under 3h
|
||||||
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc)
|
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc)
|
||||||
- ccache -s
|
- ccache -svv
|
||||||
after_script:
|
after_script:
|
||||||
- tar cfz cov-int.tar.gz cov-int
|
- tar cfz cov-int.tar.gz cov-int
|
||||||
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
|
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
|
||||||
|
|
@ -138,7 +141,7 @@ Coverity:
|
||||||
Ubuntu_GCC:
|
Ubuntu_GCC:
|
||||||
extends: .Ubuntu
|
extends: .Ubuntu
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC.ubuntu_22.04.v1
|
key: Ubuntu_GCC.ubuntu_24.04.v1
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
||||||
variables:
|
variables:
|
||||||
|
|
@ -151,7 +154,7 @@ Ubuntu_GCC:
|
||||||
Ubuntu_GCC_asan:
|
Ubuntu_GCC_asan:
|
||||||
extends: Ubuntu_GCC
|
extends: Ubuntu_GCC
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_asan.ubuntu_22.04.v1
|
key: Ubuntu_GCC_asan.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_CXX_FLAGS_DEBUG: -g -O1 -fno-omit-frame-pointer -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak
|
CMAKE_CXX_FLAGS_DEBUG: -g -O1 -fno-omit-frame-pointer -fsanitize=address -fsanitize=pointer-subtract -fsanitize=leak
|
||||||
|
|
@ -164,7 +167,7 @@ Clang_Format:
|
||||||
extends: .Ubuntu_Image
|
extends: .Ubuntu_Image
|
||||||
stage: checks
|
stage: checks
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Clang_Format.ubuntu_22.04.v1
|
key: Ubuntu_Clang_Format.ubuntu_24.04.v1
|
||||||
paths:
|
paths:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
variables:
|
variables:
|
||||||
|
|
@ -180,11 +183,11 @@ Lupdate:
|
||||||
extends: .Ubuntu_Image
|
extends: .Ubuntu_Image
|
||||||
stage: checks
|
stage: checks
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_lupdate.ubuntu_22.04.v1
|
key: Ubuntu_lupdate.ubuntu_24.04.v1
|
||||||
paths:
|
paths:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
variables:
|
variables:
|
||||||
LUPDATE: lupdate
|
LUPDATE: /usr/lib/qt6/bin/lupdate
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh openmw-qt-translations
|
- CI/install_debian_deps.sh openmw-qt-translations
|
||||||
script:
|
script:
|
||||||
|
|
@ -206,7 +209,7 @@ Teal:
|
||||||
Ubuntu_GCC_Debug:
|
Ubuntu_GCC_Debug:
|
||||||
extends: .Ubuntu
|
extends: .Ubuntu
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_Debug.ubuntu_22.04.v2
|
key: Ubuntu_GCC_Debug.ubuntu_24.04.v2
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
||||||
variables:
|
variables:
|
||||||
|
|
@ -222,7 +225,7 @@ Ubuntu_GCC_Debug:
|
||||||
Ubuntu_GCC_tests:
|
Ubuntu_GCC_tests:
|
||||||
extends: Ubuntu_GCC
|
extends: Ubuntu_GCC
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_tests.ubuntu_22.04.v1
|
key: Ubuntu_GCC_tests.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -236,7 +239,7 @@ Ubuntu_GCC_tests:
|
||||||
.Ubuntu_GCC_tests_Debug:
|
.Ubuntu_GCC_tests_Debug:
|
||||||
extends: Ubuntu_GCC
|
extends: Ubuntu_GCC
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_tests_Debug.ubuntu_22.04.v1
|
key: Ubuntu_GCC_tests_Debug.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -252,7 +255,7 @@ Ubuntu_GCC_tests:
|
||||||
Ubuntu_GCC_tests_asan:
|
Ubuntu_GCC_tests_asan:
|
||||||
extends: Ubuntu_GCC
|
extends: Ubuntu_GCC
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_tests_asan.ubuntu_22.04.v1
|
key: Ubuntu_GCC_tests_asan.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -268,11 +271,14 @@ Ubuntu_GCC_tests_asan:
|
||||||
when: always
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit: build/*-tests.xml
|
junit: build/*-tests.xml
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||||
|
|
||||||
Ubuntu_GCC_tests_ubsan:
|
Ubuntu_GCC_tests_ubsan:
|
||||||
extends: Ubuntu_GCC
|
extends: Ubuntu_GCC
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_tests_ubsan.ubuntu_22.04.v1
|
key: Ubuntu_GCC_tests_ubsan.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -285,11 +291,14 @@ Ubuntu_GCC_tests_ubsan:
|
||||||
when: always
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit: build/*-tests.xml
|
junit: build/*-tests.xml
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||||
|
|
||||||
.Ubuntu_GCC_tests_tsan:
|
.Ubuntu_GCC_tests_tsan:
|
||||||
extends: Ubuntu_GCC
|
extends: Ubuntu_GCC
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_tests_tsan.ubuntu_22.04.v1
|
key: Ubuntu_GCC_tests_tsan.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -307,11 +316,15 @@ Ubuntu_GCC_tests_ubsan:
|
||||||
Ubuntu_GCC_tests_coverage:
|
Ubuntu_GCC_tests_coverage:
|
||||||
extends: .Ubuntu_GCC_tests_Debug
|
extends: .Ubuntu_GCC_tests_Debug
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_tests_coverage.ubuntu_22.04.v1
|
key: Ubuntu_GCC_tests_coverage.ubuntu_24.04.v1
|
||||||
|
paths:
|
||||||
|
- .cache/pip
|
||||||
variables:
|
variables:
|
||||||
BUILD_WITH_CODE_COVERAGE: 1
|
BUILD_WITH_CODE_COVERAGE: 1
|
||||||
|
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic openmw-coverage
|
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic openmw-coverage
|
||||||
|
- pipx install gcovr
|
||||||
coverage: /^\s*lines:\s*\d+.\d+\%/
|
coverage: /^\s*lines:\s*\d+.\d+\%/
|
||||||
artifacts:
|
artifacts:
|
||||||
paths: []
|
paths: []
|
||||||
|
|
@ -322,6 +335,9 @@ Ubuntu_GCC_tests_coverage:
|
||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: coverage.xml
|
path: coverage.xml
|
||||||
junit: build/*-tests.xml
|
junit: build/*-tests.xml
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||||
|
|
||||||
.Ubuntu_Static_Deps:
|
.Ubuntu_Static_Deps:
|
||||||
extends: Ubuntu_Clang
|
extends: Ubuntu_Clang
|
||||||
|
|
@ -333,7 +349,7 @@ Ubuntu_GCC_tests_coverage:
|
||||||
- "CI/**/*"
|
- "CI/**/*"
|
||||||
- ".gitlab-ci.yml"
|
- ".gitlab-ci.yml"
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Static_Deps.ubuntu_22.04.v1
|
key: Ubuntu_Static_Deps.ubuntu_24.04.v1
|
||||||
paths:
|
paths:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
- ccache/
|
- ccache/
|
||||||
|
|
@ -350,7 +366,7 @@ Ubuntu_GCC_tests_coverage:
|
||||||
.Ubuntu_Static_Deps_tests:
|
.Ubuntu_Static_Deps_tests:
|
||||||
extends: .Ubuntu_Static_Deps
|
extends: .Ubuntu_Static_Deps
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Static_Deps_tests.ubuntu_22.04.v1
|
key: Ubuntu_Static_Deps_tests.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -369,7 +385,7 @@ Ubuntu_Clang:
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic
|
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Clang.ubuntu_22.04.v2
|
key: Ubuntu_Clang.ubuntu_24.04.v2
|
||||||
variables:
|
variables:
|
||||||
CC: clang
|
CC: clang
|
||||||
CXX: clang++
|
CXX: clang++
|
||||||
|
|
@ -382,7 +398,7 @@ Ubuntu_Clang:
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh clang clang-tidy openmw-deps openmw-deps-dynamic
|
- CI/install_debian_deps.sh clang clang-tidy openmw-deps openmw-deps-dynamic
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Clang_Tidy.ubuntu_22.04.v1
|
key: Ubuntu_Clang_Tidy.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_CXX_FLAGS_DEBUG: -O0
|
CMAKE_CXX_FLAGS_DEBUG: -O0
|
||||||
|
|
@ -396,12 +412,15 @@ Ubuntu_Clang:
|
||||||
- cd build
|
- cd build
|
||||||
- find . -name *.o -exec touch {} \;
|
- find . -name *.o -exec touch {} \;
|
||||||
- cmake --build . -- -j $(nproc) ${BUILD_TARGETS}
|
- cmake --build . -- -j $(nproc) ${BUILD_TARGETS}
|
||||||
- ccache -s
|
- ccache -svv
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/
|
- build/
|
||||||
expire_in: 12h
|
expire_in: 12h
|
||||||
timeout: 3h
|
timeout: 3h
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ID != "7107382"
|
||||||
|
|
||||||
Ubuntu_Clang_Tidy_components:
|
Ubuntu_Clang_Tidy_components:
|
||||||
extends: .Ubuntu_Clang_Tidy_Base
|
extends: .Ubuntu_Clang_Tidy_Base
|
||||||
|
|
@ -414,7 +433,7 @@ Ubuntu_Clang_Tidy_openmw:
|
||||||
needs:
|
needs:
|
||||||
- Ubuntu_Clang_Tidy_components
|
- Ubuntu_Clang_Tidy_components
|
||||||
variables:
|
variables:
|
||||||
BUILD_TARGETS: openmw
|
BUILD_TARGETS: openmw openmw-tests
|
||||||
timeout: 3h
|
timeout: 3h
|
||||||
|
|
||||||
Ubuntu_Clang_Tidy_openmw-cs:
|
Ubuntu_Clang_Tidy_openmw-cs:
|
||||||
|
|
@ -430,13 +449,13 @@ Ubuntu_Clang_Tidy_other:
|
||||||
needs:
|
needs:
|
||||||
- Ubuntu_Clang_Tidy_components
|
- Ubuntu_Clang_Tidy_components
|
||||||
variables:
|
variables:
|
||||||
BUILD_TARGETS: bsatool esmtool openmw-launcher openmw-iniimporter openmw-essimporter openmw-wizard niftest components-tests openmw-tests openmw-cs-tests openmw-navmeshtool openmw-bulletobjecttool
|
BUILD_TARGETS: components-tests bsatool esmtool openmw-launcher openmw-iniimporter openmw-essimporter openmw-wizard niftest openmw-navmeshtool openmw-bulletobjecttool
|
||||||
timeout: 3h
|
timeout: 3h
|
||||||
|
|
||||||
.Ubuntu_Clang_tests:
|
.Ubuntu_Clang_tests:
|
||||||
extends: Ubuntu_Clang
|
extends: Ubuntu_Clang
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Clang_tests.ubuntu_22.04.v1
|
key: Ubuntu_Clang_tests.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -450,7 +469,7 @@ Ubuntu_Clang_Tidy_other:
|
||||||
Ubuntu_Clang_tests_Debug:
|
Ubuntu_Clang_tests_Debug:
|
||||||
extends: Ubuntu_Clang
|
extends: Ubuntu_Clang
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Clang_tests_Debug.ubuntu_22.04.v1
|
key: Ubuntu_Clang_tests_Debug.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 1G
|
CCACHE_SIZE: 1G
|
||||||
BUILD_TESTS_ONLY: 1
|
BUILD_TESTS_ONLY: 1
|
||||||
|
|
@ -474,7 +493,7 @@ Ubuntu_Clang_tests_Debug:
|
||||||
- apt-cache/
|
- apt-cache/
|
||||||
before_script:
|
before_script:
|
||||||
- CI/install_debian_deps.sh $OPENMW_DEPS
|
- CI/install_debian_deps.sh $OPENMW_DEPS
|
||||||
- pip3 install --user numpy matplotlib termtables click
|
- pip3 install --user --break-system-packages numpy matplotlib termtables click
|
||||||
script:
|
script:
|
||||||
- CI/run_integration_tests.sh
|
- CI/run_integration_tests.sh
|
||||||
after_script:
|
after_script:
|
||||||
|
|
@ -485,7 +504,7 @@ Ubuntu_Clang_integration_tests:
|
||||||
needs:
|
needs:
|
||||||
- Ubuntu_Clang
|
- Ubuntu_Clang
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_Clang_integration_tests.ubuntu_22.04.v2
|
key: Ubuntu_Clang_integration_tests.ubuntu_24.04.v2
|
||||||
variables:
|
variables:
|
||||||
OPENMW_DEPS: openmw-integration-tests
|
OPENMW_DEPS: openmw-integration-tests
|
||||||
|
|
||||||
|
|
@ -494,15 +513,22 @@ Ubuntu_GCC_integration_tests_asan:
|
||||||
needs:
|
needs:
|
||||||
- Ubuntu_GCC_asan
|
- Ubuntu_GCC_asan
|
||||||
cache:
|
cache:
|
||||||
key: Ubuntu_GCC_integration_tests_asan.ubuntu_22.04.v1
|
key: Ubuntu_GCC_integration_tests_asan.ubuntu_24.04.v1
|
||||||
variables:
|
variables:
|
||||||
OPENMW_DEPS: openmw-integration-tests libasan6
|
OPENMW_DEPS: openmw-integration-tests libasan
|
||||||
ASAN_OPTIONS: halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_leaks=0
|
ASAN_OPTIONS: halt_on_error=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:detect_leaks=0
|
||||||
|
|
||||||
.MacOS:
|
.MacOS:
|
||||||
stage: build
|
stage: build
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_PROJECT_ID == "7107382"
|
- if: $CI_PROJECT_ID != "7107382"
|
||||||
|
when: never
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
when: manual
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule"
|
||||||
|
image: macos-15-xcode-16
|
||||||
|
tags:
|
||||||
|
- saas-macos-medium-m1
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- ccache/
|
- ccache/
|
||||||
|
|
@ -535,28 +561,28 @@ Ubuntu_GCC_integration_tests_asan:
|
||||||
paths:
|
paths:
|
||||||
- build/OpenMW-*.dmg
|
- build/OpenMW-*.dmg
|
||||||
|
|
||||||
macOS14_Xcode15_amd64:
|
macOS15_Xcode16_amd64:
|
||||||
extends: .MacOS
|
extends: .MacOS
|
||||||
image: macos-14-xcode-15
|
|
||||||
tags:
|
|
||||||
- saas-macos-medium-m1
|
|
||||||
cache:
|
cache:
|
||||||
key: macOS14_Xcode15_amd64.v2
|
key: macOS15_Xcode16_amd64.v1
|
||||||
variables:
|
variables:
|
||||||
CCACHE_SIZE: 3G
|
CCACHE_SIZE: 3G
|
||||||
DMG_IDENTIFIER: amd64
|
DMG_IDENTIFIER: amd64
|
||||||
MACOS_AMD64: true
|
MACOS_AMD64: true
|
||||||
|
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||||
|
HOMEBREW_NO_EMOJI: true
|
||||||
|
HOMEBREW_NO_INSTALL_CLEANUP: true
|
||||||
|
|
||||||
macOS14_Xcode15_arm64:
|
macOS15_Xcode16_arm64:
|
||||||
extends: .MacOS
|
extends: .MacOS
|
||||||
image: macos-14-xcode-15
|
|
||||||
tags:
|
|
||||||
- saas-macos-medium-m1
|
|
||||||
cache:
|
cache:
|
||||||
key: macOS14_Xcode15_arm64.v1
|
key: macOS15_Xcode16_arm64.v1
|
||||||
variables:
|
variables:
|
||||||
DMG_IDENTIFIER: arm64
|
DMG_IDENTIFIER: arm64
|
||||||
CCACHE_SIZE: 3G
|
CCACHE_SIZE: 3G
|
||||||
|
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||||
|
HOMEBREW_NO_EMOJI: true
|
||||||
|
HOMEBREW_NO_INSTALL_CLEANUP: true
|
||||||
|
|
||||||
.Compress_And_Upload_Symbols_Base:
|
.Compress_And_Upload_Symbols_Base:
|
||||||
extends: .Ubuntu_Image
|
extends: .Ubuntu_Image
|
||||||
|
|
@ -681,7 +707,7 @@ macOS14_Xcode15_arm64:
|
||||||
- Get-Volume
|
- Get-Volume
|
||||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||||
cache:
|
cache:
|
||||||
key: ninja-2022-v12
|
key: ninja-2022-v13
|
||||||
paths:
|
paths:
|
||||||
- ccache
|
- ccache
|
||||||
- deps
|
- deps
|
||||||
|
|
@ -839,7 +865,7 @@ macOS14_Xcode15_arm64:
|
||||||
- Get-Volume
|
- Get-Volume
|
||||||
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
|
||||||
cache:
|
cache:
|
||||||
key: msbuild-2022-v12
|
key: msbuild-2022-v13
|
||||||
paths:
|
paths:
|
||||||
- deps
|
- deps
|
||||||
- MSVC2022_64/deps/Qt
|
- MSVC2022_64/deps/Qt
|
||||||
|
|
@ -940,7 +966,7 @@ Windows_MSBuild_CacheInit:
|
||||||
- cd build
|
- cd build
|
||||||
- cmake --build . -- -j $(nproc)
|
- cmake --build . -- -j $(nproc)
|
||||||
# - cmake --install . # no one uses builds anyway, disable until 'no space left' is resolved
|
# - cmake --install . # no one uses builds anyway, disable until 'no space left' is resolved
|
||||||
- ccache -s
|
- ccache -svv
|
||||||
- df -h
|
- df -h
|
||||||
- ls | grep -v -e '^extern$' -e '^install$' | xargs -I '{}' rm -rf './{}'
|
- ls | grep -v -e '^extern$' -e '^install$' | xargs -I '{}' rm -rf './{}'
|
||||||
- cd ..
|
- cd ..
|
||||||
|
|
@ -968,7 +994,7 @@ Windows_MSBuild_CacheInit:
|
||||||
paths:
|
paths:
|
||||||
- .cache/pip
|
- .cache/pip
|
||||||
before_script:
|
before_script:
|
||||||
- pip3 install --user requests click discord_webhook
|
- pip3 install --user --break-system-packages requests click discord_webhook
|
||||||
script:
|
script:
|
||||||
- scripts/find_missing_merge_requests.py --project_id=$CI_PROJECT_ID --ignored_mrs_path=$CI_PROJECT_DIR/.resubmitted_merge_requests.txt
|
- scripts/find_missing_merge_requests.py --project_id=$CI_PROJECT_ID --ignored_mrs_path=$CI_PROJECT_DIR/.resubmitted_merge_requests.txt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ If you feel your name is missing from this list, please add it to `AUTHORS.md`.
|
||||||
Programmers
|
Programmers
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Bret Curtis (psi29a) - Project leader 2019-present
|
Alexey Dobrokhotov (Capo) - Project leader 2025-present
|
||||||
|
Bret Curtis (psi29a) - Project leader 2019-2025
|
||||||
Marc Zinnschlag (Zini) - Project leader 2010-2018
|
Marc Zinnschlag (Zini) - Project leader 2010-2018
|
||||||
Nicolay Korslund - Project leader 2008-2010
|
Nicolay Korslund - Project leader 2008-2010
|
||||||
scrawl - Top contributor
|
scrawl - Top contributor
|
||||||
|
|
@ -49,7 +50,6 @@ Programmers
|
||||||
Berulacks
|
Berulacks
|
||||||
Bo Svensson
|
Bo Svensson
|
||||||
Britt Mathis (galdor557)
|
Britt Mathis (galdor557)
|
||||||
Capostrophic
|
|
||||||
Carl Maxwell
|
Carl Maxwell
|
||||||
cc9cii
|
cc9cii
|
||||||
Cédric Mocquillon
|
Cédric Mocquillon
|
||||||
|
|
@ -197,7 +197,7 @@ Programmers
|
||||||
Qlonever
|
Qlonever
|
||||||
Radu-Marius Popovici (rpopovici)
|
Radu-Marius Popovici (rpopovici)
|
||||||
Rafael Moura (dhustkoder)
|
Rafael Moura (dhustkoder)
|
||||||
Randy Davin (Kindi)
|
Randy Davin (Kuyondo)
|
||||||
rdimesio
|
rdimesio
|
||||||
rexelion
|
rexelion
|
||||||
riothamus
|
riothamus
|
||||||
|
|
|
||||||
73
CHANGELOG.md
73
CHANGELOG.md
|
|
@ -1,7 +1,80 @@
|
||||||
0.50.0
|
0.50.0
|
||||||
------
|
------
|
||||||
|
|
||||||
|
Bug #2967: Inventory windows don't update when changing items by script
|
||||||
|
Bug #4437: Transformations for NiSkinInstance are ignored
|
||||||
|
Bug #4885: Disable in dialogue result script causes a crash
|
||||||
|
Bug #5331: Pathfinding works incorrectly when actor is moved from one interior cell to another
|
||||||
|
Bug #6039: Next Spell keybind fails while selected enchanted item has multiple copies
|
||||||
|
Bug #6573: Editor: Selection behaves incorrectly on high-DPI displays
|
||||||
|
Bug #6792: Birth sign info box has no line breaks
|
||||||
|
Bug #7371: Equipping item from inventory does not play a Down sound when equipping fails
|
||||||
|
Bug #7622: Player's marksman weapons don't work on close actors underwater
|
||||||
|
Bug #7649: The sound and vfx of resisted enchanted items' magic still play
|
||||||
|
Bug #7693: I.ItemUsage should return an item to the selected stack if equipping/consumption is denied
|
||||||
|
Bug #7740: Magic items in the HUD aren't composited correctly
|
||||||
|
Bug #7799: Picking up ingredients while object paging active grid is on may cause a hiccup
|
||||||
|
Bug #7871: Kwama Queen doesn't start combat with player
|
||||||
|
Bug #7979: Paralyzed NPCs battlecry
|
||||||
|
Bug #8245: The console command ShowVars does not list global mwscripts
|
||||||
|
Bug #8265: Topics are linked incorrectly
|
||||||
|
Bug #8303: On target spells cast by non-actors should fire underwater
|
||||||
|
Bug #8318: Missing global variables are not handled gracefully in dialogue conditions
|
||||||
|
Bug #8333: Quest status subrecords should not actually cause parsing to skip remaining data
|
||||||
|
Bug #8340: Multi-effect enchantments are too expensive
|
||||||
|
Bug #8341: Repeat shader visitor passes discard parallax
|
||||||
|
Bug #8349: Travel to non-existent cell causes persistent black screen
|
||||||
|
Bug #8359: Some quick keys menu related issues
|
||||||
|
Bug #8371: Silence affects powers
|
||||||
|
Bug #8375: Moon phase cycle doesn't match Morrowind
|
||||||
|
Bug #8383: Casting bound helm or boots on beast races doesn't cleanup properly
|
||||||
|
Bug #8385: Russian encoding broken with locale parameters and calendar
|
||||||
|
Bug #8404: Prevent merchant equipping breaks on lights
|
||||||
|
Bug #8408: OpenMW doesn't report all the potential resting hindrances
|
||||||
|
Bug #8414: Waterwalking works when collision is disabled
|
||||||
|
Bug #8431: Behaviour of removed items from a container is buggy
|
||||||
|
Bug #8432: Changing to and from an interior cell doesn't update collision
|
||||||
|
Bug #8433: Wandering NPCs are not capable of avoiding easy obstacles
|
||||||
|
Bug #8436: Spell selection in a pinned spellbook window doesn't update
|
||||||
|
Bug #8437: Pinned inventory window's pin button doesn't look pressed
|
||||||
|
Bug #8446: Travel prices are strangely inconsistent
|
||||||
|
Bug #8447: Werewolf swimming animation breaks in third person perspective
|
||||||
|
Bug #8459: Changing magic effect base cost doesn't change spell price
|
||||||
|
Bug #8466: Showmap "" reveals nameless cells
|
||||||
|
Bug #8485: Witchwither disease and probably other common diseases don't work correctly
|
||||||
|
Bug #8490: Normals on Water disappear when Water Shader is Enabled but Refraction is Disabled
|
||||||
|
Bug #8500: OpenMW Alarm behaviour doesn't match morrowind.exe
|
||||||
|
Bug #8519: Multiple bounty is sometimes assigned to player when detected during a pickpocketing action
|
||||||
|
Bug #8540: Magic resistance is applied to effects without a magnitude
|
||||||
|
Bug #8557: Charm's disposition changes capped on 100, uncapped below 0
|
||||||
|
Bug #8582: addScript-attached local scripts start out inactive
|
||||||
|
Bug #8585: Dialogue topic list doesn't have enough padding
|
||||||
|
Bug #8587: Minor INI importer problems
|
||||||
|
Bug #8593: Render targets do not generate mipmaps
|
||||||
|
Bug #8598: Post processing shaders don't interact with the vfs correctly
|
||||||
|
Bug #8599: Non-ASCII paths in BSA files don't work
|
||||||
|
Bug #8606: Floating point imprecision can mess with container capacity
|
||||||
|
Bug #8609: The crosshair is too large
|
||||||
|
Bug #8610: Terrain normal maps using NormalGL format instead of NormalDX
|
||||||
|
Bug #8612: Using aiactivate on an ingredient when graphical herbalism is enabled triggers non-stop pickup sounds
|
||||||
|
Bug #8614: Lua garbage collection fails to remove unused data
|
||||||
|
Bug #8615: Rest/wait time progress speed is different from vanilla
|
||||||
|
Feature #2522: Support quick item transfer
|
||||||
|
Feature #3769: Allow GetSpellEffects on enchantments
|
||||||
|
Feature #6976: [Lua] Weather API
|
||||||
|
Feature #8077: Save settings changes when clicking "ok"/closing the window
|
||||||
|
Feature #8112: Expose landscape record data to Lua
|
||||||
|
Feature #8113: Support extended selection in autodetected subdirectory dialog
|
||||||
|
Feature #8139: Editor: Redesign the selection markers
|
||||||
Feature #8285: Expose list of active shaders in postprocessing API
|
Feature #8285: Expose list of active shaders in postprocessing API
|
||||||
|
Feature #8313: Show the character name in the savegame details
|
||||||
|
Feature #8320: Add access mwscript source text to lua api
|
||||||
|
Feature #8334: Lua: AddTopic equivalent
|
||||||
|
Feature #8355: Lua: Window visibility checking in interfaces.UI
|
||||||
|
Feature #8509: FillJournal script instruction
|
||||||
|
Feature #8580: Sort characters in the save loading menu
|
||||||
|
Feature #8597: Lua: Add more built-in event handlers
|
||||||
|
Feature #8629: Expose path grid data to Lua
|
||||||
|
|
||||||
0.49.0
|
0.49.0
|
||||||
------
|
------
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
#!/bin/sh -ex
|
#!/bin/sh -ex
|
||||||
|
|
||||||
export HOMEBREW_NO_EMOJI=1
|
|
||||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
|
||||||
export HOMEBREW_AUTOREMOVE=1
|
|
||||||
|
|
||||||
if [[ "${MACOS_AMD64}" ]]; then
|
if [[ "${MACOS_AMD64}" ]]; then
|
||||||
./CI/macos/before_install.amd64.sh
|
./CI/macos/before_install.amd64.sh
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -377,6 +377,8 @@ case $VS_VERSION in
|
||||||
MSVC_DISPLAY_YEAR="2022"
|
MSVC_DISPLAY_YEAR="2022"
|
||||||
|
|
||||||
QT_MSVC_YEAR="2019"
|
QT_MSVC_YEAR="2019"
|
||||||
|
|
||||||
|
VCPKG_TRIPLET="x64-windows"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
16|16.0|2019 )
|
16|16.0|2019 )
|
||||||
|
|
@ -386,6 +388,8 @@ case $VS_VERSION in
|
||||||
MSVC_DISPLAY_YEAR="2019"
|
MSVC_DISPLAY_YEAR="2019"
|
||||||
|
|
||||||
QT_MSVC_YEAR="2019"
|
QT_MSVC_YEAR="2019"
|
||||||
|
|
||||||
|
VCPKG_TRIPLET="x64-windows-2019"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
15|15.0|2017 )
|
15|15.0|2017 )
|
||||||
|
|
@ -546,7 +550,7 @@ fi
|
||||||
QT_VER='6.6.3'
|
QT_VER='6.6.3'
|
||||||
AQT_VERSION='v3.1.15'
|
AQT_VERSION='v3.1.15'
|
||||||
|
|
||||||
VCPKG_TAG="2024-11-10"
|
VCPKG_TAG="2025-07-23"
|
||||||
VCPKG_PATH="vcpkg-x64-${VS_VERSION:?}-${VCPKG_TAG:?}"
|
VCPKG_PATH="vcpkg-x64-${VS_VERSION:?}-${VCPKG_TAG:?}"
|
||||||
VCPKG_PDB_PATH="vcpkg-x64-${VS_VERSION:?}-pdb-${VCPKG_TAG:?}"
|
VCPKG_PDB_PATH="vcpkg-x64-${VS_VERSION:?}-pdb-${VCPKG_TAG:?}"
|
||||||
VCPKG_MANIFEST="${VCPKG_PATH:?}.txt"
|
VCPKG_MANIFEST="${VCPKG_PATH:?}.txt"
|
||||||
|
|
@ -633,16 +637,16 @@ printf "vcpkg packages ${VCPKG_TAG:?}... "
|
||||||
fi
|
fi
|
||||||
|
|
||||||
add_cmake_opts -DCMAKE_TOOLCHAIN_FILE="$(real_pwd)/${VCPKG_PATH:?}/scripts/buildsystems/vcpkg.cmake"
|
add_cmake_opts -DCMAKE_TOOLCHAIN_FILE="$(real_pwd)/${VCPKG_PATH:?}/scripts/buildsystems/vcpkg.cmake"
|
||||||
add_cmake_opts -DLuaJit_INCLUDE_DIR="$(real_pwd)/${VCPKG_PATH:?}/installed/x64-windows/include/luajit"
|
add_cmake_opts -DLuaJit_INCLUDE_DIR="$(real_pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/include/luajit"
|
||||||
add_cmake_opts -DLuaJit_LIBRARY="$(real_pwd)/${VCPKG_PATH:?}/installed/x64-windows/lib/lua51.lib"
|
add_cmake_opts -DLuaJit_LIBRARY="$(real_pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/lib/lua51.lib"
|
||||||
|
|
||||||
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
for CONFIGURATION in ${CONFIGURATIONS[@]}; do
|
||||||
if [[ ${CONFIGURATION:?} == "Debug" ]]; then
|
if [[ ${CONFIGURATION:?} == "Debug" ]]; then
|
||||||
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/x64-windows/debug/bin"
|
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/debug/bin"
|
||||||
|
|
||||||
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Debug/MyGUIEngine_d.dll"
|
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Debug/MyGUIEngine_d.dll"
|
||||||
else
|
else
|
||||||
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/x64-windows/bin"
|
VCPKG_DLL_BIN="$(pwd)/${VCPKG_PATH:?}/installed/${VCPKG_TRIPLET}/bin"
|
||||||
|
|
||||||
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Release/MyGUIEngine.dll"
|
add_runtime_dlls ${CONFIGURATION:?} "${VCPKG_DLL_BIN:?}/Release/MyGUIEngine.dll"
|
||||||
fi
|
fi
|
||||||
|
|
@ -704,7 +708,6 @@ printf "Qt ${QT_VER}... "
|
||||||
DLLSUFFIX=""
|
DLLSUFFIX=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${QT_MAJOR_VER}" -eq 6 ]; then
|
|
||||||
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_MAJOR_VER}"{Core,Gui,Network,OpenGL,OpenGLWidgets,Widgets,Svg}${DLLSUFFIX}.dll
|
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_MAJOR_VER}"{Core,Gui,Network,OpenGL,OpenGLWidgets,Widgets,Svg}${DLLSUFFIX}.dll
|
||||||
|
|
||||||
# Since Qt 6.7.0 plugin is called "qmodernwindowsstyle"
|
# Since Qt 6.7.0 plugin is called "qmodernwindowsstyle"
|
||||||
|
|
@ -713,10 +716,6 @@ printf "Qt ${QT_VER}... "
|
||||||
else
|
else
|
||||||
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
|
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
add_runtime_dlls $CONFIGURATION "$(pwd)/bin/Qt${QT_MAJOR_VER}"{Core,Gui,Network,OpenGL,Widgets,Svg}${DLLSUFFIX}.dll
|
|
||||||
add_qt_style_dlls $CONFIGURATION "$(pwd)/plugins/styles/qwindowsvistastyle${DLLSUFFIX}.dll"
|
|
||||||
fi
|
|
||||||
|
|
||||||
add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll"
|
add_qt_platform_dlls $CONFIGURATION "$(pwd)/plugins/platforms/qwindows${DLLSUFFIX}.dll"
|
||||||
add_qt_image_dlls $CONFIGURATION "$(pwd)/plugins/imageformats/qsvg${DLLSUFFIX}.dll"
|
add_qt_image_dlls $CONFIGURATION "$(pwd)/plugins/imageformats/qsvg${DLLSUFFIX}.dll"
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
VCPKG_DEPS_TAG=2024-11-10
|
VCPKG_DEPS_TAG=2025-07-23
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ declare -rA GROUPED_DEPS=(
|
||||||
libboost-system-dev libboost-iostreams-dev
|
libboost-system-dev libboost-iostreams-dev
|
||||||
|
|
||||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||||
libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev
|
libsdl2-dev libqt6opengl6-dev qt6-tools-dev qt6-tools-dev-tools libopenal-dev
|
||||||
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev
|
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev
|
||||||
libluajit-5.1-dev librecast-dev libsqlite3-dev ca-certificates libicu-dev
|
libluajit-5.1-dev librecast-dev libsqlite3-dev ca-certificates libicu-dev
|
||||||
libyaml-cpp-dev libqt5svg5 libqt5svg5-dev
|
libyaml-cpp-dev libqt6svg6 libqt6svg6-dev
|
||||||
"
|
"
|
||||||
|
|
||||||
# These dependencies can alternatively be built and linked statically.
|
# These dependencies can alternatively be built and linked statically.
|
||||||
|
|
@ -57,22 +57,22 @@ declare -rA GROUPED_DEPS=(
|
||||||
libsdl2-dev libboost-system-dev libboost-filesystem-dev libgl-dev
|
libsdl2-dev libboost-system-dev libboost-filesystem-dev libgl-dev
|
||||||
"
|
"
|
||||||
|
|
||||||
[openmw-coverage]="gcovr"
|
[openmw-coverage]="pipx"
|
||||||
|
|
||||||
[openmw-integration-tests]="
|
[openmw-integration-tests]="
|
||||||
ca-certificates
|
ca-certificates
|
||||||
gdb
|
gdb
|
||||||
git
|
git
|
||||||
git-lfs
|
git-lfs
|
||||||
libavcodec58
|
libavcodec60
|
||||||
libavformat58
|
libavformat60
|
||||||
libavutil56
|
libavutil58
|
||||||
libboost-iostreams1.74.0
|
libboost-iostreams1.83.0
|
||||||
libboost-program-options1.74.0
|
libboost-program-options1.83.0
|
||||||
libboost-system1.74.0
|
libboost-system1.83.0
|
||||||
libbullet3.24
|
libbullet3.24
|
||||||
libcollada-dom2.5-dp0
|
libcollada-dom2.5-dp0
|
||||||
libicu70
|
libicu74
|
||||||
libjpeg8
|
libjpeg8
|
||||||
libluajit-5.1-2
|
libluajit-5.1-2
|
||||||
liblz4-1
|
liblz4-1
|
||||||
|
|
@ -80,19 +80,19 @@ declare -rA GROUPED_DEPS=(
|
||||||
libopenal1
|
libopenal1
|
||||||
libopenscenegraph161
|
libopenscenegraph161
|
||||||
libpng16-16
|
libpng16-16
|
||||||
libqt5opengl5
|
libqt6opengl6
|
||||||
librecast1
|
librecast1
|
||||||
libsdl2-2.0-0
|
libsdl2-2.0-0
|
||||||
libsqlite3-0
|
libsqlite3-0
|
||||||
libswresample3
|
libswresample4
|
||||||
libswscale5
|
libswscale7
|
||||||
libtinyxml2.6.2v5
|
libtinyxml2.6.2v5
|
||||||
libyaml-cpp0.8
|
libyaml-cpp0.8
|
||||||
python3-pip
|
python3-pip
|
||||||
xvfb
|
xvfb
|
||||||
"
|
"
|
||||||
|
|
||||||
[libasan6]="libasan6"
|
[libasan]="libasan8"
|
||||||
|
|
||||||
[android]="binutils build-essential cmake ccache curl unzip git pkg-config"
|
[android]="binutils build-essential cmake ccache curl unzip git pkg-config"
|
||||||
|
|
||||||
|
|
@ -102,8 +102,8 @@ declare -rA GROUPED_DEPS=(
|
||||||
"
|
"
|
||||||
|
|
||||||
[openmw-qt-translations]="
|
[openmw-qt-translations]="
|
||||||
qttools5-dev
|
qt6-tools-dev
|
||||||
qttools5-dev-tools
|
qt6-tools-dev-tools
|
||||||
git-core
|
git-core
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,7 @@
|
||||||
brew tap --repair
|
brew tap --repair
|
||||||
brew update --quiet
|
brew update --quiet
|
||||||
|
|
||||||
brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd
|
brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd ccache cmake qt@6 openal-soft icu4c yaml-cpp sqlite
|
||||||
|
|
||||||
command -v ccache >/dev/null 2>&1 || brew install ccache
|
|
||||||
command -v cmake >/dev/null 2>&1 || brew install cmake
|
|
||||||
command -v qmake >/dev/null 2>&1 || brew install qt@6
|
|
||||||
|
|
||||||
# Install deps
|
|
||||||
brew install openal-soft icu4c yaml-cpp sqlite
|
|
||||||
|
|
||||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240818-arm64.tar.xz -o ~/openmw-deps.tar.xz
|
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20240818-arm64.tar.xz -o ~/openmw-deps.tar.xz
|
||||||
tar xf ~/openmw-deps.tar.xz -C /tmp > /dev/null
|
tar xf ~/openmw-deps.tar.xz -C /tmp > /dev/null
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh -ex
|
#!/bin/sh -ex
|
||||||
|
|
||||||
if [[ "${MACOS_AMD64}" ]]; then
|
if [[ "${MACOS_AMD64}" ]]; then
|
||||||
arch -x86_64 ccache -s
|
arch -x86_64 ccache -svv
|
||||||
else
|
else
|
||||||
ccache -s
|
ccache -svv
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
146
CMakeLists.txt
146
CMakeLists.txt
|
|
@ -82,8 +82,8 @@ message(STATUS "Configuring OpenMW...")
|
||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 50)
|
set(OPENMW_VERSION_MINOR 50)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
set(OPENMW_LUA_API_REVISION 78)
|
set(OPENMW_LUA_API_REVISION 87)
|
||||||
set(OPENMW_POSTPROCESSING_API_REVISION 2)
|
set(OPENMW_POSTPROCESSING_API_REVISION 3)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
set(OPENMW_VERSION_TAGHASH "")
|
set(OPENMW_VERSION_TAGHASH "")
|
||||||
|
|
@ -249,12 +249,8 @@ endif()
|
||||||
find_package(LZ4 REQUIRED)
|
find_package(LZ4 REQUIRED)
|
||||||
|
|
||||||
if (USE_QT)
|
if (USE_QT)
|
||||||
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
|
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6)
|
||||||
if (QT_VERSION_MAJOR VERSION_EQUAL 5)
|
|
||||||
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL LinguistTools Svg REQUIRED)
|
|
||||||
else()
|
|
||||||
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools Svg REQUIRED)
|
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools Svg REQUIRED)
|
||||||
endif()
|
|
||||||
message(STATUS "Using Qt${QT_VERSION}")
|
message(STATUS "Using Qt${QT_VERSION}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -466,7 +462,7 @@ find_package(Boost 1.70.0 CONFIG REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONA
|
||||||
if(OPENMW_USE_SYSTEM_MYGUI)
|
if(OPENMW_USE_SYSTEM_MYGUI)
|
||||||
find_package(MyGUI 3.4.3 REQUIRED)
|
find_package(MyGUI 3.4.3 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
find_package(SDL2 2.0.10 REQUIRED)
|
find_package(SDL2 2.0.20 REQUIRED)
|
||||||
find_package(OpenAL REQUIRED)
|
find_package(OpenAL REQUIRED)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
|
|
@ -590,31 +586,11 @@ if(OPENMW_LTO_BUILD)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|
||||||
set(OPENMW_CXX_FLAGS "-Wall -Wextra -Wundef -Wextra-semi -Wno-unused-parameter -pedantic -Wno-long-long -Wnon-virtual-dtor -Wunused ${OPENMW_CXX_FLAGS}")
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438
|
|
||||||
set(OPENMW_CXX_FLAGS "-Wno-array-bounds ${OPENMW_CXX_FLAGS}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
|
||||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
|
||||||
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-potentially-evaluated-expression")
|
|
||||||
endif ()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
|
||||||
set(OPENMW_CXX_FLAGS "${OPENMW_CXX_FLAGS} -Wno-unused-but-set-parameter -Wduplicated-branches -Wduplicated-cond -Wlogical-op")
|
|
||||||
endif()
|
|
||||||
endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|
||||||
|
|
||||||
# Extern
|
# Extern
|
||||||
|
|
||||||
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
||||||
|
|
@ -624,8 +600,42 @@ if (BUILD_OPENCS OR BUILD_OPENCS_TESTS)
|
||||||
add_subdirectory (extern/osgQt)
|
add_subdirectory (extern/osgQt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||||
|
add_compile_options("/W4")
|
||||||
|
|
||||||
|
set(WARNINGS_DISABLE
|
||||||
|
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||||
|
4127 # Conditional expression is constant
|
||||||
|
4996 # Function was declared deprecated
|
||||||
|
5054 # Deprecated operations between enumerations of different types caused by Qt headers
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(d ${WARNINGS_DISABLE})
|
||||||
|
add_compile_options("/wd${d}")
|
||||||
|
endforeach(d)
|
||||||
|
|
||||||
|
if(OPENMW_MSVC_WERROR)
|
||||||
|
add_compile_options("/WX")
|
||||||
|
endif()
|
||||||
|
else ()
|
||||||
|
add_compile_options("-Wall" "-Wextra" "-Wundef" "-Wextra-semi" "-Wno-unused-parameter" "-pedantic" "-Wno-long-long" "-Wnon-virtual-dtor" "-Wunused")
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||||
|
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
||||||
|
add_compile_options("-Wno-potentially-evaluated-expression")
|
||||||
|
endif ()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||||
|
add_compile_options("-Wno-unused-but-set-parameter" "-Wduplicated-branches" "-Wduplicated-cond" "-Wlogical-op")
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105438
|
||||||
|
add_compile_options("-Wno-array-bounds")
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (OPENMW_CXX_FLAGS)
|
if (OPENMW_CXX_FLAGS)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPENMW_CXX_FLAGS}")
|
separate_arguments(OPENMW_CXX_FLAGS NATIVE_COMMAND "${OPENMW_CXX_FLAGS}")
|
||||||
|
add_compile_options(${OPENMW_CXX_FLAGS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
|
|
@ -715,87 +725,9 @@ if (WIN32)
|
||||||
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Play a bit with the warning levels
|
|
||||||
|
|
||||||
set(WARNINGS "/W4")
|
|
||||||
|
|
||||||
set(WARNINGS_DISABLE
|
|
||||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
|
||||||
4127 # Conditional expression is constant
|
|
||||||
4996 # Function was declared deprecated
|
|
||||||
5054 # Deprecated operations between enumerations of different types caused by Qt headers
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach(d ${WARNINGS_DISABLE})
|
|
||||||
list(APPEND WARNINGS "/wd${d}")
|
|
||||||
endforeach(d)
|
|
||||||
|
|
||||||
if(OPENMW_MSVC_WERROR)
|
|
||||||
list(APPEND WARNINGS "/WX")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_options(components PRIVATE ${WARNINGS})
|
|
||||||
target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS})
|
|
||||||
|
|
||||||
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
|
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
|
||||||
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
|
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_BSATOOL)
|
|
||||||
target_compile_options(bsatool PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_ESMTOOL)
|
|
||||||
target_compile_options(esmtool PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_ESSIMPORTER)
|
|
||||||
target_compile_options(openmw-essimporter PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_LAUNCHER)
|
|
||||||
target_compile_options(openmw-launcher PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_MWINIIMPORTER)
|
|
||||||
target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENCS)
|
|
||||||
target_compile_options(openmw-cs PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENMW)
|
|
||||||
target_compile_options(openmw PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_WIZARD)
|
|
||||||
target_compile_options(openmw-wizard PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_COMPONENTS_TESTS)
|
|
||||||
target_compile_options(components-tests PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_BENCHMARKS)
|
|
||||||
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_NAVMESHTOOL)
|
|
||||||
target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_BULLETOBJECTTOOL)
|
|
||||||
target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENCS_TESTS)
|
|
||||||
target_compile_options(openmw-cs-tests PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENMW_TESTS)
|
|
||||||
target_compile_options(openmw-tests PRIVATE ${WARNINGS})
|
|
||||||
endif()
|
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
||||||
# TODO: At some point release builds should not use the console but rather write to a log file
|
# TODO: At some point release builds should not use the console but rather write to a log file
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ namespace
|
||||||
|
|
||||||
VFS::Manager vfs;
|
VFS::Manager vfs;
|
||||||
|
|
||||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder());
|
||||||
|
|
||||||
Settings::Manager::load(config);
|
Settings::Manager::load(config);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ namespace
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty)
|
TEST_F(DetourNavigatorNavigatorTest, find_path_for_empty_should_return_empty)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::NavMeshNotFound);
|
Status::NavMeshNotFound);
|
||||||
EXPECT_EQ(mPath, std::deque<osg::Vec3f>());
|
EXPECT_EQ(mPath, std::deque<osg::Vec3f>());
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +147,7 @@ namespace
|
||||||
TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception)
|
TEST_F(DetourNavigatorNavigatorTest, find_path_for_existing_agent_with_no_navmesh_should_throw_exception)
|
||||||
{
|
{
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::StartPolygonNotFound);
|
Status::StartPolygonNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@ namespace
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
mNavigator->removeAgent(mAgentBounds);
|
mNavigator->removeAgent(mAgentBounds);
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::StartPolygonNotFound);
|
Status::StartPolygonNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,7 +172,7 @@ namespace
|
||||||
updateGuard.reset();
|
updateGuard.reset();
|
||||||
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
|
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -194,7 +194,7 @@ namespace
|
||||||
updateGuard.reset();
|
updateGuard.reset();
|
||||||
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
|
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mStart, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mStart, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath, ElementsAre(Vec3fEq(56.66666412353515625, 460, 1.99998295307159423828125))) << mPath;
|
EXPECT_THAT(mPath, ElementsAre(Vec3fEq(56.66666412353515625, 460, 1.99998295307159423828125))) << mPath;
|
||||||
|
|
@ -218,7 +218,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -237,7 +237,7 @@ namespace
|
||||||
|
|
||||||
mPath.clear();
|
mPath.clear();
|
||||||
mOut = std::back_inserter(mPath);
|
mOut = std::back_inserter(mPath);
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -265,7 +265,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -285,7 +285,7 @@ namespace
|
||||||
|
|
||||||
mPath.clear();
|
mPath.clear();
|
||||||
mOut = std::back_inserter(mPath);
|
mOut = std::back_inserter(mPath);
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -318,7 +318,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -386,7 +386,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -421,7 +421,7 @@ namespace
|
||||||
mEnd.x() = 256;
|
mEnd.x() = 256;
|
||||||
mEnd.z() = 300;
|
mEnd.z() = 300;
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -453,8 +453,8 @@ namespace
|
||||||
mStart.x() = 256;
|
mStart.x() = 256;
|
||||||
mEnd.x() = 256;
|
mEnd.x() = 256;
|
||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance,
|
||||||
findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
{}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -487,8 +487,8 @@ namespace
|
||||||
mStart.x() = 256;
|
mStart.x() = 256;
|
||||||
mEnd.x() = 256;
|
mEnd.x() = 256;
|
||||||
|
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance,
|
||||||
findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_swim | Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
{}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -520,7 +520,7 @@ namespace
|
||||||
mStart.x() = 256;
|
mStart.x() = 256;
|
||||||
mEnd.x() = 256;
|
mEnd.x() = 256;
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -549,7 +549,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -577,7 +577,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -658,7 +658,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -781,7 +781,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
|
mNavigator->wait(WaitConditionType::requiredTilesPresent, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -806,7 +806,7 @@ namespace
|
||||||
mNavigator->update(mPlayerPosition, nullptr);
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
Status::PartialPath);
|
Status::PartialPath);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -834,7 +834,7 @@ namespace
|
||||||
|
|
||||||
const float endTolerance = 1000.0f;
|
const float endTolerance = 1000.0f;
|
||||||
|
|
||||||
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, endTolerance, mOut),
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, mStart, mEnd, Flag_walk, mAreaCosts, endTolerance, {}, mOut),
|
||||||
Status::Success);
|
Status::Success);
|
||||||
|
|
||||||
EXPECT_THAT(mPath,
|
EXPECT_THAT(mPath,
|
||||||
|
|
@ -979,6 +979,146 @@ namespace
|
||||||
EXPECT_EQ(usedNavMeshTiles, 854);
|
EXPECT_EQ(usedNavMeshTiles, 854);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, find_path_should_return_path_around_steep_mountains)
|
||||||
|
{
|
||||||
|
const std::array<float, 5 * 5> heightfieldData{ {
|
||||||
|
0, 0, 0, 0, 0, // row 0
|
||||||
|
0, 0, 0, 0, 0, // row 1
|
||||||
|
0, 0, 1000, 0, 0, // row 2
|
||||||
|
0, 0, 1000, 0, 0, // row 3
|
||||||
|
0, 0, 0, 0, 0, // row 4
|
||||||
|
} };
|
||||||
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
|
const osg::Vec3f start(56, 56, 12);
|
||||||
|
const osg::Vec3f end(464, 464, 12);
|
||||||
|
|
||||||
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
|
Status::Success);
|
||||||
|
|
||||||
|
EXPECT_THAT(mPath,
|
||||||
|
ElementsAre( //
|
||||||
|
Vec3fEq(56.66664886474609375, 56.66664886474609375, 11.33333301544189453125),
|
||||||
|
Vec3fEq(396.666656494140625, 79.33331298828125, 11.33333301544189453125),
|
||||||
|
Vec3fEq(430.666656494140625, 113.33331298828125, 11.33333301544189453125),
|
||||||
|
Vec3fEq(463.999969482421875, 463.999969482421875, 11.33333301544189453125)))
|
||||||
|
<< mPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, find_path_should_return_path_around_steep_cliffs)
|
||||||
|
{
|
||||||
|
const std::array<float, 5 * 5> heightfieldData{ {
|
||||||
|
0, 0, 0, 0, 0, // row 0
|
||||||
|
0, 0, 0, 0, 0, // row 1
|
||||||
|
0, 0, -1000, 0, 0, // row 2
|
||||||
|
0, 0, -1000, 0, 0, // row 3
|
||||||
|
0, 0, 0, 0, 0, // row 4
|
||||||
|
} };
|
||||||
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
|
const osg::Vec3f start(56, 56, 12);
|
||||||
|
const osg::Vec3f end(464, 464, 12);
|
||||||
|
|
||||||
|
EXPECT_EQ(findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, {}, mOut),
|
||||||
|
Status::Success);
|
||||||
|
|
||||||
|
EXPECT_THAT(mPath,
|
||||||
|
ElementsAre( //
|
||||||
|
Vec3fEq(56.66664886474609375, 56.66664886474609375, 8.66659259796142578125),
|
||||||
|
Vec3fEq(385.33331298828125, 79.33331298828125, 8.66659259796142578125),
|
||||||
|
Vec3fEq(430.666656494140625, 124.66664886474609375, 8.66659259796142578125),
|
||||||
|
Vec3fEq(463.999969482421875, 463.999969482421875, 8.66659259796142578125)))
|
||||||
|
<< mPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, find_path_should_return_path_with_checkpoints)
|
||||||
|
{
|
||||||
|
const std::array<float, 5 * 5> heightfieldData{ {
|
||||||
|
0, 0, 0, 0, 0, // row 0
|
||||||
|
0, 0, 0, 0, 0, // row 1
|
||||||
|
0, 0, 1000, 0, 0, // row 2
|
||||||
|
0, 0, 1000, 0, 0, // row 3
|
||||||
|
0, 0, 0, 0, 0, // row 4
|
||||||
|
} };
|
||||||
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
|
const std::vector<osg::Vec3f> checkpoints = {
|
||||||
|
osg::Vec3f(400, 70, 12),
|
||||||
|
};
|
||||||
|
|
||||||
|
const osg::Vec3f start(56, 56, 12);
|
||||||
|
const osg::Vec3f end(464, 464, 12);
|
||||||
|
|
||||||
|
EXPECT_EQ(
|
||||||
|
findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, checkpoints, mOut),
|
||||||
|
Status::Success);
|
||||||
|
|
||||||
|
EXPECT_THAT(mPath,
|
||||||
|
ElementsAre( //
|
||||||
|
Vec3fEq(56.66664886474609375, 56.66664886474609375, 11.33333301544189453125),
|
||||||
|
Vec3fEq(400, 70, 11.33333301544189453125),
|
||||||
|
Vec3fEq(430.666656494140625, 113.33331298828125, 11.33333301544189453125),
|
||||||
|
Vec3fEq(463.999969482421875, 463.999969482421875, 11.33333301544189453125)))
|
||||||
|
<< mPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, find_path_should_skip_unreachable_checkpoints)
|
||||||
|
{
|
||||||
|
const std::array<float, 5 * 5> heightfieldData{ {
|
||||||
|
0, 0, 0, 0, 0, // row 0
|
||||||
|
0, 0, 0, 0, 0, // row 1
|
||||||
|
0, 0, 1000, 0, 0, // row 2
|
||||||
|
0, 0, 1000, 0, 0, // row 3
|
||||||
|
0, 0, 0, 0, 0, // row 4
|
||||||
|
} };
|
||||||
|
const HeightfieldSurface surface = makeSquareHeightfieldSurface(heightfieldData);
|
||||||
|
const int cellSize = heightfieldTileSize * static_cast<int>(surface.mSize - 1);
|
||||||
|
|
||||||
|
ASSERT_TRUE(mNavigator->addAgent(mAgentBounds));
|
||||||
|
mNavigator->addHeightfield(mCellPosition, cellSize, surface, nullptr);
|
||||||
|
mNavigator->update(mPlayerPosition, nullptr);
|
||||||
|
mNavigator->wait(WaitConditionType::allJobsDone, &mListener);
|
||||||
|
|
||||||
|
const std::vector<osg::Vec3f> checkpoints = {
|
||||||
|
osg::Vec3f(400, 70, 10000),
|
||||||
|
osg::Vec3f(256, 256, 1000),
|
||||||
|
osg::Vec3f(-1000, -1000, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
const osg::Vec3f start(56, 56, 12);
|
||||||
|
const osg::Vec3f end(464, 464, 12);
|
||||||
|
|
||||||
|
EXPECT_EQ(
|
||||||
|
findPath(*mNavigator, mAgentBounds, start, end, Flag_walk, mAreaCosts, mEndTolerance, checkpoints, mOut),
|
||||||
|
Status::Success);
|
||||||
|
|
||||||
|
EXPECT_THAT(mPath,
|
||||||
|
ElementsAre( //
|
||||||
|
Vec3fEq(56.66664886474609375, 56.66664886474609375, 11.33333301544189453125),
|
||||||
|
Vec3fEq(396.666656494140625, 79.33331298828125, 11.33333301544189453125),
|
||||||
|
Vec3fEq(430.666656494140625, 113.33331298828125, 11.33333301544189453125),
|
||||||
|
Vec3fEq(463.999969482421875, 463.999969482421875, 11.33333301544189453125)))
|
||||||
|
<< mPath;
|
||||||
|
}
|
||||||
|
|
||||||
struct DetourNavigatorNavigatorNotSupportedAgentBoundsTest : TestWithParam<AgentBounds>
|
struct DetourNavigatorNavigatorNotSupportedAgentBoundsTest : TestWithParam<AgentBounds>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -723,7 +723,7 @@ namespace ESM
|
||||||
TEST_P(Esm3SaveLoadRecordTest, landShouldNotChange)
|
TEST_P(Esm3SaveLoadRecordTest, landShouldNotChange)
|
||||||
{
|
{
|
||||||
LandRecordData data;
|
LandRecordData data;
|
||||||
std::iota(data.mHeights.begin(), data.mHeights.end(), 1);
|
std::iota(data.mHeights.begin(), data.mHeights.end(), 1.0f);
|
||||||
std::for_each(data.mHeights.begin(), data.mHeights.end(), [](float& v) { v *= Land::sHeightScale; });
|
std::for_each(data.mHeights.begin(), data.mHeights.end(), [](float& v) { v *= Land::sHeightScale; });
|
||||||
data.mMinHeight = *std::min_element(data.mHeights.begin(), data.mHeights.end());
|
data.mMinHeight = *std::min_element(data.mHeights.begin(), data.mHeights.end());
|
||||||
data.mMaxHeight = *std::max_element(data.mHeights.begin(), data.mHeights.end());
|
data.mMaxHeight = *std::max_element(data.mHeights.begin(), data.mHeights.end());
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
using namespace fx::Lexer;
|
using namespace Fx::Lexer;
|
||||||
|
|
||||||
struct LexerTest : Test
|
struct LexerTest : Test
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ namespace
|
||||||
)" };
|
)" };
|
||||||
|
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
using namespace fx;
|
using namespace Fx;
|
||||||
|
|
||||||
struct TechniqueTest : Test
|
struct TechniqueTest : Test
|
||||||
{
|
{
|
||||||
|
|
@ -113,7 +113,8 @@ namespace
|
||||||
|
|
||||||
void compile(const std::string& name)
|
void compile(const std::string& name)
|
||||||
{
|
{
|
||||||
mTechnique = std::make_unique<Technique>(*mVFS.get(), mImageManager, name, 1, 1, true, true);
|
mTechnique = std::make_unique<Technique>(
|
||||||
|
*mVFS.get(), mImageManager, Technique::makeFileName(name), name, 1, 1, true, true);
|
||||||
mTechnique->compile();
|
mTechnique->compile();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ namespace
|
||||||
constexpr VFS::Path::NormalizedView test2EnPath("l10n/test2/en.yaml");
|
constexpr VFS::Path::NormalizedView test2EnPath("l10n/test2/en.yaml");
|
||||||
constexpr VFS::Path::NormalizedView test3EnPath("l10n/test3/en.yaml");
|
constexpr VFS::Path::NormalizedView test3EnPath("l10n/test3/en.yaml");
|
||||||
constexpr VFS::Path::NormalizedView test3DePath("l10n/test3/de.yaml");
|
constexpr VFS::Path::NormalizedView test3DePath("l10n/test3/de.yaml");
|
||||||
|
constexpr VFS::Path::NormalizedView test4RuPath("l10n/test4/ru.yaml");
|
||||||
|
constexpr VFS::Path::NormalizedView test4EnPath("l10n/test4/en.yaml");
|
||||||
|
|
||||||
VFSTestFile invalidScript("not a script");
|
VFSTestFile invalidScript("not a script");
|
||||||
VFSTestFile incorrectScript(
|
VFSTestFile incorrectScript(
|
||||||
|
|
@ -69,6 +71,16 @@ currency: "You have {money, number, currency}"
|
||||||
VFSTestFile test2En(R"X(
|
VFSTestFile test2En(R"X(
|
||||||
good_morning: "Morning!"
|
good_morning: "Morning!"
|
||||||
you_have_arrows: "Arrows count: {count}"
|
you_have_arrows: "Arrows count: {count}"
|
||||||
|
)X");
|
||||||
|
|
||||||
|
VFSTestFile test4Ru(R"X(
|
||||||
|
skill_increase: "Ваш навык {навык} увеличился до {value}"
|
||||||
|
acrobatics: "Акробатика"
|
||||||
|
)X");
|
||||||
|
|
||||||
|
VFSTestFile test4En(R"X(
|
||||||
|
stat_increase: "Your {stat} has increased to {value}"
|
||||||
|
speed: "Speed"
|
||||||
)X");
|
)X");
|
||||||
|
|
||||||
struct LuaL10nTest : Test
|
struct LuaL10nTest : Test
|
||||||
|
|
@ -80,6 +92,8 @@ you_have_arrows: "Arrows count: {count}"
|
||||||
{ test2EnPath, &test2En },
|
{ test2EnPath, &test2En },
|
||||||
{ test3EnPath, &test1En },
|
{ test3EnPath, &test1En },
|
||||||
{ test3DePath, &test1De },
|
{ test3DePath, &test1De },
|
||||||
|
{ test4RuPath, &test4Ru },
|
||||||
|
{ test4EnPath, &test4En },
|
||||||
});
|
});
|
||||||
|
|
||||||
LuaUtil::ScriptsConfiguration mCfg;
|
LuaUtil::ScriptsConfiguration mCfg;
|
||||||
|
|
@ -91,7 +105,7 @@ you_have_arrows: "Arrows count: {count}"
|
||||||
lua.protectedCall([&](LuaUtil::LuaView& view) {
|
lua.protectedCall([&](LuaUtil::LuaView& view) {
|
||||||
sol::state_view& l = view.sol();
|
sol::state_view& l = view.sol();
|
||||||
internal::CaptureStdout();
|
internal::CaptureStdout();
|
||||||
l10n::Manager l10nManager(mVFS.get());
|
L10n::Manager l10nManager(mVFS.get());
|
||||||
l10nManager.setPreferredLocales({ "de", "en" });
|
l10nManager.setPreferredLocales({ "de", "en" });
|
||||||
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n");
|
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n");
|
||||||
|
|
||||||
|
|
@ -169,6 +183,18 @@ you_have_arrows: "Arrows count: {count}"
|
||||||
l.safe_script("t3 = l10n('Test3', 'de')");
|
l.safe_script("t3 = l10n('Test3', 'de')");
|
||||||
l10nManager.setPreferredLocales({ "en" });
|
l10nManager.setPreferredLocales({ "en" });
|
||||||
EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!");
|
EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!");
|
||||||
|
|
||||||
|
// Test that formatting arguments use a correct encoding
|
||||||
|
l.safe_script("t4 = l10n('Test4', 'ru')");
|
||||||
|
l10nManager.setPreferredLocales({ "ru", "en" });
|
||||||
|
EXPECT_EQ(get<std::string>(l, "t4('skill_increase', {навык='Акробатика', value=100})"),
|
||||||
|
"Ваш навык Акробатика увеличился до 100");
|
||||||
|
EXPECT_EQ(get<std::string>(l, "t4('skill_increase', {навык=t4('acrobatics'), value=100})"),
|
||||||
|
"Ваш навык Акробатика увеличился до 100");
|
||||||
|
EXPECT_EQ(get<std::string>(l, "t4('stat_increase', {stat='Speed', value=100})"),
|
||||||
|
"Your Speed has increased to 100");
|
||||||
|
EXPECT_EQ(get<std::string>(l, "t4('stat_increase', {stat=t4('speed'), value=100})"),
|
||||||
|
"Your Speed has increased to 100");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -638,8 +638,9 @@ CUSTOM: customdata.lua
|
||||||
sol::object deserialized = LuaUtil::deserialize(lua.sol(), data2.mScripts[0].mData, &serializer1);
|
sol::object deserialized = LuaUtil::deserialize(lua.sol(), data2.mScripts[0].mData, &serializer1);
|
||||||
EXPECT_TRUE(deserialized.is<sol::table>());
|
EXPECT_TRUE(deserialized.is<sol::table>());
|
||||||
sol::table table = deserialized;
|
sol::table table = deserialized;
|
||||||
for (const auto& [key, value] : table)
|
if (!table.empty())
|
||||||
{
|
{
|
||||||
|
const auto [key, value] = *table.cbegin();
|
||||||
EXPECT_TRUE(key.is<ESM::RefNum>());
|
EXPECT_TRUE(key.is<ESM::RefNum>());
|
||||||
EXPECT_TRUE(value.is<ESM::RefNum>());
|
EXPECT_TRUE(value.is<ESM::RefNum>());
|
||||||
EXPECT_EQ(key.as<ESM::RefNum>(), (ESM::RefNum{ 42, 34 }));
|
EXPECT_EQ(key.as<ESM::RefNum>(), (ESM::RefNum{ 42, 34 }));
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,33 @@ namespace
|
||||||
{
|
{
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
|
|
||||||
|
struct LuaUtilPackageTest : Test
|
||||||
|
{
|
||||||
|
LuaUtil::LuaState mLuaState{ nullptr, nullptr };
|
||||||
|
|
||||||
|
LuaUtilPackageTest()
|
||||||
|
{
|
||||||
|
mLuaState.addInternalLibSearchPath(
|
||||||
|
std::filesystem::path{ OPENMW_PROJECT_SOURCE_DIR } / "components" / "lua");
|
||||||
|
sol::state_view sol = mLuaState.unsafeState();
|
||||||
|
sol["util"] = LuaUtil::initUtilPackage(sol);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get(sol::state& lua, const std::string& luaCode)
|
T get(sol::state_view& lua, const std::string& luaCode)
|
||||||
{
|
{
|
||||||
return lua.safe_script("return " + luaCode).get<T>();
|
return lua.safe_script("return " + luaCode).get<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getAsString(sol::state& lua, std::string luaCode)
|
std::string getAsString(sol::state_view& lua, std::string luaCode)
|
||||||
{
|
{
|
||||||
return LuaUtil::toString(lua.safe_script("return " + luaCode));
|
return LuaUtil::toString(lua.safe_script("return " + luaCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LuaUtilPackageTest, Vector2)
|
TEST_F(LuaUtilPackageTest, Vector2)
|
||||||
{
|
{
|
||||||
sol::state lua;
|
sol::state_view lua = mLuaState.unsafeState();
|
||||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
|
||||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
|
||||||
lua.safe_script("v = util.vector2(3, 4)");
|
lua.safe_script("v = util.vector2(3, 4)");
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 3);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 3);
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 4);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 4);
|
||||||
|
|
@ -55,11 +66,9 @@ namespace
|
||||||
EXPECT_TRUE(get<bool>(lua, "swizzle['01'] == util.vector2(0, 1) and swizzle['0y'] == util.vector2(0, 2)"));
|
EXPECT_TRUE(get<bool>(lua, "swizzle['01'] == util.vector2(0, 1) and swizzle['0y'] == util.vector2(0, 2)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LuaUtilPackageTest, Vector3)
|
TEST_F(LuaUtilPackageTest, Vector3)
|
||||||
{
|
{
|
||||||
sol::state lua;
|
sol::state_view lua = mLuaState.unsafeState();
|
||||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
|
||||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
|
||||||
lua.safe_script("v = util.vector3(5, 12, 13)");
|
lua.safe_script("v = util.vector3(5, 12, 13)");
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
|
||||||
|
|
@ -94,11 +103,9 @@ namespace
|
||||||
get<bool>(lua, "swizzle['001'] == util.vector3(0, 0, 1) and swizzle['0yx'] == util.vector3(0, 2, 1)"));
|
get<bool>(lua, "swizzle['001'] == util.vector3(0, 0, 1) and swizzle['0yx'] == util.vector3(0, 2, 1)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LuaUtilPackageTest, Vector4)
|
TEST_F(LuaUtilPackageTest, Vector4)
|
||||||
{
|
{
|
||||||
sol::state lua;
|
sol::state_view lua = mLuaState.unsafeState();
|
||||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
|
||||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
|
||||||
lua.safe_script("v = util.vector4(5, 12, 13, 15)");
|
lua.safe_script("v = util.vector4(5, 12, 13, 15)");
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
|
||||||
|
|
@ -136,11 +143,9 @@ namespace
|
||||||
lua, "swizzle['0001'] == util.vector4(0, 0, 0, 1) and swizzle['0yx1'] == util.vector4(0, 2, 1, 1)"));
|
lua, "swizzle['0001'] == util.vector4(0, 0, 0, 1) and swizzle['0yx1'] == util.vector4(0, 2, 1, 1)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LuaUtilPackageTest, Color)
|
TEST_F(LuaUtilPackageTest, Color)
|
||||||
{
|
{
|
||||||
sol::state lua;
|
sol::state_view lua = mLuaState.unsafeState();
|
||||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
|
||||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
|
||||||
lua.safe_script("brown = util.color.rgba(0.75, 0.25, 0, 1)");
|
lua.safe_script("brown = util.color.rgba(0.75, 0.25, 0, 1)");
|
||||||
EXPECT_EQ(get<std::string>(lua, "tostring(brown)"), "(0.75, 0.25, 0, 1)");
|
EXPECT_EQ(get<std::string>(lua, "tostring(brown)"), "(0.75, 0.25, 0, 1)");
|
||||||
lua.safe_script("blue = util.color.rgb(0, 1, 0, 1)");
|
lua.safe_script("blue = util.color.rgb(0, 1, 0, 1)");
|
||||||
|
|
@ -155,11 +160,9 @@ namespace
|
||||||
EXPECT_TRUE(get<bool>(lua, "red:asRgb() == util.vector3(1, 0, 0)"));
|
EXPECT_TRUE(get<bool>(lua, "red:asRgb() == util.vector3(1, 0, 0)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LuaUtilPackageTest, Transform)
|
TEST_F(LuaUtilPackageTest, Transform)
|
||||||
{
|
{
|
||||||
sol::state lua;
|
sol::state_view lua = mLuaState.unsafeState();
|
||||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
|
||||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
|
||||||
lua["T"] = lua["util"]["transform"];
|
lua["T"] = lua["util"]["transform"];
|
||||||
lua["v"] = lua["util"]["vector3"];
|
lua["v"] = lua["util"]["vector3"];
|
||||||
EXPECT_ERROR(lua.safe_script("T.identity = nil"), "attempt to index");
|
EXPECT_ERROR(lua.safe_script("T.identity = nil"), "attempt to index");
|
||||||
|
|
@ -191,11 +194,9 @@ namespace
|
||||||
EXPECT_LT(get<float>(lua, "(rz_move_rx:inverse() * v(0, 1, 2) - v(1, 2, 3)):length()"), 1e-6);
|
EXPECT_LT(get<float>(lua, "(rz_move_rx:inverse() * v(0, 1, 2) - v(1, 2, 3)):length()"), 1e-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LuaUtilPackageTest, UtilityFunctions)
|
TEST_F(LuaUtilPackageTest, UtilityFunctions)
|
||||||
{
|
{
|
||||||
sol::state lua;
|
sol::state_view lua = mLuaState.unsafeState();
|
||||||
lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
|
|
||||||
lua["util"] = LuaUtil::initUtilPackage(lua);
|
|
||||||
lua.safe_script("v = util.vector2(1, 0):rotate(math.rad(120))");
|
lua.safe_script("v = util.vector2(1, 0):rotate(math.rad(120))");
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), -0.5f);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), -0.5f);
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 0.86602539f);
|
EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 0.86602539f);
|
||||||
|
|
@ -203,6 +204,10 @@ namespace
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(0.1, 0, 1.5)"), 0.1f);
|
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(0.1, 0, 1.5)"), 0.1f);
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(-0.1, 0, 1.5)"), 0);
|
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(-0.1, 0, 1.5)"), 0);
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(2.1, 0, 1.5)"), 1.5f);
|
EXPECT_FLOAT_EQ(get<float>(lua, "util.clamp(2.1, 0, 1.5)"), 1.5f);
|
||||||
|
EXPECT_FLOAT_EQ(get<float>(lua, "util.round(2.1)"), 2.0f);
|
||||||
|
EXPECT_FLOAT_EQ(get<float>(lua, "util.round(-2.1)"), -2.0f);
|
||||||
|
EXPECT_FLOAT_EQ(get<float>(lua, "util.remap(5, 0, 10, 0, 100)"), 50.0f);
|
||||||
|
EXPECT_FLOAT_EQ(get<float>(lua, "util.remap(-5, 0, 10, 0, 100)"), -50.0f);
|
||||||
lua.safe_script("t = util.makeReadOnly({x = 1})");
|
lua.safe_script("t = util.makeReadOnly({x = 1})");
|
||||||
EXPECT_FLOAT_EQ(get<float>(lua, "t.x"), 1);
|
EXPECT_FLOAT_EQ(get<float>(lua, "t.x"), 1);
|
||||||
EXPECT_ERROR(lua.safe_script("t.y = 2"), "userdata value");
|
EXPECT_ERROR(lua.safe_script("t.y = 2"), "userdata value");
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,15 @@ namespace
|
||||||
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({});
|
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({});
|
||||||
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
||||||
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3");
|
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3");
|
||||||
|
|
||||||
|
auto correctESM4SoundPath = [](auto path, auto* vfs) {
|
||||||
|
return Misc::ResourceHelpers::correctResourcePath({ { "sound" } }, path, vfs, ".mp3");
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_EQ(correctESM4SoundPath("foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||||
|
EXPECT_EQ(correctESM4SoundPath("SOUND/foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||||
|
EXPECT_EQ(correctESM4SoundPath("DATA\\SOUND\\foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||||
|
EXPECT_EQ(correctESM4SoundPath("\\Data/Sound\\foo.WAV", mVFS.get()), "sound\\foo.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
|
||||||
|
|
@ -215,8 +215,6 @@ int main(int argc, char** argv)
|
||||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ namespace ESSImport
|
||||||
{
|
{
|
||||||
ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory;
|
ESM::InventoryState& invState = mContext->mPlayer.mObject.mInventory;
|
||||||
|
|
||||||
for (size_t i = 0; i < invState.mItems.size(); ++i)
|
for (uint32_t i = 0; i < static_cast<uint32_t>(invState.mItems.size()); ++i)
|
||||||
{
|
{
|
||||||
// FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
|
// FIXME: in case of conflict (multiple items with this refID) use the already equipped one?
|
||||||
if (invState.mItems[i].mRef.mRefID == refr.mActorData.mSelectedEnchantItem)
|
if (invState.mItems[i].mRef.mRefID == refr.mActorData.mSelectedEnchantItem)
|
||||||
|
|
|
||||||
|
|
@ -242,11 +242,36 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
|
||||||
{
|
{
|
||||||
if (mode == Settings::WindowMode::Fullscreen || mode == Settings::WindowMode::WindowedFullscreen)
|
if (mode == Settings::WindowMode::Fullscreen || mode == Settings::WindowMode::WindowedFullscreen)
|
||||||
{
|
{
|
||||||
|
QString customSizeMessage = tr("Custom window size is available only in Windowed mode.");
|
||||||
|
QString windowBorderMessage = tr("Window border is available only in Windowed mode.");
|
||||||
|
|
||||||
standardRadioButton->toggle();
|
standardRadioButton->toggle();
|
||||||
customRadioButton->setEnabled(false);
|
customRadioButton->setEnabled(false);
|
||||||
customWidthSpinBox->setEnabled(false);
|
customWidthSpinBox->setEnabled(false);
|
||||||
customHeightSpinBox->setEnabled(false);
|
customHeightSpinBox->setEnabled(false);
|
||||||
windowBorderCheckBox->setEnabled(false);
|
windowBorderCheckBox->setEnabled(false);
|
||||||
|
windowBorderCheckBox->setToolTip(windowBorderMessage);
|
||||||
|
customWidthSpinBox->setToolTip(customSizeMessage);
|
||||||
|
customHeightSpinBox->setToolTip(customSizeMessage);
|
||||||
|
customRadioButton->setToolTip(customSizeMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == Settings::WindowMode::Fullscreen)
|
||||||
|
{
|
||||||
|
resolutionComboBox->setEnabled(true);
|
||||||
|
resolutionComboBox->setToolTip("");
|
||||||
|
standardRadioButton->setToolTip("");
|
||||||
|
}
|
||||||
|
else if (mode == Settings::WindowMode::WindowedFullscreen)
|
||||||
|
{
|
||||||
|
QString fullScreenMessage = tr("Windowed Fullscreen mode always uses the native display resolution.");
|
||||||
|
|
||||||
|
resolutionComboBox->setEnabled(false);
|
||||||
|
resolutionComboBox->setToolTip(fullScreenMessage);
|
||||||
|
standardRadioButton->setToolTip(fullScreenMessage);
|
||||||
|
|
||||||
|
// Assume that a first item is a native screen resolution
|
||||||
|
resolutionComboBox->setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -254,6 +279,13 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
|
||||||
customWidthSpinBox->setEnabled(true);
|
customWidthSpinBox->setEnabled(true);
|
||||||
customHeightSpinBox->setEnabled(true);
|
customHeightSpinBox->setEnabled(true);
|
||||||
windowBorderCheckBox->setEnabled(true);
|
windowBorderCheckBox->setEnabled(true);
|
||||||
|
resolutionComboBox->setEnabled(true);
|
||||||
|
resolutionComboBox->setToolTip("");
|
||||||
|
standardRadioButton->setToolTip("");
|
||||||
|
windowBorderCheckBox->setToolTip("");
|
||||||
|
customWidthSpinBox->setToolTip("");
|
||||||
|
customHeightSpinBox->setToolTip("");
|
||||||
|
customRadioButton->setToolTip("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,11 +88,7 @@ void Launcher::ImportPage::on_importerButton_clicked()
|
||||||
// Create the file if it doesn't already exist, else the importer will fail
|
// Create the file if it doesn't already exist, else the importer will fail
|
||||||
auto path = mCfgMgr.getUserConfigPath();
|
auto path = mCfgMgr.getUserConfigPath();
|
||||||
path /= "openmw.cfg";
|
path /= "openmw.cfg";
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
#else
|
|
||||||
QFile file(Files::pathToQString(path));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ int runLauncher(int argc, char* argv[])
|
||||||
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
|
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
|
||||||
}
|
}
|
||||||
|
|
||||||
l10n::installQtTranslations(app, "launcher", resourcesPath);
|
L10n::installQtTranslations(app, "launcher", resourcesPath);
|
||||||
|
|
||||||
Launcher::MainDialog mainWin(configurationManager);
|
Launcher::MainDialog mainWin(configurationManager);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -497,11 +497,7 @@ bool Launcher::MainDialog::writeSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Game settings
|
// Game settings
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
QFile file(userPath / Files::openmwCfgFile);
|
QFile file(userPath / Files::openmwCfgFile);
|
||||||
#else
|
|
||||||
QFile file(Files::getUserConfigPathQString(mCfgMgr));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text))
|
if (!file.open(QIODevice::ReadWrite | QIODevice::Text))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,6 @@ bool Launcher::SettingsPage::loadSettings()
|
||||||
loadSettingInt(Settings::physics().mAsyncNumThreads, *physicsThreadsSpinBox);
|
loadSettingInt(Settings::physics().mAsyncNumThreads, *physicsThreadsSpinBox);
|
||||||
loadSettingBool(
|
loadSettingBool(
|
||||||
Settings::game().mAllowActorsToFollowOverWaterSurface, *allowNPCToFollowOverWaterSurfaceCheckBox);
|
Settings::game().mAllowActorsToFollowOverWaterSurface, *allowNPCToFollowOverWaterSurfaceCheckBox);
|
||||||
loadSettingBool(
|
|
||||||
Settings::game().mUnarmedCreatureAttacksDamageArmor, *unarmedCreatureAttacksDamageArmorCheckBox);
|
|
||||||
loadSettingInt(Settings::game().mActorCollisionShapeType, *actorCollisonShapeTypeComboBox);
|
loadSettingInt(Settings::game().mActorCollisionShapeType, *actorCollisonShapeTypeComboBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,8 +371,6 @@ void Launcher::SettingsPage::saveSettings()
|
||||||
saveSettingInt(*physicsThreadsSpinBox, Settings::physics().mAsyncNumThreads);
|
saveSettingInt(*physicsThreadsSpinBox, Settings::physics().mAsyncNumThreads);
|
||||||
saveSettingBool(
|
saveSettingBool(
|
||||||
*allowNPCToFollowOverWaterSurfaceCheckBox, Settings::game().mAllowActorsToFollowOverWaterSurface);
|
*allowNPCToFollowOverWaterSurfaceCheckBox, Settings::game().mAllowActorsToFollowOverWaterSurface);
|
||||||
saveSettingBool(
|
|
||||||
*unarmedCreatureAttacksDamageArmorCheckBox, Settings::game().mUnarmedCreatureAttacksDamageArmor);
|
|
||||||
saveSettingInt(*actorCollisonShapeTypeComboBox, Settings::game().mActorCollisionShapeType);
|
saveSettingInt(*actorCollisonShapeTypeComboBox, Settings::game().mActorCollisionShapeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="QCheckBox" name="normaliseRaceSpeedCheckBox">
|
<widget class="QCheckBox" name="normaliseRaceSpeedCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Don't use race weight in NPC movement speed calculations.</p></body></html></string>
|
<string><html><head/><body><p>Don't use race weight in NPC movement speed calculations.</p></body></html></string>
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="1">
|
<item row="8" column="1">
|
||||||
<widget class="QCheckBox" name="classicCalmSpellsCheckBox">
|
<widget class="QCheckBox" name="classicCalmSpellsCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Stops combat with NPCs affected by Calm spells every frame -- like in Morrowind without the MCP.</p></body></html></string>
|
<string><html><head/><body><p>Stops combat with NPCs affected by Calm spells every frame -- like in Morrowind without the MCP.</p></body></html></string>
|
||||||
|
|
@ -73,7 +73,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="12" column="1">
|
<item row="11" column="1">
|
||||||
<widget class="QCheckBox" name="avoidCollisionsCheckBox">
|
<widget class="QCheckBox" name="avoidCollisionsCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>If enabled NPCs apply evasion maneuver to avoid collisions with others.</p></body></html></string>
|
<string><html><head/><body><p>If enabled NPCs apply evasion maneuver to avoid collisions with others.</p></body></html></string>
|
||||||
|
|
@ -123,7 +123,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QCheckBox" name="requireAppropriateAmmunitionCheckBox">
|
<widget class="QCheckBox" name="requireAppropriateAmmunitionCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>If enabled, a magical ammunition is required to bypass normal weapon resistance or weakness. If disabled, a magical ranged weapon or a magical ammunition is required.</p></body></html></string>
|
<string><html><head/><body><p>If enabled, a magical ammunition is required to bypass normal weapon resistance or weakness. If disabled, a magical ranged weapon or a magical ammunition is required.</p></body></html></string>
|
||||||
|
|
@ -133,7 +133,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="13" column="1">
|
<item row="12" column="1">
|
||||||
<widget class="QCheckBox" name="graphicHerbalismCheckBox">
|
<widget class="QCheckBox" name="graphicHerbalismCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.</p></body></html></string>
|
<string><html><head/><body><p>If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.</p></body></html></string>
|
||||||
|
|
@ -143,7 +143,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="11" column="1">
|
<item row="10" column="1">
|
||||||
<widget class="QCheckBox" name="swimUpwardCorrectionCheckBox">
|
<widget class="QCheckBox" name="swimUpwardCorrectionCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Makes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.</p></body></html></string>
|
<string><html><head/><body><p>Makes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.</p></body></html></string>
|
||||||
|
|
@ -153,7 +153,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="QCheckBox" name="enchantedWeaponsMagicalCheckBox">
|
<widget class="QCheckBox" name="enchantedWeaponsMagicalCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Make enchanted weapons without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html></string>
|
<string><html><head/><body><p>Make enchanted weapons without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html></string>
|
||||||
|
|
@ -183,7 +183,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QCheckBox" name="canLootDuringDeathAnimationCheckBox">
|
<widget class="QCheckBox" name="canLootDuringDeathAnimationCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html></string>
|
<string><html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html></string>
|
||||||
|
|
@ -203,7 +203,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox">
|
<widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Effects of reflected Absorb spells are not mirrored - like in Morrowind.</p></body></html></string>
|
<string><html><head/><body><p>Effects of reflected Absorb spells are not mirrored - like in Morrowind.</p></body></html></string>
|
||||||
|
|
@ -213,16 +213,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QCheckBox" name="unarmedCreatureAttacksDamageArmorCheckBox">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Makes unarmed creature attacks able to reduce armor condition, just as attacks from NPCs and armed creatures.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Unarmed Creature Attacks Damage Armor</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace sfs = std::filesystem;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// from configfileparser.cpp
|
// from configfileparser.cpp
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
#include <components/files/conversion.hpp>
|
#include <components/files/conversion.hpp>
|
||||||
|
|
||||||
namespace bpo = boost::program_options;
|
namespace bpo = boost::program_options;
|
||||||
namespace sfs = std::filesystem;
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ namespace NavMeshTool
|
||||||
|
|
||||||
VFS::Manager vfs;
|
VFS::Manager vfs;
|
||||||
|
|
||||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
VFS::registerArchives(&vfs, fileCollections, archives, true, &encoder.getStatelessEncoder());
|
||||||
|
|
||||||
Settings::Manager::load(config);
|
Settings::Manager::load(config);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,8 +201,8 @@ namespace NavMeshTool
|
||||||
{
|
{
|
||||||
if (!land.has_value() || osg::Vec2i(land->mX, land->mY) != cellPosition
|
if (!land.has_value() || osg::Vec2i(land->mX, land->mY) != cellPosition
|
||||||
|| (land->mDataTypes & ESM::Land::DATA_VHGT) == 0)
|
|| (land->mDataTypes & ESM::Land::DATA_VHGT) == 0)
|
||||||
return { HeightfieldPlane{ ESM::Land::DEFAULT_HEIGHT }, ESM::Land::DEFAULT_HEIGHT,
|
return { HeightfieldPlane{ static_cast<float>(ESM::Land::DEFAULT_HEIGHT) },
|
||||||
ESM::Land::DEFAULT_HEIGHT };
|
static_cast<float>(ESM::Land::DEFAULT_HEIGHT), static_cast<float>(ESM::Land::DEFAULT_HEIGHT) };
|
||||||
|
|
||||||
ESM::Land::LandData& landData = *landDatas.emplace_back(std::make_unique<ESM::Land::LandData>());
|
ESM::Land::LandData& landData = *landDatas.emplace_back(std::make_unique<ESM::Land::LandData>());
|
||||||
land->loadData(ESM::Land::DATA_VHGT, landData);
|
land->loadData(ESM::Land::DATA_VHGT, landData);
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ bool isBSA(const std::filesystem::path& path)
|
||||||
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
if (isBSA(path))
|
if (isBSA(path))
|
||||||
return VFS::makeBsaArchive(path);
|
return VFS::makeBsaArchive(path, nullptr);
|
||||||
if (std::filesystem::is_directory(path))
|
if (std::filesystem::is_directory(path))
|
||||||
return std::make_unique<VFS::FileSystemArchive>(path);
|
return std::make_unique<VFS::FileSystemArchive>(path);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -198,7 +198,7 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
readVFS(VFS::makeBsaArchive(file.second), file.second, quiet);
|
readVFS(VFS::makeBsaArchive(file.second, nullptr), file.second, quiet);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ opencs_units (view/render
|
||||||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||||
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
||||||
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
||||||
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands
|
cellwater terraintexturemode actor terrainselection terrainshapemode brushdraw commands objectmarker
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units (view/render
|
opencs_units (view/render
|
||||||
|
|
@ -240,11 +240,7 @@ target_link_libraries(openmw-cs-lib
|
||||||
components_qt
|
components_qt
|
||||||
)
|
)
|
||||||
|
|
||||||
if (QT_VERSION_MAJOR VERSION_EQUAL 6)
|
|
||||||
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::OpenGLWidgets Qt::Svg)
|
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::OpenGLWidgets Qt::Svg)
|
||||||
else()
|
|
||||||
target_link_libraries(openmw-cs-lib Qt::Widgets Qt::Core Qt::Network Qt::OpenGL Qt::Svg)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_sources(openmw-cs PRIVATE ${CMAKE_SOURCE_DIR}/files/windows/openmw-cs.exe.manifest)
|
target_sources(openmw-cs PRIVATE ${CMAKE_SOURCE_DIR}/files/windows/openmw-cs.exe.manifest)
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,11 @@ bool CSMFilter::TextNode::test(const CSMWorld::IdTableBase& table, int row, cons
|
||||||
|
|
||||||
QString string;
|
QString string;
|
||||||
|
|
||||||
if (data.type() == QVariant::String)
|
if (data.typeId() == QMetaType::QString)
|
||||||
{
|
{
|
||||||
string = data.toString();
|
string = data.toString();
|
||||||
}
|
}
|
||||||
else if ((data.type() == QVariant::Int || data.type() == QVariant::UInt)
|
else if ((data.typeId() == QMetaType::Int || data.typeId() == QMetaType::UInt)
|
||||||
&& CSMWorld::Columns::hasEnums(static_cast<CSMWorld::Columns::ColumnId>(mColumnId)))
|
&& CSMWorld::Columns::hasEnums(static_cast<CSMWorld::Columns::ColumnId>(mColumnId)))
|
||||||
{
|
{
|
||||||
int value = data.toInt();
|
int value = data.toInt();
|
||||||
|
|
@ -49,7 +49,7 @@ bool CSMFilter::TextNode::test(const CSMWorld::IdTableBase& table, int row, cons
|
||||||
if (value >= 0 && value < static_cast<int>(enums.size()))
|
if (value >= 0 && value < static_cast<int>(enums.size()))
|
||||||
string = QString::fromUtf8(enums[value].second.c_str());
|
string = QString::fromUtf8(enums[value].second.c_str());
|
||||||
}
|
}
|
||||||
else if (data.type() == QVariant::Bool)
|
else if (data.typeId() == QMetaType::Bool)
|
||||||
{
|
{
|
||||||
string = data.toBool() ? "true" : "false";
|
string = data.toBool() ? "true" : "false";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ bool CSMFilter::ValueNode::test(const CSMWorld::IdTableBase& table, int row, con
|
||||||
|
|
||||||
QVariant data = table.data(index);
|
QVariant data = table.data(index);
|
||||||
|
|
||||||
if (data.type() != QVariant::Double && data.type() != QVariant::Bool && data.type() != QVariant::Int
|
if (data.typeId() != QMetaType::Double && data.typeId() != QMetaType::Bool && data.typeId() != QMetaType::Int
|
||||||
&& data.type() != QVariant::UInt && data.type() != static_cast<QVariant::Type>(QMetaType::Float))
|
&& data.typeId() != QMetaType::UInt && data.typeId() != QMetaType::Float)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
double value = data.toDouble();
|
double value = data.toDouble();
|
||||||
|
|
|
||||||
|
|
@ -62,39 +62,31 @@ namespace CSMPrefs
|
||||||
{
|
{
|
||||||
QWidget* widget = static_cast<QWidget*>(watched);
|
QWidget* widget = static_cast<QWidget*>(watched);
|
||||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
unsigned int mod = (unsigned int)keyEvent->modifiers();
|
|
||||||
unsigned int key = (unsigned int)keyEvent->key();
|
|
||||||
|
|
||||||
if (!keyEvent->isAutoRepeat())
|
if (!keyEvent->isAutoRepeat())
|
||||||
return activate(widget, mod, key);
|
return activate(widget, keyEvent->keyCombination());
|
||||||
}
|
}
|
||||||
else if (event->type() == QEvent::KeyRelease)
|
else if (event->type() == QEvent::KeyRelease)
|
||||||
{
|
{
|
||||||
QWidget* widget = static_cast<QWidget*>(watched);
|
QWidget* widget = static_cast<QWidget*>(watched);
|
||||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
unsigned int mod = (unsigned int)keyEvent->modifiers();
|
|
||||||
unsigned int key = (unsigned int)keyEvent->key();
|
|
||||||
|
|
||||||
if (!keyEvent->isAutoRepeat())
|
if (!keyEvent->isAutoRepeat())
|
||||||
return deactivate(widget, mod, key);
|
return deactivate(widget, keyEvent->keyCombination());
|
||||||
}
|
}
|
||||||
else if (event->type() == QEvent::MouseButtonPress)
|
else if (event->type() == QEvent::MouseButtonPress)
|
||||||
{
|
{
|
||||||
QWidget* widget = static_cast<QWidget*>(watched);
|
QWidget* widget = static_cast<QWidget*>(watched);
|
||||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||||
unsigned int mod = (unsigned int)mouseEvent->modifiers();
|
|
||||||
unsigned int button = (unsigned int)mouseEvent->button();
|
|
||||||
|
|
||||||
return activate(widget, mod, button);
|
return activate(widget, QKeyCombination(mouseEvent->modifiers(), Qt::Key(mouseEvent->button())));
|
||||||
}
|
}
|
||||||
else if (event->type() == QEvent::MouseButtonRelease)
|
else if (event->type() == QEvent::MouseButtonRelease)
|
||||||
{
|
{
|
||||||
QWidget* widget = static_cast<QWidget*>(watched);
|
QWidget* widget = static_cast<QWidget*>(watched);
|
||||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||||
unsigned int mod = (unsigned int)mouseEvent->modifiers();
|
|
||||||
unsigned int button = (unsigned int)mouseEvent->button();
|
|
||||||
|
|
||||||
return deactivate(widget, mod, button);
|
return deactivate(widget, QKeyCombination(mouseEvent->modifiers(), Qt::Key(mouseEvent->button())));
|
||||||
}
|
}
|
||||||
else if (event->type() == QEvent::FocusOut)
|
else if (event->type() == QEvent::FocusOut)
|
||||||
{
|
{
|
||||||
|
|
@ -149,7 +141,7 @@ namespace CSMPrefs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShortcutEventHandler::activate(QWidget* widget, unsigned int mod, unsigned int button)
|
bool ShortcutEventHandler::activate(QWidget* widget, QKeyCombination keyCombination)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<MatchResult, Shortcut*>> potentials;
|
std::vector<std::pair<MatchResult, Shortcut*>> potentials;
|
||||||
bool used = false;
|
bool used = false;
|
||||||
|
|
@ -167,7 +159,7 @@ namespace CSMPrefs
|
||||||
if (!shortcut->isEnabled())
|
if (!shortcut->isEnabled())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (checkModifier(mod, button, shortcut, true))
|
if (checkModifier(keyCombination, shortcut, true))
|
||||||
used = true;
|
used = true;
|
||||||
|
|
||||||
if (shortcut->getActivationStatus() != Shortcut::AS_Inactive)
|
if (shortcut->getActivationStatus() != Shortcut::AS_Inactive)
|
||||||
|
|
@ -175,7 +167,8 @@ namespace CSMPrefs
|
||||||
|
|
||||||
int pos = shortcut->getPosition();
|
int pos = shortcut->getPosition();
|
||||||
int lastPos = shortcut->getLastPosition();
|
int lastPos = shortcut->getLastPosition();
|
||||||
MatchResult result = match(mod, button, shortcut->getSequence()[pos]);
|
MatchResult result = match(keyCombination.keyboardModifiers(), keyCombination.key(),
|
||||||
|
shortcut->getSequence()[pos].toCombined());
|
||||||
|
|
||||||
if (result == Matches_WithMod || result == Matches_NoMod)
|
if (result == Matches_WithMod || result == Matches_NoMod)
|
||||||
{
|
{
|
||||||
|
|
@ -220,10 +213,8 @@ namespace CSMPrefs
|
||||||
return used;
|
return used;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShortcutEventHandler::deactivate(QWidget* widget, unsigned int mod, unsigned int button)
|
bool ShortcutEventHandler::deactivate(QWidget* widget, QKeyCombination keyCombination)
|
||||||
{
|
{
|
||||||
const int KeyMask = 0x01FFFFFF;
|
|
||||||
|
|
||||||
bool used = false;
|
bool used = false;
|
||||||
|
|
||||||
while (widget)
|
while (widget)
|
||||||
|
|
@ -235,11 +226,11 @@ namespace CSMPrefs
|
||||||
{
|
{
|
||||||
Shortcut* shortcut = *it;
|
Shortcut* shortcut = *it;
|
||||||
|
|
||||||
if (checkModifier(mod, button, shortcut, false))
|
if (checkModifier(keyCombination, shortcut, false))
|
||||||
used = true;
|
used = true;
|
||||||
|
|
||||||
int pos = shortcut->getPosition();
|
int pos = shortcut->getPosition();
|
||||||
MatchResult result = match(0, button, shortcut->getSequence()[pos] & KeyMask);
|
MatchResult result = match(0, keyCombination.key(), shortcut->getSequence()[pos].key());
|
||||||
|
|
||||||
if (result != Matches_Not)
|
if (result != Matches_Not)
|
||||||
{
|
{
|
||||||
|
|
@ -268,13 +259,13 @@ namespace CSMPrefs
|
||||||
return used;
|
return used;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShortcutEventHandler::checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate)
|
bool ShortcutEventHandler::checkModifier(QKeyCombination keyCombination, Shortcut* shortcut, bool activate)
|
||||||
{
|
{
|
||||||
if (!shortcut->isEnabled() || !shortcut->getModifier() || shortcut->getSecondaryMode() == Shortcut::SM_Ignore
|
if (!shortcut->isEnabled() || !shortcut->getModifier() || shortcut->getSecondaryMode() == Shortcut::SM_Ignore
|
||||||
|| shortcut->getModifierStatus() == activate)
|
|| shortcut->getModifierStatus() == activate)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MatchResult result = match(mod, button, shortcut->getModifier());
|
MatchResult result = match(keyCombination.keyboardModifiers(), keyCombination.key(), shortcut->getModifier());
|
||||||
bool used = false;
|
bool used = false;
|
||||||
|
|
||||||
if (result != Matches_Not)
|
if (result != Matches_Not)
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ namespace CSMPrefs
|
||||||
|
|
||||||
void updateParent(QWidget* widget);
|
void updateParent(QWidget* widget);
|
||||||
|
|
||||||
bool activate(QWidget* widget, unsigned int mod, unsigned int button);
|
bool activate(QWidget* widget, QKeyCombination keyCombination);
|
||||||
|
|
||||||
bool deactivate(QWidget* widget, unsigned int mod, unsigned int button);
|
bool deactivate(QWidget* widget, QKeyCombination keyCombination);
|
||||||
|
|
||||||
bool checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate);
|
bool checkModifier(QKeyCombination keyCombination, Shortcut* shortcut, bool activate);
|
||||||
|
|
||||||
MatchResult match(unsigned int mod, unsigned int button, unsigned int value);
|
MatchResult match(unsigned int mod, unsigned int button, unsigned int value);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,15 +115,12 @@ namespace CSMPrefs
|
||||||
|
|
||||||
std::string ShortcutManager::convertToString(const QKeySequence& sequence) const
|
std::string ShortcutManager::convertToString(const QKeySequence& sequence) const
|
||||||
{
|
{
|
||||||
const int MouseKeyMask = 0x01FFFFFF;
|
|
||||||
const int ModMask = 0x7E000000;
|
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
for (int i = 0; i < (int)sequence.count(); ++i)
|
for (int i = 0; i < sequence.count(); ++i)
|
||||||
{
|
{
|
||||||
int mods = sequence[i] & ModMask;
|
int mods = sequence[i].keyboardModifiers();
|
||||||
int key = sequence[i] & MouseKeyMask;
|
int key = sequence[i].key();
|
||||||
|
|
||||||
if (key)
|
if (key)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,6 @@ void CSMPrefs::State::declare()
|
||||||
.setTooltip("Minimum width of subviews.")
|
.setTooltip("Minimum width of subviews.")
|
||||||
.setRange(50, 10000);
|
.setRange(50, 10000);
|
||||||
declareEnum(mValues->mWindows.mMainwindowScrollbar, "Main Window Horizontal Scrollbar Mode");
|
declareEnum(mValues->mWindows.mMainwindowScrollbar, "Main Window Horizontal Scrollbar Mode");
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
declareBool(mValues->mWindows.mGrowLimit, "Grow Limit Screen")
|
|
||||||
.setTooltip(
|
|
||||||
"When \"Grow then Scroll\" option is selected, the window size grows to"
|
|
||||||
" the width of the virtual desktop. \nIf this option is selected the the window growth"
|
|
||||||
"is limited to the current screen.");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
declareCategory("Records");
|
declareCategory("Records");
|
||||||
declareEnum(mValues->mRecords.mStatusFormat, "Modification Status Display Format");
|
declareEnum(mValues->mRecords.mStatusFormat, "Modification Status Display Format");
|
||||||
|
|
@ -180,7 +173,10 @@ void CSMPrefs::State::declare()
|
||||||
declareInt(mValues->mRendering.mCameraOrthoSize, "Orthographic Projection Size Parameter")
|
declareInt(mValues->mRendering.mCameraOrthoSize, "Orthographic Projection Size Parameter")
|
||||||
.setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.")
|
.setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.")
|
||||||
.setRange(10, 10000);
|
.setRange(10, 10000);
|
||||||
declareDouble(mValues->mRendering.mObjectMarkerAlpha, "Object Marker Transparency").setPrecision(2).setRange(0, 1);
|
declareDouble(mValues->mRendering.mObjectMarkerScale, "Object Marker Scale Factor")
|
||||||
|
.setPrecision(2)
|
||||||
|
.setRange(.01f, 100.f)
|
||||||
|
.setTooltip("Multiplier for the size of object selection markers.");
|
||||||
declareBool(mValues->mRendering.mSceneUseGradient, "Use Gradient Background");
|
declareBool(mValues->mRendering.mSceneUseGradient, "Use Gradient Background");
|
||||||
declareColour(mValues->mRendering.mSceneDayBackgroundColour, "Day Background Colour");
|
declareColour(mValues->mRendering.mSceneDayBackgroundColour, "Day Background Colour");
|
||||||
declareColour(mValues->mRendering.mSceneDayGradientColour, "Day Gradient Colour")
|
declareColour(mValues->mRendering.mSceneDayGradientColour, "Day Gradient Colour")
|
||||||
|
|
@ -376,6 +372,7 @@ void CSMPrefs::State::declare()
|
||||||
declareShortcut(mValues->mKeyBindings.mSceneScaleSubmode, "Scale Object Submode");
|
declareShortcut(mValues->mKeyBindings.mSceneScaleSubmode, "Scale Object Submode");
|
||||||
declareShortcut(mValues->mKeyBindings.mSceneRotateSubmode, "Rotate Object Submode");
|
declareShortcut(mValues->mKeyBindings.mSceneRotateSubmode, "Rotate Object Submode");
|
||||||
declareShortcut(mValues->mKeyBindings.mSceneCameraCycle, "Cycle Camera Mode");
|
declareShortcut(mValues->mKeyBindings.mSceneCameraCycle, "Cycle Camera Mode");
|
||||||
|
declareShortcut(mValues->mKeyBindings.mSceneToggleMarker, "Toggle Selection Marker");
|
||||||
|
|
||||||
declareSubcategory("1st/Free Camera");
|
declareSubcategory("1st/Free Camera");
|
||||||
declareShortcut(mValues->mKeyBindings.mFreeForward, "Forward");
|
declareShortcut(mValues->mKeyBindings.mFreeForward, "Forward");
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ namespace CSMPrefs
|
||||||
Settings::SettingValue<int> mCameraFov{ mIndex, sName, "camera-fov", 90 };
|
Settings::SettingValue<int> mCameraFov{ mIndex, sName, "camera-fov", 90 };
|
||||||
Settings::SettingValue<bool> mCameraOrtho{ mIndex, sName, "camera-ortho", false };
|
Settings::SettingValue<bool> mCameraOrtho{ mIndex, sName, "camera-ortho", false };
|
||||||
Settings::SettingValue<int> mCameraOrthoSize{ mIndex, sName, "camera-ortho-size", 100 };
|
Settings::SettingValue<int> mCameraOrthoSize{ mIndex, sName, "camera-ortho-size", 100 };
|
||||||
Settings::SettingValue<double> mObjectMarkerAlpha{ mIndex, sName, "object-marker-alpha", 0.5 };
|
Settings::SettingValue<double> mObjectMarkerScale{ mIndex, sName, "object-marker-scale", 5.0 };
|
||||||
Settings::SettingValue<bool> mSceneUseGradient{ mIndex, sName, "scene-use-gradient", true };
|
Settings::SettingValue<bool> mSceneUseGradient{ mIndex, sName, "scene-use-gradient", true };
|
||||||
Settings::SettingValue<std::string> mSceneDayBackgroundColour{ mIndex, sName, "scene-day-background-colour",
|
Settings::SettingValue<std::string> mSceneDayBackgroundColour{ mIndex, sName, "scene-day-background-colour",
|
||||||
"#6e7880" };
|
"#6e7880" };
|
||||||
|
|
@ -491,7 +491,7 @@ namespace CSMPrefs
|
||||||
Settings::SettingValue<std::string> mSceneScaleSubmode{ mIndex, sName, "scene-submode-scale", "V" };
|
Settings::SettingValue<std::string> mSceneScaleSubmode{ mIndex, sName, "scene-submode-scale", "V" };
|
||||||
Settings::SettingValue<std::string> mSceneRotateSubmode{ mIndex, sName, "scene-submode-rotate", "R" };
|
Settings::SettingValue<std::string> mSceneRotateSubmode{ mIndex, sName, "scene-submode-rotate", "R" };
|
||||||
Settings::SettingValue<std::string> mSceneCameraCycle{ mIndex, sName, "scene-cam-cycle", "Tab" };
|
Settings::SettingValue<std::string> mSceneCameraCycle{ mIndex, sName, "scene-cam-cycle", "Tab" };
|
||||||
Settings::SettingValue<std::string> mSceneToggleMarkers{ mIndex, sName, "scene-toggle-markers", "F4" };
|
Settings::SettingValue<std::string> mSceneToggleMarker{ mIndex, sName, "scene-toggle-marker", "F4" };
|
||||||
Settings::SettingValue<std::string> mFreeForward{ mIndex, sName, "free-forward", "W" };
|
Settings::SettingValue<std::string> mFreeForward{ mIndex, sName, "free-forward", "W" };
|
||||||
Settings::SettingValue<std::string> mFreeBackward{ mIndex, sName, "free-backward", "S" };
|
Settings::SettingValue<std::string> mFreeBackward{ mIndex, sName, "free-backward", "S" };
|
||||||
Settings::SettingValue<std::string> mFreeLeft{ mIndex, sName, "free-left", "A" };
|
Settings::SettingValue<std::string> mFreeLeft{ mIndex, sName, "free-left", "A" };
|
||||||
|
|
@ -507,8 +507,10 @@ namespace CSMPrefs
|
||||||
Settings::SettingValue<std::string> mOrbitRollRight{ mIndex, sName, "orbit-roll-right", "E" };
|
Settings::SettingValue<std::string> mOrbitRollRight{ mIndex, sName, "orbit-roll-right", "E" };
|
||||||
Settings::SettingValue<std::string> mOrbitSpeedMode{ mIndex, sName, "orbit-speed-mode", "" };
|
Settings::SettingValue<std::string> mOrbitSpeedMode{ mIndex, sName, "orbit-speed-mode", "" };
|
||||||
Settings::SettingValue<std::string> mOrbitCenterSelection{ mIndex, sName, "orbit-center-selection", "C" };
|
Settings::SettingValue<std::string> mOrbitCenterSelection{ mIndex, sName, "orbit-center-selection", "C" };
|
||||||
Settings::SettingValue<std::string> mScriptEditorComment{ mIndex, sName, "script-editor-comment", "" };
|
Settings::SettingValue<std::string> mScriptEditorComment{ mIndex, sName, "script-editor-comment",
|
||||||
Settings::SettingValue<std::string> mScriptEditorUncomment{ mIndex, sName, "script-editor-uncomment", "" };
|
"Ctrl+Slash" };
|
||||||
|
Settings::SettingValue<std::string> mScriptEditorUncomment{ mIndex, sName, "script-editor-uncomment",
|
||||||
|
"Ctrl+Shift+Question" };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ModelsCategory : Settings::WithIndex
|
struct ModelsCategory : Settings::WithIndex
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
|
||||||
, mArchives(archives)
|
, mArchives(archives)
|
||||||
, mVFS(std::make_unique<VFS::Manager>())
|
, mVFS(std::make_unique<VFS::Manager>())
|
||||||
{
|
{
|
||||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true);
|
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder());
|
||||||
|
|
||||||
mResourcesManager.setVFS(mVFS.get());
|
mResourcesManager.setVFS(mVFS.get());
|
||||||
|
|
||||||
|
|
@ -1465,7 +1465,7 @@ std::vector<ESM::RefId> CSMWorld::Data::getIds(bool listDeleted) const
|
||||||
void CSMWorld::Data::assetsChanged()
|
void CSMWorld::Data::assetsChanged()
|
||||||
{
|
{
|
||||||
mVFS.get()->reset();
|
mVFS.get()->reset();
|
||||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true);
|
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths), mArchives, true, &mEncoder.getStatelessEncoder());
|
||||||
|
|
||||||
const UniversalId assetTableIds[] = { UniversalId::Type_Meshes, UniversalId::Type_Icons, UniversalId::Type_Musics,
|
const UniversalId assetTableIds[] = { UniversalId::Type_Meshes, UniversalId::Type_Icons, UniversalId::Type_Musics,
|
||||||
UniversalId::Type_SoundsRes, UniversalId::Type_Textures, UniversalId::Type_Videos };
|
UniversalId::Type_SoundsRes, UniversalId::Type_Textures, UniversalId::Type_Videos };
|
||||||
|
|
|
||||||
|
|
@ -625,8 +625,6 @@ bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue(
|
||||||
default:
|
default:
|
||||||
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
|
|
@ -651,8 +649,6 @@ bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue(
|
||||||
default:
|
default:
|
||||||
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CSMWorld::ConstInfoSelectWrapper::getValue() const
|
QVariant CSMWorld::ConstInfoSelectWrapper::getValue() const
|
||||||
|
|
|
||||||
|
|
@ -753,7 +753,6 @@ void CSMWorld::RefIdCollection::cloneRecord(
|
||||||
bool CSMWorld::RefIdCollection::touchRecord(const ESM::RefId& id)
|
bool CSMWorld::RefIdCollection::touchRecord(const ESM::RefId& id)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("RefIdCollection::touchRecord is unimplemented");
|
throw std::runtime_error("RefIdCollection::touchRecord is unimplemented");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::appendRecord(std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
void CSMWorld::RefIdCollection::appendRecord(std::unique_ptr<RecordBase> record, UniversalId::Type type)
|
||||||
|
|
|
||||||
|
|
@ -659,11 +659,7 @@ void CSVDoc::View::addSubView(const CSMWorld::UniversalId& id, const std::string
|
||||||
//
|
//
|
||||||
mScrollbarOnly = windows["mainwindow-scrollbar"].toString() == "Scrollbar Only";
|
mScrollbarOnly = windows["mainwindow-scrollbar"].toString() == "Scrollbar Only";
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
updateWidth(windows["grow-limit"].isTrue(), minWidth);
|
|
||||||
#else
|
|
||||||
updateWidth(true, minWidth);
|
updateWidth(true, minWidth);
|
||||||
#endif
|
|
||||||
|
|
||||||
mSubViewWindow.addDockWidget(Qt::TopDockWidgetArea, view);
|
mSubViewWindow.addDockWidget(Qt::TopDockWidgetArea, view);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ namespace CSVFilter
|
||||||
std::pair<std::string, FilterType> operator()(const QVariant& variantData)
|
std::pair<std::string, FilterType> operator()(const QVariant& variantData)
|
||||||
{
|
{
|
||||||
FilterType filterType = FilterType::String;
|
FilterType filterType = FilterType::String;
|
||||||
QMetaType::Type dataType = static_cast<QMetaType::Type>(variantData.type());
|
QMetaType::Type dataType = static_cast<QMetaType::Type>(variantData.typeId());
|
||||||
if (dataType == QMetaType::QString || dataType == QMetaType::Bool || dataType == QMetaType::Int)
|
if (dataType == QMetaType::QString || dataType == QMetaType::Bool || dataType == QMetaType::Int)
|
||||||
filterType = FilterType::String;
|
filterType = FilterType::String;
|
||||||
if (dataType == QMetaType::Int || dataType == QMetaType::Float)
|
if (dataType == QMetaType::Int || dataType == QMetaType::Float)
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,10 @@
|
||||||
#include "cellwater.hpp"
|
#include "cellwater.hpp"
|
||||||
#include "instancedragmodes.hpp"
|
#include "instancedragmodes.hpp"
|
||||||
#include "mask.hpp"
|
#include "mask.hpp"
|
||||||
#include "object.hpp"
|
#include "objectmarker.hpp"
|
||||||
#include "pathgrid.hpp"
|
#include "pathgrid.hpp"
|
||||||
#include "terrainstorage.hpp"
|
#include "terrainstorage.hpp"
|
||||||
|
#include "worldspacewidget.hpp"
|
||||||
|
|
||||||
#include <apps/opencs/model/world/cell.hpp>
|
#include <apps/opencs/model/world/cell.hpp>
|
||||||
#include <apps/opencs/model/world/cellcoordinates.hpp>
|
#include <apps/opencs/model/world/cellcoordinates.hpp>
|
||||||
|
|
@ -107,9 +108,6 @@ bool CSVRender::Cell::addObjects(int start, int end)
|
||||||
|
|
||||||
auto object = std::make_unique<Object>(mData, mCellNode, id, false);
|
auto object = std::make_unique<Object>(mData, mCellNode, id, false);
|
||||||
|
|
||||||
if (mSubModeElementMask & Mask_Reference)
|
|
||||||
object->setSubMode(mSubMode);
|
|
||||||
|
|
||||||
mObjects.insert(std::make_pair(id, object.release()));
|
mObjects.insert(std::make_pair(id, object.release()));
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
@ -168,9 +166,10 @@ void CSVRender::Cell::unloadLand()
|
||||||
mCellBorder.reset();
|
mCellBorder.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::Cell::Cell(
|
CSVRender::Cell::Cell(CSMDoc::Document& document, ObjectMarker* selectionMarker, osg::Group* rootNode,
|
||||||
CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted, bool isExterior)
|
const std::string& id, bool deleted, bool isExterior)
|
||||||
: mData(document.getData())
|
: mSelectionMarker(selectionMarker)
|
||||||
|
, mData(document.getData())
|
||||||
, mId(ESM::RefId::stringRefId(id))
|
, mId(ESM::RefId::stringRefId(id))
|
||||||
, mDeleted(deleted)
|
, mDeleted(deleted)
|
||||||
, mSubMode(0)
|
, mSubMode(0)
|
||||||
|
|
@ -466,7 +465,10 @@ void CSVRender::Cell::setSelection(int elementMask, Selection mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
iter->second->setSelected(selected);
|
iter->second->setSelected(selected);
|
||||||
|
if (selected)
|
||||||
|
mSelectionMarker->addToSelectionHistory(iter->second->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||||
{
|
{
|
||||||
|
|
@ -506,8 +508,10 @@ void CSVRender::Cell::selectAllWithSameParentId(int elementMask)
|
||||||
if (!iter->second->getSelected() && ids.find(iter->second->getReferenceableId()) != ids.end())
|
if (!iter->second->getSelected() && ids.find(iter->second->getReferenceableId()) != ids.end())
|
||||||
{
|
{
|
||||||
iter->second->setSelected(true);
|
iter->second->setSelected(true);
|
||||||
|
mSelectionMarker->addToSelectionHistory(iter->second->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
||||||
|
|
@ -520,6 +524,9 @@ void CSVRender::Cell::handleSelectDrag(Object* object, DragMode dragMode)
|
||||||
|
|
||||||
else if (dragMode == DragMode_Select_Invert)
|
else if (dragMode == DragMode_Select_Invert)
|
||||||
object->setSelected(!object->getSelected());
|
object->setSelected(!object->getSelected());
|
||||||
|
|
||||||
|
if (object->getSelected())
|
||||||
|
mSelectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)
|
void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3d& pointB, DragMode dragMode)
|
||||||
|
|
@ -542,6 +549,8 @@ void CSVRender::Cell::selectInsideCube(const osg::Vec3d& pointA, const osg::Vec3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)
|
void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode)
|
||||||
|
|
@ -555,6 +564,8 @@ void CSVRender::Cell::selectWithinDistance(const osg::Vec3d& point, float distan
|
||||||
if (distanceFromObject < distance)
|
if (distanceFromObject < distance)
|
||||||
handleSelectDrag(object.second, dragMode);
|
handleSelectDrag(object.second, dragMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::setCellArrows(int mask)
|
void CSVRender::Cell::setCellArrows(int mask)
|
||||||
|
|
@ -625,9 +636,11 @@ void CSVRender::Cell::selectFromGroup(const std::vector<std::string>& group)
|
||||||
if (objectName == object->getReferenceId())
|
if (objectName == object->getReferenceId())
|
||||||
{
|
{
|
||||||
object->setSelected(true, osg::Vec4f(1, 0, 1, 1));
|
object->setSelected(true, osg::Vec4f(1, 0, 1, 1));
|
||||||
|
mSelectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::unhideAll()
|
void CSVRender::Cell::unhideAll()
|
||||||
|
|
@ -673,8 +686,7 @@ void CSVRender::Cell::setSubMode(int subMode, unsigned int elementMask)
|
||||||
mSubModeElementMask = elementMask;
|
mSubModeElementMask = elementMask;
|
||||||
|
|
||||||
if (elementMask & Mask_Reference)
|
if (elementMask & Mask_Reference)
|
||||||
for (std::map<std::string, Object*>::const_iterator iter(mObjects.begin()); iter != mObjects.end(); ++iter)
|
mSelectionMarker->setSubMode(subMode);
|
||||||
iter->second->setSubMode(subMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Cell::reset(unsigned int elementMask)
|
void CSVRender::Cell::reset(unsigned int elementMask)
|
||||||
|
|
@ -685,3 +697,11 @@ void CSVRender::Cell::reset(unsigned int elementMask)
|
||||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||||
mPathgrid->resetIndicators();
|
mPathgrid->resetIndicators();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSVRender::Object* CSVRender::Cell::getObjectByReferenceId(const std::string& referenceId)
|
||||||
|
{
|
||||||
|
if (auto iter = mObjects.find(Misc::StringUtils::lowerCase(referenceId)); iter != mObjects.end())
|
||||||
|
return iter->second;
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@
|
||||||
#include <osg/Vec3d>
|
#include <osg/Vec3d>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include "../../model/doc/document.hpp"
|
|
||||||
#include "../../model/world/cellcoordinates.hpp"
|
#include "../../model/world/cellcoordinates.hpp"
|
||||||
#include "instancedragmodes.hpp"
|
#include "instancedragmodes.hpp"
|
||||||
|
#include "worldspacewidget.hpp"
|
||||||
#include <components/esm/refid.hpp>
|
#include <components/esm/refid.hpp>
|
||||||
#include <components/misc/algorithm.hpp>
|
#include <components/misc/algorithm.hpp>
|
||||||
|
|
||||||
|
|
@ -44,8 +44,11 @@ namespace CSVRender
|
||||||
class CellBorder;
|
class CellBorder;
|
||||||
class CellMarker;
|
class CellMarker;
|
||||||
|
|
||||||
|
class ObjectMarker;
|
||||||
|
|
||||||
class Cell
|
class Cell
|
||||||
{
|
{
|
||||||
|
ObjectMarker* const mSelectionMarker;
|
||||||
CSMWorld::Data& mData;
|
CSMWorld::Data& mData;
|
||||||
ESM::RefId mId;
|
ESM::RefId mId;
|
||||||
osg::ref_ptr<osg::Group> mCellNode;
|
osg::ref_ptr<osg::Group> mCellNode;
|
||||||
|
|
@ -90,8 +93,8 @@ namespace CSVRender
|
||||||
public:
|
public:
|
||||||
/// \note Deleted covers both cells that are deleted and cells that don't exist in
|
/// \note Deleted covers both cells that are deleted and cells that don't exist in
|
||||||
/// the first place.
|
/// the first place.
|
||||||
Cell(CSMDoc::Document& document, osg::Group* rootNode, const std::string& id, bool deleted = false,
|
Cell(CSMDoc::Document& document, ObjectMarker* selectionMarker, osg::Group* rootNode, const std::string& id,
|
||||||
bool isExterior = false);
|
bool deleted = false, bool isExterior = false);
|
||||||
|
|
||||||
~Cell();
|
~Cell();
|
||||||
|
|
||||||
|
|
@ -182,6 +185,8 @@ namespace CSVRender
|
||||||
/// true state.
|
/// true state.
|
||||||
void reset(unsigned int elementMask);
|
void reset(unsigned int elementMask);
|
||||||
|
|
||||||
|
CSVRender::Object* getObjectByReferenceId(const std::string& referenceId);
|
||||||
|
|
||||||
friend class CellNodeCallback;
|
friend class CellNodeCallback;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -362,7 +362,29 @@ CSVRender::InstanceMode::InstanceMode(
|
||||||
|
|
||||||
for (const char axis : "xyz")
|
for (const char axis : "xyz")
|
||||||
connect(new CSMPrefs::Shortcut(std::string("scene-axis-") + axis, worldspaceWidget),
|
connect(new CSMPrefs::Shortcut(std::string("scene-axis-") + axis, worldspaceWidget),
|
||||||
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, axis] { this->setDragAxis(axis); });
|
qOverload<>(&CSMPrefs::Shortcut::activated), this, [this, axis] {
|
||||||
|
this->setDragAxis(axis);
|
||||||
|
std::string axisStr(1, toupper(axis));
|
||||||
|
switch (getSubMode())
|
||||||
|
{
|
||||||
|
case (Object::Mode_Move):
|
||||||
|
axisStr += "_Axis";
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Rotate):
|
||||||
|
axisStr += "_Axis_Rot";
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Scale):
|
||||||
|
axisStr += "_Axis_Scale";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto selectionMarker = getWorldspaceWidget().getSelectionMarker();
|
||||||
|
|
||||||
|
if (mDragAxis != -1)
|
||||||
|
selectionMarker->updateMarkerHighlight(axisStr, axis - 'x');
|
||||||
|
else
|
||||||
|
selectionMarker->resetMarkerHighlight();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::InstanceMode::activate(CSVWidget::SceneToolbar* toolbar)
|
void CSVRender::InstanceMode::activate(CSVWidget::SceneToolbar* toolbar)
|
||||||
|
|
@ -460,52 +482,58 @@ void CSVRender::InstanceMode::secondaryEditPressed(const WorldspaceHitResult& hi
|
||||||
|
|
||||||
void CSVRender::InstanceMode::primarySelectPressed(const WorldspaceHitResult& hit)
|
void CSVRender::InstanceMode::primarySelectPressed(const WorldspaceHitResult& hit)
|
||||||
{
|
{
|
||||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
auto& worldspaceWidget = getWorldspaceWidget();
|
||||||
|
|
||||||
|
worldspaceWidget.clearSelection(Mask_Reference);
|
||||||
|
|
||||||
|
if (!hit.tag)
|
||||||
|
return;
|
||||||
|
|
||||||
if (hit.tag)
|
|
||||||
{
|
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
// hit an Object, select it
|
// hit an Object, select it
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
object->setSelected(true);
|
object->setSelected(true);
|
||||||
return;
|
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::InstanceMode::secondarySelectPressed(const WorldspaceHitResult& hit)
|
void CSVRender::InstanceMode::secondarySelectPressed(const WorldspaceHitResult& hit)
|
||||||
{
|
{
|
||||||
if (hit.tag)
|
if (!hit.tag)
|
||||||
{
|
return;
|
||||||
|
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
// hit an Object, toggle its selection state
|
// hit an Object, toggle its selection state
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
object->setSelected(!object->getSelected());
|
object->setSelected(!object->getSelected());
|
||||||
return;
|
|
||||||
}
|
const auto selectionMarker = getWorldspaceWidget().getSelectionMarker();
|
||||||
|
|
||||||
|
if (object->getSelected())
|
||||||
|
selectionMarker->addToSelectionHistory(object->getReferenceId(), false);
|
||||||
|
|
||||||
|
selectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::InstanceMode::tertiarySelectPressed(const WorldspaceHitResult& hit)
|
void CSVRender::InstanceMode::tertiarySelectPressed(const WorldspaceHitResult& hit)
|
||||||
{
|
{
|
||||||
auto* snapTarget = dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get());
|
if (auto* snapTarget
|
||||||
|
= dynamic_cast<CSVRender::ObjectTag*>(getWorldspaceWidget().getSnapTarget(Mask_Reference).get()))
|
||||||
if (snapTarget)
|
|
||||||
{
|
{
|
||||||
snapTarget->mObject->setSnapTarget(false);
|
snapTarget->mObject->setSnapTarget(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hit.tag)
|
if (!hit.tag)
|
||||||
{
|
return;
|
||||||
|
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
// hit an Object, toggle its selection state
|
// hit an Object, toggle its selection state
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
object->setSnapTarget(!object->getSnapTarget());
|
object->setSnapTarget(!object->getSnapTarget());
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -514,23 +542,26 @@ bool CSVRender::InstanceMode::primaryEditStartDrag(const QPoint& pos)
|
||||||
if (mDragMode != DragMode_None || mLocked)
|
if (mDragMode != DragMode_None || mLocked)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
|
auto& worldspaceWidget = getWorldspaceWidget();
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
WorldspaceHitResult hit = worldspaceWidget.mousePick(pos, worldspaceWidget.getInteractionMask());
|
||||||
|
|
||||||
|
std::vector<osg::ref_ptr<TagBase>> selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
{
|
{
|
||||||
// Only change selection at the start of drag if no object is already selected
|
// Only change selection at the start of drag if no object is already selected
|
||||||
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
||||||
{
|
{
|
||||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
worldspaceWidget.clearSelection(Mask_Reference);
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
object->setSelected(true);
|
object->setSelected(true);
|
||||||
|
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -591,23 +622,26 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag(const QPoint& pos)
|
||||||
if (mDragMode != DragMode_None || mLocked)
|
if (mDragMode != DragMode_None || mLocked)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
|
auto& worldspaceWidget = getWorldspaceWidget();
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
WorldspaceHitResult hit = worldspaceWidget.mousePick(pos, worldspaceWidget.getInteractionMask());
|
||||||
|
|
||||||
|
std::vector<osg::ref_ptr<TagBase>> selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
{
|
{
|
||||||
// Only change selection at the start of drag if no object is already selected
|
// Only change selection at the start of drag if no object is already selected
|
||||||
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
if (hit.tag && CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
|
||||||
{
|
{
|
||||||
getWorldspaceWidget().clearSelection(Mask_Reference);
|
worldspaceWidget.clearSelection(Mask_Reference);
|
||||||
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
if (CSVRender::ObjectTag* objectTag = dynamic_cast<CSVRender::ObjectTag*>(hit.tag.get()))
|
||||||
{
|
{
|
||||||
CSVRender::Object* object = objectTag->mObject;
|
CSVRender::Object* object = objectTag->mObject;
|
||||||
object->setSelected(true);
|
object->setSelected(true);
|
||||||
|
worldspaceWidget.getSelectionMarker()->addToSelectionHistory(object->getReferenceId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selection = getWorldspaceWidget().getSelection(Mask_Reference);
|
selection = worldspaceWidget.getSelection(Mask_Reference);
|
||||||
if (selection.empty())
|
if (selection.empty())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -641,10 +675,10 @@ bool CSVRender::InstanceMode::secondaryEditStartDrag(const QPoint& pos)
|
||||||
mDragMode = DragMode_Scale_Snap;
|
mDragMode = DragMode_Scale_Snap;
|
||||||
|
|
||||||
// Calculate scale factor
|
// Calculate scale factor
|
||||||
std::vector<osg::ref_ptr<TagBase>> editedSelection = getWorldspaceWidget().getEdited(Mask_Reference);
|
std::vector<osg::ref_ptr<TagBase>> editedSelection = worldspaceWidget.getEdited(Mask_Reference);
|
||||||
osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));
|
osg::Vec3f center = getScreenCoords(getSelectionCenter(editedSelection));
|
||||||
|
|
||||||
int widgetHeight = getWorldspaceWidget().height();
|
int widgetHeight = worldspaceWidget.height();
|
||||||
|
|
||||||
float dx = pos.x() - center.x();
|
float dx = pos.x() - center.x();
|
||||||
float dy = (widgetHeight - pos.y()) - center.y();
|
float dy = (widgetHeight - pos.y()) - center.y();
|
||||||
|
|
@ -1098,7 +1132,7 @@ void CSVRender::InstanceMode::dropEvent(QDropEvent* event)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WorldspaceHitResult hit
|
WorldspaceHitResult hit
|
||||||
= getWorldspaceWidget().mousePick(event->pos(), getWorldspaceWidget().getInteractionMask());
|
= getWorldspaceWidget().mousePick(event->position().toPoint(), getWorldspaceWidget().getInteractionMask());
|
||||||
|
|
||||||
std::string cellId = getWorldspaceWidget().getCellId(hit.worldPos);
|
std::string cellId = getWorldspaceWidget().getCellId(hit.worldPos);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,25 +18,11 @@
|
||||||
#include <apps/opencs/model/world/universalid.hpp>
|
#include <apps/opencs/model/world/universalid.hpp>
|
||||||
#include <apps/opencs/view/render/tagbase.hpp>
|
#include <apps/opencs/view/render/tagbase.hpp>
|
||||||
|
|
||||||
#include <osg/Array>
|
|
||||||
#include <osg/BoundingSphere>
|
|
||||||
#include <osg/GL>
|
|
||||||
#include <osg/Geometry>
|
|
||||||
#include <osg/Group>
|
|
||||||
#include <osg/Math>
|
|
||||||
#include <osg/Node>
|
|
||||||
#include <osg/PositionAttitudeTransform>
|
|
||||||
#include <osg/PrimitiveSet>
|
|
||||||
#include <osg/Quat>
|
#include <osg/Quat>
|
||||||
#include <osg/Shape>
|
|
||||||
#include <osg/ShapeDrawable>
|
#include <osg/ShapeDrawable>
|
||||||
#include <osg/StateAttribute>
|
|
||||||
#include <osg/StateSet>
|
|
||||||
#include <osg/Vec3>
|
|
||||||
|
|
||||||
#include <osgFX/Scribe>
|
#include <osgFX/Scribe>
|
||||||
|
|
||||||
#include "../../model/prefs/state.hpp"
|
|
||||||
#include "../../model/world/cellcoordinates.hpp"
|
#include "../../model/world/cellcoordinates.hpp"
|
||||||
#include "../../model/world/commandmacro.hpp"
|
#include "../../model/world/commandmacro.hpp"
|
||||||
#include "../../model/world/commands.hpp"
|
#include "../../model/world/commands.hpp"
|
||||||
|
|
@ -63,11 +49,6 @@ namespace ESM
|
||||||
struct Light;
|
struct Light;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float CSVRender::Object::MarkerShaftWidth = 30;
|
|
||||||
const float CSVRender::Object::MarkerShaftBaseLength = 70;
|
|
||||||
const float CSVRender::Object::MarkerHeadWidth = 50;
|
|
||||||
const float CSVRender::Object::MarkerHeadLength = 50;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -95,12 +76,6 @@ QString CSVRender::ObjectTag::getToolTip(bool /*hideBasics*/, const WorldspaceHi
|
||||||
return QString::fromUtf8(mObject->getReferenceableId().c_str());
|
return QString::fromUtf8(mObject->getReferenceableId().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::ObjectMarkerTag::ObjectMarkerTag(Object* object, int axis)
|
|
||||||
: ObjectTag(object)
|
|
||||||
, mAxis(axis)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::clear() {}
|
void CSVRender::Object::clear() {}
|
||||||
|
|
||||||
void CSVRender::Object::update()
|
void CSVRender::Object::update()
|
||||||
|
|
@ -204,238 +179,6 @@ const CSMWorld::CellRef& CSVRender::Object::getReference() const
|
||||||
return mData.getReferences().getRecord(mReferenceId).get();
|
return mData.getReferences().getRecord(mReferenceId).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Object::updateMarker()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
if (mMarker[i])
|
|
||||||
{
|
|
||||||
mRootNode->removeChild(mMarker[i]);
|
|
||||||
mMarker[i] = osg::ref_ptr<osg::Node>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mSelected)
|
|
||||||
{
|
|
||||||
if (mSubMode == 0)
|
|
||||||
{
|
|
||||||
mMarker[i] = makeMoveOrScaleMarker(i);
|
|
||||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
|
||||||
|
|
||||||
mRootNode->addChild(mMarker[i]);
|
|
||||||
}
|
|
||||||
else if (mSubMode == 1)
|
|
||||||
{
|
|
||||||
mMarker[i] = makeRotateMarker(i);
|
|
||||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
|
||||||
|
|
||||||
mRootNode->addChild(mMarker[i]);
|
|
||||||
}
|
|
||||||
else if (mSubMode == 2)
|
|
||||||
{
|
|
||||||
mMarker[i] = makeMoveOrScaleMarker(i);
|
|
||||||
mMarker[i]->setUserData(new ObjectMarkerTag(this, i));
|
|
||||||
|
|
||||||
mRootNode->addChild(mMarker[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> CSVRender::Object::makeMoveOrScaleMarker(int axis)
|
|
||||||
{
|
|
||||||
osg::ref_ptr<osg::Geometry> geometry(new osg::Geometry);
|
|
||||||
|
|
||||||
float shaftLength = MarkerShaftBaseLength + mBaseNode->getBound().radius();
|
|
||||||
|
|
||||||
// shaft
|
|
||||||
osg::Vec3Array* vertices = new osg::Vec3Array;
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i)
|
|
||||||
{
|
|
||||||
float length = i ? shaftLength : MarkerShaftWidth;
|
|
||||||
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, MarkerShaftWidth / 2, length, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerShaftWidth / 2, -MarkerShaftWidth / 2, length, axis));
|
|
||||||
}
|
|
||||||
|
|
||||||
// head backside
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(-MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
vertices->push_back(getMarkerPosition(MarkerHeadWidth / 2, -MarkerHeadWidth / 2, shaftLength, axis));
|
|
||||||
|
|
||||||
// head
|
|
||||||
vertices->push_back(getMarkerPosition(0, 0, shaftLength + MarkerHeadLength, axis));
|
|
||||||
|
|
||||||
geometry->setVertexArray(vertices);
|
|
||||||
|
|
||||||
osg::DrawElementsUShort* primitives = new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, 0);
|
|
||||||
|
|
||||||
// shaft
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
int i2 = i == 3 ? 0 : i + 1;
|
|
||||||
primitives->push_back(i);
|
|
||||||
primitives->push_back(4 + i);
|
|
||||||
primitives->push_back(i2);
|
|
||||||
|
|
||||||
primitives->push_back(4 + i);
|
|
||||||
primitives->push_back(4 + i2);
|
|
||||||
primitives->push_back(i2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cap
|
|
||||||
primitives->push_back(0);
|
|
||||||
primitives->push_back(1);
|
|
||||||
primitives->push_back(2);
|
|
||||||
|
|
||||||
primitives->push_back(2);
|
|
||||||
primitives->push_back(3);
|
|
||||||
primitives->push_back(0);
|
|
||||||
|
|
||||||
// head, backside
|
|
||||||
primitives->push_back(0 + 8);
|
|
||||||
primitives->push_back(1 + 8);
|
|
||||||
primitives->push_back(2 + 8);
|
|
||||||
|
|
||||||
primitives->push_back(2 + 8);
|
|
||||||
primitives->push_back(3 + 8);
|
|
||||||
primitives->push_back(0 + 8);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
{
|
|
||||||
primitives->push_back(12);
|
|
||||||
primitives->push_back(8 + (i == 3 ? 0 : i + 1));
|
|
||||||
primitives->push_back(8 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry->addPrimitiveSet(primitives);
|
|
||||||
|
|
||||||
osg::Vec4Array* colours = new osg::Vec4Array;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
colours->push_back(
|
|
||||||
osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency));
|
|
||||||
|
|
||||||
for (int i = 8; i < 8 + 4 + 1; ++i)
|
|
||||||
colours->push_back(
|
|
||||||
osg::Vec4f(axis == 0 ? 1.0f : 0.0f, axis == 1 ? 1.0f : 0.0f, axis == 2 ? 1.0f : 0.0f, mMarkerTransparency));
|
|
||||||
|
|
||||||
geometry->setColorArray(colours, osg::Array::BIND_PER_VERTEX);
|
|
||||||
|
|
||||||
setupCommonMarkerState(geometry);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> group(new osg::Group);
|
|
||||||
group->addChild(geometry);
|
|
||||||
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker(int axis)
|
|
||||||
{
|
|
||||||
const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius());
|
|
||||||
const float OuterRadius = InnerRadius + MarkerShaftWidth;
|
|
||||||
|
|
||||||
const float SegmentDistance = 100.f;
|
|
||||||
const size_t SegmentCount = std::clamp<int>(OuterRadius * 2 * osg::PI / SegmentDistance, 24, 64);
|
|
||||||
const size_t VerticesPerSegment = 4;
|
|
||||||
const size_t IndicesPerSegment = 24;
|
|
||||||
|
|
||||||
const size_t VertexCount = SegmentCount * VerticesPerSegment;
|
|
||||||
const size_t IndexCount = SegmentCount * IndicesPerSegment;
|
|
||||||
|
|
||||||
const float Angle = 2 * osg::PI / SegmentCount;
|
|
||||||
|
|
||||||
const unsigned short IndexPattern[IndicesPerSegment]
|
|
||||||
= { 0, 4, 5, 0, 5, 1, 2, 6, 4, 2, 4, 0, 3, 7, 6, 3, 6, 2, 1, 5, 7, 1, 7, 3 };
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(VertexCount);
|
|
||||||
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
|
|
||||||
osg::ref_ptr<osg::DrawElementsUShort> primitives
|
|
||||||
= new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, IndexCount);
|
|
||||||
|
|
||||||
// prevent some depth collision issues from overlaps
|
|
||||||
osg::Vec3f offset = getMarkerPosition(0, MarkerShaftWidth / 4, 0, axis);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < SegmentCount; ++i)
|
|
||||||
{
|
|
||||||
size_t index = i * VerticesPerSegment;
|
|
||||||
|
|
||||||
float innerX = InnerRadius * std::cos(i * Angle);
|
|
||||||
float innerY = InnerRadius * std::sin(i * Angle);
|
|
||||||
|
|
||||||
float outerX = OuterRadius * std::cos(i * Angle);
|
|
||||||
float outerY = OuterRadius * std::sin(i * Angle);
|
|
||||||
|
|
||||||
vertices->at(index++) = getMarkerPosition(innerX, innerY, MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
vertices->at(index++) = getMarkerPosition(innerX, innerY, -MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
vertices->at(index++) = getMarkerPosition(outerX, outerY, MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
vertices->at(index++) = getMarkerPosition(outerX, outerY, -MarkerShaftWidth / 2, axis) + offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
colors->at(0)
|
|
||||||
= osg::Vec4f(axis == 0 ? 1.0f : 0.2f, axis == 1 ? 1.0f : 0.2f, axis == 2 ? 1.0f : 0.2f, mMarkerTransparency);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < SegmentCount; ++i)
|
|
||||||
{
|
|
||||||
size_t indices[IndicesPerSegment];
|
|
||||||
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
|
||||||
{
|
|
||||||
indices[j] = i * VerticesPerSegment + j;
|
|
||||||
|
|
||||||
if (indices[j] >= VertexCount)
|
|
||||||
indices[j] -= VertexCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t elementOffset = i * IndicesPerSegment;
|
|
||||||
for (size_t j = 0; j < IndicesPerSegment; ++j)
|
|
||||||
{
|
|
||||||
primitives->setElement(elementOffset++, indices[IndexPattern[j]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
geometry->setVertexArray(vertices);
|
|
||||||
geometry->setColorArray(colors, osg::Array::BIND_OVERALL);
|
|
||||||
geometry->addPrimitiveSet(primitives);
|
|
||||||
|
|
||||||
setupCommonMarkerState(geometry);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> group = new osg::Group();
|
|
||||||
group->addChild(geometry);
|
|
||||||
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry)
|
|
||||||
{
|
|
||||||
osg::ref_ptr<osg::StateSet> state = geometry->getOrCreateStateSet();
|
|
||||||
state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
|
||||||
state->setMode(GL_BLEND, osg::StateAttribute::ON);
|
|
||||||
|
|
||||||
state->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Vec3f CSVRender::Object::getMarkerPosition(float x, float y, float z, int axis)
|
|
||||||
{
|
|
||||||
switch (axis)
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
return osg::Vec3f(x, y, z);
|
|
||||||
case 0:
|
|
||||||
return osg::Vec3f(z, x, y);
|
|
||||||
case 1:
|
|
||||||
return osg::Vec3f(y, z, x);
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
throw std::logic_error("invalid axis for marker geometry");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CSVRender::Object::Object(
|
CSVRender::Object::Object(
|
||||||
CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero)
|
CSMWorld::Data& data, osg::Group* parentNode, const std::string& id, bool referenceable, bool forceBaseToZero)
|
||||||
: mData(data)
|
: mData(data)
|
||||||
|
|
@ -446,8 +189,6 @@ CSVRender::Object::Object(
|
||||||
, mForceBaseToZero(forceBaseToZero)
|
, mForceBaseToZero(forceBaseToZero)
|
||||||
, mScaleOverride(1)
|
, mScaleOverride(1)
|
||||||
, mOverrideFlags(0)
|
, mOverrideFlags(0)
|
||||||
, mSubMode(-1)
|
|
||||||
, mMarkerTransparency(0.5f)
|
|
||||||
{
|
{
|
||||||
mRootNode = new osg::PositionAttitudeTransform;
|
mRootNode = new osg::PositionAttitudeTransform;
|
||||||
|
|
||||||
|
|
@ -476,7 +217,6 @@ CSVRender::Object::Object(
|
||||||
|
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::Object::~Object()
|
CSVRender::Object::~Object()
|
||||||
|
|
@ -506,9 +246,6 @@ void CSVRender::Object::setSelected(bool selected, const osg::Vec4f& color)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mRootNode->addChild(mBaseNode);
|
mRootNode->addChild(mBaseNode);
|
||||||
|
|
||||||
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
|
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::Object::getSelected() const
|
bool CSVRender::Object::getSelected() const
|
||||||
|
|
@ -536,9 +273,6 @@ void CSVRender::Object::setSnapTarget(bool isSnapTarget)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mRootNode->addChild(mBaseNode);
|
mRootNode->addChild(mBaseNode);
|
||||||
|
|
||||||
mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
|
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSVRender::Object::getSnapTarget() const
|
bool CSVRender::Object::getSnapTarget() const
|
||||||
|
|
@ -566,7 +300,6 @@ bool CSVRender::Object::referenceableDataChanged(const QModelIndex& topLeft, con
|
||||||
{
|
{
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,7 +347,6 @@ bool CSVRender::Object::referenceDataChanged(const QModelIndex& topLeft, const Q
|
||||||
= ESM::RefId::stringRefId(references.getData(index, columnIndex).toString().toUtf8().constData());
|
= ESM::RefId::stringRefId(references.getData(index, columnIndex).toString().toUtf8().constData());
|
||||||
|
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -626,7 +358,6 @@ bool CSVRender::Object::referenceDataChanged(const QModelIndex& topLeft, const Q
|
||||||
void CSVRender::Object::reloadAssets()
|
void CSVRender::Object::reloadAssets()
|
||||||
{
|
{
|
||||||
update();
|
update();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CSVRender::Object::getReferenceId() const
|
std::string CSVRender::Object::getReferenceId() const
|
||||||
|
|
@ -720,12 +451,6 @@ void CSVRender::Object::setScale(float scale)
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Object::setMarkerTransparency(float value)
|
|
||||||
{
|
|
||||||
mMarkerTransparency = value;
|
|
||||||
updateMarker();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
||||||
{
|
{
|
||||||
const CSMWorld::RefCollection& collection = mData.getReferences();
|
const CSMWorld::RefCollection& collection = mData.getReferences();
|
||||||
|
|
@ -796,18 +521,8 @@ void CSVRender::Object::apply(CSMWorld::CommandMacro& commands)
|
||||||
mOverrideFlags = 0;
|
mOverrideFlags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::Object::setSubMode(int subMode)
|
|
||||||
{
|
|
||||||
if (subMode != mSubMode)
|
|
||||||
{
|
|
||||||
mSubMode = subMode;
|
|
||||||
updateMarker();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSVRender::Object::reset()
|
void CSVRender::Object::reset()
|
||||||
{
|
{
|
||||||
mOverrideFlags = 0;
|
mOverrideFlags = 0;
|
||||||
adjustTransform();
|
adjustTransform();
|
||||||
updateMarker();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,14 +58,6 @@ namespace CSVRender
|
||||||
QString getToolTip(bool hideBasics, const WorldspaceHitResult& hit) const override;
|
QString getToolTip(bool hideBasics, const WorldspaceHitResult& hit) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ObjectMarkerTag : public ObjectTag
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ObjectMarkerTag(Object* object, int axis);
|
|
||||||
|
|
||||||
int mAxis;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Object
|
class Object
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -76,12 +68,22 @@ namespace CSVRender
|
||||||
Override_Scale = 4
|
Override_Scale = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
enum SubMode
|
||||||
static const float MarkerShaftWidth;
|
{
|
||||||
static const float MarkerShaftBaseLength;
|
Mode_Move,
|
||||||
static const float MarkerHeadWidth;
|
Mode_Rotate,
|
||||||
static const float MarkerHeadLength;
|
Mode_Scale,
|
||||||
|
Mode_None,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Axis
|
||||||
|
{
|
||||||
|
Axis_X,
|
||||||
|
Axis_Y,
|
||||||
|
Axis_Z
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
CSMWorld::Data& mData;
|
CSMWorld::Data& mData;
|
||||||
ESM::RefId mReferenceId;
|
ESM::RefId mReferenceId;
|
||||||
ESM::RefId mReferenceableId;
|
ESM::RefId mReferenceableId;
|
||||||
|
|
@ -96,9 +98,6 @@ namespace CSVRender
|
||||||
ESM::Position mPositionOverride;
|
ESM::Position mPositionOverride;
|
||||||
float mScaleOverride;
|
float mScaleOverride;
|
||||||
int mOverrideFlags;
|
int mOverrideFlags;
|
||||||
osg::ref_ptr<osg::Node> mMarker[3];
|
|
||||||
int mSubMode;
|
|
||||||
float mMarkerTransparency;
|
|
||||||
std::unique_ptr<Actor> mActor;
|
std::unique_ptr<Actor> mActor;
|
||||||
|
|
||||||
/// Not implemented
|
/// Not implemented
|
||||||
|
|
@ -120,16 +119,6 @@ namespace CSVRender
|
||||||
/// Throws an exception if *this was constructed with referenceable
|
/// Throws an exception if *this was constructed with referenceable
|
||||||
const CSMWorld::CellRef& getReference() const;
|
const CSMWorld::CellRef& getReference() const;
|
||||||
|
|
||||||
void updateMarker();
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> makeMoveOrScaleMarker(int axis);
|
|
||||||
osg::ref_ptr<osg::Node> makeRotateMarker(int axis);
|
|
||||||
|
|
||||||
/// Sets up a stateset with properties common to all marker types.
|
|
||||||
void setupCommonMarkerState(osg::ref_ptr<osg::Geometry> geometry);
|
|
||||||
|
|
||||||
osg::Vec3f getMarkerPosition(float x, float y, float z, int axis);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Object(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, bool referenceable,
|
Object(CSMWorld::Data& data, osg::Group* cellNode, const std::string& id, bool referenceable,
|
||||||
bool forceBaseToZero = false);
|
bool forceBaseToZero = false);
|
||||||
|
|
@ -199,8 +188,6 @@ namespace CSVRender
|
||||||
/// Apply override changes via command and end edit mode
|
/// Apply override changes via command and end edit mode
|
||||||
void apply(CSMWorld::CommandMacro& commands);
|
void apply(CSMWorld::CommandMacro& commands);
|
||||||
|
|
||||||
void setSubMode(int subMode);
|
|
||||||
|
|
||||||
/// Erase all overrides and restore the visual representation of the object to its
|
/// Erase all overrides and restore the visual representation of the object to its
|
||||||
/// true state.
|
/// true state.
|
||||||
void reset();
|
void reset();
|
||||||
|
|
|
||||||
307
apps/opencs/view/render/objectmarker.cpp
Normal file
307
apps/opencs/view/render/objectmarker.cpp
Normal file
|
|
@ -0,0 +1,307 @@
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include <osg/ClipPlane>
|
||||||
|
#include <osg/Material>
|
||||||
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
#include <osgUtil/CullVisitor>
|
||||||
|
|
||||||
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
#include <components/sceneutil/visitor.hpp>
|
||||||
|
|
||||||
|
#include "../../model/prefs/state.hpp"
|
||||||
|
#include "objectmarker.hpp"
|
||||||
|
#include "worldspacewidget.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class FindMaterialVisitor : public osg::NodeVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FindMaterialVisitor(CSVRender::NodeMap& map)
|
||||||
|
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
|
||||||
|
, mMap(map)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::Geometry& node) override
|
||||||
|
{
|
||||||
|
osg::StateSet* state = node.getStateSet();
|
||||||
|
if (state->getAttribute(osg::StateAttribute::MATERIAL))
|
||||||
|
mMap.emplace(node.getName(), &node);
|
||||||
|
|
||||||
|
traverse(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CSVRender::NodeMap& mMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ToCamera : public SceneUtil::NodeCallback<ToCamera, osg::Node*, osgUtil::CullVisitor*>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ToCamera(osg::ref_ptr<osg::ClipPlane> clipPlane)
|
||||||
|
: mClipPlane(std::move(clipPlane))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
||||||
|
{
|
||||||
|
osg::Vec3f normal = cv->getEyePoint();
|
||||||
|
mClipPlane->setClipPlane(normal.x(), normal.y(), normal.z(), 0);
|
||||||
|
traverse(node, cv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
osg::ref_ptr<osg::ClipPlane> mClipPlane;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto addTagToActiveMarkerNodes = [](CSVRender::NodeMap& mMarkerNodes, CSVRender::Object* object,
|
||||||
|
std::initializer_list<std::string> suffixes) {
|
||||||
|
for (const auto& markerSuffix : suffixes)
|
||||||
|
{
|
||||||
|
for (char axis = 'X'; axis <= 'Z'; ++axis)
|
||||||
|
mMarkerNodes[axis + markerSuffix]->setUserData(new CSVRender::ObjectMarkerTag(object, axis - 'X'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVRender
|
||||||
|
{
|
||||||
|
ObjectMarkerTag::ObjectMarkerTag(Object* object, int axis)
|
||||||
|
: ObjectTag(object)
|
||||||
|
, mAxis(axis)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectMarker::ObjectMarker(WorldspaceWidget* worldspaceWidget, Resource::ResourceSystem* resourceSystem)
|
||||||
|
: mWorldspaceWidget(worldspaceWidget)
|
||||||
|
, mResourceSystem(resourceSystem)
|
||||||
|
, mMarkerScale(CSMPrefs::get()["Rendering"]["object-marker-scale"].toDouble())
|
||||||
|
, mSubMode(Object::Mode_None)
|
||||||
|
{
|
||||||
|
mBaseNode = new osg::PositionAttitudeTransform;
|
||||||
|
mBaseNode->setNodeMask(Mask_Reference);
|
||||||
|
mBaseNode->setScale(osg::Vec3f(mMarkerScale, mMarkerScale, mMarkerScale));
|
||||||
|
|
||||||
|
mRootNode = new osg::PositionAttitudeTransform;
|
||||||
|
mRootNode->addChild(mBaseNode);
|
||||||
|
worldspaceWidget->setSelectionMarkerRoot(mRootNode);
|
||||||
|
|
||||||
|
QFile file(":render/selection-marker");
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
throw std::runtime_error("Failed to open selection marker file");
|
||||||
|
|
||||||
|
auto markerData = file.readAll();
|
||||||
|
|
||||||
|
mResourceSystem->getSceneManager()->loadSelectionMarker(mBaseNode, markerData.data(), markerData.size());
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> baseNodeState = mBaseNode->getOrCreateStateSet();
|
||||||
|
baseNodeState->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
||||||
|
baseNodeState->setRenderBinDetails(1000, "RenderBin");
|
||||||
|
|
||||||
|
FindMaterialVisitor matMapper(mMarkerNodes);
|
||||||
|
|
||||||
|
mBaseNode->accept(matMapper);
|
||||||
|
|
||||||
|
for (const auto& [name, node] : mMarkerNodes)
|
||||||
|
{
|
||||||
|
osg::StateSet* state = node->getStateSet();
|
||||||
|
osg::Material* mat = static_cast<osg::Material*>(state->getAttribute(osg::StateAttribute::MATERIAL));
|
||||||
|
osg::Vec4f emis = mat->getEmission(osg::Material::FRONT_AND_BACK);
|
||||||
|
mat->setEmission(osg::Material::FRONT_AND_BACK, emis / 4);
|
||||||
|
mOriginalColors.emplace(name, emis);
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneUtil::NodeMap sceneNodes;
|
||||||
|
SceneUtil::NodeMapVisitor nodeMapper(sceneNodes);
|
||||||
|
mBaseNode->accept(nodeMapper);
|
||||||
|
|
||||||
|
mMarkerNodes.insert(sceneNodes.begin(), sceneNodes.end());
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> rotateMarkers = mMarkerNodes["rotateMarkers"];
|
||||||
|
osg::ClipPlane* clip = new osg::ClipPlane(0);
|
||||||
|
rotateMarkers->setCullCallback(new ToCamera(clip));
|
||||||
|
rotateMarkers->getStateSet()->setAttributeAndModes(clip, osg::StateAttribute::ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::toggleVisibility()
|
||||||
|
{
|
||||||
|
bool isVisible = mBaseNode->getNodeMask() == Mask_Reference;
|
||||||
|
mBaseNode->setNodeMask(isVisible ? Mask_Hidden : Mask_Reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::updateScale(const float scale)
|
||||||
|
{
|
||||||
|
mMarkerScale = scale;
|
||||||
|
mBaseNode->setScale(osg::Vec3f(scale, scale, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::setSubMode(const int subMode)
|
||||||
|
{
|
||||||
|
if (subMode == mSubMode)
|
||||||
|
return;
|
||||||
|
mSubMode = subMode;
|
||||||
|
resetMarkerHighlight();
|
||||||
|
updateSelectionMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectMarker::hitBehindMarker(const osg::Vec3d& hitPos, osg::ref_ptr<osg::Camera> camera)
|
||||||
|
{
|
||||||
|
if (mSubMode != Object::Mode_Rotate)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
osg::Vec3d center, eye, forwardVector, _;
|
||||||
|
std::vector<osg::Node*> rotMark = mMarkerNodes["rotateMarkers"]->getParentalNodePaths()[0];
|
||||||
|
const osg::Vec3f markerPos = osg::computeLocalToWorld(rotMark).getTrans();
|
||||||
|
|
||||||
|
camera->getViewMatrixAsLookAt(eye, center, _);
|
||||||
|
forwardVector = center - eye;
|
||||||
|
forwardVector.normalize();
|
||||||
|
|
||||||
|
return (hitPos - markerPos) * forwardVector > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectMarker::attachMarker(const std::string& refId)
|
||||||
|
{
|
||||||
|
const auto& object = mWorldspaceWidget->getObjectByReferenceId(refId);
|
||||||
|
|
||||||
|
if (!object)
|
||||||
|
removeFromSelectionHistory(refId);
|
||||||
|
|
||||||
|
if (!object || !object->getSelected())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!object->getRootNode()->addChild(mRootNode))
|
||||||
|
throw std::runtime_error("Failed to add marker to object");
|
||||||
|
|
||||||
|
std::string parentMarkerNode;
|
||||||
|
|
||||||
|
switch (mSubMode)
|
||||||
|
{
|
||||||
|
case (Object::Mode_Rotate):
|
||||||
|
parentMarkerNode = "rotateMarkers";
|
||||||
|
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis_Rot" });
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Scale):
|
||||||
|
parentMarkerNode = "scaleMarkers";
|
||||||
|
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis_Scale", "_Wall_Scale" });
|
||||||
|
break;
|
||||||
|
case (Object::Mode_Move):
|
||||||
|
default:
|
||||||
|
parentMarkerNode = "moveMarkers";
|
||||||
|
addTagToActiveMarkerNodes(mMarkerNodes, object, { "_Axis", "_Wall" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mMarkerNodes[parentMarkerNode]->asGroup()->setNodeMask(Mask_Reference);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::detachMarker()
|
||||||
|
{
|
||||||
|
for (std::size_t index = mRootNode->getNumParents(); index > 0;)
|
||||||
|
mRootNode->getParent(--index)->removeChild(mRootNode);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Group> widgetRoot = mMarkerNodes["unitArrows"]->asGroup();
|
||||||
|
for (std::size_t index = widgetRoot->getNumChildren(); index > 0;)
|
||||||
|
widgetRoot->getChild(--index)->setNodeMask(Mask_Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::addToSelectionHistory(const std::string& refId, bool update)
|
||||||
|
{
|
||||||
|
auto foundObject = std::find_if(mSelectionHistory.begin(), mSelectionHistory.end(),
|
||||||
|
[&refId](const std::string& objId) { return objId == refId; });
|
||||||
|
|
||||||
|
if (foundObject == mSelectionHistory.end())
|
||||||
|
mSelectionHistory.push_back(refId);
|
||||||
|
else
|
||||||
|
std::rotate(foundObject, foundObject + 1, mSelectionHistory.end());
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
updateSelectionMarker(refId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::removeFromSelectionHistory(const std::string& refId)
|
||||||
|
{
|
||||||
|
mSelectionHistory.erase(std::remove_if(mSelectionHistory.begin(), mSelectionHistory.end(),
|
||||||
|
[&refId](const std::string& objId) { return objId == refId; }),
|
||||||
|
mSelectionHistory.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::updateSelectionMarker(const std::string& refId)
|
||||||
|
{
|
||||||
|
if (mSelectionHistory.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
detachMarker();
|
||||||
|
|
||||||
|
if (refId.empty())
|
||||||
|
{
|
||||||
|
for (std::size_t index = mSelectionHistory.size(); index > 0;)
|
||||||
|
if (attachMarker(mSelectionHistory[--index]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
attachMarker(refId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::resetMarkerHighlight()
|
||||||
|
{
|
||||||
|
if (mLastHighlightedNodes.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const auto& [nodeName, mat] : mLastHighlightedNodes)
|
||||||
|
mat->setEmission(osg::Material::FRONT_AND_BACK, mat->getEmission(osg::Material::FRONT_AND_BACK) / 4);
|
||||||
|
|
||||||
|
mLastHighlightedNodes.clear();
|
||||||
|
mLastHitNode.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMarker::updateMarkerHighlight(const std::string_view hitNode, const int axis)
|
||||||
|
{
|
||||||
|
if (hitNode == mLastHitNode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
resetMarkerHighlight();
|
||||||
|
|
||||||
|
std::string colorName;
|
||||||
|
|
||||||
|
switch (axis)
|
||||||
|
{
|
||||||
|
case Object::Axis_X:
|
||||||
|
colorName = "red";
|
||||||
|
break;
|
||||||
|
case Object::Axis_Y:
|
||||||
|
colorName = "green";
|
||||||
|
break;
|
||||||
|
case Object::Axis_Z:
|
||||||
|
colorName = "blue";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Invalid axis for highlighting: " + std::to_string(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> targetMaterials = { colorName + "-material" };
|
||||||
|
|
||||||
|
if (mSubMode != Object::Mode_Rotate)
|
||||||
|
targetMaterials.emplace_back(colorName + "_alpha-material");
|
||||||
|
|
||||||
|
for (const auto& materialNodeName : targetMaterials)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Node> matNode = mMarkerNodes[materialNodeName];
|
||||||
|
osg::StateSet* state = matNode->getStateSet();
|
||||||
|
osg::StateAttribute* matAttr = state->getAttribute(osg::StateAttribute::MATERIAL);
|
||||||
|
|
||||||
|
osg::Material* mat = static_cast<osg::Material*>(matAttr);
|
||||||
|
mat->setEmission(osg::Material::FRONT_AND_BACK, mOriginalColors[materialNodeName]);
|
||||||
|
|
||||||
|
mLastHighlightedNodes.emplace(std::make_pair(matNode->getName(), mat));
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastHitNode = hitNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
apps/opencs/view/render/objectmarker.hpp
Normal file
77
apps/opencs/view/render/objectmarker.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef OPENCS_VIEW_OBJECT_MARKER_H
|
||||||
|
#define OPENCS_VIEW_OBJECT_MARKER_H
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
namespace osg
|
||||||
|
{
|
||||||
|
class Camera;
|
||||||
|
class Material;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVRender
|
||||||
|
{
|
||||||
|
using NodeMap = std::unordered_map<std::string, osg::ref_ptr<osg::Node>>;
|
||||||
|
class WorldspaceWidget;
|
||||||
|
|
||||||
|
class ObjectMarkerTag : public ObjectTag
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ObjectMarkerTag(Object* object, int axis);
|
||||||
|
|
||||||
|
int mAxis;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ObjectMarker
|
||||||
|
{
|
||||||
|
friend class WorldspaceWidget;
|
||||||
|
|
||||||
|
WorldspaceWidget* mWorldspaceWidget;
|
||||||
|
Resource::ResourceSystem* mResourceSystem;
|
||||||
|
NodeMap mMarkerNodes;
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> mRootNode;
|
||||||
|
std::unordered_map<std::string, osg::Vec4f> mOriginalColors;
|
||||||
|
std::vector<std::string> mSelectionHistory;
|
||||||
|
std::string mLastHitNode;
|
||||||
|
std::unordered_map<std::string, osg::Material*> mLastHighlightedNodes;
|
||||||
|
float mMarkerScale;
|
||||||
|
int mSubMode;
|
||||||
|
|
||||||
|
ObjectMarker(WorldspaceWidget* worldspaceWidget, Resource::ResourceSystem* resourceSystem);
|
||||||
|
|
||||||
|
static std::unique_ptr<ObjectMarker> create(WorldspaceWidget* widget, Resource::ResourceSystem* resourceSystem)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<ObjectMarker>(new ObjectMarker(widget, resourceSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool attachMarker(const std::string& refId);
|
||||||
|
|
||||||
|
void removeFromSelectionHistory(const std::string& refId);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ObjectMarker(ObjectMarker&) = delete;
|
||||||
|
ObjectMarker(ObjectMarker&&) = delete;
|
||||||
|
ObjectMarker& operator=(const ObjectMarker&) = delete;
|
||||||
|
ObjectMarker& operator=(ObjectMarker&&) = delete;
|
||||||
|
|
||||||
|
void toggleVisibility();
|
||||||
|
|
||||||
|
bool hitBehindMarker(const osg::Vec3d& hitPos, osg::ref_ptr<osg::Camera> camera);
|
||||||
|
|
||||||
|
void detachMarker();
|
||||||
|
|
||||||
|
void addToSelectionHistory(const std::string& refId, bool update = true);
|
||||||
|
|
||||||
|
void updateSelectionMarker(const std::string& refId = std::string());
|
||||||
|
|
||||||
|
void resetMarkerHighlight();
|
||||||
|
|
||||||
|
void updateMarkerHighlight(const std::string_view hitNode, const int axis);
|
||||||
|
|
||||||
|
void setSubMode(const int subMode);
|
||||||
|
|
||||||
|
void updateScale(const float scale);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // OPENCS_VIEW_OBJECT_MARKER_H
|
||||||
|
|
@ -86,8 +86,8 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
|
||||||
{
|
{
|
||||||
modified = true;
|
modified = true;
|
||||||
|
|
||||||
auto cell
|
auto cell = std::make_unique<Cell>(getDocument(), mSelectionMarker.get(), mRootNode,
|
||||||
= std::make_unique<Cell>(mDocument, mRootNode, iter->first.getId(mWorldspace), deleted, true);
|
iter->first.getId(mWorldspace), deleted, true);
|
||||||
|
|
||||||
delete iter->second;
|
delete iter->second;
|
||||||
iter->second = cell.release();
|
iter->second = cell.release();
|
||||||
|
|
@ -465,7 +465,8 @@ void CSVRender::PagedWorldspaceWidget::addCellToScene(const CSMWorld::CellCoordi
|
||||||
|
|
||||||
bool deleted = index == -1 || cells.getRecord(index).mState == CSMWorld::RecordBase::State_Deleted;
|
bool deleted = index == -1 || cells.getRecord(index).mState == CSMWorld::RecordBase::State_Deleted;
|
||||||
|
|
||||||
auto cell = std::make_unique<Cell>(mDocument, mRootNode, coordinates.getId(mWorldspace), deleted, true);
|
auto cell = std::make_unique<Cell>(
|
||||||
|
getDocument(), mSelectionMarker.get(), mRootNode, coordinates.getId(mWorldspace), deleted, true);
|
||||||
EditMode* editMode = getEditMode();
|
EditMode* editMode = getEditMode();
|
||||||
cell->setSubMode(editMode->getSubMode(), editMode->getInteractionMask());
|
cell->setSubMode(editMode->getSubMode(), editMode->getInteractionMask());
|
||||||
|
|
||||||
|
|
@ -750,6 +751,7 @@ void CSVRender::PagedWorldspaceWidget::clearSelection(int elementMask)
|
||||||
iter->second->setSelection(elementMask, Cell::Selection_Clear);
|
iter->second->setSelection(elementMask, Cell::Selection_Clear);
|
||||||
|
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
|
mSelectionMarker->detachMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::PagedWorldspaceWidget::invertSelection(int elementMask)
|
void CSVRender::PagedWorldspaceWidget::invertSelection(int elementMask)
|
||||||
|
|
@ -907,6 +909,7 @@ void CSVRender::PagedWorldspaceWidget::setSubMode(int subMode, unsigned int elem
|
||||||
{
|
{
|
||||||
for (std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator iter = mCells.begin(); iter != mCells.end(); ++iter)
|
for (std::map<CSMWorld::CellCoordinates, Cell*>::const_iterator iter = mCells.begin(); iter != mCells.end(); ++iter)
|
||||||
iter->second->setSubMode(subMode, elementMask);
|
iter->second->setSubMode(subMode, elementMask);
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::PagedWorldspaceWidget::reset(unsigned int elementMask)
|
void CSVRender::PagedWorldspaceWidget::reset(unsigned int elementMask)
|
||||||
|
|
@ -986,3 +989,12 @@ void CSVRender::PagedWorldspaceWidget::loadSouthCell()
|
||||||
{
|
{
|
||||||
addCellToSceneFromCamera(0, -1);
|
addCellToSceneFromCamera(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSVRender::Object* CSVRender::PagedWorldspaceWidget::getObjectByReferenceId(const std::string& referenceId)
|
||||||
|
{
|
||||||
|
for (const auto& [_, cell] : mCells)
|
||||||
|
if (const auto& object = cell->getObjectByReferenceId(referenceId))
|
||||||
|
return object;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,8 @@ namespace CSVRender
|
||||||
/// Erase all overrides and restore the visual representation to its true state.
|
/// Erase all overrides and restore the visual representation to its true state.
|
||||||
void reset(unsigned int elementMask) override;
|
void reset(unsigned int elementMask) override;
|
||||||
|
|
||||||
|
CSVRender::Object* getObjectByReferenceId(const std::string& referenceId) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void addVisibilitySelectorButtons(CSVWidget::SceneToolToggle2* tool) override;
|
void addVisibilitySelectorButtons(CSVWidget::SceneToolToggle2* tool) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -161,8 +161,6 @@ namespace CSVRender
|
||||||
, mLighting(nullptr)
|
, mLighting(nullptr)
|
||||||
, mHasDefaultAmbient(false)
|
, mHasDefaultAmbient(false)
|
||||||
, mIsExterior(true)
|
, mIsExterior(true)
|
||||||
, mPrevMouseX(0)
|
|
||||||
, mPrevMouseY(0)
|
|
||||||
, mCamPositionSet(false)
|
, mCamPositionSet(false)
|
||||||
{
|
{
|
||||||
mFreeCamControl = new FreeCameraController(this);
|
mFreeCamControl = new FreeCameraController(this);
|
||||||
|
|
@ -423,10 +421,10 @@ namespace CSVRender
|
||||||
|
|
||||||
void SceneWidget::mouseMoveEvent(QMouseEvent* event)
|
void SceneWidget::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
mCurrentCamControl->handleMouseMoveEvent(event->x() - mPrevMouseX, event->y() - mPrevMouseY);
|
QPointF pos = event->position();
|
||||||
|
mCurrentCamControl->handleMouseMoveEvent(pos.x() - mPrevMouse.x(), pos.y() - mPrevMouse.y());
|
||||||
|
|
||||||
mPrevMouseX = event->x();
|
mPrevMouse = pos;
|
||||||
mPrevMouseY = event->y();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneWidget::wheelEvent(QWheelEvent* event)
|
void SceneWidget::wheelEvent(QWheelEvent* event)
|
||||||
|
|
@ -445,6 +443,32 @@ namespace CSVRender
|
||||||
mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);
|
mCurrentCamControl->setup(mRootNode, Mask_Reference | Mask_Terrain, CameraController::WorldUp);
|
||||||
mCamPositionSet = true;
|
mCamPositionSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mSelectionMarkerNode)
|
||||||
|
{
|
||||||
|
osg::MatrixList worldMats = mSelectionMarkerNode->getWorldMatrices();
|
||||||
|
if (!worldMats.empty())
|
||||||
|
{
|
||||||
|
osg::Matrixd markerWorldMat = worldMats[0];
|
||||||
|
|
||||||
|
osg::Vec3f eye, _;
|
||||||
|
mView->getCamera()->getViewMatrix().getLookAt(eye, _, _);
|
||||||
|
osg::Vec3f cameraLocalPos = eye * osg::Matrixd::inverse(markerWorldMat);
|
||||||
|
|
||||||
|
bool isInFrontRightQuadrant = (cameraLocalPos.x() > 0.1f) && (cameraLocalPos.y() > 0.1f);
|
||||||
|
bool isSignificantlyBehind = (cameraLocalPos.x() < 1.f) && (cameraLocalPos.y() < 1.f);
|
||||||
|
|
||||||
|
if (!isInFrontRightQuadrant && isSignificantlyBehind)
|
||||||
|
{
|
||||||
|
osg::Quat current = mSelectionMarkerNode->getAttitude();
|
||||||
|
mSelectionMarkerNode->setAttitude(current * osg::Quat(osg::PI, osg::Vec3f(0, 0, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance = (markerWorldMat.getTrans() - eye).length();
|
||||||
|
float scale = std::max(distance / 75.0f, 1.0f);
|
||||||
|
mSelectionMarkerNode->setScale(osg::Vec3(scale, scale, scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneWidget::settingChanged(const CSMPrefs::Setting* setting)
|
void SceneWidget::settingChanged(const CSMPrefs::Setting* setting)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/Vec4f>
|
#include <osg/Vec4f>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
|
@ -105,6 +106,11 @@ namespace CSVRender
|
||||||
|
|
||||||
void setExterior(bool isExterior);
|
void setExterior(bool isExterior);
|
||||||
|
|
||||||
|
void setSelectionMarkerRoot(osg::ref_ptr<osg::PositionAttitudeTransform> selectionMarker)
|
||||||
|
{
|
||||||
|
mSelectionMarkerNode = selectionMarker;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setLighting(Lighting* lighting);
|
void setLighting(Lighting* lighting);
|
||||||
///< \attention The ownership of \a lighting is not transferred to *this.
|
///< \attention The ownership of \a lighting is not transferred to *this.
|
||||||
|
|
@ -122,6 +128,7 @@ namespace CSVRender
|
||||||
|
|
||||||
Lighting* mLighting;
|
Lighting* mLighting;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::PositionAttitudeTransform> mSelectionMarkerNode;
|
||||||
osg::ref_ptr<osg::Camera> mGradientCamera;
|
osg::ref_ptr<osg::Camera> mGradientCamera;
|
||||||
osg::Vec4f mDefaultAmbient;
|
osg::Vec4f mDefaultAmbient;
|
||||||
bool mHasDefaultAmbient;
|
bool mHasDefaultAmbient;
|
||||||
|
|
@ -130,7 +137,7 @@ namespace CSVRender
|
||||||
LightingNight mLightingNight;
|
LightingNight mLightingNight;
|
||||||
LightingBright mLightingBright;
|
LightingBright mLightingBright;
|
||||||
|
|
||||||
int mPrevMouseX, mPrevMouseY;
|
QPointF mPrevMouse;
|
||||||
|
|
||||||
/// Tells update that camera isn't set
|
/// Tells update that camera isn't set
|
||||||
bool mCamPositionSet;
|
bool mCamPositionSet;
|
||||||
|
|
|
||||||
|
|
@ -1661,7 +1661,7 @@ void CSVRender::TerrainShapeMode::dragMoveEvent(QDragMoveEvent* event) {}
|
||||||
|
|
||||||
void CSVRender::TerrainShapeMode::mouseMoveEvent(QMouseEvent* event)
|
void CSVRender::TerrainShapeMode::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask());
|
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->position().toPoint(), getInteractionMask());
|
||||||
if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing))
|
if (hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing))
|
||||||
mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape);
|
mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape);
|
||||||
if (!hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing))
|
if (!hit.hit && mBrushDraw && !(mShapeEditTool == ShapeEditTool_Drag && mIsEditing))
|
||||||
|
|
|
||||||
|
|
@ -724,7 +724,7 @@ void CSVRender::TerrainTextureMode::dragMoveEvent(QDragMoveEvent* event) {}
|
||||||
|
|
||||||
void CSVRender::TerrainTextureMode::mouseMoveEvent(QMouseEvent* event)
|
void CSVRender::TerrainTextureMode::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->pos(), getInteractionMask());
|
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(event->position().toPoint(), getInteractionMask());
|
||||||
if (hit.hit && mBrushDraw)
|
if (hit.hit && mBrushDraw)
|
||||||
mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape);
|
mBrushDraw->update(hit.worldPos, mBrushSize, mBrushShape);
|
||||||
if (!hit.hit && mBrushDraw)
|
if (!hit.hit && mBrushDraw)
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget(
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
mCell = std::make_unique<Cell>(document, mRootNode, mCellId);
|
mCell = std::make_unique<Cell>(document, mSelectionMarker.get(), mRootNode, mCellId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::UnpagedWorldspaceWidget::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
void CSVRender::UnpagedWorldspaceWidget::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||||
|
|
@ -127,7 +127,7 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop(
|
||||||
|
|
||||||
mCellId = universalIdData.begin()->getId();
|
mCellId = universalIdData.begin()->getId();
|
||||||
|
|
||||||
mCell = std::make_unique<Cell>(getDocument(), mRootNode, mCellId);
|
mCell = std::make_unique<Cell>(getDocument(), mSelectionMarker.get(), mRootNode, mCellId);
|
||||||
mCamPositionSet = false;
|
mCamPositionSet = false;
|
||||||
mOrbitCamControl->reset();
|
mOrbitCamControl->reset();
|
||||||
|
|
||||||
|
|
@ -141,6 +141,7 @@ void CSVRender::UnpagedWorldspaceWidget::clearSelection(int elementMask)
|
||||||
{
|
{
|
||||||
mCell->setSelection(elementMask, Cell::Selection_Clear);
|
mCell->setSelection(elementMask, Cell::Selection_Clear);
|
||||||
flagAsModified();
|
flagAsModified();
|
||||||
|
mSelectionMarker->detachMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::UnpagedWorldspaceWidget::invertSelection(int elementMask)
|
void CSVRender::UnpagedWorldspaceWidget::invertSelection(int elementMask)
|
||||||
|
|
@ -218,6 +219,7 @@ std::vector<osg::ref_ptr<CSVRender::TagBase>> CSVRender::UnpagedWorldspaceWidget
|
||||||
void CSVRender::UnpagedWorldspaceWidget::setSubMode(int subMode, unsigned int elementMask)
|
void CSVRender::UnpagedWorldspaceWidget::setSubMode(int subMode, unsigned int elementMask)
|
||||||
{
|
{
|
||||||
mCell->setSubMode(subMode, elementMask);
|
mCell->setSubMode(subMode, elementMask);
|
||||||
|
mSelectionMarker->updateSelectionMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::UnpagedWorldspaceWidget::reset(unsigned int elementMask)
|
void CSVRender::UnpagedWorldspaceWidget::reset(unsigned int elementMask)
|
||||||
|
|
@ -383,3 +385,8 @@ CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget:
|
||||||
return ignored;
|
return ignored;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSVRender::Object* CSVRender::UnpagedWorldspaceWidget::getObjectByReferenceId(const std::string& referenceId)
|
||||||
|
{
|
||||||
|
return mCell->getObjectByReferenceId(referenceId);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,8 @@ namespace CSVRender
|
||||||
/// Erase all overrides and restore the visual representation to its true state.
|
/// Erase all overrides and restore the visual representation to its true state.
|
||||||
void reset(unsigned int elementMask) override;
|
void reset(unsigned int elementMask) override;
|
||||||
|
|
||||||
|
CSVRender::Object* getObjectByReferenceId(const std::string& id) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void referenceableDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) override;
|
void referenceableDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@
|
||||||
#include "cameracontroller.hpp"
|
#include "cameracontroller.hpp"
|
||||||
#include "instancemode.hpp"
|
#include "instancemode.hpp"
|
||||||
#include "mask.hpp"
|
#include "mask.hpp"
|
||||||
#include "object.hpp"
|
|
||||||
#include "pathgridmode.hpp"
|
#include "pathgridmode.hpp"
|
||||||
|
|
||||||
CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidget* parent)
|
CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidget* parent)
|
||||||
|
|
@ -74,8 +73,8 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
|
||||||
, mToolTipPos(-1, -1)
|
, mToolTipPos(-1, -1)
|
||||||
, mShowToolTips(false)
|
, mShowToolTips(false)
|
||||||
, mToolTipDelay(0)
|
, mToolTipDelay(0)
|
||||||
, mInConstructor(true)
|
|
||||||
, mSelectedNavigationMode(0)
|
, mSelectedNavigationMode(0)
|
||||||
|
, mSelectionMarker(ObjectMarker::create(this, document.getData().getResourceSystem().get()))
|
||||||
{
|
{
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
|
|
||||||
|
|
@ -145,13 +144,14 @@ CSVRender::WorldspaceWidget::WorldspaceWidget(CSMDoc::Document& document, QWidge
|
||||||
&WorldspaceWidget::unhideAll);
|
&WorldspaceWidget::unhideAll);
|
||||||
|
|
||||||
connect(new CSMPrefs::Shortcut("scene-clear-selection", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
connect(new CSMPrefs::Shortcut("scene-clear-selection", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||||
[this] { this->clearSelection(Mask_Reference); });
|
[this] { clearSelection(Mask_Reference); });
|
||||||
|
|
||||||
CSMPrefs::Shortcut* switchPerspectiveShortcut = new CSMPrefs::Shortcut("scene-cam-cycle", this);
|
CSMPrefs::Shortcut* switchPerspectiveShortcut = new CSMPrefs::Shortcut("scene-cam-cycle", this);
|
||||||
connect(switchPerspectiveShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
connect(switchPerspectiveShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||||
&WorldspaceWidget::cycleNavigationMode);
|
&WorldspaceWidget::cycleNavigationMode);
|
||||||
|
|
||||||
mInConstructor = false;
|
connect(new CSMPrefs::Shortcut("scene-toggle-marker", this), qOverload<>(&CSMPrefs::Shortcut::activated), this,
|
||||||
|
[this]() { mSelectionMarker->toggleVisibility(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* setting)
|
void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* setting)
|
||||||
|
|
@ -162,17 +162,8 @@ void CSVRender::WorldspaceWidget::settingChanged(const CSMPrefs::Setting* settin
|
||||||
mDragWheelFactor = setting->toDouble();
|
mDragWheelFactor = setting->toDouble();
|
||||||
else if (*setting == "3D Scene Input/drag-shift-factor")
|
else if (*setting == "3D Scene Input/drag-shift-factor")
|
||||||
mDragShiftFactor = setting->toDouble();
|
mDragShiftFactor = setting->toDouble();
|
||||||
else if (*setting == "Rendering/object-marker-alpha" && !mInConstructor)
|
else if (*setting == "Rendering/object-marker-scale")
|
||||||
{
|
mSelectionMarker->updateScale(setting->toDouble());
|
||||||
float alpha = setting->toDouble();
|
|
||||||
// getSelection is virtual, thus this can not be called from the constructor
|
|
||||||
auto selection = getSelection(Mask_Reference);
|
|
||||||
for (osg::ref_ptr<TagBase> tag : selection)
|
|
||||||
{
|
|
||||||
if (auto objTag = dynamic_cast<ObjectTag*>(tag.get()))
|
|
||||||
objTag->mObject->setMarkerTransparency(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (*setting == "Tooltips/scene-delay")
|
else if (*setting == "Tooltips/scene-delay")
|
||||||
mToolTipDelay = setting->toInt();
|
mToolTipDelay = setting->toInt();
|
||||||
else if (*setting == "Tooltips/scene")
|
else if (*setting == "Tooltips/scene")
|
||||||
|
|
@ -396,8 +387,29 @@ CSMDoc::Document& CSVRender::WorldspaceWidget::getDocument()
|
||||||
return mDocument;
|
return mDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
template <typename Tag>
|
||||||
const QPoint& localPos, unsigned int interactionMask) const
|
std::optional<CSVRender::WorldspaceHitResult> CSVRender::WorldspaceWidget::checkTag(
|
||||||
|
const osgUtil::LineSegmentIntersector::Intersection& intersection) const
|
||||||
|
{
|
||||||
|
for (auto* node : intersection.nodePath)
|
||||||
|
{
|
||||||
|
if (auto* tag = dynamic_cast<Tag*>(node->getUserData()))
|
||||||
|
{
|
||||||
|
WorldspaceHitResult hit = { true, tag, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
||||||
|
if (intersection.indexList.size() >= 3)
|
||||||
|
{
|
||||||
|
hit.index0 = intersection.indexList[0];
|
||||||
|
hit.index1 = intersection.indexList[1];
|
||||||
|
hit.index2 = intersection.indexList[2];
|
||||||
|
}
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<osg::Vec3d, osg::Vec3d, osg::Vec3d> CSVRender::WorldspaceWidget::getStartEndDirection(
|
||||||
|
int pointX, int pointY) const
|
||||||
{
|
{
|
||||||
// may be okay to just use devicePixelRatio() directly
|
// may be okay to just use devicePixelRatio() directly
|
||||||
QScreen* screen = SceneWidget::windowHandle() && SceneWidget::windowHandle()->screen()
|
QScreen* screen = SceneWidget::windowHandle() && SceneWidget::windowHandle()->screen()
|
||||||
|
|
@ -405,8 +417,8 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
: QGuiApplication::primaryScreen();
|
: QGuiApplication::primaryScreen();
|
||||||
|
|
||||||
// (0,0) is considered the lower left corner of an OpenGL window
|
// (0,0) is considered the lower left corner of an OpenGL window
|
||||||
int x = localPos.x() * screen->devicePixelRatio();
|
int x = pointX * screen->devicePixelRatio();
|
||||||
int y = height() * screen->devicePixelRatio() - localPos.y() * screen->devicePixelRatio();
|
int y = height() * screen->devicePixelRatio() - pointY * screen->devicePixelRatio();
|
||||||
|
|
||||||
// Convert from screen space to world space
|
// Convert from screen space to world space
|
||||||
osg::Matrixd wpvMat;
|
osg::Matrixd wpvMat;
|
||||||
|
|
@ -418,6 +430,13 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
osg::Vec3d start = wpvMat.preMult(osg::Vec3d(x, y, 0));
|
osg::Vec3d start = wpvMat.preMult(osg::Vec3d(x, y, 0));
|
||||||
osg::Vec3d end = wpvMat.preMult(osg::Vec3d(x, y, 1));
|
osg::Vec3d end = wpvMat.preMult(osg::Vec3d(x, y, 1));
|
||||||
osg::Vec3d direction = end - start;
|
osg::Vec3d direction = end - start;
|
||||||
|
return { start, end, direction };
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
|
const QPoint& localPos, unsigned int interactionMask) const
|
||||||
|
{
|
||||||
|
auto [start, end, direction] = getStartEndDirection(localPos.x(), localPos.y());
|
||||||
|
|
||||||
// Get intersection
|
// Get intersection
|
||||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
||||||
|
|
@ -430,51 +449,46 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick(
|
||||||
|
|
||||||
mView->getCamera()->accept(visitor);
|
mView->getCamera()->accept(visitor);
|
||||||
|
|
||||||
// Get relevant data
|
auto intersections = intersector->getIntersections();
|
||||||
for (osgUtil::LineSegmentIntersector::Intersections::iterator it = intersector->getIntersections().begin();
|
|
||||||
it != intersector->getIntersections().end(); ++it)
|
|
||||||
{
|
|
||||||
osgUtil::LineSegmentIntersector::Intersection intersection = *it;
|
|
||||||
|
|
||||||
// reject back-facing polygons
|
std::vector<osgUtil::LineSegmentIntersector::Intersection> validIntersections
|
||||||
if (direction * intersection.getWorldIntersectNormal() > 0)
|
= { intersections.begin(), intersections.end() };
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<osg::Node*>::iterator nodeIter = intersection.nodePath.begin();
|
const auto& removeBackfaces = [direction = direction](const osgUtil::LineSegmentIntersector::Intersection& i) {
|
||||||
nodeIter != intersection.nodePath.end(); ++nodeIter)
|
return direction * i.getWorldIntersectNormal() > 0;
|
||||||
{
|
};
|
||||||
osg::Node* node = *nodeIter;
|
|
||||||
if (osg::ref_ptr<CSVRender::TagBase> tag = dynamic_cast<CSVRender::TagBase*>(node->getUserData()))
|
|
||||||
{
|
|
||||||
WorldspaceHitResult hit = { true, std::move(tag), 0, 0, 0, intersection.getWorldIntersectPoint() };
|
|
||||||
if (intersection.indexList.size() >= 3)
|
|
||||||
{
|
|
||||||
hit.index0 = intersection.indexList[0];
|
|
||||||
hit.index1 = intersection.indexList[1];
|
|
||||||
hit.index2 = intersection.indexList[2];
|
|
||||||
}
|
|
||||||
return hit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Something untagged, probably terrain
|
validIntersections.erase(std::remove_if(validIntersections.begin(), validIntersections.end(), removeBackfaces),
|
||||||
WorldspaceHitResult hit = { true, nullptr, 0, 0, 0, intersection.getWorldIntersectPoint() };
|
validIntersections.end());
|
||||||
if (intersection.indexList.size() >= 3)
|
|
||||||
{
|
|
||||||
hit.index0 = intersection.indexList[0];
|
|
||||||
hit.index1 = intersection.indexList[1];
|
|
||||||
hit.index2 = intersection.indexList[2];
|
|
||||||
}
|
|
||||||
return hit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default placement
|
// Default placement
|
||||||
direction.normalize();
|
direction.normalize();
|
||||||
direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt();
|
direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt();
|
||||||
|
|
||||||
WorldspaceHitResult hit = { false, nullptr, 0, 0, 0, start + direction };
|
if (validIntersections.empty())
|
||||||
|
return WorldspaceHitResult{ false, nullptr, 0, 0, 0, start + direction };
|
||||||
|
|
||||||
|
const auto& firstHit = validIntersections.front();
|
||||||
|
|
||||||
|
for (const auto& hit : validIntersections)
|
||||||
|
if (const auto& markerHit = checkTag<ObjectMarkerTag>(hit))
|
||||||
|
{
|
||||||
|
if (mSelectionMarker->hitBehindMarker(markerHit->worldPos, mView->getCamera()))
|
||||||
|
return WorldspaceHitResult{ false, nullptr, 0, 0, 0, start + direction };
|
||||||
|
else
|
||||||
|
return *markerHit;
|
||||||
|
}
|
||||||
|
if (auto hit = checkTag<TagBase>(firstHit))
|
||||||
|
return *hit;
|
||||||
|
|
||||||
|
// Something untagged, probably terrain
|
||||||
|
WorldspaceHitResult hit = { true, nullptr, 0, 0, 0, firstHit.getWorldIntersectPoint() };
|
||||||
|
if (firstHit.indexList.size() >= 3)
|
||||||
|
{
|
||||||
|
hit.index0 = firstHit.indexList[0];
|
||||||
|
hit.index1 = firstHit.indexList[1];
|
||||||
|
hit.index2 = firstHit.indexList[2];
|
||||||
|
}
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -632,17 +646,53 @@ void CSVRender::WorldspaceWidget::elementSelectionChanged()
|
||||||
|
|
||||||
void CSVRender::WorldspaceWidget::updateOverlay() {}
|
void CSVRender::WorldspaceWidget::updateOverlay() {}
|
||||||
|
|
||||||
|
void CSVRender::WorldspaceWidget::handleMarkerHighlight(const int x, const int y)
|
||||||
|
{
|
||||||
|
auto [start, end, _] = getStartEndDirection(x, y);
|
||||||
|
|
||||||
|
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector(
|
||||||
|
new osgUtil::LineSegmentIntersector(osgUtil::Intersector::MODEL, start, end));
|
||||||
|
|
||||||
|
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::NO_LIMIT);
|
||||||
|
osgUtil::IntersectionVisitor visitor(intersector);
|
||||||
|
|
||||||
|
visitor.setTraversalMask(Mask_Reference);
|
||||||
|
|
||||||
|
mView->getCamera()->accept(visitor);
|
||||||
|
|
||||||
|
bool hitMarker = false;
|
||||||
|
for (const auto& intersection : intersector->getIntersections())
|
||||||
|
{
|
||||||
|
if (mSelectionMarker->hitBehindMarker(intersection.getWorldIntersectPoint(), mView->getCamera()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (const auto& node : intersection.nodePath)
|
||||||
|
{
|
||||||
|
if (const auto& marker = dynamic_cast<ObjectMarkerTag*>(node->getUserData()))
|
||||||
|
{
|
||||||
|
hitMarker = true;
|
||||||
|
mSelectionMarker->updateMarkerHighlight(node->getName(), marker->mAxis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hitMarker)
|
||||||
|
mSelectionMarker->resetMarkerHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent()).mouseMoveEvent(event);
|
dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent()).mouseMoveEvent(event);
|
||||||
|
|
||||||
if (mDragging)
|
if (mDragging)
|
||||||
{
|
{
|
||||||
int diffX = event->x() - mDragX;
|
QPoint pos = event->position().toPoint();
|
||||||
int diffY = (height() - event->y()) - mDragY;
|
int diffX = pos.x() - mDragX;
|
||||||
|
int diffY = (height() - pos.y()) - mDragY;
|
||||||
|
|
||||||
mDragX = event->x();
|
mDragX = pos.x();
|
||||||
mDragY = height() - event->y();
|
mDragY = height() - pos.y();
|
||||||
|
|
||||||
double factor = mDragFactor;
|
double factor = mDragFactor;
|
||||||
|
|
||||||
|
|
@ -651,32 +701,32 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
||||||
|
|
||||||
EditMode& editMode = dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent());
|
EditMode& editMode = dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent());
|
||||||
|
|
||||||
editMode.drag(event->pos(), diffX, diffY, factor);
|
editMode.drag(event->position().toPoint(), diffX, diffY, factor);
|
||||||
}
|
}
|
||||||
else if (mDragMode != InteractionType_None)
|
else if (mDragMode != InteractionType_None)
|
||||||
{
|
{
|
||||||
EditMode& editMode = dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent());
|
EditMode& editMode = dynamic_cast<CSVRender::EditMode&>(*mEditMode->getCurrent());
|
||||||
|
|
||||||
if (mDragMode == InteractionType_PrimaryEdit)
|
if (mDragMode == InteractionType_PrimaryEdit)
|
||||||
mDragging = editMode.primaryEditStartDrag(event->pos());
|
mDragging = editMode.primaryEditStartDrag(event->position().toPoint());
|
||||||
else if (mDragMode == InteractionType_SecondaryEdit)
|
else if (mDragMode == InteractionType_SecondaryEdit)
|
||||||
mDragging = editMode.secondaryEditStartDrag(event->pos());
|
mDragging = editMode.secondaryEditStartDrag(event->position().toPoint());
|
||||||
else if (mDragMode == InteractionType_PrimarySelect)
|
else if (mDragMode == InteractionType_PrimarySelect)
|
||||||
mDragging = editMode.primarySelectStartDrag(event->pos());
|
mDragging = editMode.primarySelectStartDrag(event->position().toPoint());
|
||||||
else if (mDragMode == InteractionType_SecondarySelect)
|
else if (mDragMode == InteractionType_SecondarySelect)
|
||||||
mDragging = editMode.secondarySelectStartDrag(event->pos());
|
mDragging = editMode.secondarySelectStartDrag(event->position().toPoint());
|
||||||
|
|
||||||
if (mDragging)
|
if (mDragging)
|
||||||
{
|
{
|
||||||
mDragX = event->localPos().x();
|
mDragX = event->position().x();
|
||||||
mDragY = height() - event->localPos().y();
|
mDragY = height() - event->position().y();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (event->globalPos() != mToolTipPos)
|
if (event->globalPosition().toPoint() != mToolTipPos)
|
||||||
{
|
{
|
||||||
mToolTipPos = event->globalPos();
|
mToolTipPos = event->globalPosition().toPoint();
|
||||||
|
|
||||||
if (mShowToolTips)
|
if (mShowToolTips)
|
||||||
{
|
{
|
||||||
|
|
@ -685,6 +735,8 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent(QMouseEvent* event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPoint pos = event->position().toPoint();
|
||||||
|
handleMarkerHighlight(pos.x(), pos.y());
|
||||||
SceneWidget::mouseMoveEvent(event);
|
SceneWidget::mouseMoveEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include <apps/opencs/view/render/tagbase.hpp>
|
#include <apps/opencs/view/render/tagbase.hpp>
|
||||||
|
|
||||||
#include "instancedragmodes.hpp"
|
#include "instancedragmodes.hpp"
|
||||||
|
#include "objectmarker.hpp"
|
||||||
#include "scenewidget.hpp"
|
#include "scenewidget.hpp"
|
||||||
|
|
||||||
class QDragEnterEvent;
|
class QDragEnterEvent;
|
||||||
|
|
@ -89,7 +90,6 @@ namespace CSVRender
|
||||||
QPoint mToolTipPos;
|
QPoint mToolTipPos;
|
||||||
bool mShowToolTips;
|
bool mShowToolTips;
|
||||||
int mToolTipDelay;
|
int mToolTipDelay;
|
||||||
bool mInConstructor;
|
|
||||||
int mSelectedNavigationMode;
|
int mSelectedNavigationMode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -186,6 +186,12 @@ namespace CSVRender
|
||||||
|
|
||||||
virtual void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) = 0;
|
virtual void selectWithinDistance(const osg::Vec3d& point, float distance, DragMode dragMode) = 0;
|
||||||
|
|
||||||
|
template <typename Tag>
|
||||||
|
std::optional<WorldspaceHitResult> checkTag(
|
||||||
|
const osgUtil::LineSegmentIntersector::Intersection& intersection) const;
|
||||||
|
|
||||||
|
std::tuple<osg::Vec3d, osg::Vec3d, osg::Vec3d> getStartEndDirection(int pointX, int pointY) const;
|
||||||
|
|
||||||
/// Return the next intersection with scene elements matched by
|
/// Return the next intersection with scene elements matched by
|
||||||
/// \a interactionMask based on \a localPos and the camera vector.
|
/// \a interactionMask based on \a localPos and the camera vector.
|
||||||
/// If there is no such intersection, instead a point "in front" of \a localPos will be
|
/// If there is no such intersection, instead a point "in front" of \a localPos will be
|
||||||
|
|
@ -216,7 +222,14 @@ namespace CSVRender
|
||||||
|
|
||||||
EditMode* getEditMode();
|
EditMode* getEditMode();
|
||||||
|
|
||||||
|
virtual CSVRender::Object* getObjectByReferenceId(const std::string& id) = 0;
|
||||||
|
|
||||||
|
ObjectMarker* getSelectionMarker() { return mSelectionMarker.get(); }
|
||||||
|
const ObjectMarker* getSelectionMarker() const { return mSelectionMarker.get(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
const std::unique_ptr<CSVRender::ObjectMarker> mSelectionMarker;
|
||||||
|
|
||||||
/// Visual elements in a scene
|
/// Visual elements in a scene
|
||||||
/// @note do not change the enumeration values, they are used in pre-existing button file names!
|
/// @note do not change the enumeration values, they are used in pre-existing button file names!
|
||||||
enum ButtonId
|
enum ButtonId
|
||||||
|
|
@ -247,11 +260,13 @@ namespace CSVRender
|
||||||
|
|
||||||
void settingChanged(const CSMPrefs::Setting* setting) override;
|
void settingChanged(const CSMPrefs::Setting* setting) override;
|
||||||
|
|
||||||
bool getSpeedMode();
|
|
||||||
|
|
||||||
void cycleNavigationMode();
|
void cycleNavigationMode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool hitBehindMarker(const osg::Vec3d& hitPos) const;
|
||||||
|
|
||||||
|
void handleMarkerHighlight(const int x, const int y);
|
||||||
|
|
||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
|
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ void CSVTools::ReportTable::contextMenuEvent(QContextMenuEvent* event)
|
||||||
void CSVTools::ReportTable::mouseMoveEvent(QMouseEvent* event)
|
void CSVTools::ReportTable::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
if (event->buttons() & Qt::LeftButton)
|
if (event->buttons() & Qt::LeftButton)
|
||||||
startDragFromTable(*this, indexAt(event->pos()));
|
startDragFromTable(*this, indexAt(event->position().toPoint()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVTools::ReportTable::mouseDoubleClickEvent(QMouseEvent* event)
|
void CSVTools::ReportTable::mouseDoubleClickEvent(QMouseEvent* event)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent* event)
|
||||||
|
|
||||||
// If the mouse is pressed above the pop-up parent,
|
// If the mouse is pressed above the pop-up parent,
|
||||||
// the pop-up will be hidden and the pressed signal won't be repeated for the parent
|
// the pop-up will be hidden and the pressed signal won't be repeated for the parent
|
||||||
if (buttonRect.contains(event->globalPos()) || buttonRect.contains(event->pos()))
|
if (buttonRect.contains(event->globalPosition().toPoint()) || buttonRect.contains(event->position().toPoint()))
|
||||||
{
|
{
|
||||||
setAttribute(Qt::WA_NoMouseReplay);
|
setAttribute(Qt::WA_NoMouseReplay);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,8 @@ int CSVWidget::CompleterPopup::sizeHintForRow(int row) const
|
||||||
|
|
||||||
ensurePolished();
|
ensurePolished();
|
||||||
QModelIndex index = model()->index(row, modelColumn());
|
QModelIndex index = model()->index(row, modelColumn());
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
QStyleOptionViewItem option;
|
QStyleOptionViewItem option;
|
||||||
initViewItemOption(&option);
|
initViewItemOption(&option);
|
||||||
#else
|
QAbstractItemDelegate* delegate = itemDelegateForIndex(index);
|
||||||
QStyleOptionViewItem option = viewOptions();
|
|
||||||
#endif
|
|
||||||
QAbstractItemDelegate* delegate = itemDelegate(index);
|
|
||||||
return delegate->sizeHint(option, index).height();
|
return delegate->sizeHint(option, index).height();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ void CSVWorld::NotEditableSubDelegate::setEditorData(QWidget* editor, const QMod
|
||||||
CSMWorld::Columns::ColumnId columnId
|
CSMWorld::Columns::ColumnId columnId
|
||||||
= static_cast<CSMWorld::Columns::ColumnId>(mTable->getColumnId(index.column()));
|
= static_cast<CSMWorld::Columns::ColumnId>(mTable->getColumnId(index.column()));
|
||||||
|
|
||||||
if (QVariant::String == v.type())
|
if (QMetaType::QString == v.typeId())
|
||||||
{
|
{
|
||||||
label->setText(v.toString());
|
label->setText(v.toString());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ void CSVWorld::DragRecordTable::dragEnterEvent(QDragEnterEvent* event)
|
||||||
|
|
||||||
void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent* event)
|
void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent* event)
|
||||||
{
|
{
|
||||||
QModelIndex index = indexAt(event->pos());
|
QModelIndex index = indexAt(event->position().toPoint());
|
||||||
if (CSVWorld::DragDropUtils::canAcceptData(*event, getIndexDisplayType(index))
|
if (CSVWorld::DragDropUtils::canAcceptData(*event, getIndexDisplayType(index))
|
||||||
|| CSVWorld::DragDropUtils::isInfo(*event, getIndexDisplayType(index))
|
|| CSVWorld::DragDropUtils::isInfo(*event, getIndexDisplayType(index))
|
||||||
|| CSVWorld::DragDropUtils::isTopicOrJournal(*event, getIndexDisplayType(index)))
|
|| CSVWorld::DragDropUtils::isTopicOrJournal(*event, getIndexDisplayType(index)))
|
||||||
|
|
@ -71,7 +71,7 @@ void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent* event)
|
||||||
|
|
||||||
void CSVWorld::DragRecordTable::dropEvent(QDropEvent* event)
|
void CSVWorld::DragRecordTable::dropEvent(QDropEvent* event)
|
||||||
{
|
{
|
||||||
QModelIndex index = indexAt(event->pos());
|
QModelIndex index = indexAt(event->position().toPoint());
|
||||||
CSMWorld::ColumnBase::Display display = getIndexDisplayType(index);
|
CSMWorld::ColumnBase::Display display = getIndexDisplayType(index);
|
||||||
if (CSVWorld::DragDropUtils::canAcceptData(*event, display))
|
if (CSVWorld::DragDropUtils::canAcceptData(*event, display))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ void CSVWorld::RegionMap::viewInTable()
|
||||||
|
|
||||||
void CSVWorld::RegionMap::mouseMoveEvent(QMouseEvent* event)
|
void CSVWorld::RegionMap::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
startDragFromTable(*this, indexAt(event->pos()));
|
startDragFromTable(*this, indexAt(event->position().toPoint()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CSMWorld::UniversalId> CSVWorld::RegionMap::getDraggedRecords() const
|
std::vector<CSMWorld::UniversalId> CSVWorld::RegionMap::getDraggedRecords() const
|
||||||
|
|
@ -376,7 +376,7 @@ void CSVWorld::RegionMap::dragMoveEvent(QDragMoveEvent* event)
|
||||||
|
|
||||||
void CSVWorld::RegionMap::dropEvent(QDropEvent* event)
|
void CSVWorld::RegionMap::dropEvent(QDropEvent* event)
|
||||||
{
|
{
|
||||||
QModelIndex index = indexAt(event->pos());
|
QModelIndex index = indexAt(event->position().toPoint());
|
||||||
|
|
||||||
bool exists = QTableView::model()->data(index, Qt::BackgroundRole) != QBrush(Qt::DiagCrossPattern);
|
bool exists = QTableView::model()->data(index, Qt::BackgroundRole) != QBrush(Qt::DiagCrossPattern);
|
||||||
if (!index.isValid() || !exists)
|
if (!index.isValid() || !exists)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,24 @@
|
||||||
#include "../../model/world/tablemimedata.hpp"
|
#include "../../model/world/tablemimedata.hpp"
|
||||||
#include "../../model/world/universalid.hpp"
|
#include "../../model/world/universalid.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void prependToEachLine(QTextCursor begin, const QString& text)
|
||||||
|
{
|
||||||
|
QTextCursor end = begin;
|
||||||
|
begin.setPosition(begin.selectionStart());
|
||||||
|
begin.movePosition(QTextCursor::StartOfLine);
|
||||||
|
end.setPosition(end.selectionEnd());
|
||||||
|
end.movePosition(QTextCursor::EndOfLine);
|
||||||
|
begin.beginEditBlock();
|
||||||
|
for (; begin < end; begin.movePosition(QTextCursor::EndOfLine), begin.movePosition(QTextCursor::Right))
|
||||||
|
{
|
||||||
|
begin.insertText(text);
|
||||||
|
}
|
||||||
|
begin.endEditBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CSVWorld::ScriptEdit::ChangeLock::ChangeLock(ScriptEdit& edit)
|
CSVWorld::ScriptEdit::ChangeLock::ChangeLock(ScriptEdit& edit)
|
||||||
: mEdit(edit)
|
: mEdit(edit)
|
||||||
{
|
{
|
||||||
|
|
@ -46,6 +64,55 @@ bool CSVWorld::ScriptEdit::event(QEvent* event)
|
||||||
return QPlainTextEdit::event(event);
|
return QPlainTextEdit::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSVWorld::ScriptEdit::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
if (event->key() == Qt::Key_Backtab)
|
||||||
|
{
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
|
QTextCursor end = cursor;
|
||||||
|
cursor.setPosition(cursor.selectionStart());
|
||||||
|
cursor.movePosition(QTextCursor::StartOfLine);
|
||||||
|
end.setPosition(end.selectionEnd());
|
||||||
|
end.movePosition(QTextCursor::EndOfLine);
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
for (; cursor < end; cursor.movePosition(QTextCursor::EndOfLine), cursor.movePosition(QTextCursor::Right))
|
||||||
|
{
|
||||||
|
cursor.select(QTextCursor::LineUnderCursor);
|
||||||
|
QString line = cursor.selectedText();
|
||||||
|
|
||||||
|
if (line.isEmpty())
|
||||||
|
continue;
|
||||||
|
qsizetype index = 0;
|
||||||
|
if (line[0] == '\t')
|
||||||
|
index = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Remove up to a tab worth of spaces instead
|
||||||
|
while (line[index].isSpace() && index < mTabCharCount && line[index] != '\t')
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index != 0)
|
||||||
|
{
|
||||||
|
line.remove(0, index);
|
||||||
|
cursor.insertText(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.endEditBlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (event->key() == Qt::Key_Tab)
|
||||||
|
{
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
|
if (cursor.hasSelection())
|
||||||
|
{
|
||||||
|
prependToEachLine(cursor, "\t");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QPlainTextEdit::keyPressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
CSVWorld::ScriptEdit::ScriptEdit(const CSMDoc::Document& document, ScriptHighlighter::Mode mode, QWidget* parent)
|
CSVWorld::ScriptEdit::ScriptEdit(const CSMDoc::Document& document, ScriptHighlighter::Mode mode, QWidget* parent)
|
||||||
: QPlainTextEdit(parent)
|
: QPlainTextEdit(parent)
|
||||||
, mChangeLocked(0)
|
, mChangeLocked(0)
|
||||||
|
|
@ -136,7 +203,7 @@ void CSVWorld::ScriptEdit::dragEnterEvent(QDragEnterEvent* event)
|
||||||
QPlainTextEdit::dragEnterEvent(event);
|
QPlainTextEdit::dragEnterEvent(event);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setTextCursor(cursorForPosition(event->pos()));
|
setTextCursor(cursorForPosition(event->position().toPoint()));
|
||||||
event->acceptProposedAction();
|
event->acceptProposedAction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +215,7 @@ void CSVWorld::ScriptEdit::dragMoveEvent(QDragMoveEvent* event)
|
||||||
QPlainTextEdit::dragMoveEvent(event);
|
QPlainTextEdit::dragMoveEvent(event);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setTextCursor(cursorForPosition(event->pos()));
|
setTextCursor(cursorForPosition(event->position().toPoint()));
|
||||||
event->accept();
|
event->accept();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +229,7 @@ void CSVWorld::ScriptEdit::dropEvent(QDropEvent* event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTextCursor(cursorForPosition(event->pos()));
|
setTextCursor(cursorForPosition(event->position().toPoint()));
|
||||||
|
|
||||||
if (mime->fromDocument(mDocument))
|
if (mime->fromDocument(mDocument))
|
||||||
{
|
{
|
||||||
|
|
@ -316,22 +383,7 @@ void CSVWorld::ScriptEdit::markOccurrences()
|
||||||
|
|
||||||
void CSVWorld::ScriptEdit::commentSelection()
|
void CSVWorld::ScriptEdit::commentSelection()
|
||||||
{
|
{
|
||||||
QTextCursor begin = textCursor();
|
prependToEachLine(textCursor(), ";");
|
||||||
QTextCursor end = begin;
|
|
||||||
begin.setPosition(begin.selectionStart());
|
|
||||||
begin.movePosition(QTextCursor::StartOfLine);
|
|
||||||
|
|
||||||
end.setPosition(end.selectionEnd());
|
|
||||||
end.movePosition(QTextCursor::EndOfLine);
|
|
||||||
|
|
||||||
begin.beginEditBlock();
|
|
||||||
|
|
||||||
for (; begin < end; begin.movePosition(QTextCursor::EndOfLine), begin.movePosition(QTextCursor::Right))
|
|
||||||
{
|
|
||||||
begin.insertText(";");
|
|
||||||
}
|
|
||||||
|
|
||||||
begin.endEditBlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVWorld::ScriptEdit::uncommentSelection()
|
void CSVWorld::ScriptEdit::uncommentSelection()
|
||||||
|
|
@ -345,17 +397,16 @@ void CSVWorld::ScriptEdit::uncommentSelection()
|
||||||
end.movePosition(QTextCursor::EndOfLine);
|
end.movePosition(QTextCursor::EndOfLine);
|
||||||
|
|
||||||
begin.beginEditBlock();
|
begin.beginEditBlock();
|
||||||
|
|
||||||
for (; begin < end; begin.movePosition(QTextCursor::EndOfLine), begin.movePosition(QTextCursor::Right))
|
for (; begin < end; begin.movePosition(QTextCursor::EndOfLine), begin.movePosition(QTextCursor::Right))
|
||||||
{
|
{
|
||||||
begin.select(QTextCursor::LineUnderCursor);
|
begin.select(QTextCursor::LineUnderCursor);
|
||||||
QString line = begin.selectedText();
|
QString line = begin.selectedText();
|
||||||
|
|
||||||
if (line.size() == 0)
|
if (line.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// get first nonspace character in line
|
// get first nonspace character in line
|
||||||
int index;
|
qsizetype index;
|
||||||
for (index = 0; index != line.size(); ++index)
|
for (index = 0; index != line.size(); ++index)
|
||||||
{
|
{
|
||||||
if (!line[index].isSpace())
|
if (!line[index].isSpace())
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ namespace CSVWorld
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
|
void keyPressEvent(QKeyEvent* e) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScriptEdit(const CSMDoc::Document& document, ScriptHighlighter::Mode mode, QWidget* parent);
|
ScriptEdit(const CSMDoc::Document& document, ScriptHighlighter::Mode mode, QWidget* parent);
|
||||||
|
|
|
||||||
|
|
@ -592,7 +592,7 @@ void CSVWorld::Table::moveRecords(QDropEvent* event)
|
||||||
if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
|
if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QModelIndex targedIndex = indexAt(event->pos());
|
QModelIndex targedIndex = indexAt(event->position().toPoint());
|
||||||
|
|
||||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||||
int targetRowRaw = targedIndex.row();
|
int targetRowRaw = targedIndex.row();
|
||||||
|
|
@ -872,7 +872,7 @@ void CSVWorld::Table::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
if (event->buttons() & Qt::LeftButton)
|
if (event->buttons() & Qt::LeftButton)
|
||||||
{
|
{
|
||||||
startDragFromTable(*this, indexAt(event->pos()));
|
startDragFromTable(*this, indexAt(event->position().toPoint()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace CSVWorld
|
||||||
auto& clickEvent = static_cast<QMouseEvent&>(*event);
|
auto& clickEvent = static_cast<QMouseEvent&>(*event);
|
||||||
if ((clickEvent.button() == Qt::MiddleButton))
|
if ((clickEvent.button() == Qt::MiddleButton))
|
||||||
{
|
{
|
||||||
const auto& index = table.indexAt(clickEvent.pos());
|
const auto& index = table.indexAt(clickEvent.position().toPoint());
|
||||||
table.setColumnHidden(index.column(), true);
|
table.setColumnHidden(index.column(), true);
|
||||||
clickEvent.accept();
|
clickEvent.accept();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ QWidget* CSVWorld::CommandDelegate::createEditor(
|
||||||
// TODO: Find a better solution?
|
// TODO: Find a better solution?
|
||||||
if (display == CSMWorld::ColumnBase::Display_Boolean)
|
if (display == CSMWorld::ColumnBase::Display_Boolean)
|
||||||
{
|
{
|
||||||
return QItemEditorFactory::defaultFactory()->createEditor(QVariant::Bool, parent);
|
return QItemEditorFactory::defaultFactory()->createEditor(QMetaType::Bool, parent);
|
||||||
}
|
}
|
||||||
// For tables the pop-up of the color editor should appear immediately after the editor creation
|
// For tables the pop-up of the color editor should appear immediately after the editor creation
|
||||||
// (the third parameter of ColorEditor's constructor)
|
// (the third parameter of ColorEditor's constructor)
|
||||||
|
|
@ -362,11 +362,7 @@ void CSVWorld::CommandDelegate::setEditorData(QWidget* editor, const QModelIndex
|
||||||
if (!n.isEmpty())
|
if (!n.isEmpty())
|
||||||
{
|
{
|
||||||
if (!variant.isValid())
|
if (!variant.isValid())
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
variant = QVariant(editor->property(n).metaType(), (const void*)nullptr);
|
variant = QVariant(editor->property(n).metaType(), (const void*)nullptr);
|
||||||
#else
|
|
||||||
variant = QVariant(editor->property(n).userType(), (const void*)nullptr);
|
|
||||||
#endif
|
|
||||||
editor->setProperty(n, variant);
|
editor->setProperty(n, variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ add_openmw_dir (mwgui
|
||||||
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
|
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
|
||||||
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
|
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
|
||||||
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher
|
draganddrop timeadvancer jailscreen itemchargeview keyboardnavigation textcolours statswatcher
|
||||||
postprocessorhud settings
|
postprocessorhud settings worlditemmodel itemtransfer
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwdialogue
|
add_openmw_dir (mwdialogue
|
||||||
|
|
@ -63,7 +63,7 @@ add_openmw_dir (mwlua
|
||||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings coremwscriptbindings
|
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings coremwscriptbindings
|
||||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings
|
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings
|
||||||
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker landbindings magicbindings factionbindings
|
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker landbindings magicbindings factionbindings
|
||||||
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings
|
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings weatherbindings
|
||||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
||||||
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
||||||
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
|
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
|
||||||
|
|
|
||||||
|
|
@ -433,6 +433,8 @@ OMW::Engine::~Engine()
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
|
Log(Debug::Info) << "Quitting peacefully.";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set data dir
|
// Set data dir
|
||||||
|
|
@ -729,7 +731,7 @@ void OMW::Engine::prepareEngine()
|
||||||
|
|
||||||
mVFS = std::make_unique<VFS::Manager>();
|
mVFS = std::make_unique<VFS::Manager>();
|
||||||
|
|
||||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true, &mEncoder.get()->getStatelessEncoder());
|
||||||
|
|
||||||
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
|
mResourceSystem = std::make_unique<Resource::ResourceSystem>(
|
||||||
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());
|
mVFS.get(), Settings::cells().mCacheExpiryDelay, &mEncoder.get()->getStatelessEncoder());
|
||||||
|
|
@ -753,7 +755,7 @@ void OMW::Engine::prepareEngine()
|
||||||
|
|
||||||
mViewer->addEventHandler(mScreenCaptureHandler);
|
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||||
|
|
||||||
mL10nManager = std::make_unique<l10n::Manager>(mVFS.get());
|
mL10nManager = std::make_unique<L10n::Manager>(mVFS.get());
|
||||||
mL10nManager->setPreferredLocales(Settings::general().mPreferredLocales, Settings::general().mGmstOverridesL10n);
|
mL10nManager->setPreferredLocales(Settings::general().mPreferredLocales, Settings::general().mGmstOverridesL10n);
|
||||||
mEnvironment.setL10nManager(*mL10nManager);
|
mEnvironment.setL10nManager(*mL10nManager);
|
||||||
|
|
||||||
|
|
@ -779,7 +781,6 @@ void OMW::Engine::prepareEngine()
|
||||||
|
|
||||||
const auto userdefault = mCfgMgr.getUserConfigPath() / "gamecontrollerdb.txt";
|
const auto userdefault = mCfgMgr.getUserConfigPath() / "gamecontrollerdb.txt";
|
||||||
const auto localdefault = mCfgMgr.getLocalPath() / "gamecontrollerdb.txt";
|
const auto localdefault = mCfgMgr.getLocalPath() / "gamecontrollerdb.txt";
|
||||||
const auto globaldefault = mCfgMgr.getGlobalPath() / "gamecontrollerdb.txt";
|
|
||||||
|
|
||||||
std::filesystem::path userGameControllerdb;
|
std::filesystem::path userGameControllerdb;
|
||||||
if (std::filesystem::exists(userdefault))
|
if (std::filesystem::exists(userdefault))
|
||||||
|
|
@ -788,9 +789,13 @@ void OMW::Engine::prepareEngine()
|
||||||
std::filesystem::path gameControllerdb;
|
std::filesystem::path gameControllerdb;
|
||||||
if (std::filesystem::exists(localdefault))
|
if (std::filesystem::exists(localdefault))
|
||||||
gameControllerdb = localdefault;
|
gameControllerdb = localdefault;
|
||||||
else if (std::filesystem::exists(globaldefault))
|
else if (!mCfgMgr.getGlobalPath().empty())
|
||||||
|
{
|
||||||
|
const auto globaldefault = mCfgMgr.getGlobalPath() / "gamecontrollerdb.txt";
|
||||||
|
if (std::filesystem::exists(globaldefault))
|
||||||
gameControllerdb = globaldefault;
|
gameControllerdb = globaldefault;
|
||||||
// else if it doesn't exist, pass in an empty string
|
}
|
||||||
|
// else if it doesn't exist, pass in an empty path
|
||||||
|
|
||||||
// gui needs our shaders path before everything else
|
// gui needs our shaders path before everything else
|
||||||
mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders");
|
mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders");
|
||||||
|
|
@ -1069,8 +1074,6 @@ void OMW::Engine::go()
|
||||||
Settings::Manager::saveUser(mCfgMgr.getUserConfigPath() / "settings.cfg");
|
Settings::Manager::saveUser(mCfgMgr.getUserConfigPath() / "settings.cfg");
|
||||||
Settings::ShaderManager::get().save();
|
Settings::ShaderManager::get().save();
|
||||||
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath());
|
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath());
|
||||||
|
|
||||||
Log(Debug::Info) << "Quitting peacefully.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OMW::Engine::setCompileAll(bool all)
|
void OMW::Engine::setCompileAll(bool all)
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ namespace MWDialogue
|
||||||
class Journal;
|
class Journal;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace l10n
|
namespace L10n
|
||||||
{
|
{
|
||||||
class Manager;
|
class Manager;
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +141,7 @@ namespace OMW
|
||||||
std::unique_ptr<MWState::StateManager> mStateManager;
|
std::unique_ptr<MWState::StateManager> mStateManager;
|
||||||
std::unique_ptr<MWLua::LuaManager> mLuaManager;
|
std::unique_ptr<MWLua::LuaManager> mLuaManager;
|
||||||
std::unique_ptr<MWLua::Worker> mLuaWorker;
|
std::unique_ptr<MWLua::Worker> mLuaWorker;
|
||||||
std::unique_ptr<l10n::Manager> mL10nManager;
|
std::unique_ptr<L10n::Manager> mL10nManager;
|
||||||
MWBase::Environment mEnvironment;
|
MWBase::Environment mEnvironment;
|
||||||
ToUTF8::FromType mEncoding;
|
ToUTF8::FromType mEncoding;
|
||||||
std::unique_ptr<ToUTF8::Utf8Encoder> mEncoder;
|
std::unique_ptr<ToUTF8::Utf8Encoder> mEncoder;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Resource
|
||||||
class ResourceSystem;
|
class ResourceSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace l10n
|
namespace L10n
|
||||||
{
|
{
|
||||||
class Manager;
|
class Manager;
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ namespace MWBase
|
||||||
StateManager* mStateManager = nullptr;
|
StateManager* mStateManager = nullptr;
|
||||||
LuaManager* mLuaManager = nullptr;
|
LuaManager* mLuaManager = nullptr;
|
||||||
Resource::ResourceSystem* mResourceSystem = nullptr;
|
Resource::ResourceSystem* mResourceSystem = nullptr;
|
||||||
l10n::Manager* mL10nManager = nullptr;
|
L10n::Manager* mL10nManager = nullptr;
|
||||||
float mFrameRateLimit = 0;
|
float mFrameRateLimit = 0;
|
||||||
float mFrameDuration = 0;
|
float mFrameDuration = 0;
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ namespace MWBase
|
||||||
|
|
||||||
void setResourceSystem(Resource::ResourceSystem& value) { mResourceSystem = &value; }
|
void setResourceSystem(Resource::ResourceSystem& value) { mResourceSystem = &value; }
|
||||||
|
|
||||||
void setL10nManager(l10n::Manager& value) { mL10nManager = &value; }
|
void setL10nManager(L10n::Manager& value) { mL10nManager = &value; }
|
||||||
|
|
||||||
Misc::NotNullPtr<World> getWorld() const { return mWorld; }
|
Misc::NotNullPtr<World> getWorld() const { return mWorld; }
|
||||||
Misc::NotNullPtr<MWWorld::WorldModel> getWorldModel() const { return mWorldModel; }
|
Misc::NotNullPtr<MWWorld::WorldModel> getWorldModel() const { return mWorldModel; }
|
||||||
|
|
@ -122,7 +122,7 @@ namespace MWBase
|
||||||
|
|
||||||
Misc::NotNullPtr<Resource::ResourceSystem> getResourceSystem() const { return mResourceSystem; }
|
Misc::NotNullPtr<Resource::ResourceSystem> getResourceSystem() const { return mResourceSystem; }
|
||||||
|
|
||||||
Misc::NotNullPtr<l10n::Manager> getL10nManager() const { return mL10nManager; }
|
Misc::NotNullPtr<L10n::Manager> getL10nManager() const { return mL10nManager; }
|
||||||
|
|
||||||
float getFrameRateLimit() const { return mFrameRateLimit; }
|
float getFrameRateLimit() const { return mFrameRateLimit; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ namespace MWBase
|
||||||
virtual void executeAction(int action) = 0;
|
virtual void executeAction(int action) = 0;
|
||||||
|
|
||||||
virtual bool controlsDisabled() = 0;
|
virtual bool controlsDisabled() = 0;
|
||||||
|
|
||||||
|
virtual void saveBindings() = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef GAME_MWBASE_LUAMANAGER_H
|
#ifndef GAME_MWBASE_LUAMANAGER_H
|
||||||
#define GAME_MWBASE_LUAMANAGER_H
|
#define GAME_MWBASE_LUAMANAGER_H
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
@ -8,6 +9,7 @@
|
||||||
#include <SDL_events.h>
|
#include <SDL_events.h>
|
||||||
|
|
||||||
#include "../mwgui/mode.hpp"
|
#include "../mwgui/mode.hpp"
|
||||||
|
#include "../mwmechanics/damagesourcetype.hpp"
|
||||||
#include "../mwrender/animationpriority.hpp"
|
#include "../mwrender/animationpriority.hpp"
|
||||||
#include <components/sdlutil/events.hpp>
|
#include <components/sdlutil/events.hpp>
|
||||||
|
|
||||||
|
|
@ -38,6 +40,11 @@ namespace LuaUtil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace osg
|
||||||
|
{
|
||||||
|
class Vec3f;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWBase
|
namespace MWBase
|
||||||
{
|
{
|
||||||
// \brief LuaManager is the central interface through which the engine invokes lua scripts.
|
// \brief LuaManager is the central interface through which the engine invokes lua scripts.
|
||||||
|
|
@ -68,13 +75,19 @@ namespace MWBase
|
||||||
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
||||||
std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback)
|
std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback)
|
||||||
= 0;
|
= 0;
|
||||||
|
virtual void jailTimeServed(const MWWorld::Ptr& actor, int days) = 0;
|
||||||
virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0;
|
virtual void skillLevelUp(const MWWorld::Ptr& actor, ESM::RefId skillId, std::string_view source) = 0;
|
||||||
virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0;
|
virtual void skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale) = 0;
|
||||||
|
virtual void onHit(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& weapon,
|
||||||
|
const MWWorld::Ptr& ammo, int attackType, float attackStrength, float damage, bool isHealth,
|
||||||
|
const osg::Vec3f& hitPos, bool successful, MWMechanics::DamageSourceType)
|
||||||
|
= 0;
|
||||||
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
virtual void exteriorCreated(MWWorld::CellStore& cell) = 0;
|
||||||
virtual void actorDied(const MWWorld::Ptr& actor) = 0;
|
virtual void actorDied(const MWWorld::Ptr& actor) = 0;
|
||||||
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
virtual void questUpdated(const ESM::RefId& questId, int stage) = 0;
|
||||||
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
// `arg` is either forwarded from MWGui::pushGuiMode or empty
|
||||||
virtual void uiModeChanged(const MWWorld::Ptr& arg) = 0;
|
virtual void uiModeChanged(const MWWorld::Ptr& arg) = 0;
|
||||||
|
virtual void savePermanentStorage(const std::filesystem::path& userConfigPath) = 0;
|
||||||
|
|
||||||
// TODO: notify LuaManager about other events
|
// TODO: notify LuaManager about other events
|
||||||
// virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object,
|
// virtual void objectOnHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object,
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "../mwmechanics/greetingstate.hpp"
|
|
||||||
#include "../mwrender/animationpriority.hpp"
|
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
|
|
@ -27,6 +24,11 @@ namespace ESM
|
||||||
class ESMWriter;
|
class ESMWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
enum class GreetingState;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class Ptr;
|
class Ptr;
|
||||||
|
|
|
||||||
|
|
@ -384,6 +384,7 @@ namespace MWBase
|
||||||
// Used in Lua bindings
|
// Used in Lua bindings
|
||||||
virtual const std::vector<MWGui::GuiMode>& getGuiModeStack() const = 0;
|
virtual const std::vector<MWGui::GuiMode>& getGuiModeStack() const = 0;
|
||||||
virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0;
|
virtual void setDisabledByLua(std::string_view windowId, bool disabled) = 0;
|
||||||
|
virtual bool isWindowVisible(std::string_view windowId) const = 0;
|
||||||
virtual std::vector<std::string_view> getAllWindowIds() const = 0;
|
virtual std::vector<std::string_view> getAllWindowIds() const = 0;
|
||||||
virtual std::vector<std::string_view> getAllowedWindowIds(MWGui::GuiMode mode) const = 0;
|
virtual std::vector<std::string_view> getAllowedWindowIds(MWGui::GuiMode mode) const = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class Vec3f;
|
class Vec3f;
|
||||||
|
class Vec4f;
|
||||||
class Matrixf;
|
class Matrixf;
|
||||||
class Quat;
|
class Quat;
|
||||||
class Image;
|
class Image;
|
||||||
|
|
@ -93,6 +94,7 @@ namespace MWWorld
|
||||||
class RefData;
|
class RefData;
|
||||||
class Cell;
|
class Cell;
|
||||||
class DateTimeManager;
|
class DateTimeManager;
|
||||||
|
class Weather;
|
||||||
|
|
||||||
typedef std::vector<std::pair<MWWorld::Ptr, MWMechanics::Movement>> PtrMovementList;
|
typedef std::vector<std::pair<MWWorld::Ptr, MWMechanics::Movement>> PtrMovementList;
|
||||||
}
|
}
|
||||||
|
|
@ -216,9 +218,21 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void changeWeather(const ESM::RefId& region, const unsigned int id) = 0;
|
virtual void changeWeather(const ESM::RefId& region, const unsigned int id) = 0;
|
||||||
|
|
||||||
virtual int getCurrentWeather() const = 0;
|
virtual void changeWeather(const ESM::RefId& region, const ESM::RefId& id) = 0;
|
||||||
|
|
||||||
virtual int getNextWeather() const = 0;
|
virtual const std::vector<MWWorld::Weather>& getAllWeather() const = 0;
|
||||||
|
|
||||||
|
virtual int getCurrentWeatherScriptId() const = 0;
|
||||||
|
|
||||||
|
virtual const MWWorld::Weather& getCurrentWeather() const = 0;
|
||||||
|
|
||||||
|
virtual const MWWorld::Weather* getWeather(size_t index) const = 0;
|
||||||
|
|
||||||
|
virtual const MWWorld::Weather* getWeather(const ESM::RefId& id) const = 0;
|
||||||
|
|
||||||
|
virtual int getNextWeatherScriptId() const = 0;
|
||||||
|
|
||||||
|
virtual const MWWorld::Weather* getNextWeather() const = 0;
|
||||||
|
|
||||||
virtual float getWeatherTransition() const = 0;
|
virtual float getWeatherTransition() const = 0;
|
||||||
|
|
||||||
|
|
@ -478,6 +492,7 @@ namespace MWBase
|
||||||
// Allow NPCs to use torches?
|
// Allow NPCs to use torches?
|
||||||
virtual bool useTorches() const = 0;
|
virtual bool useTorches() const = 0;
|
||||||
|
|
||||||
|
virtual const osg::Vec4f& getSunLightPosition() const = 0;
|
||||||
virtual float getSunVisibility() const = 0;
|
virtual float getSunVisibility() const = 0;
|
||||||
virtual float getSunPercentage() const = 0;
|
virtual float getSunPercentage() const = 0;
|
||||||
|
|
||||||
|
|
@ -511,9 +526,6 @@ namespace MWBase
|
||||||
/// Spawn a random creature from a levelled list next to the player
|
/// Spawn a random creature from a levelled list next to the player
|
||||||
virtual void spawnRandomCreature(const ESM::RefId& creatureList) = 0;
|
virtual void spawnRandomCreature(const ESM::RefId& creatureList) = 0;
|
||||||
|
|
||||||
/// Spawn a blood effect for \a ptr at \a worldPosition
|
|
||||||
virtual void spawnBloodEffect(const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
|
|
||||||
|
|
||||||
virtual void spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
|
virtual void spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
|
||||||
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true, bool useAmbientLight = true)
|
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true, bool useAmbientLight = true)
|
||||||
= 0;
|
= 0;
|
||||||
|
|
@ -574,8 +586,7 @@ namespace MWBase
|
||||||
virtual bool hasCollisionWithDoor(
|
virtual bool hasCollisionWithDoor(
|
||||||
const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
|
const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
|
||||||
|
|
||||||
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
|
virtual bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& position) const = 0;
|
||||||
std::span<const MWWorld::ConstPtr> ignore, std::vector<MWWorld::Ptr>* occupyingActors = nullptr) const = 0;
|
|
||||||
|
|
||||||
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
|
#include "../mwlua/localscripts.hpp"
|
||||||
|
|
||||||
#include "../mwworld/actionequip.hpp"
|
#include "../mwworld/actionequip.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
#include "../mwworld/containerstore.hpp"
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
@ -109,8 +111,23 @@ namespace MWClass
|
||||||
return std::make_pair(slots_, false);
|
return std::make_pair(slots_, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::RefId Armor::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
ESM::RefId Armor::getEquipmentSkill(const MWWorld::ConstPtr& ptr, bool useLuaInterfaceIfAvailable) const
|
||||||
{
|
{
|
||||||
|
// We don't actually need an actor as such. We just need an object that has
|
||||||
|
// lua scripts and the Combat interface.
|
||||||
|
if (useLuaInterfaceIfAvailable)
|
||||||
|
{
|
||||||
|
// In this interface call, both objects are effectively const, so stripping Const from the ConstPtr is fine.
|
||||||
|
MWWorld::Ptr mutablePtr(
|
||||||
|
const_cast<MWWorld::LiveCellRefBase*>(ptr.mRef), const_cast<MWWorld::CellStore*>(ptr.mCell));
|
||||||
|
auto res = MWLua::LocalScripts::callPlayerInterface<std::string>(
|
||||||
|
"Combat", "getArmorSkill", MWLua::LObject(mutablePtr));
|
||||||
|
if (res)
|
||||||
|
return ESM::RefId::deserializeText(res.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to the old engine implementation when actors don't have their scripts attached yet.
|
||||||
|
|
||||||
const MWWorld::LiveCellRef<ESM::Armor>* ref = ptr.get<ESM::Armor>();
|
const MWWorld::LiveCellRef<ESM::Armor>* ref = ptr.get<ESM::Armor>();
|
||||||
|
|
||||||
std::string_view typeGmst;
|
std::string_view typeGmst;
|
||||||
|
|
@ -175,7 +192,7 @@ namespace MWClass
|
||||||
|
|
||||||
const ESM::RefId& Armor::getUpSoundId(const MWWorld::ConstPtr& ptr) const
|
const ESM::RefId& Armor::getUpSoundId(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
const ESM::RefId es = getEquipmentSkill(ptr);
|
const ESM::RefId es = getEquipmentSkill(ptr, false);
|
||||||
static const ESM::RefId lightUp = ESM::RefId::stringRefId("Item Armor Light Up");
|
static const ESM::RefId lightUp = ESM::RefId::stringRefId("Item Armor Light Up");
|
||||||
static const ESM::RefId mediumUp = ESM::RefId::stringRefId("Item Armor Medium Up");
|
static const ESM::RefId mediumUp = ESM::RefId::stringRefId("Item Armor Medium Up");
|
||||||
static const ESM::RefId heavyUp = ESM::RefId::stringRefId("Item Armor Heavy Up");
|
static const ESM::RefId heavyUp = ESM::RefId::stringRefId("Item Armor Heavy Up");
|
||||||
|
|
@ -190,7 +207,7 @@ namespace MWClass
|
||||||
|
|
||||||
const ESM::RefId& Armor::getDownSoundId(const MWWorld::ConstPtr& ptr) const
|
const ESM::RefId& Armor::getDownSoundId(const MWWorld::ConstPtr& ptr) const
|
||||||
{
|
{
|
||||||
const ESM::RefId es = getEquipmentSkill(ptr);
|
const ESM::RefId es = getEquipmentSkill(ptr, false);
|
||||||
static const ESM::RefId lightDown = ESM::RefId::stringRefId("Item Armor Light Down");
|
static const ESM::RefId lightDown = ESM::RefId::stringRefId("Item Armor Light Down");
|
||||||
static const ESM::RefId mediumDown = ESM::RefId::stringRefId("Item Armor Medium Down");
|
static const ESM::RefId mediumDown = ESM::RefId::stringRefId("Item Armor Medium Down");
|
||||||
static const ESM::RefId heavyDown = ESM::RefId::stringRefId("Item Armor Heavy Down");
|
static const ESM::RefId heavyDown = ESM::RefId::stringRefId("Item Armor Heavy Down");
|
||||||
|
|
@ -221,24 +238,29 @@ namespace MWClass
|
||||||
std::string text;
|
std::string text;
|
||||||
|
|
||||||
// get armor type string (light/medium/heavy)
|
// get armor type string (light/medium/heavy)
|
||||||
std::string_view typeText;
|
std::string typeText;
|
||||||
if (ref->mBase->mData.mWeight == 0)
|
if (ref->mBase->mData.mWeight == 0)
|
||||||
{
|
{
|
||||||
// no type
|
// no type
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const ESM::RefId armorType = getEquipmentSkill(ptr);
|
const ESM::RefId armorType = getEquipmentSkill(ptr, true);
|
||||||
if (armorType == ESM::Skill::LightArmor)
|
if (armorType == ESM::Skill::LightArmor)
|
||||||
typeText = "#{sLight}";
|
typeText = "#{sLight}";
|
||||||
else if (armorType == ESM::Skill::MediumArmor)
|
else if (armorType == ESM::Skill::MediumArmor)
|
||||||
typeText = "#{sMedium}";
|
typeText = "#{sMedium}";
|
||||||
else
|
else if (armorType == ESM::Skill::HeavyArmor)
|
||||||
typeText = "#{sHeavy}";
|
typeText = "#{sHeavy}";
|
||||||
|
// For other skills, just subtitute the skill name
|
||||||
|
// Normally you would never see this case, but modding allows getEquipmentSkill() to return any skill.
|
||||||
|
else
|
||||||
|
typeText = "#{sSkill" + armorType.toString() + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
text += "\n#{sArmorRating}: "
|
text += "\n#{sArmorRating}: "
|
||||||
+ MWGui::ToolTips::toString(static_cast<int>(getEffectiveArmorRating(ptr, MWMechanics::getPlayer())));
|
+ MWGui::ToolTips::toString(
|
||||||
|
static_cast<int>(getSkillAdjustedArmorRating(ptr, MWMechanics::getPlayer(), true)));
|
||||||
|
|
||||||
int remainingHealth = getItemHealth(ptr);
|
int remainingHealth = getItemHealth(ptr);
|
||||||
text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/"
|
text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/"
|
||||||
|
|
@ -289,11 +311,25 @@ namespace MWClass
|
||||||
return record->mId;
|
return record->mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Armor::getEffectiveArmorRating(const MWWorld::ConstPtr& ptr, const MWWorld::Ptr& actor) const
|
float Armor::getSkillAdjustedArmorRating(
|
||||||
|
const MWWorld::ConstPtr& ptr, const MWWorld::Ptr& actor, bool useLuaInterfaceIfAvailable) const
|
||||||
{
|
{
|
||||||
|
if (useLuaInterfaceIfAvailable && actor == MWMechanics::getPlayer())
|
||||||
|
{
|
||||||
|
// In this interface call, both objects are effectively const, so stripping Const from the ConstPtr is fine.
|
||||||
|
MWWorld::Ptr mutablePtr(
|
||||||
|
const_cast<MWWorld::LiveCellRefBase*>(ptr.mRef), const_cast<MWWorld::CellStore*>(ptr.mCell));
|
||||||
|
auto res = MWLua::LocalScripts::callPlayerInterface<float>(
|
||||||
|
"Combat", "getSkillAdjustedArmorRating", MWLua::LObject(mutablePtr), MWLua::LObject(actor));
|
||||||
|
if (res)
|
||||||
|
return res.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to the old engine implementation when actors don't have their scripts attached yet.
|
||||||
|
|
||||||
const MWWorld::LiveCellRef<ESM::Armor>* ref = ptr.get<ESM::Armor>();
|
const MWWorld::LiveCellRef<ESM::Armor>* ref = ptr.get<ESM::Armor>();
|
||||||
|
|
||||||
const ESM::RefId armorSkillType = getEquipmentSkill(ptr);
|
const ESM::RefId armorSkillType = getEquipmentSkill(ptr, useLuaInterfaceIfAvailable);
|
||||||
float armorSkill = actor.getClass().getSkill(actor, armorSkillType);
|
float armorSkill = actor.getClass().getSkill(actor, armorSkillType);
|
||||||
|
|
||||||
int iBaseArmorSkill = MWBase::Environment::get()
|
int iBaseArmorSkill = MWBase::Environment::get()
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace MWClass
|
||||||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||||
/// stay stacked when equipped?
|
/// stay stacked when equipped?
|
||||||
|
|
||||||
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr, bool useLuaInterfaceIfAvailable) const override;
|
||||||
|
|
||||||
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override;
|
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override;
|
||||||
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
|
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
|
||||||
|
|
@ -81,7 +81,8 @@ namespace MWClass
|
||||||
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
bool canSell(const MWWorld::ConstPtr& item, int npcServices) const override;
|
||||||
|
|
||||||
/// Get the effective armor rating, factoring in the actor's skills, for the given armor.
|
/// Get the effective armor rating, factoring in the actor's skills, for the given armor.
|
||||||
float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const override;
|
float getSkillAdjustedArmorRating(
|
||||||
|
const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor, bool useLuaInterfaceIfAvailable) const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ namespace MWClass
|
||||||
return std::make_pair(slots_, false);
|
return std::make_pair(slots_, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::RefId Clothing::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
ESM::RefId Clothing::getEquipmentSkill(const MWWorld::ConstPtr& ptr, bool useLuaInterfaceIfAvailable) const
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::Clothing>* ref = ptr.get<ESM::Clothing>();
|
const MWWorld::LiveCellRef<ESM::Clothing>* ref = ptr.get<ESM::Clothing>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace MWClass
|
||||||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||||
/// stay stacked when equipped?
|
/// stay stacked when equipped?
|
||||||
|
|
||||||
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr, bool useLuaInterfaceIfAvailable) const override;
|
||||||
|
|
||||||
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override;
|
MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override;
|
||||||
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
|
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
|
||||||
|
|
|
||||||
|
|
@ -192,12 +192,13 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
if (!isTrapped)
|
if (!isTrapped)
|
||||||
{
|
{
|
||||||
if (canBeHarvested(ptr))
|
if (!canBeHarvested(ptr))
|
||||||
{
|
|
||||||
return std::make_unique<MWWorld::ActionHarvest>(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
return std::make_unique<MWWorld::ActionOpen>(ptr);
|
||||||
|
|
||||||
|
if (hasToolTip(ptr))
|
||||||
|
return std::make_unique<MWWorld::ActionHarvest>(ptr);
|
||||||
|
|
||||||
|
return std::make_unique<MWWorld::FailedAction>(std::string_view{}, ptr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,14 @@
|
||||||
#include "../mwmechanics/setbaseaisetting.hpp"
|
#include "../mwmechanics/setbaseaisetting.hpp"
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/luamanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwlua/localscripts.hpp"
|
||||||
|
|
||||||
#include "../mwworld/actionopen.hpp"
|
#include "../mwworld/actionopen.hpp"
|
||||||
#include "../mwworld/actiontalk.hpp"
|
#include "../mwworld/actiontalk.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
@ -283,8 +286,8 @@ namespace MWClass
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
victim.getClass().onHit(
|
MWBase::Environment::get().getLuaManager()->onHit(ptr, victim, weapon, MWWorld::Ptr(), type, attackStrength,
|
||||||
victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false, MWMechanics::DamageSourceType::Melee);
|
0.0f, false, hitPosition, false, MWMechanics::DamageSourceType::Melee);
|
||||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -342,12 +345,12 @@ namespace MWClass
|
||||||
|
|
||||||
MWMechanics::diseaseContact(victim, ptr);
|
MWMechanics::diseaseContact(victim, ptr);
|
||||||
|
|
||||||
victim.getClass().onHit(
|
MWBase::Environment::get().getLuaManager()->onHit(ptr, victim, weapon, MWWorld::Ptr(), type, attackStrength,
|
||||||
victim, damage, healthdmg, weapon, ptr, hitPosition, true, MWMechanics::DamageSourceType::Melee);
|
damage, healthdmg, hitPosition, true, MWMechanics::DamageSourceType::Melee);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void Creature::onHit(const MWWorld::Ptr& ptr, const std::map<std::string, float>& damages,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
const MWWorld::Ptr& object, const MWWorld::Ptr& attacker, bool successful,
|
||||||
const MWMechanics::DamageSourceType sourceType) const
|
const MWMechanics::DamageSourceType sourceType) const
|
||||||
{
|
{
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
|
|
@ -360,9 +363,6 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
stats.setAttacked(true);
|
stats.setAttacked(true);
|
||||||
|
|
||||||
// No retaliation for totally static creatures (they have no movement or attacks anyway)
|
|
||||||
if (isMobile(ptr))
|
|
||||||
{
|
|
||||||
bool complain = sourceType == MWMechanics::DamageSourceType::Melee;
|
bool complain = sourceType == MWMechanics::DamageSourceType::Melee;
|
||||||
bool supportFriendlyFire = sourceType != MWMechanics::DamageSourceType::Ranged;
|
bool supportFriendlyFire = sourceType != MWMechanics::DamageSourceType::Ranged;
|
||||||
if (supportFriendlyFire && MWMechanics::friendlyHit(attacker, ptr, complain))
|
if (supportFriendlyFire && MWMechanics::friendlyHit(attacker, ptr, complain))
|
||||||
|
|
@ -370,7 +370,6 @@ namespace MWClass
|
||||||
else
|
else
|
||||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||||
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
||||||
|
|
@ -401,19 +400,44 @@ namespace MWClass
|
||||||
if (!successful)
|
if (!successful)
|
||||||
{
|
{
|
||||||
// Missed
|
// Missed
|
||||||
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
|
||||||
MWBase::Environment::get().getSoundManager()->playSound3D(
|
|
||||||
ptr, ESM::RefId::stringRefId("miss"), 1.0f, 1.0f);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!object.isEmpty())
|
if (!object.isEmpty())
|
||||||
stats.setLastHitObject(object.getCellRef().getRefId());
|
stats.setLastHitObject(object.getCellRef().getRefId());
|
||||||
|
|
||||||
|
bool hasDamage = false;
|
||||||
|
bool hasHealthDamage = false;
|
||||||
|
float healthDamage = 0.f;
|
||||||
|
for (auto& [stat, damage] : damages)
|
||||||
|
{
|
||||||
if (damage < 0.001f)
|
if (damage < 0.001f)
|
||||||
damage = 0;
|
continue;
|
||||||
|
hasDamage = true;
|
||||||
|
|
||||||
if (damage > 0.f)
|
if (stat == "health")
|
||||||
|
{
|
||||||
|
hasHealthDamage = true;
|
||||||
|
healthDamage = damage;
|
||||||
|
MWMechanics::DynamicStat<float> health(getCreatureStats(ptr).getHealth());
|
||||||
|
health.setCurrent(health.getCurrent() - damage);
|
||||||
|
stats.setHealth(health);
|
||||||
|
}
|
||||||
|
else if (stat == "fatigue")
|
||||||
|
{
|
||||||
|
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
|
||||||
|
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
||||||
|
stats.setFatigue(fatigue);
|
||||||
|
}
|
||||||
|
else if (stat == "magicka")
|
||||||
|
{
|
||||||
|
MWMechanics::DynamicStat<float> magicka(getCreatureStats(ptr).getMagicka());
|
||||||
|
magicka.setCurrent(magicka.getCurrent() - damage);
|
||||||
|
stats.setMagicka(magicka);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDamage)
|
||||||
{
|
{
|
||||||
if (!attacker.isEmpty())
|
if (!attacker.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -424,35 +448,11 @@ namespace MWClass
|
||||||
* getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f
|
* getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f
|
||||||
+ getGmst().iKnockDownOddsBase->mValue.getInteger();
|
+ getGmst().iKnockDownOddsBase->mValue.getInteger();
|
||||||
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99(prng))
|
if (hasHealthDamage && agilityTerm <= healthDamage && knockdownTerm <= Misc::Rng::roll0to99(prng))
|
||||||
stats.setKnockedDown(true);
|
stats.setKnockedDown(true);
|
||||||
else
|
else
|
||||||
stats.setHitRecovery(true); // Is this supposed to always occur?
|
stats.setHitRecovery(true); // Is this supposed to always occur?
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ishealth)
|
|
||||||
{
|
|
||||||
damage *= damage / (damage + getArmorRating(ptr));
|
|
||||||
damage = std::max(1.f, damage);
|
|
||||||
if (!attacker.isEmpty())
|
|
||||||
{
|
|
||||||
damage = scaleDamage(damage, attacker, ptr);
|
|
||||||
MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->playSound3D(
|
|
||||||
ptr, ESM::RefId::stringRefId("Health Damage"), 1.0f, 1.0f);
|
|
||||||
|
|
||||||
MWMechanics::DynamicStat<float> health(stats.getHealth());
|
|
||||||
health.setCurrent(health.getCurrent() - damage);
|
|
||||||
stats.setHealth(health);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MWMechanics::DynamicStat<float> fatigue(stats.getFatigue());
|
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
|
||||||
stats.setFatigue(fatigue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -534,10 +534,11 @@ namespace MWClass
|
||||||
|
|
||||||
const MWBase::World* world = MWBase::Environment::get().getWorld();
|
const MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
|
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
|
||||||
|
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||||
|
|
||||||
float moveSpeed;
|
float moveSpeed;
|
||||||
|
|
||||||
if (getEncumbrance(ptr) > getCapacity(ptr))
|
if (normalizedEncumbrance > 1.0f)
|
||||||
moveSpeed = 0.0f;
|
moveSpeed = 0.0f;
|
||||||
else if (canFly(ptr)
|
else if (canFly(ptr)
|
||||||
|| (mageffects.getOrDefault(ESM::MagicEffect::Levitate).getMagnitude() > 0 && world->isLevitationEnabled()))
|
|| (mageffects.getOrDefault(ESM::MagicEffect::Levitate).getMagnitude() > 0 && world->isLevitationEnabled()))
|
||||||
|
|
@ -547,7 +548,6 @@ namespace MWClass
|
||||||
+ mageffects.getOrDefault(ESM::MagicEffect::Levitate).getMagnitude());
|
+ mageffects.getOrDefault(ESM::MagicEffect::Levitate).getMagnitude());
|
||||||
flySpeed = gmst.fMinFlySpeed->mValue.getFloat()
|
flySpeed = gmst.fMinFlySpeed->mValue.getFloat()
|
||||||
+ flySpeed * (gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat());
|
+ flySpeed * (gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat());
|
||||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
|
||||||
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance;
|
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance;
|
||||||
flySpeed = std::max(0.0f, flySpeed);
|
flySpeed = std::max(0.0f, flySpeed);
|
||||||
moveSpeed = flySpeed;
|
moveSpeed = flySpeed;
|
||||||
|
|
@ -595,7 +595,7 @@ namespace MWClass
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Creature::getArmorRating(const MWWorld::Ptr& ptr) const
|
float Creature::getArmorRating(const MWWorld::Ptr& ptr, bool useLuaInterfaceIfAvailable) const
|
||||||
{
|
{
|
||||||
// Equipment armor rating is deliberately ignored.
|
// Equipment armor rating is deliberately ignored.
|
||||||
return getCreatureStats(ptr).getMagicEffects().getOrDefault(ESM::MagicEffect::Shield).getMagnitude();
|
return getCreatureStats(ptr).getMagicEffects().getOrDefault(ESM::MagicEffect::Shield).getMagnitude();
|
||||||
|
|
@ -768,11 +768,6 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Creature::getBloodTexture(const MWWorld::ConstPtr& ptr) const
|
|
||||||
{
|
|
||||||
return ptr.get<ESM::Creature>()->mBase->mBloodType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Creature::readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
|
void Creature::readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
|
||||||
{
|
{
|
||||||
if (!state.mHasCustomState)
|
if (!state.mHasCustomState)
|
||||||
|
|
@ -871,7 +866,8 @@ namespace MWClass
|
||||||
ptr.getRefData().setCustomData(nullptr);
|
ptr.getRefData().setCustomData(nullptr);
|
||||||
|
|
||||||
// Reset to original position
|
// Reset to original position
|
||||||
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3());
|
MWBase::Environment::get().getWorld()->moveObject(
|
||||||
|
ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3());
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(
|
MWBase::Environment::get().getWorld()->rotateObject(
|
||||||
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ namespace MWClass
|
||||||
void hit(const MWWorld::Ptr& ptr, float attackStrength, int type, const MWWorld::Ptr& victim,
|
void hit(const MWWorld::Ptr& ptr, float attackStrength, int type, const MWWorld::Ptr& victim,
|
||||||
const osg::Vec3f& hitPosition, bool success) const override;
|
const osg::Vec3f& hitPosition, bool success) const override;
|
||||||
|
|
||||||
void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void onHit(const MWWorld::Ptr& ptr, const std::map<std::string, float>& damages, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
const MWWorld::Ptr& attacker, bool successful,
|
||||||
const MWMechanics::DamageSourceType sourceType) const override;
|
const MWMechanics::DamageSourceType sourceType) const override;
|
||||||
|
|
||||||
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
|
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
|
||||||
|
|
@ -88,7 +88,7 @@ namespace MWClass
|
||||||
///< Return total weight that fits into the object. Throws an exception, if the object can't
|
///< Return total weight that fits into the object. Throws an exception, if the object can't
|
||||||
/// hold other objects.
|
/// hold other objects.
|
||||||
|
|
||||||
float getArmorRating(const MWWorld::Ptr& ptr) const override;
|
float getArmorRating(const MWWorld::Ptr& ptr, bool useLuaInterfaceIfAvailable) const override;
|
||||||
///< @return combined armor rating of this actor
|
///< @return combined armor rating of this actor
|
||||||
|
|
||||||
bool isEssential(const MWWorld::ConstPtr& ptr) const override;
|
bool isEssential(const MWWorld::ConstPtr& ptr) const override;
|
||||||
|
|
@ -118,9 +118,6 @@ namespace MWClass
|
||||||
|
|
||||||
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
||||||
|
|
||||||
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
|
|
||||||
int getBloodTexture(const MWWorld::ConstPtr& ptr) const override;
|
|
||||||
|
|
||||||
void readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;
|
void readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;
|
||||||
///< Read additional state from \a state into \a ptr.
|
///< Read additional state from \a state into \a ptr.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,16 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Figure out a better way to find markers and LOD meshes
|
||||||
|
inline bool isMarkerModel(std::string_view model)
|
||||||
|
{
|
||||||
|
return Misc::StringUtils::ciStartsWith(model, "marker");
|
||||||
|
}
|
||||||
|
inline bool isLodModel(std::string_view model)
|
||||||
|
{
|
||||||
|
return Misc::StringUtils::ciEndsWith(model, "lod.nif");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base for many ESM4 Classes
|
// Base for many ESM4 Classes
|
||||||
|
|
@ -100,11 +110,8 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
std::string_view model = getClassModel<Record>(ptr);
|
std::string_view model = getClassModel<Record>(ptr);
|
||||||
|
|
||||||
// Hide meshes meshes/marker/* and *LOD.nif in ESM4 cells. It is a temporarty hack.
|
// TODO: There should be a better way to hide markers
|
||||||
// Needed because otherwise LOD meshes are rendered on top of normal meshes.
|
if (ESM4Impl::isMarkerModel(model) || ESM4Impl::isLodModel(model))
|
||||||
// TODO: Figure out a better way find markers and LOD meshes; show LOD only outside of active grid.
|
|
||||||
if (model.empty() || Misc::StringUtils::ciStartsWith(model, "marker")
|
|
||||||
|| Misc::StringUtils::ciEndsWith(model, "lod.nif"))
|
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwlua/localscripts.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
#include "../mwmechanics/aisetting.hpp"
|
#include "../mwmechanics/aisetting.hpp"
|
||||||
#include "../mwmechanics/autocalcspell.hpp"
|
#include "../mwmechanics/autocalcspell.hpp"
|
||||||
|
|
@ -620,8 +622,8 @@ namespace MWClass
|
||||||
float damage = 0.0f;
|
float damage = 0.0f;
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
othercls.onHit(
|
MWBase::Environment::get().getLuaManager()->onHit(ptr, victim, weapon, MWWorld::Ptr(), type, attackStrength,
|
||||||
victim, damage, false, weapon, ptr, osg::Vec3f(), false, MWMechanics::DamageSourceType::Melee);
|
damage, false, hitPosition, false, MWMechanics::DamageSourceType::Melee);
|
||||||
MWMechanics::reduceWeaponCondition(damage, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(damage, false, weapon, ptr);
|
||||||
MWMechanics::resistNormalWeapon(victim, ptr, weapon, damage);
|
MWMechanics::resistNormalWeapon(victim, ptr, weapon, damage);
|
||||||
return;
|
return;
|
||||||
|
|
@ -694,14 +696,13 @@ namespace MWClass
|
||||||
|
|
||||||
MWMechanics::diseaseContact(victim, ptr);
|
MWMechanics::diseaseContact(victim, ptr);
|
||||||
|
|
||||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, MWMechanics::DamageSourceType::Melee);
|
MWBase::Environment::get().getLuaManager()->onHit(ptr, victim, weapon, MWWorld::Ptr(), type, attackStrength,
|
||||||
|
damage, healthdmg, hitPosition, true, MWMechanics::DamageSourceType::Melee);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Npc::onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void Npc::onHit(const MWWorld::Ptr& ptr, const std::map<std::string, float>& damages, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
const MWWorld::Ptr& attacker, bool successful, const MWMechanics::DamageSourceType sourceType) const
|
||||||
const MWMechanics::DamageSourceType sourceType) const
|
|
||||||
{
|
{
|
||||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
bool wasDead = stats.isDead();
|
bool wasDead = stats.isDead();
|
||||||
|
|
||||||
|
|
@ -748,23 +749,47 @@ namespace MWClass
|
||||||
if (!successful)
|
if (!successful)
|
||||||
{
|
{
|
||||||
// Missed
|
// Missed
|
||||||
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("miss"), 1.0f, 1.0f);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!object.isEmpty())
|
if (!object.isEmpty())
|
||||||
stats.setLastHitObject(object.getCellRef().getRefId());
|
stats.setLastHitObject(object.getCellRef().getRefId());
|
||||||
|
|
||||||
|
if (ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool hasDamage = false;
|
||||||
|
bool hasHealthDamage = false;
|
||||||
|
float healthDamage = 0.f;
|
||||||
|
for (auto& [stat, damage] : damages)
|
||||||
|
{
|
||||||
if (damage < 0.001f)
|
if (damage < 0.001f)
|
||||||
damage = 0;
|
continue;
|
||||||
|
hasDamage = true;
|
||||||
|
|
||||||
bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
if (stat == "health")
|
||||||
|
{
|
||||||
|
hasHealthDamage = true;
|
||||||
|
healthDamage = damage;
|
||||||
|
MWMechanics::DynamicStat<float> health(getCreatureStats(ptr).getHealth());
|
||||||
|
health.setCurrent(health.getCurrent() - damage);
|
||||||
|
stats.setHealth(health);
|
||||||
|
}
|
||||||
|
else if (stat == "fatigue")
|
||||||
|
{
|
||||||
|
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
|
||||||
|
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
||||||
|
stats.setFatigue(fatigue);
|
||||||
|
}
|
||||||
|
else if (stat == "magicka")
|
||||||
|
{
|
||||||
|
MWMechanics::DynamicStat<float> magicka(getCreatureStats(ptr).getMagicka());
|
||||||
|
magicka.setCurrent(magicka.getCurrent() - damage);
|
||||||
|
stats.setMagicka(magicka);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (godmode)
|
if (hasDamage && !attacker.isEmpty())
|
||||||
damage = 0;
|
|
||||||
|
|
||||||
if (damage > 0.0f && !attacker.isEmpty())
|
|
||||||
{
|
{
|
||||||
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
|
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
|
||||||
// something, alert the character controller, scripts, etc.
|
// something, alert the character controller, scripts, etc.
|
||||||
|
|
@ -783,109 +808,16 @@ namespace MWClass
|
||||||
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
||||||
* gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f
|
* gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f
|
||||||
+ gmst.iKnockDownOddsBase->mValue.getInteger();
|
+ gmst.iKnockDownOddsBase->mValue.getInteger();
|
||||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99(prng))
|
if (hasHealthDamage && agilityTerm <= healthDamage && knockdownTerm <= Misc::Rng::roll0to99(prng))
|
||||||
stats.setKnockedDown(true);
|
stats.setKnockedDown(true);
|
||||||
else
|
else
|
||||||
stats.setHitRecovery(true); // Is this supposed to always occur?
|
stats.setHitRecovery(true); // Is this supposed to always occur?
|
||||||
|
|
||||||
if (damage > 0 && ishealth)
|
|
||||||
{
|
|
||||||
// Hit percentages:
|
|
||||||
// cuirass = 30%
|
|
||||||
// shield, helmet, greaves, boots, pauldrons = 10% each
|
|
||||||
// guantlets = 5% each
|
|
||||||
static const int hitslots[20]
|
|
||||||
= { MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
|
|
||||||
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
|
|
||||||
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
|
|
||||||
MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft,
|
|
||||||
MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet,
|
|
||||||
MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves,
|
|
||||||
MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots,
|
|
||||||
MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron,
|
|
||||||
MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron,
|
|
||||||
MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet };
|
|
||||||
int hitslot = hitslots[Misc::Rng::rollDice(20, prng)];
|
|
||||||
|
|
||||||
float unmitigatedDamage = damage;
|
|
||||||
float x = damage / (damage + getArmorRating(ptr));
|
|
||||||
damage *= std::max(gmst.fCombatArmorMinMult->mValue.getFloat(), x);
|
|
||||||
int damageDiff = static_cast<int>(unmitigatedDamage - damage);
|
|
||||||
damage = std::max(1.f, damage);
|
|
||||||
damageDiff = std::max(1, damageDiff);
|
|
||||||
|
|
||||||
MWWorld::InventoryStore& inv = getInventoryStore(ptr);
|
|
||||||
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
|
|
||||||
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
|
|
||||||
bool hasArmor = !armor.isEmpty() && armor.getType() == ESM::Armor::sRecordId;
|
|
||||||
// If there's no item in the carried left slot or if it is not a shield redistribute the hit.
|
|
||||||
if (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft)
|
|
||||||
{
|
|
||||||
if (Misc::Rng::rollDice(2, prng) == 0)
|
|
||||||
hitslot = MWWorld::InventoryStore::Slot_Cuirass;
|
|
||||||
else
|
|
||||||
hitslot = MWWorld::InventoryStore::Slot_LeftPauldron;
|
|
||||||
armorslot = inv.getSlot(hitslot);
|
|
||||||
if (armorslot != inv.end())
|
|
||||||
{
|
|
||||||
armor = *armorslot;
|
|
||||||
hasArmor = !armor.isEmpty() && armor.getType() == ESM::Armor::sRecordId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasArmor)
|
|
||||||
{
|
|
||||||
// Unarmed creature attacks don't affect armor condition unless it was
|
|
||||||
// explicitly requested.
|
|
||||||
if (!object.isEmpty() || attacker.isEmpty() || attacker.getClass().isNpc()
|
|
||||||
|| Settings::game().mUnarmedCreatureAttacksDamageArmor)
|
|
||||||
{
|
|
||||||
int armorhealth = armor.getClass().getItemHealth(armor);
|
|
||||||
armorhealth -= std::min(damageDiff, armorhealth);
|
|
||||||
armor.getCellRef().setCharge(armorhealth);
|
|
||||||
|
|
||||||
// Armor broken? unequip it
|
|
||||||
if (armorhealth == 0)
|
|
||||||
armor = *inv.unequipItem(armor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::RefId skill = armor.getClass().getEquipmentSkill(armor);
|
if (hasHealthDamage && healthDamage > 0.0f)
|
||||||
if (ptr == MWMechanics::getPlayer())
|
|
||||||
skillUsageSucceeded(ptr, skill, ESM::Skill::Armor_HitByOpponent);
|
|
||||||
|
|
||||||
if (skill == ESM::Skill::LightArmor)
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f);
|
|
||||||
else if (skill == ESM::Skill::MediumArmor)
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f);
|
|
||||||
else if (skill == ESM::Skill::HeavyArmor)
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
else if (ptr == MWMechanics::getPlayer())
|
|
||||||
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, ESM::Skill::Armor_HitByOpponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ishealth)
|
|
||||||
{
|
{
|
||||||
if (!attacker.isEmpty() && !godmode)
|
|
||||||
damage = scaleDamage(damage, attacker, ptr);
|
|
||||||
|
|
||||||
if (damage > 0.0f)
|
|
||||||
{
|
|
||||||
sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Health Damage"), 1.0f, 1.0f);
|
|
||||||
if (ptr == MWMechanics::getPlayer())
|
if (ptr == MWMechanics::getPlayer())
|
||||||
MWBase::Environment::get().getWindowManager()->activateHitOverlay();
|
MWBase::Environment::get().getWindowManager()->activateHitOverlay();
|
||||||
if (!attacker.isEmpty())
|
|
||||||
MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition);
|
|
||||||
}
|
|
||||||
MWMechanics::DynamicStat<float> health(getCreatureStats(ptr).getHealth());
|
|
||||||
health.setCurrent(health.getCurrent() - damage);
|
|
||||||
stats.setHealth(health);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
|
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
|
|
||||||
stats.setFatigue(fatigue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wasDead && getCreatureStats(ptr).isDead())
|
if (!wasDead && getCreatureStats(ptr).isDead())
|
||||||
|
|
@ -990,15 +922,10 @@ namespace MWClass
|
||||||
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
|
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
|
||||||
|
|
||||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||||
|
const bool running = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr);
|
||||||
bool swimming = world->isSwimming(ptr);
|
|
||||||
bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr);
|
|
||||||
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
|
|
||||||
bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
|
|
||||||
running = running && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
|
|
||||||
|
|
||||||
float moveSpeed;
|
float moveSpeed;
|
||||||
if (getEncumbrance(ptr) > getCapacity(ptr))
|
if (normalizedEncumbrance > 1.0f)
|
||||||
moveSpeed = 0.0f;
|
moveSpeed = 0.0f;
|
||||||
else if (mageffects.getOrDefault(ESM::MagicEffect::Levitate).getMagnitude() > 0 && world->isLevitationEnabled())
|
else if (mageffects.getOrDefault(ESM::MagicEffect::Levitate).getMagnitude() > 0 && world->isLevitationEnabled())
|
||||||
{
|
{
|
||||||
|
|
@ -1011,9 +938,9 @@ namespace MWClass
|
||||||
flySpeed = std::max(0.0f, flySpeed);
|
flySpeed = std::max(0.0f, flySpeed);
|
||||||
moveSpeed = flySpeed;
|
moveSpeed = flySpeed;
|
||||||
}
|
}
|
||||||
else if (swimming)
|
else if (world->isSwimming(ptr))
|
||||||
moveSpeed = getSwimSpeed(ptr);
|
moveSpeed = getSwimSpeed(ptr);
|
||||||
else if (running && !sneaking)
|
else if (running && !MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr))
|
||||||
moveSpeed = getRunSpeed(ptr);
|
moveSpeed = getRunSpeed(ptr);
|
||||||
else
|
else
|
||||||
moveSpeed = getWalkSpeed(ptr);
|
moveSpeed = getWalkSpeed(ptr);
|
||||||
|
|
@ -1141,8 +1068,17 @@ namespace MWClass
|
||||||
MWBase::Environment::get().getLuaManager()->skillUse(ptr, skill, usageType, extraFactor);
|
MWBase::Environment::get().getLuaManager()->skillUse(ptr, skill, usageType, extraFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Npc::getArmorRating(const MWWorld::Ptr& ptr) const
|
float Npc::getArmorRating(const MWWorld::Ptr& ptr, bool useLuaInterfaceIfAvailable) const
|
||||||
{
|
{
|
||||||
|
if (useLuaInterfaceIfAvailable && ptr == MWMechanics::getPlayer())
|
||||||
|
{
|
||||||
|
auto res = MWLua::LocalScripts::callPlayerInterface<float>("Combat", "getArmorRating");
|
||||||
|
if (res)
|
||||||
|
return res.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to the old engine implementation when actors don't have their scripts attached yet.
|
||||||
|
|
||||||
const MWWorld::Store<ESM::GameSetting>& store
|
const MWWorld::Store<ESM::GameSetting>& store
|
||||||
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||||
|
|
||||||
|
|
@ -1164,7 +1100,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ratings[i] = it->getClass().getEffectiveArmorRating(*it, ptr);
|
ratings[i] = it->getClass().getSkillAdjustedArmorRating(*it, ptr);
|
||||||
|
|
||||||
// Take in account armor condition
|
// Take in account armor condition
|
||||||
const bool hasHealth = it->getClass().hasItemHealth(*it);
|
const bool hasHealth = it->getClass().hasItemHealth(*it);
|
||||||
|
|
@ -1313,11 +1249,6 @@ namespace MWClass
|
||||||
return getNpcStats(ptr).getSkill(id).getModified();
|
return getNpcStats(ptr).getSkill(id).getModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Npc::getBloodTexture(const MWWorld::ConstPtr& ptr) const
|
|
||||||
{
|
|
||||||
return ptr.get<ESM::NPC>()->mBase->mBloodType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Npc::readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
|
void Npc::readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
|
||||||
{
|
{
|
||||||
if (!state.mHasCustomState)
|
if (!state.mHasCustomState)
|
||||||
|
|
@ -1427,7 +1358,8 @@ namespace MWClass
|
||||||
ptr.getRefData().setCustomData(nullptr);
|
ptr.getRefData().setCustomData(nullptr);
|
||||||
|
|
||||||
// Reset to original position
|
// Reset to original position
|
||||||
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().asVec3());
|
MWBase::Environment::get().getWorld()->moveObject(
|
||||||
|
ptr, ptr.getCell()->getOriginCell(ptr), ptr.getCellRef().getPosition().asVec3());
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(
|
MWBase::Environment::get().getWorld()->rotateObject(
|
||||||
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
ptr, ptr.getCellRef().getPosition().asRotationVec3(), MWBase::RotationFlag_none);
|
||||||
}
|
}
|
||||||
|
|
@ -1508,14 +1440,8 @@ namespace MWClass
|
||||||
|
|
||||||
float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const
|
float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
const MWBase::World* world = MWBase::Environment::get().getWorld();
|
const MWMechanics::MagicEffects& effects = getNpcStats(ptr).getMagicEffects();
|
||||||
const MWMechanics::NpcStats& stats = getNpcStats(ptr);
|
const bool running = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr);
|
||||||
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
|
return getSwimSpeedImpl(ptr, getGmst(), effects, running ? getRunSpeed(ptr) : getWalkSpeed(ptr));
|
||||||
const bool swimming = world->isSwimming(ptr);
|
|
||||||
const bool inair = !world->isOnGround(ptr) && !swimming && !world->isFlying(ptr);
|
|
||||||
const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run)
|
|
||||||
&& (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
|
|
||||||
|
|
||||||
return getSwimSpeedImpl(ptr, getGmst(), mageffects, running ? getRunSpeed(ptr) : getWalkSpeed(ptr));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ namespace MWClass
|
||||||
void hit(const MWWorld::Ptr& ptr, float attackStrength, int type, const MWWorld::Ptr& victim,
|
void hit(const MWWorld::Ptr& ptr, float attackStrength, int type, const MWWorld::Ptr& victim,
|
||||||
const osg::Vec3f& hitPosition, bool success) const override;
|
const osg::Vec3f& hitPosition, bool success) const override;
|
||||||
|
|
||||||
void onHit(const MWWorld::Ptr& ptr, float damage, bool ishealth, const MWWorld::Ptr& object,
|
void onHit(const MWWorld::Ptr& ptr, const std::map<std::string, float>& damages, const MWWorld::Ptr& object,
|
||||||
const MWWorld::Ptr& attacker, const osg::Vec3f& hitPosition, bool successful,
|
const MWWorld::Ptr& attacker, bool successful,
|
||||||
const MWMechanics::DamageSourceType sourceType) const override;
|
const MWMechanics::DamageSourceType sourceType) const override;
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const override;
|
void getModelsToPreload(const MWWorld::ConstPtr& ptr, std::vector<std::string_view>& models) const override;
|
||||||
|
|
@ -112,7 +112,7 @@ namespace MWClass
|
||||||
///< Returns total weight of objects inside this object (including modifications from magic
|
///< Returns total weight of objects inside this object (including modifications from magic
|
||||||
/// effects). Throws an exception, if the object can't hold other objects.
|
/// effects). Throws an exception, if the object can't hold other objects.
|
||||||
|
|
||||||
float getArmorRating(const MWWorld::Ptr& ptr) const override;
|
float getArmorRating(const MWWorld::Ptr& ptr, bool useLuaInterfaceIfAvailable) const override;
|
||||||
///< @return combined armor rating of this actor
|
///< @return combined armor rating of this actor
|
||||||
|
|
||||||
void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const override;
|
void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const override;
|
||||||
|
|
@ -137,9 +137,6 @@ namespace MWClass
|
||||||
|
|
||||||
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override;
|
||||||
|
|
||||||
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
|
|
||||||
int getBloodTexture(const MWWorld::ConstPtr& ptr) const override;
|
|
||||||
|
|
||||||
bool isNpc() const override { return true; }
|
bool isNpc() const override { return true; }
|
||||||
|
|
||||||
void readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;
|
void readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const override;
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ namespace MWClass
|
||||||
return std::make_pair(slots_, stack);
|
return std::make_pair(slots_, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::RefId Weapon::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const
|
ESM::RefId Weapon::getEquipmentSkill(const MWWorld::ConstPtr& ptr, bool useLuaInterfaceIfAvailable) const
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::Weapon>* ref = ptr.get<ESM::Weapon>();
|
const MWWorld::LiveCellRef<ESM::Weapon>* ref = ptr.get<ESM::Weapon>();
|
||||||
int type = ref->mBase->mData.mType;
|
int type = ref->mBase->mData.mType;
|
||||||
|
|
@ -270,14 +270,14 @@ namespace MWClass
|
||||||
|
|
||||||
std::pair<int, std::string_view> Weapon::canBeEquipped(const MWWorld::ConstPtr& ptr, const MWWorld::Ptr& npc) const
|
std::pair<int, std::string_view> Weapon::canBeEquipped(const MWWorld::ConstPtr& ptr, const MWWorld::Ptr& npc) const
|
||||||
{
|
{
|
||||||
if (hasItemHealth(ptr) && getItemHealth(ptr) == 0)
|
|
||||||
return { 0, "#{sInventoryMessage1}" };
|
|
||||||
|
|
||||||
// Do not allow equip weapons from inventory during attack
|
// Do not allow equip weapons from inventory during attack
|
||||||
if (npc.isInCell() && MWBase::Environment::get().getWindowManager()->isGuiMode()
|
if (npc.isInCell() && MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
|
&& MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
|
||||||
return { 0, "#{sCantEquipWeapWarning}" };
|
return { 0, "#{sCantEquipWeapWarning}" };
|
||||||
|
|
||||||
|
if (hasItemHealth(ptr) && getItemHealth(ptr) == 0)
|
||||||
|
return { 0, "#{sInventoryMessage1}" };
|
||||||
|
|
||||||
std::pair<std::vector<int>, bool> slots_ = getEquipmentSlots(ptr);
|
std::pair<std::vector<int>, bool> slots_ = getEquipmentSlots(ptr);
|
||||||
|
|
||||||
if (slots_.first.empty())
|
if (slots_.first.empty())
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ namespace MWClass
|
||||||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||||
/// stay stacked when equipped?
|
/// stay stacked when equipped?
|
||||||
|
|
||||||
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override;
|
ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr, bool useLuaInterfaceIfAvailable) const override;
|
||||||
|
|
||||||
int getValue(const MWWorld::ConstPtr& ptr) const override;
|
int getValue(const MWWorld::ConstPtr& ptr) const override;
|
||||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <components/esm3/loaddial.hpp>
|
#include <components/esm3/loaddial.hpp>
|
||||||
#include <components/esm3/loadfact.hpp>
|
#include <components/esm3/loadfact.hpp>
|
||||||
#include <components/esm3/loadinfo.hpp>
|
#include <components/esm3/loadinfo.hpp>
|
||||||
|
#include <components/esm3/loadmgef.hpp>
|
||||||
|
|
||||||
#include <components/compiler/errorhandler.hpp>
|
#include <components/compiler/errorhandler.hpp>
|
||||||
#include <components/compiler/exception.hpp>
|
#include <components/compiler/exception.hpp>
|
||||||
|
|
@ -448,12 +449,14 @@ namespace MWDialogue
|
||||||
{
|
{
|
||||||
updateOriginalDisposition();
|
updateOriginalDisposition();
|
||||||
MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor);
|
MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor);
|
||||||
// Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with
|
|
||||||
// intimidate)
|
|
||||||
npcStats.setBaseDisposition(0);
|
|
||||||
int zero = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false);
|
|
||||||
int disposition = std::clamp(mOriginalDisposition + mPermanentDispositionChange, -zero, 100 - zero);
|
|
||||||
|
|
||||||
|
// Get the sum of disposition effects minus charm (shouldn't be made permanent)
|
||||||
|
npcStats.setBaseDisposition(0);
|
||||||
|
int zero = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false)
|
||||||
|
- npcStats.getMagicEffects().getOrDefault(ESM::MagicEffect::Charm).getMagnitude();
|
||||||
|
|
||||||
|
// Clamp new permanent disposition to avoid negative derived disposition (can be caused by intimidate)
|
||||||
|
int disposition = std::clamp(mOriginalDisposition + mPermanentDispositionChange, -zero, 100 - zero);
|
||||||
npcStats.setBaseDisposition(disposition);
|
npcStats.setBaseDisposition(disposition);
|
||||||
}
|
}
|
||||||
mPermanentDispositionChange = 0;
|
mPermanentDispositionChange = 0;
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
|
||||||
|
|
||||||
case ESM::DialogueCondition::Function_Weather:
|
case ESM::DialogueCondition::Function_Weather:
|
||||||
|
|
||||||
return MWBase::Environment::get().getWorld()->getCurrentWeather();
|
return MWBase::Environment::get().getWorld()->getCurrentWeatherScriptId();
|
||||||
|
|
||||||
case ESM::DialogueCondition::Function_Reputation:
|
case ESM::DialogueCondition::Function_Reputation:
|
||||||
if (!mActor.getClass().isNpc())
|
if (!mActor.getClass().isNpc())
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "companionitemmodel.hpp"
|
#include "companionitemmodel.hpp"
|
||||||
#include "countdialog.hpp"
|
#include "countdialog.hpp"
|
||||||
#include "draganddrop.hpp"
|
#include "draganddrop.hpp"
|
||||||
|
#include "itemtransfer.hpp"
|
||||||
#include "itemview.hpp"
|
#include "itemview.hpp"
|
||||||
#include "messagebox.hpp"
|
#include "messagebox.hpp"
|
||||||
#include "sortfilteritemmodel.hpp"
|
#include "sortfilteritemmodel.hpp"
|
||||||
|
|
@ -38,12 +39,14 @@ namespace
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
CompanionWindow::CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager)
|
CompanionWindow::CompanionWindow(DragAndDrop& dragAndDrop, ItemTransfer& itemTransfer, MessageBoxManager* manager)
|
||||||
: WindowBase("openmw_companion_window.layout")
|
: WindowBase("openmw_companion_window.layout")
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
, mModel(nullptr)
|
, mModel(nullptr)
|
||||||
, mSelectedItem(-1)
|
, mSelectedItem(-1)
|
||||||
, mDragAndDrop(dragAndDrop)
|
, mUpdateNextFrame(false)
|
||||||
|
, mDragAndDrop(&dragAndDrop)
|
||||||
|
, mItemTransfer(&itemTransfer)
|
||||||
, mMessageBoxManager(manager)
|
, mMessageBoxManager(manager)
|
||||||
{
|
{
|
||||||
getWidget(mCloseButton, "CloseButton");
|
getWidget(mCloseButton, "CloseButton");
|
||||||
|
|
@ -93,8 +96,14 @@ namespace MWGui
|
||||||
name += MWGui::ToolTips::getSoulString(object.getCellRef());
|
name += MWGui::ToolTips::getSoulString(object.getCellRef());
|
||||||
dialog->openCountDialog(name, "#{sTake}", count);
|
dialog->openCountDialog(name, "#{sTake}", count);
|
||||||
dialog->eventOkClicked.clear();
|
dialog->eventOkClicked.clear();
|
||||||
|
|
||||||
|
if (MyGUI::InputManager::getInstance().isAltPressed())
|
||||||
|
dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::transferItem);
|
||||||
|
else
|
||||||
dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem);
|
dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem);
|
||||||
}
|
}
|
||||||
|
else if (MyGUI::InputManager::getInstance().isAltPressed())
|
||||||
|
transferItem(nullptr, count);
|
||||||
else
|
else
|
||||||
dragItem(nullptr, count);
|
dragItem(nullptr, count);
|
||||||
}
|
}
|
||||||
|
|
@ -105,11 +114,16 @@ namespace MWGui
|
||||||
mItemView->update();
|
mItemView->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompanionWindow::dragItem(MyGUI::Widget* sender, int count)
|
void CompanionWindow::dragItem(MyGUI::Widget* /*sender*/, std::size_t count)
|
||||||
{
|
{
|
||||||
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompanionWindow::transferItem(MyGUI::Widget* /*sender*/, std::size_t count)
|
||||||
|
{
|
||||||
|
mItemTransfer->apply(mModel->getItem(mSelectedItem), count, *mItemView);
|
||||||
|
}
|
||||||
|
|
||||||
void CompanionWindow::onBackgroundSelected()
|
void CompanionWindow::onBackgroundSelected()
|
||||||
{
|
{
|
||||||
if (mDragAndDrop->mIsOnDragAndDrop)
|
if (mDragAndDrop->mIsOnDragAndDrop)
|
||||||
|
|
@ -134,12 +148,20 @@ namespace MWGui
|
||||||
mItemView->resetScrollBars();
|
mItemView->resetScrollBars();
|
||||||
|
|
||||||
setTitle(actor.getClass().getName(actor));
|
setTitle(actor.getClass().getName(actor));
|
||||||
|
|
||||||
|
mPtr.getClass().getContainerStore(mPtr).setContListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompanionWindow::onFrame(float dt)
|
void CompanionWindow::onFrame(float dt)
|
||||||
{
|
{
|
||||||
checkReferenceAvailable();
|
checkReferenceAvailable();
|
||||||
|
|
||||||
|
if (mUpdateNextFrame)
|
||||||
|
{
|
||||||
updateEncumbranceBar();
|
updateEncumbranceBar();
|
||||||
|
mItemView->update();
|
||||||
|
mUpdateNextFrame = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompanionWindow::updateEncumbranceBar()
|
void CompanionWindow::updateEncumbranceBar()
|
||||||
|
|
@ -202,4 +224,23 @@ namespace MWGui
|
||||||
mSortModel = nullptr;
|
mSortModel = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompanionWindow::itemAdded(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompanionWindow::itemRemoved(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompanionWindow::onOpen()
|
||||||
|
{
|
||||||
|
mItemTransfer->addTarget(*mItemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompanionWindow::onClose()
|
||||||
|
{
|
||||||
|
mItemTransfer->removeTarget(*mItemView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
#include "referenceinterface.hpp"
|
#include "referenceinterface.hpp"
|
||||||
#include "windowbase.hpp"
|
#include "windowbase.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/notnullptr.hpp>
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
namespace Widgets
|
namespace Widgets
|
||||||
|
|
@ -16,11 +20,12 @@ namespace MWGui
|
||||||
class DragAndDrop;
|
class DragAndDrop;
|
||||||
class SortFilterItemModel;
|
class SortFilterItemModel;
|
||||||
class CompanionItemModel;
|
class CompanionItemModel;
|
||||||
|
class ItemTransfer;
|
||||||
|
|
||||||
class CompanionWindow : public WindowBase, public ReferenceInterface
|
class CompanionWindow : public WindowBase, public ReferenceInterface, public MWWorld::ContainerStoreListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CompanionWindow(DragAndDrop* dragAndDrop, MessageBoxManager* manager);
|
explicit CompanionWindow(DragAndDrop& dragAndDrop, ItemTransfer& itemTransfer, MessageBoxManager* manager);
|
||||||
|
|
||||||
bool exit() override;
|
bool exit() override;
|
||||||
|
|
||||||
|
|
@ -30,6 +35,9 @@ namespace MWGui
|
||||||
void onFrame(float dt) override;
|
void onFrame(float dt) override;
|
||||||
void clear() override { resetReference(); }
|
void clear() override { resetReference(); }
|
||||||
|
|
||||||
|
void itemAdded(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
void itemRemoved(const MWWorld::ConstPtr& item, int count) override;
|
||||||
|
|
||||||
std::string_view getWindowIdForLua() const override { return "Companion"; }
|
std::string_view getWindowIdForLua() const override { return "Companion"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -37,8 +45,10 @@ namespace MWGui
|
||||||
SortFilterItemModel* mSortModel;
|
SortFilterItemModel* mSortModel;
|
||||||
CompanionItemModel* mModel;
|
CompanionItemModel* mModel;
|
||||||
int mSelectedItem;
|
int mSelectedItem;
|
||||||
|
bool mUpdateNextFrame;
|
||||||
|
|
||||||
DragAndDrop* mDragAndDrop;
|
Misc::NotNullPtr<DragAndDrop> mDragAndDrop;
|
||||||
|
Misc::NotNullPtr<ItemTransfer> mItemTransfer;
|
||||||
|
|
||||||
MyGUI::Button* mCloseButton;
|
MyGUI::Button* mCloseButton;
|
||||||
MyGUI::EditBox* mFilterEdit;
|
MyGUI::EditBox* mFilterEdit;
|
||||||
|
|
@ -49,7 +59,8 @@ namespace MWGui
|
||||||
void onItemSelected(int index);
|
void onItemSelected(int index);
|
||||||
void onNameFilterChanged(MyGUI::EditBox* _sender);
|
void onNameFilterChanged(MyGUI::EditBox* _sender);
|
||||||
void onBackgroundSelected();
|
void onBackgroundSelected();
|
||||||
void dragItem(MyGUI::Widget* sender, int count);
|
void dragItem(MyGUI::Widget* sender, std::size_t count);
|
||||||
|
void transferItem(MyGUI::Widget* sender, std::size_t count);
|
||||||
|
|
||||||
void onMessageBoxButtonClicked(int button);
|
void onMessageBoxButtonClicked(int button);
|
||||||
|
|
||||||
|
|
@ -58,6 +69,10 @@ namespace MWGui
|
||||||
void onCloseButtonClicked(MyGUI::Widget* _sender);
|
void onCloseButtonClicked(MyGUI::Widget* _sender);
|
||||||
|
|
||||||
void onReferenceUnavailable() override;
|
void onReferenceUnavailable() override;
|
||||||
|
|
||||||
|
void onOpen() override;
|
||||||
|
|
||||||
|
void onClose() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
|
|
||||||
#include "../mwscript/interpretercontext.hpp"
|
#include "../mwscript/interpretercontext.hpp"
|
||||||
|
|
||||||
#include "countdialog.hpp"
|
|
||||||
#include "inventorywindow.hpp"
|
|
||||||
|
|
||||||
#include "containeritemmodel.hpp"
|
#include "containeritemmodel.hpp"
|
||||||
|
#include "countdialog.hpp"
|
||||||
#include "draganddrop.hpp"
|
#include "draganddrop.hpp"
|
||||||
#include "inventoryitemmodel.hpp"
|
#include "inventoryitemmodel.hpp"
|
||||||
|
#include "inventorywindow.hpp"
|
||||||
|
#include "itemtransfer.hpp"
|
||||||
#include "itemview.hpp"
|
#include "itemview.hpp"
|
||||||
#include "pickpocketitemmodel.hpp"
|
#include "pickpocketitemmodel.hpp"
|
||||||
#include "sortfilteritemmodel.hpp"
|
#include "sortfilteritemmodel.hpp"
|
||||||
|
|
@ -32,12 +32,14 @@
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop)
|
ContainerWindow::ContainerWindow(DragAndDrop& dragAndDrop, ItemTransfer& itemTransfer)
|
||||||
: WindowBase("openmw_container_window.layout")
|
: WindowBase("openmw_container_window.layout")
|
||||||
, mDragAndDrop(dragAndDrop)
|
, mDragAndDrop(&dragAndDrop)
|
||||||
|
, mItemTransfer(&itemTransfer)
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
, mModel(nullptr)
|
, mModel(nullptr)
|
||||||
, mSelectedItem(-1)
|
, mSelectedItem(-1)
|
||||||
|
, mUpdateNextFrame(false)
|
||||||
, mTreatNextOpenAsLoot(false)
|
, mTreatNextOpenAsLoot(false)
|
||||||
{
|
{
|
||||||
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
||||||
|
|
@ -88,26 +90,47 @@ namespace MWGui
|
||||||
name += MWGui::ToolTips::getSoulString(object.getCellRef());
|
name += MWGui::ToolTips::getSoulString(object.getCellRef());
|
||||||
dialog->openCountDialog(name, "#{sTake}", count);
|
dialog->openCountDialog(name, "#{sTake}", count);
|
||||||
dialog->eventOkClicked.clear();
|
dialog->eventOkClicked.clear();
|
||||||
|
|
||||||
|
if (MyGUI::InputManager::getInstance().isAltPressed())
|
||||||
|
dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::transferItem);
|
||||||
|
else
|
||||||
dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem);
|
dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem);
|
||||||
}
|
}
|
||||||
|
else if (MyGUI::InputManager::getInstance().isAltPressed())
|
||||||
|
transferItem(nullptr, count);
|
||||||
else
|
else
|
||||||
dragItem(nullptr, count);
|
dragItem(nullptr, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
|
void ContainerWindow::dragItem(MyGUI::Widget* /*sender*/, std::size_t count)
|
||||||
{
|
{
|
||||||
if (!mModel)
|
if (mModel == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!onTakeItem(mModel->getItem(mSelectedItem), count))
|
const ItemStack item = mModel->getItem(mSelectedItem);
|
||||||
|
|
||||||
|
if (!mModel->onTakeItem(item.mBase, count))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::transferItem(MyGUI::Widget* /*sender*/, std::size_t count)
|
||||||
|
{
|
||||||
|
if (mModel == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ItemStack item = mModel->getItem(mSelectedItem);
|
||||||
|
|
||||||
|
if (!mModel->onTakeItem(item.mBase, count))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mItemTransfer->apply(item, count, *mItemView);
|
||||||
|
}
|
||||||
|
|
||||||
void ContainerWindow::dropItem()
|
void ContainerWindow::dropItem()
|
||||||
{
|
{
|
||||||
if (!mModel)
|
if (mModel == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);
|
bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);
|
||||||
|
|
@ -160,6 +183,8 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
|
||||||
|
|
||||||
setTitle(container.getClass().getName(container));
|
setTitle(container.getClass().getName(container));
|
||||||
|
|
||||||
|
mPtr.getClass().getContainerStore(mPtr).setContListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainerWindow::resetReference()
|
void ContainerWindow::resetReference()
|
||||||
|
|
@ -170,10 +195,13 @@ namespace MWGui
|
||||||
mSortModel = nullptr;
|
mSortModel = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::onOpen()
|
||||||
|
{
|
||||||
|
mItemTransfer->addTarget(*mItemView);
|
||||||
|
}
|
||||||
|
|
||||||
void ContainerWindow::onClose()
|
void ContainerWindow::onClose()
|
||||||
{
|
{
|
||||||
WindowBase::onClose();
|
|
||||||
|
|
||||||
// Make sure the window was actually closed and not temporarily hidden.
|
// Make sure the window was actually closed and not temporarily hidden.
|
||||||
if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Container))
|
if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Container))
|
||||||
return;
|
return;
|
||||||
|
|
@ -184,6 +212,8 @@ namespace MWGui
|
||||||
if (!mPtr.isEmpty())
|
if (!mPtr.isEmpty())
|
||||||
MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);
|
MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);
|
||||||
resetReference();
|
resetReference();
|
||||||
|
|
||||||
|
mItemTransfer->removeTarget(*mItemView);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
||||||
|
|
@ -231,9 +261,9 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->playSound(sound);
|
MWBase::Environment::get().getWindowManager()->playSound(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ItemStack& item = mModel->getItem(i);
|
const ItemStack item = mModel->getItem(i);
|
||||||
|
|
||||||
if (!onTakeItem(item, item.mCount))
|
if (!mModel->onTakeItem(item.mBase, item.mCount))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
mModel->moveItem(item, item.mCount, playerModel);
|
mModel->moveItem(item, item.mCount, playerModel);
|
||||||
|
|
@ -310,14 +340,30 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContainerWindow::onTakeItem(const ItemStack& item, int count)
|
|
||||||
{
|
|
||||||
return mModel->onTakeItem(item.mBase, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContainerWindow::onDeleteCustomData(const MWWorld::Ptr& ptr)
|
void ContainerWindow::onDeleteCustomData(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
if (mModel && mModel->usesContainer(ptr))
|
if (mModel && mModel->usesContainer(ptr))
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::onFrame(float dt)
|
||||||
|
{
|
||||||
|
checkReferenceAvailable();
|
||||||
|
|
||||||
|
if (mUpdateNextFrame)
|
||||||
|
{
|
||||||
|
mItemView->update();
|
||||||
|
mUpdateNextFrame = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::itemAdded(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::itemRemoved(const MWWorld::ConstPtr& item, int count)
|
||||||
|
{
|
||||||
|
mUpdateNextFrame = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue