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.
pull/3225/head
AnyOldName3 3 years ago
commit 8fc09f8c51

@ -8,7 +8,7 @@ env:
BUILD_TYPE: RelWithDebInfo
jobs:
build:
Ubuntu:
runs-on: ubuntu-latest
steps:
@ -26,25 +26,62 @@ jobs:
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
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
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
run: cmake --build . --config ${{env.BUILD_TYPE}} --parallel 3
- name: Install
shell: bash
run: cmake --install .
- name: Test
run: ./openmw_test_suite
# - name: Install
# shell: bash
# run: cmake --install .
- name: Create Artifact
shell: bash
working-directory: install
run: |
ls -laR
7z a ../build_artifact.7z .
# - name: Create Artifact
# shell: bash
# working-directory: install
# run: |
# ls -laR
# 7z a ../build_artifact.7z .
- name: Upload Artifact
uses: actions/upload-artifact@v1
# - 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:
path: ./build_artifact.7z
name: build_artifact.7z
key: ${{ matrix.os }}-${{ env.BUILD_TYPE }}
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

@ -3,14 +3,17 @@
stages:
- build
.Debian_Image:
.Ubuntu_Image:
tags:
- docker
- linux
image: debian:bullseye
image: ubuntu:focal
rules:
- if: $CI_PIPELINE_SOURCE == "push"
.Debian:
extends: .Debian_Image
.Ubuntu:
extends: .Ubuntu_Image
cache:
paths:
- apt-cache/
@ -32,11 +35,12 @@ stages:
- build/install/
Clang_Tidy:
extends: .Debian_Image
extends: .Ubuntu_Image
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
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
script:
- CI/before_script.linux.sh
@ -47,13 +51,17 @@ Clang_Tidy:
CXX: clang++
CI_CLANG_TIDY: 1
timeout: 8h
artifacts:
paths: []
expire_in: 1 minute
Coverity:
extends: .Debian_Image
extends: .Ubuntu_Image
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
- if: $CI_PIPELINE_SOURCE == "schedule"
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
- 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
@ -67,16 +75,20 @@ Coverity:
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="`git describe --tags`"
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
- cat /builds/OpenMW/openmw/cov-int/build-log.txt
variables:
CC: gcc
CXX: g++
artifacts:
paths: []
expire_in: 1 minute
Debian_GCC:
extends: .Debian
Ubuntu_GCC:
extends: .Ubuntu
cache:
key: Debian_GCC.v2
key: Ubuntu_GCC.v2
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
variables:
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.
timeout: 2h
Debian_GCC_tests:
extends: Debian_GCC
Ubuntu_GCC_tests:
extends: Ubuntu_GCC
cache:
key: Debian_GCC_tests.v2
key: Ubuntu_GCC_tests.v2
variables:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
artifacts:
paths: []
expire_in: 1 minute
Debian_GCC_tests_Debug:
extends: Debian_GCC
Ubuntu_GCC_tests_Debug:
extends: Ubuntu_GCC
cache:
key: Debian_GCC_tests_Debug.v1
key: Ubuntu_GCC_tests_Debug.v1
variables:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
CMAKE_BUILD_TYPE: Debug
artifacts:
paths: []
expire_in: 1 minute
Debian_GCC_Static_Deps:
extends: Debian_GCC
Ubuntu_GCC_Static_Deps:
extends: Ubuntu_GCC
cache:
key: Debian_GCC_Static_Deps
key: Ubuntu_GCC_Static_Deps
paths:
- apt-cache/
- ccache/
- build/extern/fetched/
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
variables:
CI_OPENMW_USE_STATIC_DEPS: 1
timeout: 3h
Debian_GCC_Static_Deps_tests:
extends: Debian_GCC_Static_Deps
Ubuntu_GCC_Static_Deps_tests:
extends: Ubuntu_GCC_Static_Deps
cache:
key: Debian_GCC_Static_Deps_tests
key: Ubuntu_GCC_Static_Deps_tests
variables:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
artifacts:
paths: []
expire_in: 1 minute
Debian_Clang:
extends: .Debian
Ubuntu_Clang:
extends: .Ubuntu
before_script:
- 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
cache:
key: Debian_Clang.v2
key: Ubuntu_Clang.v2
variables:
CC: 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.
timeout: 2h
Debian_Clang_tests:
extends: Debian_Clang
Ubuntu_Clang_tests:
extends: Ubuntu_Clang
cache:
key: Debian_Clang_tests.v2
key: Ubuntu_Clang_tests.v2
variables:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
artifacts:
paths: []
expire_in: 1 minute
Debian_Clang_tests_Debug:
extends: Debian_Clang
Ubuntu_Clang_tests_Debug:
extends: Ubuntu_Clang
cache:
key: Debian_Clang_tests_Debug.v1
key: Ubuntu_Clang_tests_Debug.v1
variables:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
CMAKE_BUILD_TYPE: Debug
artifacts:
paths: []
expire_in: 1 minute
.MacOS:
image: macos-11-xcode-12
@ -161,7 +190,7 @@ Debian_Clang_tests_Debug:
stage: build
only:
variables:
- $CI_PROJECT_ID == "7107382"
- $CI_PROJECT_ID == "7107382" && $CI_PIPELINE_SOURCE == "push"
cache:
paths:
- ccache/
@ -174,7 +203,7 @@ Debian_Clang_tests_Debug:
- ccache -z -M "${CCACHE_SIZE}"
- CI/before_script.osx.sh
- cd build; make -j $(sysctl -n hw.logicalcpu) package
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME##*/}_${CI_JOB_ID}.dmg"; done
- ccache -s
artifacts:
paths:
@ -184,26 +213,17 @@ Debian_Clang_tests_Debug:
macOS11_Xcode12:
extends: .MacOS
image: macos-11-xcode-12
allow_failure: true
cache:
key: macOS11_Xcode12.v1
variables:
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
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
targets: "openmw,openmw-iniimporter,openmw-launcher,openmw-wizard"
package: "Engine"
variables: &cs-targets
targets: "openmw-cs,bsatool,esmtool,niftest"
targets: "openmw-cs,bsatool,esmtool,niftest,openmw-essimporter"
package: "CS"
variables: &tests-targets
@ -213,6 +233,8 @@ variables: &tests-targets
.Windows_Ninja_Base:
tags:
- windows
rules:
- if: $CI_PIPELINE_SOURCE == "push"
before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- 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"
# Gitlab can't successfully execute following binaries due to unknown reason
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
artifacts:
paths: []
expire_in: 1 minute
.Windows_MSBuild_Base:
tags:
- windows
rules:
- if: $CI_PIPELINE_SOURCE == "push"
before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
@ -395,6 +422,10 @@ Windows_MSBuild_Engine_Release:
variables:
<<: *engine-targets
config: "Release"
rules:
# run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "schedule"
Windows_MSBuild_Engine_Debug:
extends:
@ -416,6 +447,10 @@ Windows_MSBuild_CS_Release:
variables:
<<: *cs-targets
config: "Release"
rules:
# run this for both pushes and schedules so 'latest successful pipeline for branch' always includes it
- if: $CI_PIPELINE_SOURCE == "push"
- if: $CI_PIPELINE_SOURCE == "schedule"
Windows_MSBuild_CS_Debug:
extends:
@ -439,25 +474,28 @@ Windows_MSBuild_Tests_RelWithDebInfo:
config: "RelWithDebInfo"
# Gitlab can't successfully execute following binaries due to unknown reason
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
artifacts:
paths: []
expire_in: 1 minute
Debian_AndroidNDK_arm64-v8a:
Ubuntu_AndroidNDK_arm64-v8a:
tags:
- linux
image: debian:bullseye
image: psi29a/android-ndk:focal-ndk22
rules:
- if: $CI_PIPELINE_SOURCE == "push"
variables:
CCACHE_SIZE: 3G
cache:
key: Debian_AndroidNDK_arm64-v8a.v3
key: Ubuntu__Focal_AndroidNDK_r22b_arm64-v8a.v1
paths:
- apt-cache/
- ccache/
- build/extern/fetched/
before_script:
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
- echo "deb http://deb.debian.org/debian unstable main contrib" > /etc/apt/sources.list
- echo "google-android-ndk-installer google-android-installers/mirror select https://dl.google.com" | debconf-set-selections
- apt-get update -yq
- apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake ccache curl unzip git build-essential google-android-ndk-installer
- apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake ccache curl unzip git build-essential
stage: build
script:
- export CCACHE_BASEDIR="`pwd`"
@ -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.
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

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

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

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

@ -2,19 +2,29 @@
------
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 #3488: AI combat aiming is too slow
Bug #3514: Editing a reference's position after loading an esp file makes the reference disappear
Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions)
Bug #3792: 1 frame late magicka recalc breaks early scripted magicka reactions to Intelligence change
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
Bug #3855: AI sometimes spams defensive spells
Bug #3905: Great House Dagoth issues
Bug #4203: Resurrecting an actor should close the loot GUI
Bug #4376: Moved actors don't respawn in their original cells
Bug #4389: NPC's lips do not move if his head model has the NiBSAnimationNode root node
Bug #4602: Robert's Bodies: crash inside createInstance()
Bug #4700: Editor: Incorrect command implementation
Bug #4744: Invisible particles must still be processed
Bug #5088: Sky abruptly changes direction during certain weather transitions
Bug #5100: Persuasion doesn't always clamp the resulting disposition
Bug #5120: Scripted object spawning updates physics system
Bug #5207: Loose summons can be present in scene
Bug #5377: console does not appear after using menutest in inventory
Bug #5379: Wandering NPCs falling through cantons
Bug #5394: Windows snapping no longer works
Bug #5434: Pinned windows shouldn't cover breath progress bar
Bug #5453: Magic effect VFX are offset for creatures
Bug #5483: AutoCalc flag is not used to calculate spells cost
Bug #5508: Engine binary links to Qt without using it
@ -25,6 +35,8 @@
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 #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 #6051: NaN water height in ESM file is not handled gracefully
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 #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 #6168: Weather particles flicker for a frame at start of storms
Bug #6172: Some creatures can't open doors
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
Bug #6177: Followers of player follower stop following after waiting for a day
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
Bug #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 #6282: Laura craft doesn't follow the player character
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 #6291: Can't pickup the dead mage's journal from the mysterious hunter mod
Bug #6302: Teleporting disabled actor breaks its disabled state
Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken
Bug #6321: Arrow enchantments should always be applied to the target
Bug #6322: Total sold/cost should reset to 0 when there are no items offered
Bug #6323: Wyrmhaven: Alboin doesn't follower the player character out of his house
Bug #6324: Special Slave Companions: Can't buy the slave companions
Bug #6326: Detect Enchantment/Key should detect items in unresolved containers
Bug #6327: Blocking roots the character in place
Bug #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 #1465: "Reset" argument for AI functions
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
Feature #2780: A way to see current OpenMW version in the console
Feature #3616: Allow Zoom levels on the World Map
@ -66,15 +104,20 @@
Feature #5996: Support Lua scripts in OpenMW
Feature #6017: Separate persistent and temporary cell references when saving
Feature #6032: Reverse-z depth buffer
Feature #6078: First person should not clear depth buffer
Feature #6128: Soft Particles
Feature #6161: Refactor Sky to use shaders and GLES/GL3 friendly
Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
Feature #6199: Support FBO Rendering
Feature #6248: Embedded error marker mesh
Feature #6249: Alpha testing support for Collada
Feature #6251: OpenMW-CS: Set instance movement based on camera zoom
Feature #6288: Preserve the "blocked" record flag for referenceable objects.
Feature #6380: Commas are treated as whitespace in vanilla
Feature #6419: Topics shouldn't be greyed out if they can produce another topic reference
Task #6201: Remove the "Note: No relevant classes found. No output generated" warnings
Task #6264: Remove the old classes in animation.cpp
0.47.0
------
@ -208,6 +251,8 @@
Bug #6043: Actor can have torch missing when torch animation is played
Bug #6047: Mouse bindings can be triggered during save loading
Bug #6136: Game freezes when NPCs try to open doors that are about to be closed
Bug #6142: Groundcover plugins change cells flags
Bug #6276: Deleted groundcover instances are not deleted in game
Bug #6294: Game crashes with empty pathgrid
Feature #390: 3rd person look "over the shoulder"
Feature #832: OpenMW-CS: Handle deleted references

@ -1,4 +1,4 @@
#!/bin/sh -ex
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20201230.zip -o ~/openmw-android-deps.zip
unzip -o ~/openmw-android-deps -d /usr/lib/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null
curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/android/openmw-android-deps-20211114.zip -o ~/openmw-android-deps.zip
unzip -o ~/openmw-android-deps -d /android-ndk-r22/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr > /dev/null

@ -7,9 +7,10 @@ mkdir -p build
cd build
cmake \
-DCMAKE_TOOLCHAIN_FILE=/usr/lib/android-sdk/ndk-bundle/build/cmake/android.toolchain.cmake \
-DCMAKE_TOOLCHAIN_FILE=/android-ndk-r22/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-21 \
-DANDROID_LD=deprecated \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX=install \
@ -22,6 +23,5 @@ cmake \
-DBUILD_OPENCS=0 \
-DBUILD_WIZARD=0 \
-DOPENMW_USE_SYSTEM_MYGUI=OFF \
-DOPENMW_USE_SYSTEM_OSG=OFF \
-DOPENMW_USE_SYSTEM_BULLET=OFF \
-DOPENMW_USE_SYSTEM_SQLITE3=OFF \
..

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

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

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

@ -27,7 +27,7 @@ declare -rA GROUPED_DEPS=(
# TODO: add librecastnavigation-dev when debian is ready
# 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"
[clang-tidy]="clang-tidy"

@ -149,6 +149,8 @@ else()
endif()
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_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)
endif()
if(MSVC)
add_compile_options("/utf-8")
endif()
# Dependencies
find_package(OpenGL REQUIRED)
@ -408,7 +414,8 @@ else(USE_LUAJIT)
endif(USE_LUAJIT)
# C++ library binding to Lua
set(SOL_INCLUDE_DIRS ${OpenMW_SOURCE_DIR}/extern/sol3.2.2 ${OpenMW_SOURCE_DIR}/extern/sol_config)
set(SOL_INCLUDE_DIR ${OpenMW_SOURCE_DIR}/extern/sol3.2.2)
set(SOL_CONFIG_DIR ${OpenMW_SOURCE_DIR}/extern/sol_config)
include_directories(
BEFORE SYSTEM
@ -420,7 +427,8 @@ include_directories(
${OPENGL_INCLUDE_DIR}
${BULLET_INCLUDE_DIRS}
${LUA_INCLUDE_DIR}
${SOL_INCLUDE_DIRS}
${SOL_INCLUDE_DIR}
${SOL_CONFIG_DIR}
)
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
@ -437,9 +445,8 @@ if (APPLE)
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
endif (APPLE)
if (NOT APPLE)
set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR})
set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})
if (NOT APPLE) # this is modified for macOS use later in "apps/open[mw|cs]/CMakeLists.txt"
set(OPENMW_RESOURCES_ROOT ${OpenMW_BINARY_DIR})
endif ()
add_subdirectory(files/)
@ -703,8 +710,12 @@ if (WIN32)
endif()
if (BUILD_OPENMW AND APPLE)
# Without these flags LuaJit crashes on startup on OSX
set_target_properties(openmw PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000")
if (USE_LUAJIT)
# Without these flags LuaJit crashes on startup on OSX
set_target_properties(openmw PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000")
endif(USE_LUAJIT)
target_compile_definitions(components PRIVATE GL_SILENCE_DEPRECATION=1)
target_compile_definitions(openmw PRIVATE GL_SILENCE_DEPRECATION=1)
endif()
# Apple bundling

@ -1,8 +1,6 @@
OpenMW
======
[![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) [![pipeline status](https://gitlab.com/OpenMW/openmw/badges/master/pipeline.svg)](https://gitlab.com/OpenMW/openmw/commits/master)
OpenMW is an open-source game engine that supports playing Morrowind by Bethesda Softworks. You need to own the game for OpenMW to play Morrowind.
OpenMW also comes with OpenMW-CS, a replacement for Bethesda's Construction Set.

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

@ -233,7 +233,17 @@ int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
std::string extractPath = info.extractfile;
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 << "In archive: " << info.filename << std::endl;
@ -260,9 +270,6 @@ int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
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);
// 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
// (inefficient because getFile iter on the list again)
Files::IStreamPtr data = bsa->getFile(file.name());
Files::IStreamPtr data = bsa->getFile(&file);
bfs::ofstream out(target, std::ios::binary);
// Write the file to disk

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

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

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

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

@ -41,8 +41,12 @@ namespace Launcher
* @param filePaths the file paths of the content files to be examined
*/
void loadCellsForAutocomplete(QStringList filePaths);
void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
static void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
static void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
static void loadSettingInt(QComboBox *comboBox, const std::string& setting, const std::string& group);
static void saveSettingInt(QComboBox *comboBox, const std::string& setting, const std::string& group);
static void loadSettingInt(QSpinBox *spinBox, const std::string& setting, const std::string& group);
static void saveSettingInt(QSpinBox *spinBox, const std::string& setting, const std::string& group);
};
}
#endif

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

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

@ -146,7 +146,6 @@ void Launcher::MainDialog::createPages()
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
// Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread
connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);
}
Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()

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

@ -2,8 +2,8 @@
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <components/misc/stringops.hpp>
#include <components/nif/niffile.hpp>
#include <components/files/constrainedfilestream.hpp>
#include <components/vfs/manager.hpp>
@ -18,18 +18,10 @@ namespace bpo = boost::program_options;
namespace bfs = boost::filesystem;
///See if the file has the named extension
bool hasExtension(std::string filename, std::string extensionToFind)
bool hasExtension(std::string filename, std::string extensionToFind)
{
std::string extension = filename.substr(filename.find_last_of('.')+1);
//Convert strings to lower case for comparison
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower);
if(extension == extensionToFind)
return true;
else
return false;
return Misc::StringUtils::ciEqual(extension, extensionToFind);
}
///See if the file has the "nif" extension.

@ -183,8 +183,7 @@ if(APPLE)
set(OPENCS_BUNDLE_NAME "OpenMW-CS")
set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources")
set(OPENMW_MYGUI_FILES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
set(OPENMW_RESOURCES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)

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

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

@ -395,12 +395,12 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW
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;
}
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;
}

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

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

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

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

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

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

@ -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));
}
bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const
{
return mData.getJournals().searchId (name)!=-1;
}
void CSMWorld::ScriptContext::invalidateIds()
{
mIdsUpdated = false;

@ -39,9 +39,6 @@ namespace CSMWorld
bool isId (const std::string& name) const override;
///< 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 clear();

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

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

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

@ -308,7 +308,7 @@ osg::ref_ptr<osg::Node> CSVRender::Object::makeRotateMarker (int axis)
const float OuterRadius = InnerRadius + MarkerShaftWidth;
const float SegmentDistance = 100.f;
const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * osg::PI / SegmentDistance)));
const size_t SegmentCount = std::clamp<int>(OuterRadius * 2 * osg::PI / SegmentDistance, 24, 64);
const size_t VerticesPerSegment = 4;
const size_t IndicesPerSegment = 24;

@ -16,11 +16,13 @@
#include "worldspacewidget.hpp"
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();
mSelectionNode = new osg::Group();
mSelectionNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mSelectionNode->getOrCreateStateSet()->setRenderBinDetails(11, "RenderBin");
mSelectionNode->addChild(mGeometry);
activate();
@ -43,19 +45,24 @@ void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, in
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()
@ -102,26 +109,16 @@ void CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec
float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x));
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(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(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y) + 2));
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(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1) + 2));
}
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(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)));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1)));
vertices->push_back(pointXY);
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 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(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y + 1), calculateLandHeight(x1+i, 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)));
}
}
@ -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 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(drawCurrentX, CSMWorld::CellCoordinates::textureGlobalYToWorldCoords(y), calculateLandHeight(x1+i, 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)));
}
}
@ -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 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), drawCurrentY, calculateLandHeight(x2, y1+i)+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)));
}
}
@ -190,103 +187,60 @@ 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 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), drawCurrentY, calculateLandHeight(x1, y1+i)+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)));
}
}
}
}
}
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)
{
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);
const 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)
switch (selectionMethod)
{
const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
case SelectionMethod::OnlySelect:
break;
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
{
const auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
if (iterTemp == mTemporarySelection.end())
{
mSelection.erase(iter);
if (iter == mSelection.end())
{
mSelection.emplace_back(localPos);
}
else
{
mSelection.erase(iter);
}
}
mTemporarySelection.emplace_back(localPos);
break;
default: break;
}
}
}
else
{
mDraggedOperationFlag = false;
mTemporarySelection.clear();
default:
break;
}
}
update();
@ -336,11 +290,9 @@ int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global ver
else if (isLandLoaded(CSMWorld::CellCoordinates::generateId(cellX, cellY)))
{
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);
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
return mPointer[localY*ESM::Land::LAND_SIZE + localX];
const ESM::Land::LandData* landData = document.getData().getLand().getRecord(cellId).get().getLandData(ESM::Land::DATA_VHGT);
return landData->mHeights[localY*ESM::Land::LAND_SIZE + localX];
}
return landHeight;

@ -44,9 +44,10 @@ namespace CSVRender
~TerrainSelection();
void onlySelect(const std::vector<std::pair<int, int>> &localPositions);
void addSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress);
void removeSelect(const std::vector<std::pair<int, int>>& localPositions, bool toggleInProgress);
void toggleSelect(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);
void toggleSelect(const std::vector<std::pair<int, int>> &localPositions);
void clearTemporarySelection();
void activate();
void deactivate();
@ -64,7 +65,7 @@ namespace CSVRender
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);
@ -73,7 +74,7 @@ namespace CSVRender
bool noLandLoaded(const std::string& cellId);
bool isLandLoaded(const std::string& cellId);
osg::Group* mParentNode;
WorldspaceWidget *mWorldspaceWidget;
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
@ -81,7 +82,6 @@ namespace CSVRender
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>> mTemporarySelection; // Used during toggle to compare the most recent drag operation
bool mDraggedOperationFlag; //true during drag operation, false when click-operation
TerrainSelectionType mSelectionType;
};
}

@ -106,16 +106,6 @@ void CSVRender::TerrainShapeMode::primaryEditPressed(const WorldspaceHitResult&
editTerrainShapeGrid(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), true);
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();
}
@ -124,7 +114,8 @@ void CSVRender::TerrainShapeMode::primarySelectPressed(const WorldspaceHitResult
{
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)
{
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;
return false;
}
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0, true);
return false;
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 0);
return true;
}
bool CSVRender::TerrainShapeMode::secondarySelectStartDrag (const QPoint& pos)
@ -180,8 +172,8 @@ bool CSVRender::TerrainShapeMode::secondarySelectStartDrag (const QPoint& pos)
mDragMode = InteractionType_None;
return false;
}
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1, true);
return false;
selectTerrainShapes(CSMWorld::CellCoordinates::toVertexCoords(hit.worldPos), 1);
return true;
}
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)
{
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)
{
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();
clearTransientEdits();
}
if (mDragMode == InteractionType_PrimarySelect || mDragMode == InteractionType_SecondarySelect)
{
mTerrainShapeSelection->clearTemporarySelection();
}
}
void CSVRender::TerrainShapeMode::dragAborted()
{
clearTransientEdits();
mDragMode = InteractionType_None;
}
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;
std::vector<std::pair<int, int>> selections;
@ -1136,11 +1133,11 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair<int, int>&
if (selectAction == "Select only")
mTerrainShapeSelection->onlySelect(selections);
else if (selectAction == "Add to selection")
mTerrainShapeSelection->addSelect(selections, dragOperation);
mTerrainShapeSelection->addSelect(selections);
else if (selectAction == "Remove from selection")
mTerrainShapeSelection->removeSelect(selections, dragOperation);
mTerrainShapeSelection->removeSelect(selections);
else if (selectAction == "Invert selection")
mTerrainShapeSelection->toggleSelect(selections, dragOperation);
mTerrainShapeSelection->toggleSelect(selections);
}
void CSVRender::TerrainShapeMode::pushEditToCommand(const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,

@ -142,7 +142,7 @@ namespace CSVRender
void handleSelection(int globalSelectionX, int globalSelectionY, std::vector<std::pair<int, int>>* selections);
/// 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
void pushEditToCommand (const CSMWorld::LandHeightsColumn::DataType& newLandGrid, CSMDoc::Document& document,

@ -48,15 +48,14 @@ namespace CSVRender
float TerrainStorage::getSumOfAlteredAndTrueHeight(int cellX, int cellY, int inCellX, int inCellY)
{
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)

@ -129,7 +129,8 @@ void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResu
{
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)
{
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;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);
return false;
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0);
return true;
}
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
@ -201,8 +203,8 @@ bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
mDragMode = InteractionType_None;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);
return false;
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1);
return true;
}
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)
{
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)
{
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;
}
}
mTerrainTextureSelection->clearTemporarySelection();
}
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;
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);
if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation);
std::string selectAction;
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,

@ -95,7 +95,7 @@ namespace CSVRender
bool isInCellSelection(int globalSelectionX, int globalSelectionY);
/// \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
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,

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

@ -19,7 +19,7 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER})
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
bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover postprocessor
@ -27,7 +27,7 @@ add_openmw_dir (mwrender
add_openmw_dir (mwinput
actions actionmanager bindingsmanager controllermanager controlswitch
inputmanagerimp mousemanager keyboardmanager sdlmappings sensormanager
inputmanagerimp mousemanager keyboardmanager sensormanager
)
add_openmw_dir (mwgui
@ -72,9 +72,9 @@ add_openmw_dir (mwworld
containerstore actiontalk actiontake manualref player cellvisitors failedaction
cells localscripts customdata inventorystore ptr actionopen actionread actionharvest
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
cellpreloader datetimemanager
cellpreloader datetimemanager groundcoverstore magiceffects
)
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
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
character actors objects aistate trading weaponpriority spellpriority weapontype spellutil
spellabsorption spelleffects
spelleffects
)
add_openmw_dir (mwstate
@ -153,9 +153,12 @@ target_link_libraries(openmw
"osg-ffmpeg-videoplayer"
"oics"
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)
target_link_libraries(openmw EGL android log z)
endif (ANDROID)
@ -176,8 +179,7 @@ endif()
if(APPLE)
set(BUNDLE_RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources")
set(OPENMW_MYGUI_FILES_ROOT ${BUNDLE_RESOURCES_DIR})
set(OPENMW_SHADERS_ROOT ${BUNDLE_RESOURCES_DIR})
set(OPENMW_RESOURCES_ROOT ${BUNDLE_RESOURCES_DIR})
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)

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

@ -8,6 +8,8 @@
#include <boost/filesystem/fstream.hpp>
#include <osg/Version>
#include <osgViewer/ViewerEventHandlers>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
@ -37,11 +39,10 @@
#include <components/version/version.hpp>
#include <components/detournavigator/navigator.hpp>
#include <components/misc/frameratelimiter.hpp>
#include <components/sceneutil/screencapture.hpp>
#include <components/sceneutil/depth.hpp>
#include "mwinput/inputmanagerimp.hpp"
@ -293,6 +294,10 @@ bool OMW::Engine::frame(float frametime)
// Main menu opened? Then scripts are also paused.
bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu);
// Should be called after input manager update and before any change to the game world.
// It applies to the game world queued changes from the previous frame.
mLuaManager->synchronizedUpdate(mEnvironment.getWindowManager()->isGuiMode(), frametime);
// update game state
{
ScopedProfile<UserStatsType::State> profile(frameStart, frameNumber, *timer, *stats);
@ -495,11 +500,6 @@ void OMW::Engine::addGroundcoverFile(const std::string& 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)
{
mSkipMenu = skipMenu;
@ -551,6 +551,10 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
if(fullscreen)
flags |= SDL_WINDOW_FULLSCREEN;
// Allows for Windows snapping features to properly work in borderless window
SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "1");
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1");
if (!windowBorder)
flags |= SDL_WINDOW_BORDERLESS;
@ -674,7 +678,7 @@ void OMW::Engine::setWindowIcon()
void OMW::Engine::prepareEngine (Settings::Manager & settings)
{
mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles));
createWindow(settings);
@ -714,7 +718,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mViewer->addEventHandler(mScreenCaptureHandler);
mLuaManager = new MWLua::LuaManager(mVFS.get(), mLuaScriptListFiles);
mLuaManager = new MWLua::LuaManager(mVFS.get());
mEnvironment.setLuaManager(mLuaManager);
// 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);
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f);
bool enableReverseZ = false;
if (Settings::Manager::getBool("reverse z", "Camera"))
{
if (exts && exts->isClipControlSupported)
{
enableReverseZ = true;
Log(Debug::Info) << "Using reverse-z depth buffer";
}
else
Log(Debug::Warning) << "GL_ARB_clip_control not supported: disabling reverse-z depth buffer";
}
else
Log(Debug::Info) << "Using standard depth buffer";
SceneUtil::AutoDepth::setReversed(enableReverseZ);
#if OSG_VERSION_LESS_THAN(3, 6, 6)
// hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028
if (exts)
exts->glRenderbufferStorageMultisampleCoverageNV = nullptr;
#endif
std::string myguiResources = (mResDir / "mygui").string();
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
@ -869,7 +895,6 @@ public:
}
else
update();
mEngine->mLuaManager->applyQueuedChanges();
};
void join()
@ -1064,8 +1089,6 @@ void OMW::Engine::go()
// Save user settings
settings.saveUser(settingspath);
mViewer->stopThreading();
Log(Debug::Info) << "Quitting peacefully.";
}

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

@ -123,9 +123,11 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.addGroundcoverFile(file);
}
StringsVector luaScriptLists = variables["lua-scripts"].as<StringsVector>();
for (const auto& file : luaScriptLists)
engine.addLuaScriptListFile(file);
if (variables.count("lua-scripts"))
{
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
engine.setCell(variables["start"].as<std::string>());

@ -49,8 +49,8 @@ namespace MWBase
virtual void setGamepadGuiCursorEnabled(bool enabled) = 0;
virtual void setAttemptJump(bool jumping) = 0;
virtual void toggleControlSwitch (const std::string& sw, bool value) = 0;
virtual bool getControlSwitch (const std::string& sw) = 0;
virtual void toggleControlSwitch(std::string_view sw, bool value) = 0;
virtual bool getControlSwitch(std::string_view sw) = 0;
virtual std::string getActionDescription (int action) const = 0;
virtual std::string getActionKeyBindingName (int action) const = 0;
@ -58,8 +58,8 @@ namespace MWBase
virtual bool actionIsActive(int action) const = 0;
virtual float getActionValue(int action) const = 0; // returns value in range [0, 1]
virtual bool isControllerButtonPressed(SDL_GameControllerButton button) const = 0;
virtual float getControllerAxisValue(SDL_GameControllerAxis axis) const = 0; // returns value in range [-1, 1]
virtual uint32_t getMouseButtonsState() const = 0;
virtual int getMouseMoveX() const = 0;
virtual int getMouseMoveY() const = 0;

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

@ -112,6 +112,9 @@ namespace MWBase
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
/// Removes an actor and its allies from combat with the actor's targets.
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;
enum OffenseType
{
OT_Theft, // Taking items owned by an NPC or a faction you are not a member of

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

@ -62,6 +62,7 @@ namespace MWPhysics
namespace MWRender
{
class Animation;
class Camera;
}
namespace MWMechanics
@ -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;
///< @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
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 MWRender::Camera* getCamera() = 0;
virtual void togglePOV(bool force = false) = 0;
virtual bool isFirstPerson() const = 0;
virtual bool isPreviewModeEnabled() const = 0;
virtual void togglePreviewMode(bool enable) = 0;
virtual bool toggleVanityMode(bool enable) = 0;
virtual void allowVanityMode(bool allow) = 0;
virtual bool vanityRotateCamera(float * rot) = 0;
virtual void adjustCameraDistance(float dist) = 0;
virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0;
virtual void disableDeferredPreviewRotation() = 0;

@ -89,7 +89,7 @@ namespace MWClass
{
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

@ -3,6 +3,8 @@
#include "../mwworld/class.hpp"
#include <components/esm/loadmgef.hpp>
namespace ESM
{
struct GameSetting;
@ -17,6 +19,15 @@ namespace MWClass
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:
~Actor() override = default;

@ -69,7 +69,7 @@ namespace MWClass
{
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

@ -163,7 +163,7 @@ namespace MWClass
{
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
@ -322,7 +322,7 @@ namespace MWClass
if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft)
{
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>();
if (MWMechanics::getWeaponType(ref->mBase->mData.mType)->mFlags & ESM::WeaponType::TwoHanded)

@ -36,7 +36,7 @@ namespace MWClass
{
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

@ -85,7 +85,7 @@ namespace MWClass
{
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

@ -121,7 +121,7 @@ namespace MWClass
{
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

@ -242,7 +242,7 @@ namespace MWClass
{
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

@ -82,12 +82,13 @@ namespace MWClass
const Creature::GMST& Creature::getGmst()
{
static GMST gmst;
static bool inited = false;
if (!inited)
static const GMST staticGmst = []
{
GMST gmst;
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature");
gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature");
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
@ -101,16 +102,20 @@ namespace MWClass
gmst.fKnockDownMult = store.find("fKnockDownMult");
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
inited = true;
}
return gmst;
return gmst;
} ();
return staticGmst;
}
void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const
{
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>();
@ -154,10 +159,7 @@ namespace MWClass
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
data->mCreatureStats.setNeedRecalcDynamicStats(false);
// store
ptr.getRefData().setCustomData(std::move(data));
resetter.mPtr = {};
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
@ -238,7 +240,7 @@ namespace MWClass
{
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
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;
}
@ -497,7 +499,7 @@ namespace MWClass
{
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
@ -891,12 +893,8 @@ namespace MWClass
float Creature::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
const MWMechanics::MagicEffects& mageffects = stats.getMagicEffects();
return 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());
return getSwimSpeedImpl(ptr, getGmst(), mageffects, getWalkSpeed(ptr));
}
}

@ -5,6 +5,7 @@
#include "../mwmechanics/levelledlist.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/customdata.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
{
return "";
@ -45,7 +64,13 @@ namespace MWClass
if (customData.mSpawn)
return;
MWWorld::Ptr creature = (customData.mSpawnActorId == -1) ? MWWorld::Ptr() : MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
MWWorld::Ptr creature;
if(customData.mSpawnActorId != -1)
{
creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
if(creature.isEmpty())
creature = ptr.getCell()->getMovedActor(customData.mSpawnActorId);
}
if (!creature.isEmpty())
{
const MWMechanics::CreatureStats& creatureStats = creature.getClass().getCreatureStats(creature);
@ -70,7 +95,7 @@ namespace MWClass
{
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

@ -32,6 +32,10 @@ namespace MWClass
///< Write additional state from \a ptr into \a state.
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;
};
}

@ -264,7 +264,7 @@ namespace MWClass
{
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

@ -81,7 +81,7 @@ namespace MWClass
{
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

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

@ -124,7 +124,7 @@ namespace MWClass
{
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

@ -80,7 +80,7 @@ namespace MWClass
{
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

@ -106,7 +106,7 @@ namespace MWClass
{
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

@ -266,10 +266,10 @@ namespace MWClass
const Npc::GMST& Npc::getGmst()
{
static GMST gmst;
static bool inited = false;
if(!inited)
static const GMST staticGmst = []
{
GMST gmst;
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
@ -294,16 +294,20 @@ namespace MWClass
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
gmst.fCombatArmorMinMult = store.find("fCombatArmorMinMult");
inited = true;
}
return gmst;
return gmst;
} ();
return staticGmst;
}
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
{
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>();
@ -334,8 +338,6 @@ namespace MWClass
data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel);
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition);
data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);
data->mNpcStats.setNeedRecalcDynamicStats(false);
}
else
{
@ -351,7 +353,7 @@ namespace MWClass
autoCalculateAttributes(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats, ptr, spellsInitialised);
data->mNpcStats.setNeedRecalcDynamicStats(true);
recalculate = true;
}
// Persistent actors with 0 health do not play death animation
@ -387,7 +389,9 @@ namespace MWClass
data->mNpcStats.setGoldPool(gold);
// store
ptr.getRefData().setCustomData(std::move(data));
resetter.mPtr = {};
if(recalculate)
data->mNpcStats.recalculateMagicka();
// inventory
// 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())
{
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;
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;
parts = armor->mParts.mParts;
@ -543,7 +547,7 @@ namespace MWClass
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
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();
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
@ -766,7 +770,7 @@ namespace MWClass
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
bool hasArmor = !armor.isEmpty() && armor.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 (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft)
{
@ -778,7 +782,7 @@ namespace MWClass
if (armorslot != inv.end())
{
armor = *armorslot;
hasArmor = !armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name();
hasArmor = !armor.isEmpty() && armor.getType() == ESM::Armor::sRecordId;
}
}
if (hasArmor)
@ -1036,7 +1040,7 @@ namespace MWClass
void Npc::registerSelf()
{
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
@ -1135,7 +1139,7 @@ namespace MWClass
for(int i = 0;i < MWWorld::InventoryStore::Slots;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
ratings[i] = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
@ -1232,7 +1236,7 @@ namespace MWClass
const MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
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";
switch(boots->getClass().getEquipmentSkill(*boots))
@ -1476,7 +1480,6 @@ namespace MWClass
float Npc::getSwimSpeed(const MWWorld::Ptr& ptr) const
{
const GMST& gmst = getGmst();
const MWBase::World* world = MWBase::Environment::get().getWorld();
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
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)
&& (inair || MWBase::Environment::get().getMechanicsManager()->isRunning(ptr));
float swimSpeed;
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;
return getSwimSpeedImpl(ptr, getGmst(), mageffects, running ? getRunSpeed(ptr) : getWalkSpeed(ptr));
}
}

@ -74,7 +74,7 @@ namespace MWClass
{
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

@ -80,7 +80,7 @@ namespace MWClass
{
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

@ -69,7 +69,7 @@ namespace MWClass
{
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

@ -59,7 +59,7 @@ namespace MWClass
{
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

@ -125,7 +125,7 @@ namespace MWClass
{
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

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

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

@ -23,7 +23,7 @@
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
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 isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name());
bool isCreature = (mActor.getType() != ESM::NPC::sRecordId);
if (isCreature)
return true;
@ -207,7 +207,7 @@ bool MWDialogue::Filter::testFunctionLocal(const MWDialogue::SelectWrapper& sele
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.
return true;
@ -452,7 +452,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
{
if (target.getClass().isNpc() && target.getClass().getNpcStats(target).isWerewolf())
return 2;
if (target.getTypeName() == typeid(ESM::Creature).name())
if (target.getType() == ESM::Creature::sRecordId)
return 1;
}
}

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

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

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

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

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

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

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

@ -109,7 +109,7 @@ namespace MWGui
{
mEnchantmentPoints->setCaption(std::to_string(static_cast<int>(mEnchanting.getEnchantPoints(false))) + " / " + std::to_string(mEnchanting.getMaxEnchantValue()));
mCharge->setCaption(std::to_string(mEnchanting.getGemCharge()));
mSuccessChance->setCaption(std::to_string(std::max(0, std::min(100, mEnchanting.getEnchantChance()))));
mSuccessChance->setCaption(std::to_string(std::clamp(mEnchanting.getEnchantChance(), 0, 100)));
mCastCost->setCaption(std::to_string(mEnchanting.getEffectiveCastCost()));
mPrice->setCaption(std::to_string(mEnchanting.getEnchantPrice()));

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

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

@ -46,7 +46,7 @@ namespace
bool isRightHandWeapon(const MWWorld::Ptr& item)
{
if (item.getClass().getTypeName() != typeid(ESM::Weapon).name())
if (item.getClass().getType() != ESM::Weapon::sRecordId)
return false;
std::vector<int> equipmentSlots = item.getClass().getEquipmentSlots(item).first;
return (!equipmentSlots.empty() && equipmentSlots.front() == MWWorld::InventoryStore::Slot_CarriedRight);
@ -70,7 +70,7 @@ namespace MWGui
, mTrading(false)
, mUpdateTimer(0.f)
{
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture(), mPreview->getTextureStateSet()));
mPreview->rebuild();
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 (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);
if (isWeapon && invStore.isEquipped(item.mBase))
@ -555,9 +555,9 @@ namespace MWGui
if (!script.empty())
{
// Ingredients, books and repair hammers must not have OnPCEquip set to 1 here
const std::string& type = ptr.getTypeName();
bool isBook = type == typeid(ESM::Book).name();
if (!isBook && type != typeid(ESM::Ingredient).name() && type != typeid(ESM::Repair).name())
auto type = ptr.getType();
bool isBook = type == ESM::Book::sRecordId;
if (!isBook && type != ESM::Ingredient::sRecordId && type != ESM::Repair::sRecordId)
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
// Books must have PCSkipEquip set to 1 instead
else if (isBook)
@ -593,8 +593,8 @@ namespace MWGui
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 ((ptr.getTypeName() == typeid(ESM::Potion).name() ||
ptr.getTypeName() == typeid(ESM::Ingredient).name())
if ((ptr.getType() == ESM::Potion::sRecordId ||
ptr.getType() == ESM::Ingredient::sRecordId)
&& mDragAndDrop->mDraggedCount > 1)
{
// Item can be provided from other window for example container.
@ -704,19 +704,19 @@ namespace MWGui
if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))
return;
// make sure the object is of a type that can be picked up
const std::string& type = object.getTypeName();
if ( (type != typeid(ESM::Apparatus).name())
&& (type != typeid(ESM::Armor).name())
&& (type != typeid(ESM::Book).name())
&& (type != typeid(ESM::Clothing).name())
&& (type != typeid(ESM::Ingredient).name())
&& (type != typeid(ESM::Light).name())
&& (type != typeid(ESM::Miscellaneous).name())
&& (type != typeid(ESM::Lockpick).name())
&& (type != typeid(ESM::Probe).name())
&& (type != typeid(ESM::Repair).name())
&& (type != typeid(ESM::Weapon).name())
&& (type != typeid(ESM::Potion).name()))
auto type = object.getType();
if ( (type != ESM::Apparatus::sRecordId)
&& (type != ESM::Armor::sRecordId)
&& (type != ESM::Book::sRecordId)
&& (type != ESM::Clothing::sRecordId)
&& (type != ESM::Ingredient::sRecordId)
&& (type != ESM::Light::sRecordId)
&& (type != ESM::Miscellaneous::sRecordId)
&& (type != ESM::Lockpick::sRecordId)
&& (type != ESM::Probe::sRecordId)
&& (type != ESM::Repair::sRecordId)
&& (type != ESM::Weapon::sRecordId)
&& (type != ESM::Potion::sRecordId))
return;
// An object that can be picked up must have a tooltip.
@ -809,7 +809,7 @@ namespace MWGui
lastId = item.getCellRef().getRefId();
if (item.getClass().getTypeName() == typeid(ESM::Weapon).name() &&
if (item.getClass().getType() == ESM::Weapon::sRecordId &&
isRightHandWeapon(item) &&
item.getClass().canBeEquipped(item, player).first)
{

@ -313,9 +313,9 @@ struct JournalViewModelImpl : JournalViewModel
for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i)
{
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;
visitor (i->second.getName());

@ -79,7 +79,7 @@ void KeyboardNavigation::restoreFocus(int mode)
if (found != mKeyFocus.end())
{
MyGUI::Widget* w = found->second;
if (w && w->getVisible() && w->getEnabled())
if (w && w->getVisible() && w->getEnabled() && w->getInheritedVisible() && w->getInheritedEnabled())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(found->second);
}
}
@ -273,7 +273,7 @@ bool KeyboardNavigation::switchFocus(int direction, bool wrap)
if (wrap)
index = (index + keyFocusList.size())%keyFocusList.size();
else
index = std::min(std::max(0, index), static_cast<int>(keyFocusList.size())-1);
index = std::clamp<int>(index, 0, keyFocusList.size() - 1);
MyGUI::Widget* next = keyFocusList[index];
int vertdiff = next->getTop() - focus->getTop();

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

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

Loading…
Cancel
Save