mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 09:35:28 +00:00
Merge branch 'upstream' into movement_tweaks
This commit is contained in:
commit
784b1888a9
174
.gitlab-ci.yml
174
.gitlab-ci.yml
@ -3,17 +3,25 @@
|
||||
stages:
|
||||
- build
|
||||
|
||||
.Debian_Image:
|
||||
# https://blog.nimbleways.com/let-s-make-faster-gitlab-ci-cd-pipelines/
|
||||
variables:
|
||||
FF_USE_NEW_SHELL_ESCAPE: "true"
|
||||
FF_USE_FASTZIP: "true"
|
||||
# These can be specified per job or per pipeline
|
||||
ARTIFACT_COMPRESSION_LEVEL: "fast"
|
||||
CACHE_COMPRESSION_LEVEL: "fast"
|
||||
|
||||
.Ubuntu_Image:
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
image: debian:bullseye
|
||||
image: ubuntu:focal
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
|
||||
|
||||
.Debian:
|
||||
extends: .Debian_Image
|
||||
.Ubuntu:
|
||||
extends: .Ubuntu_Image
|
||||
cache:
|
||||
paths:
|
||||
- apt-cache/
|
||||
@ -35,7 +43,7 @@ stages:
|
||||
- build/install/
|
||||
|
||||
Clang_Tidy:
|
||||
extends: .Debian_Image
|
||||
extends: .Ubuntu_Image
|
||||
stage: build
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
@ -50,34 +58,41 @@ Clang_Tidy:
|
||||
CXX: clang++
|
||||
CI_CLANG_TIDY: 1
|
||||
timeout: 8h
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
Coverity:
|
||||
extends: .Debian_Image
|
||||
extends: .Ubuntu_Image
|
||||
stage: build
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic coverity
|
||||
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic coverity
|
||||
- curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
|
||||
- tar xfz /tmp/cov-analysis-linux64.tgz
|
||||
script:
|
||||
- CI/before_script.linux.sh
|
||||
# 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) openmw esmtool bsatool niftest openmw-wizard openmw-launcher openmw-iniimporter openmw-essimporter
|
||||
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) openmw esmtool bsatool niftest openmw-wizard openmw-launcher openmw-iniimporter openmw-essimporter openmw-navmeshtool openmw-cs
|
||||
after_script:
|
||||
- tar cfz cov-int.tar.gz cov-int
|
||||
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
|
||||
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
|
||||
--form file=@cov-int.tar.gz --form version="`git describe --tags`"
|
||||
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
|
||||
--form file=@cov-int.tar.gz --form version="$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA"
|
||||
--form description="CI_COMMIT_SHORT_SHA / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
|
||||
variables:
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
CXXFLAGS: -O0
|
||||
artifacts:
|
||||
paths:
|
||||
- /builds/OpenMW/openmw/cov-int/build-log.txt
|
||||
|
||||
Debian_GCC:
|
||||
extends: .Debian
|
||||
Ubuntu_GCC:
|
||||
extends: .Ubuntu
|
||||
cache:
|
||||
key: Debian_GCC.v2
|
||||
key: Ubuntu_GCC.v2
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
|
||||
variables:
|
||||
@ -87,27 +102,41 @@ Debian_GCC:
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 2h
|
||||
|
||||
Debian_GCC_tests:
|
||||
extends: Debian_GCC
|
||||
Ubuntu_GCC_tests:
|
||||
extends: Ubuntu_GCC
|
||||
cache:
|
||||
key: Debian_GCC_tests.v2
|
||||
key: Ubuntu_GCC_tests.v2
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
Debian_GCC_tests_Debug:
|
||||
extends: Debian_GCC
|
||||
Ubuntu_GCC_tests_Debug:
|
||||
extends: Ubuntu_GCC
|
||||
cache:
|
||||
key: Debian_GCC_tests_Debug.v1
|
||||
key: Ubuntu_GCC_tests_Debug.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
Debian_GCC_Static_Deps:
|
||||
extends: Debian_GCC
|
||||
Ubuntu_GCC_Static_Deps:
|
||||
extends: Ubuntu_GCC
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
changes:
|
||||
- "**/CMakeLists.txt"
|
||||
- "cmake/**/*"
|
||||
- "CI/**/*"
|
||||
- ".gitlab-ci.yml"
|
||||
allow_failure: true
|
||||
cache:
|
||||
key: Debian_GCC_Static_Deps
|
||||
key: Ubuntu_GCC_Static_Deps
|
||||
paths:
|
||||
- apt-cache/
|
||||
- ccache/
|
||||
@ -118,20 +147,23 @@ Debian_GCC_Static_Deps:
|
||||
CI_OPENMW_USE_STATIC_DEPS: 1
|
||||
timeout: 3h
|
||||
|
||||
Debian_GCC_Static_Deps_tests:
|
||||
extends: Debian_GCC_Static_Deps
|
||||
Ubuntu_GCC_Static_Deps_tests:
|
||||
extends: Ubuntu_GCC_Static_Deps
|
||||
cache:
|
||||
key: Debian_GCC_Static_Deps_tests
|
||||
key: Ubuntu_GCC_Static_Deps_tests
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
Debian_Clang:
|
||||
extends: .Debian
|
||||
Ubuntu_Clang:
|
||||
extends: .Ubuntu
|
||||
before_script:
|
||||
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic
|
||||
cache:
|
||||
key: Debian_Clang.v2
|
||||
key: Ubuntu_Clang.v2
|
||||
variables:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
@ -139,22 +171,28 @@ Debian_Clang:
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 2h
|
||||
|
||||
Debian_Clang_tests:
|
||||
extends: Debian_Clang
|
||||
Ubuntu_Clang_tests:
|
||||
extends: Ubuntu_Clang
|
||||
cache:
|
||||
key: Debian_Clang_tests.v2
|
||||
key: Ubuntu_Clang_tests.v2
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
Debian_Clang_tests_Debug:
|
||||
extends: Debian_Clang
|
||||
Ubuntu_Clang_tests_Debug:
|
||||
extends: Ubuntu_Clang
|
||||
cache:
|
||||
key: Debian_Clang_tests_Debug.v1
|
||||
key: Ubuntu_Clang_tests_Debug.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 1G
|
||||
BUILD_TESTS_ONLY: 1
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
.MacOS:
|
||||
image: macos-11-xcode-12
|
||||
@ -163,8 +201,7 @@ Debian_Clang_tests_Debug:
|
||||
stage: build
|
||||
only:
|
||||
variables:
|
||||
- $CI_PROJECT_ID == "7107382"
|
||||
- $CI_PIPELINE_SOURCE == "push"
|
||||
- $CI_PROJECT_ID == "7107382" && $CI_PIPELINE_SOURCE == "push"
|
||||
cache:
|
||||
paths:
|
||||
- ccache/
|
||||
@ -177,7 +214,7 @@ Debian_Clang_tests_Debug:
|
||||
- ccache -z -M "${CCACHE_SIZE}"
|
||||
- CI/before_script.osx.sh
|
||||
- cd build; make -j $(sysctl -n hw.logicalcpu) package
|
||||
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done
|
||||
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME##*/}_${CI_JOB_ID}.dmg"; done
|
||||
- ccache -s
|
||||
artifacts:
|
||||
paths:
|
||||
@ -192,21 +229,20 @@ macOS11_Xcode12:
|
||||
variables:
|
||||
CCACHE_SIZE: 3G
|
||||
|
||||
macOS10.15_Xcode11:
|
||||
macOS12_Xcode13:
|
||||
extends: .MacOS
|
||||
image: macos-10.15-xcode-11
|
||||
allow_failure: true
|
||||
image: macos-12-xcode-13
|
||||
cache:
|
||||
key: macOS10.15_Xcode11.v1
|
||||
key: macOS12_Xcode13.v1
|
||||
variables:
|
||||
CCACHE_SIZE: 3G
|
||||
|
||||
variables: &engine-targets
|
||||
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
|
||||
targets: "openmw,openmw-iniimporter,openmw-launcher,openmw-wizard,openmw-navmeshtool"
|
||||
package: "Engine"
|
||||
|
||||
variables: &cs-targets
|
||||
targets: "openmw-cs,bsatool,esmtool,niftest"
|
||||
targets: "openmw-cs,bsatool,esmtool,niftest,openmw-essimporter"
|
||||
package: "CS"
|
||||
|
||||
variables: &tests-targets
|
||||
@ -330,6 +366,9 @@ Windows_Ninja_Tests_RelWithDebInfo:
|
||||
config: "RelWithDebInfo"
|
||||
# Gitlab can't successfully execute following binaries due to unknown reason
|
||||
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
.Windows_MSBuild_Base:
|
||||
tags:
|
||||
@ -396,21 +435,16 @@ Windows_Ninja_Tests_RelWithDebInfo:
|
||||
- MSVC2019_64/*/*/*/*/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*/*/*/*/*.log
|
||||
|
||||
Daily_Windows_MSBuild_Engine_Release:on-schedule:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Release"
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
|
||||
Windows_MSBuild_Engine_Release:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Release"
|
||||
rules:
|
||||
# run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
|
||||
Windows_MSBuild_Engine_Debug:
|
||||
extends:
|
||||
@ -432,6 +466,10 @@ Windows_MSBuild_CS_Release:
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Release"
|
||||
rules:
|
||||
# run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
|
||||
Windows_MSBuild_CS_Debug:
|
||||
extends:
|
||||
@ -455,27 +493,28 @@ Windows_MSBuild_Tests_RelWithDebInfo:
|
||||
config: "RelWithDebInfo"
|
||||
# Gitlab can't successfully execute following binaries due to unknown reason
|
||||
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
|
||||
artifacts:
|
||||
paths: []
|
||||
expire_in: 1 minute
|
||||
|
||||
Debian_AndroidNDK_arm64-v8a:
|
||||
Ubuntu_AndroidNDK_arm64-v8a:
|
||||
tags:
|
||||
- linux
|
||||
image: debian:bullseye
|
||||
image: psi29a/android-ndk:focal-ndk22
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "push"
|
||||
variables:
|
||||
CCACHE_SIZE: 3G
|
||||
cache:
|
||||
key: Debian_AndroidNDK_arm64-v8a.v3
|
||||
key: Ubuntu__Focal_AndroidNDK_r22b_arm64-v8a.v1
|
||||
paths:
|
||||
- apt-cache/
|
||||
- ccache/
|
||||
- build/extern/fetched/
|
||||
before_script:
|
||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||
- echo "deb http://deb.debian.org/debian unstable main contrib" > /etc/apt/sources.list
|
||||
- echo "google-android-ndk-installer google-android-installers/mirror select https://dl.google.com" | debconf-set-selections
|
||||
- apt-get update -yq
|
||||
- apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake ccache curl unzip git build-essential google-android-ndk-installer
|
||||
- apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake ccache curl unzip git build-essential
|
||||
stage: build
|
||||
script:
|
||||
- export CCACHE_BASEDIR="`pwd`"
|
||||
@ -493,3 +532,18 @@ Debian_AndroidNDK_arm64-v8a:
|
||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||
timeout: 1h30m
|
||||
|
||||
FindMissingMergeRequests:
|
||||
image: python:latest
|
||||
stage: build
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
cache:
|
||||
key: FindMissingMergeRequests.v1
|
||||
paths:
|
||||
- .cache/pip
|
||||
before_script:
|
||||
- pip3 install --user requests click discord_webhook
|
||||
script:
|
||||
- scripts/find_missing_merge_requests.py --project_id=$CI_PROJECT_ID --ignored_mrs_path=$CI_PROJECT_DIR/.resubmitted_merge_requests.txt
|
||||
|
8
.resubmitted_merge_requests.txt
Normal file
8
.resubmitted_merge_requests.txt
Normal file
@ -0,0 +1,8 @@
|
||||
1471
|
||||
1450
|
||||
1420
|
||||
1314
|
||||
1216
|
||||
1172
|
||||
1160
|
||||
1051
|
80
.travis.yml
80
.travis.yml
@ -1,80 +0,0 @@
|
||||
language: cpp
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /openmw-.*$/
|
||||
cache: ccache
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:openmw/openmw'
|
||||
packages: [
|
||||
# Dev
|
||||
build-essential, cmake, clang-tools-9, ccache,
|
||||
# Boost
|
||||
libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,
|
||||
# FFmpeg
|
||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswresample-dev, libswscale-dev,
|
||||
# Audio, Video and Misc. deps
|
||||
libsdl2-dev, libqt5opengl5-dev, libopenal-dev, libunshield-dev, libtinyxml-dev, liblz4-dev,
|
||||
# The other ones from OpenMW ppa
|
||||
libbullet-dev, libopenscenegraph-dev, libmygui-dev
|
||||
]
|
||||
matrix:
|
||||
include:
|
||||
- name: OpenMW (all) on MacOS 10.15 with Xcode 11.6
|
||||
os: osx
|
||||
osx_image: xcode11.6
|
||||
- name: OpenMW (all) on Ubuntu Focal with GCC
|
||||
os: linux
|
||||
dist: focal
|
||||
- name: OpenMW (tests only) on Ubuntu Focal with GCC
|
||||
os: linux
|
||||
dist: focal
|
||||
env:
|
||||
- BUILD_TESTS_ONLY: 1
|
||||
- name: OpenMW (openmw) on Ubuntu Focal with Clang's Static Analysis
|
||||
os: linux
|
||||
dist: focal
|
||||
env:
|
||||
- MATRIX_EVAL="CC=clang-9 && CXX=clang++-9"
|
||||
- ANALYZE="scan-build-9 --force-analyze-debug-code --use-cc clang-9 --use-c++ clang++-9"
|
||||
compiler: clang
|
||||
|
||||
before_install:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi
|
||||
- ./CI/before_install.${TRAVIS_OS_NAME}.sh
|
||||
before_script:
|
||||
- ccache -z
|
||||
- ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
||||
script:
|
||||
- cd ./build
|
||||
- ${ANALYZE} make -j3;
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ] && [ "${BUILD_TESTS_ONLY}" ]; then ./openmw_test_suite; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
- cd "${TRAVIS_BUILD_DIR}"
|
||||
- ccache -s
|
||||
deploy:
|
||||
provider: script
|
||||
script: ./CI/deploy.osx.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
branch: master
|
||||
condition: "$TRAVIS_EVENT_TYPE = cron && $TRAVIS_OS_NAME = osx"
|
||||
repo: OpenMW/openmw
|
||||
notifications:
|
||||
email:
|
||||
if: repository_slug = OpenMW/openmw AND branch = master
|
||||
recipients:
|
||||
- corrmage+travis-ci@gmail.com
|
||||
on_success: change
|
||||
on_failure: always
|
||||
irc:
|
||||
if: repository_slug = OpenMW/openmw AND branch = master
|
||||
channels:
|
||||
- "irc.libera.chat#openmw"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
use_notice: true
|
@ -31,6 +31,7 @@ Programmers
|
||||
Allofich
|
||||
Andreas Stöckel
|
||||
Andrei Kortunov (akortunov)
|
||||
Andrew Appuhamy (andrew-app)
|
||||
AnyOldName3
|
||||
Ardekantur
|
||||
Armin Preiml
|
||||
@ -92,6 +93,7 @@ Programmers
|
||||
Haoda Wang (h313)
|
||||
hristoast
|
||||
Internecine
|
||||
Ivan Beloborodov (myrix)
|
||||
Jackerty
|
||||
Jacob Essex (Yacoby)
|
||||
Jacob Turnbull (Tankinfrank)
|
||||
@ -113,6 +115,7 @@ Programmers
|
||||
John Blomberg (fstp)
|
||||
Jordan Ayers
|
||||
Jordan Milne
|
||||
Josquin Frei
|
||||
Josua Grawitter
|
||||
Jules Blok (Armada651)
|
||||
julianko
|
||||
@ -215,6 +218,7 @@ Programmers
|
||||
tlmullis
|
||||
tri4ng1e
|
||||
Thoronador
|
||||
Tom Lowe (Vulpen)
|
||||
Tom Mason (wheybags)
|
||||
Torben Leif Carrington (TorbenC)
|
||||
unelsson
|
||||
@ -230,7 +234,7 @@ Programmers
|
||||
Yuri Krupenin
|
||||
zelurker
|
||||
Noah Gooder
|
||||
Andrew Appuhamy (andrew-app)
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
42
CHANGELOG.md
42
CHANGELOG.md
@ -2,21 +2,31 @@
|
||||
------
|
||||
|
||||
Bug #1751: Birthsign abilities increase modified attribute values instead of base ones
|
||||
Bug #1930: Followers are still fighting if a target stops combat with a leader
|
||||
Bug #2036: SetStat and ModStat instructions aren't implemented the same way as in Morrowind
|
||||
Bug #3246: ESSImporter: Most NPCs are dead on save load
|
||||
Bug #3488: AI combat aiming is too slow
|
||||
Bug #3514: Editing a reference's position after loading an esp file makes the reference disappear
|
||||
Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions)
|
||||
Bug #3792: 1 frame late magicka recalc breaks early scripted magicka reactions to Intelligence change
|
||||
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
|
||||
Bug #3855: AI sometimes spams defensive spells
|
||||
Bug #3905: Great House Dagoth issues
|
||||
Bug #4203: Resurrecting an actor should close the loot GUI
|
||||
Bug #4376: Moved actors don't respawn in their original cells
|
||||
Bug #4389: NPC's lips do not move if his head model has the NiBSAnimationNode root node
|
||||
Bug #4602: Robert's Bodies: crash inside createInstance()
|
||||
Bug #4700: Editor: Incorrect command implementation
|
||||
Bug #4744: Invisible particles must still be processed
|
||||
Bug #4949: Incorrect particle lighting
|
||||
Bug #5088: Sky abruptly changes direction during certain weather transitions
|
||||
Bug #5100: Persuasion doesn't always clamp the resulting disposition
|
||||
Bug #5120: Scripted object spawning updates physics system
|
||||
Bug #5207: Loose summons can be present in scene
|
||||
Bug #5377: console does not appear after using menutest in inventory
|
||||
Bug #5379: Wandering NPCs falling through cantons
|
||||
Bug #5394: Windows snapping no longer works
|
||||
Bug #5434: Pinned windows shouldn't cover breath progress bar
|
||||
Bug #5453: Magic effect VFX are offset for creatures
|
||||
Bug #5483: AutoCalc flag is not used to calculate spells cost
|
||||
Bug #5508: Engine binary links to Qt without using it
|
||||
@ -28,6 +38,7 @@
|
||||
Bug #5842: GetDisposition adds temporary disposition change from different actors
|
||||
Bug #5863: GetEffect should return true after the player has teleported
|
||||
Bug #5913: Failed assertion during Ritual of Trees quest
|
||||
Bug #5928: Glow in the Dahrk functionality used without mod installed
|
||||
Bug #5937: Lights always need to be rotated by 90 degrees
|
||||
Bug #6037: Morrowind Content Language Cannot be Set to English in OpenMW Launcher
|
||||
Bug #6051: NaN water height in ESM file is not handled gracefully
|
||||
@ -47,7 +58,9 @@
|
||||
Bug #6168: Weather particles flicker for a frame at start of storms
|
||||
Bug #6172: Some creatures can't open doors
|
||||
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
|
||||
Bug #6177: Followers of player follower stop following after waiting for a day
|
||||
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
|
||||
Bug #6191: Encumbrance messagebox timer works incorrectly
|
||||
Bug #6197: Infinite Casting Loop
|
||||
Bug #6253: Multiple instances of Reflect stack additively
|
||||
Bug #6255: Reflect is different from vanilla
|
||||
@ -59,15 +72,36 @@
|
||||
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters
|
||||
Bug #6291: Can't pickup the dead mage's journal from the mysterious hunter mod
|
||||
Bug #6302: Teleporting disabled actor breaks its disabled state
|
||||
Bug #6303: After "go to jail" weapon can stuck in the ready to attack state
|
||||
Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken
|
||||
Bug #6321: Arrow enchantments should always be applied to the target
|
||||
Bug #6322: Total sold/cost should reset to 0 when there are no items offered
|
||||
Bug #6323: Wyrmhaven: Alboin doesn't follower the player character out of his house
|
||||
Bug #6324: Special Slave Companions: Can't buy the slave companions
|
||||
Bug #6326: Detect Enchantment/Key should detect items in unresolved containers
|
||||
Bug #6327: Blocking roots the character in place
|
||||
Bug #6333: Werewolf stat changes should be implemented as damage/fortifications
|
||||
Bug #6343: Magic projectile speed doesn't take race weight into account
|
||||
Bug #6347: PlaceItem/PlaceItemCell/PlaceAt should work with levelled creatures
|
||||
Bug #6354: SFX abruptly cut off after crossing max distance; implement soft fading of sound effects
|
||||
Bug #6358: Changeweather command does not report an error when entering non-existent region
|
||||
Bug #6363: Some scripts in Morrowland fail to work
|
||||
Bug #6376: Creatures should be able to use torches
|
||||
Bug #6386: Artifacts in water reflection due to imprecise screen-space coordinate computation
|
||||
Bug #6396: Inputting certain Unicode characters triggers an assertion
|
||||
Bug #6416: Morphs are applied to the wrong target
|
||||
Bug #6417: OpenMW doesn't always use the right node to accumulate movement
|
||||
Bug #6429: Wyrmhaven: Can't add AI packages to player
|
||||
Bug #6433: Items bound to Quick Keys sometimes do not appear until the Quick Key menu is opened
|
||||
Bug #6451: Weapon summoned from Cast When Used item will have the name "None"
|
||||
Bug #6473: Strings from NIF should be parsed only to first null terminator
|
||||
Bug #6493: Unlocking owned but not locked or unlocked containers is considered a crime
|
||||
Bug #6517: Rotations for KeyframeData in NIFs should be optional
|
||||
Bug #6519: Effects tooltips for ingredients work incorrectly
|
||||
Bug #6523: Disintegrate Weapon is resisted by Resist Magicka instead of Sanctuary
|
||||
Bug #6544: Far from world origin objects jitter when camera is still
|
||||
Feature #890: OpenMW-CS: Column filtering
|
||||
Feature #1465: "Reset" argument for AI functions
|
||||
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
|
||||
Feature #2780: A way to see current OpenMW version in the console
|
||||
Feature #3616: Allow Zoom levels on the World Map
|
||||
@ -82,12 +116,18 @@
|
||||
Feature #6017: Separate persistent and temporary cell references when saving
|
||||
Feature #6032: Reverse-z depth buffer
|
||||
Feature #6078: First person should not clear depth buffer
|
||||
Feature #6128: Soft Particles
|
||||
Feature #6161: Refactor Sky to use shaders and GLES/GL3 friendly
|
||||
Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
|
||||
Feature #6189: Navigation mesh disk cache
|
||||
Feature #6199: Support FBO Rendering
|
||||
Feature #6248: Embedded error marker mesh
|
||||
Feature #6249: Alpha testing support for Collada
|
||||
Feature #6251: OpenMW-CS: Set instance movement based on camera zoom
|
||||
Feature #6288: Preserve the "blocked" record flag for referenceable objects.
|
||||
Feature #6380: Commas are treated as whitespace in vanilla
|
||||
Feature #6419: Topics shouldn't be greyed out if they can produce another topic reference
|
||||
Feature #6534: Shader-based object texture blending
|
||||
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
|
||||
Task #6264: Remove the old classes in animation.cpp
|
||||
|
||||
@ -224,6 +264,8 @@
|
||||
Bug #6043: Actor can have torch missing when torch animation is played
|
||||
Bug #6047: Mouse bindings can be triggered during save loading
|
||||
Bug #6136: Game freezes when NPCs try to open doors that are about to be closed
|
||||
Bug #6142: Groundcover plugins change cells flags
|
||||
Bug #6276: Deleted groundcover instances are not deleted in game
|
||||
Bug #6294: Game crashes with empty pathgrid
|
||||
Feature #390: 3rd person look "over the shoulder"
|
||||
Feature #832: OpenMW-CS: Handle deleted references
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20201230.zip -o ~/openmw-android-deps.zip
|
||||
unzip -o ~/openmw-android-deps -d /usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
|
||||
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20211114.zip -o ~/openmw-android-deps.zip
|
||||
unzip -o ~/openmw-android-deps -d /android-ndk-r22/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
|
||||
|
@ -7,9 +7,10 @@ mkdir -p build
|
||||
cd build
|
||||
|
||||
cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/usr/lib/android-sdk/ndk-bundle/build/cmake/android.toolchain.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/android-ndk-r22/build/cmake/android.toolchain.cmake \
|
||||
-DANDROID_ABI=arm64-v8a \
|
||||
-DANDROID_PLATFORM=android-21 \
|
||||
-DANDROID_LD=deprecated \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_INSTALL_PREFIX=install \
|
||||
@ -21,8 +22,7 @@ cmake \
|
||||
-DBUILD_ESSIMPORTER=0 \
|
||||
-DBUILD_OPENCS=0 \
|
||||
-DBUILD_WIZARD=0 \
|
||||
-DBUILD_NAVMESHTOOL=OFF \
|
||||
-DOPENMW_USE_SYSTEM_MYGUI=OFF \
|
||||
-DOPENMW_USE_SYSTEM_OSG=OFF \
|
||||
-DOPENMW_USE_SYSTEM_BULLET=OFF \
|
||||
-DOPENMW_USE_SYSTEM_SQLITE3=OFF \
|
||||
..
|
||||
|
@ -46,7 +46,7 @@ fi
|
||||
|
||||
if [[ $CI_CLANG_TIDY ]]; then
|
||||
CMAKE_CONF_OPTS+=(
|
||||
-DCMAKE_CXX_CLANG_TIDY='clang-tidy;-checks=-*,boost-*,clang-analyzer-*,concurrency-*,performance-*,-header-filter=.*,bugprone-*,misc-definitions-in-headers,misc-misplaced-const,misc-redundant-expression'
|
||||
-DCMAKE_CXX_CLANG_TIDY='clang-tidy;-checks=-*,boost-*,clang-analyzer-*,concurrency-*,performance-*,-header-filter=.*,bugprone-*,misc-definitions-in-headers,misc-misplaced-const,misc-redundant-expression,-bugprone-narrowing-conversions'
|
||||
)
|
||||
fi
|
||||
|
||||
@ -71,6 +71,7 @@ if [[ "${BUILD_TESTS_ONLY}" ]]; then
|
||||
-DBUILD_ESSIMPORTER=OFF \
|
||||
-DBUILD_OPENCS=OFF \
|
||||
-DBUILD_WIZARD=OFF \
|
||||
-DBUILD_NAVMESHTOOL=OFF \
|
||||
-DBUILD_UNITTESTS=${BUILD_UNITTESTS} \
|
||||
-DBUILD_BENCHMARKS=${BUILD_BENCHMARKS} \
|
||||
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
|
||||
|
@ -566,9 +566,9 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
download "SDL 2.0.12" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-2.0.12.zip" \
|
||||
"SDL2-2.0.12.zip"
|
||||
download "SDL 2.0.18" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/SDL2-2.0.18.zip" \
|
||||
"SDL2-2.0.18.zip"
|
||||
|
||||
# LZ4
|
||||
download "LZ4 1.9.2" \
|
||||
@ -898,17 +898,17 @@ fi
|
||||
cd $DEPS
|
||||
echo
|
||||
# SDL2
|
||||
printf "SDL 2.0.12... "
|
||||
printf "SDL 2.0.18... "
|
||||
{
|
||||
if [ -d SDL2-2.0.12 ]; then
|
||||
if [ -d SDL2-2.0.18 ]; then
|
||||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf SDL2-2.0.12
|
||||
eval 7z x -y SDL2-2.0.12.zip $STRIP
|
||||
rm -rf SDL2-2.0.18
|
||||
eval 7z x -y SDL2-2.0.18.zip $STRIP
|
||||
fi
|
||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.12"
|
||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.18"
|
||||
for config in ${CONFIGURATIONS[@]}; do
|
||||
add_runtime_dlls $config "$(pwd)/SDL2-2.0.12/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||
add_runtime_dlls $config "$(pwd)/SDL2-2.0.18/lib/x${ARCHSUFFIX}/SDL2.dll"
|
||||
done
|
||||
echo Done.
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ cmake \
|
||||
-D CMAKE_CXX_FLAGS="-stdlib=libc++" \
|
||||
-D CMAKE_C_FLAGS_RELEASE="-g -O0" \
|
||||
-D CMAKE_CXX_FLAGS_RELEASE="-g -O0" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.14" \
|
||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.15" \
|
||||
-D CMAKE_BUILD_TYPE=RELEASE \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D OPENMW_USE_SYSTEM_SQLITE3=OFF \
|
||||
|
@ -22,9 +22,8 @@ declare -rA GROUPED_DEPS=(
|
||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||
libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev
|
||||
libbullet-dev liblz4-dev libpng-dev libjpeg-dev libluajit-5.1-dev
|
||||
ca-certificates
|
||||
librecast-dev libsqlite3-dev ca-certificates
|
||||
"
|
||||
# TODO: add librecastnavigation-dev when debian is ready
|
||||
|
||||
# These dependencies can alternatively be built and linked statically.
|
||||
[openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev libsqlite3-dev"
|
||||
@ -64,5 +63,7 @@ done
|
||||
export APT_CACHE_DIR="${PWD}/apt-cache"
|
||||
set -x
|
||||
mkdir -pv "$APT_CACHE_DIR"
|
||||
apt-get update -yq
|
||||
apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}"
|
||||
apt-get update -yqq
|
||||
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common >/dev/null
|
||||
add-apt-repository -y ppa:openmw/openmw
|
||||
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
|
||||
|
@ -37,6 +37,7 @@ option(BUILD_DOCS "Build documentation." OFF )
|
||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
||||
option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
|
||||
option(BUILD_NAVMESHTOOL "Build navmesh tool" ON)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up.
|
||||
|
||||
@ -398,7 +399,7 @@ set(Boost_NO_BOOST_CMAKE ON)
|
||||
|
||||
find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS})
|
||||
if(OPENMW_USE_SYSTEM_MYGUI)
|
||||
find_package(MyGUI 3.2.2 REQUIRED)
|
||||
find_package(MyGUI 3.4.1 REQUIRED)
|
||||
endif()
|
||||
find_package(SDL2 2.0.9 REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
@ -414,7 +415,8 @@ else(USE_LUAJIT)
|
||||
endif(USE_LUAJIT)
|
||||
|
||||
# C++ library binding to Lua
|
||||
set(SOL_INCLUDE_DIRS ${OpenMW_SOURCE_DIR}/extern/sol3.2.2 ${OpenMW_SOURCE_DIR}/extern/sol_config)
|
||||
set(SOL_INCLUDE_DIR ${OpenMW_SOURCE_DIR}/extern/sol3.2.2)
|
||||
set(SOL_CONFIG_DIR ${OpenMW_SOURCE_DIR}/extern/sol_config)
|
||||
|
||||
include_directories(
|
||||
BEFORE SYSTEM
|
||||
@ -426,7 +428,8 @@ include_directories(
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${BULLET_INCLUDE_DIRS}
|
||||
${LUA_INCLUDE_DIR}
|
||||
${SOL_INCLUDE_DIRS}
|
||||
${SOL_INCLUDE_DIR}
|
||||
${SOL_CONFIG_DIR}
|
||||
)
|
||||
|
||||
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
|
||||
@ -443,9 +446,8 @@ if (APPLE)
|
||||
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
|
||||
endif (APPLE)
|
||||
|
||||
if (NOT APPLE)
|
||||
set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR})
|
||||
set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})
|
||||
if (NOT APPLE) # this is modified for macOS use later in "apps/open[mw|cs]/CMakeLists.txt"
|
||||
set(OPENMW_RESOURCES_ROOT ${OpenMW_BINARY_DIR})
|
||||
endif ()
|
||||
|
||||
add_subdirectory(files/)
|
||||
@ -602,6 +604,10 @@ if (BUILD_BENCHMARKS)
|
||||
add_subdirectory(apps/benchmarks)
|
||||
endif()
|
||||
|
||||
if (BUILD_NAVMESHTOOL)
|
||||
add_subdirectory(apps/navmeshtool)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (MSVC)
|
||||
if (OPENMW_MP_BUILD)
|
||||
@ -701,6 +707,10 @@ if (WIN32)
|
||||
if (BUILD_BENCHMARKS)
|
||||
set_target_properties(openmw_detournavigator_navmeshtilescache_benchmark PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
endif()
|
||||
|
||||
if (BUILD_NAVMESHTOOL)
|
||||
set_target_properties(openmw-navmeshtool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
# TODO: At some point release builds should not use the console but rather write to a log file
|
||||
@ -709,8 +719,10 @@ if (WIN32)
|
||||
endif()
|
||||
|
||||
if (BUILD_OPENMW AND APPLE)
|
||||
# Without these flags LuaJit crashes on startup on OSX
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000")
|
||||
if (USE_LUAJIT)
|
||||
# Without these flags LuaJit crashes on startup on OSX
|
||||
set_target_properties(openmw PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000")
|
||||
endif(USE_LUAJIT)
|
||||
target_compile_definitions(components PRIVATE GL_SILENCE_DEPRECATION=1)
|
||||
target_compile_definitions(openmw PRIVATE GL_SILENCE_DEPRECATION=1)
|
||||
endif()
|
||||
@ -941,6 +953,9 @@ elseif(NOT APPLE)
|
||||
IF(BUILD_WIZARD)
|
||||
INSTALL(PROGRAMS "${INSTALL_SOURCE}/openmw-wizard" DESTINATION "${BINDIR}" )
|
||||
ENDIF(BUILD_WIZARD)
|
||||
if(BUILD_NAVMESHTOOL)
|
||||
install(PROGRAMS "${INSTALL_SOURCE}/openmw-navmeshtool" DESTINATION "${BINDIR}" )
|
||||
endif()
|
||||
|
||||
# Install licenses
|
||||
INSTALL(FILES "files/mygui/DejaVuFontLicense.txt" DESTINATION "${LICDIR}" )
|
||||
|
@ -1,8 +1,6 @@
|
||||
OpenMW
|
||||
======
|
||||
|
||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) [![pipeline status](https://gitlab.com/OpenMW/openmw/badges/master/pipeline.svg)](https://gitlab.com/OpenMW/openmw/commits/master)
|
||||
|
||||
OpenMW is an open-source game engine that supports playing Morrowind by Bethesda Softworks. You need to own the game for OpenMW to play Morrowind.
|
||||
|
||||
OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set.
|
||||
|
@ -25,18 +25,10 @@ namespace
|
||||
};
|
||||
|
||||
template <typename Random>
|
||||
TilePosition generateTilePosition(int max, Random& random)
|
||||
osg::Vec2i generateVec2i(int max, Random& random)
|
||||
{
|
||||
std::uniform_int_distribution<int> distribution(0, max);
|
||||
return TilePosition(distribution(random), distribution(random));
|
||||
}
|
||||
|
||||
template <typename Random>
|
||||
TileBounds generateTileBounds(Random& random)
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
const osg::Vec2f min(distribution(random), distribution(random));
|
||||
return TileBounds {min, min + osg::Vec2f(1.0, 1.0)};
|
||||
return osg::Vec2i(distribution(random), distribution(random));
|
||||
}
|
||||
|
||||
template <typename Random>
|
||||
@ -91,8 +83,7 @@ namespace
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
std::generate_n(out, count, [&] {
|
||||
const osg::Vec3f shift(distribution(random), distribution(random), distribution(random));
|
||||
return Cell {1, shift};
|
||||
return CellWater {generateVec2i(1000, random), Water {ESM::Land::REAL_SIZE, distribution(random)}};
|
||||
});
|
||||
}
|
||||
|
||||
@ -117,16 +108,18 @@ namespace
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
Heightfield result;
|
||||
result.mBounds = generateTileBounds(random);
|
||||
result.mCellPosition = generateVec2i(1000, random);
|
||||
result.mCellSize = ESM::Land::REAL_SIZE;
|
||||
result.mMinHeight = distribution(random);
|
||||
result.mMaxHeight = result.mMinHeight + 1.0;
|
||||
result.mShift = osg::Vec3f(distribution(random), distribution(random), distribution(random));
|
||||
result.mScale = distribution(random);
|
||||
result.mLength = static_cast<std::uint8_t>(ESM::Land::LAND_SIZE);
|
||||
std::generate_n(std::back_inserter(result.mHeights), ESM::Land::LAND_NUM_VERTS, [&]
|
||||
{
|
||||
return distribution(random);
|
||||
});
|
||||
result.mOriginalSize = ESM::Land::LAND_SIZE;
|
||||
result.mMinX = 0;
|
||||
result.mMinY = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -135,7 +128,8 @@ namespace
|
||||
{
|
||||
std::uniform_real_distribution<float> distribution(0.0, 1.0);
|
||||
FlatHeightfield result;
|
||||
result.mBounds = generateTileBounds(random);
|
||||
result.mCellPosition = generateVec2i(1000, random);
|
||||
result.mCellSize = ESM::Land::REAL_SIZE;
|
||||
result.mHeight = distribution(random);
|
||||
return result;
|
||||
}
|
||||
@ -144,14 +138,14 @@ namespace
|
||||
Key generateKey(std::size_t triangles, Random& random)
|
||||
{
|
||||
const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, random);
|
||||
const TilePosition tilePosition = generateTilePosition(10000, random);
|
||||
const TilePosition tilePosition = generateVec2i(10000, random);
|
||||
const std::size_t generation = std::uniform_int_distribution<std::size_t>(0, 100)(random);
|
||||
const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
|
||||
Mesh mesh = generateMesh(triangles, random);
|
||||
std::vector<Cell> water;
|
||||
std::vector<CellWater> water;
|
||||
generateWater(std::back_inserter(water), 1, random);
|
||||
RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water),
|
||||
{generateHeightfield(random)}, {generateFlatHeightfield(random)});
|
||||
{generateHeightfield(random)}, {generateFlatHeightfield(random)}, {});
|
||||
return Key {agentHalfExtents, tilePosition, std::move(recastMesh)};
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ void printAIPackage(const ESM::AIPackage& p)
|
||||
{
|
||||
std::cout << " Travel Coordinates: (" << p.mTravel.mX << ","
|
||||
<< p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl;
|
||||
std::cout << " Travel Unknown: " << p.mTravel.mUnk << std::endl;
|
||||
std::cout << " Should repeat: " << p.mTravel.mShouldRepeat << std::endl;
|
||||
}
|
||||
else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort)
|
||||
{
|
||||
@ -38,12 +38,12 @@ void printAIPackage(const ESM::AIPackage& p)
|
||||
<< p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl;
|
||||
std::cout << " Duration: " << p.mTarget.mDuration << std::endl;
|
||||
std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl;
|
||||
std::cout << " Unknown: " << p.mTarget.mUnk << std::endl;
|
||||
std::cout << " Should repeat: " << p.mTarget.mShouldRepeat << std::endl;
|
||||
}
|
||||
else if (p.mType == ESM::AI_Activate)
|
||||
{
|
||||
std::cout << " Name: " << p.mActivate.mName.toString() << std::endl;
|
||||
std::cout << " Activate Unknown: " << p.mActivate.mUnk << std::endl;
|
||||
std::cout << " Should repeat: " << p.mActivate.mShouldRepeat << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << " BadPackage: " << Misc::StringUtils::format("0x%08X", p.mType) << std::endl;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <climits> // INT_MIN
|
||||
|
||||
#include <osgDB/WriteFile>
|
||||
|
||||
@ -371,7 +370,7 @@ namespace ESSImport
|
||||
if (cellref.mHasACDT)
|
||||
convertACDT(cellref.mACDT, objstate.mCreatureStats);
|
||||
else
|
||||
objstate.mCreatureStats.mGoldPool = INT_MIN; // HACK: indicates no ACDT
|
||||
objstate.mCreatureStats.mMissingACDT = true;
|
||||
if (cellref.mHasACSC)
|
||||
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
||||
convertNpcData(cellref, objstate.mNpcStats);
|
||||
@ -414,7 +413,7 @@ namespace ESSImport
|
||||
if (cellref.mHasACDT)
|
||||
convertACDT(cellref.mACDT, objstate.mCreatureStats);
|
||||
else
|
||||
objstate.mCreatureStats.mGoldPool = INT_MIN; // HACK: indicates no ACDT
|
||||
objstate.mCreatureStats.mMissingACDT = true;
|
||||
if (cellref.mHasACSC)
|
||||
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
||||
convertCREC(crecIt->second, objstate);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "advancedpage.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <QFileDialog>
|
||||
@ -20,13 +21,13 @@ Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings, QWidget
|
||||
setObjectName ("AdvancedPage");
|
||||
setupUi(this);
|
||||
|
||||
for(const char * name : Launcher::enumerateOpenALDevices())
|
||||
for(const std::string& name : Launcher::enumerateOpenALDevices())
|
||||
{
|
||||
audioDeviceSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));
|
||||
audioDeviceSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
|
||||
}
|
||||
for(const char * name : Launcher::enumerateOpenALDevicesHrtf())
|
||||
for(const std::string& name : Launcher::enumerateOpenALDevicesHrtf())
|
||||
{
|
||||
hrtfProfileSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));
|
||||
hrtfProfileSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name));
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
@ -117,6 +118,11 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||
loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
|
||||
loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
|
||||
loadSettingBool(radialFogCheckBox, "radial fog", "Shaders");
|
||||
loadSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
|
||||
loadSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
|
||||
if (Settings::Manager::getInt("antialiasing", "Video") == 0) {
|
||||
antialiasAlphaTestCheckBox->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
|
||||
connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool)));
|
||||
loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
|
||||
@ -137,6 +143,8 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||
loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
|
||||
viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt("viewing distance", "Camera")));
|
||||
objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain"));
|
||||
|
||||
loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
||||
}
|
||||
|
||||
// Audio
|
||||
@ -207,7 +215,7 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||
{
|
||||
// Saves
|
||||
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
||||
maximumQuicksavesComboBox->setValue(Settings::Manager::getInt("max quicksaves", "Saves"));
|
||||
loadSettingInt(maximumQuicksavesComboBox,"max quicksaves", "Saves");
|
||||
|
||||
// Other Settings
|
||||
QString screenshotFormatString = QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper();
|
||||
@ -252,14 +260,10 @@ void Launcher::AdvancedPage::saveSettings()
|
||||
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
|
||||
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
|
||||
saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
|
||||
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();
|
||||
if (unarmedFactorsStrengthIndex != Settings::Manager::getInt("strength influences hand to hand", "Game"))
|
||||
Settings::Manager::setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
|
||||
saveSettingInt(unarmedFactorsStrengthComboBox, "strength influences hand to hand", "Game");
|
||||
saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
|
||||
saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
|
||||
int numPhysicsThreads = physicsThreadsSpinBox->value();
|
||||
if (numPhysicsThreads != Settings::Manager::getInt("async num threads", "Physics"))
|
||||
Settings::Manager::setInt("async num threads", "Physics", numPhysicsThreads);
|
||||
saveSettingInt(physicsThreadsSpinBox, "async num threads", "Physics");
|
||||
}
|
||||
|
||||
// Visuals
|
||||
@ -270,6 +274,8 @@ void Launcher::AdvancedPage::saveSettings()
|
||||
saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
|
||||
saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
|
||||
saveSettingBool(radialFogCheckBox, "radial fog", "Shaders");
|
||||
saveSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
|
||||
saveSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders");
|
||||
saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
|
||||
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
|
||||
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
|
||||
@ -294,6 +300,8 @@ void Launcher::AdvancedPage::saveSettings()
|
||||
double objectPagingMinSize = objectPagingMinSizeComboBox->value();
|
||||
if (objectPagingMinSize != Settings::Manager::getDouble("object paging min size", "Terrain"))
|
||||
Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize);
|
||||
|
||||
saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game");
|
||||
}
|
||||
|
||||
// Audio
|
||||
@ -349,9 +357,7 @@ void Launcher::AdvancedPage::saveSettings()
|
||||
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
||||
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
||||
saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
|
||||
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
|
||||
if (showOwnedCurrentIndex != Settings::Manager::getInt("show owned", "Game"))
|
||||
Settings::Manager::setInt("show owned", "Game", showOwnedCurrentIndex);
|
||||
saveSettingInt(showOwnedComboBox,"show owned", "Game");
|
||||
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
|
||||
saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
|
||||
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
|
||||
@ -370,11 +376,7 @@ void Launcher::AdvancedPage::saveSettings()
|
||||
{
|
||||
// Saves Settings
|
||||
saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
||||
int maximumQuicksaves = maximumQuicksavesComboBox->value();
|
||||
if (maximumQuicksaves != Settings::Manager::getInt("max quicksaves", "Saves"))
|
||||
{
|
||||
Settings::Manager::setInt("max quicksaves", "Saves", maximumQuicksaves);
|
||||
}
|
||||
saveSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves");
|
||||
|
||||
// Other Settings
|
||||
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
|
||||
@ -416,6 +418,32 @@ void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::str
|
||||
Settings::Manager::setBool(setting, group, cValue);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::loadSettingInt(QComboBox *comboBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int currentIndex = Settings::Manager::getInt(setting, group);
|
||||
comboBox->setCurrentIndex(currentIndex);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::saveSettingInt(QComboBox *comboBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int currentIndex = comboBox->currentIndex();
|
||||
if (currentIndex != Settings::Manager::getInt(setting, group))
|
||||
Settings::Manager::setInt(setting, group, currentIndex);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::loadSettingInt(QSpinBox *spinBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int value = Settings::Manager::getInt(setting, group);
|
||||
spinBox->setValue(value);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::saveSettingInt(QSpinBox *spinBox, const std::string &setting, const std::string &group)
|
||||
{
|
||||
int value = spinBox->value();
|
||||
if (value != Settings::Manager::getInt(setting, group))
|
||||
Settings::Manager::setInt(setting, group, value);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
|
||||
{
|
||||
loadCellsForAutocomplete(cellNames);
|
||||
|
@ -41,8 +41,12 @@ namespace Launcher
|
||||
* @param filePaths the file paths of the content files to be examined
|
||||
*/
|
||||
void loadCellsForAutocomplete(QStringList filePaths);
|
||||
void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
static void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
static void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
||||
static void loadSettingInt(QComboBox *comboBox, const std::string& setting, const std::string& group);
|
||||
static void saveSettingInt(QComboBox *comboBox, const std::string& setting, const std::string& group);
|
||||
static void loadSettingInt(QSpinBox *spinBox, const std::string& setting, const std::string& group);
|
||||
static void saveSettingInt(QSpinBox *spinBox, const std::string& setting, const std::string& group);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "datafilespage.hpp"
|
||||
#include "maindialog.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
@ -8,6 +9,7 @@
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
|
||||
#include <apps/launcher/utils/cellnameloader.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
@ -24,11 +26,14 @@
|
||||
|
||||
const char *Launcher::DataFilesPage::mDefaultContentListName = "Default";
|
||||
|
||||
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
|
||||
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent)
|
||||
: QWidget(parent)
|
||||
, mMainDialog(parent)
|
||||
, mCfgMgr(cfg)
|
||||
, mGameSettings(gameSettings)
|
||||
, mLauncherSettings(launcherSettings)
|
||||
, mNavMeshToolInvoker(new Process::ProcessInvoker(this))
|
||||
{
|
||||
ui.setupUi (this);
|
||||
setObjectName ("DataFilesPage");
|
||||
@ -57,8 +62,6 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
|
||||
|
||||
void Launcher::DataFilesPage::buildView()
|
||||
{
|
||||
ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
|
||||
|
||||
QToolButton * refreshButton = mSelector->refreshButton();
|
||||
|
||||
//tool buttons
|
||||
@ -89,6 +92,13 @@ void Launcher::DataFilesPage::buildView()
|
||||
this, SLOT (slotProfileChangedByUser(QString, QString)));
|
||||
|
||||
connect(ui.refreshDataFilesAction, SIGNAL(triggered()),this, SLOT(slotRefreshButtonClicked()));
|
||||
|
||||
connect(ui.updateNavMeshButton, SIGNAL(clicked()), this, SLOT(startNavMeshTool()));
|
||||
connect(ui.cancelNavMeshButton, SIGNAL(clicked()), this, SLOT(killNavMeshTool()));
|
||||
|
||||
connect(mNavMeshToolInvoker->getProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(updateNavMeshProgress()));
|
||||
connect(mNavMeshToolInvoker->getProcess(), SIGNAL(readyReadStandardError()), this, SLOT(updateNavMeshProgress()));
|
||||
connect(mNavMeshToolInvoker->getProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(navMeshToolFinished(int, QProcess::ExitStatus)));
|
||||
}
|
||||
|
||||
bool Launcher::DataFilesPage::loadSettings()
|
||||
@ -121,6 +131,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
|
||||
|
||||
for (const QString &path : paths)
|
||||
mSelector->addFiles(path);
|
||||
mSelector->sortFiles();
|
||||
|
||||
PathIterator pathIterator(paths);
|
||||
|
||||
@ -410,3 +421,62 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
|
||||
std::sort(cellNamesList.begin(), cellNamesList.end());
|
||||
emit signalLoadedCellsChanged(cellNamesList);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::startNavMeshTool()
|
||||
{
|
||||
mMainDialog->writeSettings();
|
||||
|
||||
ui.navMeshLogPlainTextEdit->clear();
|
||||
ui.navMeshProgressBar->setValue(0);
|
||||
ui.navMeshProgressBar->setMaximum(1);
|
||||
|
||||
if (!mNavMeshToolInvoker->startProcess(QLatin1String("openmw-navmeshtool")))
|
||||
return;
|
||||
|
||||
ui.cancelNavMeshButton->setEnabled(true);
|
||||
ui.navMeshProgressBar->setEnabled(true);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::killNavMeshTool()
|
||||
{
|
||||
mNavMeshToolInvoker->killProcess();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::updateNavMeshProgress()
|
||||
{
|
||||
QProcess& process = *mNavMeshToolInvoker->getProcess();
|
||||
QString text;
|
||||
while (process.canReadLine())
|
||||
{
|
||||
const QByteArray line = process.readLine();
|
||||
const auto end = std::find_if(line.rbegin(), line.rend(), [] (auto v) { return v != '\n' && v != '\r'; });
|
||||
text = QString::fromUtf8(line.mid(0, line.size() - (end - line.rbegin())));
|
||||
ui.navMeshLogPlainTextEdit->appendPlainText(text);
|
||||
}
|
||||
const QRegularExpression pattern(R"([\( ](\d+)/(\d+)[\) ])");
|
||||
QRegularExpressionMatch match = pattern.match(text);
|
||||
if (!match.hasMatch())
|
||||
return;
|
||||
int value = match.captured(1).toInt();
|
||||
const int maximum = match.captured(2).toInt();
|
||||
if (text.contains("cell"))
|
||||
ui.navMeshProgressBar->setMaximum(maximum * 100);
|
||||
else if (maximum > ui.navMeshProgressBar->maximum())
|
||||
ui.navMeshProgressBar->setMaximum(maximum);
|
||||
else
|
||||
value += static_cast<int>(std::round(
|
||||
(ui.navMeshProgressBar->maximum() - maximum)
|
||||
* (static_cast<float>(value) / static_cast<float>(maximum))
|
||||
));
|
||||
ui.navMeshProgressBar->setValue(value);
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::navMeshToolFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
updateNavMeshProgress();
|
||||
ui.navMeshLogPlainTextEdit->appendPlainText(QString::fromUtf8(mNavMeshToolInvoker->getProcess()->readAll()));
|
||||
if (exitCode == 0 && exitStatus == QProcess::ExitStatus::NormalExit)
|
||||
ui.navMeshProgressBar->setValue(ui.navMeshProgressBar->maximum());
|
||||
ui.cancelNavMeshButton->setEnabled(false);
|
||||
ui.navMeshProgressBar->setEnabled(false);
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
#define DATAFILESPAGE_H
|
||||
|
||||
#include "ui_datafilespage.h"
|
||||
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
@ -19,6 +22,7 @@ namespace Config { class GameSettings;
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
class MainDialog;
|
||||
class TextInputDialog;
|
||||
class ProfilesComboBox;
|
||||
|
||||
@ -31,7 +35,7 @@ namespace Launcher
|
||||
|
||||
public:
|
||||
explicit DataFilesPage (Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
||||
Config::LauncherSettings &launcherSettings, QWidget *parent = nullptr);
|
||||
Config::LauncherSettings &launcherSettings, MainDialog *parent = nullptr);
|
||||
|
||||
QAbstractItemModel* profilesModel() const;
|
||||
|
||||
@ -69,12 +73,18 @@ namespace Launcher
|
||||
void on_cloneProfileAction_triggered();
|
||||
void on_deleteProfileAction_triggered();
|
||||
|
||||
void startNavMeshTool();
|
||||
void killNavMeshTool();
|
||||
void updateNavMeshProgress();
|
||||
void navMeshToolFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
public:
|
||||
/// Content List that is always present
|
||||
const static char *mDefaultContentListName;
|
||||
|
||||
private:
|
||||
|
||||
MainDialog *mMainDialog;
|
||||
TextInputDialog *mNewProfileDialog;
|
||||
TextInputDialog *mCloneProfileDialog;
|
||||
|
||||
@ -87,6 +97,8 @@ namespace Launcher
|
||||
QStringList previousSelectedFiles;
|
||||
QString mDataLocal;
|
||||
|
||||
Process::ProcessInvoker* mNavMeshToolInvoker;
|
||||
|
||||
void buildView();
|
||||
void setProfile (int index, bool savePrevious);
|
||||
void setProfile (const QString &previous, const QString ¤t, bool savePrevious);
|
||||
|
@ -47,7 +47,6 @@ Launcher::GraphicsPage::GraphicsPage(QWidget *parent)
|
||||
connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int)));
|
||||
connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool)));
|
||||
connect(shadowDistanceCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotShadowDistLimitToggled(bool)));
|
||||
|
||||
}
|
||||
|
||||
bool Launcher::GraphicsPage::setupSDL()
|
||||
|
@ -146,7 +146,6 @@ void Launcher::MainDialog::createPages()
|
||||
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
|
||||
// Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread
|
||||
connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);
|
||||
|
||||
}
|
||||
|
||||
Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
|
||||
|
@ -9,9 +9,9 @@
|
||||
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||
#endif
|
||||
|
||||
std::vector<const char *> Launcher::enumerateOpenALDevices()
|
||||
std::vector<std::string> Launcher::enumerateOpenALDevices()
|
||||
{
|
||||
std::vector<const char *> devlist;
|
||||
std::vector<std::string> devlist;
|
||||
const ALCchar *devnames;
|
||||
|
||||
if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"))
|
||||
@ -22,7 +22,7 @@ std::vector<const char *> Launcher::enumerateOpenALDevices()
|
||||
{
|
||||
devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
|
||||
}
|
||||
|
||||
|
||||
while(devnames && *devnames)
|
||||
{
|
||||
devlist.emplace_back(devnames);
|
||||
@ -31,9 +31,9 @@ std::vector<const char *> Launcher::enumerateOpenALDevices()
|
||||
return devlist;
|
||||
}
|
||||
|
||||
std::vector<const char *> Launcher::enumerateOpenALDevicesHrtf()
|
||||
std::vector<std::string> Launcher::enumerateOpenALDevicesHrtf()
|
||||
{
|
||||
std::vector<const char *> ret;
|
||||
std::vector<std::string> ret;
|
||||
|
||||
ALCdevice *device = alcOpenDevice(nullptr);
|
||||
if(device)
|
||||
|
@ -1,7 +1,8 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Launcher
|
||||
{
|
||||
std::vector<const char *> enumerateOpenALDevices();
|
||||
std::vector<const char *> enumerateOpenALDevicesHrtf();
|
||||
}
|
||||
std::vector<std::string> enumerateOpenALDevices();
|
||||
std::vector<std::string> enumerateOpenALDevicesHrtf();
|
||||
}
|
||||
|
22
apps/navmeshtool/CMakeLists.txt
Normal file
22
apps/navmeshtool/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
set(NAVMESHTOOL
|
||||
worldspacedata.cpp
|
||||
navmesh.cpp
|
||||
main.cpp
|
||||
)
|
||||
source_group(apps\\navmeshtool FILES ${NAVMESHTOOL})
|
||||
|
||||
openmw_add_executable(openmw-navmeshtool ${NAVMESHTOOL})
|
||||
|
||||
target_link_libraries(openmw-navmeshtool
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
components
|
||||
)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions(--coverage)
|
||||
target_link_libraries(openmw-navmeshtool gcov)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
install(TARGETS openmw-navmeshtool RUNTIME DESTINATION ".")
|
||||
endif()
|
209
apps/navmeshtool/main.cpp
Normal file
209
apps/navmeshtool/main.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
#include "worldspacedata.hpp"
|
||||
#include "navmesh.hpp"
|
||||
|
||||
#include <components/debug/debugging.hpp>
|
||||
#include <components/detournavigator/navmeshdb.hpp>
|
||||
#include <components/detournavigator/recastglobalallocator.hpp>
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/variant.hpp>
|
||||
#include <components/esmloader/esmdata.hpp>
|
||||
#include <components/esmloader/load.hpp>
|
||||
#include <components/fallback/fallback.hpp>
|
||||
#include <components/fallback/validate.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/resource/niffilemanager.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace NavMeshTool
|
||||
{
|
||||
namespace
|
||||
{
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
using StringsVector = std::vector<std::string>;
|
||||
|
||||
bpo::options_description makeOptionsDescription()
|
||||
{
|
||||
using Fallback::FallbackMap;
|
||||
|
||||
bpo::options_description result;
|
||||
|
||||
result.add_options()
|
||||
("help", "print help message")
|
||||
|
||||
("version", "print version information and quit")
|
||||
|
||||
("data", bpo::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "data")
|
||||
->multitoken()->composing(), "set data directories (later directories have higher priority)")
|
||||
|
||||
("data-local", bpo::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(Files::MaybeQuotedPathContainer::value_type(), ""),
|
||||
"set local data directory (highest priority)")
|
||||
|
||||
("fallback-archive", bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")
|
||||
->multitoken()->composing(), "set fallback BSA archives (later archives have higher priority)")
|
||||
|
||||
("resources", bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
|
||||
"set resources directory")
|
||||
|
||||
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
||||
->multitoken()->composing(), "content file(s): esm/esp, or omwgame/omwaddon/omwscripts")
|
||||
|
||||
("fs-strict", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "strict file system handling (no case folding)")
|
||||
|
||||
("encoding", bpo::value<std::string>()->
|
||||
default_value("win1252"),
|
||||
"Character encoding used in OpenMW game messages:\n"
|
||||
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||
|
||||
("fallback", bpo::value<Fallback::FallbackMap>()->default_value(Fallback::FallbackMap(), "")
|
||||
->multitoken()->composing(), "fallback values")
|
||||
|
||||
("threads", bpo::value<std::size_t>()->default_value(std::max<std::size_t>(std::thread::hardware_concurrency() - 1, 1)),
|
||||
"number of threads for parallel processing")
|
||||
|
||||
("process-interior-cells", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "build navmesh for interior cells")
|
||||
;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void loadSettings(const Files::ConfigurationManager& config, Settings::Manager& settings)
|
||||
{
|
||||
const std::string localDefault = (config.getLocalPath() / "defaults.bin").string();
|
||||
const std::string globalDefault = (config.getGlobalPath() / "defaults.bin").string();
|
||||
|
||||
if (boost::filesystem::exists(localDefault))
|
||||
settings.loadDefault(localDefault);
|
||||
else if (boost::filesystem::exists(globalDefault))
|
||||
settings.loadDefault(globalDefault);
|
||||
else
|
||||
throw std::runtime_error("No default settings file found! Make sure the file \"defaults.bin\" was properly installed.");
|
||||
|
||||
const std::string settingsPath = (config.getUserConfigPath() / "settings.cfg").string();
|
||||
if (boost::filesystem::exists(settingsPath))
|
||||
settings.loadUser(settingsPath);
|
||||
}
|
||||
|
||||
int runNavMeshTool(int argc, char *argv[])
|
||||
{
|
||||
bpo::options_description desc = makeOptionsDescription();
|
||||
|
||||
bpo::parsed_options options = bpo::command_line_parser(argc, argv)
|
||||
.options(desc).allow_unregistered().run();
|
||||
bpo::variables_map variables;
|
||||
|
||||
bpo::store(options, variables);
|
||||
bpo::notify(variables);
|
||||
|
||||
if (variables.find("help") != variables.end())
|
||||
{
|
||||
getRawStdout() << desc << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Files::ConfigurationManager config;
|
||||
|
||||
bpo::variables_map composingVariables = Files::separateComposingVariables(variables, desc);
|
||||
config.readConfiguration(variables, desc);
|
||||
Files::mergeComposingVariables(variables, composingVariables, desc);
|
||||
|
||||
const std::string encoding(variables["encoding"].as<std::string>());
|
||||
Log(Debug::Info) << ToUTF8::encodingUsingMessage(encoding);
|
||||
ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(encoding));
|
||||
|
||||
Files::PathContainer dataDirs(asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>()));
|
||||
|
||||
auto local = variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>();
|
||||
if (!local.empty())
|
||||
dataDirs.push_back(std::move(local));
|
||||
|
||||
config.processPaths(dataDirs);
|
||||
|
||||
const auto fsStrict = variables["fs-strict"].as<bool>();
|
||||
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
|
||||
Version::Version v = Version::getOpenmwVersion(resDir.string());
|
||||
Log(Debug::Info) << v.describe();
|
||||
dataDirs.insert(dataDirs.begin(), resDir / "vfs");
|
||||
const auto fileCollections = Files::Collections(dataDirs, !fsStrict);
|
||||
const auto archives = variables["fallback-archive"].as<StringsVector>();
|
||||
const auto contentFiles = variables["content"].as<StringsVector>();
|
||||
const std::size_t threadsNumber = variables["threads"].as<std::size_t>();
|
||||
|
||||
if (threadsNumber < 1)
|
||||
{
|
||||
std::cerr << "Invalid threads number: " << threadsNumber << ", expected >= 1";
|
||||
return -1;
|
||||
}
|
||||
|
||||
const bool processInteriorCells = variables["process-interior-cells"].as<bool>();
|
||||
|
||||
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap);
|
||||
|
||||
VFS::Manager vfs(fsStrict);
|
||||
|
||||
VFS::registerArchives(&vfs, fileCollections, archives, true);
|
||||
|
||||
Settings::Manager settings;
|
||||
loadSettings(config, settings);
|
||||
|
||||
const osg::Vec3f agentHalfExtents = Settings::Manager::getVector3("default actor pathfind half extents", "Game");
|
||||
|
||||
DetourNavigator::NavMeshDb db((config.getUserDataPath() / "navmesh.db").string());
|
||||
|
||||
std::vector<ESM::ESMReader> readers(contentFiles.size());
|
||||
EsmLoader::Query query;
|
||||
query.mLoadActivators = true;
|
||||
query.mLoadCells = true;
|
||||
query.mLoadContainers = true;
|
||||
query.mLoadDoors = true;
|
||||
query.mLoadGameSettings = true;
|
||||
query.mLoadLands = true;
|
||||
query.mLoadStatics = true;
|
||||
const EsmLoader::EsmData esmData = EsmLoader::loadEsmData(query, contentFiles, fileCollections, readers, &encoder);
|
||||
|
||||
Resource::ImageManager imageManager(&vfs);
|
||||
Resource::NifFileManager nifFileManager(&vfs);
|
||||
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager);
|
||||
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager);
|
||||
DetourNavigator::RecastGlobalAllocator::init();
|
||||
DetourNavigator::Settings navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
|
||||
navigatorSettings.mRecast.mSwimHeightScale = EsmLoader::getGameSetting(esmData.mGameSettings, "fSwimHeightScale").getFloat();
|
||||
|
||||
WorldspaceData cellsData = gatherWorldspaceData(navigatorSettings, readers, vfs, bulletShapeManager,
|
||||
esmData, processInteriorCells);
|
||||
|
||||
generateAllNavMeshTiles(agentHalfExtents, navigatorSettings, threadsNumber, cellsData, std::move(db));
|
||||
|
||||
Log(Debug::Info) << "Done";
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return wrapApplication(NavMeshTool::runNavMeshTool, argc, argv, "NavMeshTool");
|
||||
}
|
212
apps/navmeshtool/navmesh.cpp
Normal file
212
apps/navmeshtool/navmesh.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
#include "navmesh.hpp"
|
||||
|
||||
#include "worldspacedata.hpp"
|
||||
|
||||
#include <components/bullethelpers/aabb.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/detournavigator/generatenavmeshtile.hpp>
|
||||
#include <components/detournavigator/gettilespositions.hpp>
|
||||
#include <components/detournavigator/navmeshdb.hpp>
|
||||
#include <components/detournavigator/navmeshdbutils.hpp>
|
||||
#include <components/detournavigator/offmeshconnection.hpp>
|
||||
#include <components/detournavigator/offmeshconnectionsmanager.hpp>
|
||||
#include <components/detournavigator/preparednavmeshdata.hpp>
|
||||
#include <components/detournavigator/recastmesh.hpp>
|
||||
#include <components/detournavigator/recastmeshprovider.hpp>
|
||||
#include <components/detournavigator/serialization.hpp>
|
||||
#include <components/detournavigator/tilecachedrecastmeshmanager.hpp>
|
||||
#include <components/detournavigator/tileposition.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/misc/guarded.hpp>
|
||||
#include <components/misc/progressreporter.hpp>
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
#include <components/sqlite3/transaction.hpp>
|
||||
|
||||
#include <DetourNavMesh.h>
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace NavMeshTool
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using DetourNavigator::GenerateNavMeshTile;
|
||||
using DetourNavigator::NavMeshDb;
|
||||
using DetourNavigator::NavMeshTileInfo;
|
||||
using DetourNavigator::PreparedNavMeshData;
|
||||
using DetourNavigator::RecastMeshProvider;
|
||||
using DetourNavigator::MeshSource;
|
||||
using DetourNavigator::Settings;
|
||||
using DetourNavigator::ShapeId;
|
||||
using DetourNavigator::TileId;
|
||||
using DetourNavigator::TilePosition;
|
||||
using DetourNavigator::TileVersion;
|
||||
using Sqlite3::Transaction;
|
||||
|
||||
void logGeneratedTiles(std::size_t provided, std::size_t expected)
|
||||
{
|
||||
Log(Debug::Info) << provided << "/" << expected << " ("
|
||||
<< (static_cast<double>(provided) / static_cast<double>(expected) * 100)
|
||||
<< "%) navmesh tiles are generated";
|
||||
}
|
||||
|
||||
struct LogGeneratedTiles
|
||||
{
|
||||
void operator()(std::size_t provided, std::size_t expected) const
|
||||
{
|
||||
logGeneratedTiles(provided, expected);
|
||||
}
|
||||
};
|
||||
|
||||
class NavMeshTileConsumer final : public DetourNavigator::NavMeshTileConsumer
|
||||
{
|
||||
public:
|
||||
std::atomic_size_t mExpected {0};
|
||||
|
||||
explicit NavMeshTileConsumer(NavMeshDb&& db)
|
||||
: mDb(std::move(db))
|
||||
, mTransaction(mDb.startTransaction())
|
||||
, mNextTileId(mDb.getMaxTileId() + 1)
|
||||
, mNextShapeId(mDb.getMaxShapeId() + 1)
|
||||
{}
|
||||
|
||||
std::size_t getProvided() const { return mProvided.load(); }
|
||||
|
||||
std::size_t getInserted() const { return mInserted.load(); }
|
||||
|
||||
std::size_t getUpdated() const { return mUpdated.load(); }
|
||||
|
||||
std::int64_t resolveMeshSource(const MeshSource& source) override
|
||||
{
|
||||
const std::lock_guard lock(mMutex);
|
||||
return DetourNavigator::resolveMeshSource(mDb, source, mNextShapeId);
|
||||
}
|
||||
|
||||
std::optional<NavMeshTileInfo> find(const std::string& worldspace, const TilePosition &tilePosition,
|
||||
const std::vector<std::byte> &input) override
|
||||
{
|
||||
std::optional<NavMeshTileInfo> result;
|
||||
std::lock_guard lock(mMutex);
|
||||
if (const auto tile = mDb.findTile(worldspace, tilePosition, input))
|
||||
{
|
||||
NavMeshTileInfo info;
|
||||
info.mTileId = tile->mTileId;
|
||||
info.mVersion = tile->mVersion;
|
||||
result.emplace(info);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ignore() override { report(); }
|
||||
|
||||
void insert(const std::string& worldspace, const TilePosition& tilePosition, std::int64_t version,
|
||||
const std::vector<std::byte>& input, PreparedNavMeshData& data) override
|
||||
{
|
||||
data.mUserId = static_cast<unsigned>(mNextTileId);
|
||||
{
|
||||
std::lock_guard lock(mMutex);
|
||||
mDb.insertTile(mNextTileId, worldspace, tilePosition, TileVersion {version}, input, serialize(data));
|
||||
++mNextTileId.t;
|
||||
}
|
||||
++mInserted;
|
||||
report();
|
||||
}
|
||||
|
||||
void update(std::int64_t tileId, std::int64_t version, PreparedNavMeshData& data) override
|
||||
{
|
||||
data.mUserId = static_cast<unsigned>(tileId);
|
||||
{
|
||||
std::lock_guard lock(mMutex);
|
||||
mDb.updateTile(TileId {tileId}, TileVersion {version}, serialize(data));
|
||||
}
|
||||
++mUpdated;
|
||||
report();
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
constexpr std::size_t tilesPerTransaction = 3000;
|
||||
std::unique_lock lock(mMutex);
|
||||
while (mProvided < mExpected)
|
||||
{
|
||||
mHasTile.wait(lock);
|
||||
if (mProvided % tilesPerTransaction == 0)
|
||||
{
|
||||
mTransaction.commit();
|
||||
mTransaction = mDb.startTransaction();
|
||||
}
|
||||
}
|
||||
logGeneratedTiles(mProvided, mExpected);
|
||||
}
|
||||
|
||||
void commit() { mTransaction.commit(); }
|
||||
|
||||
private:
|
||||
std::atomic_size_t mProvided {0};
|
||||
std::atomic_size_t mInserted {0};
|
||||
std::atomic_size_t mUpdated {0};
|
||||
std::mutex mMutex;
|
||||
NavMeshDb mDb;
|
||||
Transaction mTransaction;
|
||||
TileId mNextTileId;
|
||||
std::condition_variable mHasTile;
|
||||
Misc::ProgressReporter<LogGeneratedTiles> mReporter;
|
||||
ShapeId mNextShapeId;
|
||||
|
||||
void report()
|
||||
{
|
||||
mReporter(mProvided + 1, mExpected);
|
||||
++mProvided;
|
||||
mHasTile.notify_one();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const Settings& settings,
|
||||
const std::size_t threadsNumber, WorldspaceData& data, NavMeshDb&& db)
|
||||
{
|
||||
Log(Debug::Info) << "Generating navmesh tiles by " << threadsNumber << " parallel workers...";
|
||||
|
||||
SceneUtil::WorkQueue workQueue(threadsNumber);
|
||||
auto navMeshTileConsumer = std::make_shared<NavMeshTileConsumer>(std::move(db));
|
||||
std::size_t tiles = 0;
|
||||
|
||||
for (const std::unique_ptr<WorldspaceNavMeshInput>& input : data.mNavMeshInputs)
|
||||
{
|
||||
DetourNavigator::getTilesPositions(
|
||||
Misc::Convert::toOsg(input->mAabb.m_min), Misc::Convert::toOsg(input->mAabb.m_max), settings.mRecast,
|
||||
[&] (const TilePosition& tilePosition)
|
||||
{
|
||||
workQueue.addWorkItem(new GenerateNavMeshTile(
|
||||
input->mWorldspace,
|
||||
tilePosition,
|
||||
RecastMeshProvider(input->mTileCachedRecastMeshManager),
|
||||
agentHalfExtents,
|
||||
settings,
|
||||
navMeshTileConsumer
|
||||
));
|
||||
|
||||
++tiles;
|
||||
});
|
||||
|
||||
navMeshTileConsumer->mExpected = tiles;
|
||||
}
|
||||
|
||||
navMeshTileConsumer->wait();
|
||||
navMeshTileConsumer->commit();
|
||||
|
||||
Log(Debug::Info) << "Generated navmesh for " << navMeshTileConsumer->getProvided() << " tiles, "
|
||||
<< navMeshTileConsumer->getInserted() << " are inserted and "
|
||||
<< navMeshTileConsumer->getUpdated() << " updated";
|
||||
}
|
||||
}
|
23
apps/navmeshtool/navmesh.hpp
Normal file
23
apps/navmeshtool/navmesh.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef OPENMW_NAVMESHTOOL_NAVMESH_H
|
||||
#define OPENMW_NAVMESHTOOL_NAVMESH_H
|
||||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
class NavMeshDb;
|
||||
struct Settings;
|
||||
}
|
||||
|
||||
namespace NavMeshTool
|
||||
{
|
||||
struct WorldspaceData;
|
||||
|
||||
void generateAllNavMeshTiles(const osg::Vec3f& agentHalfExtents, const DetourNavigator::Settings& settings,
|
||||
const std::size_t threadsNumber, WorldspaceData& cellsData, DetourNavigator::NavMeshDb&& db);
|
||||
}
|
||||
|
||||
#endif
|
330
apps/navmeshtool/worldspacedata.cpp
Normal file
330
apps/navmeshtool/worldspacedata.cpp
Normal file
@ -0,0 +1,330 @@
|
||||
#include "worldspacedata.hpp"
|
||||
|
||||
#include <components/bullethelpers/aabb.hpp>
|
||||
#include <components/bullethelpers/heightfield.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/detournavigator/gettilespositions.hpp>
|
||||
#include <components/detournavigator/objectid.hpp>
|
||||
#include <components/detournavigator/recastmesh.hpp>
|
||||
#include <components/detournavigator/tilecachedrecastmeshmanager.hpp>
|
||||
#include <components/esm/cellref.hpp>
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/esm/loadland.hpp>
|
||||
#include <components/esmloader/esmdata.hpp>
|
||||
#include <components/esmloader/lessbyid.hpp>
|
||||
#include <components/esmloader/record.hpp>
|
||||
#include <components/misc/coordinateconverter.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
#include <LinearMath/btVector3.h>
|
||||
|
||||
#include <osg/Vec2i>
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace NavMeshTool
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using DetourNavigator::CollisionShape;
|
||||
using DetourNavigator::HeightfieldPlane;
|
||||
using DetourNavigator::HeightfieldShape;
|
||||
using DetourNavigator::HeightfieldSurface;
|
||||
using DetourNavigator::ObjectId;
|
||||
using DetourNavigator::ObjectTransform;
|
||||
|
||||
struct CellRef
|
||||
{
|
||||
ESM::RecNameInts mType;
|
||||
ESM::RefNum mRefNum;
|
||||
std::string mRefId;
|
||||
float mScale;
|
||||
ESM::Position mPos;
|
||||
|
||||
CellRef(ESM::RecNameInts type, ESM::RefNum refNum, std::string&& refId, float scale, const ESM::Position& pos)
|
||||
: mType(type), mRefNum(refNum), mRefId(std::move(refId)), mScale(scale), mPos(pos) {}
|
||||
};
|
||||
|
||||
ESM::RecNameInts getType(const EsmLoader::EsmData& esmData, std::string_view refId)
|
||||
{
|
||||
const auto it = std::lower_bound(esmData.mRefIdTypes.begin(), esmData.mRefIdTypes.end(),
|
||||
refId, EsmLoader::LessById {});
|
||||
if (it == esmData.mRefIdTypes.end() || it->mId != refId)
|
||||
return {};
|
||||
return it->mType;
|
||||
}
|
||||
|
||||
std::vector<CellRef> loadCellRefs(const ESM::Cell& cell, const EsmLoader::EsmData& esmData,
|
||||
std::vector<ESM::ESMReader>& readers)
|
||||
{
|
||||
std::vector<EsmLoader::Record<CellRef>> cellRefs;
|
||||
|
||||
for (std::size_t i = 0; i < cell.mContextList.size(); i++)
|
||||
{
|
||||
ESM::ESMReader& reader = readers[static_cast<std::size_t>(cell.mContextList[i].index)];
|
||||
cell.restore(reader, static_cast<int>(i));
|
||||
ESM::CellRef cellRef;
|
||||
bool deleted = false;
|
||||
while (ESM::Cell::getNextRef(reader, cellRef, deleted))
|
||||
{
|
||||
Misc::StringUtils::lowerCaseInPlace(cellRef.mRefID);
|
||||
const ESM::RecNameInts type = getType(esmData, cellRef.mRefID);
|
||||
if (type == ESM::RecNameInts {})
|
||||
continue;
|
||||
cellRefs.emplace_back(deleted, type, cellRef.mRefNum, std::move(cellRef.mRefID),
|
||||
cellRef.mScale, cellRef.mPos);
|
||||
}
|
||||
}
|
||||
|
||||
Log(Debug::Debug) << "Loaded " << cellRefs.size() << " cell refs";
|
||||
|
||||
const auto getKey = [] (const EsmLoader::Record<CellRef>& v) -> const ESM::RefNum& { return v.mValue.mRefNum; };
|
||||
std::vector<CellRef> result = prepareRecords(cellRefs, getKey);
|
||||
|
||||
Log(Debug::Debug) << "Prepared " << result.size() << " unique cell refs";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void forEachObject(const ESM::Cell& cell, const EsmLoader::EsmData& esmData, const VFS::Manager& vfs,
|
||||
Resource::BulletShapeManager& bulletShapeManager, std::vector<ESM::ESMReader>& readers,
|
||||
F&& f)
|
||||
{
|
||||
std::vector<CellRef> cellRefs = loadCellRefs(cell, esmData, readers);
|
||||
|
||||
Log(Debug::Debug) << "Prepared " << cellRefs.size() << " unique cell refs";
|
||||
|
||||
for (CellRef& cellRef : cellRefs)
|
||||
{
|
||||
std::string model(getModel(esmData, cellRef.mRefId, cellRef.mType));
|
||||
if (model.empty())
|
||||
continue;
|
||||
|
||||
if (cellRef.mType != ESM::REC_STAT)
|
||||
model = Misc::ResourceHelpers::correctActorModelPath(model, &vfs);
|
||||
|
||||
osg::ref_ptr<const Resource::BulletShape> shape = [&]
|
||||
{
|
||||
try
|
||||
{
|
||||
return bulletShapeManager.getShape("meshes/" + model);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Log(Debug::Warning) << "Failed to load cell ref \"" << cellRef.mRefId << "\" model \"" << model << "\": " << e.what();
|
||||
return osg::ref_ptr<const Resource::BulletShape>();
|
||||
}
|
||||
} ();
|
||||
|
||||
if (shape == nullptr || shape->mCollisionShape == nullptr)
|
||||
continue;
|
||||
|
||||
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance(new Resource::BulletShapeInstance(std::move(shape)));
|
||||
|
||||
switch (cellRef.mType)
|
||||
{
|
||||
case ESM::REC_ACTI:
|
||||
case ESM::REC_CONT:
|
||||
case ESM::REC_DOOR:
|
||||
case ESM::REC_STAT:
|
||||
f(BulletObject(std::move(shapeInstance), cellRef.mPos, cellRef.mScale));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GetXY
|
||||
{
|
||||
osg::Vec2i operator()(const ESM::Land& value) const { return osg::Vec2i(value.mX, value.mY); }
|
||||
};
|
||||
|
||||
struct LessByXY
|
||||
{
|
||||
bool operator ()(const ESM::Land& lhs, const ESM::Land& rhs) const
|
||||
{
|
||||
return GetXY {}(lhs) < GetXY {}(rhs);
|
||||
}
|
||||
|
||||
bool operator ()(const ESM::Land& lhs, const osg::Vec2i& rhs) const
|
||||
{
|
||||
return GetXY {}(lhs) < rhs;
|
||||
}
|
||||
|
||||
bool operator ()(const osg::Vec2i& lhs, const ESM::Land& rhs) const
|
||||
{
|
||||
return lhs < GetXY {}(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
btAABB getAabb(const osg::Vec2i& cellPosition, btScalar minHeight, btScalar maxHeight)
|
||||
{
|
||||
btAABB aabb;
|
||||
aabb.m_min = btVector3(
|
||||
static_cast<btScalar>(cellPosition.x() * ESM::Land::REAL_SIZE),
|
||||
static_cast<btScalar>(cellPosition.y() * ESM::Land::REAL_SIZE),
|
||||
minHeight
|
||||
);
|
||||
aabb.m_min = btVector3(
|
||||
static_cast<btScalar>((cellPosition.x() + 1) * ESM::Land::REAL_SIZE),
|
||||
static_cast<btScalar>((cellPosition.y() + 1) * ESM::Land::REAL_SIZE),
|
||||
maxHeight
|
||||
);
|
||||
return aabb;
|
||||
}
|
||||
|
||||
void mergeOrAssign(const btAABB& aabb, btAABB& target, bool& initialized)
|
||||
{
|
||||
if (initialized)
|
||||
return target.merge(aabb);
|
||||
|
||||
target.m_min = aabb.m_min;
|
||||
target.m_max = aabb.m_max;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
std::tuple<HeightfieldShape, float, float> makeHeightfieldShape(const std::optional<ESM::Land>& land,
|
||||
const osg::Vec2i& cellPosition, std::vector<std::vector<float>>& heightfields,
|
||||
std::vector<std::unique_ptr<ESM::Land::LandData>>& landDatas)
|
||||
{
|
||||
if (!land.has_value() || osg::Vec2i(land->mX, land->mY) != cellPosition
|
||||
|| (land->mDataTypes & ESM::Land::DATA_VHGT) == 0)
|
||||
return {HeightfieldPlane {ESM::Land::DEFAULT_HEIGHT}, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT};
|
||||
|
||||
ESM::Land::LandData& landData = *landDatas.emplace_back(std::make_unique<ESM::Land::LandData>());
|
||||
land->loadData(ESM::Land::DATA_VHGT, &landData);
|
||||
heightfields.push_back(std::vector<float>(std::begin(landData.mHeights), std::end(landData.mHeights)));
|
||||
HeightfieldSurface surface;
|
||||
surface.mHeights = heightfields.back().data();
|
||||
surface.mMinHeight = landData.mMinHeight;
|
||||
surface.mMaxHeight = landData.mMaxHeight;
|
||||
surface.mSize = static_cast<std::size_t>(ESM::Land::LAND_SIZE);
|
||||
return {surface, landData.mMinHeight, landData.mMaxHeight};
|
||||
}
|
||||
}
|
||||
|
||||
WorldspaceNavMeshInput::WorldspaceNavMeshInput(std::string worldspace, const DetourNavigator::RecastSettings& settings)
|
||||
: mWorldspace(std::move(worldspace))
|
||||
, mTileCachedRecastMeshManager(settings)
|
||||
{
|
||||
mAabb.m_min = btVector3(0, 0, 0);
|
||||
mAabb.m_max = btVector3(0, 0, 0);
|
||||
}
|
||||
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, std::vector<ESM::ESMReader>& readers,
|
||||
const VFS::Manager& vfs, Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
bool processInteriorCells)
|
||||
{
|
||||
Log(Debug::Info) << "Processing " << esmData.mCells.size() << " cells...";
|
||||
|
||||
std::map<std::string_view, std::unique_ptr<WorldspaceNavMeshInput>> navMeshInputs;
|
||||
WorldspaceData data;
|
||||
|
||||
std::size_t objectsCounter = 0;
|
||||
|
||||
for (std::size_t i = 0; i < esmData.mCells.size(); ++i)
|
||||
{
|
||||
const ESM::Cell& cell = esmData.mCells[i];
|
||||
const bool exterior = cell.isExterior();
|
||||
|
||||
if (!exterior && !processInteriorCells)
|
||||
{
|
||||
Log(Debug::Info) << "Skipped interior"
|
||||
<< " cell (" << (i + 1) << "/" << esmData.mCells.size() << ") \"" << cell.getDescription() << "\"";
|
||||
continue;
|
||||
}
|
||||
|
||||
Log(Debug::Debug) << "Processing " << (exterior ? "exterior" : "interior")
|
||||
<< " cell (" << (i + 1) << "/" << esmData.mCells.size() << ") \"" << cell.getDescription() << "\"";
|
||||
|
||||
const osg::Vec2i cellPosition(cell.mData.mX, cell.mData.mY);
|
||||
const std::size_t cellObjectsBegin = data.mObjects.size();
|
||||
|
||||
WorldspaceNavMeshInput& navMeshInput = [&] () -> WorldspaceNavMeshInput&
|
||||
{
|
||||
auto it = navMeshInputs.find(cell.mCellId.mWorldspace);
|
||||
if (it == navMeshInputs.end())
|
||||
{
|
||||
it = navMeshInputs.emplace(cell.mCellId.mWorldspace,
|
||||
std::make_unique<WorldspaceNavMeshInput>(cell.mCellId.mWorldspace, settings.mRecast)).first;
|
||||
it->second->mTileCachedRecastMeshManager.setWorldspace(cell.mCellId.mWorldspace);
|
||||
}
|
||||
return *it->second;
|
||||
} ();
|
||||
|
||||
if (exterior)
|
||||
{
|
||||
const auto it = std::lower_bound(esmData.mLands.begin(), esmData.mLands.end(), cellPosition, LessByXY {});
|
||||
const auto [heightfieldShape, minHeight, maxHeight] = makeHeightfieldShape(
|
||||
it == esmData.mLands.end() ? std::optional<ESM::Land>() : *it,
|
||||
cellPosition, data.mHeightfields, data.mLandData
|
||||
);
|
||||
|
||||
mergeOrAssign(getAabb(cellPosition, minHeight, maxHeight),
|
||||
navMeshInput.mAabb, navMeshInput.mAabbInitialized);
|
||||
|
||||
navMeshInput.mTileCachedRecastMeshManager.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, heightfieldShape);
|
||||
|
||||
navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, ESM::Land::REAL_SIZE, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((cell.mData.mFlags & ESM::Cell::HasWater) != 0)
|
||||
navMeshInput.mTileCachedRecastMeshManager.addWater(cellPosition, std::numeric_limits<int>::max(), cell.mWater);
|
||||
}
|
||||
|
||||
forEachObject(cell, esmData, vfs, bulletShapeManager, readers,
|
||||
[&] (BulletObject object)
|
||||
{
|
||||
const btTransform& transform = object.getCollisionObject().getWorldTransform();
|
||||
const btAABB aabb = BulletHelpers::getAabb(*object.getCollisionObject().getCollisionShape(), transform);
|
||||
mergeOrAssign(aabb, navMeshInput.mAabb, navMeshInput.mAabbInitialized);
|
||||
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
||||
navMeshInput.mAabb.merge(BulletHelpers::getAabb(*avoid, transform));
|
||||
|
||||
const ObjectId objectId(++objectsCounter);
|
||||
const CollisionShape shape(object.getShapeInstance(), *object.getCollisionObject().getCollisionShape(), object.getObjectTransform());
|
||||
|
||||
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, shape, transform, DetourNavigator::AreaType_ground);
|
||||
|
||||
if (const btCollisionShape* avoid = object.getShapeInstance()->mAvoidCollisionShape.get())
|
||||
{
|
||||
const CollisionShape avoidShape(object.getShapeInstance(), *avoid, object.getObjectTransform());
|
||||
navMeshInput.mTileCachedRecastMeshManager.addObject(objectId, avoidShape, transform, DetourNavigator::AreaType_null);
|
||||
}
|
||||
|
||||
data.mObjects.emplace_back(std::move(object));
|
||||
});
|
||||
|
||||
Log(Debug::Info) << "Processed " << (exterior ? "exterior" : "interior")
|
||||
<< " cell (" << (i + 1) << "/" << esmData.mCells.size() << ") " << cell.getDescription()
|
||||
<< " with " << (data.mObjects.size() - cellObjectsBegin) << " objects";
|
||||
}
|
||||
|
||||
data.mNavMeshInputs.reserve(navMeshInputs.size());
|
||||
std::transform(navMeshInputs.begin(), navMeshInputs.end(), std::back_inserter(data.mNavMeshInputs),
|
||||
[] (auto& v) { return std::move(v.second); });
|
||||
|
||||
Log(Debug::Info) << "Processed " << esmData.mCells.size() << " cells, added "
|
||||
<< data.mObjects.size() << " objects and " << data.mHeightfields.size() << " height fields";
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
97
apps/navmeshtool/worldspacedata.hpp
Normal file
97
apps/navmeshtool/worldspacedata.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
#ifndef OPENMW_NAVMESHTOOL_WORLDSPACEDATA_H
|
||||
#define OPENMW_NAVMESHTOOL_WORLDSPACEDATA_H
|
||||
|
||||
#include <components/bullethelpers/collisionobject.hpp>
|
||||
#include <components/detournavigator/tilecachedrecastmeshmanager.hpp>
|
||||
#include <components/esm/loadland.hpp>
|
||||
#include <components/misc/convert.hpp>
|
||||
#include <components/resource/bulletshape.hpp>
|
||||
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||
#include <BulletCollision/Gimpact/btBoxCollision.h>
|
||||
#include <LinearMath/btVector3.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
}
|
||||
|
||||
namespace VFS
|
||||
{
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class BulletShapeManager;
|
||||
}
|
||||
|
||||
namespace EsmLoader
|
||||
{
|
||||
struct EsmData;
|
||||
}
|
||||
|
||||
namespace DetourNavigator
|
||||
{
|
||||
struct Settings;
|
||||
}
|
||||
|
||||
namespace NavMeshTool
|
||||
{
|
||||
using DetourNavigator::TileCachedRecastMeshManager;
|
||||
using DetourNavigator::ObjectTransform;
|
||||
|
||||
struct WorldspaceNavMeshInput
|
||||
{
|
||||
std::string mWorldspace;
|
||||
TileCachedRecastMeshManager mTileCachedRecastMeshManager;
|
||||
btAABB mAabb;
|
||||
bool mAabbInitialized = false;
|
||||
|
||||
explicit WorldspaceNavMeshInput(std::string worldspace, const DetourNavigator::RecastSettings& settings);
|
||||
};
|
||||
|
||||
class BulletObject
|
||||
{
|
||||
public:
|
||||
BulletObject(osg::ref_ptr<Resource::BulletShapeInstance>&& shapeInstance, const ESM::Position& position,
|
||||
float localScaling)
|
||||
: mShapeInstance(std::move(shapeInstance))
|
||||
, mObjectTransform {position, localScaling}
|
||||
, mCollisionObject(BulletHelpers::makeCollisionObject(
|
||||
mShapeInstance->mCollisionShape.get(),
|
||||
Misc::Convert::toBullet(position.asVec3()),
|
||||
Misc::Convert::toBullet(Misc::Convert::makeOsgQuat(position))
|
||||
))
|
||||
{
|
||||
mShapeInstance->setLocalScaling(btVector3(localScaling, localScaling, localScaling));
|
||||
}
|
||||
|
||||
const osg::ref_ptr<Resource::BulletShapeInstance>& getShapeInstance() const noexcept { return mShapeInstance; }
|
||||
const DetourNavigator::ObjectTransform& getObjectTransform() const noexcept { return mObjectTransform; }
|
||||
btCollisionObject& getCollisionObject() const noexcept { return *mCollisionObject; }
|
||||
|
||||
private:
|
||||
osg::ref_ptr<Resource::BulletShapeInstance> mShapeInstance;
|
||||
DetourNavigator::ObjectTransform mObjectTransform;
|
||||
std::unique_ptr<btCollisionObject> mCollisionObject;
|
||||
};
|
||||
|
||||
struct WorldspaceData
|
||||
{
|
||||
std::vector<std::unique_ptr<WorldspaceNavMeshInput>> mNavMeshInputs;
|
||||
std::vector<BulletObject> mObjects;
|
||||
std::vector<std::unique_ptr<ESM::Land::LandData>> mLandData;
|
||||
std::vector<std::vector<float>> mHeightfields;
|
||||
};
|
||||
|
||||
WorldspaceData gatherWorldspaceData(const DetourNavigator::Settings& settings, std::vector<ESM::ESMReader>& readers,
|
||||
const VFS::Manager& vfs, Resource::BulletShapeManager& bulletShapeManager, const EsmLoader::EsmData& esmData,
|
||||
bool processInteriorCells);
|
||||
}
|
||||
|
||||
#endif
|
@ -2,8 +2,8 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/nif/niffile.hpp>
|
||||
#include <components/files/constrainedfilestream.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
@ -18,18 +18,10 @@ namespace bpo = boost::program_options;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
///See if the file has the named extension
|
||||
bool hasExtension(std::string filename, std::string extensionToFind)
|
||||
bool hasExtension(std::string filename, std::string extensionToFind)
|
||||
{
|
||||
std::string extension = filename.substr(filename.find_last_of('.')+1);
|
||||
|
||||
//Convert strings to lower case for comparison
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
|
||||
std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);
|
||||
|
||||
if(extension == extensionToFind)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return Misc::StringUtils::ciEqual(extension, extensionToFind);
|
||||
}
|
||||
|
||||
///See if the file has the "nif" extension.
|
||||
|
@ -183,8 +183,7 @@ if(APPLE)
|
||||
set(OPENCS_BUNDLE_NAME "OpenMW-CS")
|
||||
set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources")
|
||||
|
||||
set(OPENMW_MYGUI_FILES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
|
||||
set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
|
||||
set(OPENMW_RESOURCES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
|
||||
|
||||
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)
|
||||
|
||||
|
@ -88,16 +88,16 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||
boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
|
||||
|
||||
desc.add_options()
|
||||
("data", boost::program_options::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), "data")->multitoken()->composing())
|
||||
("data-local", boost::program_options::value<Files::EscapePath>()->default_value(Files::EscapePath(), ""))
|
||||
("data", boost::program_options::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "data")->multitoken()->composing())
|
||||
("data-local", boost::program_options::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(Files::MaybeQuotedPathContainer::value_type(), ""))
|
||||
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
||||
("encoding", boost::program_options::value<Files::EscapeHashString>()->default_value("win1252"))
|
||||
("resources", boost::program_options::value<Files::EscapePath>()->default_value(Files::EscapePath(), "resources"))
|
||||
("fallback-archive", boost::program_options::value<Files::EscapeStringVector>()->
|
||||
default_value(Files::EscapeStringVector(), "fallback-archive")->multitoken())
|
||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"))
|
||||
("resources", boost::program_options::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"))
|
||||
("fallback-archive", boost::program_options::value<std::vector<std::string>>()->
|
||||
default_value(std::vector<std::string>(), "fallback-archive")->multitoken())
|
||||
("fallback", boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")
|
||||
->multitoken()->composing(), "fallback values")
|
||||
("script-blacklist", boost::program_options::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), "")
|
||||
("script-blacklist", boost::program_options::value<std::vector<std::string>>()->default_value(std::vector<std::string>(), "")
|
||||
->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)")
|
||||
("script-blacklist-use", boost::program_options::value<bool>()->implicit_value(true)
|
||||
->default_value(true), "enable script blacklisting");
|
||||
@ -108,24 +108,24 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||
|
||||
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
|
||||
|
||||
mEncodingName = variables["encoding"].as<Files::EscapeHashString>().toStdString();
|
||||
mEncodingName = variables["encoding"].as<std::string>();
|
||||
mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName));
|
||||
mFileDialog.setEncoding (QString::fromUtf8(mEncodingName.c_str()));
|
||||
|
||||
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapePath>().mPath);
|
||||
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::MaybeQuotedPath>());
|
||||
|
||||
if (variables["script-blacklist-use"].as<bool>())
|
||||
mDocumentManager.setBlacklistedScripts (
|
||||
variables["script-blacklist"].as<Files::EscapeStringVector>().toStdStringVector());
|
||||
variables["script-blacklist"].as<std::vector<std::string>>());
|
||||
|
||||
mFsStrict = variables["fs-strict"].as<bool>();
|
||||
|
||||
Files::PathContainer dataDirs, dataLocal;
|
||||
if (!variables["data"].empty()) {
|
||||
dataDirs = Files::PathContainer(Files::EscapePath::toPathContainer(variables["data"].as<Files::EscapePathContainer>()));
|
||||
dataDirs = asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>());
|
||||
}
|
||||
|
||||
Files::PathContainer::value_type local(variables["data-local"].as<Files::EscapePath>().mPath);
|
||||
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>());
|
||||
if (!local.empty())
|
||||
dataLocal.push_back(local);
|
||||
|
||||
@ -149,13 +149,9 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
|
||||
|
||||
//iterate the data directories and add them to the file dialog for loading
|
||||
for (Files::PathContainer::const_reverse_iterator iter = dataDirs.rbegin(); iter != dataDirs.rend(); ++iter)
|
||||
{
|
||||
QString path = QString::fromUtf8 (iter->string().c_str());
|
||||
mFileDialog.addFiles(path);
|
||||
}
|
||||
mFileDialog.addFiles(dataDirs);
|
||||
|
||||
return std::make_pair (dataDirs, variables["fallback-archive"].as<Files::EscapeStringVector>().toStdStringVector());
|
||||
return std::make_pair (dataDirs, variables["fallback-archive"].as<std::vector<std::string>>());
|
||||
}
|
||||
|
||||
void CS::Editor::createGame()
|
||||
|
@ -17,6 +17,19 @@
|
||||
#include "textnode.hpp"
|
||||
#include "valuenode.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isAlpha(char c)
|
||||
{
|
||||
return std::isalpha(static_cast<unsigned char>(c));
|
||||
}
|
||||
|
||||
bool isDigit(char c)
|
||||
{
|
||||
return std::isdigit(static_cast<unsigned char>(c));
|
||||
}
|
||||
}
|
||||
|
||||
namespace CSMFilter
|
||||
{
|
||||
struct Token
|
||||
@ -103,7 +116,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken()
|
||||
{
|
||||
char c = mInput[mIndex];
|
||||
|
||||
if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' ||
|
||||
if (isAlpha(c) || c==':' || c=='_' || (!string.empty() && isDigit(c)) || c=='"' ||
|
||||
(!string.empty() && string[0]=='"'))
|
||||
string += c;
|
||||
else
|
||||
@ -150,7 +163,7 @@ CSMFilter::Token CSMFilter::Parser::getNumberToken()
|
||||
{
|
||||
char c = mInput[mIndex];
|
||||
|
||||
if (std::isdigit (c))
|
||||
if (isDigit(c))
|
||||
{
|
||||
string += c;
|
||||
hasDigit = true;
|
||||
@ -225,10 +238,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken()
|
||||
case '!': ++mIndex; return Token (Token::Type_OneShot);
|
||||
}
|
||||
|
||||
if (c=='"' || c=='_' || std::isalpha (c) || c==':')
|
||||
if (c=='"' || c=='_' || isAlpha(c) || c==':')
|
||||
return getStringToken();
|
||||
|
||||
if (c=='-' || c=='.' || std::isdigit (c))
|
||||
if (c=='-' || c=='.' || isDigit(c))
|
||||
return getNumberToken();
|
||||
|
||||
error();
|
||||
|
@ -223,6 +223,7 @@ void CSMPrefs::State::declare()
|
||||
declareColour ("scene-night-gradient-colour", "Scene Night Gradient Colour", QColor (47, 51, 51, 255)).
|
||||
setTooltip("Sets the gradient color to use in conjunction with the night background color. Ignored if "
|
||||
"the gradient option is disabled.");
|
||||
declareBool("scene-day-night-switch-nodes", "Use Day/Night Switch Nodes", true);
|
||||
|
||||
declareCategory ("Tooltips");
|
||||
declareBool ("scene", "Show Tooltips in 3D scenes", true);
|
||||
|
@ -255,7 +255,7 @@ namespace CSMWorld
|
||||
{ ColumnId_AiWanderDist, "Wander Dist" },
|
||||
{ ColumnId_AiDuration, "Ai Duration" },
|
||||
{ ColumnId_AiWanderToD, "Wander ToD" },
|
||||
{ ColumnId_AiWanderRepeat, "Wander Repeat" },
|
||||
{ ColumnId_AiWanderRepeat, "Ai Repeat" },
|
||||
{ ColumnId_AiActivateName, "Activate" },
|
||||
{ ColumnId_AiTargetId, "Target ID" },
|
||||
{ ColumnId_AiTargetCell, "Target Cell" },
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/cellid.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "collectionbase.hpp"
|
||||
#include "columnbase.hpp"
|
||||
@ -354,8 +355,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import
|
||||
for (int i = 0; i < idCollection()->getSize(); ++i)
|
||||
{
|
||||
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(i));
|
||||
std::string texture = record.get().mTexture;
|
||||
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
|
||||
std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture);
|
||||
if (record.isModified())
|
||||
reverseLookupMap.emplace(texture, idCollection()->getId(i));
|
||||
}
|
||||
@ -376,8 +376,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import
|
||||
|
||||
// Look for a pre-existing record
|
||||
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow));
|
||||
std::string texture = record.get().mTexture;
|
||||
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
|
||||
std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture);
|
||||
auto searchIt = reverseLookupMap.find(texture);
|
||||
if (searchIt != reverseLookupMap.end())
|
||||
{
|
||||
|
@ -1678,7 +1678,7 @@ namespace CSMWorld
|
||||
newRow.mWander.mTimeOfDay = 0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
newRow.mWander.mIdle[i] = 0;
|
||||
newRow.mWander.mShouldRepeat = 0;
|
||||
newRow.mWander.mShouldRepeat = 1;
|
||||
newRow.mCellName = "";
|
||||
|
||||
if (position >= (int)list.size())
|
||||
@ -1784,9 +1784,15 @@ namespace CSMWorld
|
||||
return static_cast<int>(content.mWander.mIdle[subColIndex-4]);
|
||||
else
|
||||
return QVariant();
|
||||
case 12: // wander repeat
|
||||
case 12: // repeat
|
||||
if (content.mType == ESM::AI_Wander)
|
||||
return content.mWander.mShouldRepeat != 0;
|
||||
else if (content.mType == ESM::AI_Travel)
|
||||
return content.mTravel.mShouldRepeat != 0;
|
||||
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
|
||||
return content.mTarget.mShouldRepeat != 0;
|
||||
else if (content.mType == ESM::AI_Activate)
|
||||
return content.mActivate.mShouldRepeat != 0;
|
||||
else
|
||||
return QVariant();
|
||||
case 13: // activate name
|
||||
@ -1895,6 +1901,12 @@ namespace CSMWorld
|
||||
case 12:
|
||||
if (content.mType == ESM::AI_Wander)
|
||||
content.mWander.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||
else if (content.mType == ESM::AI_Travel)
|
||||
content.mTravel.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
|
||||
content.mTarget.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||
else if (content.mType == ESM::AI_Activate)
|
||||
content.mActivate.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||
else
|
||||
return; // return without saving
|
||||
|
||||
|
@ -28,9 +28,14 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) :
|
||||
mAdjusterWidget = new AdjusterWidget (this);
|
||||
}
|
||||
|
||||
void CSVDoc::FileDialog::addFiles(const QString &path)
|
||||
void CSVDoc::FileDialog::addFiles(const std::vector<boost::filesystem::path>& dataDirs)
|
||||
{
|
||||
mSelector->addFiles(path);
|
||||
for (auto iter = dataDirs.rbegin(); iter != dataDirs.rend(); ++iter)
|
||||
{
|
||||
QString path = QString::fromUtf8(iter->string().c_str());
|
||||
mSelector->addFiles(path);
|
||||
}
|
||||
mSelector->sortFiles();
|
||||
}
|
||||
|
||||
void CSVDoc::FileDialog::setEncoding(const QString &encoding)
|
||||
|
@ -45,7 +45,7 @@ namespace CSVDoc
|
||||
explicit FileDialog(QWidget *parent = nullptr);
|
||||
void showDialog (ContentAction action);
|
||||
|
||||
void addFiles (const QString &path);
|
||||
void addFiles(const std::vector<boost::filesystem::path>& dataDirs);
|
||||
void setEncoding (const QString &encoding);
|
||||
void clearFiles ();
|
||||
|
||||
|
@ -3,9 +3,12 @@
|
||||
#include <osg/LightSource>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Switch>
|
||||
#include <osg/ValueObject>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
#include "../../model/prefs/state.hpp"
|
||||
|
||||
class DayNightSwitchVisitor : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
@ -16,8 +19,33 @@ public:
|
||||
|
||||
void apply(osg::Switch &switchNode) override
|
||||
{
|
||||
if (switchNode.getName() == Constants::NightDayLabel)
|
||||
switchNode.setSingleChildOn(mIndex);
|
||||
constexpr int NoIndex = -1;
|
||||
|
||||
int initialIndex = NoIndex;
|
||||
if (!switchNode.getUserValue("initialIndex", initialIndex))
|
||||
{
|
||||
for (size_t i = 0; i < switchNode.getValueList().size(); ++i)
|
||||
{
|
||||
if (switchNode.getValueList()[i])
|
||||
{
|
||||
initialIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (initialIndex != NoIndex)
|
||||
switchNode.setUserValue("initialIndex", initialIndex);
|
||||
}
|
||||
|
||||
if (CSMPrefs::get()["Rendering"]["scene-day-night-switch-nodes"].isTrue())
|
||||
{
|
||||
if (switchNode.getName() == Constants::NightDayLabel)
|
||||
switchNode.setSingleChildOn(mIndex);
|
||||
}
|
||||
else if (initialIndex != NoIndex)
|
||||
{
|
||||
switchNode.setSingleChildOn(initialIndex);
|
||||
}
|
||||
|
||||
traverse(switchNode);
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
|
||||
const float OuterRadius = InnerRadius + MarkerShaftWidth;
|
||||
|
||||
const float SegmentDistance = 100.f;
|
||||
const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * osg::PI / SegmentDistance)));
|
||||
const size_t SegmentCount = std::clamp<int>(OuterRadius * 2 * osg::PI / SegmentDistance, 24, 64);
|
||||
const size_t VerticesPerSegment = 4;
|
||||
const size_t IndicesPerSegment = 24;
|
||||
|
||||
|
@ -550,6 +550,11 @@ void SceneWidget::settingChanged (const CSMPrefs::Setting *setting)
|
||||
{
|
||||
updateCameraParameters();
|
||||
}
|
||||
else if (*setting == "Rendering/scene-day-night-switch-nodes")
|
||||
{
|
||||
if (mLighting)
|
||||
setLighting(mLighting);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderWidget::updateCameraParameters(double overrideAspect)
|
||||
|
@ -25,6 +25,8 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
|
||||
else
|
||||
mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this);
|
||||
|
||||
mScene->setExterior(true);
|
||||
|
||||
CSVWidget::SceneToolbar *toolbar = new CSVWidget::SceneToolbar (48+6, this);
|
||||
|
||||
CSVWidget::SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar);
|
||||
|
@ -27,7 +27,7 @@ add_openmw_dir (mwrender
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
actions actionmanager bindingsmanager controllermanager controlswitch
|
||||
inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager
|
||||
inputmanagerimp mousemanager keyboardmanager sensormanager
|
||||
)
|
||||
|
||||
add_openmw_dir (mwgui
|
||||
@ -74,7 +74,7 @@ add_openmw_dir (mwworld
|
||||
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
||||
store esmstore fallback actionrepair actionsoulgem livecellref actiondoor
|
||||
contentloader esmloader actiontrap cellreflist cellref weather projectilemanager
|
||||
cellpreloader datetimemanager
|
||||
cellpreloader datetimemanager groundcoverstore magiceffects
|
||||
)
|
||||
|
||||
add_openmw_dir (mwphysics
|
||||
@ -153,9 +153,12 @@ target_link_libraries(openmw
|
||||
"osg-ffmpeg-videoplayer"
|
||||
"oics"
|
||||
components
|
||||
${LUA_LIBRARIES}
|
||||
)
|
||||
|
||||
if (MSVC AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.16)
|
||||
target_precompile_headers(openmw PRIVATE ${SOL_INCLUDE_DIR}/sol/sol.hpp)
|
||||
endif ()
|
||||
|
||||
if (ANDROID)
|
||||
target_link_libraries(openmw EGL android log z)
|
||||
endif (ANDROID)
|
||||
@ -176,8 +179,7 @@ endif()
|
||||
if(APPLE)
|
||||
set(BUNDLE_RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources")
|
||||
|
||||
set(OPENMW_MYGUI_FILES_ROOT ${BUNDLE_RESOURCES_DIR})
|
||||
set(OPENMW_SHADERS_ROOT ${BUNDLE_RESOURCES_DIR})
|
||||
set(OPENMW_RESOURCES_ROOT ${BUNDLE_RESOURCES_DIR})
|
||||
|
||||
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
#ifndef stderr
|
||||
int stderr = 0; // Hack: fix linker error
|
||||
#endif
|
||||
|
||||
#include "SDL_main.h"
|
||||
#include <SDL_gamecontroller.h>
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <osg/Version>
|
||||
|
||||
#include <osgViewer/ViewerEventHandlers>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/WriteFile>
|
||||
@ -37,11 +39,11 @@
|
||||
|
||||
#include <components/version/version.hpp>
|
||||
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
|
||||
#include <components/misc/frameratelimiter.hpp>
|
||||
|
||||
#include <components/sceneutil/screencapture.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
|
||||
#include "mwinput/inputmanagerimp.hpp"
|
||||
|
||||
@ -238,6 +240,20 @@ namespace
|
||||
{
|
||||
void operator()(std::string) const {}
|
||||
};
|
||||
|
||||
class IdentifyOpenGLOperation : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
IdentifyOpenGLOperation() : GraphicsOperation("IdentifyOpenGLOperation", false)
|
||||
{}
|
||||
|
||||
void operator()(osg::GraphicsContext* graphicsContext) override
|
||||
{
|
||||
Log(Debug::Info) << "OpenGL Vendor: " << glGetString(GL_VENDOR);
|
||||
Log(Debug::Info) << "OpenGL Renderer: " << glGetString(GL_RENDERER);
|
||||
Log(Debug::Info) << "OpenGL Version: " << glGetString(GL_VERSION);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void OMW::Engine::executeLocalScripts()
|
||||
@ -293,6 +309,10 @@ bool OMW::Engine::frame(float frametime)
|
||||
// Main menu opened? Then scripts are also paused.
|
||||
bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu);
|
||||
|
||||
// Should be called after input manager update and before any change to the game world.
|
||||
// It applies to the game world queued changes from the previous frame.
|
||||
mLuaManager->synchronizedUpdate();
|
||||
|
||||
// update game state
|
||||
{
|
||||
ScopedProfile<UserStatsType::State> profile(frameStart, frameNumber, *timer, *stats);
|
||||
@ -546,6 +566,10 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||
if(fullscreen)
|
||||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
|
||||
// Allows for Windows snapping features to properly work in borderless window
|
||||
SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "1");
|
||||
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1");
|
||||
|
||||
if (!windowBorder)
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
|
||||
@ -634,8 +658,12 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||
camera->setGraphicsContext(graphicsWindow);
|
||||
camera->setViewport(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height);
|
||||
|
||||
osg::ref_ptr<SceneUtil::OperationSequence> realizeOperations = new SceneUtil::OperationSequence(false);
|
||||
mViewer->setRealizeOperation(realizeOperations);
|
||||
realizeOperations->add(new IdentifyOpenGLOperation());
|
||||
|
||||
if (Debug::shouldDebugOpenGL())
|
||||
mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation());
|
||||
realizeOperations->add(new Debug::EnableGLDebugOperation());
|
||||
|
||||
mViewer->realize();
|
||||
|
||||
@ -709,7 +737,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||
|
||||
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||
|
||||
mLuaManager = new MWLua::LuaManager(mVFS.get());
|
||||
mLuaManager = new MWLua::LuaManager(mVFS.get(), (mResDir / "lua_libs").string());
|
||||
mEnvironment.setLuaManager(mLuaManager);
|
||||
|
||||
// Create input and UI first to set up a bootstrapping environment for
|
||||
@ -753,6 +781,28 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||
|
||||
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
|
||||
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f);
|
||||
bool enableReverseZ = false;
|
||||
|
||||
if (Settings::Manager::getBool("reverse z", "Camera"))
|
||||
{
|
||||
if (exts && exts->isClipControlSupported)
|
||||
{
|
||||
enableReverseZ = true;
|
||||
Log(Debug::Info) << "Using reverse-z depth buffer";
|
||||
}
|
||||
else
|
||||
Log(Debug::Warning) << "GL_ARB_clip_control not supported: disabling reverse-z depth buffer";
|
||||
}
|
||||
else
|
||||
Log(Debug::Info) << "Using standard depth buffer";
|
||||
|
||||
SceneUtil::AutoDepth::setReversed(enableReverseZ);
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3, 6, 6)
|
||||
// hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028
|
||||
if (exts)
|
||||
exts->glRenderbufferStorageMultisampleCoverageNV = nullptr;
|
||||
#endif
|
||||
|
||||
std::string myguiResources = (mResDir / "mygui").string();
|
||||
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
|
||||
@ -831,6 +881,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||
}
|
||||
|
||||
mLuaManager->init();
|
||||
mLuaManager->loadPermanentStorage(mCfgMgr.getUserConfigPath().string());
|
||||
}
|
||||
|
||||
class OMW::Engine::LuaWorker
|
||||
@ -842,10 +893,8 @@ public:
|
||||
mThread = std::thread([this]{ threadBody(); });
|
||||
};
|
||||
|
||||
void allowUpdate(double dt)
|
||||
void allowUpdate()
|
||||
{
|
||||
mDt = dt;
|
||||
mIsGuiMode = mEngine->mEnvironment.getWindowManager()->isGuiMode();
|
||||
if (!mThread)
|
||||
return;
|
||||
{
|
||||
@ -864,7 +913,6 @@ public:
|
||||
}
|
||||
else
|
||||
update();
|
||||
mEngine->mLuaManager->applyQueuedChanges();
|
||||
};
|
||||
|
||||
void join()
|
||||
@ -888,7 +936,7 @@ private:
|
||||
const unsigned int frameNumber = viewer->getFrameStamp()->getFrameNumber();
|
||||
ScopedProfile<UserStatsType::Lua> profile(frameStart, frameNumber, *osg::Timer::instance(), *viewer->getViewerStats());
|
||||
|
||||
mEngine->mLuaManager->update(mIsGuiMode, mDt);
|
||||
mEngine->mLuaManager->update();
|
||||
}
|
||||
|
||||
void threadBody()
|
||||
@ -913,8 +961,6 @@ private:
|
||||
std::condition_variable mCV;
|
||||
bool mUpdateRequest = false;
|
||||
bool mJoinRequest = false;
|
||||
double mDt = 0;
|
||||
bool mIsGuiMode = false;
|
||||
std::optional<std::thread> mThread;
|
||||
};
|
||||
|
||||
@ -1027,7 +1073,7 @@ void OMW::Engine::go()
|
||||
|
||||
mEnvironment.getWorld()->updateWindowManager();
|
||||
|
||||
luaWorker.allowUpdate(dt); // if there is a separate Lua thread, it starts the update now
|
||||
luaWorker.allowUpdate(); // if there is a separate Lua thread, it starts the update now
|
||||
|
||||
mViewer->renderingTraversals();
|
||||
|
||||
@ -1058,8 +1104,7 @@ void OMW::Engine::go()
|
||||
|
||||
// Save user settings
|
||||
settings.saveUser(settingspath);
|
||||
|
||||
mViewer->stopThreading();
|
||||
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath().string());
|
||||
|
||||
Log(Debug::Info) << "Quitting peacefully.";
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <components/version/version.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/files/escape.hpp>
|
||||
#include <components/fallback/fallback.hpp>
|
||||
#include <components/fallback/validate.hpp>
|
||||
#include <components/debug/debugging.hpp>
|
||||
@ -57,7 +56,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||
{
|
||||
cfgMgr.readConfiguration(variables, desc, true);
|
||||
|
||||
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::EscapePath>().mPath.string());
|
||||
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::MaybeQuotedPath>().string());
|
||||
getRawStdout() << v.describe() << std::endl;
|
||||
return false;
|
||||
}
|
||||
@ -66,38 +65,38 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||
cfgMgr.readConfiguration(variables, desc);
|
||||
Files::mergeComposingVariables(variables, composingVariables, desc);
|
||||
|
||||
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::EscapePath>().mPath.string());
|
||||
Version::Version v = Version::getOpenmwVersion(variables["resources"].as<Files::MaybeQuotedPath>().string());
|
||||
Log(Debug::Info) << v.describe();
|
||||
|
||||
engine.setGrabMouse(!variables["no-grab"].as<bool>());
|
||||
|
||||
// Font encoding settings
|
||||
std::string encoding(variables["encoding"].as<Files::EscapeHashString>().toStdString());
|
||||
std::string encoding(variables["encoding"].as<std::string>());
|
||||
Log(Debug::Info) << ToUTF8::encodingUsingMessage(encoding);
|
||||
engine.setEncoding(ToUTF8::calculateEncoding(encoding));
|
||||
|
||||
// directory settings
|
||||
engine.enableFSStrict(variables["fs-strict"].as<bool>());
|
||||
|
||||
Files::PathContainer dataDirs(Files::EscapePath::toPathContainer(variables["data"].as<Files::EscapePathContainer>()));
|
||||
Files::PathContainer dataDirs(asPathContainer(variables["data"].as<Files::MaybeQuotedPathContainer>()));
|
||||
|
||||
Files::PathContainer::value_type local(variables["data-local"].as<Files::EscapePath>().mPath);
|
||||
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>());
|
||||
if (!local.empty())
|
||||
dataDirs.push_back(local);
|
||||
|
||||
cfgMgr.processPaths(dataDirs);
|
||||
|
||||
engine.setResourceDir(variables["resources"].as<Files::EscapePath>().mPath);
|
||||
engine.setResourceDir(variables["resources"].as<Files::MaybeQuotedPath>());
|
||||
engine.setDataDirs(dataDirs);
|
||||
|
||||
// fallback archives
|
||||
StringsVector archives = variables["fallback-archive"].as<Files::EscapeStringVector>().toStdStringVector();
|
||||
StringsVector archives = variables["fallback-archive"].as<StringsVector>();
|
||||
for (StringsVector::const_iterator it = archives.begin(); it != archives.end(); ++it)
|
||||
{
|
||||
engine.addArchive(*it);
|
||||
}
|
||||
|
||||
StringsVector content = variables["content"].as<Files::EscapeStringVector>().toStdStringVector();
|
||||
StringsVector content = variables["content"].as<StringsVector>();
|
||||
if (content.empty())
|
||||
{
|
||||
Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting...";
|
||||
@ -118,7 +117,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||
engine.addContentFile(file);
|
||||
}
|
||||
|
||||
StringsVector groundcover = variables["groundcover"].as<Files::EscapeStringVector>().toStdStringVector();
|
||||
StringsVector groundcover = variables["groundcover"].as<StringsVector>();
|
||||
for (auto& file : groundcover)
|
||||
{
|
||||
engine.addGroundcoverFile(file);
|
||||
@ -131,7 +130,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||
}
|
||||
|
||||
// startup-settings
|
||||
engine.setCell(variables["start"].as<Files::EscapeHashString>().toStdString());
|
||||
engine.setCell(variables["start"].as<std::string>());
|
||||
engine.setSkipMenu (variables["skip-menu"].as<bool>(), variables["new-game"].as<bool>());
|
||||
if (!variables["skip-menu"].as<bool>() && variables["new-game"].as<bool>())
|
||||
Log(Debug::Warning) << "Warning: new-game used without skip-menu -> ignoring it";
|
||||
@ -140,11 +139,11 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||
engine.setCompileAll(variables["script-all"].as<bool>());
|
||||
engine.setCompileAllDialogue(variables["script-all-dialogue"].as<bool>());
|
||||
engine.setScriptConsoleMode (variables["script-console"].as<bool>());
|
||||
engine.setStartupScript (variables["script-run"].as<Files::EscapeHashString>().toStdString());
|
||||
engine.setStartupScript (variables["script-run"].as<std::string>());
|
||||
engine.setWarningsMode (variables["script-warn"].as<int>());
|
||||
engine.setScriptBlacklist (variables["script-blacklist"].as<Files::EscapeStringVector>().toStdStringVector());
|
||||
engine.setScriptBlacklist (variables["script-blacklist"].as<StringsVector>());
|
||||
engine.setScriptBlacklistUse (variables["script-blacklist-use"].as<bool>());
|
||||
engine.setSaveGameFile (variables["load-savegame"].as<Files::EscapePath>().mPath.string());
|
||||
engine.setSaveGameFile (variables["load-savegame"].as<Files::MaybeQuotedPath>().string());
|
||||
|
||||
// other settings
|
||||
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
|
||||
|
@ -49,8 +49,8 @@ namespace MWBase
|
||||
virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;
|
||||
virtual void setAttemptJump(bool jumping) = 0;
|
||||
|
||||
virtual void toggleControlSwitch (const std::string& sw, bool value) = 0;
|
||||
virtual bool getControlSwitch (const std::string& sw) = 0;
|
||||
virtual void toggleControlSwitch(std::string_view sw, bool value) = 0;
|
||||
virtual bool getControlSwitch(std::string_view sw) = 0;
|
||||
|
||||
virtual std::string getActionDescription (int action) const = 0;
|
||||
virtual std::string getActionKeyBindingName (int action) const = 0;
|
||||
@ -58,8 +58,8 @@ namespace MWBase
|
||||
virtual bool actionIsActive(int action) const = 0;
|
||||
|
||||
virtual float getActionValue(int action) const = 0; // returns value in range [0, 1]
|
||||
virtual bool isControllerButtonPressed(SDL_GameControllerButton button) const = 0;
|
||||
virtual float getControllerAxisValue(SDL_GameControllerAxis axis) const = 0; // returns value in range [-1, 1]
|
||||
virtual uint32_t getMouseButtonsState() const = 0;
|
||||
virtual int getMouseMoveX() const = 0;
|
||||
virtual int getMouseMoveY() const = 0;
|
||||
|
||||
|
@ -112,6 +112,9 @@ namespace MWBase
|
||||
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
|
||||
virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
|
||||
|
||||
/// Removes an actor and its allies from combat with the actor's targets.
|
||||
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
enum OffenseType
|
||||
{
|
||||
OT_Theft, // Taking items owned by an NPC or a faction you are not a member of
|
||||
|
@ -69,6 +69,7 @@ namespace MWGui
|
||||
class DialogueWindow;
|
||||
class WindowModal;
|
||||
class JailScreen;
|
||||
class MessageBox;
|
||||
|
||||
enum ShowInDialogueMode {
|
||||
ShowInDialogueMode_IfPossible,
|
||||
@ -145,6 +146,7 @@ namespace MWBase
|
||||
virtual MWGui::CountDialog* getCountDialog() = 0;
|
||||
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
|
||||
virtual MWGui::TradeWindow* getTradeWindow() = 0;
|
||||
virtual const std::vector<MWGui::MessageBox*> getActiveMessageBoxes() = 0;
|
||||
|
||||
/// Make the player use an item, while updating GUI state accordingly
|
||||
virtual void useItem(const MWWorld::Ptr& item, bool force=false) = 0;
|
||||
@ -355,6 +357,7 @@ namespace MWBase
|
||||
virtual const std::string& getVersionDescription() const = 0;
|
||||
|
||||
virtual void onDeleteCustomData(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual void forceLootMode(const MWWorld::Ptr& ptr) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,7 @@ namespace MWPhysics
|
||||
namespace MWRender
|
||||
{
|
||||
class Animation;
|
||||
class Camera;
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
@ -433,14 +434,12 @@ namespace MWBase
|
||||
|
||||
virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;
|
||||
|
||||
virtual MWRender::Camera* getCamera() = 0;
|
||||
virtual void togglePOV(bool force = false) = 0;
|
||||
virtual bool isFirstPerson() const = 0;
|
||||
virtual bool isPreviewModeEnabled() const = 0;
|
||||
virtual void togglePreviewMode(bool enable) = 0;
|
||||
virtual bool toggleVanityMode(bool enable) = 0;
|
||||
virtual void allowVanityMode(bool allow) = 0;
|
||||
virtual bool vanityRotateCamera(float * rot) = 0;
|
||||
virtual void adjustCameraDistance(float dist) = 0;
|
||||
virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0;
|
||||
virtual void disableDeferredPreviewRotation() = 0;
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "creature.hpp"
|
||||
|
||||
#include <climits> // INT_MIN
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
@ -758,9 +756,7 @@ namespace MWClass
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
// FIXME: the use of mGoldPool can be replaced with another flag the next time
|
||||
// the save file format is changed
|
||||
if (creatureState.mCreatureStats.mGoldPool == INT_MIN)
|
||||
if (creatureState.mCreatureStats.mMissingACDT)
|
||||
ensureCustomData(ptr);
|
||||
else
|
||||
{
|
||||
|
@ -64,7 +64,13 @@ namespace MWClass
|
||||
if (customData.mSpawn)
|
||||
return;
|
||||
|
||||
MWWorld::Ptr creature = (customData.mSpawnActorId == -1) ? MWWorld::Ptr() : MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
|
||||
MWWorld::Ptr creature;
|
||||
if(customData.mSpawnActorId != -1)
|
||||
{
|
||||
creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
|
||||
if(creature.isEmpty())
|
||||
creature = ptr.getCell()->getMovedActor(customData.mSpawnActorId);
|
||||
}
|
||||
if (!creature.isEmpty())
|
||||
{
|
||||
const MWMechanics::CreatureStats& creatureStats = creature.getClass().getCreatureStats(creature);
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "npc.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <climits> // INT_MIN
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
@ -1302,9 +1301,7 @@ namespace MWClass
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
// FIXME: the use of mGoldPool can be replaced with another flag the next time
|
||||
// the save file format is changed
|
||||
if (npcState.mCreatureStats.mGoldPool == INT_MIN)
|
||||
if (npcState.mCreatureStats.mMissingACDT)
|
||||
ensureCustomData(ptr);
|
||||
else
|
||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||
|
@ -76,9 +76,10 @@ namespace MWDialogue
|
||||
mKnownTopics.insert( Misc::StringUtils::lowerCase(topic) );
|
||||
}
|
||||
|
||||
void DialogueManager::parseText (const std::string& text)
|
||||
std::vector<std::string> DialogueManager::parseTopicIdsFromText (const std::string& text)
|
||||
{
|
||||
updateActorKnownTopics();
|
||||
std::vector<std::string> topicIdList;
|
||||
|
||||
std::vector<HyperTextParser::Token> hypertext = HyperTextParser::parseHyperText(text);
|
||||
|
||||
for (std::vector<HyperTextParser::Token>::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok)
|
||||
@ -95,6 +96,18 @@ namespace MWDialogue
|
||||
topicId = mTranslationDataStorage.topicStandardForm(topicId);
|
||||
}
|
||||
|
||||
topicIdList.push_back(topicId);
|
||||
}
|
||||
|
||||
return topicIdList;
|
||||
}
|
||||
|
||||
void DialogueManager::addTopicsFromText (const std::string& text)
|
||||
{
|
||||
updateActorKnownTopics();
|
||||
|
||||
for (const auto& topicId : parseTopicIdsFromText(text))
|
||||
{
|
||||
if (mActorKnownTopics.count( topicId ))
|
||||
mKnownTopics.insert( topicId );
|
||||
}
|
||||
@ -136,7 +149,6 @@ namespace MWDialogue
|
||||
mTalkedTo = creatureStats.hasTalkedToPlayer();
|
||||
|
||||
mActorKnownTopics.clear();
|
||||
mActorKnownTopicsFlag.clear();
|
||||
|
||||
//greeting
|
||||
const MWWorld::Store<ESM::Dialogue> &dialogs =
|
||||
@ -163,7 +175,7 @@ namespace MWDialogue
|
||||
executeScript (info->mResultScript, mActor);
|
||||
mLastTopic = it->mId;
|
||||
|
||||
parseText (info->mResponse);
|
||||
addTopicsFromText (info->mResponse);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -277,7 +289,10 @@ namespace MWDialogue
|
||||
|
||||
const ESM::Dialogue& dialogue = *dialogues.find (topic);
|
||||
|
||||
const ESM::DialInfo* info = filter.search(dialogue, true);
|
||||
const ESM::DialInfo* info =
|
||||
mChoice == -1 && mActorKnownTopics.count(topic) ?
|
||||
mActorKnownTopics[topic].mInfo : filter.search(dialogue, true);
|
||||
|
||||
if (info)
|
||||
{
|
||||
std::string title;
|
||||
@ -320,7 +335,7 @@ namespace MWDialogue
|
||||
|
||||
executeScript (info->mResultScript, mActor);
|
||||
|
||||
parseText (info->mResponse);
|
||||
addTopicsFromText (info->mResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,7 +354,6 @@ namespace MWDialogue
|
||||
updateGlobals();
|
||||
|
||||
mActorKnownTopics.clear();
|
||||
mActorKnownTopicsFlag.clear();
|
||||
|
||||
const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
||||
|
||||
@ -354,21 +368,41 @@ namespace MWDialogue
|
||||
|
||||
if (answer != nullptr)
|
||||
{
|
||||
int flag = 0;
|
||||
int topicFlags = 0;
|
||||
if(!inJournal(topicId, answer->mId))
|
||||
{
|
||||
// Does this dialogue contains some actor-specific answer?
|
||||
if (Misc::StringUtils::ciEqual(answer->mActor, mActor.getCellRef().getRefId()))
|
||||
flag |= MWBase::DialogueManager::TopicType::Specific;
|
||||
topicFlags |= MWBase::DialogueManager::TopicType::Specific;
|
||||
}
|
||||
else
|
||||
flag |= MWBase::DialogueManager::TopicType::Exhausted;
|
||||
mActorKnownTopics.insert (dialog.mId);
|
||||
mActorKnownTopicsFlag[dialog.mId] = flag;
|
||||
topicFlags |= MWBase::DialogueManager::TopicType::Exhausted;
|
||||
mActorKnownTopics.insert (std::make_pair(dialog.mId, ActorKnownTopicInfo {topicFlags, answer}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If response to a topic leads to a new topic, the original topic is not exhausted.
|
||||
|
||||
for (auto& [dialogId, topicInfo] : mActorKnownTopics)
|
||||
{
|
||||
// If the topic is not marked as exhausted, we don't need to do anything about it.
|
||||
// If the topic will not be shown to the player, the flag actually does not matter.
|
||||
|
||||
if (!(topicInfo.mFlags & MWBase::DialogueManager::TopicType::Exhausted) ||
|
||||
!mKnownTopics.count(dialogId))
|
||||
continue;
|
||||
|
||||
for (const auto& topicId : parseTopicIdsFromText(topicInfo.mInfo->mResponse))
|
||||
{
|
||||
if (mActorKnownTopics.count( topicId ) && !mKnownTopics.count( topicId ))
|
||||
{
|
||||
topicInfo.mFlags &= ~MWBase::DialogueManager::TopicType::Exhausted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::list<std::string> DialogueManager::getAvailableTopics()
|
||||
@ -377,7 +411,7 @@ namespace MWDialogue
|
||||
|
||||
std::list<std::string> keywordList;
|
||||
|
||||
for (const std::string& topic : mActorKnownTopics)
|
||||
for (const auto& [topic, topicInfo] : mActorKnownTopics)
|
||||
{
|
||||
//does the player know the topic?
|
||||
if (mKnownTopics.count(topic))
|
||||
@ -391,7 +425,7 @@ namespace MWDialogue
|
||||
|
||||
int DialogueManager::getTopicFlag(const std::string& topicId)
|
||||
{
|
||||
return mActorKnownTopicsFlag[topicId];
|
||||
return mActorKnownTopics[topicId].mFlags;
|
||||
}
|
||||
|
||||
void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback)
|
||||
@ -421,7 +455,7 @@ namespace MWDialogue
|
||||
// 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::min(100 - zero, std::max(mOriginalDisposition + mPermanentDispositionChange, -zero));
|
||||
int disposition = std::clamp(mOriginalDisposition + mPermanentDispositionChange, -zero, 100 - zero);
|
||||
|
||||
npcStats.setBaseDisposition(disposition);
|
||||
}
|
||||
@ -444,7 +478,7 @@ namespace MWDialogue
|
||||
if (const ESM::DialInfo *info = filter.search (*dialogue, true))
|
||||
{
|
||||
std::string text = info->mResponse;
|
||||
parseText (text);
|
||||
addTopicsFromText (text);
|
||||
|
||||
mChoice = -1;
|
||||
mIsInChoice = false;
|
||||
@ -579,7 +613,7 @@ namespace MWDialogue
|
||||
{
|
||||
const ESM::DialInfo* info = infos[0];
|
||||
|
||||
parseText (info->mResponse);
|
||||
addTopicsFromText (info->mResponse);
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting>& gmsts =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <components/compiler/streamerrorhandler.hpp>
|
||||
#include <components/translation/translation.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/esm/loadinfo.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
@ -24,14 +25,19 @@ namespace MWDialogue
|
||||
{
|
||||
class DialogueManager : public MWBase::DialogueManager
|
||||
{
|
||||
struct ActorKnownTopicInfo
|
||||
{
|
||||
int mFlags;
|
||||
const ESM::DialInfo* mInfo;
|
||||
};
|
||||
|
||||
std::set<std::string, Misc::StringUtils::CiComp> mKnownTopics;// Those are the topics the player knows.
|
||||
|
||||
// Modified faction reactions. <Faction1, <Faction2, Difference> >
|
||||
typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap;
|
||||
ModFactionReactionMap mChangedFactionReaction;
|
||||
|
||||
std::set<std::string, Misc::StringUtils::CiComp> mActorKnownTopics;
|
||||
std::unordered_map<std::string, int> mActorKnownTopicsFlag;
|
||||
std::map<std::string, ActorKnownTopicInfo, Misc::StringUtils::CiComp> mActorKnownTopics;
|
||||
|
||||
Translation::Storage& mTranslationDataStorage;
|
||||
MWScript::CompilerContext mCompilerContext;
|
||||
@ -51,7 +57,8 @@ namespace MWDialogue
|
||||
int mCurrentDisposition;
|
||||
int mPermanentDispositionChange;
|
||||
|
||||
void parseText (const std::string& text);
|
||||
std::vector<std::string> parseTopicIdsFromText (const std::string& text);
|
||||
void addTopicsFromText (const std::string& text);
|
||||
|
||||
void updateActorKnownTopics();
|
||||
void updateGlobals();
|
||||
|
@ -47,19 +47,8 @@ namespace MWDialogue
|
||||
|
||||
void tokenizeKeywords(const std::string & text, std::vector<Token> & tokens)
|
||||
{
|
||||
const MWWorld::Store<ESM::Dialogue> & dialogs =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
|
||||
|
||||
std::vector<std::string> keywordList;
|
||||
keywordList.reserve(dialogs.getSize());
|
||||
for (const auto& it : dialogs)
|
||||
keywordList.push_back(Misc::StringUtils::lowerCase(it.mId));
|
||||
sort(keywordList.begin(), keywordList.end());
|
||||
|
||||
KeywordSearch<std::string, int /*unused*/> keywordSearch;
|
||||
|
||||
for (const auto& it : keywordList)
|
||||
keywordSearch.seed(it, 0 /*unused*/);
|
||||
const auto& keywordSearch =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().getDialogIdKeywordSearch();
|
||||
|
||||
std::vector<KeywordSearch<std::string, int /*unused*/>::Match> matches;
|
||||
keywordSearch.highlightKeywords(text.begin(), text.end(), matches);
|
||||
|
@ -74,13 +74,13 @@ public:
|
||||
return left.mBeg < right.mBeg;
|
||||
}
|
||||
|
||||
void highlightKeywords (Point beg, Point end, std::vector<Match>& out)
|
||||
void highlightKeywords (Point beg, Point end, std::vector<Match>& out) const
|
||||
{
|
||||
std::vector<Match> matches;
|
||||
for (Point i = beg; i != end; ++i)
|
||||
{
|
||||
// check first character
|
||||
typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (Misc::StringUtils::toLower (*i));
|
||||
typename Entry::childen_t::const_iterator candidate = mRoot.mChildren.find (Misc::StringUtils::toLower (*i));
|
||||
|
||||
// no match, on to next character
|
||||
if (candidate == mRoot.mChildren.end ())
|
||||
@ -91,11 +91,11 @@ public:
|
||||
|
||||
// some keywords might be longer variations of other keywords, so we definitely need a list of candidates
|
||||
// the first element in the pair is length of the match, i.e. depth from the first character on
|
||||
std::vector< typename std::pair<int, typename Entry::childen_t::iterator> > candidates;
|
||||
std::vector< typename std::pair<int, typename Entry::childen_t::const_iterator> > candidates;
|
||||
|
||||
while ((j + 1) != end)
|
||||
{
|
||||
typename Entry::childen_t::iterator next = candidate->second.mChildren.find (Misc::StringUtils::toLower (*++j));
|
||||
typename Entry::childen_t::const_iterator next = candidate->second.mChildren.find (Misc::StringUtils::toLower (*++j));
|
||||
|
||||
if (next == candidate->second.mChildren.end ())
|
||||
{
|
||||
@ -116,7 +116,7 @@ public:
|
||||
// shorter candidates will be added to the vector first. however, we want to check against longer candidates first
|
||||
std::reverse(candidates.begin(), candidates.end());
|
||||
|
||||
for (typename std::vector< std::pair<int, typename Entry::childen_t::iterator> >::iterator it = candidates.begin();
|
||||
for (typename std::vector< std::pair<int, typename Entry::childen_t::const_iterator> >::iterator it = candidates.begin();
|
||||
it != candidates.end(); ++it)
|
||||
{
|
||||
candidate = it->second;
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "MyGUI_FactoryManager.h"
|
||||
|
||||
#include <components/misc/utf8stream.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
@ -907,12 +907,6 @@ protected:
|
||||
return {};
|
||||
|
||||
MyGUI::IntPoint pos (left, top);
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
// work around inconsistency in MyGUI where the mouse press coordinates aren't
|
||||
// transformed by the current Layer (even though mouse *move* events are).
|
||||
if(!move)
|
||||
pos = mNode->getLayer()->getPosition(left, top);
|
||||
#endif
|
||||
pos.left -= mCroppedParent->getAbsoluteLeft ();
|
||||
pos.top -= mCroppedParent->getAbsoluteTop ();
|
||||
pos.top += mViewTop;
|
||||
@ -1221,7 +1215,7 @@ public:
|
||||
|
||||
RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo());
|
||||
|
||||
float z = SceneUtil::getReverseZ() ? 1.f : -1.f;
|
||||
float z = SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f;
|
||||
|
||||
GlyphStream glyphStream(textFormat.mFont, static_cast<float>(mCoord.left), static_cast<float>(mCoord.top - mViewTop),
|
||||
z /*mNode->getNodeDepth()*/, vertices, renderXform);
|
||||
|
@ -38,6 +38,7 @@ namespace MWGui
|
||||
, mSortModel(nullptr)
|
||||
, mModel(nullptr)
|
||||
, mSelectedItem(-1)
|
||||
, mTreatNextOpenAsLoot(false)
|
||||
{
|
||||
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
||||
getWidget(mTakeButton, "TakeButton");
|
||||
@ -121,13 +122,15 @@ namespace MWGui
|
||||
|
||||
void ContainerWindow::setPtr(const MWWorld::Ptr& container)
|
||||
{
|
||||
bool lootAnyway = mTreatNextOpenAsLoot;
|
||||
mTreatNextOpenAsLoot = false;
|
||||
mPtr = container;
|
||||
|
||||
bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead();
|
||||
|
||||
if (mPtr.getClass().hasInventoryStore(mPtr))
|
||||
{
|
||||
if (mPtr.getClass().isNpc() && !loot)
|
||||
if (mPtr.getClass().isNpc() && !loot && !lootAnyway)
|
||||
{
|
||||
// we are stealing stuff
|
||||
mModel = new PickpocketItemModel(mPtr, new InventoryItemModel(container),
|
||||
|
@ -37,6 +37,7 @@ namespace MWGui
|
||||
|
||||
void onDeleteCustomData(const MWWorld::Ptr& ptr) override;
|
||||
|
||||
void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; };
|
||||
private:
|
||||
DragAndDrop* mDragAndDrop;
|
||||
|
||||
@ -44,7 +45,7 @@ namespace MWGui
|
||||
SortFilterItemModel* mSortModel;
|
||||
ItemModel* mModel;
|
||||
int mSelectedItem;
|
||||
|
||||
bool mTreatNextOpenAsLoot;
|
||||
MyGUI::Button* mDisposeCorpseButton;
|
||||
MyGUI::Button* mTakeButton;
|
||||
MyGUI::Button* mCloseButton;
|
||||
|
@ -347,8 +347,7 @@ namespace MWGui
|
||||
{
|
||||
if (!mScrollBar->getVisible())
|
||||
return;
|
||||
mScrollBar->setScrollPosition(std::min(static_cast<int>(mScrollBar->getScrollRange()-1),
|
||||
std::max(0, static_cast<int>(mScrollBar->getScrollPosition() - _rel*0.3))));
|
||||
mScrollBar->setScrollPosition(std::clamp<int>(mScrollBar->getScrollPosition() - _rel*0.3, 0, mScrollBar->getScrollRange() - 1));
|
||||
onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition());
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ namespace MWGui
|
||||
{
|
||||
mEnchantmentPoints->setCaption(std::to_string(static_cast<int>(mEnchanting.getEnchantPoints(false))) + " / " + std::to_string(mEnchanting.getMaxEnchantValue()));
|
||||
mCharge->setCaption(std::to_string(mEnchanting.getGemCharge()));
|
||||
mSuccessChance->setCaption(std::to_string(std::max(0, std::min(100, mEnchanting.getEnchantChance()))));
|
||||
mSuccessChance->setCaption(std::to_string(std::clamp(mEnchanting.getEnchantChance(), 0, 100)));
|
||||
mCastCost->setCaption(std::to_string(mEnchanting.getEffectiveCastCost()));
|
||||
mPrice->setCaption(std::to_string(mEnchanting.getEnchantPrice()));
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace MWGui
|
||||
, mMinimap(nullptr)
|
||||
, mCrosshair(nullptr)
|
||||
, mCellNameBox(nullptr)
|
||||
, mDrowningFrame(nullptr)
|
||||
, mDrowningBar(nullptr)
|
||||
, mDrowningFlash(nullptr)
|
||||
, mHealthManaStaminaBaseLeft(0)
|
||||
, mWeapBoxBaseLeft(0)
|
||||
@ -119,6 +119,7 @@ namespace MWGui
|
||||
fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked);
|
||||
|
||||
//Drowning bar
|
||||
getWidget(mDrowningBar, "DrowningBar");
|
||||
getWidget(mDrowningFrame, "DrowningFrame");
|
||||
getWidget(mDrowning, "Drowning");
|
||||
getWidget(mDrowningFlash, "Flash");
|
||||
@ -224,7 +225,7 @@ namespace MWGui
|
||||
|
||||
void HUD::setDrowningBarVisible(bool visible)
|
||||
{
|
||||
mDrowningFrame->setVisible(visible);
|
||||
mDrowningBar->setVisible(visible);
|
||||
}
|
||||
|
||||
void HUD::onWorldClicked(MyGUI::Widget* _sender)
|
||||
@ -368,9 +369,6 @@ namespace MWGui
|
||||
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20));
|
||||
}
|
||||
|
||||
if (mIsDrowning)
|
||||
mDrowningFlashTheta += dt * osg::PI*2;
|
||||
|
||||
mSpellIcons->updateWidgets(mEffectBox, true);
|
||||
|
||||
if (mEnemyActorId != -1 && mEnemyHealth->getVisible())
|
||||
@ -378,8 +376,13 @@ namespace MWGui
|
||||
updateEnemyHealthBar();
|
||||
}
|
||||
|
||||
if (mDrowningBar->getVisible())
|
||||
mDrowningBar->setPosition(mMainWidget->getWidth()/2 - mDrowningFrame->getWidth()/2, mMainWidget->getTop());
|
||||
|
||||
if (mIsDrowning)
|
||||
{
|
||||
mDrowningFlashTheta += dt * osg::PI*2;
|
||||
|
||||
float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f;
|
||||
|
||||
mDrowningFlash->setAlpha(intensity);
|
||||
@ -610,7 +613,7 @@ namespace MWGui
|
||||
|
||||
static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarFade")->mValue.getFloat();
|
||||
if (fNPCHealthBarFade > 0.f)
|
||||
mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade)));
|
||||
mEnemyHealth->setAlpha(std::clamp(mEnemyHealthTimer / fNPCHealthBarFade, 0.f, 1.f));
|
||||
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ namespace MWGui
|
||||
MyGUI::ImageBox* mCrosshair;
|
||||
MyGUI::TextBox* mCellNameBox;
|
||||
MyGUI::TextBox* mWeaponSpellBox;
|
||||
MyGUI::Widget *mDrowningFrame, *mDrowningFlash;
|
||||
MyGUI::Widget *mDrowningBar, *mDrowningFrame, *mDrowningFlash;
|
||||
|
||||
// bottom left elements
|
||||
int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft;
|
||||
|
@ -79,7 +79,7 @@ void KeyboardNavigation::restoreFocus(int mode)
|
||||
if (found != mKeyFocus.end())
|
||||
{
|
||||
MyGUI::Widget* w = found->second;
|
||||
if (w && w->getVisible() && w->getEnabled())
|
||||
if (w && w->getVisible() && w->getEnabled() && w->getInheritedVisible() && w->getInheritedEnabled())
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(found->second);
|
||||
}
|
||||
}
|
||||
@ -93,19 +93,6 @@ void KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget)
|
||||
mCurrentFocus = nullptr;
|
||||
}
|
||||
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
void styleFocusedButton(MyGUI::Widget* w)
|
||||
{
|
||||
if (w)
|
||||
{
|
||||
if (MyGUI::Button* b = w->castType<MyGUI::Button>(false))
|
||||
{
|
||||
b->_setWidgetState("highlighted");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isRootParent(MyGUI::Widget* widget, MyGUI::Widget* root)
|
||||
{
|
||||
while (widget && widget->getParent())
|
||||
@ -128,9 +115,6 @@ void KeyboardNavigation::onFrame()
|
||||
|
||||
if (focus == mCurrentFocus)
|
||||
{
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
styleFocusedButton(mCurrentFocus);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@ -143,19 +127,8 @@ void KeyboardNavigation::onFrame()
|
||||
|
||||
if (focus != mCurrentFocus)
|
||||
{
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
if (mCurrentFocus)
|
||||
{
|
||||
if (MyGUI::Button* b = mCurrentFocus->castType<MyGUI::Button>(false))
|
||||
b->_setWidgetState("normal");
|
||||
}
|
||||
#endif
|
||||
mCurrentFocus = focus;
|
||||
}
|
||||
|
||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||
styleFocusedButton(mCurrentFocus);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KeyboardNavigation::setDefaultFocus(MyGUI::Widget *window, MyGUI::Widget *defaultFocus)
|
||||
@ -273,7 +246,7 @@ bool KeyboardNavigation::switchFocus(int direction, bool wrap)
|
||||
if (wrap)
|
||||
index = (index + keyFocusList.size())%keyFocusList.size();
|
||||
else
|
||||
index = std::min(std::max(0, index), static_cast<int>(keyFocusList.size())-1);
|
||||
index = std::clamp<int>(index, 0, keyFocusList.size() - 1);
|
||||
|
||||
MyGUI::Widget* next = keyFocusList[index];
|
||||
int vertdiff = next->getTop() - focus->getTop();
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
|
||||
#include <osg/Vec2f>
|
||||
|
||||
#include "windowpinnablebase.hpp"
|
||||
|
||||
#include <components/esm/cellid.hpp>
|
||||
|
@ -145,7 +145,6 @@ namespace MWGui
|
||||
return mInterMessageBoxe != nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool MessageBoxManager::removeMessageBox (MessageBox *msgbox)
|
||||
{
|
||||
std::vector<MessageBox*>::iterator it;
|
||||
@ -161,6 +160,11 @@ namespace MWGui
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<MessageBox*> MessageBoxManager::getActiveMessageBoxes()
|
||||
{
|
||||
return mMessageBoxes;
|
||||
}
|
||||
|
||||
int MessageBoxManager::readPressedButton (bool reset)
|
||||
{
|
||||
int pressed = mLastButtonPressed;
|
||||
|
@ -49,6 +49,8 @@ namespace MWGui
|
||||
|
||||
void setVisible(bool value);
|
||||
|
||||
const std::vector<MessageBox*> getActiveMessageBoxes();
|
||||
|
||||
private:
|
||||
std::vector<MessageBox*> mMessageBoxes;
|
||||
InteractiveMessageBox* mInterMessageBoxe;
|
||||
@ -63,6 +65,7 @@ namespace MWGui
|
||||
public:
|
||||
MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message);
|
||||
void setMessage (const std::string& message);
|
||||
const std::string& getMessage() { return mMessage; };
|
||||
int getHeight ();
|
||||
void update (int height);
|
||||
void setVisible(bool value);
|
||||
@ -72,7 +75,7 @@ namespace MWGui
|
||||
|
||||
protected:
|
||||
MessageBoxManager& mMessageBoxManager;
|
||||
const std::string& mMessage;
|
||||
std::string mMessage;
|
||||
MyGUI::EditBox* mMessageWidget;
|
||||
int mBottomPadding;
|
||||
int mNextBoxPadding;
|
||||
|
@ -79,41 +79,45 @@ namespace MWGui
|
||||
delete mMagicSelectionDialog;
|
||||
}
|
||||
|
||||
inline void QuickKeysMenu::validate(int index)
|
||||
{
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
||||
switch (mKey[index].type)
|
||||
{
|
||||
case Type_Unassigned:
|
||||
case Type_HandToHand:
|
||||
case Type_Magic:
|
||||
break;
|
||||
case Type_Item:
|
||||
case Type_MagicItem:
|
||||
{
|
||||
MWWorld::Ptr item = *mKey[index].button->getUserData<MWWorld::Ptr>();
|
||||
// Make sure the item is available and is not broken
|
||||
if (!item || item.getRefData().getCount() < 1 ||
|
||||
(item.getClass().hasItemHealth(item) &&
|
||||
item.getClass().getItemHealth(item) <= 0))
|
||||
{
|
||||
// Try searching for a compatible replacement
|
||||
item = store.findReplacement(mKey[index].id);
|
||||
|
||||
if (item)
|
||||
mKey[index].button->setUserData(MWWorld::Ptr(item));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onOpen()
|
||||
{
|
||||
WindowBase::onOpen();
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
||||
|
||||
// Check if quick keys are still valid
|
||||
for (int i=0; i<10; ++i)
|
||||
// Quick key index
|
||||
for (int index = 0; index < 10; ++index)
|
||||
{
|
||||
switch (mKey[i].type)
|
||||
{
|
||||
case Type_Unassigned:
|
||||
case Type_HandToHand:
|
||||
case Type_Magic:
|
||||
break;
|
||||
case Type_Item:
|
||||
case Type_MagicItem:
|
||||
{
|
||||
MWWorld::Ptr item = *mKey[i].button->getUserData<MWWorld::Ptr>();
|
||||
// Make sure the item is available and is not broken
|
||||
if (!item || item.getRefData().getCount() < 1 ||
|
||||
(item.getClass().hasItemHealth(item) &&
|
||||
item.getClass().getItemHealth(item) <= 0))
|
||||
{
|
||||
// Try searching for a compatible replacement
|
||||
item = store.findReplacement(mKey[i].id);
|
||||
|
||||
if (item)
|
||||
mKey[i].button->setUserData(MWWorld::Ptr(item));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
validate(index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,11 +333,13 @@ namespace MWGui
|
||||
assert(index >= 1 && index <= 10);
|
||||
|
||||
keyData *key = &mKey[index-1];
|
||||
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
||||
const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player);
|
||||
|
||||
validate(index-1);
|
||||
|
||||
// Delay action executing,
|
||||
// if player is busy for now (casting a spell, attacking someone, etc.)
|
||||
bool isDelayNeeded = MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player)
|
||||
|
@ -76,7 +76,8 @@ namespace MWGui
|
||||
|
||||
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
|
||||
void onOkButtonClicked(MyGUI::Widget* sender);
|
||||
|
||||
// Check if quick key is still valid
|
||||
inline void validate(int index);
|
||||
void unassign(keyData* key);
|
||||
};
|
||||
|
||||
|
@ -171,7 +171,7 @@ namespace MWGui
|
||||
else
|
||||
valueStr = MyGUI::utility::toString(int(value));
|
||||
|
||||
value = std::max(min, std::min(value, max));
|
||||
value = std::clamp(value, min, max);
|
||||
value = (value-min)/(max-min);
|
||||
|
||||
scroll->setScrollPosition(static_cast<size_t>(value * (scroll->getScrollRange() - 1)));
|
||||
@ -232,6 +232,7 @@ namespace MWGui
|
||||
getWidget(mControllerSwitch, "ControllerButton");
|
||||
getWidget(mWaterTextureSize, "WaterTextureSize");
|
||||
getWidget(mWaterReflectionDetail, "WaterReflectionDetail");
|
||||
getWidget(mWaterRainRippleDetail, "WaterRainRippleDetail");
|
||||
getWidget(mLightingMethodButton, "LightingMethodButton");
|
||||
getWidget(mLightsResetButton, "LightsResetButton");
|
||||
getWidget(mMaxLights, "MaxLights");
|
||||
@ -259,6 +260,7 @@ namespace MWGui
|
||||
|
||||
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
|
||||
mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);
|
||||
mWaterRainRippleDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterRainRippleDetailChanged);
|
||||
|
||||
mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodButtonChanged);
|
||||
mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);
|
||||
@ -267,6 +269,8 @@ namespace MWGui
|
||||
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
|
||||
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
|
||||
|
||||
computeMinimumWindowSize();
|
||||
|
||||
center();
|
||||
|
||||
mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);
|
||||
@ -305,10 +309,12 @@ namespace MWGui
|
||||
if (waterTextureSize >= 2048)
|
||||
mWaterTextureSize->setIndexSelected(2);
|
||||
|
||||
int waterReflectionDetail = Settings::Manager::getInt("reflection detail", "Water");
|
||||
waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail));
|
||||
int waterReflectionDetail = std::clamp(Settings::Manager::getInt("reflection detail", "Water"), 0, 5);
|
||||
mWaterReflectionDetail->setIndexSelected(waterReflectionDetail);
|
||||
|
||||
int waterRainRippleDetail = std::clamp(Settings::Manager::getInt("rain ripple detail", "Water"), 0, 2);
|
||||
mWaterRainRippleDetail->setIndexSelected(waterRainRippleDetail);
|
||||
|
||||
updateMaxLightsComboBox(mMaxLights);
|
||||
|
||||
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
|
||||
@ -392,11 +398,18 @@ namespace MWGui
|
||||
|
||||
void SettingsWindow::onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||
{
|
||||
unsigned int level = std::min((unsigned int)5, (unsigned int)pos);
|
||||
unsigned int level = static_cast<unsigned int>(std::min<size_t>(pos, 5));
|
||||
Settings::Manager::setInt("reflection detail", "Water", level);
|
||||
apply();
|
||||
}
|
||||
|
||||
void SettingsWindow::onWaterRainRippleDetailChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||
{
|
||||
unsigned int level = static_cast<unsigned int>(std::min<size_t>(pos, 2));
|
||||
Settings::Manager::setInt("rain ripple detail", "Water", level);
|
||||
apply();
|
||||
}
|
||||
|
||||
void SettingsWindow::onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||
{
|
||||
if (pos == MyGUI::ITEM_NONE)
|
||||
@ -739,6 +752,32 @@ namespace MWGui
|
||||
layoutControlsBox();
|
||||
}
|
||||
|
||||
void SettingsWindow::computeMinimumWindowSize()
|
||||
{
|
||||
auto* window = mMainWidget->castType<MyGUI::Window>();
|
||||
auto minSize = window->getMinSize();
|
||||
|
||||
// Window should be at minimum wide enough to show all tabs.
|
||||
int tabBarWidth = 0;
|
||||
for (uint32_t i = 0; i < mSettingsTab->getItemCount(); i++)
|
||||
{
|
||||
tabBarWidth += mSettingsTab->getButtonWidthAt(i);
|
||||
}
|
||||
|
||||
// Need to include window margins
|
||||
int margins = mMainWidget->getWidth() - mSettingsTab->getWidth();
|
||||
int minimumWindowWidth = tabBarWidth + margins;
|
||||
|
||||
if (minimumWindowWidth > minSize.width)
|
||||
{
|
||||
minSize.width = minimumWindowWidth;
|
||||
window->setMinSize(minSize);
|
||||
|
||||
// Make a dummy call to setSize so MyGUI can apply any resize resulting from the change in MinSize
|
||||
mMainWidget->setSize(mMainWidget->getSize());
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWindow::resetScrollbars()
|
||||
{
|
||||
mResolutionList->setScrollPosition(0);
|
||||
|
@ -31,6 +31,7 @@ namespace MWGui
|
||||
|
||||
MyGUI::ComboBox* mWaterTextureSize;
|
||||
MyGUI::ComboBox* mWaterReflectionDetail;
|
||||
MyGUI::ComboBox* mWaterRainRippleDetail;
|
||||
|
||||
MyGUI::ComboBox* mMaxLights;
|
||||
MyGUI::ComboBox* mLightingMethodButton;
|
||||
@ -55,6 +56,7 @@ namespace MWGui
|
||||
|
||||
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
void onWaterRainRippleDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
|
||||
void onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
void onLightsResetButtonClicked(MyGUI::Widget* _sender);
|
||||
@ -75,6 +77,8 @@ namespace MWGui
|
||||
void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value);
|
||||
|
||||
void layoutControlsBox();
|
||||
|
||||
void computeMinimumWindowSize();
|
||||
|
||||
private:
|
||||
void resetScrollbars();
|
||||
|
@ -599,8 +599,7 @@ namespace MWGui
|
||||
text += "\n#{fontcolourhtml=normal}#{sExpelled}";
|
||||
else
|
||||
{
|
||||
int rank = factionPair.second;
|
||||
rank = std::max(0, std::min(9, rank));
|
||||
const int rank = std::clamp(factionPair.second, 0, 9);
|
||||
text += std::string("\n#{fontcolourhtml=normal}") + faction->mRanks[rank];
|
||||
|
||||
if (rank < 9)
|
||||
|
@ -493,6 +493,7 @@ namespace MWGui
|
||||
std::vector<MyGUI::Widget*> effectItems;
|
||||
int flag = info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0;
|
||||
flag |= info.isIngredient ? Widgets::MWEffectList::EF_NoMagnitude : 0;
|
||||
flag |= info.isIngredient ? Widgets::MWEffectList::EF_Constant : 0;
|
||||
effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, flag);
|
||||
totalSize.height += coord.top-6;
|
||||
totalSize.width = std::max(totalSize.width, coord.width);
|
||||
@ -649,7 +650,7 @@ namespace MWGui
|
||||
|
||||
std::string ToolTips::getSoulString(const MWWorld::CellRef& cellref)
|
||||
{
|
||||
std::string soul = cellref.getSoul();
|
||||
const std::string& soul = cellref.getSoul();
|
||||
if (soul.empty())
|
||||
return std::string();
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
@ -665,7 +666,7 @@ namespace MWGui
|
||||
{
|
||||
std::string ret;
|
||||
ret += getMiscString(cellref.getOwner(), "Owner");
|
||||
const std::string factionId = cellref.getFaction();
|
||||
const std::string& factionId = cellref.getFaction();
|
||||
if (!factionId.empty())
|
||||
{
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
@ -53,6 +53,8 @@
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/misc/frameratelimiter.hpp>
|
||||
|
||||
#include <components/lua_ui/widgetlist.hpp>
|
||||
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
@ -163,6 +165,7 @@ namespace MWGui
|
||||
, mScreenFader(nullptr)
|
||||
, mDebugWindow(nullptr)
|
||||
, mJailScreen(nullptr)
|
||||
, mContainerWindow(nullptr)
|
||||
, mTranslationDataStorage (translationDataStorage)
|
||||
, mCharGen(nullptr)
|
||||
, mInputBlocker(nullptr)
|
||||
@ -220,6 +223,7 @@ namespace MWGui
|
||||
ItemWidget::registerComponents();
|
||||
SpellView::registerComponents();
|
||||
Gui::registerAllWidgets();
|
||||
LuaUi::registerAllWidgets();
|
||||
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller");
|
||||
|
||||
@ -357,10 +361,10 @@ namespace MWGui
|
||||
mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow);
|
||||
mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete);
|
||||
|
||||
ContainerWindow* containerWindow = new ContainerWindow(mDragAndDrop);
|
||||
mWindows.push_back(containerWindow);
|
||||
trackWindow(containerWindow, "container");
|
||||
mGuiModeStates[GM_Container] = GuiModeState({containerWindow, mInventoryWindow});
|
||||
mContainerWindow = new ContainerWindow(mDragAndDrop);
|
||||
mWindows.push_back(mContainerWindow);
|
||||
trackWindow(mContainerWindow, "container");
|
||||
mGuiModeStates[GM_Container] = GuiModeState({mContainerWindow, mInventoryWindow});
|
||||
|
||||
mHud = new HUD(mCustomMarkers, mDragAndDrop, mLocalMapRender);
|
||||
mWindows.push_back(mHud);
|
||||
@ -635,6 +639,7 @@ namespace MWGui
|
||||
mMap->setVisible(false);
|
||||
mStatsWindow->setVisible(false);
|
||||
mSpellWindow->setVisible(false);
|
||||
mHud->setDrowningBarVisible(false);
|
||||
mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter || getMode() == GM_Companion);
|
||||
}
|
||||
|
||||
@ -768,6 +773,11 @@ namespace MWGui
|
||||
mMessageBoxManager->removeStaticMessageBox();
|
||||
}
|
||||
|
||||
const std::vector<MWGui::MessageBox*> WindowManager::getActiveMessageBoxes()
|
||||
{
|
||||
return mMessageBoxManager->getActiveMessageBoxes();
|
||||
}
|
||||
|
||||
int WindowManager::readPressedButton ()
|
||||
{
|
||||
return mMessageBoxManager->readPressedButton();
|
||||
@ -1095,12 +1105,21 @@ namespace MWGui
|
||||
|
||||
void WindowManager::windowResized(int x, int y)
|
||||
{
|
||||
// Note: this is a side effect of resolution change or window resize.
|
||||
// There is no need to track these changes.
|
||||
Settings::Manager::setInt("resolution x", "Video", x);
|
||||
Settings::Manager::setInt("resolution y", "Video", y);
|
||||
Settings::Manager::resetPendingChange("resolution x", "Video");
|
||||
Settings::Manager::resetPendingChange("resolution y", "Video");
|
||||
|
||||
// We only want to process changes to window-size related settings.
|
||||
Settings::CategorySettingVector filter = {{"Video", "resolution x"},
|
||||
{"Video", "resolution y"}};
|
||||
|
||||
// If the HUD has not been initialised, the World singleton will not be available.
|
||||
if (mHud)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->processChangedSettings(
|
||||
Settings::Manager::getPendingChanges(filter));
|
||||
}
|
||||
|
||||
Settings::Manager::resetPendingChanges(filter);
|
||||
|
||||
mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y);
|
||||
|
||||
@ -1163,6 +1182,16 @@ namespace MWGui
|
||||
}
|
||||
|
||||
void WindowManager::pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg)
|
||||
{
|
||||
pushGuiMode(mode, arg, false);
|
||||
}
|
||||
|
||||
void WindowManager::forceLootMode(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
pushGuiMode(MWGui::GM_Container, ptr, true);
|
||||
}
|
||||
|
||||
void WindowManager::pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg, bool force)
|
||||
{
|
||||
if (mode==GM_Inventory && mAllowed==GW_None)
|
||||
return;
|
||||
@ -1185,6 +1214,8 @@ namespace MWGui
|
||||
mGuiModeStates[mode].update(true);
|
||||
playSound(mGuiModeStates[mode].mOpenSound);
|
||||
}
|
||||
if(force)
|
||||
mContainerWindow->treatNextOpenAsLoot();
|
||||
for (WindowBase* window : mGuiModeStates[mode].mWindows)
|
||||
window->setPtr(arg);
|
||||
|
||||
|
@ -187,6 +187,7 @@ namespace MWGui
|
||||
MWGui::CountDialog* getCountDialog() override;
|
||||
MWGui::ConfirmationDialog* getConfirmationDialog() override;
|
||||
MWGui::TradeWindow* getTradeWindow() override;
|
||||
const std::vector<MWGui::MessageBox*> getActiveMessageBoxes() override;
|
||||
|
||||
/// Make the player use an item, while updating GUI state accordingly
|
||||
void useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions=false) override;
|
||||
@ -389,6 +390,7 @@ namespace MWGui
|
||||
const std::string& getVersionDescription() const override;
|
||||
|
||||
void onDeleteCustomData(const MWWorld::Ptr& ptr) override;
|
||||
void forceLootMode(const MWWorld::Ptr& ptr) override;
|
||||
|
||||
private:
|
||||
unsigned int mOldUpdateMask; unsigned int mOldCullMask;
|
||||
@ -447,6 +449,7 @@ namespace MWGui
|
||||
ScreenFader* mScreenFader;
|
||||
DebugWindow* mDebugWindow;
|
||||
JailScreen* mJailScreen;
|
||||
ContainerWindow* mContainerWindow;
|
||||
|
||||
std::vector<WindowBase*> mWindows;
|
||||
|
||||
@ -573,6 +576,8 @@ namespace MWGui
|
||||
void enableScene(bool enable);
|
||||
|
||||
void handleScheduledMessageBoxes();
|
||||
|
||||
void pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg, bool force);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,13 @@
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
||||
#include "../mwgui/messagebox.hpp"
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "bindingsmanager.hpp"
|
||||
|
||||
namespace MWInput
|
||||
{
|
||||
const float ZOOM_SCALE = 10.f; /// Used for scrolling camera in and out
|
||||
|
||||
ActionManager::ActionManager(BindingsManager* bindingsManager,
|
||||
osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
|
||||
@ -40,8 +41,6 @@ namespace MWInput
|
||||
, mAlwaysRunActive(Settings::Manager::getBool("always run", "Input"))
|
||||
, mSneaking(false)
|
||||
, mAttemptJump(false)
|
||||
, mOverencumberedMessageDelay(0.f)
|
||||
, mPreviewPOVDelay(0.f)
|
||||
, mTimeIdle(0.f)
|
||||
{
|
||||
}
|
||||
@ -90,43 +89,26 @@ namespace MWInput
|
||||
{
|
||||
player.setUpDown(1);
|
||||
triedToMove = true;
|
||||
mOverencumberedMessageDelay = 0.f;
|
||||
}
|
||||
|
||||
// if player tried to start moving, but can't (due to being overencumbered), display a notification.
|
||||
if (triedToMove)
|
||||
{
|
||||
MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
||||
mOverencumberedMessageDelay -= dt;
|
||||
if (playerPtr.getClass().getEncumbrance(playerPtr) > playerPtr.getClass().getCapacity(playerPtr))
|
||||
{
|
||||
player.setAutoMove (false);
|
||||
if (mOverencumberedMessageDelay <= 0)
|
||||
std::vector<MWGui::MessageBox*> msgboxs = MWBase::Environment::get().getWindowManager()->getActiveMessageBoxes();
|
||||
const std::vector<MWGui::MessageBox*>::iterator it = std::find_if(msgboxs.begin(), msgboxs.end(), [](MWGui::MessageBox*& msgbox)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage59}");
|
||||
mOverencumberedMessageDelay = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (msgbox->getMessage() == "#{sNotifyMessage59}");
|
||||
});
|
||||
|
||||
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch"))
|
||||
{
|
||||
const float switchLimit = 0.25;
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
if (mBindingsManager->actionIsActive(A_TogglePOV))
|
||||
{
|
||||
if (world->isFirstPerson() ? mPreviewPOVDelay > switchLimit : mPreviewPOVDelay == 0)
|
||||
world->togglePreviewMode(true);
|
||||
mPreviewPOVDelay += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
//disable preview mode
|
||||
if (mPreviewPOVDelay > 0)
|
||||
world->togglePreviewMode(false);
|
||||
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= switchLimit)
|
||||
world->togglePOV();
|
||||
mPreviewPOVDelay = 0.f;
|
||||
// if an overencumbered messagebox is already present, reset its expiry timer, otherwise create new one.
|
||||
if (it != msgboxs.end())
|
||||
(*it)->mCurrentTime = 0;
|
||||
else
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage59}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,38 +144,16 @@ namespace MWInput
|
||||
resetIdleTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
updateIdleTime(dt);
|
||||
}
|
||||
mTimeIdle += dt;
|
||||
|
||||
mAttemptJump = false;
|
||||
}
|
||||
|
||||
bool ActionManager::isPreviewModeEnabled()
|
||||
{
|
||||
return MWBase::Environment::get().getWorld()->isPreviewModeEnabled();
|
||||
}
|
||||
|
||||
void ActionManager::resetIdleTime()
|
||||
{
|
||||
if (mTimeIdle < 0)
|
||||
MWBase::Environment::get().getWorld()->toggleVanityMode(false);
|
||||
mTimeIdle = 0.f;
|
||||
}
|
||||
|
||||
void ActionManager::updateIdleTime(float dt)
|
||||
{
|
||||
static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||
.find("fVanityDelay")->mValue.getFloat();
|
||||
if (mTimeIdle >= 0.f)
|
||||
mTimeIdle += dt;
|
||||
if (mTimeIdle > vanityDelay)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->toggleVanityMode(true);
|
||||
mTimeIdle = -1.f;
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::executeAction(int action)
|
||||
{
|
||||
MWBase::Environment::get().getLuaManager()->inputEvent({MWBase::LuaManager::InputEvent::Action, action});
|
||||
@ -281,14 +241,6 @@ namespace MWInput
|
||||
case A_ToggleDebug:
|
||||
windowManager->toggleDebugWindow();
|
||||
break;
|
||||
case A_ZoomIn:
|
||||
if (inputManager->getControlSwitch("playerviewswitch") && inputManager->getControlSwitch("playercontrols") && !windowManager->isGuiMode())
|
||||
MWBase::Environment::get().getWorld()->adjustCameraDistance(-ZOOM_SCALE);
|
||||
break;
|
||||
case A_ZoomOut:
|
||||
if (inputManager->getControlSwitch("playerviewswitch") && inputManager->getControlSwitch("playercontrols") && !windowManager->isGuiMode())
|
||||
MWBase::Environment::get().getWorld()->adjustCameraDistance(ZOOM_SCALE);
|
||||
break;
|
||||
case A_QuickSave:
|
||||
quickSave();
|
||||
break;
|
||||
|
@ -55,13 +55,9 @@ namespace MWInput
|
||||
|
||||
void setAttemptJump(bool enabled) { mAttemptJump = enabled; }
|
||||
|
||||
bool isPreviewModeEnabled();
|
||||
|
||||
private:
|
||||
void handleGuiArrowKey(int action);
|
||||
|
||||
void updateIdleTime(float dt);
|
||||
|
||||
BindingsManager* mBindingsManager;
|
||||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
|
||||
@ -71,8 +67,6 @@ namespace MWInput
|
||||
bool mSneaking;
|
||||
bool mAttemptJump;
|
||||
|
||||
float mOverencumberedMessageDelay;
|
||||
float mPreviewPOVDelay;
|
||||
float mTimeIdle;
|
||||
};
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <extern/oics/ICSChannelListener.h>
|
||||
#include <extern/oics/ICSInputControlSystem.h>
|
||||
|
||||
#include <components/sdlutil/sdlmappings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
@ -13,7 +15,6 @@
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "sdlmappings.hpp"
|
||||
|
||||
namespace MWInput
|
||||
{
|
||||
@ -546,9 +547,9 @@ namespace MWInput
|
||||
ICS::Control* c = mInputBinder->getChannel(action)->getAttachedControls().front().control;
|
||||
|
||||
if (mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS::InputControlSystem::UNASSIGNED)
|
||||
return sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE));
|
||||
return SDLUtil::sdlControllerAxisToString(mInputBinder->getJoystickAxisBinding(c, sFakeDeviceId, ICS::Control::INCREASE));
|
||||
else if (mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS)
|
||||
return sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE));
|
||||
return SDLUtil::sdlControllerButtonToString(mInputBinder->getJoystickButtonBinding(c, sFakeDeviceId, ICS::Control::INCREASE));
|
||||
else
|
||||
return "#{sNone}";
|
||||
}
|
||||
@ -653,14 +654,13 @@ namespace MWInput
|
||||
return mInputBinder->getKeyBinding(mInputBinder->getControl(actionId), ICS::Control::INCREASE);
|
||||
}
|
||||
|
||||
float BindingsManager::getControllerAxisValue(SDL_GameControllerAxis axis) const
|
||||
SDL_GameController* BindingsManager::getControllerOrNull() const
|
||||
{
|
||||
const auto& controllers = mInputBinder->getJoystickInstanceMap();
|
||||
if (controllers.empty())
|
||||
return 0;
|
||||
SDL_GameController* cntrl = controllers.begin()->second;
|
||||
constexpr int AXIS_MAX_ABSOLUTE_VALUE = 32768;
|
||||
return SDL_GameControllerGetAxis(cntrl, axis) / static_cast<float>(AXIS_MAX_ABSOLUTE_VALUE);
|
||||
return nullptr;
|
||||
else
|
||||
return controllers.begin()->second;
|
||||
}
|
||||
|
||||
void BindingsManager::actionValueChanged(int action, float currentValue, float previousValue)
|
||||
|
@ -43,7 +43,8 @@ namespace MWInput
|
||||
|
||||
bool actionIsActive(int id) const;
|
||||
float getActionValue(int id) const; // returns value in range [0, 1]
|
||||
float getControllerAxisValue(SDL_GameControllerAxis axis) const; // returns value in range [-1, 1]
|
||||
|
||||
SDL_GameController* getControllerOrNull() const;
|
||||
|
||||
void mousePressed(const SDL_MouseButtonEvent &evt, int deviceID);
|
||||
void mouseReleased(const SDL_MouseButtonEvent &arg, int deviceID);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <MyGUI_Widget.h>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlmappings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
@ -19,7 +20,6 @@
|
||||
#include "actionmanager.hpp"
|
||||
#include "bindingsmanager.hpp"
|
||||
#include "mousemanager.hpp"
|
||||
#include "sdlmappings.hpp"
|
||||
|
||||
namespace MWInput
|
||||
{
|
||||
@ -34,12 +34,10 @@ namespace MWInput
|
||||
, mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input"))
|
||||
, mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
|
||||
, mSneakToggleShortcutTimer(0.f)
|
||||
, mGamepadZoom(0)
|
||||
, mGamepadGuiCursorEnabled(true)
|
||||
, mGuiCursorEnabled(true)
|
||||
, mJoystickLastUsed(false)
|
||||
, mSneakGamepadShortcut(false)
|
||||
, mGamepadPreviewMode(false)
|
||||
{
|
||||
if (!controllerBindingsFile.empty())
|
||||
{
|
||||
@ -70,7 +68,7 @@ namespace MWInput
|
||||
}
|
||||
|
||||
float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input");
|
||||
deadZoneRadius = std::min(std::max(deadZoneRadius, 0.0f), 0.5f);
|
||||
deadZoneRadius = std::clamp(deadZoneRadius, 0.f, 0.5f);
|
||||
mBindingsManager->setJoystickDeadZone(deadZoneRadius);
|
||||
}
|
||||
|
||||
@ -85,8 +83,6 @@ namespace MWInput
|
||||
|
||||
bool ControllerManager::update(float dt)
|
||||
{
|
||||
mGamepadPreviewMode = mActionManager->isPreviewModeEnabled();
|
||||
|
||||
if (mGuiCursorEnabled && !(mJoystickLastUsed && !mGamepadGuiCursorEnabled))
|
||||
{
|
||||
float xAxis = mBindingsManager->getActionValue(A_MoveLeftRight) * 2.0f - 1.0f;
|
||||
@ -115,7 +111,6 @@ namespace MWInput
|
||||
if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
|
||||
{
|
||||
mGamepadZoom = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -182,15 +177,6 @@ namespace MWInput
|
||||
}
|
||||
}
|
||||
|
||||
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch"))
|
||||
{
|
||||
if (!mBindingsManager->actionIsActive(A_TogglePOV))
|
||||
mGamepadZoom = 0;
|
||||
|
||||
if (mGamepadZoom)
|
||||
MWBase::Environment::get().getWorld()->adjustCameraDistance(-mGamepadZoom);
|
||||
}
|
||||
|
||||
return triedToMove;
|
||||
}
|
||||
|
||||
@ -229,7 +215,7 @@ namespace MWInput
|
||||
mBindingsManager->setPlayerControlsEnabled(true);
|
||||
|
||||
//esc, to leave initial movie screen
|
||||
auto kc = sdlKeyToMyGUI(SDLK_ESCAPE);
|
||||
auto kc = SDLUtil::sdlKeyToMyGUI(SDLK_ESCAPE);
|
||||
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyPress(kc, 0));
|
||||
|
||||
if (!MWBase::Environment::get().getInputManager()->controlsDisabled())
|
||||
@ -273,7 +259,7 @@ namespace MWInput
|
||||
mBindingsManager->setPlayerControlsEnabled(true);
|
||||
|
||||
//esc, to leave initial movie screen
|
||||
auto kc = sdlKeyToMyGUI(SDLK_ESCAPE);
|
||||
auto kc = SDLUtil::sdlKeyToMyGUI(SDLK_ESCAPE);
|
||||
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));
|
||||
|
||||
mBindingsManager->controllerButtonReleased(deviceID, arg);
|
||||
@ -289,21 +275,11 @@ namespace MWInput
|
||||
{
|
||||
gamepadToGuiControl(arg);
|
||||
}
|
||||
else
|
||||
else if (MWBase::Environment::get().getWorld()->isPreviewModeEnabled() &&
|
||||
(arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT || arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT))
|
||||
{
|
||||
if (mGamepadPreviewMode) // Preview Mode Gamepad Zooming
|
||||
{
|
||||
if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
|
||||
{
|
||||
mGamepadZoom = arg.value * 0.85f / 1000.f / 12.f;
|
||||
return; // Do not propagate event.
|
||||
}
|
||||
else if (arg.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)
|
||||
{
|
||||
mGamepadZoom = -arg.value * 0.85f / 1000.f / 12.f;
|
||||
return; // Do not propagate event.
|
||||
}
|
||||
}
|
||||
// Preview Mode Gamepad Zooming; do not propagate to mBindingsManager
|
||||
return;
|
||||
}
|
||||
mBindingsManager->controllerAxisMoved(deviceID, arg);
|
||||
}
|
||||
@ -403,4 +379,24 @@ namespace MWInput
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float ControllerManager::getAxisValue(SDL_GameControllerAxis axis) const
|
||||
{
|
||||
SDL_GameController* cntrl = mBindingsManager->getControllerOrNull();
|
||||
constexpr int AXIS_MAX_ABSOLUTE_VALUE = 32768;
|
||||
if (cntrl)
|
||||
return SDL_GameControllerGetAxis(cntrl, axis) / static_cast<float>(AXIS_MAX_ABSOLUTE_VALUE);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ControllerManager::isButtonPressed(SDL_GameControllerButton button) const
|
||||
{
|
||||
SDL_GameController* cntrl = mBindingsManager->getControllerOrNull();
|
||||
if (cntrl)
|
||||
return SDL_GameControllerGetButton(cntrl, button) > 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,12 +34,15 @@ namespace MWInput
|
||||
void processChangedSettings(const Settings::CategorySettingVector& changed);
|
||||
|
||||
void setJoystickLastUsed(bool enabled) { mJoystickLastUsed = enabled; }
|
||||
bool joystickLastUsed() { return mJoystickLastUsed; }
|
||||
bool joystickLastUsed() const { return mJoystickLastUsed; }
|
||||
|
||||
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
|
||||
|
||||
void setGamepadGuiCursorEnabled(bool enabled) { mGamepadGuiCursorEnabled = enabled; }
|
||||
bool gamepadGuiCursorEnabled() { return mGamepadGuiCursorEnabled; }
|
||||
bool gamepadGuiCursorEnabled() const { return mGamepadGuiCursorEnabled; }
|
||||
|
||||
float getAxisValue(SDL_GameControllerAxis axis) const; // returns value in range [-1, 1]
|
||||
bool isButtonPressed(SDL_GameControllerButton button) const;
|
||||
|
||||
private:
|
||||
// Return true if GUI consumes input.
|
||||
@ -53,12 +56,10 @@ namespace MWInput
|
||||
bool mJoystickEnabled;
|
||||
float mGamepadCursorSpeed;
|
||||
float mSneakToggleShortcutTimer;
|
||||
float mGamepadZoom;
|
||||
bool mGamepadGuiCursorEnabled;
|
||||
bool mGuiCursorEnabled;
|
||||
bool mJoystickLastUsed;
|
||||
bool mSneakGamepadShortcut;
|
||||
bool mGamepadPreviewMode;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -29,12 +29,15 @@ namespace MWInput
|
||||
mSwitches["vanitymode"] = true;
|
||||
}
|
||||
|
||||
bool ControlSwitch::get(const std::string& key)
|
||||
bool ControlSwitch::get(std::string_view key)
|
||||
{
|
||||
return mSwitches[key];
|
||||
auto it = mSwitches.find(key);
|
||||
if (it == mSwitches.end())
|
||||
throw std::runtime_error("Incorrect ControlSwitch: " + std::string(key));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void ControlSwitch::set(const std::string& key, bool value)
|
||||
void ControlSwitch::set(std::string_view key, bool value)
|
||||
{
|
||||
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
||||
|
||||
@ -51,15 +54,14 @@ namespace MWInput
|
||||
/// \fixme maybe crouching at this time
|
||||
player.setUpDown(0);
|
||||
}
|
||||
else if (key == "vanitymode")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->allowVanityMode(value);
|
||||
}
|
||||
else if (key == "playerlooking" && !value)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->rotateObject(player.getPlayer(), osg::Vec3f());
|
||||
}
|
||||
mSwitches[key] = value;
|
||||
auto it = mSwitches.find(key);
|
||||
if (it == mSwitches.end())
|
||||
throw std::runtime_error("Incorrect ControlSwitch: " + std::string(key));
|
||||
it->second = value;
|
||||
}
|
||||
|
||||
void ControlSwitch::write(ESM::ESMWriter& writer, Loading::Listener& /*progress*/)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
@ -23,8 +24,8 @@ namespace MWInput
|
||||
public:
|
||||
ControlSwitch();
|
||||
|
||||
bool get(const std::string& key);
|
||||
void set(const std::string& key, bool value);
|
||||
bool get(std::string_view key);
|
||||
void set(std::string_view key, bool value);
|
||||
void clear();
|
||||
|
||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress);
|
||||
@ -32,7 +33,7 @@ namespace MWInput
|
||||
int countSavedGameRecords() const;
|
||||
|
||||
private:
|
||||
std::map<std::string, bool> mSwitches;
|
||||
std::map<std::string, bool, std::less<>> mSwitches;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "controlswitch.hpp"
|
||||
#include "keyboardmanager.hpp"
|
||||
#include "mousemanager.hpp"
|
||||
#include "sdlmappings.hpp"
|
||||
#include "sensormanager.hpp"
|
||||
|
||||
namespace MWInput
|
||||
@ -101,8 +100,6 @@ namespace MWInput
|
||||
mMouseManager->update(dt);
|
||||
mSensorManager->update(dt);
|
||||
mActionManager->update(dt, controllerMove);
|
||||
|
||||
MWBase::Environment::get().getWorld()->applyDeferredPreviewRotationToPlayer(dt);
|
||||
}
|
||||
|
||||
void InputManager::setDragDrop(bool dragDrop)
|
||||
@ -135,12 +132,12 @@ namespace MWInput
|
||||
mSensorManager->processChangedSettings(changed);
|
||||
}
|
||||
|
||||
bool InputManager::getControlSwitch(const std::string& sw)
|
||||
bool InputManager::getControlSwitch(std::string_view sw)
|
||||
{
|
||||
return mControlSwitch->get(sw);
|
||||
}
|
||||
|
||||
void InputManager::toggleControlSwitch(const std::string& sw, bool value)
|
||||
void InputManager::toggleControlSwitch(std::string_view sw, bool value)
|
||||
{
|
||||
mControlSwitch->set(sw, value);
|
||||
}
|
||||
@ -180,14 +177,14 @@ namespace MWInput
|
||||
return mBindingsManager->getActionValue(action);
|
||||
}
|
||||
|
||||
float InputManager::getControllerAxisValue(SDL_GameControllerAxis axis) const
|
||||
bool InputManager::isControllerButtonPressed(SDL_GameControllerButton button) const
|
||||
{
|
||||
return mBindingsManager->getControllerAxisValue(axis);
|
||||
return mControllerManager->isButtonPressed(button);
|
||||
}
|
||||
|
||||
uint32_t InputManager::getMouseButtonsState() const
|
||||
float InputManager::getControllerAxisValue(SDL_GameControllerAxis axis) const
|
||||
{
|
||||
return mMouseManager->getButtonsState();
|
||||
return mControllerManager->getAxisValue(axis);
|
||||
}
|
||||
|
||||
int InputManager::getMouseMoveX() const
|
||||
|
@ -70,8 +70,8 @@ namespace MWInput
|
||||
void setGamepadGuiCursorEnabled(bool enabled) override;
|
||||
void setAttemptJump(bool jumping) override;
|
||||
|
||||
void toggleControlSwitch (const std::string& sw, bool value) override;
|
||||
bool getControlSwitch (const std::string& sw) override;
|
||||
void toggleControlSwitch(std::string_view sw, bool value) override;
|
||||
bool getControlSwitch(std::string_view sw) override;
|
||||
|
||||
std::string getActionDescription (int action) const override;
|
||||
std::string getActionKeyBindingName (int action) const override;
|
||||
@ -79,8 +79,8 @@ namespace MWInput
|
||||
bool actionIsActive(int action) const override;
|
||||
|
||||
float getActionValue(int action) const override;
|
||||
bool isControllerButtonPressed(SDL_GameControllerButton button) const override;
|
||||
float getControllerAxisValue(SDL_GameControllerAxis axis) const override;
|
||||
uint32_t getMouseButtonsState() const override;
|
||||
int getMouseMoveX() const override;
|
||||
int getMouseMoveY() const override;
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include <MyGUI_InputManager.h>
|
||||
|
||||
#include <components/sdlutil/sdlmappings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
@ -13,7 +15,6 @@
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "bindingsmanager.hpp"
|
||||
#include "sdlmappings.hpp"
|
||||
|
||||
namespace MWInput
|
||||
{
|
||||
@ -35,16 +36,16 @@ namespace MWInput
|
||||
// HACK: to make default keybinding for the console work without printing an extra "^" upon closing
|
||||
// This assumes that SDL_TextInput events always come *after* the key event
|
||||
// (which is somewhat reasonable, and hopefully true for all SDL platforms)
|
||||
auto kc = sdlKeyToMyGUI(arg.keysym.sym);
|
||||
auto kc = SDLUtil::sdlKeyToMyGUI(arg.keysym.sym);
|
||||
if (mBindingsManager->getKeyBinding(A_Console) == arg.keysym.scancode
|
||||
&& MWBase::Environment::get().getWindowManager()->isConsoleMode())
|
||||
SDL_StopTextInput();
|
||||
|
||||
bool consumed = SDL_IsTextInputActive() && // Little trick to check if key is printable
|
||||
(!(SDLK_SCANCODE_MASK & arg.keysym.sym) &&
|
||||
(std::isprint(arg.keysym.sym) ||
|
||||
// Don't trust isprint for symbols outside the extended ASCII range
|
||||
(kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff)));
|
||||
((kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff) ||
|
||||
(arg.keysym.sym >= 0 && arg.keysym.sym <= 255 && std::isprint(arg.keysym.sym))));
|
||||
if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState())
|
||||
{
|
||||
if (MWBase::Environment::get().getWindowManager()->injectKeyPress(kc, 0, arg.repeat))
|
||||
@ -71,7 +72,7 @@ namespace MWInput
|
||||
void KeyboardManager::keyReleased(const SDL_KeyboardEvent &arg)
|
||||
{
|
||||
MWBase::Environment::get().getInputManager()->setJoystickLastUsed(false);
|
||||
auto kc = sdlKeyToMyGUI(arg.keysym.sym);
|
||||
auto kc = SDLUtil::sdlKeyToMyGUI(arg.keysym.sym);
|
||||
|
||||
if (!mBindingsManager->isDetectingBindingState())
|
||||
mBindingsManager->setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(kc));
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/sdlutil/sdlinputwrapper.hpp>
|
||||
#include <components/sdlutil/sdlmappings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
@ -17,7 +18,6 @@
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "bindingsmanager.hpp"
|
||||
#include "sdlmappings.hpp"
|
||||
|
||||
namespace MWInput
|
||||
{
|
||||
@ -34,7 +34,6 @@ namespace MWInput
|
||||
, mMouseWheel(0)
|
||||
, mMouseLookEnabled(false)
|
||||
, mGuiCursorEnabled(true)
|
||||
, mButtonsState(0)
|
||||
, mMouseMoveX(0)
|
||||
, mMouseMoveY(0)
|
||||
{
|
||||
@ -126,7 +125,11 @@ namespace MWInput
|
||||
else
|
||||
{
|
||||
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode;
|
||||
guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(
|
||||
static_cast<int>(mGuiCursorX),
|
||||
static_cast<int>(mGuiCursorY),
|
||||
SDLUtil::sdlMouseButtonToMyGui(id)
|
||||
) && guiMode;
|
||||
|
||||
if (mBindingsManager->isDetectingBindingState())
|
||||
return; // don't allow same mouseup to bind as initiated bind
|
||||
@ -154,7 +157,11 @@ namespace MWInput
|
||||
if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events
|
||||
{
|
||||
guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
guiMode = MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(id)) && guiMode;
|
||||
guiMode = MyGUI::InputManager::getInstance().injectMousePress(
|
||||
static_cast<int>(mGuiCursorX),
|
||||
static_cast<int>(mGuiCursorY),
|
||||
SDLUtil::sdlMouseButtonToMyGui(id)
|
||||
) && guiMode;
|
||||
if (MyGUI::InputManager::getInstance().getMouseFocusWidget () != nullptr)
|
||||
{
|
||||
MyGUI::Button* b = MyGUI::InputManager::getInstance().getMouseFocusWidget()->castType<MyGUI::Button>(false);
|
||||
@ -199,7 +206,7 @@ namespace MWInput
|
||||
|
||||
void MouseManager::update(float dt)
|
||||
{
|
||||
mButtonsState = SDL_GetRelativeMouseState(&mMouseMoveX, &mMouseMoveY);
|
||||
SDL_GetRelativeMouseState(&mMouseMoveX, &mMouseMoveY);
|
||||
|
||||
if (!mMouseLookEnabled)
|
||||
return;
|
||||
@ -230,12 +237,18 @@ namespace MWInput
|
||||
|
||||
bool MouseManager::injectMouseButtonPress(Uint8 button)
|
||||
{
|
||||
return MyGUI::InputManager::getInstance().injectMousePress(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));
|
||||
return MyGUI::InputManager::getInstance().injectMousePress(
|
||||
static_cast<int>(mGuiCursorX),
|
||||
static_cast<int>(mGuiCursorY),
|
||||
SDLUtil::sdlMouseButtonToMyGui(button));
|
||||
}
|
||||
|
||||
bool MouseManager::injectMouseButtonRelease(Uint8 button)
|
||||
{
|
||||
return MyGUI::InputManager::getInstance().injectMouseRelease(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), sdlButtonToMyGUI(button));
|
||||
return MyGUI::InputManager::getInstance().injectMouseRelease(
|
||||
static_cast<int>(mGuiCursorX),
|
||||
static_cast<int>(mGuiCursorY),
|
||||
SDLUtil::sdlMouseButtonToMyGui(button));
|
||||
}
|
||||
|
||||
void MouseManager::injectMouseMove(float xMove, float yMove, float mouseWheelMove)
|
||||
@ -245,8 +258,8 @@ namespace MWInput
|
||||
mMouseWheel += mouseWheelMove;
|
||||
|
||||
const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||
mGuiCursorX = std::max(0.f, std::min(mGuiCursorX, float(viewSize.width - 1)));
|
||||
mGuiCursorY = std::max(0.f, std::min(mGuiCursorY, float(viewSize.height - 1)));
|
||||
mGuiCursorX = std::clamp<float>(mGuiCursorX, 0.f, viewSize.width - 1);
|
||||
mGuiCursorY = std::clamp<float>(mGuiCursorY, 0.f, viewSize.height - 1);
|
||||
|
||||
MyGUI::InputManager::getInstance().injectMouseMove(static_cast<int>(mGuiCursorX), static_cast<int>(mGuiCursorY), static_cast<int>(mMouseWheel));
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ namespace MWInput
|
||||
void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; }
|
||||
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
|
||||
|
||||
uint32_t getButtonsState() const { return mButtonsState; }
|
||||
int getMouseMoveX() const { return mMouseMoveX; }
|
||||
int getMouseMoveY() const { return mMouseMoveY; }
|
||||
|
||||
@ -58,7 +57,6 @@ namespace MWInput
|
||||
bool mMouseLookEnabled;
|
||||
bool mGuiCursorEnabled;
|
||||
|
||||
uint32_t mButtonsState;
|
||||
int mMouseMoveX;
|
||||
int mMouseMoveY;
|
||||
};
|
||||
|
@ -1,218 +0,0 @@
|
||||
#include "sdlmappings.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <MyGUI_MouseButton.h>
|
||||
|
||||
#include <SDL_gamecontroller.h>
|
||||
#include <SDL_mouse.h>
|
||||
|
||||
namespace MWInput
|
||||
{
|
||||
std::string sdlControllerButtonToString(int button)
|
||||
{
|
||||
switch(button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_A:
|
||||
return "A Button";
|
||||
case SDL_CONTROLLER_BUTTON_B:
|
||||
return "B Button";
|
||||
case SDL_CONTROLLER_BUTTON_BACK:
|
||||
return "Back Button";
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
|
||||
return "DPad Down";
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
|
||||
return "DPad Left";
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
|
||||
return "DPad Right";
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP:
|
||||
return "DPad Up";
|
||||
case SDL_CONTROLLER_BUTTON_GUIDE:
|
||||
return "Guide Button";
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
|
||||
return "Left Shoulder";
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
|
||||
return "Left Stick Button";
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
|
||||
return "Right Shoulder";
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
|
||||
return "Right Stick Button";
|
||||
case SDL_CONTROLLER_BUTTON_START:
|
||||
return "Start Button";
|
||||
case SDL_CONTROLLER_BUTTON_X:
|
||||
return "X Button";
|
||||
case SDL_CONTROLLER_BUTTON_Y:
|
||||
return "Y Button";
|
||||
default:
|
||||
return "Button " + std::to_string(button);
|
||||
}
|
||||
}
|
||||
|
||||
std::string sdlControllerAxisToString(int axis)
|
||||
{
|
||||
switch(axis)
|
||||
{
|
||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||
return "Left Stick X";
|
||||
case SDL_CONTROLLER_AXIS_LEFTY:
|
||||
return "Left Stick Y";
|
||||
case SDL_CONTROLLER_AXIS_RIGHTX:
|
||||
return "Right Stick X";
|
||||
case SDL_CONTROLLER_AXIS_RIGHTY:
|
||||
return "Right Stick Y";
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||
return "Left Trigger";
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||
return "Right Trigger";
|
||||
default:
|
||||
return "Axis " + std::to_string(axis);
|
||||
}
|
||||
}
|
||||
|
||||
MyGUI::MouseButton sdlButtonToMyGUI(Uint8 button)
|
||||
{
|
||||
//The right button is the second button, according to MyGUI
|
||||
if(button == SDL_BUTTON_RIGHT)
|
||||
button = SDL_BUTTON_MIDDLE;
|
||||
else if(button == SDL_BUTTON_MIDDLE)
|
||||
button = SDL_BUTTON_RIGHT;
|
||||
|
||||
//MyGUI's buttons are 0 indexed
|
||||
return MyGUI::MouseButton::Enum(button - 1);
|
||||
}
|
||||
|
||||
void initKeyMap(std::map<SDL_Keycode, MyGUI::KeyCode>& keyMap)
|
||||
{
|
||||
keyMap[SDLK_UNKNOWN] = MyGUI::KeyCode::None;
|
||||
keyMap[SDLK_ESCAPE] = MyGUI::KeyCode::Escape;
|
||||
keyMap[SDLK_1] = MyGUI::KeyCode::One;
|
||||
keyMap[SDLK_2] = MyGUI::KeyCode::Two;
|
||||
keyMap[SDLK_3] = MyGUI::KeyCode::Three;
|
||||
keyMap[SDLK_4] = MyGUI::KeyCode::Four;
|
||||
keyMap[SDLK_5] = MyGUI::KeyCode::Five;
|
||||
keyMap[SDLK_6] = MyGUI::KeyCode::Six;
|
||||
keyMap[SDLK_7] = MyGUI::KeyCode::Seven;
|
||||
keyMap[SDLK_8] = MyGUI::KeyCode::Eight;
|
||||
keyMap[SDLK_9] = MyGUI::KeyCode::Nine;
|
||||
keyMap[SDLK_0] = MyGUI::KeyCode::Zero;
|
||||
keyMap[SDLK_MINUS] = MyGUI::KeyCode::Minus;
|
||||
keyMap[SDLK_EQUALS] = MyGUI::KeyCode::Equals;
|
||||
keyMap[SDLK_BACKSPACE] = MyGUI::KeyCode::Backspace;
|
||||
keyMap[SDLK_TAB] = MyGUI::KeyCode::Tab;
|
||||
keyMap[SDLK_q] = MyGUI::KeyCode::Q;
|
||||
keyMap[SDLK_w] = MyGUI::KeyCode::W;
|
||||
keyMap[SDLK_e] = MyGUI::KeyCode::E;
|
||||
keyMap[SDLK_r] = MyGUI::KeyCode::R;
|
||||
keyMap[SDLK_t] = MyGUI::KeyCode::T;
|
||||
keyMap[SDLK_y] = MyGUI::KeyCode::Y;
|
||||
keyMap[SDLK_u] = MyGUI::KeyCode::U;
|
||||
keyMap[SDLK_i] = MyGUI::KeyCode::I;
|
||||
keyMap[SDLK_o] = MyGUI::KeyCode::O;
|
||||
keyMap[SDLK_p] = MyGUI::KeyCode::P;
|
||||
keyMap[SDLK_RETURN] = MyGUI::KeyCode::Return;
|
||||
keyMap[SDLK_a] = MyGUI::KeyCode::A;
|
||||
keyMap[SDLK_s] = MyGUI::KeyCode::S;
|
||||
keyMap[SDLK_d] = MyGUI::KeyCode::D;
|
||||
keyMap[SDLK_f] = MyGUI::KeyCode::F;
|
||||
keyMap[SDLK_g] = MyGUI::KeyCode::G;
|
||||
keyMap[SDLK_h] = MyGUI::KeyCode::H;
|
||||
keyMap[SDLK_j] = MyGUI::KeyCode::J;
|
||||
keyMap[SDLK_k] = MyGUI::KeyCode::K;
|
||||
keyMap[SDLK_l] = MyGUI::KeyCode::L;
|
||||
keyMap[SDLK_SEMICOLON] = MyGUI::KeyCode::Semicolon;
|
||||
keyMap[SDLK_QUOTE] = MyGUI::KeyCode::Apostrophe;
|
||||
keyMap[SDLK_BACKQUOTE] = MyGUI::KeyCode::Grave;
|
||||
keyMap[SDLK_LSHIFT] = MyGUI::KeyCode::LeftShift;
|
||||
keyMap[SDLK_BACKSLASH] = MyGUI::KeyCode::Backslash;
|
||||
keyMap[SDLK_z] = MyGUI::KeyCode::Z;
|
||||
keyMap[SDLK_x] = MyGUI::KeyCode::X;
|
||||
keyMap[SDLK_c] = MyGUI::KeyCode::C;
|
||||
keyMap[SDLK_v] = MyGUI::KeyCode::V;
|
||||
keyMap[SDLK_b] = MyGUI::KeyCode::B;
|
||||
keyMap[SDLK_n] = MyGUI::KeyCode::N;
|
||||
keyMap[SDLK_m] = MyGUI::KeyCode::M;
|
||||
keyMap[SDLK_COMMA] = MyGUI::KeyCode::Comma;
|
||||
keyMap[SDLK_PERIOD] = MyGUI::KeyCode::Period;
|
||||
keyMap[SDLK_SLASH] = MyGUI::KeyCode::Slash;
|
||||
keyMap[SDLK_RSHIFT] = MyGUI::KeyCode::RightShift;
|
||||
keyMap[SDLK_KP_MULTIPLY] = MyGUI::KeyCode::Multiply;
|
||||
keyMap[SDLK_LALT] = MyGUI::KeyCode::LeftAlt;
|
||||
keyMap[SDLK_SPACE] = MyGUI::KeyCode::Space;
|
||||
keyMap[SDLK_CAPSLOCK] = MyGUI::KeyCode::Capital;
|
||||
keyMap[SDLK_F1] = MyGUI::KeyCode::F1;
|
||||
keyMap[SDLK_F2] = MyGUI::KeyCode::F2;
|
||||
keyMap[SDLK_F3] = MyGUI::KeyCode::F3;
|
||||
keyMap[SDLK_F4] = MyGUI::KeyCode::F4;
|
||||
keyMap[SDLK_F5] = MyGUI::KeyCode::F5;
|
||||
keyMap[SDLK_F6] = MyGUI::KeyCode::F6;
|
||||
keyMap[SDLK_F7] = MyGUI::KeyCode::F7;
|
||||
keyMap[SDLK_F8] = MyGUI::KeyCode::F8;
|
||||
keyMap[SDLK_F9] = MyGUI::KeyCode::F9;
|
||||
keyMap[SDLK_F10] = MyGUI::KeyCode::F10;
|
||||
keyMap[SDLK_NUMLOCKCLEAR] = MyGUI::KeyCode::NumLock;
|
||||
keyMap[SDLK_SCROLLLOCK] = MyGUI::KeyCode::ScrollLock;
|
||||
keyMap[SDLK_KP_7] = MyGUI::KeyCode::Numpad7;
|
||||
keyMap[SDLK_KP_8] = MyGUI::KeyCode::Numpad8;
|
||||
keyMap[SDLK_KP_9] = MyGUI::KeyCode::Numpad9;
|
||||
keyMap[SDLK_KP_MINUS] = MyGUI::KeyCode::Subtract;
|
||||
keyMap[SDLK_KP_4] = MyGUI::KeyCode::Numpad4;
|
||||
keyMap[SDLK_KP_5] = MyGUI::KeyCode::Numpad5;
|
||||
keyMap[SDLK_KP_6] = MyGUI::KeyCode::Numpad6;
|
||||
keyMap[SDLK_KP_PLUS] = MyGUI::KeyCode::Add;
|
||||
keyMap[SDLK_KP_1] = MyGUI::KeyCode::Numpad1;
|
||||
keyMap[SDLK_KP_2] = MyGUI::KeyCode::Numpad2;
|
||||
keyMap[SDLK_KP_3] = MyGUI::KeyCode::Numpad3;
|
||||
keyMap[SDLK_KP_0] = MyGUI::KeyCode::Numpad0;
|
||||
keyMap[SDLK_KP_PERIOD] = MyGUI::KeyCode::Decimal;
|
||||
keyMap[SDLK_F11] = MyGUI::KeyCode::F11;
|
||||
keyMap[SDLK_F12] = MyGUI::KeyCode::F12;
|
||||
keyMap[SDLK_F13] = MyGUI::KeyCode::F13;
|
||||
keyMap[SDLK_F14] = MyGUI::KeyCode::F14;
|
||||
keyMap[SDLK_F15] = MyGUI::KeyCode::F15;
|
||||
keyMap[SDLK_KP_EQUALS] = MyGUI::KeyCode::NumpadEquals;
|
||||
keyMap[SDLK_COLON] = MyGUI::KeyCode::Colon;
|
||||
keyMap[SDLK_KP_ENTER] = MyGUI::KeyCode::NumpadEnter;
|
||||
keyMap[SDLK_KP_DIVIDE] = MyGUI::KeyCode::Divide;
|
||||
keyMap[SDLK_SYSREQ] = MyGUI::KeyCode::SysRq;
|
||||
keyMap[SDLK_RALT] = MyGUI::KeyCode::RightAlt;
|
||||
keyMap[SDLK_HOME] = MyGUI::KeyCode::Home;
|
||||
keyMap[SDLK_UP] = MyGUI::KeyCode::ArrowUp;
|
||||
keyMap[SDLK_PAGEUP] = MyGUI::KeyCode::PageUp;
|
||||
keyMap[SDLK_LEFT] = MyGUI::KeyCode::ArrowLeft;
|
||||
keyMap[SDLK_RIGHT] = MyGUI::KeyCode::ArrowRight;
|
||||
keyMap[SDLK_END] = MyGUI::KeyCode::End;
|
||||
keyMap[SDLK_DOWN] = MyGUI::KeyCode::ArrowDown;
|
||||
keyMap[SDLK_PAGEDOWN] = MyGUI::KeyCode::PageDown;
|
||||
keyMap[SDLK_INSERT] = MyGUI::KeyCode::Insert;
|
||||
keyMap[SDLK_DELETE] = MyGUI::KeyCode::Delete;
|
||||
keyMap[SDLK_APPLICATION] = MyGUI::KeyCode::AppMenu;
|
||||
|
||||
//The function of the Ctrl and Meta keys are switched on macOS compared to other platforms.
|
||||
//For instance] = Cmd+C versus Ctrl+C to copy from the system clipboard
|
||||
#if defined(__APPLE__)
|
||||
keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftControl;
|
||||
keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightControl;
|
||||
keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftWindows;
|
||||
keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightWindows;
|
||||
#else
|
||||
keyMap[SDLK_LGUI] = MyGUI::KeyCode::LeftWindows;
|
||||
keyMap[SDLK_RGUI] = MyGUI::KeyCode::RightWindows;
|
||||
keyMap[SDLK_LCTRL] = MyGUI::KeyCode::LeftControl;
|
||||
keyMap[SDLK_RCTRL] = MyGUI::KeyCode::RightControl;
|
||||
#endif
|
||||
}
|
||||
|
||||
MyGUI::KeyCode sdlKeyToMyGUI(SDL_Keycode code)
|
||||
{
|
||||
static std::map<SDL_Keycode, MyGUI::KeyCode> keyMap;
|
||||
if (keyMap.empty())
|
||||
initKeyMap(keyMap);
|
||||
|
||||
MyGUI::KeyCode kc = MyGUI::KeyCode::None;
|
||||
auto foundKey = keyMap.find(code);
|
||||
if (foundKey != keyMap.end())
|
||||
kc = foundKey->second;
|
||||
|
||||
return kc;
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
#include <cstring>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
@ -11,15 +13,35 @@
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
Action::Action(LuaUtil::LuaState* state)
|
||||
{
|
||||
static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua");
|
||||
if (luaDebug)
|
||||
mCallerTraceback = state->debugTraceback();
|
||||
}
|
||||
|
||||
void Action::safeApply(WorldView& w) const
|
||||
{
|
||||
try
|
||||
{
|
||||
apply(w);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Error in " << this->toString() << ": " << e.what();
|
||||
|
||||
if (mCallerTraceback.empty())
|
||||
Log(Debug::Error) << "Set 'lua_debug=true' in settings.cfg to enable action tracebacks";
|
||||
else
|
||||
Log(Debug::Error) << "Caller " << mCallerTraceback;
|
||||
}
|
||||
}
|
||||
|
||||
void TeleportAction::apply(WorldView& worldView) const
|
||||
{
|
||||
MWWorld::CellStore* cell = worldView.findCell(mCell, mPos);
|
||||
if (!cell)
|
||||
{
|
||||
Log(Debug::Error) << "LuaManager::applyTeleport -> cell not found: '" << mCell << "'";
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error(std::string("cell not found: '") + mCell + "'");
|
||||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false);
|
||||
|
@ -6,6 +6,11 @@
|
||||
#include "object.hpp"
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
class LuaState;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
@ -16,17 +21,25 @@ namespace MWLua
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
Action(LuaUtil::LuaState* state);
|
||||
virtual ~Action() {}
|
||||
|
||||
void safeApply(WorldView&) const;
|
||||
virtual void apply(WorldView&) const = 0;
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
private:
|
||||
std::string mCallerTraceback;
|
||||
};
|
||||
|
||||
class TeleportAction final : public Action
|
||||
{
|
||||
public:
|
||||
TeleportAction(ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot)
|
||||
: mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {}
|
||||
TeleportAction(LuaUtil::LuaState* state, ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot)
|
||||
: Action(state), mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {}
|
||||
|
||||
void apply(WorldView&) const override;
|
||||
std::string toString() const override { return "TeleportAction"; }
|
||||
|
||||
private:
|
||||
ObjectId mObject;
|
||||
@ -41,9 +54,11 @@ namespace MWLua
|
||||
using Item = std::variant<std::string, ObjectId>; // recordId or ObjectId
|
||||
using Equipment = std::map<int, Item>; // slot to item
|
||||
|
||||
SetEquipmentAction(ObjectId actor, Equipment equipment) : mActor(actor), mEquipment(std::move(equipment)) {}
|
||||
SetEquipmentAction(LuaUtil::LuaState* state, ObjectId actor, Equipment equipment)
|
||||
: Action(state), mActor(actor), mEquipment(std::move(equipment)) {}
|
||||
|
||||
void apply(WorldView&) const override;
|
||||
std::string toString() const override { return "SetEquipmentAction"; }
|
||||
|
||||
private:
|
||||
ObjectId mActor;
|
||||
|
@ -19,36 +19,36 @@ namespace MWLua
|
||||
|
||||
sol::function getAsyncPackageInitializer(const Context& context)
|
||||
{
|
||||
using TimeUnit = LuaUtil::ScriptsContainer::TimeUnit;
|
||||
using TimerType = LuaUtil::ScriptsContainer::TimerType;
|
||||
sol::usertype<AsyncPackageId> api = context.mLua->sol().new_usertype<AsyncPackageId>("AsyncPackage");
|
||||
api["registerTimerCallback"] = [](const AsyncPackageId& asyncId, std::string_view name, sol::function callback)
|
||||
{
|
||||
asyncId.mContainer->registerTimerCallback(asyncId.mScriptId, name, std::move(callback));
|
||||
return TimerCallback{asyncId, std::string(name)};
|
||||
};
|
||||
api["newTimerInSeconds"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
|
||||
const TimerCallback& callback, sol::object callbackArg)
|
||||
api["newSimulationTimer"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
|
||||
const TimerCallback& callback, sol::object callbackArg)
|
||||
{
|
||||
callback.mAsyncId.mContainer->setupSerializableTimer(
|
||||
TimeUnit::SECONDS, world->getGameTimeInSeconds() + delay,
|
||||
TimerType::SIMULATION_TIME, world->getSimulationTime() + delay,
|
||||
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
|
||||
};
|
||||
api["newTimerInHours"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
|
||||
const TimerCallback& callback, sol::object callbackArg)
|
||||
api["newGameTimer"] = [world=context.mWorldView](const AsyncPackageId&, double delay,
|
||||
const TimerCallback& callback, sol::object callbackArg)
|
||||
{
|
||||
callback.mAsyncId.mContainer->setupSerializableTimer(
|
||||
TimeUnit::HOURS, world->getGameTimeInHours() + delay,
|
||||
TimerType::GAME_TIME, world->getGameTime() + delay,
|
||||
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
|
||||
};
|
||||
api["newUnsavableTimerInSeconds"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
|
||||
api["newUnsavableSimulationTimer"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
|
||||
{
|
||||
asyncId.mContainer->setupUnsavableTimer(
|
||||
TimeUnit::SECONDS, world->getGameTimeInSeconds() + delay, asyncId.mScriptId, std::move(callback));
|
||||
TimerType::SIMULATION_TIME, world->getSimulationTime() + delay, asyncId.mScriptId, std::move(callback));
|
||||
};
|
||||
api["newUnsavableTimerInHours"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
|
||||
api["newUnsavableGameTimer"] = [world=context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback)
|
||||
{
|
||||
asyncId.mContainer->setupUnsavableTimer(
|
||||
TimeUnit::HOURS, world->getGameTimeInHours() + delay, asyncId.mScriptId, std::move(callback));
|
||||
TimerType::GAME_TIME, world->getGameTime() + delay, asyncId.mScriptId, std::move(callback));
|
||||
};
|
||||
api["callback"] = [](const AsyncPackageId& asyncId, sol::function fn)
|
||||
{
|
||||
|
@ -1,12 +1,82 @@
|
||||
#include "luabindings.hpp"
|
||||
|
||||
#include "../mwrender/camera.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
using CameraMode = MWRender::Camera::Mode;
|
||||
|
||||
sol::table initCameraPackage(const Context& context)
|
||||
{
|
||||
MWRender::Camera* camera = MWBase::Environment::get().getWorld()->getCamera();
|
||||
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
// TODO
|
||||
api["MODE"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with(
|
||||
"Static", CameraMode::Static,
|
||||
"FirstPerson", CameraMode::FirstPerson,
|
||||
"ThirdPerson", CameraMode::ThirdPerson,
|
||||
"Vanity", CameraMode::Vanity,
|
||||
"Preview", CameraMode::Preview
|
||||
));
|
||||
|
||||
api["getMode"] = [camera]() -> int { return static_cast<int>(camera->getMode()); };
|
||||
api["getQueuedMode"] = [camera]() -> sol::optional<int>
|
||||
{
|
||||
std::optional<CameraMode> mode = camera->getQueuedMode();
|
||||
if (mode)
|
||||
return static_cast<int>(*mode);
|
||||
else
|
||||
return sol::nullopt;
|
||||
};
|
||||
api["setMode"] = [camera](int mode, sol::optional<bool> force)
|
||||
{
|
||||
camera->setMode(static_cast<CameraMode>(mode), force ? *force : false);
|
||||
};
|
||||
|
||||
api["allowCharacterDeferredRotation"] = [camera](bool v) { camera->allowCharacterDeferredRotation(v); };
|
||||
api["showCrosshair"] = [camera](bool v) { camera->showCrosshair(v); };
|
||||
|
||||
api["getTrackedPosition"] = [camera]() -> osg::Vec3f { return camera->getTrackedPosition(); };
|
||||
api["getPosition"] = [camera]() -> osg::Vec3f { return camera->getPosition(); };
|
||||
|
||||
// All angles are negated in order to make camera rotation consistent with objects rotation.
|
||||
// TODO: Fix the inconsistency of rotation direction in camera.cpp.
|
||||
api["getPitch"] = [camera]() { return -camera->getPitch(); };
|
||||
api["getYaw"] = [camera]() { return -camera->getYaw(); };
|
||||
api["getRoll"] = [camera]() { return -camera->getRoll(); };
|
||||
|
||||
api["setStaticPosition"] = [camera](const osg::Vec3f& pos) { camera->setStaticPosition(pos); };
|
||||
api["setPitch"] = [camera](float v)
|
||||
{
|
||||
camera->setPitch(-v, true);
|
||||
if (camera->getMode() == CameraMode::ThirdPerson)
|
||||
camera->calculateDeferredRotation();
|
||||
};
|
||||
api["setYaw"] = [camera](float v)
|
||||
{
|
||||
camera->setYaw(-v, true);
|
||||
if (camera->getMode() == CameraMode::ThirdPerson)
|
||||
camera->calculateDeferredRotation();
|
||||
};
|
||||
api["setRoll"] = [camera](float v) { camera->setRoll(-v); };
|
||||
api["setExtraPitch"] = [camera](float v) { camera->setExtraPitch(-v); };
|
||||
api["setExtraYaw"] = [camera](float v) { camera->setExtraYaw(-v); };
|
||||
api["getExtraPitch"] = [camera]() { return -camera->getExtraPitch(); };
|
||||
api["getExtraYaw"] = [camera]() { return -camera->getExtraYaw(); };
|
||||
|
||||
api["getThirdPersonDistance"] = [camera]() { return camera->getCameraDistance(); };
|
||||
api["setPreferredThirdPersonDistance"] = [camera](float v) { camera->setPreferredCameraDistance(v); };
|
||||
|
||||
api["getFirstPersonOffset"] = [camera]() { return camera->getFirstPersonOffset(); };
|
||||
api["setFirstPersonOffset"] = [camera](const osg::Vec3f& v) { camera->setFirstPersonOffset(v); };
|
||||
|
||||
api["getFocalPreferredOffset"] = [camera]() -> osg::Vec2f { return camera->getFocalPointTargetOffset(); };
|
||||
api["setFocalPreferredOffset"] = [camera](const osg::Vec2f& v) { camera->setFocalPointTargetOffset(v); };
|
||||
api["getFocalTransitionSpeed"] = [camera]() { return camera->getFocalPointTransitionSpeed(); };
|
||||
api["setFocalTransitionSpeed"] = [camera](float v) { camera->setFocalPointTransitionSpeed(v); };
|
||||
api["instantTransition"] = [camera]() { camera->instantTransition(); };
|
||||
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,14 @@
|
||||
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<MWLua::LCell> : std::false_type {};
|
||||
template <>
|
||||
struct is_automagical<MWLua::GCell> : std::false_type {};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
|
@ -7,6 +7,7 @@ namespace LuaUtil
|
||||
{
|
||||
class LuaState;
|
||||
class UserdataSerializer;
|
||||
class I18nManager;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
@ -20,6 +21,7 @@ namespace MWLua
|
||||
LuaManager* mLuaManager;
|
||||
LuaUtil::LuaState* mLua;
|
||||
LuaUtil::UserdataSerializer* mSerializer;
|
||||
LuaUtil::I18nManager* mI18n;
|
||||
WorldView* mWorldView;
|
||||
LocalEventQueue* mLocalEventQueue;
|
||||
GlobalEventQueue* mGlobalEventQueue;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_gamecontroller.h>
|
||||
#include <SDL_mouse.h>
|
||||
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwinput/actions.hpp"
|
||||
@ -18,9 +19,14 @@ namespace MWLua
|
||||
sol::table initInputPackage(const Context& context)
|
||||
{
|
||||
sol::usertype<SDL_Keysym> keyEvent = context.mLua->sol().new_usertype<SDL_Keysym>("KeyEvent");
|
||||
keyEvent["symbol"] = sol::readonly_property([](const SDL_Keysym& e) { return std::string(1, static_cast<char>(e.sym)); });
|
||||
keyEvent["code"] = sol::readonly_property([](const SDL_Keysym& e) -> int { return e.sym; });
|
||||
keyEvent["modifiers"] = sol::readonly_property([](const SDL_Keysym& e) -> int { return e.mod; });
|
||||
keyEvent["symbol"] = sol::readonly_property([](const SDL_Keysym& e)
|
||||
{
|
||||
if (e.sym > 0 && e.sym <= 255)
|
||||
return std::string(1, static_cast<char>(e.sym));
|
||||
else
|
||||
return std::string();
|
||||
});
|
||||
keyEvent["code"] = sol::readonly_property([](const SDL_Keysym& e) -> int { return e.scancode; });
|
||||
keyEvent["withShift"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_SHIFT; });
|
||||
keyEvent["withCtrl"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_CTRL; });
|
||||
keyEvent["withAlt"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_ALT; });
|
||||
@ -31,9 +37,26 @@ namespace MWLua
|
||||
|
||||
api["isIdle"] = [input]() { return input->isIdle(); };
|
||||
api["isActionPressed"] = [input](int action) { return input->actionIsActive(action); };
|
||||
api["isMouseButtonPressed"] = [input](int button) -> bool
|
||||
api["isKeyPressed"] = [](SDL_Scancode code) -> bool
|
||||
{
|
||||
return input->getMouseButtonsState() & (1 << (button - 1));
|
||||
int maxCode;
|
||||
const auto* state = SDL_GetKeyboardState(&maxCode);
|
||||
if (code >= 0 && code < maxCode)
|
||||
return state[code] != 0;
|
||||
else
|
||||
return false;
|
||||
};
|
||||
api["isShiftPressed"] = []() -> bool { return SDL_GetModState() & KMOD_SHIFT; };
|
||||
api["isCtrlPressed"] = []() -> bool { return SDL_GetModState() & KMOD_CTRL; };
|
||||
api["isAltPressed"] = []() -> bool { return SDL_GetModState() & KMOD_ALT; };
|
||||
api["isSuperPressed"] = []() -> bool { return SDL_GetModState() & KMOD_GUI; };
|
||||
api["isControllerButtonPressed"] = [input](int button)
|
||||
{
|
||||
return input->isControllerButtonPressed(static_cast<SDL_GameControllerButton>(button));
|
||||
};
|
||||
api["isMouseButtonPressed"] = [](int button) -> bool
|
||||
{
|
||||
return SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(button);
|
||||
};
|
||||
api["getMouseMoveX"] = [input]() { return input->getMouseMoveX(); };
|
||||
api["getMouseMoveY"] = [input]() { return input->getMouseMoveY(); };
|
||||
@ -45,104 +68,219 @@ namespace MWLua
|
||||
return input->getActionValue(axis - SDL_CONTROLLER_AXIS_MAX) * 2 - 1;
|
||||
};
|
||||
|
||||
api["getControlSwitch"] = [input](const std::string& key) { return input->getControlSwitch(key); };
|
||||
api["setControlSwitch"] = [input](const std::string& key, bool v) { input->toggleControlSwitch(key, v); };
|
||||
api["getControlSwitch"] = [input](std::string_view key) { return input->getControlSwitch(key); };
|
||||
api["setControlSwitch"] = [input](std::string_view key, bool v) { input->toggleControlSwitch(key, v); };
|
||||
|
||||
api["ACTION"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with(
|
||||
"GameMenu", MWInput::A_GameMenu,
|
||||
"Screenshot", MWInput::A_Screenshot,
|
||||
"Inventory", MWInput::A_Inventory,
|
||||
"Console", MWInput::A_Console,
|
||||
api["getKeyName"] = [](SDL_Scancode code) {
|
||||
return SDL_GetKeyName(SDL_GetKeyFromScancode(code));
|
||||
};
|
||||
|
||||
"MoveLeft", MWInput::A_MoveLeft,
|
||||
"MoveRight", MWInput::A_MoveRight,
|
||||
"MoveForward", MWInput::A_MoveForward,
|
||||
"MoveBackward", MWInput::A_MoveBackward,
|
||||
api["ACTION"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, MWInput::Actions>({
|
||||
{"GameMenu", MWInput::A_GameMenu},
|
||||
{"Screenshot", MWInput::A_Screenshot},
|
||||
{"Inventory", MWInput::A_Inventory},
|
||||
{"Console", MWInput::A_Console},
|
||||
|
||||
"Activate", MWInput::A_Activate,
|
||||
"Use", MWInput::A_Use,
|
||||
"Jump", MWInput::A_Jump,
|
||||
"AutoMove", MWInput::A_AutoMove,
|
||||
"Rest", MWInput::A_Rest,
|
||||
"Journal", MWInput::A_Journal,
|
||||
"Weapon", MWInput::A_Weapon,
|
||||
"Spell", MWInput::A_Spell,
|
||||
"Run", MWInput::A_Run,
|
||||
"CycleSpellLeft", MWInput::A_CycleSpellLeft,
|
||||
"CycleSpellRight", MWInput::A_CycleSpellRight,
|
||||
"CycleWeaponLeft", MWInput::A_CycleWeaponLeft,
|
||||
"CycleWeaponRight", MWInput::A_CycleWeaponRight,
|
||||
"ToggleSneak", MWInput::A_ToggleSneak,
|
||||
"AlwaysRun", MWInput::A_AlwaysRun,
|
||||
"Sneak", MWInput::A_Sneak,
|
||||
{"MoveLeft", MWInput::A_MoveLeft},
|
||||
{"MoveRight", MWInput::A_MoveRight},
|
||||
{"MoveForward", MWInput::A_MoveForward},
|
||||
{"MoveBackward", MWInput::A_MoveBackward},
|
||||
|
||||
"QuickSave", MWInput::A_QuickSave,
|
||||
"QuickLoad", MWInput::A_QuickLoad,
|
||||
"QuickMenu", MWInput::A_QuickMenu,
|
||||
"ToggleWeapon", MWInput::A_ToggleWeapon,
|
||||
"ToggleSpell", MWInput::A_ToggleSpell,
|
||||
"TogglePOV", MWInput::A_TogglePOV,
|
||||
{"Activate", MWInput::A_Activate},
|
||||
{"Use", MWInput::A_Use},
|
||||
{"Jump", MWInput::A_Jump},
|
||||
{"AutoMove", MWInput::A_AutoMove},
|
||||
{"Rest", MWInput::A_Rest},
|
||||
{"Journal", MWInput::A_Journal},
|
||||
{"Weapon", MWInput::A_Weapon},
|
||||
{"Spell", MWInput::A_Spell},
|
||||
{"Run", MWInput::A_Run},
|
||||
{"CycleSpellLeft", MWInput::A_CycleSpellLeft},
|
||||
{"CycleSpellRight", MWInput::A_CycleSpellRight},
|
||||
{"CycleWeaponLeft", MWInput::A_CycleWeaponLeft},
|
||||
{"CycleWeaponRight", MWInput::A_CycleWeaponRight},
|
||||
{"ToggleSneak", MWInput::A_ToggleSneak},
|
||||
{"AlwaysRun", MWInput::A_AlwaysRun},
|
||||
{"Sneak", MWInput::A_Sneak},
|
||||
|
||||
"QuickKey1", MWInput::A_QuickKey1,
|
||||
"QuickKey2", MWInput::A_QuickKey2,
|
||||
"QuickKey3", MWInput::A_QuickKey3,
|
||||
"QuickKey4", MWInput::A_QuickKey4,
|
||||
"QuickKey5", MWInput::A_QuickKey5,
|
||||
"QuickKey6", MWInput::A_QuickKey6,
|
||||
"QuickKey7", MWInput::A_QuickKey7,
|
||||
"QuickKey8", MWInput::A_QuickKey8,
|
||||
"QuickKey9", MWInput::A_QuickKey9,
|
||||
"QuickKey10", MWInput::A_QuickKey10,
|
||||
"QuickKeysMenu", MWInput::A_QuickKeysMenu,
|
||||
{"QuickSave", MWInput::A_QuickSave},
|
||||
{"QuickLoad", MWInput::A_QuickLoad},
|
||||
{"QuickMenu", MWInput::A_QuickMenu},
|
||||
{"ToggleWeapon", MWInput::A_ToggleWeapon},
|
||||
{"ToggleSpell", MWInput::A_ToggleSpell},
|
||||
{"TogglePOV", MWInput::A_TogglePOV},
|
||||
|
||||
"ToggleHUD", MWInput::A_ToggleHUD,
|
||||
"ToggleDebug", MWInput::A_ToggleDebug,
|
||||
{"QuickKey1", MWInput::A_QuickKey1},
|
||||
{"QuickKey2", MWInput::A_QuickKey2},
|
||||
{"QuickKey3", MWInput::A_QuickKey3},
|
||||
{"QuickKey4", MWInput::A_QuickKey4},
|
||||
{"QuickKey5", MWInput::A_QuickKey5},
|
||||
{"QuickKey6", MWInput::A_QuickKey6},
|
||||
{"QuickKey7", MWInput::A_QuickKey7},
|
||||
{"QuickKey8", MWInput::A_QuickKey8},
|
||||
{"QuickKey9", MWInput::A_QuickKey9},
|
||||
{"QuickKey10", MWInput::A_QuickKey10},
|
||||
{"QuickKeysMenu", MWInput::A_QuickKeysMenu},
|
||||
|
||||
"ZoomIn", MWInput::A_ZoomIn,
|
||||
"ZoomOut", MWInput::A_ZoomOut
|
||||
));
|
||||
{"ToggleHUD", MWInput::A_ToggleHUD},
|
||||
{"ToggleDebug", MWInput::A_ToggleDebug},
|
||||
|
||||
api["CONTROL_SWITCH"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with(
|
||||
"Controls", "playercontrols",
|
||||
"Fighting", "playerfighting",
|
||||
"Jumping", "playerjumping",
|
||||
"Looking", "playerlooking",
|
||||
"Magic", "playermagic",
|
||||
"ViewMode", "playerviewswitch",
|
||||
"VanityMode", "vanitymode"
|
||||
));
|
||||
{"ZoomIn", MWInput::A_ZoomIn},
|
||||
{"ZoomOut", MWInput::A_ZoomOut}
|
||||
}));
|
||||
|
||||
api["CONTROLLER_BUTTON"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with(
|
||||
"A", SDL_CONTROLLER_BUTTON_A,
|
||||
"B", SDL_CONTROLLER_BUTTON_B,
|
||||
"X", SDL_CONTROLLER_BUTTON_X,
|
||||
"Y", SDL_CONTROLLER_BUTTON_Y,
|
||||
"Back", SDL_CONTROLLER_BUTTON_BACK,
|
||||
"Guide", SDL_CONTROLLER_BUTTON_GUIDE,
|
||||
"Start", SDL_CONTROLLER_BUTTON_START,
|
||||
"LeftStick", SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
||||
"RightStick", SDL_CONTROLLER_BUTTON_RIGHTSTICK,
|
||||
"LeftShoulder", SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
|
||||
"RightShoulder", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
||||
"DPadUp", SDL_CONTROLLER_BUTTON_DPAD_UP,
|
||||
"DPadDown", SDL_CONTROLLER_BUTTON_DPAD_DOWN,
|
||||
"DPadLeft", SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
"DPadRight", SDL_CONTROLLER_BUTTON_DPAD_RIGHT
|
||||
));
|
||||
api["CONTROL_SWITCH"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, std::string_view>({
|
||||
{"Controls", "playercontrols"},
|
||||
{"Fighting", "playerfighting"},
|
||||
{"Jumping", "playerjumping"},
|
||||
{"Looking", "playerlooking"},
|
||||
{"Magic", "playermagic"},
|
||||
{"ViewMode", "playerviewswitch"},
|
||||
{"VanityMode", "vanitymode"}
|
||||
}));
|
||||
|
||||
api["CONTROLLER_AXIS"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with(
|
||||
"LeftX", SDL_CONTROLLER_AXIS_LEFTX,
|
||||
"LeftY", SDL_CONTROLLER_AXIS_LEFTY,
|
||||
"RightX", SDL_CONTROLLER_AXIS_RIGHTX,
|
||||
"RightY", SDL_CONTROLLER_AXIS_RIGHTY,
|
||||
"TriggerLeft", SDL_CONTROLLER_AXIS_TRIGGERLEFT,
|
||||
"TriggerRight", SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
||||
api["CONTROLLER_BUTTON"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, SDL_GameControllerButton>({
|
||||
{"A", SDL_CONTROLLER_BUTTON_A},
|
||||
{"B", SDL_CONTROLLER_BUTTON_B},
|
||||
{"X", SDL_CONTROLLER_BUTTON_X},
|
||||
{"Y", SDL_CONTROLLER_BUTTON_Y},
|
||||
{"Back", SDL_CONTROLLER_BUTTON_BACK},
|
||||
{"Guide", SDL_CONTROLLER_BUTTON_GUIDE},
|
||||
{"Start", SDL_CONTROLLER_BUTTON_START},
|
||||
{"LeftStick", SDL_CONTROLLER_BUTTON_LEFTSTICK},
|
||||
{"RightStick", SDL_CONTROLLER_BUTTON_RIGHTSTICK},
|
||||
{"LeftShoulder", SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
|
||||
{"RightShoulder", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
|
||||
{"DPadUp", SDL_CONTROLLER_BUTTON_DPAD_UP},
|
||||
{"DPadDown", SDL_CONTROLLER_BUTTON_DPAD_DOWN},
|
||||
{"DPadLeft", SDL_CONTROLLER_BUTTON_DPAD_LEFT},
|
||||
{"DPadRight", SDL_CONTROLLER_BUTTON_DPAD_RIGHT}
|
||||
}));
|
||||
|
||||
"LookUpDown", SDL_CONTROLLER_AXIS_MAX + MWInput::A_LookUpDown,
|
||||
"LookLeftRight", SDL_CONTROLLER_AXIS_MAX + MWInput::A_LookLeftRight,
|
||||
"MoveForwardBackward", SDL_CONTROLLER_AXIS_MAX + MWInput::A_MoveForwardBackward,
|
||||
"MoveLeftRight", SDL_CONTROLLER_AXIS_MAX + MWInput::A_MoveLeftRight
|
||||
));
|
||||
api["CONTROLLER_AXIS"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
|
||||
{"LeftX", SDL_CONTROLLER_AXIS_LEFTX},
|
||||
{"LeftY", SDL_CONTROLLER_AXIS_LEFTY},
|
||||
{"RightX", SDL_CONTROLLER_AXIS_RIGHTX},
|
||||
{"RightY", SDL_CONTROLLER_AXIS_RIGHTY},
|
||||
{"TriggerLeft", SDL_CONTROLLER_AXIS_TRIGGERLEFT},
|
||||
{"TriggerRight", SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
|
||||
|
||||
{"LookUpDown", SDL_CONTROLLER_AXIS_MAX + MWInput::A_LookUpDown},
|
||||
{"LookLeftRight", SDL_CONTROLLER_AXIS_MAX + MWInput::A_LookLeftRight},
|
||||
{"MoveForwardBackward", SDL_CONTROLLER_AXIS_MAX + MWInput::A_MoveForwardBackward},
|
||||
{"MoveLeftRight", SDL_CONTROLLER_AXIS_MAX + MWInput::A_MoveLeftRight}
|
||||
}));
|
||||
|
||||
api["KEY"] = LuaUtil::makeReadOnly(context.mLua->tableFromPairs<std::string_view, SDL_Scancode>({
|
||||
{"_0", SDL_SCANCODE_0},
|
||||
{"_1", SDL_SCANCODE_1},
|
||||
{"_2", SDL_SCANCODE_2},
|
||||
{"_3", SDL_SCANCODE_3},
|
||||
{"_4", SDL_SCANCODE_4},
|
||||
{"_5", SDL_SCANCODE_5},
|
||||
{"_6", SDL_SCANCODE_6},
|
||||
{"_7", SDL_SCANCODE_7},
|
||||
{"_8", SDL_SCANCODE_8},
|
||||
{"_9", SDL_SCANCODE_9},
|
||||
|
||||
{"NP_0", SDL_SCANCODE_KP_0},
|
||||
{"NP_1", SDL_SCANCODE_KP_1},
|
||||
{"NP_2", SDL_SCANCODE_KP_2},
|
||||
{"NP_3", SDL_SCANCODE_KP_3},
|
||||
{"NP_4", SDL_SCANCODE_KP_4},
|
||||
{"NP_5", SDL_SCANCODE_KP_5},
|
||||
{"NP_6", SDL_SCANCODE_KP_6},
|
||||
{"NP_7", SDL_SCANCODE_KP_7},
|
||||
{"NP_8", SDL_SCANCODE_KP_8},
|
||||
{"NP_9", SDL_SCANCODE_KP_9},
|
||||
{"NP_Divide", SDL_SCANCODE_KP_DIVIDE},
|
||||
{"NP_Enter", SDL_SCANCODE_KP_ENTER},
|
||||
{"NP_Minus", SDL_SCANCODE_KP_MINUS},
|
||||
{"NP_Multiply", SDL_SCANCODE_KP_MULTIPLY},
|
||||
{"NP_Delete", SDL_SCANCODE_KP_PERIOD},
|
||||
{"NP_Plus", SDL_SCANCODE_KP_PLUS},
|
||||
|
||||
{"F1", SDL_SCANCODE_F1},
|
||||
{"F2", SDL_SCANCODE_F2},
|
||||
{"F3", SDL_SCANCODE_F3},
|
||||
{"F4", SDL_SCANCODE_F4},
|
||||
{"F5", SDL_SCANCODE_F5},
|
||||
{"F6", SDL_SCANCODE_F6},
|
||||
{"F7", SDL_SCANCODE_F7},
|
||||
{"F8", SDL_SCANCODE_F8},
|
||||
{"F9", SDL_SCANCODE_F9},
|
||||
{"F10", SDL_SCANCODE_F10},
|
||||
{"F11", SDL_SCANCODE_F11},
|
||||
{"F12", SDL_SCANCODE_F12},
|
||||
|
||||
{"A", SDL_SCANCODE_A},
|
||||
{"B", SDL_SCANCODE_B},
|
||||
{"C", SDL_SCANCODE_C},
|
||||
{"D", SDL_SCANCODE_D},
|
||||
{"E", SDL_SCANCODE_E},
|
||||
{"F", SDL_SCANCODE_F},
|
||||
{"G", SDL_SCANCODE_G},
|
||||
{"H", SDL_SCANCODE_H},
|
||||
{"I", SDL_SCANCODE_I},
|
||||
{"J", SDL_SCANCODE_J},
|
||||
{"K", SDL_SCANCODE_K},
|
||||
{"L", SDL_SCANCODE_L},
|
||||
{"M", SDL_SCANCODE_M},
|
||||
{"N", SDL_SCANCODE_N},
|
||||
{"O", SDL_SCANCODE_O},
|
||||
{"P", SDL_SCANCODE_P},
|
||||
{"Q", SDL_SCANCODE_Q},
|
||||
{"R", SDL_SCANCODE_R},
|
||||
{"S", SDL_SCANCODE_S},
|
||||
{"T", SDL_SCANCODE_T},
|
||||
{"U", SDL_SCANCODE_U},
|
||||
{"V", SDL_SCANCODE_V},
|
||||
{"W", SDL_SCANCODE_W},
|
||||
{"X", SDL_SCANCODE_X},
|
||||
{"Y", SDL_SCANCODE_Y},
|
||||
{"Z", SDL_SCANCODE_Z},
|
||||
|
||||
{"LeftArrow", SDL_SCANCODE_LEFT},
|
||||
{"RightArrow", SDL_SCANCODE_RIGHT},
|
||||
{"UpArrow", SDL_SCANCODE_UP},
|
||||
{"DownArrow", SDL_SCANCODE_DOWN},
|
||||
|
||||
{"LeftAlt", SDL_SCANCODE_LALT},
|
||||
{"LeftCtrl", SDL_SCANCODE_LCTRL},
|
||||
{"LeftBracket", SDL_SCANCODE_LEFTBRACKET},
|
||||
{"LeftSuper", SDL_SCANCODE_LGUI},
|
||||
{"LeftShift", SDL_SCANCODE_LSHIFT},
|
||||
{"RightAlt", SDL_SCANCODE_RALT},
|
||||
{"RightCtrl", SDL_SCANCODE_RCTRL},
|
||||
{"RightSuper", SDL_SCANCODE_RGUI},
|
||||
{"RightBracket", SDL_SCANCODE_RIGHTBRACKET},
|
||||
{"RightShift", SDL_SCANCODE_RSHIFT},
|
||||
|
||||
{"Apostrophe", SDL_SCANCODE_APOSTROPHE},
|
||||
{"BackSlash", SDL_SCANCODE_BACKSLASH},
|
||||
{"Backspace", SDL_SCANCODE_BACKSPACE},
|
||||
{"CapsLock", SDL_SCANCODE_CAPSLOCK},
|
||||
{"Comma", SDL_SCANCODE_COMMA},
|
||||
{"Delete", SDL_SCANCODE_DELETE},
|
||||
{"End", SDL_SCANCODE_END},
|
||||
{"Enter", SDL_SCANCODE_RETURN},
|
||||
{"Equals", SDL_SCANCODE_EQUALS},
|
||||
{"Escape", SDL_SCANCODE_ESCAPE},
|
||||
{"Home", SDL_SCANCODE_HOME},
|
||||
{"Insert", SDL_SCANCODE_INSERT},
|
||||
{"Minus", SDL_SCANCODE_MINUS},
|
||||
{"NumLock", SDL_SCANCODE_NUMLOCKCLEAR},
|
||||
{"PageDown", SDL_SCANCODE_PAGEDOWN},
|
||||
{"PageUp", SDL_SCANCODE_PAGEUP},
|
||||
{"Period", SDL_SCANCODE_PERIOD},
|
||||
{"Pause", SDL_SCANCODE_PAUSE},
|
||||
{"PrintScreen", SDL_SCANCODE_PRINTSCREEN},
|
||||
{"ScrollLock", SDL_SCANCODE_SCROLLLOCK},
|
||||
{"Semicolon", SDL_SCANCODE_SEMICOLON},
|
||||
{"Slash", SDL_SCANCODE_SLASH},
|
||||
{"Space", SDL_SCANCODE_SPACE},
|
||||
{"Tab", SDL_SCANCODE_TAB}
|
||||
}));
|
||||
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace MWLua
|
||||
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
|
||||
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
|
||||
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; };
|
||||
selfAPI["setEquipment"] = [manager=context.mLuaManager](const SelfObject& obj, sol::table equipment)
|
||||
selfAPI["setEquipment"] = [context](const SelfObject& obj, sol::table equipment)
|
||||
{
|
||||
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
|
||||
{
|
||||
@ -56,7 +56,7 @@ namespace MWLua
|
||||
else
|
||||
eqp[slot] = value.as<std::string>();
|
||||
}
|
||||
manager->addAction(std::make_unique<SetEquipmentAction>(obj.id(), std::move(eqp)));
|
||||
context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
|
||||
};
|
||||
selfAPI["getCombatTarget"] = [worldView=context.mWorldView](SelfObject& self) -> sol::optional<LObject>
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user