1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-29 04:06:40 +00:00

Merge remote-tracking branch 'upstream/master' into detain-hash-selectively-reluctant

Merge conflicts included:
* One setting being removed (branch had changed its type).
* One setting's description being changed (branch had changed its type).
* List of files in components/files was changed both upstream and on the
  branch.
* Upstream had changed something in a file the branch deletes.
This commit is contained in:
AnyOldName3 2021-12-09 17:09:52 +00:00
commit 8fc09f8c51
727 changed files with 22674 additions and 11072 deletions

View file

@ -8,7 +8,7 @@ env:
BUILD_TYPE: RelWithDebInfo BUILD_TYPE: RelWithDebInfo
jobs: jobs:
build: Ubuntu:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -26,25 +26,62 @@ jobs:
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }} key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
max-size: 1000M max-size: 1000M
- name: Install gtest
run: |
export CONFIGURATION="Release"
export GOOGLETEST_DIR="."
export GENERATOR="Unix Makefiles"
export CC="gcc"
export CXX="g++"
sudo -E CI/build_googletest.sh
- name: Configure - name: Configure
run: cmake -S . -B . -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_FLAGS='-Werror' -DCMAKE_CXX_FLAGS="-Werror -Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy" run: cmake -S . -B . -DGTEST_ROOT="$(pwd)/googletest/build" -DGMOCK_ROOT="$(pwd)/googletest/build" -DBUILD_UNITTESTS=ON -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_FLAGS='-Werror' -DCMAKE_CXX_FLAGS="-Werror -Wno-error=deprecated-declarations -Wno-error=nonnull -Wno-error=deprecated-copy"
- name: Build - name: Build
run: cmake --build . --config ${{env.BUILD_TYPE}} --parallel 3 run: cmake --build . --config ${{env.BUILD_TYPE}} --parallel 3
- name: Install - name: Test
shell: bash run: ./openmw_test_suite
run: cmake --install .
- name: Create Artifact # - name: Install
shell: bash # shell: bash
working-directory: install # run: cmake --install .
run: |
ls -laR
7z a ../build_artifact.7z .
- name: Upload Artifact # - name: Create Artifact
uses: actions/upload-artifact@v1 # shell: bash
# working-directory: install
# run: |
# ls -laR
# 7z a ../build_artifact.7z .
# - name: Upload Artifact
# uses: actions/upload-artifact@v1
# with:
# path: ./build_artifact.7z
# name: build_artifact.7z
MacOS:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Install Building Dependancies
run: CI/before_install.osx.sh
- name: Prime ccache
uses: hendrikmuhs/ccache-action@v1
with: with:
path: ./build_artifact.7z key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
name: build_artifact.7z max-size: 1000M
- name: Configure
run: |
rm -fr build # remove the build directory
CI/before_script.osx.sh
- name: Build
run: |
cd build
make -j $(sysctl -n hw.logicalcpu) package

View file

@ -3,14 +3,17 @@
stages: stages:
- build - build
.Debian_Image: .Ubuntu_Image:
tags: tags:
- docker - docker
- linux - linux
image: debian:bullseye image: ubuntu:focal
rules:
- if: $CI_PIPELINE_SOURCE == "push"
.Debian:
extends: .Debian_Image .Ubuntu:
extends: .Ubuntu_Image
cache: cache:
paths: paths:
- apt-cache/ - apt-cache/
@ -32,11 +35,12 @@ stages:
- build/install/ - build/install/
Clang_Tidy: Clang_Tidy:
extends: .Debian_Image extends: .Ubuntu_Image
stage: build stage: build
rules: rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"' - if: '$CI_PIPELINE_SOURCE == "schedule"'
before_script: before_script:
- apt-get update; apt-get -y install software-properties-common; add-apt-repository -y ppa:openmw/openmw
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic clang-tidy clang - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic clang-tidy clang
script: script:
- CI/before_script.linux.sh - CI/before_script.linux.sh
@ -47,13 +51,17 @@ Clang_Tidy:
CXX: clang++ CXX: clang++
CI_CLANG_TIDY: 1 CI_CLANG_TIDY: 1
timeout: 8h timeout: 8h
artifacts:
paths: []
expire_in: 1 minute
Coverity: Coverity:
extends: .Debian_Image extends: .Ubuntu_Image
stage: build stage: build
rules: rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"' - if: $CI_PIPELINE_SOURCE == "schedule"
before_script: before_script:
- apt-get update; apt-get -y install software-properties-common; add-apt-repository -y ppa:openmw/openmw
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic coverity - CI/install_debian_deps.sh gcc 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 - 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 - tar xfz /tmp/cov-analysis-linux64.tgz
@ -67,16 +75,20 @@ Coverity:
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="`git describe --tags`" --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 description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
- cat /builds/OpenMW/openmw/cov-int/build-log.txt
variables: variables:
CC: gcc CC: gcc
CXX: g++ CXX: g++
artifacts: artifacts:
paths: []
expire_in: 1 minute
Debian_GCC: Ubuntu_GCC:
extends: .Debian extends: .Ubuntu
cache: cache:
key: Debian_GCC.v2 key: Ubuntu_GCC.v2
before_script: before_script:
- apt-get update; apt-get -y install software-properties-common; add-apt-repository -y ppa:openmw/openmw
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-dynamic
variables: variables:
CC: gcc CC: gcc
@ -85,51 +97,62 @@ 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. # 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 timeout: 2h
Debian_GCC_tests: Ubuntu_GCC_tests:
extends: Debian_GCC extends: Ubuntu_GCC
cache: cache:
key: Debian_GCC_tests.v2 key: Ubuntu_GCC_tests.v2
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
artifacts:
paths: []
expire_in: 1 minute
Debian_GCC_tests_Debug: Ubuntu_GCC_tests_Debug:
extends: Debian_GCC extends: Ubuntu_GCC
cache: cache:
key: Debian_GCC_tests_Debug.v1 key: Ubuntu_GCC_tests_Debug.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
artifacts:
paths: []
expire_in: 1 minute
Debian_GCC_Static_Deps: Ubuntu_GCC_Static_Deps:
extends: Debian_GCC extends: Ubuntu_GCC
cache: cache:
key: Debian_GCC_Static_Deps key: Ubuntu_GCC_Static_Deps
paths: paths:
- apt-cache/ - apt-cache/
- ccache/ - ccache/
- build/extern/fetched/ - build/extern/fetched/
before_script: before_script:
- apt-get update; apt-get -y install software-properties-common; add-apt-repository -y ppa:openmw/openmw
- CI/install_debian_deps.sh gcc openmw-deps openmw-deps-static - CI/install_debian_deps.sh gcc openmw-deps openmw-deps-static
variables: variables:
CI_OPENMW_USE_STATIC_DEPS: 1 CI_OPENMW_USE_STATIC_DEPS: 1
timeout: 3h timeout: 3h
Debian_GCC_Static_Deps_tests: Ubuntu_GCC_Static_Deps_tests:
extends: Debian_GCC_Static_Deps extends: Ubuntu_GCC_Static_Deps
cache: cache:
key: Debian_GCC_Static_Deps_tests key: Ubuntu_GCC_Static_Deps_tests
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
artifacts:
paths: []
expire_in: 1 minute
Debian_Clang: Ubuntu_Clang:
extends: .Debian extends: .Ubuntu
before_script: before_script:
- apt-get update; apt-get -y install software-properties-common; add-apt-repository -y ppa:openmw/openmw
- CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic - CI/install_debian_deps.sh clang openmw-deps openmw-deps-dynamic
cache: cache:
key: Debian_Clang.v2 key: Ubuntu_Clang.v2
variables: variables:
CC: clang CC: clang
CXX: clang++ CXX: clang++
@ -137,22 +160,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. # 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 timeout: 2h
Debian_Clang_tests: Ubuntu_Clang_tests:
extends: Debian_Clang extends: Ubuntu_Clang
cache: cache:
key: Debian_Clang_tests.v2 key: Ubuntu_Clang_tests.v2
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
artifacts:
paths: []
expire_in: 1 minute
Debian_Clang_tests_Debug: Ubuntu_Clang_tests_Debug:
extends: Debian_Clang extends: Ubuntu_Clang
cache: cache:
key: Debian_Clang_tests_Debug.v1 key: Ubuntu_Clang_tests_Debug.v1
variables: variables:
CCACHE_SIZE: 1G CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1 BUILD_TESTS_ONLY: 1
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
artifacts:
paths: []
expire_in: 1 minute
.MacOS: .MacOS:
image: macos-11-xcode-12 image: macos-11-xcode-12
@ -161,7 +190,7 @@ Debian_Clang_tests_Debug:
stage: build stage: build
only: only:
variables: variables:
- $CI_PROJECT_ID == "7107382" - $CI_PROJECT_ID == "7107382" && $CI_PIPELINE_SOURCE == "push"
cache: cache:
paths: paths:
- ccache/ - ccache/
@ -174,7 +203,7 @@ Debian_Clang_tests_Debug:
- ccache -z -M "${CCACHE_SIZE}" - ccache -z -M "${CCACHE_SIZE}"
- CI/before_script.osx.sh - CI/before_script.osx.sh
- cd build; make -j $(sysctl -n hw.logicalcpu) package - cd build; make -j $(sysctl -n hw.logicalcpu) package
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done - for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME##*/}_${CI_JOB_ID}.dmg"; done
- ccache -s - ccache -s
artifacts: artifacts:
paths: paths:
@ -184,26 +213,17 @@ Debian_Clang_tests_Debug:
macOS11_Xcode12: macOS11_Xcode12:
extends: .MacOS extends: .MacOS
image: macos-11-xcode-12 image: macos-11-xcode-12
allow_failure: true
cache: cache:
key: macOS11_Xcode12.v1 key: macOS11_Xcode12.v1
variables: variables:
CCACHE_SIZE: 3G CCACHE_SIZE: 3G
macOS10.15_Xcode11:
extends: .MacOS
image: macos-10.15-xcode-11
cache:
key: macOS10.15_Xcode11.v1
variables:
CCACHE_SIZE: 3G
variables: &engine-targets variables: &engine-targets
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard" targets: "openmw,openmw-iniimporter,openmw-launcher,openmw-wizard"
package: "Engine" package: "Engine"
variables: &cs-targets variables: &cs-targets
targets: "openmw-cs,bsatool,esmtool,niftest" targets: "openmw-cs,bsatool,esmtool,niftest,openmw-essimporter"
package: "CS" package: "CS"
variables: &tests-targets variables: &tests-targets
@ -213,6 +233,8 @@ variables: &tests-targets
.Windows_Ninja_Base: .Windows_Ninja_Base:
tags: tags:
- windows - windows
rules:
- if: $CI_PIPELINE_SOURCE == "push"
before_script: before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1 - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
@ -325,10 +347,15 @@ Windows_Ninja_Tests_RelWithDebInfo:
config: "RelWithDebInfo" config: "RelWithDebInfo"
# Gitlab can't successfully execute following binaries due to unknown reason # Gitlab can't successfully execute following binaries due to unknown reason
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe" # executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
artifacts:
paths: []
expire_in: 1 minute
.Windows_MSBuild_Base: .Windows_MSBuild_Base:
tags: tags:
- windows - windows
rules:
- if: $CI_PIPELINE_SOURCE == "push"
before_script: before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1 - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
@ -395,6 +422,10 @@ Windows_MSBuild_Engine_Release:
variables: variables:
<<: *engine-targets <<: *engine-targets
config: "Release" 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: Windows_MSBuild_Engine_Debug:
extends: extends:
@ -416,6 +447,10 @@ Windows_MSBuild_CS_Release:
variables: variables:
<<: *cs-targets <<: *cs-targets
config: "Release" 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: Windows_MSBuild_CS_Debug:
extends: extends:
@ -439,25 +474,28 @@ Windows_MSBuild_Tests_RelWithDebInfo:
config: "RelWithDebInfo" config: "RelWithDebInfo"
# Gitlab can't successfully execute following binaries due to unknown reason # Gitlab can't successfully execute following binaries due to unknown reason
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe" # 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: tags:
- linux - linux
image: debian:bullseye image: psi29a/android-ndk:focal-ndk22
rules:
- if: $CI_PIPELINE_SOURCE == "push"
variables: variables:
CCACHE_SIZE: 3G CCACHE_SIZE: 3G
cache: cache:
key: Debian_AndroidNDK_arm64-v8a.v3 key: Ubuntu__Focal_AndroidNDK_r22b_arm64-v8a.v1
paths: paths:
- apt-cache/ - apt-cache/
- ccache/ - ccache/
- build/extern/fetched/ - build/extern/fetched/
before_script: before_script:
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR - 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 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 stage: build
script: script:
- export CCACHE_BASEDIR="`pwd`" - export CCACHE_BASEDIR="`pwd`"
@ -475,3 +513,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. # 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 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

10
.readthedocs.yaml Normal file
View file

@ -0,0 +1,10 @@
version: 2
sphinx:
configuration: docs/source/conf.py
python:
version: 3.8
install:
- requirements: docs/requirements.txt

View file

@ -0,0 +1,7 @@
1450
1420
1314
1216
1172
1160
1051

View file

@ -31,6 +31,7 @@ Programmers
Allofich Allofich
Andreas Stöckel Andreas Stöckel
Andrei Kortunov (akortunov) Andrei Kortunov (akortunov)
Andrew Appuhamy (andrew-app)
AnyOldName3 AnyOldName3
Ardekantur Ardekantur
Armin Preiml Armin Preiml
@ -92,6 +93,7 @@ Programmers
Haoda Wang (h313) Haoda Wang (h313)
hristoast hristoast
Internecine Internecine
Ivan Beloborodov (myrix)
Jackerty Jackerty
Jacob Essex (Yacoby) Jacob Essex (Yacoby)
Jacob Turnbull (Tankinfrank) Jacob Turnbull (Tankinfrank)
@ -177,6 +179,7 @@ Programmers
PlutonicOverkill PlutonicOverkill
Radu-Marius Popovici (rpopovici) Radu-Marius Popovici (rpopovici)
Rafael Moura (dhustkoder) Rafael Moura (dhustkoder)
Randy Davin (Kindi)
rdimesio rdimesio
rexelion rexelion
riothamus riothamus
@ -214,6 +217,7 @@ Programmers
tlmullis tlmullis
tri4ng1e tri4ng1e
Thoronador Thoronador
Tom Lowe (Vulpen)
Tom Mason (wheybags) Tom Mason (wheybags)
Torben Leif Carrington (TorbenC) Torben Leif Carrington (TorbenC)
unelsson unelsson
@ -229,7 +233,7 @@ Programmers
Yuri Krupenin Yuri Krupenin
zelurker zelurker
Noah Gooder Noah Gooder
Andrew Appuhamy (andrew-app)
Documentation Documentation
------------- -------------

View file

@ -2,19 +2,29 @@
------ ------
Bug #1751: Birthsign abilities increase modified attribute values instead of base ones 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 #3246: ESSImporter: Most NPCs are dead on save load 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 #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 #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 #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 #3905: Great House Dagoth issues
Bug #4203: Resurrecting an actor should close the loot GUI 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 #4602: Robert's Bodies: crash inside createInstance()
Bug #4700: Editor: Incorrect command implementation Bug #4700: Editor: Incorrect command implementation
Bug #4744: Invisible particles must still be processed Bug #4744: Invisible particles must still be processed
Bug #5088: Sky abruptly changes direction during certain weather transitions
Bug #5100: Persuasion doesn't always clamp the resulting disposition Bug #5100: Persuasion doesn't always clamp the resulting disposition
Bug #5120: Scripted object spawning updates physics system Bug #5120: Scripted object spawning updates physics system
Bug #5207: Loose summons can be present in scene 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 #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 #5453: Magic effect VFX are offset for creatures
Bug #5483: AutoCalc flag is not used to calculate spells cost Bug #5483: AutoCalc flag is not used to calculate spells cost
Bug #5508: Engine binary links to Qt without using it Bug #5508: Engine binary links to Qt without using it
@ -25,6 +35,8 @@
Bug #5801: A multi-effect spell with the intervention effects and recall always favors Almsivi intervention Bug #5801: A multi-effect spell with the intervention effects and recall always favors Almsivi intervention
Bug #5842: GetDisposition adds temporary disposition change from different actors Bug #5842: GetDisposition adds temporary disposition change from different actors
Bug #5863: GetEffect should return true after the player has teleported Bug #5863: GetEffect should return true after the player has teleported
Bug #5913: Failed assertion during Ritual of Trees quest
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 #6037: Morrowind Content Language Cannot be Set to English in OpenMW Launcher
Bug #6051: NaN water height in ESM file is not handled gracefully Bug #6051: NaN water height in ESM file is not handled gracefully
Bug #6066: addtopic "return" does not work from within script. No errors thrown Bug #6066: addtopic "return" does not work from within script. No errors thrown
@ -40,19 +52,45 @@
Bug #6133: Cannot reliably sneak or steal in the sight of the NPCs siding with player Bug #6133: Cannot reliably sneak or steal in the sight of the NPCs siding with player
Bug #6143: Capturing a screenshot makes engine to be a temporary unresponsive Bug #6143: Capturing a screenshot makes engine to be a temporary unresponsive
Bug #6165: Paralyzed player character can pickup items when the inventory is open Bug #6165: Paralyzed player character can pickup items when the inventory is open
Bug #6168: Weather particles flicker for a frame at start of storms
Bug #6172: Some creatures can't open doors Bug #6172: Some creatures can't open doors
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla 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 #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
Bug #6197: Infinite Casting Loop Bug #6197: Infinite Casting Loop
Bug #6253: Multiple instances of Reflect stack additively
Bug #6255: Reflect is different from vanilla
Bug #6258: Barter menu glitches out when modifying prices
Bug #6273: Respawning NPCs rotation is inconsistent Bug #6273: Respawning NPCs rotation is inconsistent
Bug #6282: Laura craft doesn't follow the player character Bug #6282: Laura craft doesn't follow the player character
Bug #6283: Avis Dorsey follows you after her death Bug #6283: Avis Dorsey follows you after her death
Bug #6285: Brush template drawing and terrain selection drawing performance is very bad
Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters 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 #6291: Can't pickup the dead mage's journal from the mysterious hunter mod
Bug #6302: Teleporting disabled actor breaks its disabled state Bug #6302: Teleporting disabled actor breaks its disabled state
Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken 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 #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 #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
Feature #890: OpenMW-CS: Column filtering 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 #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 #2780: A way to see current OpenMW version in the console
Feature #3616: Allow Zoom levels on the World Map Feature #3616: Allow Zoom levels on the World Map
@ -66,15 +104,20 @@
Feature #5996: Support Lua scripts in OpenMW Feature #5996: Support Lua scripts in OpenMW
Feature #6017: Separate persistent and temporary cell references when saving Feature #6017: Separate persistent and temporary cell references when saving
Feature #6032: Reverse-z depth buffer 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 #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
Feature #6199: Support FBO Rendering Feature #6199: Support FBO Rendering
Feature #6248: Embedded error marker mesh
Feature #6249: Alpha testing support for Collada Feature #6249: Alpha testing support for Collada
Feature #6251: OpenMW-CS: Set instance movement based on camera zoom Feature #6251: OpenMW-CS: Set instance movement based on camera zoom
Feature #6288: Preserve the "blocked" record flag for referenceable objects. 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
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
Task #6264: Remove the old classes in animation.cpp Task #6264: Remove the old classes in animation.cpp
0.47.0 0.47.0
------ ------
@ -208,6 +251,8 @@
Bug #6043: Actor can have torch missing when torch animation is played Bug #6043: Actor can have torch missing when torch animation is played
Bug #6047: Mouse bindings can be triggered during save loading 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 #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 Bug #6294: Game crashes with empty pathgrid
Feature #390: 3rd person look "over the shoulder" Feature #390: 3rd person look "over the shoulder"
Feature #832: OpenMW-CS: Handle deleted references Feature #832: OpenMW-CS: Handle deleted references

View file

@ -1,4 +1,4 @@
#!/bin/sh -ex #!/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 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 /usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null unzip -o ~/openmw-android-deps -d /android-ndk-r22/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null

View file

@ -7,9 +7,10 @@ mkdir -p build
cd build cd build
cmake \ 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_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-21 \ -DANDROID_PLATFORM=android-21 \
-DANDROID_LD=deprecated \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX=install \ -DCMAKE_INSTALL_PREFIX=install \
@ -22,6 +23,5 @@ cmake \
-DBUILD_OPENCS=0 \ -DBUILD_OPENCS=0 \
-DBUILD_WIZARD=0 \ -DBUILD_WIZARD=0 \
-DOPENMW_USE_SYSTEM_MYGUI=OFF \ -DOPENMW_USE_SYSTEM_MYGUI=OFF \
-DOPENMW_USE_SYSTEM_OSG=OFF \ -DOPENMW_USE_SYSTEM_SQLITE3=OFF \
-DOPENMW_USE_SYSTEM_BULLET=OFF \
.. ..

View file

@ -40,6 +40,7 @@ if [[ $CI_OPENMW_USE_STATIC_DEPS ]]; then
-DOPENMW_USE_SYSTEM_MYGUI=OFF -DOPENMW_USE_SYSTEM_MYGUI=OFF
-DOPENMW_USE_SYSTEM_OSG=OFF -DOPENMW_USE_SYSTEM_OSG=OFF
-DOPENMW_USE_SYSTEM_BULLET=OFF -DOPENMW_USE_SYSTEM_BULLET=OFF
-DOPENMW_USE_SYSTEM_SQLITE3=OFF
) )
fi fi

View file

@ -1018,6 +1018,7 @@ echo
echo "Setting up OpenMW build..." echo "Setting up OpenMW build..."
add_cmake_opts -DOPENMW_MP_BUILD=on add_cmake_opts -DOPENMW_MP_BUILD=on
add_cmake_opts -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" add_cmake_opts -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}"
add_cmake_opts -DOPENMW_USE_SYSTEM_SQLITE3=OFF
if [ ! -z $CI ]; then if [ ! -z $CI ]; then
case $STEP in case $STEP in
components ) components )

View file

@ -19,6 +19,7 @@ cmake \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.14" \ -D CMAKE_OSX_DEPLOYMENT_TARGET="10.14" \
-D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_BUILD_TYPE=RELEASE \
-D OPENMW_OSX_DEPLOYMENT=TRUE \ -D OPENMW_OSX_DEPLOYMENT=TRUE \
-D OPENMW_USE_SYSTEM_SQLITE3=OFF \
-D BUILD_OPENMW=TRUE \ -D BUILD_OPENMW=TRUE \
-D BUILD_OPENCS=TRUE \ -D BUILD_OPENCS=TRUE \
-D BUILD_ESMTOOL=TRUE \ -D BUILD_ESMTOOL=TRUE \

View file

@ -27,7 +27,7 @@ declare -rA GROUPED_DEPS=(
# TODO: add librecastnavigation-dev when debian is ready # TODO: add librecastnavigation-dev when debian is ready
# These dependencies can alternatively be built and linked statically. # These dependencies can alternatively be built and linked statically.
[openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev" [openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev libsqlite3-dev"
[coverity]="curl" [coverity]="curl"
[clang-tidy]="clang-tidy" [clang-tidy]="clang-tidy"

View file

@ -149,6 +149,8 @@ else()
endif() endif()
option(RECASTNAVIGATION_STATIC "Build recastnavigation static libraries" ${_recastnavigation_static_default}) option(RECASTNAVIGATION_STATIC "Build recastnavigation static libraries" ${_recastnavigation_static_default})
option(OPENMW_USE_SYSTEM_SQLITE3 "Use system provided SQLite3 library" ON)
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE) option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF) option(OPENMW_LTO_BUILD "Build OpenMW with Link-Time Optimization (Needs ~2GB of RAM)" OFF)
@ -197,6 +199,10 @@ if (WIN32)
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON) option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
endif() endif()
if(MSVC)
add_compile_options("/utf-8")
endif()
# Dependencies # Dependencies
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
@ -408,7 +414,8 @@ else(USE_LUAJIT)
endif(USE_LUAJIT) endif(USE_LUAJIT)
# C++ library binding to Lua # 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( include_directories(
BEFORE SYSTEM BEFORE SYSTEM
@ -420,7 +427,8 @@ include_directories(
${OPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR}
${BULLET_INCLUDE_DIRS} ${BULLET_INCLUDE_DIRS}
${LUA_INCLUDE_DIR} ${LUA_INCLUDE_DIR}
${SOL_INCLUDE_DIRS} ${SOL_INCLUDE_DIR}
${SOL_CONFIG_DIR}
) )
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS}) link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
@ -437,9 +445,8 @@ if (APPLE)
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
endif (APPLE) endif (APPLE)
if (NOT APPLE) if (NOT APPLE) # this is modified for macOS use later in "apps/open[mw|cs]/CMakeLists.txt"
set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR}) set(OPENMW_RESOURCES_ROOT ${OpenMW_BINARY_DIR})
set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})
endif () endif ()
add_subdirectory(files/) add_subdirectory(files/)
@ -703,8 +710,12 @@ if (WIN32)
endif() endif()
if (BUILD_OPENMW AND APPLE) if (BUILD_OPENMW AND APPLE)
if (USE_LUAJIT)
# Without these flags LuaJit crashes on startup on OSX # Without these flags LuaJit crashes on startup on OSX
set_target_properties(openmw PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000") 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() endif()
# Apple bundling # Apple bundling

View file

@ -1,8 +1,6 @@
OpenMW 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 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. OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set.

View file

@ -25,18 +25,10 @@ namespace
}; };
template <typename Random> template <typename Random>
TilePosition generateTilePosition(int max, Random& random) osg::Vec2i generateVec2i(int max, Random& random)
{ {
std::uniform_int_distribution<int> distribution(0, max); std::uniform_int_distribution<int> distribution(0, max);
return TilePosition(distribution(random), distribution(random)); return osg::Vec2i(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)};
} }
template <typename Random> template <typename Random>
@ -91,8 +83,7 @@ namespace
{ {
std::uniform_real_distribution<float> distribution(0.0, 1.0); std::uniform_real_distribution<float> distribution(0.0, 1.0);
std::generate_n(out, count, [&] { std::generate_n(out, count, [&] {
const osg::Vec3f shift(distribution(random), distribution(random), distribution(random)); return CellWater {generateVec2i(1000, random), Water {ESM::Land::REAL_SIZE, distribution(random)}};
return Cell {1, shift};
}); });
} }
@ -117,16 +108,18 @@ namespace
{ {
std::uniform_real_distribution<float> distribution(0.0, 1.0); std::uniform_real_distribution<float> distribution(0.0, 1.0);
Heightfield result; Heightfield result;
result.mBounds = generateTileBounds(random); result.mCellPosition = generateVec2i(1000, random);
result.mCellSize = ESM::Land::REAL_SIZE;
result.mMinHeight = distribution(random); result.mMinHeight = distribution(random);
result.mMaxHeight = result.mMinHeight + 1.0; 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); result.mLength = static_cast<std::uint8_t>(ESM::Land::LAND_SIZE);
std::generate_n(std::back_inserter(result.mHeights), ESM::Land::LAND_NUM_VERTS, [&] std::generate_n(std::back_inserter(result.mHeights), ESM::Land::LAND_NUM_VERTS, [&]
{ {
return distribution(random); return distribution(random);
}); });
result.mOriginalSize = ESM::Land::LAND_SIZE;
result.mMinX = 0;
result.mMinY = 0;
return result; return result;
} }
@ -135,7 +128,8 @@ namespace
{ {
std::uniform_real_distribution<float> distribution(0.0, 1.0); std::uniform_real_distribution<float> distribution(0.0, 1.0);
FlatHeightfield result; FlatHeightfield result;
result.mBounds = generateTileBounds(random); result.mCellPosition = generateVec2i(1000, random);
result.mCellSize = ESM::Land::REAL_SIZE;
result.mHeight = distribution(random); result.mHeight = distribution(random);
return result; return result;
} }
@ -144,11 +138,11 @@ namespace
Key generateKey(std::size_t triangles, Random& random) Key generateKey(std::size_t triangles, Random& random)
{ {
const osg::Vec3f agentHalfExtents = generateAgentHalfExtents(0.5, 1.5, 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 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); const std::size_t revision = std::uniform_int_distribution<std::size_t>(0, 10000)(random);
Mesh mesh = generateMesh(triangles, random); Mesh mesh = generateMesh(triangles, random);
std::vector<Cell> water; std::vector<CellWater> water;
generateWater(std::back_inserter(water), 1, random); generateWater(std::back_inserter(water), 1, random);
RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water), RecastMesh recastMesh(generation, revision, std::move(mesh), std::move(water),
{generateHeightfield(random)}, {generateFlatHeightfield(random)}); {generateHeightfield(random)}, {generateFlatHeightfield(random)});

View file

@ -233,7 +233,17 @@ int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
std::string extractPath = info.extractfile; std::string extractPath = info.extractfile;
Misc::StringUtils::replaceAll(extractPath, "\\", "/"); Misc::StringUtils::replaceAll(extractPath, "\\", "/");
if (!bsa->exists(archivePath.c_str())) Files::IStreamPtr stream;
// Get a stream for the file to extract
for (auto it = bsa->getList().rbegin(); it != bsa->getList().rend(); ++it)
{
if (Misc::StringUtils::ciEqual(std::string(it->name()), archivePath))
{
stream = bsa->getFile(&*it);
break;
}
}
if (!stream)
{ {
std::cout << "ERROR: file '" << archivePath << "' not found\n"; std::cout << "ERROR: file '" << archivePath << "' not found\n";
std::cout << "In archive: " << info.filename << std::endl; std::cout << "In archive: " << info.filename << std::endl;
@ -260,9 +270,6 @@ int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
return 3; return 3;
} }
// Get a stream for the file to extract
Files::IStreamPtr stream = bsa->getFile(archivePath.c_str());
bfs::ofstream out(target, std::ios::binary); bfs::ofstream out(target, std::ios::binary);
// Write the file to disk // Write the file to disk
@ -296,8 +303,7 @@ int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
} }
// Get a stream for the file to extract // Get a stream for the file to extract
// (inefficient because getFile iter on the list again) Files::IStreamPtr data = bsa->getFile(&file);
Files::IStreamPtr data = bsa->getFile(file.name());
bfs::ofstream out(target, std::ios::binary); bfs::ofstream out(target, std::ios::binary);
// Write the file to disk // Write the file to disk

View file

@ -367,10 +367,10 @@ int load(Arguments& info)
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n); EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == nullptr) if (record == nullptr)
{ {
if (skipped.count(n.intval) == 0) if (skipped.count(n.toInt()) == 0)
{ {
std::cout << "Skipping " << n.toString() << " records.\n"; std::cout << "Skipping " << n.toString() << " records.\n";
skipped.emplace(n.intval); skipped.emplace(n.toInt());
} }
esm.skipRecord(); esm.skipRecord();
@ -402,7 +402,7 @@ int load(Arguments& info)
record->print(); record->print();
} }
if (record->getType().intval == ESM::REC_CELL && loadCells && interested) if (record->getType().toInt() == ESM::REC_CELL && loadCells && interested)
{ {
loadCell(record->cast<ESM::Cell>()->get(), esm, info); loadCell(record->cast<ESM::Cell>()->get(), esm, info);
} }
@ -415,7 +415,7 @@ int load(Arguments& info)
{ {
delete record; delete record;
} }
++info.data.mRecordStats[n.intval]; ++info.data.mRecordStats[n.toInt()];
} }
} catch(std::exception &e) { } catch(std::exception &e) {
@ -459,7 +459,7 @@ int clone(Arguments& info)
for (std::pair<int, int> stat : info.data.mRecordStats) for (std::pair<int, int> stat : info.data.mRecordStats)
{ {
ESM::NAME name; ESM::NAME name;
name.intval = stat.first; name = stat.first;
int amount = stat.second; int amount = stat.second;
std::cout << std::setw(digitCount) << amount << " " << name.toString() << " "; std::cout << std::setw(digitCount) << amount << " " << name.toString() << " ";
if (++i % 3 == 0) if (++i % 3 == 0)
@ -496,7 +496,7 @@ int clone(Arguments& info)
esm.startRecord(typeName.toString(), record->getFlags()); esm.startRecord(typeName.toString(), record->getFlags());
record->save(esm); record->save(esm);
if (typeName.intval == ESM::REC_CELL) { if (typeName.toInt() == ESM::REC_CELL) {
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get(); ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
if (!info.data.mCellRefs[ptr].empty()) if (!info.data.mCellRefs[ptr].empty())
{ {

View file

@ -30,7 +30,7 @@ void printAIPackage(const ESM::AIPackage& p)
{ {
std::cout << " Travel Coordinates: (" << p.mTravel.mX << "," std::cout << " Travel Coordinates: (" << p.mTravel.mX << ","
<< p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl; << 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) 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; << p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl;
std::cout << " Duration: " << p.mTarget.mDuration << std::endl; std::cout << " Duration: " << p.mTarget.mDuration << std::endl;
std::cout << " Target ID: " << p.mTarget.mId.toString() << 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) else if (p.mType == ESM::AI_Activate)
{ {
std::cout << " Name: " << p.mActivate.mName.toString() << std::endl; 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 { else {
std::cout << " BadPackage: " << Misc::StringUtils::format("0x%08X", p.mType) << std::endl; std::cout << " BadPackage: " << Misc::StringUtils::format("0x%08X", p.mType) << std::endl;
@ -176,7 +176,7 @@ RecordBase::create(const ESM::NAME type)
{ {
RecordBase *record = nullptr; RecordBase *record = nullptr;
switch (type.intval) { switch (type.toInt()) {
case ESM::REC_ACTI: case ESM::REC_ACTI:
{ {
record = new EsmTool::Record<ESM::Activator>; record = new EsmTool::Record<ESM::Activator>;

View file

@ -324,14 +324,14 @@ namespace ESSImport
ESM::NAME n = esm.getRecName(); ESM::NAME n = esm.getRecName();
esm.getRecHeader(); esm.getRecHeader();
auto it = converters.find(n.intval); auto it = converters.find(n.toInt());
if (it != converters.end()) if (it != converters.end())
{ {
it->second->read(esm); it->second->read(esm);
} }
else else
{ {
if (unknownRecords.insert(n.intval).second) if (unknownRecords.insert(n.toInt()).second)
{ {
std::ios::fmtflags f(std::cerr.flags()); std::ios::fmtflags f(std::cerr.flags());
std::cerr << "Error: unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl; std::cerr << "Error: unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;

View file

@ -117,6 +117,11 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders"); loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
loadSettingBool(radialFogCheckBox, "radial fog", "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"); loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool))); connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool)));
loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
@ -207,7 +212,7 @@ bool Launcher::AdvancedPage::loadSettings()
{ {
// Saves // Saves
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
maximumQuicksavesComboBox->setValue(Settings::Manager::getInt("max quicksaves", "Saves")); loadSettingInt(maximumQuicksavesComboBox,"max quicksaves", "Saves");
// Other Settings // Other Settings
QString screenshotFormatString = QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper(); QString screenshotFormatString = QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper();
@ -252,14 +257,10 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game");
saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game");
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); saveSettingInt(unarmedFactorsStrengthComboBox, "strength influences hand to hand", "Game");
if (unarmedFactorsStrengthIndex != Settings::Manager::getInt("strength influences hand to hand", "Game"))
Settings::Manager::setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game"); saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game");
saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator"); saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator");
int numPhysicsThreads = physicsThreadsSpinBox->value(); saveSettingInt(physicsThreadsSpinBox, "async num threads", "Physics");
if (numPhysicsThreads != Settings::Manager::getInt("async num threads", "Physics"))
Settings::Manager::setInt("async num threads", "Physics", numPhysicsThreads);
} }
// Visuals // Visuals
@ -270,6 +271,8 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders"); saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
saveSettingBool(radialFogCheckBox, "radial fog", "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(magicItemAnimationsCheckBox, "use magic item animations", "Game");
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
@ -349,9 +352,7 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI");
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex(); saveSettingInt(showOwnedComboBox,"show owned", "Game");
if (showOwnedCurrentIndex != Settings::Manager::getInt("show owned", "Game"))
Settings::Manager::setInt("show owned", "Game", showOwnedCurrentIndex);
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map"); saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map");
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
@ -370,11 +371,7 @@ void Launcher::AdvancedPage::saveSettings()
{ {
// Saves Settings // Saves Settings
saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
int maximumQuicksaves = maximumQuicksavesComboBox->value(); saveSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves");
if (maximumQuicksaves != Settings::Manager::getInt("max quicksaves", "Saves"))
{
Settings::Manager::setInt("max quicksaves", "Saves", maximumQuicksaves);
}
// Other Settings // Other Settings
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
@ -416,6 +413,32 @@ void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::str
Settings::Manager::setBool(setting, group, cValue); 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) void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
{ {
loadCellsForAutocomplete(cellNames); loadCellsForAutocomplete(cellNames);

View file

@ -41,8 +41,12 @@ namespace Launcher
* @param filePaths the file paths of the content files to be examined * @param filePaths the file paths of the content files to be examined
*/ */
void loadCellsForAutocomplete(QStringList filePaths); void loadCellsForAutocomplete(QStringList filePaths);
void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group); static 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 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 #endif

View file

@ -32,7 +32,7 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
{ {
ui.setupUi (this); ui.setupUi (this);
setObjectName ("DataFilesPage"); setObjectName ("DataFilesPage");
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget, /*showOMWScripts=*/true);
const QString encoding = mGameSettings.value("encoding", "win1252"); const QString encoding = mGameSettings.value("encoding", "win1252");
mSelector->setEncoding(encoding); mSelector->setEncoding(encoding);
@ -121,6 +121,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
for (const QString &path : paths) for (const QString &path : paths)
mSelector->addFiles(path); mSelector->addFiles(path);
mSelector->sortFiles();
PathIterator pathIterator(paths); PathIterator pathIterator(paths);

View file

@ -47,7 +47,6 @@ Launcher::GraphicsPage::GraphicsPage(QWidget *parent)
connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int))); connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int)));
connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool))); connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool)));
connect(shadowDistanceCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotShadowDistLimitToggled(bool))); connect(shadowDistanceCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotShadowDistLimitToggled(bool)));
} }
bool Launcher::GraphicsPage::setupSDL() bool Launcher::GraphicsPage::setupSDL()

View file

@ -146,7 +146,6 @@ void Launcher::MainDialog::createPages()
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int))); 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 // 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); connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);
} }
Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog() Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()

View file

@ -10,6 +10,8 @@ QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
// Loop through all content files // Loop through all content files
for (auto &contentPath : contentPaths) { for (auto &contentPath : contentPaths) {
if (contentPath.endsWith(".omwscripts", Qt::CaseInsensitive))
continue;
esmReader.open(contentPath.toStdString()); esmReader.open(contentPath.toStdString());
// Loop through all records // Loop through all records
@ -35,7 +37,7 @@ QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
bool CellNameLoader::isCellRecord(ESM::NAME &recordName) bool CellNameLoader::isCellRecord(ESM::NAME &recordName)
{ {
return recordName.intval == ESM::REC_CELL; return recordName.toInt() == ESM::REC_CELL;
} }
QString CellNameLoader::getCellName(ESM::ESMReader &esmReader) QString CellNameLoader::getCellName(ESM::ESMReader &esmReader)

View file

@ -2,8 +2,8 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <cstdlib>
#include <components/misc/stringops.hpp>
#include <components/nif/niffile.hpp> #include <components/nif/niffile.hpp>
#include <components/files/constrainedfilestream.hpp> #include <components/files/constrainedfilestream.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -21,15 +21,7 @@ namespace bfs = boost::filesystem;
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); std::string extension = filename.substr(filename.find_last_of('.')+1);
return Misc::StringUtils::ciEqual(extension, extensionToFind);
//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;
} }
///See if the file has the "nif" extension. ///See if the file has the "nif" extension.

View file

@ -183,8 +183,7 @@ if(APPLE)
set(OPENCS_BUNDLE_NAME "OpenMW-CS") set(OPENCS_BUNDLE_NAME "OpenMW-CS")
set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources") 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_RESOURCES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files) add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)

View file

@ -149,11 +149,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
//iterate the data directories and add them to the file dialog for loading //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) mFileDialog.addFiles(dataDirs);
{
QString path = QString::fromUtf8 (iter->string().c_str());
mFileDialog.addFiles(path);
}
return std::make_pair (dataDirs, variables["fallback-archive"].as<std::vector<std::string>>()); return std::make_pair (dataDirs, variables["fallback-archive"].as<std::vector<std::string>>());
} }

View file

@ -17,6 +17,19 @@
#include "textnode.hpp" #include "textnode.hpp"
#include "valuenode.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 namespace CSMFilter
{ {
struct Token struct Token
@ -103,7 +116,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken()
{ {
char c = mInput[mIndex]; 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.empty() && string[0]=='"'))
string += c; string += c;
else else
@ -150,7 +163,7 @@ CSMFilter::Token CSMFilter::Parser::getNumberToken()
{ {
char c = mInput[mIndex]; char c = mInput[mIndex];
if (std::isdigit (c)) if (isDigit(c))
{ {
string += c; string += c;
hasDigit = true; hasDigit = true;
@ -225,10 +238,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken()
case '!': ++mIndex; return Token (Token::Type_OneShot); case '!': ++mIndex; return Token (Token::Type_OneShot);
} }
if (c=='"' || c=='_' || std::isalpha (c) || c==':') if (c=='"' || c=='_' || isAlpha(c) || c==':')
return getStringToken(); return getStringToken();
if (c=='-' || c=='.' || std::isdigit (c)) if (c=='-' || c=='.' || isDigit(c))
return getNumberToken(); return getNumberToken();
error(); error();

View file

@ -395,12 +395,12 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW
if (index == -1) if (index == -1)
{ {
messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error); messages.add(id, std::string(T::getRecordType()) + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }
else if (collection.getRecord(index).isDeleted()) else if (collection.getRecord(index).isDeleted())
{ {
messages.add(id, "Deleted " + T::getRecordType() + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error); messages.add(id, "Deleted " + std::string(T::getRecordType()) + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error);
return false; return false;
} }

View file

@ -296,7 +296,7 @@ namespace CSMWorld
const std::string& destination, const UniversalId::Type type) const std::string& destination, const UniversalId::Type type)
{ {
int index = cloneRecordImp(origin, destination, type); int index = cloneRecordImp(origin, destination, type);
mRecords.at(index)->get().mPlugin = 0; mRecords.at(index)->get().setPlugin(0);
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
@ -311,7 +311,7 @@ namespace CSMWorld
int index = touchRecordImp(id); int index = touchRecordImp(id);
if (index >= 0) if (index >= 0)
{ {
mRecords.at(index)->get().mPlugin = 0; mRecords.at(index)->get().setPlugin(0);
return true; return true;
} }

View file

@ -52,7 +52,7 @@ namespace CSMWorld
QVariant LandPluginIndexColumn::get(const Record<Land>& record) const QVariant LandPluginIndexColumn::get(const Record<Land>& record) const
{ {
return record.get().mPlugin; return record.get().getPlugin();
} }
bool LandPluginIndexColumn::isEditable() const bool LandPluginIndexColumn::isEditable() const

View file

@ -255,7 +255,7 @@ namespace CSMWorld
{ ColumnId_AiWanderDist, "Wander Dist" }, { ColumnId_AiWanderDist, "Wander Dist" },
{ ColumnId_AiDuration, "Ai Duration" }, { ColumnId_AiDuration, "Ai Duration" },
{ ColumnId_AiWanderToD, "Wander ToD" }, { ColumnId_AiWanderToD, "Wander ToD" },
{ ColumnId_AiWanderRepeat, "Wander Repeat" }, { ColumnId_AiWanderRepeat, "Ai Repeat" },
{ ColumnId_AiActivateName, "Activate" }, { ColumnId_AiActivateName, "Activate" },
{ ColumnId_AiTargetId, "Target ID" }, { ColumnId_AiTargetId, "Target ID" },
{ ColumnId_AiTargetCell, "Target Cell" }, { ColumnId_AiTargetCell, "Target Cell" },

View file

@ -1086,7 +1086,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
bool unhandledRecord = false; bool unhandledRecord = false;
switch (n.intval) switch (n.toInt())
{ {
case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break; case ESM::REC_GLOB: mGlobals.load (*mReader, mBase); break;
case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break; case ESM::REC_GMST: mGmsts.load (*mReader, mBase); break;

View file

@ -8,6 +8,7 @@
#include <stdexcept> #include <stdexcept>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include <components/misc/stringops.hpp>
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
@ -354,8 +355,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import
for (int i = 0; i < idCollection()->getSize(); ++i) for (int i = 0; i < idCollection()->getSize(); ++i)
{ {
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(i)); auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(i));
std::string texture = record.get().mTexture; std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture);
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
if (record.isModified()) if (record.isModified())
reverseLookupMap.emplace(texture, idCollection()->getId(i)); reverseLookupMap.emplace(texture, idCollection()->getId(i));
} }
@ -376,8 +376,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import
// Look for a pre-existing record // Look for a pre-existing record
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow)); auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow));
std::string texture = record.get().mTexture; std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture);
std::transform(texture.begin(), texture.end(), texture.begin(), tolower);
auto searchIt = reverseLookupMap.find(texture); auto searchIt = reverseLookupMap.find(texture);
if (searchIt != reverseLookupMap.end()) if (searchIt != reverseLookupMap.end())
{ {

View file

@ -1678,7 +1678,7 @@ namespace CSMWorld
newRow.mWander.mTimeOfDay = 0; newRow.mWander.mTimeOfDay = 0;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
newRow.mWander.mIdle[i] = 0; newRow.mWander.mIdle[i] = 0;
newRow.mWander.mShouldRepeat = 0; newRow.mWander.mShouldRepeat = 1;
newRow.mCellName = ""; newRow.mCellName = "";
if (position >= (int)list.size()) if (position >= (int)list.size())
@ -1784,9 +1784,15 @@ namespace CSMWorld
return static_cast<int>(content.mWander.mIdle[subColIndex-4]); return static_cast<int>(content.mWander.mIdle[subColIndex-4]);
else else
return QVariant(); return QVariant();
case 12: // wander repeat case 12: // repeat
if (content.mType == ESM::AI_Wander) if (content.mType == ESM::AI_Wander)
return content.mWander.mShouldRepeat != 0; 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 else
return QVariant(); return QVariant();
case 13: // activate name case 13: // activate name
@ -1895,6 +1901,12 @@ namespace CSMWorld
case 12: case 12:
if (content.mType == ESM::AI_Wander) if (content.mType == ESM::AI_Wander)
content.mWander.mShouldRepeat = static_cast<unsigned char>(value.toInt()); 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 else
return; // return without saving return; // return without saving

View file

@ -102,11 +102,6 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const
return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name)); return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name));
} }
bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const
{
return mData.getJournals().searchId (name)!=-1;
}
void CSMWorld::ScriptContext::invalidateIds() void CSMWorld::ScriptContext::invalidateIds()
{ {
mIdsUpdated = false; mIdsUpdated = false;

View file

@ -39,9 +39,6 @@ namespace CSMWorld
bool isId (const std::string& name) const override; bool isId (const std::string& name) const override;
///< Does \a name match an ID, that can be referenced? ///< Does \a name match an ID, that can be referenced?
bool isJournalId (const std::string& name) const override;
///< Does \a name match a journal ID?
void invalidateIds(); void invalidateIds();
void clear(); void clear();

View file

@ -24,13 +24,18 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) :
resize(400, 400); resize(400, 400);
setObjectName ("FileDialog"); setObjectName ("FileDialog");
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget, /*showOMWScripts=*/false);
mAdjusterWidget = new AdjusterWidget (this); mAdjusterWidget = new AdjusterWidget (this);
} }
void CSVDoc::FileDialog::addFiles(const QString &path) void CSVDoc::FileDialog::addFiles(const std::vector<boost::filesystem::path>& dataDirs)
{ {
for (auto iter = dataDirs.rbegin(); iter != dataDirs.rend(); ++iter)
{
QString path = QString::fromUtf8(iter->string().c_str());
mSelector->addFiles(path); mSelector->addFiles(path);
}
mSelector->sortFiles();
} }
void CSVDoc::FileDialog::setEncoding(const QString &encoding) void CSVDoc::FileDialog::setEncoding(const QString &encoding)

View file

@ -45,7 +45,7 @@ namespace CSVDoc
explicit FileDialog(QWidget *parent = nullptr); explicit FileDialog(QWidget *parent = nullptr);
void showDialog (ContentAction action); void showDialog (ContentAction action);
void addFiles (const QString &path); void addFiles(const std::vector<boost::filesystem::path>& dataDirs);
void setEncoding (const QString &encoding); void setEncoding (const QString &encoding);
void clearFiles (); void clearFiles ();

View file

@ -111,7 +111,7 @@ namespace CSVRender
if (!mesh.empty() && node != mNodeMap.end()) if (!mesh.empty() && node != mNodeMap.end())
{ {
auto instance = sceneMgr->getInstance(mesh); auto instance = sceneMgr->getInstance(mesh);
SceneUtil::attach(instance, mSkeleton, boneName, node->second); SceneUtil::attach(instance, mSkeleton, boneName, node->second, sceneMgr);
} }
} }

View file

@ -308,7 +308,7 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
const float OuterRadius = InnerRadius + MarkerShaftWidth; const float OuterRadius = InnerRadius + MarkerShaftWidth;
const float SegmentDistance = 100.f; 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 VerticesPerSegment = 4;
const size_t IndicesPerSegment = 24; const size_t IndicesPerSegment = 24;

View file

@ -16,11 +16,13 @@
#include "worldspacewidget.hpp" #include "worldspacewidget.hpp"
CSVRender::TerrainSelection::TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type): CSVRender::TerrainSelection::TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type):
mParentNode(parentNode), mWorldspaceWidget (worldspaceWidget), mDraggedOperationFlag(false), mSelectionType(type) mParentNode(parentNode), mWorldspaceWidget (worldspaceWidget), mSelectionType(type)
{ {
mGeometry = new osg::Geometry(); mGeometry = new osg::Geometry();
mSelectionNode = new osg::Group(); mSelectionNode = new osg::Group();
mSelectionNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mSelectionNode->getOrCreateStateSet()->setRenderBinDetails(11, "RenderBin");
mSelectionNode->addChild(mGeometry); mSelectionNode->addChild(mGeometry);
activate(); activate();
@ -43,19 +45,24 @@ void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, in
update(); update();
} }
void CSVRender::TerrainSelection::addSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress) void CSVRender::TerrainSelection::addSelect(const std::vector<std::pair<int, int>>& localPositions)
{ {
handleSelection(localPositions, toggleInProgress, SelectionMethod::AddSelect); handleSelection(localPositions, SelectionMethod::AddSelect);
} }
void CSVRender::TerrainSelection::removeSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress) void CSVRender::TerrainSelection::removeSelect(const std::vector<std::pair<int, int>>& localPositions)
{ {
handleSelection(localPositions, toggleInProgress, SelectionMethod::RemoveSelect); handleSelection(localPositions, SelectionMethod::RemoveSelect);
} }
void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress) void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>>& localPositions)
{ {
handleSelection(localPositions, toggleInProgress, SelectionMethod::ToggleSelect); handleSelection(localPositions, SelectionMethod::ToggleSelect);
}
void CSVRender::TerrainSelection::clearTemporarySelection()
{
mTemporarySelection.clear();
} }
void CSVRender::TerrainSelection::activate() void CSVRender::TerrainSelection::activate()
@ -102,26 +109,16 @@ void CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec
float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x)); float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x));
float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y)); float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y));
osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y) + 2); osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y));
vertices->push_back(pointXY); vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1), calculateLandHeight(x, y - 1) + 2)); vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1), calculateLandHeight(x, y - 1)));
vertices->push_back(pointXY); vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y) + 2)); vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y)));
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
vertices->push_back(pointXY); vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1) + 2)); vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1)));
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
vertices->push_back(pointXY); vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x + 1), yWorldCoord, calculateLandHeight(x + 1, y) + 2)); vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x + 1), yWorldCoord, calculateLandHeight(x + 1, y)));
}
} }
} }
} }
@ -154,8 +151,8 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::V
{ {
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+(i-1), y2)+2)); vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+(i-1), y2)));
vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+i, y2)+2)); vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+i, y2)));
} }
} }
@ -166,8 +163,8 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::V
{ {
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+(i-1), y1)+2)); vertices->push_back(osg::Vec3f(drawPreviousX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+(i-1), y1)));
vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+i, y1)+2)); vertices->push_back(osg::Vec3f(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+i, y1)));
} }
} }
@ -178,8 +175,8 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::V
{ {
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawPreviousY, calculateLandHeight(x2, y1+(i-1))+2)); vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawPreviousY, calculateLandHeight(x2, y1+(i-1))));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawCurrentY, calculateLandHeight(x2, y1+i)+2)); vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x + 1), drawCurrentY, calculateLandHeight(x2, y1+i)));
} }
} }
@ -190,84 +187,44 @@ void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::V
{ {
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1)); float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawPreviousY, calculateLandHeight(x1, y1+(i-1))+2)); vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawPreviousY, calculateLandHeight(x1, y1+(i-1))));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawCurrentY, calculateLandHeight(x1, y1+i)+2)); vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalXToWorldCoords(x), drawCurrentY, calculateLandHeight(x1, y1+i)));
} }
} }
} }
} }
} }
void CSVRender::TerrainSelection::handleSelection(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod) void CSVRender::TerrainSelection::handleSelection(const std::vector<std::pair<int, int>>& localPositions, SelectionMethod selectionMethod)
{ {
if (toggleInProgress)
{
for (auto const& localPos : localPositions)
{
auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
mDraggedOperationFlag = true;
if (iterTemp == mTemporarySelection.end())
{
auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
switch (selectionMethod)
{
case SelectionMethod::AddSelect:
if (iter == mSelection.end())
{
mSelection.emplace_back(localPos);
}
break;
case SelectionMethod::RemoveSelect:
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
break;
case SelectionMethod::ToggleSelect:
if (iter == mSelection.end())
{
mSelection.emplace_back(localPos);
}
else
{
mSelection.erase(iter);
}
break;
default: break;
}
}
mTemporarySelection.push_back(localPos);
}
}
else if (mDraggedOperationFlag == false)
{
for (auto const& localPos : localPositions) for (auto const& localPos : localPositions)
{ {
const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos); const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
switch (selectionMethod) switch (selectionMethod)
{ {
case SelectionMethod::OnlySelect:
break;
case SelectionMethod::AddSelect: case SelectionMethod::AddSelect:
if (iter == mSelection.end()) if (iter == mSelection.end())
{ {
mSelection.emplace_back(localPos); mSelection.emplace_back(localPos);
} }
break; break;
case SelectionMethod::RemoveSelect: case SelectionMethod::RemoveSelect:
if (iter != mSelection.end()) if (iter != mSelection.end())
{ {
mSelection.erase(iter); mSelection.erase(iter);
} }
break; break;
case SelectionMethod::ToggleSelect: case SelectionMethod::ToggleSelect:
{
const auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
if (iterTemp == mTemporarySelection.end())
{
if (iter == mSelection.end()) if (iter == mSelection.end())
{ {
mSelection.emplace_back(localPos); mSelection.emplace_back(localPos);
@ -276,17 +233,14 @@ void CSVRender::TerrainSelection::handleSelection(const std::vector<std::pair<in
{ {
mSelection.erase(iter); mSelection.erase(iter);
} }
}
mTemporarySelection.emplace_back(localPos);
break; break;
default: break;
} }
}
}
else
{
mDraggedOperationFlag = false;
mTemporarySelection.clear(); default:
break;
}
} }
update(); update();
@ -336,11 +290,9 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver
else if (isLandLoaded(CSMWorld::CellCoordinates::generateId(cellX, cellY))) else if (isLandLoaded(CSMWorld::CellCoordinates::generateId(cellX, cellY)))
{ {
CSMDoc::Document& document = mWorldspaceWidget->getDocument(); CSMDoc::Document& document = mWorldspaceWidget->getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY); std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex); const ESM::Land::LandData* landData = document.getData().getLand().getRecord(cellId).get().getLandData(ESM::Land::DATA_VHGT);
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>(); return landData->mHeights[localY*ESM::Land::LAND_SIZE + localX];
return mPointer[localY*ESM::Land::LAND_SIZE + localX];
} }
return landHeight; return landHeight;

View file

@ -44,9 +44,10 @@ namespace CSVRender
~TerrainSelection(); ~TerrainSelection();
void onlySelect(const std::vector<std::pair<int, int>> &localPositions); void onlySelect(const std::vector<std::pair<int, int>> &localPositions);
void addSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress); void addSelect(const std::vector<std::pair<int, int>>& localPositions);
void removeSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress); void removeSelect(const std::vector<std::pair<int, int>>& localPositions);
void toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress); void toggleSelect(const std::vector<std::pair<int, int>> &localPositions);
void clearTemporarySelection();
void activate(); void activate();
void deactivate(); void deactivate();
@ -64,7 +65,7 @@ namespace CSVRender
private: private:
void handleSelection(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress, SelectionMethod selectionMethod); void handleSelection(const std::vector<std::pair<int, int>>& localPositions, SelectionMethod selectionMethod);
bool noCell(const std::string& cellId); bool noCell(const std::string& cellId);
@ -81,7 +82,6 @@ namespace CSVRender
osg::ref_ptr<osg::Group> mSelectionNode; osg::ref_ptr<osg::Group> mSelectionNode;
std::vector<std::pair<int, int>> mSelection; // Global terrain selection coordinate in either vertex or texture units std::vector<std::pair<int, int>> mSelection; // Global terrain selection coordinate in either vertex or texture units
std::vector<std::pair<int, int>> mTemporarySelection; // Used during toggle to compare the most recent drag operation std::vector<std::pair<int, int>> mTemporarySelection; // Used during toggle to compare the most recent drag operation
bool mDraggedOperationFlag; //true during drag operation, false when click-operation
TerrainSelectionType mSelectionType; TerrainSelectionType mSelectionType;
}; };
} }

View file

@ -106,16 +106,6 @@ void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult&
editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true); editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true);
applyTerrainEditChanges(); applyTerrainEditChanges();
} }
if (mDragMode == InteractionType_PrimarySelect)
{
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true);
}
if (mDragMode == InteractionType_SecondarySelect)
{
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true);
}
} }
clearTransientEdits(); clearTransientEdits();
} }
@ -124,7 +114,8 @@ void CSVRender::TerrainShapeMode::primarySelectPressed(const WorldspaceHitResult
{ {
if(hit.hit && hit.tag == nullptr) if(hit.hit && hit.tag == nullptr)
{ {
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, false); selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0);
mTerrainShapeSelection->clearTemporarySelection();
} }
} }
@ -132,7 +123,8 @@ void CSVRender::TerrainShapeMode::secondarySelectPressed(const WorldspaceHitResu
{ {
if(hit.hit && hit.tag == nullptr) if(hit.hit && hit.tag == nullptr)
{ {
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, false); selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1);
mTerrainShapeSelection->clearTemporarySelection();
} }
} }
@ -167,8 +159,8 @@ bool CSVRender::TerrainShapeMode::primarySelectStartDrag (const QPoint& pos)
mDragMode = InteractionType_None; mDragMode = InteractionType_None;
return false; return false;
} }
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true); selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0);
return false; return true;
} }
bool CSVRender::TerrainShapeMode::secondarySelectStartDrag (const QPoint& pos) bool CSVRender::TerrainShapeMode::secondarySelectStartDrag (const QPoint& pos)
@ -180,8 +172,8 @@ bool CSVRender::TerrainShapeMode::secondarySelectStartDrag (const QPoint& pos)
mDragMode = InteractionType_None; mDragMode = InteractionType_None;
return false; return false;
} }
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true); selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1);
return false; return true;
} }
void CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) void CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
@ -200,13 +192,13 @@ void CSVRender::TerrainShapeMode::drag (const QPoint& pos, int diffX, int diffY,
if (mDragMode == InteractionType_PrimarySelect) if (mDragMode == InteractionType_PrimarySelect)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == nullptr) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true); if (hit.hit && hit.tag == nullptr) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0);
} }
if (mDragMode == InteractionType_SecondarySelect) if (mDragMode == InteractionType_SecondarySelect)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == nullptr) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true); if (hit.hit && hit.tag == nullptr) selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1);
} }
} }
@ -217,12 +209,17 @@ void CSVRender::TerrainShapeMode::dragCompleted(const QPoint& pos)
applyTerrainEditChanges(); applyTerrainEditChanges();
clearTransientEdits(); clearTransientEdits();
} }
if (mDragMode == InteractionType_PrimarySelect || mDragMode == InteractionType_SecondarySelect)
{
mTerrainShapeSelection->clearTemporarySelection();
}
} }
void CSVRender::TerrainShapeMode::dragAborted() void CSVRender::TerrainShapeMode::dragAborted()
{ {
clearTransientEdits(); clearTransientEdits();
mDragMode = InteractionType_None;
} }
void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor) void CSVRender::TerrainShapeMode::dragWheel (int diff, double speedFactor)
@ -1076,7 +1073,7 @@ void CSVRender::TerrainShapeMode::handleSelection(int globalSelectionX, int glob
} }
} }
void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation) void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>& vertexCoords, unsigned char selectMode)
{ {
int r = mBrushSize / 2; int r = mBrushSize / 2;
std::vector<std::pair<int, int>> selections; std::vector<std::pair<int, int>> selections;
@ -1136,11 +1133,11 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
if (selectAction == "Select only") if (selectAction == "Select only")
mTerrainShapeSelection->onlySelect(selections); mTerrainShapeSelection->onlySelect(selections);
else if (selectAction == "Add to selection") else if (selectAction == "Add to selection")
mTerrainShapeSelection->addSelect(selections, dragOperation); mTerrainShapeSelection->addSelect(selections);
else if (selectAction == "Remove from selection") else if (selectAction == "Remove from selection")
mTerrainShapeSelection->removeSelect(selections, dragOperation); mTerrainShapeSelection->removeSelect(selections);
else if (selectAction == "Invert selection") else if (selectAction == "Invert selection")
mTerrainShapeSelection->toggleSelect(selections, dragOperation); mTerrainShapeSelection->toggleSelect(selections);
} }
void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,

View file

@ -142,7 +142,7 @@ namespace CSVRender
void handleSelection(int globalSelectionX, int globalSelectionY, std::vector<std::pair<int, int>>* selections); void handleSelection(int globalSelectionX, int globalSelectionY, std::vector<std::pair<int, int>>* selections);
/// Handle brush mechanics for terrain shape selection /// Handle brush mechanics for terrain shape selection
void selectTerrainShapes (const std::pair<int, int>& vertexCoords, unsigned char selectMode, bool dragOperation); void selectTerrainShapes (const std::pair<int, int>& vertexCoords, unsigned char selectMode);
/// Push terrain shape edits to command macro /// Push terrain shape edits to command macro
void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document, void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,

View file

@ -48,15 +48,14 @@ namespace CSVRender
float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY) float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)
{ {
float height = 0.f; float height = 0.f;
osg::ref_ptr<const ESMTerrain::LandObject> land = getLand (cellX, cellY);
if (land)
{
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
if (data) height = getVertexHeight(data, inCellX, inCellY);
}
else return height;
return mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] + height;
int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY));
if (index == -1) // no land!
return height;
const ESM::Land::LandData* landData = mData.getLand().getRecord(index).get().getLandData(ESM::Land::DATA_VHGT);
height = landData->mHeights[inCellY*ESM::Land::LAND_SIZE + inCellX];
return mAlteredHeight[inCellY*ESM::Land::LAND_SIZE + inCellX] + height;
} }
float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY) float* TerrainStorage::getAlteredHeight(int inCellX, int inCellY)

View file

@ -129,7 +129,8 @@ void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResu
{ {
if(hit.hit && hit.tag == nullptr) if(hit.hit && hit.tag == nullptr)
{ {
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, false); selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0);
mTerrainTextureSelection->clearTemporarySelection();
} }
} }
@ -137,7 +138,8 @@ void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitRe
{ {
if(hit.hit && hit.tag == nullptr) if(hit.hit && hit.tag == nullptr)
{ {
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, false); selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1);
mTerrainTextureSelection->clearTemporarySelection();
} }
} }
@ -188,8 +190,8 @@ bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos)
mDragMode = InteractionType_None; mDragMode = InteractionType_None;
return false; return false;
} }
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true); selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0);
return false; return true;
} }
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos) bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
@ -201,8 +203,8 @@ bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
mDragMode = InteractionType_None; mDragMode = InteractionType_None;
return false; return false;
} }
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true); selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1);
return false; return true;
} }
void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
@ -225,13 +227,13 @@ void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diff
if (mDragMode == InteractionType_PrimarySelect) if (mDragMode == InteractionType_PrimarySelect)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == nullptr) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true); if (hit.hit && hit.tag == nullptr) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0);
} }
if (mDragMode == InteractionType_SecondarySelect) if (mDragMode == InteractionType_SecondarySelect)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == nullptr) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true); if (hit.hit && hit.tag == nullptr) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1);
} }
} }
@ -251,6 +253,8 @@ void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
mIsEditing = false; mIsEditing = false;
} }
} }
mTerrainTextureSelection->clearTemporarySelection();
} }
void CSVRender::TerrainTextureMode::dragAborted() void CSVRender::TerrainTextureMode::dragAborted()
@ -496,7 +500,7 @@ bool CSVRender::TerrainTextureMode::isInCellSelection(int globalSelectionX, int
} }
void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation) void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, int>& texCoords, unsigned char selectMode)
{ {
int r = mBrushSize / 2; int r = mBrushSize / 2;
std::vector<std::pair<int, int>> selections; std::vector<std::pair<int, int>> selections;
@ -559,8 +563,21 @@ void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, i
} }
} }
if(selectMode == 0) mTerrainTextureSelection->onlySelect(selections); std::string selectAction;
if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation);
if (selectMode == 0)
selectAction = CSMPrefs::get()["3D Scene Editing"]["primary-select-action"].toString();
else
selectAction = CSMPrefs::get()["3D Scene Editing"]["secondary-select-action"].toString();
if (selectAction == "Select only")
mTerrainTextureSelection->onlySelect(selections);
else if (selectAction == "Add to selection")
mTerrainTextureSelection->addSelect(selections);
else if (selectAction == "Remove from selection")
mTerrainTextureSelection->removeSelect(selections);
else if (selectAction == "Invert selection")
mTerrainTextureSelection->toggleSelect(selections);
} }
void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,

View file

@ -95,7 +95,7 @@ namespace CSVRender
bool isInCellSelection(int globalSelectionX, int globalSelectionY); bool isInCellSelection(int globalSelectionX, int globalSelectionY);
/// \brief Handle brush mechanics for texture selection /// \brief Handle brush mechanics for texture selection
void selectTerrainTextures (const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation); void selectTerrainTextures (const std::pair<int, int>& texCoords, unsigned char selectMode);
/// \brief Push texture edits to command macro /// \brief Push texture edits to command macro
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,

View file

@ -464,7 +464,6 @@ void CSVRender::WorldspaceWidget::abortDrag()
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent()); EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.dragAborted(); editMode.dragAborted();
mDragging = false;
mDragMode = InteractionType_None; mDragMode = InteractionType_None;
} }
} }

View file

@ -19,7 +19,7 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask actors objects renderingmanager animation rotatecontroller sky skyutil npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager
bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover postprocessor renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover postprocessor
@ -27,7 +27,7 @@ add_openmw_dir (mwrender
add_openmw_dir (mwinput add_openmw_dir (mwinput
actions actionmanager bindingsmanager controllermanager controlswitch actions actionmanager bindingsmanager controllermanager controlswitch
inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager inputmanagerimp mousemanager keyboardmanager sensormanager
) )
add_openmw_dir (mwgui add_openmw_dir (mwgui
@ -72,9 +72,9 @@ add_openmw_dir (mwworld
containerstore actiontalk actiontake manualref player cellvisitors failedaction containerstore actiontalk actiontake manualref player cellvisitors failedaction
cells localscripts customdata inventorystore ptr actionopen actionread actionharvest cells localscripts customdata inventorystore ptr actionopen actionread actionharvest
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor store esmstore fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref weather projectilemanager contentloader esmloader actiontrap cellreflist cellref weather projectilemanager
cellpreloader datetimemanager cellpreloader datetimemanager groundcoverstore magiceffects
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics
@ -94,7 +94,7 @@ add_openmw_dir (mwmechanics
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
character actors objects aistate trading weaponpriority spellpriority weapontype spellutil character actors objects aistate trading weaponpriority spellpriority weapontype spellutil
spellabsorption spelleffects spelleffects
) )
add_openmw_dir (mwstate add_openmw_dir (mwstate
@ -153,9 +153,12 @@ target_link_libraries(openmw
"osg-ffmpeg-videoplayer" "osg-ffmpeg-videoplayer"
"oics" "oics"
components components
${LUA_LIBRARIES}
) )
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16)
target_precompile_headers(openmw PRIVATE ${SOL_INCLUDE_DIR}/sol/sol.hpp)
endif ()
if (ANDROID) if (ANDROID)
target_link_libraries(openmw EGL android log z) target_link_libraries(openmw EGL android log z)
endif (ANDROID) endif (ANDROID)
@ -176,8 +179,7 @@ endif()
if(APPLE) if(APPLE)
set(BUNDLE_RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources") set(BUNDLE_RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources")
set(OPENMW_MYGUI_FILES_ROOT ${BUNDLE_RESOURCES_DIR}) set(OPENMW_RESOURCES_ROOT ${BUNDLE_RESOURCES_DIR})
set(OPENMW_SHADERS_ROOT ${BUNDLE_RESOURCES_DIR})
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files) add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)

View file

@ -1,4 +1,6 @@
#ifndef stderr
int stderr = 0; // Hack: fix linker error int stderr = 0; // Hack: fix linker error
#endif
#include "SDL_main.h" #include "SDL_main.h"
#include <SDL_gamecontroller.h> #include <SDL_gamecontroller.h>

View file

@ -8,6 +8,8 @@
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
#include <osg/Version>
#include <osgViewer/ViewerEventHandlers> #include <osgViewer/ViewerEventHandlers>
#include <osgDB/ReadFile> #include <osgDB/ReadFile>
#include <osgDB/WriteFile> #include <osgDB/WriteFile>
@ -37,11 +39,10 @@
#include <components/version/version.hpp> #include <components/version/version.hpp>
#include <components/detournavigator/navigator.hpp>
#include <components/misc/frameratelimiter.hpp> #include <components/misc/frameratelimiter.hpp>
#include <components/sceneutil/screencapture.hpp> #include <components/sceneutil/screencapture.hpp>
#include <components/sceneutil/depth.hpp>
#include "mwinput/inputmanagerimp.hpp" #include "mwinput/inputmanagerimp.hpp"
@ -293,6 +294,10 @@ bool OMW::Engine::frame(float frametime)
// Main menu opened? Then scripts are also paused. // Main menu opened? Then scripts are also paused.
bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu); 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(mEnvironment.getWindowManager()->isGuiMode(), frametime);
// update game state // update game state
{ {
ScopedProfile<UserStatsType::State> profile(frameStart, frameNumber, *timer, *stats); ScopedProfile<UserStatsType::State> profile(frameStart, frameNumber, *timer, *stats);
@ -495,11 +500,6 @@ void OMW::Engine::addGroundcoverFile(const std::string& file)
mGroundcoverFiles.emplace_back(file); mGroundcoverFiles.emplace_back(file);
} }
void OMW::Engine::addLuaScriptListFile(const std::string& file)
{
mLuaScriptListFiles.push_back(file);
}
void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame) void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame)
{ {
mSkipMenu = skipMenu; mSkipMenu = skipMenu;
@ -551,6 +551,10 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
if(fullscreen) if(fullscreen)
flags |= SDL_WINDOW_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) if (!windowBorder)
flags |= SDL_WINDOW_BORDERLESS; flags |= SDL_WINDOW_BORDERLESS;
@ -674,7 +678,7 @@ void OMW::Engine::setWindowIcon()
void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings)
{ {
mEnvironment.setStateManager ( mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0))); new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles));
createWindow(settings); createWindow(settings);
@ -714,7 +718,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mViewer->addEventHandler(mScreenCaptureHandler); mViewer->addEventHandler(mScreenCaptureHandler);
mLuaManager = new MWLua::LuaManager(mVFS.get(), mLuaScriptListFiles); mLuaManager = new MWLua::LuaManager(mVFS.get());
mEnvironment.setLuaManager(mLuaManager); mEnvironment.setLuaManager(mLuaManager);
// Create input and UI first to set up a bootstrapping environment for // Create input and UI first to set up a bootstrapping environment for
@ -758,6 +762,28 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false); osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f); 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(); std::string myguiResources = (mResDir / "mygui").string();
osg::ref_ptr<osg::Group> guiRoot = new osg::Group; osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
@ -869,7 +895,6 @@ public:
} }
else else
update(); update();
mEngine->mLuaManager->applyQueuedChanges();
}; };
void join() void join()
@ -1064,8 +1089,6 @@ void OMW::Engine::go()
// Save user settings // Save user settings
settings.saveUser(settingspath); settings.saveUser(settingspath);
mViewer->stopThreading();
Log(Debug::Info) << "Quitting peacefully."; Log(Debug::Info) << "Quitting peacefully.";
} }

View file

@ -72,7 +72,6 @@ namespace OMW
std::string mCellName; std::string mCellName;
std::vector<std::string> mContentFiles; std::vector<std::string> mContentFiles;
std::vector<std::string> mGroundcoverFiles; std::vector<std::string> mGroundcoverFiles;
std::vector<std::string> mLuaScriptListFiles;
bool mSkipMenu; bool mSkipMenu;
bool mUseSound; bool mUseSound;
bool mCompileAll; bool mCompileAll;
@ -146,7 +145,6 @@ namespace OMW
*/ */
void addContentFile(const std::string& file); void addContentFile(const std::string& file);
void addGroundcoverFile(const std::string& file); void addGroundcoverFile(const std::string& file);
void addLuaScriptListFile(const std::string& file);
/// Disable or enable all sounds /// Disable or enable all sounds
void setSoundUsage(bool soundUsage); void setSoundUsage(bool soundUsage);

View file

@ -123,9 +123,11 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.addGroundcoverFile(file); engine.addGroundcoverFile(file);
} }
StringsVector luaScriptLists = variables["lua-scripts"].as<StringsVector>(); if (variables.count("lua-scripts"))
for (const auto& file : luaScriptLists) {
engine.addLuaScriptListFile(file); Log(Debug::Warning) << "Lua scripts have been specified via the old lua-scripts option and will not be loaded. "
"Please update them to a version which uses the new omwscripts format.";
}
// startup-settings // startup-settings
engine.setCell(variables["start"].as<std::string>()); engine.setCell(variables["start"].as<std::string>());

View file

@ -49,8 +49,8 @@ namespace MWBase
virtual void setGamepadGuiCursorEnabled(bool enabled) = 0; virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;
virtual void setAttemptJump(bool jumping) = 0; virtual void setAttemptJump(bool jumping) = 0;
virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; virtual void toggleControlSwitch(std::string_view sw, bool value) = 0;
virtual bool getControlSwitch (const std::string& sw) = 0; virtual bool getControlSwitch(std::string_view sw) = 0;
virtual std::string getActionDescription (int action) const = 0; virtual std::string getActionDescription (int action) const = 0;
virtual std::string getActionKeyBindingName (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 bool actionIsActive(int action) const = 0;
virtual float getActionValue(int action) const = 0; // returns value in range [0, 1] 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 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 getMouseMoveX() const = 0;
virtual int getMouseMoveY() const = 0; virtual int getMouseMoveY() const = 0;

View file

@ -30,6 +30,7 @@ namespace MWBase
virtual ~LuaManager() = default; virtual ~LuaManager() = default;
virtual void newGameStarted() = 0; virtual void newGameStarted() = 0;
virtual void gameLoaded() = 0;
virtual void registerObject(const MWWorld::Ptr& ptr) = 0; virtual void registerObject(const MWWorld::Ptr& ptr) = 0;
virtual void deregisterObject(const MWWorld::Ptr& ptr) = 0; virtual void deregisterObject(const MWWorld::Ptr& ptr) = 0;
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0; virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;

View file

@ -112,6 +112,9 @@ namespace MWBase
/// Makes \a ptr fight \a target. Also shouts a combat taunt. /// Makes \a ptr fight \a target. Also shouts a combat taunt.
virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; 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 enum OffenseType
{ {
OT_Theft, // Taking items owned by an NPC or a faction you are not a member of OT_Theft, // Taking items owned by an NPC or a faction you are not a member of

View file

@ -355,6 +355,7 @@ namespace MWBase
virtual const std::string& getVersionDescription() const = 0; virtual const std::string& getVersionDescription() const = 0;
virtual void onDeleteCustomData(const MWWorld::Ptr& ptr) = 0; virtual void onDeleteCustomData(const MWWorld::Ptr& ptr) = 0;
virtual void forceLootMode(const MWWorld::Ptr& ptr) = 0;
}; };
} }

View file

@ -62,6 +62,7 @@ namespace MWPhysics
namespace MWRender namespace MWRender
{ {
class Animation; class Animation;
class Camera;
} }
namespace MWMechanics namespace MWMechanics
@ -289,7 +290,7 @@ namespace MWBase
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, const osg::Vec3f& position, bool movePhysics=true, bool keepActive=false) = 0; virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, const osg::Vec3f& position, bool movePhysics=true, bool keepActive=false) = 0;
///< @return an updated Ptr ///< @return an updated Ptr
virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, const osg::Vec3f& vec, bool moveToActive, bool ignoreCollisions) = 0; virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, const osg::Vec3f& vec) = 0;
///< @return an updated Ptr ///< @return an updated Ptr
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
@ -433,14 +434,12 @@ namespace MWBase
virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0; virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;
virtual MWRender::Camera* getCamera() = 0;
virtual void togglePOV(bool force = false) = 0; virtual void togglePOV(bool force = false) = 0;
virtual bool isFirstPerson() const = 0; virtual bool isFirstPerson() const = 0;
virtual bool isPreviewModeEnabled() const = 0; virtual bool isPreviewModeEnabled() const = 0;
virtual void togglePreviewMode(bool enable) = 0;
virtual bool toggleVanityMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable) = 0;
virtual void allowVanityMode(bool allow) = 0;
virtual bool vanityRotateCamera(float * rot) = 0; virtual bool vanityRotateCamera(float * rot) = 0;
virtual void adjustCameraDistance(float dist) = 0;
virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0; virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0;
virtual void disableDeferredPreviewRotation() = 0; virtual void disableDeferredPreviewRotation() = 0;

View file

@ -89,7 +89,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Activator); std::shared_ptr<Class> instance (new Activator);
registerClass (typeid (ESM::Activator).name(), instance); registerClass (ESM::Activator::sRecordId, instance);
} }
bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const

View file

@ -3,6 +3,8 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include <components/esm/loadmgef.hpp>
namespace ESM namespace ESM
{ {
struct GameSetting; struct GameSetting;
@ -17,6 +19,15 @@ namespace MWClass
Actor() = default; Actor() = default;
template <class GMST>
float getSwimSpeedImpl(const MWWorld::Ptr& ptr, const GMST& gmst, const MWMechanics::MagicEffects& mageffects, float baseSpeed) const
{
return baseSpeed
* (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude())
* (gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat());
}
public: public:
~Actor() override = default; ~Actor() override = default;

View file

@ -69,7 +69,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Apparatus); std::shared_ptr<Class> instance (new Apparatus);
registerClass (typeid (ESM::Apparatus).name(), instance); registerClass (ESM::Apparatus::sRecordId, instance);
} }
std::string Apparatus::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Apparatus::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -163,7 +163,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Armor); std::shared_ptr<Class> instance (new Armor);
registerClass (typeid (ESM::Armor).name(), instance); registerClass (ESM::Armor::sRecordId, instance);
} }
std::string Armor::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Armor::getUpSoundId (const MWWorld::ConstPtr& ptr) const
@ -322,7 +322,7 @@ namespace MWClass
if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft)
{ {
MWWorld::ConstContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ConstContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != invStore.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()) if(weapon != invStore.end() && weapon->getType() == ESM::Weapon::sRecordId)
{ {
const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>(); const MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
if (MWMechanics::getWeaponType(ref->mBase->mData.mType)->mFlags & ESM::WeaponType::TwoHanded) if (MWMechanics::getWeaponType(ref->mBase->mData.mType)->mFlags & ESM::WeaponType::TwoHanded)

View file

@ -36,7 +36,7 @@ namespace MWClass
{ {
std::shared_ptr<MWWorld::Class> instance (new BodyPart); std::shared_ptr<MWWorld::Class> instance (new BodyPart);
registerClass (typeid (ESM::BodyPart).name(), instance); registerClass (ESM::BodyPart::sRecordId, instance);
} }
std::string BodyPart::getModel(const MWWorld::ConstPtr &ptr) const std::string BodyPart::getModel(const MWWorld::ConstPtr &ptr) const

View file

@ -85,7 +85,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Book); std::shared_ptr<Class> instance (new Book);
registerClass (typeid (ESM::Book).name(), instance); registerClass (ESM::Book::sRecordId, instance);
} }
std::string Book::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Book::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -121,7 +121,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Clothing); std::shared_ptr<Class> instance (new Clothing);
registerClass (typeid (ESM::Clothing).name(), instance); registerClass (ESM::Clothing::sRecordId, instance);
} }
std::string Clothing::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Clothing::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -242,7 +242,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Container); std::shared_ptr<Class> instance (new Container);
registerClass (typeid (ESM::Container).name(), instance); registerClass (ESM::Container::sRecordId, instance);
} }
bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const

View file

@ -82,12 +82,13 @@ namespace MWClass
const Creature::GMST& Creature::getGmst() const Creature::GMST& Creature::getGmst()
{ {
static GMST gmst; static const GMST staticGmst = []
static bool inited = false;
if (!inited)
{ {
GMST gmst;
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature"); gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature");
gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature"); gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature");
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect"); gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
@ -101,16 +102,20 @@ namespace MWClass
gmst.fKnockDownMult = store.find("fKnockDownMult"); gmst.fKnockDownMult = store.find("fKnockDownMult");
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult"); gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
inited = true;
}
return gmst; return gmst;
} ();
return staticGmst;
} }
void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::unique_ptr<CreatureCustomData> data (new CreatureCustomData); auto tempData = std::make_unique<CreatureCustomData>();
CreatureCustomData* data = tempData.get();
MWMechanics::CreatureCustomDataResetter resetter(ptr);
ptr.getRefData().setCustomData(std::move(tempData));
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
@ -154,10 +159,7 @@ namespace MWClass
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold); data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
data->mCreatureStats.setNeedRecalcDynamicStats(false); resetter.mPtr = {};
// store
ptr.getRefData().setCustomData(std::move(data));
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId()); getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
@ -238,7 +240,7 @@ namespace MWClass
{ {
MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weaponslot != inv.end() && weaponslot->getTypeName() == typeid(ESM::Weapon).name()) if (weaponslot != inv.end() && weaponslot->getType() == ESM::Weapon::sRecordId)
weapon = *weaponslot; weapon = *weaponslot;
} }
@ -497,7 +499,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Creature); std::shared_ptr<Class> instance (new Creature);
registerClass (typeid (ESM::Creature).name(), instance); registerClass (ESM::Creature::sRecordId, instance);
} }
float Creature::getMaxSpeed(const MWWorld::Ptr &ptr) const float Creature::getMaxSpeed(const MWWorld::Ptr &ptr) const
@ -891,12 +893,8 @@ namespace MWClass
float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const
{ {
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects(); const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
return getWalkSpeed(ptr) return getSwimSpeedImpl(ptr, getGmst(), mageffects, getWalkSpeed(ptr));
* (1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude())
* (gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat());
} }
} }

View file

@ -5,6 +5,7 @@
#include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/levelledlist.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/customdata.hpp" #include "../mwworld/customdata.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
@ -27,6 +28,24 @@ namespace MWClass
} }
}; };
MWWorld::Ptr CreatureLevList::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const
{
const MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.get<ESM::CreatureLevList>();
return MWWorld::Ptr(cell.insert(ref), &cell);
}
void CreatureLevList::adjustPosition(const MWWorld::Ptr& ptr, bool force) const
{
if (ptr.getRefData().getCustomData() == nullptr)
return;
CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
MWWorld::Ptr creature = (customData.mSpawnActorId == -1) ? MWWorld::Ptr() : MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
if (!creature.isEmpty())
MWBase::Environment::get().getWorld()->adjustPosition(creature, force);
}
std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const
{ {
return ""; return "";
@ -45,7 +64,13 @@ namespace MWClass
if (customData.mSpawn) if (customData.mSpawn)
return; 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()) if (!creature.isEmpty())
{ {
const MWMechanics::CreatureStats& creatureStats = creature.getClass().getCreatureStats(creature); const MWMechanics::CreatureStats& creatureStats = creature.getClass().getCreatureStats(creature);
@ -70,7 +95,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new CreatureLevList); std::shared_ptr<Class> instance (new CreatureLevList);
registerClass (typeid (ESM::CreatureLevList).name(), instance); registerClass (ESM::CreatureLevList::sRecordId, instance);
} }
void CreatureLevList::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const void CreatureLevList::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const

View file

@ -32,6 +32,10 @@ namespace MWClass
///< Write additional state from \a ptr into \a state. ///< Write additional state from \a ptr into \a state.
void respawn (const MWWorld::Ptr& ptr) const override; void respawn (const MWWorld::Ptr& ptr) const override;
MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const override;
void adjustPosition(const MWWorld::Ptr& ptr, bool force) const override;
}; };
} }

View file

@ -264,7 +264,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Door); std::shared_ptr<Class> instance (new Door);
registerClass (typeid (ESM::Door).name(), instance); registerClass (ESM::Door::sRecordId, instance);
} }
MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const

View file

@ -81,7 +81,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Ingredient); std::shared_ptr<Class> instance (new Ingredient);
registerClass (typeid (ESM::Ingredient).name(), instance); registerClass (ESM::Ingredient::sRecordId, instance);
} }
std::string Ingredient::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Ingredient::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -19,6 +19,6 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new ItemLevList); std::shared_ptr<Class> instance (new ItemLevList);
registerClass (typeid (ESM::ItemLevList).name(), instance); registerClass (ESM::ItemLevList::sRecordId, instance);
} }
} }

View file

@ -124,7 +124,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Light); std::shared_ptr<Class> instance (new Light);
registerClass (typeid (ESM::Light).name(), instance); registerClass (ESM::Light::sRecordId, instance);
} }
std::string Light::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Light::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -80,7 +80,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Lockpick); std::shared_ptr<Class> instance (new Lockpick);
registerClass (typeid (ESM::Lockpick).name(), instance); registerClass (ESM::Lockpick::sRecordId, instance);
} }
std::string Lockpick::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Lockpick::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -106,7 +106,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Miscellaneous); std::shared_ptr<Class> instance (new Miscellaneous);
registerClass (typeid (ESM::Miscellaneous).name(), instance); registerClass (ESM::Miscellaneous::sRecordId, instance);
} }
std::string Miscellaneous::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Miscellaneous::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -266,10 +266,10 @@ namespace MWClass
const Npc::GMST& Npc::getGmst() const Npc::GMST& Npc::getGmst()
{ {
static GMST gmst; static const GMST staticGmst = []
static bool inited = false;
if(!inited)
{ {
GMST gmst;
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
@ -294,16 +294,20 @@ namespace MWClass
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
gmst.fCombatArmorMinMult = store.find("fCombatArmorMinMult"); gmst.fCombatArmorMinMult = store.find("fCombatArmorMinMult");
inited = true;
}
return gmst; return gmst;
} ();
return staticGmst;
} }
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::unique_ptr<NpcCustomData> data(new NpcCustomData); bool recalculate = false;
auto tempData = std::make_unique<NpcCustomData>();
NpcCustomData* data = tempData.get();
MWMechanics::CreatureCustomDataResetter resetter(ptr);
ptr.getRefData().setCustomData(std::move(tempData));
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
@ -334,8 +338,6 @@ namespace MWClass
data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel); data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel);
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition);
data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation); data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);
data->mNpcStats.setNeedRecalcDynamicStats(false);
} }
else else
{ {
@ -351,7 +353,7 @@ namespace MWClass
autoCalculateAttributes(ref->mBase, data->mNpcStats); autoCalculateAttributes(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats, ptr, spellsInitialised); autoCalculateSkills(ref->mBase, data->mNpcStats, ptr, spellsInitialised);
data->mNpcStats.setNeedRecalcDynamicStats(true); recalculate = true;
} }
// Persistent actors with 0 health do not play death animation // Persistent actors with 0 health do not play death animation
@ -387,7 +389,9 @@ namespace MWClass
data->mNpcStats.setGoldPool(gold); data->mNpcStats.setGoldPool(gold);
// store // store
ptr.getRefData().setCustomData(std::move(data)); resetter.mPtr = {};
if(recalculate)
data->mNpcStats.recalculateMagicka();
// inventory // inventory
// setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items // setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
@ -459,12 +463,12 @@ namespace MWClass
if (equipped != invStore.end()) if (equipped != invStore.end())
{ {
std::vector<ESM::PartReference> parts; std::vector<ESM::PartReference> parts;
if(equipped->getTypeName() == typeid(ESM::Clothing).name()) if(equipped->getType() == ESM::Clothing::sRecordId)
{ {
const ESM::Clothing *clothes = equipped->get<ESM::Clothing>()->mBase; const ESM::Clothing *clothes = equipped->get<ESM::Clothing>()->mBase;
parts = clothes->mParts.mParts; parts = clothes->mParts.mParts;
} }
else if(equipped->getTypeName() == typeid(ESM::Armor).name()) else if(equipped->getType() == ESM::Armor::sRecordId)
{ {
const ESM::Armor *armor = equipped->get<ESM::Armor>()->mBase; const ESM::Armor *armor = equipped->get<ESM::Armor>()->mBase;
parts = armor->mParts.mParts; parts = armor->mParts.mParts;
@ -543,7 +547,7 @@ namespace MWClass
MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr()); MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr());
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) if(!weapon.isEmpty() && weapon.getType() != ESM::Weapon::sRecordId)
weapon = MWWorld::Ptr(); weapon = MWWorld::Ptr();
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
@ -766,7 +770,7 @@ namespace MWClass
MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
bool hasArmor = !armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name(); bool hasArmor = !armor.isEmpty() && armor.getType() == ESM::Armor::sRecordId;
// If there's no item in the carried left slot or if it is not a shield redistribute the hit. // If there's no item in the carried left slot or if it is not a shield redistribute the hit.
if (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft) if (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft)
{ {
@ -778,7 +782,7 @@ namespace MWClass
if (armorslot != inv.end()) if (armorslot != inv.end())
{ {
armor = *armorslot; armor = *armorslot;
hasArmor = !armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name(); hasArmor = !armor.isEmpty() && armor.getType() == ESM::Armor::sRecordId;
} }
} }
if (hasArmor) if (hasArmor)
@ -1036,7 +1040,7 @@ namespace MWClass
void Npc::registerSelf() void Npc::registerSelf()
{ {
std::shared_ptr<Class> instance (new Npc); std::shared_ptr<Class> instance (new Npc);
registerClass (typeid (ESM::NPC).name(), instance); registerClass (ESM::NPC::sRecordId, instance);
} }
bool Npc::hasToolTip(const MWWorld::ConstPtr& ptr) const bool Npc::hasToolTip(const MWWorld::ConstPtr& ptr) const
@ -1135,7 +1139,7 @@ namespace MWClass
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
{ {
MWWorld::ConstContainerStoreIterator it = invStore.getSlot(i); MWWorld::ConstContainerStoreIterator it = invStore.getSlot(i);
if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name()) if (it == invStore.end() || it->getType() != ESM::Armor::sRecordId)
{ {
// unarmored // unarmored
ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
@ -1232,7 +1236,7 @@ namespace MWClass
const MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr); const MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
MWWorld::ConstContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); MWWorld::ConstContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name()) if(boots == inv.end() || boots->getType() != ESM::Armor::sRecordId)
return (name == "left") ? "FootBareLeft" : "FootBareRight"; return (name == "left") ? "FootBareLeft" : "FootBareRight";
switch(boots->getClass().getEquipmentSkill(*boots)) switch(boots->getClass().getEquipmentSkill(*boots))
@ -1476,7 +1480,6 @@ namespace MWClass
float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const
{ {
const GMST& gmst = getGmst();
const MWBase::World* world = MWBase::Environment::get().getWorld(); const MWBase::World* world = MWBase::Environment::get().getWorld();
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData* npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
@ -1486,17 +1489,6 @@ namespace MWClass
const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run) const bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run)
&& (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr)); && (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float swimSpeed; return getSwimSpeedImpl(ptr, getGmst(), mageffects, running ? getRunSpeed(ptr) : getWalkSpeed(ptr));
if (running)
swimSpeed = getRunSpeed(ptr);
else
swimSpeed = getWalkSpeed(ptr);
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->mValue.getFloat()
+ 0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fSwimRunAthleticsMult->mValue.getFloat();
return swimSpeed;
} }
} }

View file

@ -74,7 +74,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Potion); std::shared_ptr<Class> instance (new Potion);
registerClass (typeid (ESM::Potion).name(), instance); registerClass (ESM::Potion::sRecordId, instance);
} }
std::string Potion::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Potion::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -80,7 +80,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Probe); std::shared_ptr<Class> instance (new Probe);
registerClass (typeid (ESM::Probe).name(), instance); registerClass (ESM::Probe::sRecordId, instance);
} }
std::string Probe::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Probe::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -69,7 +69,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Repair); std::shared_ptr<Class> instance (new Repair);
registerClass (typeid (ESM::Repair).name(), instance); registerClass (ESM::Repair::sRecordId, instance);
} }
std::string Repair::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Repair::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -59,7 +59,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Static); std::shared_ptr<Class> instance (new Static);
registerClass (typeid (ESM::Static).name(), instance); registerClass (ESM::Static::sRecordId, instance);
} }
MWWorld::Ptr Static::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const MWWorld::Ptr Static::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const

View file

@ -125,7 +125,7 @@ namespace MWClass
{ {
std::shared_ptr<Class> instance (new Weapon); std::shared_ptr<Class> instance (new Weapon);
registerClass (typeid (ESM::Weapon).name(), instance); registerClass (ESM::Weapon::sRecordId, instance);
} }
std::string Weapon::getUpSoundId (const MWWorld::ConstPtr& ptr) const std::string Weapon::getUpSoundId (const MWWorld::ConstPtr& ptr) const

View file

@ -76,9 +76,10 @@ namespace MWDialogue
mKnownTopics.insert( Misc::StringUtils::lowerCase(topic) ); 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); std::vector<HyperTextParser::Token> hypertext = HyperTextParser::parseHyperText(text);
for (std::vector<HyperTextParser::Token>::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok) for (std::vector<HyperTextParser::Token>::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok)
@ -95,6 +96,18 @@ namespace MWDialogue
topicId = mTranslationDataStorage.topicStandardForm(topicId); 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 )) if (mActorKnownTopics.count( topicId ))
mKnownTopics.insert( topicId ); mKnownTopics.insert( topicId );
} }
@ -136,7 +149,6 @@ namespace MWDialogue
mTalkedTo = creatureStats.hasTalkedToPlayer(); mTalkedTo = creatureStats.hasTalkedToPlayer();
mActorKnownTopics.clear(); mActorKnownTopics.clear();
mActorKnownTopicsFlag.clear();
//greeting //greeting
const MWWorld::Store<ESM::Dialogue> &dialogs = const MWWorld::Store<ESM::Dialogue> &dialogs =
@ -163,7 +175,7 @@ namespace MWDialogue
executeScript (info->mResultScript, mActor); executeScript (info->mResultScript, mActor);
mLastTopic = it->mId; mLastTopic = it->mId;
parseText (info->mResponse); addTopicsFromText (info->mResponse);
return true; return true;
} }
@ -277,7 +289,10 @@ namespace MWDialogue
const ESM::Dialogue& dialogue = *dialogues.find (topic); 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) if (info)
{ {
std::string title; std::string title;
@ -320,7 +335,7 @@ namespace MWDialogue
executeScript (info->mResultScript, mActor); executeScript (info->mResultScript, mActor);
parseText (info->mResponse); addTopicsFromText (info->mResponse);
} }
} }
@ -339,7 +354,6 @@ namespace MWDialogue
updateGlobals(); updateGlobals();
mActorKnownTopics.clear(); mActorKnownTopics.clear();
mActorKnownTopicsFlag.clear();
const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>(); const auto& dialogs = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
@ -354,21 +368,41 @@ namespace MWDialogue
if (answer != nullptr) if (answer != nullptr)
{ {
int flag = 0; int topicFlags = 0;
if(!inJournal(topicId, answer->mId)) if(!inJournal(topicId, answer->mId))
{ {
// Does this dialogue contains some actor-specific answer? // Does this dialogue contains some actor-specific answer?
if (Misc::StringUtils::ciEqual(answer->mActor, mActor.getCellRef().getRefId())) if (Misc::StringUtils::ciEqual(answer->mActor, mActor.getCellRef().getRefId()))
flag |= MWBase::DialogueManager::TopicType::Specific; topicFlags |= MWBase::DialogueManager::TopicType::Specific;
} }
else else
flag |= MWBase::DialogueManager::TopicType::Exhausted; topicFlags |= MWBase::DialogueManager::TopicType::Exhausted;
mActorKnownTopics.insert (dialog.mId); mActorKnownTopics.insert (std::make_pair(dialog.mId, ActorKnownTopicInfo {topicFlags, answer}));
mActorKnownTopicsFlag[dialog.mId] = flag;
} }
} }
} }
// 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() std::list<std::string> DialogueManager::getAvailableTopics()
@ -377,7 +411,7 @@ namespace MWDialogue
std::list<std::string> keywordList; std::list<std::string> keywordList;
for (const std::string& topic : mActorKnownTopics) for (const auto& [topic, topicInfo] : mActorKnownTopics)
{ {
//does the player know the topic? //does the player know the topic?
if (mKnownTopics.count(topic)) if (mKnownTopics.count(topic))
@ -391,7 +425,7 @@ namespace MWDialogue
int DialogueManager::getTopicFlag(const std::string& topicId) int DialogueManager::getTopicFlag(const std::string& topicId)
{ {
return mActorKnownTopicsFlag[topicId]; return mActorKnownTopics[topicId].mFlags;
} }
void DialogueManager::keywordSelected (const std::string& keyword, ResponseCallback* callback) 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) // Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate)
npcStats.setBaseDisposition(0); npcStats.setBaseDisposition(0);
int zero = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false); 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); npcStats.setBaseDisposition(disposition);
} }
@ -444,7 +478,7 @@ namespace MWDialogue
if (const ESM::DialInfo *info = filter.search (*dialogue, true)) if (const ESM::DialInfo *info = filter.search (*dialogue, true))
{ {
std::string text = info->mResponse; std::string text = info->mResponse;
parseText (text); addTopicsFromText (text);
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
@ -579,7 +613,7 @@ namespace MWDialogue
{ {
const ESM::DialInfo* info = infos[0]; const ESM::DialInfo* info = infos[0];
parseText (info->mResponse); addTopicsFromText (info->mResponse);
const MWWorld::Store<ESM::GameSetting>& gmsts = const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

View file

@ -10,6 +10,7 @@
#include <components/compiler/streamerrorhandler.hpp> #include <components/compiler/streamerrorhandler.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadinfo.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -24,14 +25,19 @@ namespace MWDialogue
{ {
class DialogueManager : public MWBase::DialogueManager 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. std::set<std::string, Misc::StringUtils::CiComp> mKnownTopics;// Those are the topics the player knows.
// Modified faction reactions. <Faction1, <Faction2, Difference> > // Modified faction reactions. <Faction1, <Faction2, Difference> >
typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap; typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap;
ModFactionReactionMap mChangedFactionReaction; ModFactionReactionMap mChangedFactionReaction;
std::set<std::string, Misc::StringUtils::CiComp> mActorKnownTopics; std::map<std::string, ActorKnownTopicInfo, Misc::StringUtils::CiComp> mActorKnownTopics;
std::unordered_map<std::string, int> mActorKnownTopicsFlag;
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;
MWScript::CompilerContext mCompilerContext; MWScript::CompilerContext mCompilerContext;
@ -51,7 +57,8 @@ namespace MWDialogue
int mCurrentDisposition; int mCurrentDisposition;
int mPermanentDispositionChange; 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 updateActorKnownTopics();
void updateGlobals(); void updateGlobals();

View file

@ -23,7 +23,7 @@
bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
{ {
bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); bool isCreature = (mActor.getType() != ESM::NPC::sRecordId);
// actor id // actor id
if (!info.mActor.empty()) if (!info.mActor.empty())
@ -160,7 +160,7 @@ bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const
bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert) const
{ {
bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); bool isCreature = (mActor.getType() != ESM::NPC::sRecordId);
if (isCreature) if (isCreature)
return true; return true;
@ -207,7 +207,7 @@ bool MWDialogue::Filter::testFunctionLocal(const MWDialogue::SelectWrapper& sele
bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const
{ {
if (select.isNpcOnly() && (mActor.getTypeName() != typeid (ESM::NPC).name())) if (select.isNpcOnly() && (mActor.getType() != ESM::NPC::sRecordId))
// If the actor is a creature, we pass all conditions only applicable to NPCs. // If the actor is a creature, we pass all conditions only applicable to NPCs.
return true; return true;
@ -452,7 +452,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
{ {
if (target.getClass().isNpc() && target.getClass().getNpcStats(target).isWerewolf()) if (target.getClass().isNpc() && target.getClass().getNpcStats(target).isWerewolf())
return 2; return 2;
if (target.getTypeName() == typeid(ESM::Creature).name()) if (target.getType() == ESM::Creature::sRecordId)
return 1; return 1;
} }
} }

View file

@ -30,7 +30,7 @@ public:
{ {
if (keyword.empty()) if (keyword.empty())
return; return;
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), 0, mRoot); seed_impl (std::move(keyword), std::move (value), 0, mRoot);
} }
void clear () void clear ()
@ -39,7 +39,7 @@ public:
mRoot.mKeyword.clear (); mRoot.mKeyword.clear ();
} }
bool containsKeyword (string_t keyword, value_t& value) bool containsKeyword (const string_t& keyword, value_t& value)
{ {
typename Entry::childen_t::iterator current; typename Entry::childen_t::iterator current;
typename Entry::childen_t::iterator next; typename Entry::childen_t::iterator next;
@ -209,8 +209,8 @@ private:
if (j == entry.mChildren.end ()) if (j == entry.mChildren.end ())
{ {
entry.mChildren [ch].mValue = /*std::move*/ (value); entry.mChildren [ch].mValue = std::move (value);
entry.mChildren [ch].mKeyword = /*std::move*/ (keyword); entry.mChildren [ch].mKeyword = std::move (keyword);
} }
else else
{ {
@ -219,22 +219,22 @@ private:
if (keyword == j->second.mKeyword) if (keyword == j->second.mKeyword)
throw std::runtime_error ("duplicate keyword inserted"); throw std::runtime_error ("duplicate keyword inserted");
value_t pushValue = /*std::move*/ (j->second.mValue); value_t pushValue = j->second.mValue;
string_t pushKeyword = /*std::move*/ (j->second.mKeyword); string_t pushKeyword = j->second.mKeyword;
if (depth >= pushKeyword.size ()) if (depth >= pushKeyword.size ())
throw std::runtime_error ("unexpected"); throw std::runtime_error ("unexpected");
if (depth+1 < pushKeyword.size()) if (depth+1 < pushKeyword.size())
{ {
seed_impl (/*std::move*/ (pushKeyword), /*std::move*/ (pushValue), depth+1, j->second); seed_impl (std::move (pushKeyword), std::move (pushValue), depth+1, j->second);
j->second.mKeyword.clear (); j->second.mKeyword.clear ();
} }
} }
if (depth+1 == keyword.size()) if (depth+1 == keyword.size())
j->second.mKeyword = value; j->second.mKeyword = value;
else // depth+1 < keyword.size() else // depth+1 < keyword.size()
seed_impl (/*std::move*/ (keyword), /*std::move*/ (value), depth+1, j->second); seed_impl (std::move (keyword), std::move (value), depth+1, j->second);
} }
} }

View file

@ -194,7 +194,7 @@ namespace MWGui
for (size_t i = 0; i < mModel->getItemCount(); ++i) for (size_t i = 0; i < mModel->getItemCount(); ++i)
{ {
MWWorld::Ptr item = mModel->getItem(i).mBase; MWWorld::Ptr item = mModel->getItem(i).mBase;
if (item.getTypeName() != typeid(ESM::Ingredient).name()) if (item.getType() != ESM::Ingredient::sRecordId)
continue; continue;
itemNames.insert(item.getClass().getName(item)); itemNames.insert(item.getClass().getName(item));

View file

@ -8,7 +8,7 @@
#include "MyGUI_FactoryManager.h" #include "MyGUI_FactoryManager.h"
#include <components/misc/utf8stream.hpp> #include <components/misc/utf8stream.hpp>
#include <components/sceneutil/util.hpp> #include <components/sceneutil/depth.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -1221,7 +1221,7 @@ public:
RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); 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), GlyphStream glyphStream(textFormat.mFont, static_cast<float>(mCoord.left), static_cast<float>(mCoord.top - mViewTop),
z /*mNode->getNodeDepth()*/, vertices, renderXform); z /*mNode->getNodeDepth()*/, vertices, renderXform);

View file

@ -38,6 +38,7 @@ namespace MWGui
, mSortModel(nullptr) , mSortModel(nullptr)
, mModel(nullptr) , mModel(nullptr)
, mSelectedItem(-1) , mSelectedItem(-1)
, mTreatNextOpenAsLoot(false)
{ {
getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
getWidget(mTakeButton, "TakeButton"); getWidget(mTakeButton, "TakeButton");
@ -121,13 +122,15 @@ namespace MWGui
void ContainerWindow::setPtr(const MWWorld::Ptr& container) void ContainerWindow::setPtr(const MWWorld::Ptr& container)
{ {
bool lootAnyway = mTreatNextOpenAsLoot;
mTreatNextOpenAsLoot = false;
mPtr = container; mPtr = container;
bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead(); bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead();
if (mPtr.getClass().hasInventoryStore(mPtr)) if (mPtr.getClass().hasInventoryStore(mPtr))
{ {
if (mPtr.getClass().isNpc() && !loot) if (mPtr.getClass().isNpc() && !loot && !lootAnyway)
{ {
// we are stealing stuff // we are stealing stuff
mModel = new PickpocketItemModel(mPtr, new InventoryItemModel(container), mModel = new PickpocketItemModel(mPtr, new InventoryItemModel(container),

View file

@ -37,6 +37,7 @@ namespace MWGui
void onDeleteCustomData(const MWWorld::Ptr& ptr) override; void onDeleteCustomData(const MWWorld::Ptr& ptr) override;
void treatNextOpenAsLoot() { mTreatNextOpenAsLoot = true; };
private: private:
DragAndDrop* mDragAndDrop; DragAndDrop* mDragAndDrop;
@ -44,7 +45,7 @@ namespace MWGui
SortFilterItemModel* mSortModel; SortFilterItemModel* mSortModel;
ItemModel* mModel; ItemModel* mModel;
int mSelectedItem; int mSelectedItem;
bool mTreatNextOpenAsLoot;
MyGUI::Button* mDisposeCorpseButton; MyGUI::Button* mDisposeCorpseButton;
MyGUI::Button* mTakeButton; MyGUI::Button* mTakeButton;
MyGUI::Button* mCloseButton; MyGUI::Button* mCloseButton;

View file

@ -209,7 +209,7 @@ bool ContainerItemModel::onDropItem(const MWWorld::Ptr &item, int count)
MWWorld::Ptr target = mItemSources[0].first; MWWorld::Ptr target = mItemSources[0].first;
if (target.getTypeName() != typeid(ESM::Container).name()) if (target.getType() != ESM::Container::sRecordId)
return true; return true;
// check container organic flag // check container organic flag

View file

@ -347,8 +347,7 @@ namespace MWGui
{ {
if (!mScrollBar->getVisible()) if (!mScrollBar->getVisible())
return; return;
mScrollBar->setScrollPosition(std::min(static_cast<int>(mScrollBar->getScrollRange()-1), mScrollBar->setScrollPosition(std::clamp<int>(mScrollBar->getScrollPosition() - _rel*0.3, 0, mScrollBar->getScrollRange() - 1));
std::max(0, static_cast<int>(mScrollBar->getScrollPosition() - _rel*0.3))));
onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition()); onScrollbarMoved(mScrollBar, mScrollBar->getScrollPosition());
} }
@ -508,13 +507,13 @@ namespace MWGui
int services = mPtr.getClass().getServices(mPtr); int services = mPtr.getClass().getServices(mPtr);
bool travel = (mPtr.getTypeName() == typeid(ESM::NPC).name() && !mPtr.get<ESM::NPC>()->mBase->getTransport().empty()) bool travel = (mPtr.getType() == ESM::NPC::sRecordId && !mPtr.get<ESM::NPC>()->mBase->getTransport().empty())
|| (mPtr.getTypeName() == typeid(ESM::Creature).name() && !mPtr.get<ESM::Creature>()->mBase->getTransport().empty()); || (mPtr.getType() == ESM::Creature::sRecordId && !mPtr.get<ESM::Creature>()->mBase->getTransport().empty());
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (mPtr.getTypeName() == typeid(ESM::NPC).name()) if (mPtr.getType() == ESM::NPC::sRecordId)
mTopicsList->addItem(gmst.find("sPersuasion")->mValue.getString()); mTopicsList->addItem(gmst.find("sPersuasion")->mValue.getString());
if (services & ESM::NPC::AllItems) if (services & ESM::NPC::AllItems)

View file

@ -109,7 +109,7 @@ namespace MWGui
{ {
mEnchantmentPoints->setCaption(std::to_string(static_cast<int>(mEnchanting.getEnchantPoints(false))) + " / " + std::to_string(mEnchanting.getMaxEnchantValue())); mEnchantmentPoints->setCaption(std::to_string(static_cast<int>(mEnchanting.getEnchantPoints(false))) + " / " + std::to_string(mEnchanting.getMaxEnchantValue()));
mCharge->setCaption(std::to_string(mEnchanting.getGemCharge())); 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())); mCastCost->setCaption(std::to_string(mEnchanting.getEffectiveCastCost()));
mPrice->setCaption(std::to_string(mEnchanting.getEnchantPrice())); mPrice->setCaption(std::to_string(mEnchanting.getEnchantPrice()));

View file

@ -81,7 +81,7 @@ namespace MWGui
, mMinimap(nullptr) , mMinimap(nullptr)
, mCrosshair(nullptr) , mCrosshair(nullptr)
, mCellNameBox(nullptr) , mCellNameBox(nullptr)
, mDrowningFrame(nullptr) , mDrowningBar(nullptr)
, mDrowningFlash(nullptr) , mDrowningFlash(nullptr)
, mHealthManaStaminaBaseLeft(0) , mHealthManaStaminaBaseLeft(0)
, mWeapBoxBaseLeft(0) , mWeapBoxBaseLeft(0)
@ -119,6 +119,7 @@ namespace MWGui
fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked);
//Drowning bar //Drowning bar
getWidget(mDrowningBar, "DrowningBar");
getWidget(mDrowningFrame, "DrowningFrame"); getWidget(mDrowningFrame, "DrowningFrame");
getWidget(mDrowning, "Drowning"); getWidget(mDrowning, "Drowning");
getWidget(mDrowningFlash, "Flash"); getWidget(mDrowningFlash, "Flash");
@ -224,7 +225,7 @@ namespace MWGui
void HUD::setDrowningBarVisible(bool visible) void HUD::setDrowningBarVisible(bool visible)
{ {
mDrowningFrame->setVisible(visible); mDrowningBar->setVisible(visible);
} }
void HUD::onWorldClicked(MyGUI::Widget* _sender) void HUD::onWorldClicked(MyGUI::Widget* _sender)
@ -368,9 +369,6 @@ namespace MWGui
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20)); mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20));
} }
if (mIsDrowning)
mDrowningFlashTheta += dt * osg::PI*2;
mSpellIcons->updateWidgets(mEffectBox, true); mSpellIcons->updateWidgets(mEffectBox, true);
if (mEnemyActorId != -1 && mEnemyHealth->getVisible()) if (mEnemyActorId != -1 && mEnemyHealth->getVisible())
@ -378,8 +376,13 @@ namespace MWGui
updateEnemyHealthBar(); updateEnemyHealthBar();
} }
if (mDrowningBar->getVisible())
mDrowningBar->setPosition(mMainWidget->getWidth()/2 - mDrowningFrame->getWidth()/2, mMainWidget->getTop());
if (mIsDrowning) if (mIsDrowning)
{ {
mDrowningFlashTheta += dt * osg::PI*2;
float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f; float intensity = (cos(mDrowningFlashTheta) + 2.0f) / 3.0f;
mDrowningFlash->setAlpha(intensity); 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(); static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarFade")->mValue.getFloat();
if (fNPCHealthBarFade > 0.f) 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));
} }

View file

@ -73,7 +73,7 @@ namespace MWGui
MyGUI::ImageBox* mCrosshair; MyGUI::ImageBox* mCrosshair;
MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mCellNameBox;
MyGUI::TextBox* mWeaponSpellBox; MyGUI::TextBox* mWeaponSpellBox;
MyGUI::Widget *mDrowningFrame, *mDrowningFlash; MyGUI::Widget *mDrowningBar, *mDrowningFrame, *mDrowningFlash;
// bottom left elements // bottom left elements
int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft; int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft;

View file

@ -46,7 +46,7 @@ namespace
bool isRightHandWeapon(const MWWorld::Ptr& item) bool isRightHandWeapon(const MWWorld::Ptr& item)
{ {
if (item.getClass().getTypeName() != typeid(ESM::Weapon).name()) if (item.getClass().getType() != ESM::Weapon::sRecordId)
return false; return false;
std::vector<int> equipmentSlots = item.getClass().getEquipmentSlots(item).first; std::vector<int> equipmentSlots = item.getClass().getEquipmentSlots(item).first;
return (!equipmentSlots.empty() && equipmentSlots.front() == MWWorld::InventoryStore::Slot_CarriedRight); return (!equipmentSlots.empty() && equipmentSlots.front() == MWWorld::InventoryStore::Slot_CarriedRight);
@ -70,7 +70,7 @@ namespace MWGui
, mTrading(false) , mTrading(false)
, mUpdateTimer(0.f) , mUpdateTimer(0.f)
{ {
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture(), mPreview->getTextureStateSet()));
mPreview->rebuild(); mPreview->rebuild();
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize);
@ -281,7 +281,7 @@ namespace MWGui
// If we unequip weapon during attack, it can lead to unexpected behaviour // If we unequip weapon during attack, it can lead to unexpected behaviour
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPtr)) if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPtr))
{ {
bool isWeapon = item.mBase.getTypeName() == typeid(ESM::Weapon).name(); bool isWeapon = item.mBase.getType() == ESM::Weapon::sRecordId;
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr); MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
if (isWeapon && invStore.isEquipped(item.mBase)) if (isWeapon && invStore.isEquipped(item.mBase))
@ -555,9 +555,9 @@ namespace MWGui
if (!script.empty()) if (!script.empty())
{ {
// Ingredients, books and repair hammers must not have OnPCEquip set to 1 here // Ingredients, books and repair hammers must not have OnPCEquip set to 1 here
const std::string& type = ptr.getTypeName(); auto type = ptr.getType();
bool isBook = type == typeid(ESM::Book).name(); bool isBook = type == ESM::Book::sRecordId;
if (!isBook && type != typeid(ESM::Ingredient).name() && type != typeid(ESM::Repair).name()) if (!isBook && type != ESM::Ingredient::sRecordId && type != ESM::Repair::sRecordId)
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
// Books must have PCSkipEquip set to 1 instead // Books must have PCSkipEquip set to 1 instead
else if (isBook) else if (isBook)
@ -593,8 +593,8 @@ namespace MWGui
useItem(ptr); useItem(ptr);
// If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1 item // If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1 item
if ((ptr.getTypeName() == typeid(ESM::Potion).name() || if ((ptr.getType() == ESM::Potion::sRecordId ||
ptr.getTypeName() == typeid(ESM::Ingredient).name()) ptr.getType() == ESM::Ingredient::sRecordId)
&& mDragAndDrop->mDraggedCount > 1) && mDragAndDrop->mDraggedCount > 1)
{ {
// Item can be provided from other window for example container. // Item can be provided from other window for example container.
@ -704,19 +704,19 @@ namespace MWGui
if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory)) if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))
return; return;
// make sure the object is of a type that can be picked up // make sure the object is of a type that can be picked up
const std::string& type = object.getTypeName(); auto type = object.getType();
if ( (type != typeid(ESM::Apparatus).name()) if ( (type != ESM::Apparatus::sRecordId)
&& (type != typeid(ESM::Armor).name()) && (type != ESM::Armor::sRecordId)
&& (type != typeid(ESM::Book).name()) && (type != ESM::Book::sRecordId)
&& (type != typeid(ESM::Clothing).name()) && (type != ESM::Clothing::sRecordId)
&& (type != typeid(ESM::Ingredient).name()) && (type != ESM::Ingredient::sRecordId)
&& (type != typeid(ESM::Light).name()) && (type != ESM::Light::sRecordId)
&& (type != typeid(ESM::Miscellaneous).name()) && (type != ESM::Miscellaneous::sRecordId)
&& (type != typeid(ESM::Lockpick).name()) && (type != ESM::Lockpick::sRecordId)
&& (type != typeid(ESM::Probe).name()) && (type != ESM::Probe::sRecordId)
&& (type != typeid(ESM::Repair).name()) && (type != ESM::Repair::sRecordId)
&& (type != typeid(ESM::Weapon).name()) && (type != ESM::Weapon::sRecordId)
&& (type != typeid(ESM::Potion).name())) && (type != ESM::Potion::sRecordId))
return; return;
// An object that can be picked up must have a tooltip. // An object that can be picked up must have a tooltip.
@ -809,7 +809,7 @@ namespace MWGui
lastId = item.getCellRef().getRefId(); lastId = item.getCellRef().getRefId();
if (item.getClass().getTypeName() == typeid(ESM::Weapon).name() && if (item.getClass().getType() == ESM::Weapon::sRecordId &&
isRightHandWeapon(item) && isRightHandWeapon(item) &&
item.getClass().canBeEquipped(item, player).first) item.getClass().canBeEquipped(item, player).first)
{ {

View file

@ -313,9 +313,9 @@ struct JournalViewModelImpl : JournalViewModel
for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i)
{ {
Utf8Stream stream (i->first.c_str()); Utf8Stream stream (i->first.c_str());
Utf8Stream::UnicodeChar first = Misc::StringUtils::toLowerUtf8(stream.peek()); Utf8Stream::UnicodeChar first = Utf8Stream::toLowerUtf8(stream.peek());
if (first != Misc::StringUtils::toLowerUtf8(character)) if (first != Utf8Stream::toLowerUtf8(character))
continue; continue;
visitor (i->second.getName()); visitor (i->second.getName());

View file

@ -79,7 +79,7 @@ void KeyboardNavigation::restoreFocus(int mode)
if (found != mKeyFocus.end()) if (found != mKeyFocus.end())
{ {
MyGUI::Widget* w = found->second; 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); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(found->second);
} }
} }
@ -273,7 +273,7 @@ bool KeyboardNavigation::switchFocus(int direction, bool wrap)
if (wrap) if (wrap)
index = (index + keyFocusList.size())%keyFocusList.size(); index = (index + keyFocusList.size())%keyFocusList.size();
else 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]; MyGUI::Widget* next = keyFocusList[index];
int vertdiff = next->getTop() - focus->getTop(); int vertdiff = next->getTop() - focus->getTop();

View file

@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include <osg/Vec2f>
#include "windowpinnablebase.hpp" #include "windowpinnablebase.hpp"
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>

Some files were not shown because too many files have changed in this diff Show more