Merge remote-tracking branch 'remotes/origin/master' into openmw-vr

pull/615/head
madsbuvi 4 years ago
commit f4e36f4be5

@ -16,6 +16,8 @@ stages:
- apt-cache/
- ccache/
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE != "schedule"'
script:
- export CCACHE_BASEDIR="`pwd`"
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
@ -30,6 +32,30 @@ stages:
paths:
- build/install/
Coverity:
extends: .Debian_Image
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
before_script:
- 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
script:
- CI/before_script.linux.sh
# Add more than just `openmw` once we can build everything under 3h
- cov-analysis-linux64-*/bin/cov-build --dir cov-int cmake --build build -- -j $(nproc) openmw
after_script:
- tar cfz cov-int.tar.gz cov-int
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="`git describe --tags`"
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
variables:
CC: gcc
CXX: g++
timeout: 8h
Debian_GCC:
extends: .Debian
cache:
@ -93,125 +119,152 @@ Debian_Clang_tests:
CCACHE_SIZE: 1G
BUILD_TESTS_ONLY: 1
MacOS:
.MacOS:
image: macos-11-xcode-12
tags:
- macos
- shared-macos-amd64
stage: build
only:
variables:
- $CI_PROJECT_ID == "7107382"
cache:
paths:
- ccache/
script:
- rm -fr build/* # remove anything in the build directory
- rm -fr build # remove the build directory
- CI/before_install.osx.sh
- export CCACHE_BASEDIR="$(pwd)"
- export CCACHE_DIR="$(pwd)/ccache"
- mkdir -pv "${CCACHE_DIR}"
- ccache -z -M "${CCACHE_SIZE}"
- CI/before_script.osx.sh
- cd build; make -j2 package
- cd build; make -j $(sysctl -n hw.logicalcpu) package
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.dmg"; done
- ccache -s
artifacts:
paths:
- build/OpenMW-*.dmg
- "build/**/*.log"
macOS11_Xcode12:
extends: .MacOS
image: macos-11-xcode-12
cache:
key: macOS11_Xcode12.v1
variables:
CCACHE_SIZE: 3G
macOS10.15_Xcode11:
extends: .MacOS
image: macos-10.15-xcode-11
allow_failure: true
cache:
key: macOS10.15_Xcode11.v1
variables:
CCACHE_SIZE: 3G
variables: &engine-targets
targets: "openmw_vr,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
package: "Engine"
variables: &cs-targets
targets: "openmw-cs,bsatool,esmtool,niftest"
package: "CS"
#.Windows_Ninja_Base:
# tags:
# - windows
# before_script:
# - Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
# - choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
# - choco install git --force --params "/GitAndUnixToolsOnPath" -y
# - choco install 7zip -y
# - choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y
# - choco install vswhere -y
# - choco install ninja -y
# - choco install python -y
# - refreshenv
# stage: build
# script:
# - $time = (Get-Date -Format "HH:mm:ss")
# - echo ${time}
# - echo "started by ${GITLAB_USER_NAME}"
# - sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N
# - cd MSVC2019_64_Ninja
# - .\ActivateMSVC.ps1
# - cmake --build . --config $config --target ($targets.Split(','))
# - cd $config
# - echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
# - |
# if (Get-ChildItem -Recurse *.pdb) {
# 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
# Get-ChildItem -Recurse *.pdb | Remove-Item
# }
# - 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'
# after_script:
# - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
# cache:
# key: ninja-v2
# paths:
# - deps
# - MSVC2019_64_Ninja/deps/Qt
# artifacts:
# when: always
# paths:
# - "*.zip"
# - "*.log"
# - MSVC2019_64_Ninja/*.log
# - MSVC2019_64_Ninja/*/*.log
# - MSVC2019_64_Ninja/*/*/*.log
# - MSVC2019_64_Ninja/*/*/*/*.log
# - MSVC2019_64_Ninja/*/*/*/*/*.log
# - MSVC2019_64_Ninja/*/*/*/*/*/*.log
# - MSVC2019_64_Ninja/*/*/*/*/*/*/*.log
# - MSVC2019_64_Ninja/*/*/*/*/*/*/*/*.log
.Windows_Ninja_Base:
tags:
- windows
before_script:
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
- choco source add -n=openmw-proxy -s="https://repo.openmw.org/repository/Chocolatey/" --priority=1
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
- choco install 7zip -y
- choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y
- choco install vswhere -y
- choco install ninja -y
- choco install python -y
- refreshenv
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE != "schedule"'
script:
- $time = (Get-Date -Format "HH:mm:ss")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N
- cd MSVC2019_64_Ninja
- .\ActivateMSVC.ps1
- cmake --build . --config $config --target ($targets.Split(','))
- cd $config
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb' CI-ID.txt
Get-ChildItem -Recurse *.pdb | Remove-Item
}
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${package}_${config}_${CI_COMMIT_REF_NAME}.zip '*'
after_script:
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache:
key: ninja-v2
paths:
- deps
- MSVC2019_64_Ninja/deps/Qt
artifacts:
when: always
paths:
- "*.zip"
- "*.log"
- MSVC2019_64_Ninja/*.log
- MSVC2019_64_Ninja/*/*.log
- MSVC2019_64_Ninja/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*/*/*.log
- MSVC2019_64_Ninja/*/*/*/*/*/*/*/*.log
Windows_Ninja_Engine_Release:
extends:
- .Windows_Ninja_Base
variables:
<<: *engine-targets
config: "Release"
Windows_Ninja_Engine_Debug:
extends:
- .Windows_Ninja_Base
variables:
<<: *engine-targets
config: "Debug"
Windows_Ninja_Engine_RelWithDebInfo:
extends:
- .Windows_Ninja_Base
variables:
<<: *engine-targets
config: "RelWithDebInfo"
Windows_Ninja_CS_Release:
extends:
- .Windows_Ninja_Base
variables:
<<: *cs-targets
config: "Release"
Windows_Ninja_CS_Debug:
extends:
- .Windows_Ninja_Base
variables:
<<: *cs-targets
config: "Debug"
#
#Windows_Ninja_Engine_Release:
# extends:
# - .Windows_Ninja_Base
# variables:
# <<: *engine-targets
# config: "Release"
#
#Windows_Ninja_Engine_Debug:
# extends:
# - .Windows_Ninja_Base
# variables:
# <<: *engine-targets
# config: "Debug"
#
#Windows_Ninja_Engine_RelWithDebInfo:
# extends:
# - .Windows_Ninja_Base
# variables:
# <<: *engine-targets
# config: "RelWithDebInfo"
#
#Windows_Ninja_CS_Release:
# extends:
# - .Windows_Ninja_Base
# variables:
# <<: *cs-targets
# config: "Release"
#
#Windows_Ninja_CS_Debug:
# extends:
# - .Windows_Ninja_Base
# variables:
# <<: *cs-targets
# config: "Debug"
#
#Windows_Ninja_CS_RelWithDebInfo:
# extends:
# - .Windows_Ninja_Base
# variables:
# <<: *cs-targets
# config: "RelWithDebInfo"
Windows_Ninja_CS_RelWithDebInfo:
extends:
- .Windows_Ninja_Base
variables:
<<: *cs-targets
config: "RelWithDebInfo"
.Windows_MSBuild_Base:
tags:
@ -226,6 +279,8 @@ variables: &cs-targets
- choco install python -y
- refreshenv
stage: build
rules:
- if: '$CI_PIPELINE_SOURCE != "schedule"'
script:
- $time = (Get-Date -Format "HH:mm:ss")
- echo ${time}
@ -304,37 +359,37 @@ Windows_MSBuild_CS_RelWithDebInfo:
<<: *cs-targets
config: "RelWithDebInfo"
#Debian_AndroidNDK_arm64-v8a:
# tags:
# - linux
# image: debian:bullseye
# variables:
# CCACHE_SIZE: 3G
# cache:
# key: Debian_AndroidNDK_arm64-v8a.v3
# 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
# stage: build
# script:
# - export CCACHE_BASEDIR="`pwd`"
# - export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
# - ccache -z -M "${CCACHE_SIZE}"
# - CI/before_install.android.sh
# - CI/before_script.android.sh
# - cd build
# - cmake --build . -- -j $(nproc)
# - cmake --install .
# - ccache -s
# artifacts:
# paths:
# - build/install/
# # 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
Debian_AndroidNDK_arm64-v8a:
tags:
- linux
image: debian:bullseye
variables:
CCACHE_SIZE: 3G
cache:
key: Debian_AndroidNDK_arm64-v8a.v3
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
stage: build
script:
- export CCACHE_BASEDIR="`pwd`"
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
- ccache -z -M "${CCACHE_SIZE}"
- CI/before_install.android.sh
- CI/before_script.android.sh
- cd build
- cmake --build . -- -j $(nproc)
- cmake --install .
- ccache -s
artifacts:
paths:
- build/install/
# 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

@ -10,7 +10,7 @@ addons:
- sourceline: 'ppa:openmw/openmw'
packages: [
# Dev
build-essential, cmake, clang-tools, ccache,
build-essential, cmake, clang-tools-9, ccache,
# Boost
libboost-filesystem-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-system-dev,
# FFmpeg
@ -22,9 +22,9 @@ addons:
]
matrix:
include:
- name: OpenMW (all) on MacOS 10.15 with Xcode 11.6
- name: OpenMW (all) on MacOS 10.15 with Xcode 10.2
os: osx
osx_image: xcode11.6
osx_image: xcode10.2
- name: OpenMW (all) on Ubuntu Focal with GCC
os: linux
dist: focal
@ -37,8 +37,8 @@ matrix:
os: linux
dist: focal
env:
- MATRIX_EVAL="CC=clang && CXX=clang++"
- ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++"
- MATRIX_EVAL="CC=clang-9 && CXX=clang++-9"
- ANALYZE="scan-build-9 --force-analyze-debug-code --use-cc clang-9 --use-c++ clang++-9"
compiler: clang
before_install:
@ -66,11 +66,13 @@ deploy:
repo: OpenMW/openmw
notifications:
email:
if: repository_slug = OpenMW/openmw AND branch = master
recipients:
- corrmage+travis-ci@gmail.com
on_success: change
on_failure: always
irc:
if: repository_slug = OpenMW/openmw AND branch = master
channels:
- "chat.freenode.net#openmw"
on_success: change

@ -4,7 +4,7 @@ Contributors
The OpenMW project was started in 2008 by Nicolay Korslund.
In the course of years many people have contributed to the project.
If you feel your name is missing from this list, please notify a developer.
If you feel your name is missing from this list, please add it to `AUTHORS.md`.
Programmers
@ -24,7 +24,7 @@ Programmers
Alex McKibben
alexanderkjall
Alexander Nadeau (wareya)
Alexander Olofsson (Ace)
Alexander Olofsson (Ananace)
Alex Rice
Alex S (docwest)
Allofich
@ -49,6 +49,7 @@ Programmers
Cédric Mocquillon
Chris Boyce (slothlife)
Chris Robinson (KittyCat)
Cody Glassman (Wazabear)
Coleman Smith (olcoal)
Cory F. Cohen (cfcohen)
Cris Mihalache (Mirceam)
@ -89,6 +90,7 @@ Programmers
Internecine
Jackerty
Jacob Essex (Yacoby)
Jacob Turnbull (Tankinfrank)
Jake Westrip (16bitint)
James Carty (MrTopCat)
James Moore (moore.work)
@ -150,6 +152,7 @@ Programmers
Nathan Jeffords (blunted2night)
NeveHanter
Nialsy
Nick Crawford (nighthawk469)
Nikolay Kasyanov (corristo)
nobrakal
Nolan Poe (nopoe)
@ -184,6 +187,7 @@ Programmers
sergoz
ShadowRadiance
Siimacore
Simon Meulenbeek (simonmb)
sir_herrbatka
smbas
Sophie Kirschner (pineapplemachine)
@ -197,6 +201,7 @@ Programmers
Sylvain Thesnieres (Garvek)
t6
terrorfisch
Tess (tescoShoppah)
thegriglat
Thomas Luppi (Digmaster)
tlmullis
@ -235,7 +240,8 @@ Documentation
Packagers
---------
Alexander Olofsson (Ace) - Windows
Alexander Olofsson (Ananace) - Windows and Flatpak
Alexey Sokolov (DarthGandalf) - Gentoo Linux
Bret Curtis (psi29a) - Debian and Ubuntu Linux
Edmondo Tommasina (edmondo) - Gentoo Linux
Julian Ospald (hasufell) - Gentoo Linux

@ -115,6 +115,9 @@
Bug #5906: Sunglare doesn't work with Mesa drivers and AMD GPUs
Bug #5912: ImprovedBound mod doesn't work
Bug #5914: BM: The Swimmer can't reach destination
Bug #5923: Clicking on empty spaces between journal entries might show random topics
Bug #5934: AddItem command doesn't accept negative values
Bug #5975: NIF controllers from sheath meshes are used
Feature #390: 3rd person look "over the shoulder"
Feature #832: OpenMW-CS: Handle deleted references
Feature #1536: Show more information about level on menu
@ -122,8 +125,10 @@
Feature #2404: Levelled List can not be placed into a container
Feature #2686: Timestamps in openmw.log
Feature #3171: OpenMW-CS: Instance drag selection
Feature #3983: Wizard: Add link to buy Morrowind
Feature #4894: Consider actors as obstacles for pathfinding
Feature #4899: Alpha-To-Coverage Anti-Aliasing for alpha testing
Feature #4917: Do not trigger NavMesh update when RecastMesh update should not change NavMesh
Feature #4977: Use the "default icon.tga" when an item's icon is not found
Feature #5043: Head Bobbing
Feature #5199: OpenMW-CS: Improve scene view colors
@ -133,6 +138,7 @@
Feature #5456: Basic collada animation support
Feature #5457: Realistic diagonal movement
Feature #5486: Fixes trainers to choose their training skills based on their base skill points
Feature #5511: Add in game option to toggle HRTF support in OpenMW
Feature #5519: Code Patch tab in launcher
Feature #5524: Resume failed script execution after reload
Feature #5545: Option to allow stealing from an unconscious NPC during combat
@ -147,6 +153,8 @@
Feature #5730: Add graphic herbalism option to the launcher and documents
Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.
Feature #5813: Instanced groundcover support
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
Feature #5828: Support more than 8 lights
Feature #5910: Fall back to delta time when physics can't keep up
Task #5480: Drop Qt4 support
Task #5520: Improve cell name autocompleter implementation

@ -1,9 +1,9 @@
#!/bin/sh -ex
# workaround python issue on travis
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true
HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true
[-z "${TRAVIS}"] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.8 || true
[-z "${TRAVIS}"] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies python@3.9 || true
[-z "${TRAVIS}"] && HOMEBREW_NO_AUTO_UPDATE=1 brew uninstall --ignore-dependencies qt@6 || true
# Some of these tools can come from places other than brew, so check before installing
command -v ccache >/dev/null 2>&1 || brew install ccache

@ -16,7 +16,7 @@ cmake \
-D CMAKE_CXX_FLAGS="-stdlib=libc++" \
-D CMAKE_C_FLAGS_RELEASE="-g -O0" \
-D CMAKE_CXX_FLAGS_RELEASE="-g -O0" \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.12" \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.14" \
-D CMAKE_BUILD_TYPE=RELEASE \
-D OPENMW_OSX_DEPLOYMENT=TRUE \
-D BUILD_OPENMW=TRUE \

@ -28,6 +28,8 @@ declare -rA GROUPED_DEPS=(
# These dependencies can alternatively be built and linked statically.
[openmw-deps-dynamic]="libmygui-dev libopenscenegraph-dev"
[coverity]="curl"
# Pre-requisites for building MyGUI and OSG for static linking.
#

@ -612,8 +612,8 @@ if (WIN32)
# Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library file
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4582 4583 4610 4619 4623 4625
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045 5219 5220
# Warnings that are thrown on standard libraries and not OpenMW
4347 # Non-template function with same name and parameter count as template function
@ -635,7 +635,6 @@ if (WIN32)
5204 # Class has virtual functions, but its trivial destructor is not virtual
# caused by MyGUI
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
4297 # function assumed not to throw an exception but does
# OpenMW specific warnings
@ -671,6 +670,12 @@ if (WIN32)
)
endif()
if( "${MyGUI_VERSION}" VERSION_LESS_EQUAL "3.4.1" )
set(WARNINGS_DISABLE ${WARNINGS_DISABLE}
4275 # non dll-interface class 'MyGUI::delegates::IDelegateUnlink' used as base for dll-interface class 'MyGUI::Widget'
)
endif()
foreach(d ${WARNINGS_DISABLE})
set(WARNINGS "${WARNINGS} /wd${d}")
endforeach(d)

@ -20,6 +20,7 @@ struct Arguments
std::string mode;
std::string filename;
std::string extractfile;
std::string addfile;
std::string outdir;
bool longformat;
@ -36,6 +37,10 @@ bool parseOptions (int argc, char** argv, Arguments &info)
" Extract a file from the input archive.\n\n"
" bsatool extractall archivefile [output_directory]\n"
" Extract all files from the input archive.\n\n"
" bsatool add [-a] archivefile file_to_add\n"
" Add a file to the input archive.\n\n"
" bsatool create [-c] archivefile\n"
" Create an archive.\n\n"
"Allowed options");
desc.add_options()
@ -95,7 +100,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
}
info.mode = variables["mode"].as<std::string>();
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall"))
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall" || info.mode == "add" || info.mode == "create"))
{
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n"
<< desc << std::endl;
@ -126,6 +131,17 @@ bool parseOptions (int argc, char** argv, Arguments &info)
if (variables["input-file"].as< std::vector<std::string> >().size() > 2)
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2];
}
else if (info.mode == "add")
{
if (variables["input-file"].as< std::vector<std::string> >().size() < 1)
{
std::cout << "\nERROR: file to add unspecified\n\n"
<< desc << std::endl;
return false;
}
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
info.addfile = variables["input-file"].as< std::vector<std::string> >()[1];
}
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
@ -138,6 +154,7 @@ bool parseOptions (int argc, char** argv, Arguments &info)
int list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
int extract(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
int add(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info);
int main(int argc, char** argv)
{
@ -157,6 +174,12 @@ int main(int argc, char** argv)
else
bsa = std::make_unique<Bsa::BSAFile>(Bsa::BSAFile());
if (info.mode == "create")
{
bsa->open(info.filename);
return 0;
}
bsa->open(info.filename);
if (info.mode == "list")
@ -165,6 +188,8 @@ int main(int argc, char** argv)
return extract(bsa, info);
else if (info.mode == "extractall")
return extractAll(bsa, info);
else if (info.mode == "add")
return add(bsa, info);
else
{
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
@ -188,13 +213,13 @@ int list(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
{
// Long format
std::ios::fmtflags f(std::cout.flags());
std::cout << std::setw(50) << std::left << file.name;
std::cout << std::setw(50) << std::left << file.name();
std::cout << std::setw(8) << std::left << std::dec << file.fileSize;
std::cout << "@ 0x" << std::hex << file.offset << std::endl;
std::cout.flags(f);
}
else
std::cout << file.name << std::endl;
std::cout << file.name() << std::endl;
}
return 0;
@ -253,7 +278,7 @@ int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
{
for (const auto &file : bsa->getList())
{
std::string extractPath(file.name);
std::string extractPath(file.name());
Misc::StringUtils::replaceAll(extractPath, "\\", "/");
// Get the target path (the path the file will be extracted to)
@ -272,7 +297,7 @@ int extractAll(std::unique_ptr<Bsa::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.name());
bfs::ofstream out(target, std::ios::binary);
// Write the file to disk
@ -283,3 +308,11 @@ int extractAll(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
return 0;
}
int add(std::unique_ptr<Bsa::BSAFile>& bsa, Arguments& info)
{
boost::filesystem::fstream stream(info.addfile, std::ios_base::binary | std::ios_base::out | std::ios_base::in);
bsa->addFile(info.addfile, stream);
return 0;
}

@ -322,7 +322,7 @@ int load(Arguments& info)
std::string filename = info.filename;
std::cout << "Loading file: " << filename << std::endl;
std::list<int> skipped;
std::list<uint32_t> skipped;
try {

@ -11,6 +11,7 @@ namespace ESSImport
{
out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString());
out.mRunning = scpt.mRunning;
out.mTargetRef.unset(); // TODO: convert target reference of global script
convertSCRI(scpt.mSCRI, out.mLocals);
}

@ -19,6 +19,7 @@ namespace ESSImport
item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1;
item.mLockLevel = 0;
item.mRefNum.unset();
unsigned int itemCount = std::abs(item.mCount);
bool separateStacks = false;

@ -57,7 +57,7 @@ int main(int argc, char** argv)
else
{
const std::string& ext = ".omwsave";
if (boost::filesystem::exists(boost::filesystem::path(outputFile))
if (bfs::exists(bfs::path(outputFile))
&& (outputFile.size() < ext.size() || outputFile.substr(outputFile.size()-ext.size()) != ext))
{
throw std::runtime_error("Output file already exists and does not end in .omwsave. Did you mean to use --compare?");

@ -13,6 +13,7 @@ set(LAUNCHER
utils/profilescombobox.cpp
utils/textinputdialog.cpp
utils/lineedit.cpp
utils/openalutil.cpp
${CMAKE_SOURCE_DIR}/files/windows/launcher.rc
)
@ -31,6 +32,7 @@ set(LAUNCHER_HEADER
utils/profilescombobox.hpp
utils/textinputdialog.hpp
utils/lineedit.hpp
utils/openalutil.hpp
)
# Headers that must be pre-processed
@ -47,6 +49,7 @@ set(LAUNCHER_HEADER_MOC
utils/textinputdialog.hpp
utils/profilescombobox.hpp
utils/lineedit.hpp
utils/openalutil.hpp
)
@ -95,6 +98,7 @@ endif (WIN32)
target_link_libraries(openmw-launcher
${SDL2_LIBRARY_ONLY}
${OPENAL_LIBRARY}
components
)

@ -1,15 +1,20 @@
#include "advancedpage.hpp"
#include <array>
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include <QFileDialog>
#include <QCompleter>
#include <QProxyStyle>
#include <QString>
#include <components/contentselector/view/contentselector.hpp>
#include <components/contentselector/model/esmfile.hpp>
#include <cmath>
#include "utils/openalutil.hpp"
Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings,
Settings::Manager &engineSettings, QWidget *parent)
: QWidget(parent)
@ -19,7 +24,17 @@ Launcher::AdvancedPage::AdvancedPage(Config::GameSettings &gameSettings,
setObjectName ("AdvancedPage");
setupUi(this);
for(const char * name : Launcher::enumerateOpenALDevices())
{
audioDeviceSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));
}
for(const char * name : Launcher::enumerateOpenALDevicesHrtf())
{
hrtfProfileSelectorComboBox->addItem(QString::fromUtf8(name), QString::fromUtf8(name));
}
loadSettings();
mCellNameCompleter.setModel(&mCellNameCompleterModel);
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
}
@ -95,6 +110,7 @@ bool Launcher::AdvancedPage::loadSettings()
int numPhysicsThreads = mEngineSettings.getInt("async num threads", "Physics");
if (numPhysicsThreads >= 0)
physicsThreadsSpinBox->setValue(numPhysicsThreads);
loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game");
}
// Visuals
@ -124,8 +140,43 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera")));
int lightingMethod = 1;
if (mEngineSettings.getString("lighting method", "Shaders") == "legacy")
lightingMethod = 0;
else if (mEngineSettings.getString("lighting method", "Shaders") == "shaders")
lightingMethod = 2;
lightingMethodComboBox->setCurrentIndex(lightingMethod);
}
// Audio
{
std::string selectedAudioDevice = mEngineSettings.getString("device", "Sound");
if (selectedAudioDevice.empty() == false)
{
int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice));
if (audioDeviceIndex != -1)
{
audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex);
}
}
int hrtfEnabledIndex = mEngineSettings.getInt("hrtf enable", "Sound");
if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1)
{
enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1);
}
std::string selectedHRTFProfile = mEngineSettings.getString("hrtf", "Sound");
if (selectedHRTFProfile.empty() == false)
{
int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile));
if (hrtfProfileIndex != -1)
{
hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex);
}
}
}
// Camera
{
loadSettingBool(viewOverShoulderCheckBox, "view over shoulder", "Camera");
@ -152,6 +203,7 @@ bool Launcher::AdvancedPage::loadSettings()
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
scalingSpinBox->setValue(mEngineSettings.getFloat("scaling factor", "GUI"));
}
// Bug fixes
@ -266,6 +318,36 @@ void Launcher::AdvancedPage::saveSettings()
{
mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance));
}
static std::array<std::string, 3> lightingMethodMap = {"legacy", "shaders compatibility", "shaders"};
mEngineSettings.setString("lighting method", "Shaders", lightingMethodMap[lightingMethodComboBox->currentIndex()]);
}
// Audio
{
int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex();
if (audioDeviceIndex != 0)
{
mEngineSettings.setString("device", "Sound", audioDeviceSelectorComboBox->currentText().toUtf8().constData());
}
else
{
mEngineSettings.setString("device", "Sound", "");
}
int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1;
if (hrtfEnabledIndex != mEngineSettings.getInt("hrtf enable", "Sound"))
{
mEngineSettings.setInt("hrtf enable", "Sound", hrtfEnabledIndex);
}
int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex();
if (selectedHRTFProfileIndex != 0)
{
mEngineSettings.setString("hrtf", "Sound", hrtfProfileSelectorComboBox->currentText().toUtf8().constData());
}
else
{
mEngineSettings.setString("hrtf", "Sound", "");
}
}
// Camera
@ -299,6 +381,9 @@ void Launcher::AdvancedPage::saveSettings()
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI");
saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game");
float uiScalingFactor = scalingSpinBox->value();
if (uiScalingFactor != mEngineSettings.getFloat("scaling factor", "GUI"))
mEngineSettings.setFloat("scaling factor", "GUI", uiScalingFactor);
}
// Bug fixes

@ -20,6 +20,9 @@
QString getAspect(int x, int y)
{
int gcd = std::gcd (x, y);
if (gcd == 0)
return QString();
int xaspect = x / gcd;
int yaspect = y / gcd;
// special case: 8 : 5 is usually referred to as 16:10
@ -298,9 +301,9 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
return result;
}
QString aspect = getAspect(mode.w, mode.h);
QString resolution = QString::number(mode.w) + QString(" x ") + QString::number(mode.h);
QString aspect = getAspect(mode.w, mode.h);
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
resolution.append(tr("\t(Wide ") + aspect + ")");

@ -0,0 +1,61 @@
#include <cstring>
#include <vector>
#include <memory>
#include <apps/openmw/mwsound/alext.h>
#include "openalutil.hpp"
#ifndef ALC_ALL_DEVICES_SPECIFIER
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
#endif
std::vector<const char *> Launcher::enumerateOpenALDevices()
{
std::vector<const char *> devlist;
const ALCchar *devnames;
if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"))
{
devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
}
else
{
devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
}
while(devnames && *devnames)
{
devlist.emplace_back(devnames);
devnames += strlen(devnames)+1;
}
return devlist;
}
std::vector<const char *> Launcher::enumerateOpenALDevicesHrtf()
{
std::vector<const char *> ret;
ALCdevice *device = alcOpenDevice(nullptr);
if(device)
{
if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
{
LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
void* funcPtr = alcGetProcAddress(device, "alcGetStringiSOFT");
memcpy(&alcGetStringiSOFT, &funcPtr, sizeof(funcPtr));
ALCint num_hrtf;
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
ret.reserve(num_hrtf);
for(ALCint i = 0;i < num_hrtf;++i)
{
const ALCchar *entry = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
if(strcmp(entry, "") == 0)
break;
ret.emplace_back(entry);
}
}
alcCloseDevice(device);
}
return ret;
}

@ -0,0 +1,7 @@
#include <vector>
namespace Launcher
{
std::vector<const char *> enumerateOpenALDevices();
std::vector<const char *> enumerateOpenALDevicesHrtf();
}

@ -116,7 +116,7 @@ opencs_units (view/prefs
opencs_units (model/prefs
state setting intsetting doublesetting boolsetting enumsetting coloursetting shortcut
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting stringsetting
)
opencs_units_noqt (model/prefs

@ -13,8 +13,6 @@
namespace CSMPrefs
{
const int ShortcutSetting::MaxKeys;
ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label)
: Setting(parent, values, mutex, key, label)

@ -421,6 +421,16 @@ void CSMPrefs::State::declare()
declareSubcategory ("Script Editor");
declareShortcut ("script-editor-comment", "Comment Selection", QKeySequence());
declareShortcut ("script-editor-uncomment", "Uncomment Selection", QKeySequence());
declareCategory ("Models");
declareString ("baseanim", "base animations", "meshes/base_anim.nif").
setTooltip("3rd person base model with textkeys-data");
declareString ("baseanimkna", "base animations, kna", "meshes/base_animkna.nif").
setTooltip("3rd person beast race base model with textkeys-data");
declareString ("baseanimfemale", "base animations, female", "meshes/base_anim_female.nif").
setTooltip("3rd person female base model with textkeys-data");
declareString ("wolfskin", "base animations, wolf", "meshes/wolf/skin.nif").
setTooltip("3rd person werewolf skin");
}
void CSMPrefs::State::declareCategory (const std::string& key)
@ -557,6 +567,24 @@ CSMPrefs::ShortcutSetting& CSMPrefs::State::declareShortcut (const std::string&
return *setting;
}
CSMPrefs::StringSetting& CSMPrefs::State::declareString (const std::string& key, const std::string& label, std::string default_)
{
if (mCurrentCategory==mCategories.end())
throw std::logic_error ("no category for setting");
setDefault (key, default_);
default_ = mSettings.getString (key, mCurrentCategory->second.getKey());
CSMPrefs::StringSetting *setting =
new CSMPrefs::StringSetting (&mCurrentCategory->second, &mSettings, &mMutex, key, label,
default_);
mCurrentCategory->second.addSetting (setting);
return *setting;
}
CSMPrefs::ModifierSetting& CSMPrefs::State::declareModifier(const std::string& key, const std::string& label,
int default_)
{

@ -16,6 +16,7 @@
#include "category.hpp"
#include "setting.hpp"
#include "enumsetting.hpp"
#include "stringsetting.hpp"
#include "shortcutmanager.hpp"
class QColor;
@ -78,6 +79,8 @@ namespace CSMPrefs
ShortcutSetting& declareShortcut (const std::string& key, const std::string& label,
const QKeySequence& default_);
StringSetting& declareString (const std::string& key, const std::string& label, std::string default_);
ModifierSetting& declareModifier(const std::string& key, const std::string& label, int modifier_);
void declareSeparator();

@ -0,0 +1,54 @@
#include "stringsetting.hpp"
#include <QLineEdit>
#include <QMutexLocker>
#include <components/settings/settings.hpp>
#include "category.hpp"
#include "state.hpp"
CSMPrefs::StringSetting::StringSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, std::string default_)
: Setting (parent, values, mutex, key, label), mDefault (default_), mWidget(nullptr)
{}
CSMPrefs::StringSetting& CSMPrefs::StringSetting::setTooltip (const std::string& tooltip)
{
mTooltip = tooltip;
return *this;
}
std::pair<QWidget *, QWidget *> CSMPrefs::StringSetting::makeWidgets (QWidget *parent)
{
mWidget = new QLineEdit (QString::fromUtf8 (mDefault.c_str()), parent);
if (!mTooltip.empty())
{
QString tooltip = QString::fromUtf8 (mTooltip.c_str());
mWidget->setToolTip (tooltip);
}
connect (mWidget, SIGNAL (textChanged (QString)), this, SLOT (textChanged (QString)));
return std::make_pair (static_cast<QWidget *> (nullptr), mWidget);
}
void CSMPrefs::StringSetting::updateWidget()
{
if (mWidget)
{
mWidget->setText(QString::fromStdString(getValues().getString(getKey(), getParent()->getKey())));
}
}
void CSMPrefs::StringSetting::textChanged (const QString& text)
{
{
QMutexLocker lock (getMutex());
getValues().setString (getKey(), getParent()->getKey(), text.toStdString());
}
getParent()->getState()->update (*this);
}

@ -0,0 +1,36 @@
#ifndef CSM_PREFS_StringSetting_H
#define CSM_PREFS_StringSetting_H
#include "setting.hpp"
class QLineEdit;
namespace CSMPrefs
{
class StringSetting : public Setting
{
Q_OBJECT
std::string mTooltip;
std::string mDefault;
QLineEdit* mWidget;
public:
StringSetting (Category *parent, Settings::Manager *values,
QMutex *mutex, const std::string& key, const std::string& label, std::string default_);
StringSetting& setTooltip (const std::string& tooltip);
/// Return label, input widget.
std::pair<QWidget *, QWidget *> makeWidgets (QWidget *parent) override;
void updateWidget() override;
private slots:
void textChanged (const QString& text);
};
}
#endif

@ -76,6 +76,7 @@ void CSMWorld::ImportLandTexturesCommand::redo()
}
std::vector<std::string> oldTextures;
oldTextures.reserve(texIndices.size());
for (int index : texIndices)
{
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));

@ -83,6 +83,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
defines["clamp"] = "1"; // Clamp lighting
defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind
defines["radialFog"] = "0";
defines["lightingModel"] = "0";
for (const auto& define : shadowDefines)
defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
@ -985,20 +986,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mMetaData.setRecord (0, Record<MetaData> (RecordBase::State_ModifiedOnly, nullptr, &metaData));
}
// Fix uninitialized master data index
for (std::vector<ESM::Header::MasterData>::const_iterator masterData = mReader->getGameFiles().begin();
masterData != mReader->getGameFiles().end(); ++masterData)
{
std::map<std::string, int>::iterator nameResult = mContentFileNames.find(masterData->name);
if (nameResult != mContentFileNames.end())
{
ESM::Header::MasterData& hackedMasterData = const_cast<ESM::Header::MasterData&>(*masterData);
hackedMasterData.index = nameResult->second;
}
}
return mReader->getRecordCount();
}

@ -201,7 +201,7 @@ QModelIndex CSMWorld::IdTree::parent (const QModelIndex& index) const
const std::pair<int, int>& address(unfoldIndexAddress(id));
if (address.first >= this->rowCount() || address.second >= this->columnCount())
throw "Parent index is not present in the model";
throw std::logic_error("Parent index is not present in the model");
return createIndex(address.first, address.second);
}
@ -216,7 +216,7 @@ unsigned int CSMWorld::IdTree::foldIndexAddress (const QModelIndex& index) const
std::pair< int, int > CSMWorld::IdTree::unfoldIndexAddress (unsigned int id) const
{
if (id == 0)
throw "Attempt to unfold index id of the top level data cell";
throw std::runtime_error("Attempt to unfold index id of the top level data cell");
--id;
int row = id / this->columnCount();

@ -56,7 +56,9 @@ void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData
CSMWorld::IngredientColumns::IngredientColumns (const InventoryColumns& columns)
: InventoryColumns (columns) {}
: InventoryColumns (columns)
, mEffects(nullptr)
{}
CSMWorld::IngredientRefIdAdapter::IngredientRefIdAdapter (const IngredientColumns& columns)
: InventoryRefIdAdapter<ESM::Ingredient> (UniversalId::Type_Ingredient, columns),
@ -585,7 +587,13 @@ void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
}
CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns)
: InventoryColumns (columns) {}
: InventoryColumns (columns)
, mTime(nullptr)
, mRadius(nullptr)
, mColor(nullptr)
, mSound(nullptr)
, mEmitterType(nullptr)
{}
CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns)
: InventoryRefIdAdapter<ESM::Light> (UniversalId::Type_Light, columns), mColumns (columns)
@ -1454,7 +1462,15 @@ int CSMWorld::CreatureMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *co
}
CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns)
: EnchantableColumns (columns) {}
: EnchantableColumns (columns)
, mType(nullptr)
, mHealth(nullptr)
, mSpeed(nullptr)
, mReach(nullptr)
, mChop{nullptr}
, mSlash{nullptr}
, mThrust{nullptr}
{}
CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns)
: EnchantableRefIdAdapter<ESM::Weapon> (UniversalId::Type_Weapon, columns), mColumns (columns)

@ -178,7 +178,11 @@ namespace CSMWorld
const RefIdColumn *mName;
const RefIdColumn *mScript;
NameColumns (const ModelColumns& base) : ModelColumns (base) {}
NameColumns (const ModelColumns& base)
: ModelColumns (base)
, mName(nullptr)
, mScript(nullptr)
{}
};
/// \brief Adapter for IDs with names (all but levelled lists and statics)
@ -247,7 +251,12 @@ namespace CSMWorld
const RefIdColumn *mWeight;
const RefIdColumn *mValue;
InventoryColumns (const NameColumns& base) : NameColumns (base) {}
InventoryColumns (const NameColumns& base)
: NameColumns (base)
, mIcon(nullptr)
, mWeight(nullptr)
, mValue(nullptr)
{}
};
/// \brief Adapter for IDs that can go into an inventory
@ -405,7 +414,11 @@ namespace CSMWorld
const RefIdColumn *mEnchantment;
const RefIdColumn *mEnchantmentPoints;
EnchantableColumns (const InventoryColumns& base) : InventoryColumns (base) {}
EnchantableColumns (const InventoryColumns& base)
: InventoryColumns (base)
, mEnchantment(nullptr)
, mEnchantmentPoints(nullptr)
{}
};
/// \brief Adapter for enchantable IDs
@ -474,7 +487,11 @@ namespace CSMWorld
const RefIdColumn *mQuality;
const RefIdColumn *mUses;
ToolColumns (const InventoryColumns& base) : InventoryColumns (base) {}
ToolColumns (const InventoryColumns& base)
: InventoryColumns (base)
, mQuality(nullptr)
, mUses(nullptr)
{}
};
/// \brief Adapter for tools with limited uses IDs (lockpick, repair, probes)
@ -549,7 +566,17 @@ namespace CSMWorld
const RefIdColumn *mAiPackages;
std::map<const RefIdColumn *, unsigned int> mServices;
ActorColumns (const NameColumns& base) : NameColumns (base) {}
ActorColumns (const NameColumns& base)
: NameColumns (base)
, mHello(nullptr)
, mFlee(nullptr)
, mFight(nullptr)
, mAlarm(nullptr)
, mInventory(nullptr)
, mSpells(nullptr)
, mDestinations(nullptr)
, mAiPackages(nullptr)
{}
};
/// \brief Adapter for actor IDs (handles common AI functionality)
@ -2054,7 +2081,11 @@ namespace CSMWorld
const RefIdColumn *mLevList;
const RefIdColumn *mNestedListLevList;
LevListColumns (const BaseColumns& base) : BaseColumns (base) {}
LevListColumns (const BaseColumns& base)
: BaseColumns (base)
, mLevList(nullptr)
, mNestedListLevList(nullptr)
{}
};
template<typename RecordT>

@ -126,7 +126,7 @@ namespace CSVRender
{
// Try again without any mask
boundsVisitor.reset();
boundsVisitor.setTraversalMask(~0);
boundsVisitor.setTraversalMask(~0u);
root->accept(boundsVisitor);
// Last resort, set a default
@ -458,7 +458,7 @@ namespace CSVRender
, mDown(false)
, mRollLeft(false)
, mRollRight(false)
, mPickingMask(~0)
, mPickingMask(~0u)
, mCenter(0,0,0)
, mDistance(0)
, mOrbitSpeed(osg::PI / 4)

@ -151,7 +151,7 @@ void CSVRender::CellArrow::buildShape()
osg::Vec4Array *colours = new osg::Vec4Array;
for (int i=0; i<6; ++i)
colours->push_back (osg::Vec4f (0.11, 0.6f, 0.95f, 1.0f));
colours->push_back (osg::Vec4f (0.11f, 0.6f, 0.95f, 1.0f));
for (int i=0; i<6; ++i)
colours->push_back (osg::Vec4f (0.08f, 0.44f, 0.7f, 1.0f));

@ -8,7 +8,7 @@ namespace CSVRender
/// @note See the respective file in OpenMW (apps/openmw/mwrender/vismask.hpp)
/// for general usage hints about node masks.
/// @copydoc MWRender::VisMask
enum Mask
enum Mask : unsigned int
{
// elements that are part of the actual scene
Mask_Reference = 0x2,

@ -121,7 +121,7 @@ void RenderWidget::flagAsModified()
mView->requestRedraw();
}
void RenderWidget::setVisibilityMask(int mask)
void RenderWidget::setVisibilityMask(unsigned int mask)
{
mView->getCamera()->setCullMask(mask | Mask_ParticleSystem | Mask_Lighting);
}

@ -55,7 +55,7 @@ namespace CSVRender
/// Initiates a request to redraw the view
void flagAsModified();
void setVisibilityMask(int mask);
void setVisibilityMask(unsigned int mask);
osg::Camera *getCamera();

@ -606,7 +606,7 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
}
catch (const std::exception& e)
catch (const std::exception&)
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true;

@ -163,7 +163,7 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
void CSVRender::WorldspaceWidget::centerOrbitCameraOnSelection()
{
std::vector<osg::ref_ptr<TagBase> > selection = getSelection(~0);
std::vector<osg::ref_ptr<TagBase> > selection = getSelection(~0u);
for (std::vector<osg::ref_ptr<TagBase> >::iterator it = selection.begin(); it!=selection.end(); ++it)
{

@ -43,26 +43,6 @@ namespace Misc
class CallbackManager;
}
namespace MWScript
{
class ScriptManager;
}
namespace MWSound
{
class SoundManager;
}
namespace MWWorld
{
class World;
}
namespace MWGui
{
class WindowManager;
}
namespace Files
{
struct ConfigurationManager;

@ -32,7 +32,6 @@ namespace MyGUI
namespace ESM
{
struct Class;
class ESMReader;
class ESMWriter;
struct CellId;
@ -173,6 +172,8 @@ namespace MWBase
virtual void setDragDrop(bool dragDrop) = 0;
virtual bool getWorldMouseOver() = 0;
virtual float getScalingFactor() = 0;
virtual bool toggleFogOfWar() = 0;
virtual bool toggleFullHelp() = 0;

@ -288,13 +288,13 @@ namespace MWBase
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
virtual void undeleteObject (const MWWorld::Ptr& ptr) = 0;
virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool moveToActive=false) = 0;
virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z, bool movePhysics=true, bool moveToActive=false) = 0;
///< @return an updated Ptr in case the Ptr's cell changes
virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0;
///< @return an updated Ptr
virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec) = 0;
virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr &ptr, osg::Vec3f vec, bool moveToActive) = 0;
///< @return an updated Ptr
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
@ -456,6 +456,8 @@ namespace MWBase
virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0;
virtual void disableDeferredPreviewRotation() = 0;
virtual void saveLoaded() = 0;
virtual void setupPlayer() = 0;
virtual void renderPlayer() = 0;
@ -618,7 +620,7 @@ namespace MWBase
/// Return a vector aiming the actor's weapon towards a target.
/// @note The length of the vector is the distance between actor and target.
virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;
virtual osg::Vec3f aimToTarget(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target, bool isRangedCombat) = 0;
/// Return the distance between actor's weapon and target's collision box.
virtual float getHitDistance(const MWWorld::ConstPtr& actor, const MWWorld::ConstPtr& target) = 0;

@ -46,11 +46,6 @@ namespace MWClass
mStore.readState(inventory);
}
MWWorld::CustomData *ContainerCustomData::clone() const
{
return new ContainerCustomData (*this);
}
ContainerCustomData& ContainerCustomData::asContainerCustomData()
{
return *this;
@ -72,7 +67,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
// store
ptr.getRefData().setCustomData (std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()).release());
ptr.getRefData().setCustomData (std::make_unique<ContainerCustomData>(*ref->mBase, ptr.getCell()));
MWBase::Environment::get().getWorld()->addContainerScripts(ptr, ptr.getCell());
}
@ -317,7 +312,7 @@ namespace MWClass
return;
const ESM::ContainerState& containerState = state.asContainerState();
ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory).release());
ptr.getRefData().setCustomData(std::make_unique<ContainerCustomData>(containerState.mInventory));
}
void Container::writeAdditionalState (const MWWorld::ConstPtr& ptr, ESM::ObjectState& state) const

@ -13,15 +13,13 @@ namespace ESM
namespace MWClass
{
class ContainerCustomData : public MWWorld::CustomData
class ContainerCustomData : public MWWorld::TypedCustomData<ContainerCustomData>
{
MWWorld::ContainerStore mStore;
public:
ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell);
ContainerCustomData(const ESM::InventoryState& inventory);
MWWorld::CustomData *clone() const override;
ContainerCustomData& asContainerCustomData() override;
const ContainerCustomData& asContainerCustomData() const override;

@ -52,14 +52,16 @@ namespace
namespace MWClass
{
class CreatureCustomData : public MWWorld::CustomData
class CreatureCustomData : public MWWorld::TypedCustomData<CreatureCustomData>
{
public:
MWMechanics::CreatureStats mCreatureStats;
MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures
std::unique_ptr<MWWorld::ContainerStore> mContainerStore; // may be InventoryStore for some creatures
MWMechanics::Movement mMovement;
MWWorld::CustomData *clone() const override;
CreatureCustomData() = default;
CreatureCustomData(const CreatureCustomData& other);
CreatureCustomData(CreatureCustomData&& other) = default;
CreatureCustomData& asCreatureCustomData() override
{
@ -69,16 +71,13 @@ namespace MWClass
{
return *this;
}
CreatureCustomData() : mContainerStore(nullptr) {}
virtual ~CreatureCustomData() { delete mContainerStore; }
};
MWWorld::CustomData *CreatureCustomData::clone() const
CreatureCustomData::CreatureCustomData(const CreatureCustomData& other)
: mCreatureStats(other.mCreatureStats),
mContainerStore(other.mContainerStore->clone()),
mMovement(other.mMovement)
{
CreatureCustomData* cloned = new CreatureCustomData (*this);
cloned->mContainerStore = mContainerStore->clone();
return cloned;
}
const Creature::GMST& Creature::getGmst()
@ -149,16 +148,16 @@ namespace MWClass
// inventory
bool hasInventory = hasInventoryStore(ptr);
if (hasInventory)
data->mContainerStore = new MWWorld::InventoryStore();
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
else
data->mContainerStore = new MWWorld::ContainerStore();
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
data->mCreatureStats.setNeedRecalcDynamicStats(false);
// store
ptr.getRefData().setCustomData(data.release());
ptr.getRefData().setCustomData(std::move(data));
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
@ -772,11 +771,11 @@ namespace MWClass
std::unique_ptr<CreatureCustomData> data (new CreatureCustomData);
if (hasInventoryStore(ptr))
data->mContainerStore = new MWWorld::InventoryStore();
data->mContainerStore = std::make_unique<MWWorld::InventoryStore>();
else
data->mContainerStore = new MWWorld::ContainerStore();
data->mContainerStore = std::make_unique<MWWorld::ContainerStore>();
ptr.getRefData().setCustomData (data.release());
ptr.getRefData().setCustomData (std::move(data));
}
}
else

@ -10,15 +10,13 @@
namespace MWClass
{
class CreatureLevListCustomData : public MWWorld::CustomData
class CreatureLevListCustomData : public MWWorld::TypedCustomData<CreatureLevListCustomData>
{
public:
// actorId of the creature we spawned
int mSpawnActorId;
bool mSpawn; // Should a new creature be spawned?
MWWorld::CustomData *clone() const override;
CreatureLevListCustomData& asCreatureLevListCustomData() override
{
return *this;
@ -29,11 +27,6 @@ namespace MWClass
}
};
MWWorld::CustomData *CreatureLevListCustomData::clone() const
{
return new CreatureLevListCustomData (*this);
}
std::string CreatureLevList::getName (const MWWorld::ConstPtr& ptr) const
{
return "";
@ -138,11 +131,11 @@ namespace MWClass
{
if (!ptr.getRefData().getCustomData())
{
std::unique_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
std::unique_ptr<CreatureLevListCustomData> data = std::make_unique<CreatureLevListCustomData>();
data->mSpawnActorId = -1;
data->mSpawn = true;
ptr.getRefData().setCustomData(data.release());
ptr.getRefData().setCustomData(std::move(data));
}
}

@ -31,13 +31,11 @@
namespace MWClass
{
class DoorCustomData : public MWWorld::CustomData
class DoorCustomData : public MWWorld::TypedCustomData<DoorCustomData>
{
public:
MWWorld::DoorState mDoorState = MWWorld::DoorState::Idle;
MWWorld::CustomData *clone() const override;
DoorCustomData& asDoorCustomData() override
{
return *this;
@ -48,11 +46,6 @@ namespace MWClass
}
};
MWWorld::CustomData *DoorCustomData::clone() const
{
return new DoorCustomData (*this);
}
void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
{
if (!model.empty())
@ -327,8 +320,7 @@ namespace MWClass
{
if (!ptr.getRefData().getCustomData())
{
std::unique_ptr<DoorCustomData> data(new DoorCustomData);
ptr.getRefData().setCustomData(data.release());
ptr.getRefData().setCustomData(std::make_unique<DoorCustomData>());
}
}

@ -247,15 +247,13 @@ namespace
namespace MWClass
{
class NpcCustomData : public MWWorld::CustomData
class NpcCustomData : public MWWorld::TypedCustomData<NpcCustomData>
{
public:
MWMechanics::NpcStats mNpcStats;
MWMechanics::Movement mMovement;
MWWorld::InventoryStore mInventoryStore;
MWWorld::CustomData *clone() const override;
NpcCustomData& asNpcCustomData() override
{
return *this;
@ -266,11 +264,6 @@ namespace MWClass
}
};
MWWorld::CustomData *NpcCustomData::clone() const
{
return new NpcCustomData (*this);
}
const Npc::GMST& Npc::getGmst()
{
static GMST gmst;
@ -398,7 +391,7 @@ namespace MWClass
data->mNpcStats.setGoldPool(gold);
// store
ptr.getRefData().setCustomData (data.release());
ptr.getRefData().setCustomData(std::move(data));
getInventoryStore(ptr).autoEquip(ptr);
}
@ -1331,8 +1324,7 @@ namespace MWClass
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::unique_ptr<NpcCustomData> data (new NpcCustomData);
ptr.getRefData().setCustomData (data.release());
ptr.getRefData().setCustomData(std::make_unique<NpcCustomData>());
}
}
else

@ -17,7 +17,7 @@ namespace MWDialogue
std::vector<Token> parseHyperText(const std::string & text)
{
std::vector<Token> result;
size_t pos_end, iteration_pos = 0;
size_t pos_end = std::string::npos, iteration_pos = 0;
for(;;)
{
size_t pos_begin = text.find('@', iteration_pos);

@ -1,5 +1,7 @@
#include "bookpage.hpp"
#include <optional>
#include "MyGUI_RenderItem.h"
#include "MyGUI_RenderManager.h"
#include "MyGUI_TextureUtility.h"
@ -894,6 +896,27 @@ protected:
return mIsPageReset || (mPage != page);
}
std::optional<MyGUI::IntPoint> getAdjustedPos(int left, int top, bool move = false)
{
if (!mBook)
return {};
if (mPage >= mBook->mPages.size())
return {};
MyGUI::IntPoint pos (left, top);
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
// work around inconsistency in MyGUI where the mouse press coordinates aren't
// transformed by the current Layer (even though mouse *move* events are).
if(!move)
pos = mNode->getLayer()->getPosition(left, top);
#endif
pos.left -= mCroppedParent->getAbsoluteLeft ();
pos.top -= mCroppedParent->getAbsoluteTop ();
pos.top += mViewTop;
return pos;
}
public:
typedef TypesetBookImpl::StyleImpl Style;
@ -952,16 +975,10 @@ public:
void onMouseMove (int left, int top)
{
if (!mBook)
return;
if (mPage >= mBook->mPages.size())
return;
left -= mCroppedParent->getAbsoluteLeft ();
top -= mCroppedParent->getAbsoluteTop ();
Style * hit = mBook->hitTestWithMargin (left, mViewTop + top);
Style * hit = nullptr;
if(auto pos = getAdjustedPos(left, top, true))
if(pos->top <= mViewBottom)
hit = mBook->hitTestWithMargin (pos->left, pos->top);
if (mLastDown == MyGUI::MouseButton::None)
{
@ -991,24 +1008,11 @@ public:
void onMouseButtonPressed (int left, int top, MyGUI::MouseButton id)
{
if (!mBook)
return;
auto pos = getAdjustedPos(left, top);
if (mPage >= mBook->mPages.size())
return;
// work around inconsistency in MyGUI where the mouse press coordinates aren't
// transformed by the current Layer (even though mouse *move* events are).
MyGUI::IntPoint pos (left, top);
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
pos = mNode->getLayer()->getPosition(left, top);
#endif
pos.left -= mCroppedParent->getAbsoluteLeft ();
pos.top -= mCroppedParent->getAbsoluteTop ();
if (mLastDown == MyGUI::MouseButton::None)
if (pos && mLastDown == MyGUI::MouseButton::None)
{
mFocusItem = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top);
mFocusItem = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr;
mItemActive = true;
dirtyFocusItem ();
@ -1019,25 +1023,11 @@ public:
void onMouseButtonReleased(int left, int top, MyGUI::MouseButton id)
{
if (!mBook)
return;
if (mPage >= mBook->mPages.size())
return;
// work around inconsistency in MyGUI where the mouse release coordinates aren't
// transformed by the current Layer (even though mouse *move* events are).
MyGUI::IntPoint pos (left, top);
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
pos = mNode->getLayer()->getPosition(left, top);
#endif
pos.left -= mCroppedParent->getAbsoluteLeft ();
pos.top -= mCroppedParent->getAbsoluteTop ();
auto pos = getAdjustedPos(left, top);
if (mLastDown == id)
if (pos && mLastDown == id)
{
Style * item = mBook->hitTestWithMargin (pos.left, mViewTop + pos.top);
Style * item = pos->top <= mViewBottom ? mBook->hitTestWithMargin (pos->left, pos->top) : nullptr;
bool clicked = mFocusItem == item;

@ -6,11 +6,6 @@
#include "itemmodel.hpp"
namespace MWWorld
{
class Environment;
}
namespace MyGUI
{
class Gui;
@ -19,7 +14,6 @@ namespace MyGUI
namespace MWGui
{
class WindowManager;
class ContainerWindow;
class ItemView;
class SortFilterItemModel;

@ -148,7 +148,7 @@ namespace MWGui
// We need this copy for when @# hyperlinks are replaced
std::string text = mText;
size_t pos_end;
size_t pos_end = std::string::npos;
for(;;)
{
size_t pos_begin = text.find('@');

@ -15,11 +15,6 @@ namespace Gui
class MWList;
}
namespace MWGui
{
class WindowManager;
}
namespace MWGui
{
class ResponseCallback;

@ -71,13 +71,8 @@ namespace MWGui
, mLastYSize(0)
, mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer()))
, mTrading(false)
, mScaleFactor(1.0f)
, mUpdateTimer(0.f)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale > 1.0)
mScaleFactor = uiScale;
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
mPreview->rebuild();
@ -473,10 +468,11 @@ namespace MWGui
MyGUI::IntSize size = mAvatarImage->getSize();
int width = std::min(mPreview->getTextureWidth(), size.width);
int height = std::min(mPreview->getTextureHeight(), size.height);
mPreview->setViewport(int(width*mScaleFactor), int(height*mScaleFactor));
float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mPreview->setViewport(int(width*scalingFactor), int(height*scalingFactor));
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,
width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight())));
width*scalingFactor/float(mPreview->getTextureWidth()), height*scalingFactor/float(mPreview->getTextureHeight())));
}
void InventoryWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
@ -641,8 +637,9 @@ namespace MWGui
y = (mAvatarImage->getHeight()-1) - y;
// Scale coordinates
x = int(x*mScaleFactor);
y = int(y*mScaleFactor);
float scalingFactor = MWBase::Environment::get().getWindowManager()->getScalingFactor();
x = static_cast<int>(x*scalingFactor);
y = static_cast<int>(y*scalingFactor);
int slot = mPreview->getSlotSelected (x, y);

@ -104,7 +104,6 @@ namespace MWGui
std::unique_ptr<MWRender::InventoryPreview> mPreview;
bool mTrading;
float mScaleFactor;
float mUpdateTimer;
void toggleMaximized();

@ -129,7 +129,7 @@ struct JournalViewModelImpl : JournalViewModel
utf8text.replace(pos_begin, pos_end+1-pos_begin, displayName);
intptr_t value;
intptr_t value = 0;
if (mModel->mKeywordSearch.containsKeyword(topicName, value))
mHyperLinks[std::make_pair(pos_begin, pos_begin+displayName.size())] = value;
}

@ -7,11 +7,6 @@
#include <MyGUI_RenderManager.h>
namespace MWGui
{
class WindowManager;
}
namespace MWRender
{
class RaceSelectionPreview;

@ -11,11 +11,6 @@ namespace ESM
struct Spell;
}
namespace MWGui
{
class WindowManager;
}
namespace MWGui
{
class ReviewDialog : public WindowModal

@ -11,12 +11,16 @@
#include <iomanip>
#include <numeric>
#include <array>
#include <components/debug/debuglog.hpp>
#include <components/misc/stringops.hpp>
#include <components/misc/constants.hpp>
#include <components/widgets/sharedstatebutton.hpp>
#include <components/settings/settings.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -69,6 +73,9 @@ namespace
std::string getAspect (int x, int y)
{
int gcd = std::gcd (x, y);
if (gcd == 0)
return std::string();
int xaspect = x / gcd;
int yaspect = y / gcd;
// special case: 8 : 5 is usually referred to as 16:10
@ -111,11 +118,24 @@ namespace
if (!widget->getUserString(settingMax).empty())
max = MyGUI::utility::parseFloat(widget->getUserString(settingMax));
}
void updateMaxLightsComboBox(MyGUI::ComboBox* box)
{
constexpr int min = 8;
constexpr int max = 32;
constexpr int increment = 8;
int maxLights = Settings::Manager::getInt("max lights", "Shaders");
// show increments of 8 in dropdown
if (maxLights >= min && maxLights <= max && !(maxLights % increment))
box->setIndexSelected((maxLights / increment)-1);
else
box->setIndexSelected(MyGUI::ITEM_NONE);
}
}
namespace MWGui
{
void SettingsWindow::configureWidgets(MyGUI::Widget* widget)
void SettingsWindow::configureWidgets(MyGUI::Widget* widget, bool init)
{
MyGUI::EnumeratorWidgetPtr widgets = widget->getEnumerator();
while (widgets.next())
@ -129,7 +149,8 @@ namespace MWGui
getSettingCategory(current))
? "#{sOn}" : "#{sOff}";
current->castType<MyGUI::Button>()->setCaptionWithReplacing(initialValue);
current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
if (init)
current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
}
if (type == sliderType)
{
@ -149,6 +170,12 @@ namespace MWGui
ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;
valueStr = ss.str();
}
else if (valueType == "Float")
{
std::stringstream ss;
ss << std::fixed << std::setprecision(2) << value;
valueStr = ss.str();
}
else
valueStr = MyGUI::utility::toString(int(value));
@ -163,12 +190,13 @@ namespace MWGui
valueStr = MyGUI::utility::toString(value);
scroll->setScrollPosition(value);
}
scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
if (init)
scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
if (scroll->getVisible())
updateSliderLabel(scroll, valueStr);
}
configureWidgets(current);
configureWidgets(current, init);
}
}
@ -199,7 +227,7 @@ namespace MWGui
getWidget(unusedSlider, widgetName);
unusedSlider->setVisible(false);
configureWidgets(mMainWidget);
configureWidgets(mMainWidget, true);
setTitle("#{sOptions}");
@ -216,6 +244,9 @@ namespace MWGui
getWidget(mControllerSwitch, "ControllerButton");
getWidget(mWaterTextureSize, "WaterTextureSize");
getWidget(mWaterReflectionDetail, "WaterReflectionDetail");
getWidget(mLightingMethodButton, "LightingMethodButton");
getWidget(mLightsResetButton, "LightsResetButton");
getWidget(mMaxLights, "MaxLights");
if (MWBase::Environment::get().getVrMode())
{
@ -247,6 +278,10 @@ namespace MWGui
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);
mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodButtonChanged);
mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);
mMaxLights->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onMaxLightsChanged);
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
@ -273,8 +308,10 @@ namespace MWGui
std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
for (std::pair<int, int>& resolution : resolutions)
{
std::string str = MyGUI::utility::toString(resolution.first) + " x " + MyGUI::utility::toString(resolution.second)
+ " (" + getAspect(resolution.first, resolution.second) + ")";
std::string str = MyGUI::utility::toString(resolution.first) + " x " + MyGUI::utility::toString(resolution.second);
std::string aspect = getAspect(resolution.first, resolution.second);
if (!aspect.empty())
str = str + " (" + aspect + ")";
if (mResolutionList->findItemIndexWith(str) == MyGUI::ITEM_NONE)
mResolutionList->addItem(str);
@ -309,6 +346,8 @@ namespace MWGui
waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail));
mWaterReflectionDetail->setIndexSelected(waterReflectionDetail);
updateMaxLightsComboBox(mMaxLights);
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
mKeyboardSwitch->setStateSelected(true);
@ -409,6 +448,54 @@ namespace MWGui
apply();
}
void SettingsWindow::onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
return;
std::string message = "This change requires a restart to take effect.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, {"#{sOK}"}, true);
Settings::Manager::setString("lighting method", "Shaders", _sender->getItemNameAt(pos));
apply();
}
void SettingsWindow::onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos)
{
int count = 8 * (pos + 1);
Settings::Manager::setInt("max lights", "Shaders", count);
apply();
configureWidgets(mMainWidget, false);
}
void SettingsWindow::onLightsResetButtonClicked(MyGUI::Widget* _sender)
{
std::vector<std::string> buttons = {"#{sYes}", "#{sNo}"};
std::string message = "Resets to default values, would you like to continue? Changes to lighting method will require a restart.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons, true);
int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
if (selectedButton == 1 || selectedButton == -1)
return;
constexpr std::array<const char*, 6> settings = {
"light bounds multiplier",
"maximum light distance",
"light fade start",
"minimum interior brightness",
"max lights",
"lighting method",
};
for (const auto& setting : settings)
Settings::Manager::setString(setting, "Shaders", Settings::Manager::mDefaultSettings[{"Shaders", setting}]);
mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(Settings::Manager::mDefaultSettings[{"Shaders", "lighting method"}]));
updateMaxLightsComboBox(mMaxLights);
apply();
configureWidgets(mMainWidget, false);
}
void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender)
{
std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On");
@ -511,6 +598,12 @@ namespace MWGui
ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;
valueStr = ss.str();
}
else if (valueType == "Float")
{
std::stringstream ss;
ss << std::fixed << std::setprecision(2) << value;
valueStr = ss.str();
}
else
valueStr = MyGUI::utility::toString(int(value));
}
@ -609,6 +702,30 @@ namespace MWGui
layoutControlsBox();
}
void SettingsWindow::updateLightSettings()
{
auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod();
std::string lightingMethodStr = SceneUtil::LightManager::getLightingMethodString(lightingMethod);
mLightingMethodButton->removeAllItems();
std::array<SceneUtil::LightingMethod, 3> methods = {
SceneUtil::LightingMethod::FFP,
SceneUtil::LightingMethod::PerObjectUniform,
SceneUtil::LightingMethod::SingleUBO,
};
for (const auto& method : methods)
{
if (!MWBase::Environment::get().getResourceSystem()->getSceneManager()->isSupportedLightingMethod(method))
continue;
mLightingMethodButton->addItem(SceneUtil::LightManager::getLightingMethodString(method));
}
mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(lightingMethodStr));
}
void SettingsWindow::layoutControlsBox()
{
const int h = 18;
@ -671,6 +788,7 @@ namespace MWGui
{
highlightCurrentResolution();
updateControlsBox();
updateLightSettings();
resetScrollbars();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);
}

@ -3,11 +3,6 @@
#include "windowbase.hpp"
namespace MWGui
{
class WindowManager;
}
namespace MWGui
{
class SettingsWindow : public WindowBase
@ -19,6 +14,8 @@ namespace MWGui
void updateControlsBox();
void updateLightSettings();
void onResChange(int, int) override { center(); }
protected:
@ -39,6 +36,10 @@ namespace MWGui
MyGUI::ComboBox* mWaterTextureSize;
MyGUI::ComboBox* mWaterReflectionDetail;
MyGUI::ComboBox* mMaxLights;
MyGUI::ComboBox* mLightingMethodButton;
MyGUI::Button* mLightsResetButton;
// controls
MyGUI::ScrollView* mControlsBox;
MyGUI::Button* mResetControlsButton;
@ -62,6 +63,10 @@ namespace MWGui
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightsResetButtonClicked(MyGUI::Widget* _sender);
void onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos);
void onRebindAction(MyGUI::Widget* _sender);
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);
void onResetDefaultBindings(MyGUI::Widget* _sender);
@ -73,7 +78,7 @@ namespace MWGui
void apply();
void configureWidgets(MyGUI::Widget* widget);
void configureWidgets(MyGUI::Widget* widget, bool init);
void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value);
void layoutControlsBox();

@ -15,11 +15,6 @@ namespace MyGUI
class Widget;
}
namespace MWGui
{
class WindowManager;
}
namespace MWGui
{
class SpellBuyingWindow : public ReferenceInterface, public WindowBase

@ -393,7 +393,8 @@ namespace MWGui
MWWorld::Ptr player = MWMechanics::getPlayer();
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
if (MyGUI::utility::parseInt(mPriceLabel->getCaption()) > playerGold)
int price = MyGUI::utility::parseInt(mPriceLabel->getCaption());
if (price > playerGold)
{
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}");
return;
@ -401,8 +402,6 @@ namespace MWGui
mSpell.mName = mNameEdit->getCaption();
int price = MyGUI::utility::parseInt(mPriceLabel->getCaption());
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
// add gold to NPC trading gold pool

@ -97,7 +97,7 @@ namespace MWGui
int windowHeight = window->getSize().height;
//initial values defined in openmw_stats_window.layout, if custom options are not present in .layout, a default is loaded
float leftPaneRatio = 0.44;
float leftPaneRatio = 0.44f;
if (mLeftPane->isUserString("LeftPaneRatio"))
leftPaneRatio = MyGUI::utility::parseFloat(mLeftPane->getUserString("LeftPaneRatio"));

@ -6,8 +6,6 @@
namespace MWGui
{
class WindowManager;
class StatsWindow : public WindowPinnableBase, public NoDrop, public StatsListener
{
public:

@ -3,11 +3,6 @@
#include "windowbase.hpp"
namespace MWGui
{
class WindowManager;
}
namespace MWGui
{
class TextInputDialog : public WindowModal

@ -58,12 +58,12 @@ namespace MWGui
}
}
int TimeAdvancer::getHours()
int TimeAdvancer::getHours() const
{
return mHours;
}
bool TimeAdvancer::isRunning()
bool TimeAdvancer::isRunning() const
{
return mRunning;
}

@ -14,8 +14,8 @@ namespace MWGui
void stop();
void onFrame(float dt);
int getHours();
bool isRunning();
int getHours() const;
bool isRunning() const;
// signals
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;

@ -11,12 +11,6 @@ namespace MyGUI
class Widget;
}
namespace MWGui
{
class WindowManager;
}
namespace MWGui
{
class TravelWindow : public ReferenceInterface, public WindowBase

@ -3,11 +3,6 @@
#include "layout.hpp"
namespace MWBase
{
class WindowManager;
}
namespace MWWorld
{
class Ptr;
@ -15,7 +10,6 @@ namespace MWWorld
namespace MWGui
{
class WindowManager;
class DragAndDrop;
class WindowBase: public Layout

@ -1,5 +1,6 @@
#include "windowmanagerimp.hpp"
#include <algorithm>
#include <cassert>
#include <chrono>
#include <thread>
@ -199,8 +200,8 @@ namespace MWGui
, mVersionDescription(versionDescription)
, mWindowVisible(true)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
mScalingFactor = std::clamp(Settings::Manager::getFloat("scaling factor", "GUI"), 0.5f, 8.f);
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), mScalingFactor);
mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string());
@ -216,7 +217,7 @@ namespace MWGui
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
// Load fonts
mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath));
mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath, mScalingFactor));
mFontLoader->loadBitmapFonts(exportFonts);
//Register own widgets with MyGUI
@ -1372,6 +1373,11 @@ namespace MWGui
return mHud->getWorldMouseOver();
}
float WindowManager::getScalingFactor()
{
return mScalingFactor;
}
void WindowManager::executeInConsole (const std::string& path)
{
mConsole->executeFile (path);

@ -120,7 +120,6 @@ namespace MWGui
class TrainingWindow;
class SpellIcons;
class MerchantRepair;
class Repair;
class SoulgemDialog;
class Recharge;
class CompanionWindow;
@ -216,6 +215,8 @@ namespace MWGui
void setDragDrop(bool dragDrop) override;
bool getWorldMouseOver() override;
float getScalingFactor() override;
bool toggleFogOfWar() override;
bool toggleFullHelp() override; ///< show extra info in item tooltips (owner, script)
bool getFullHelp() const override;
@ -532,6 +533,8 @@ namespace MWGui
SDLUtil::VideoWrapper* mVideoWrapper;
float mScalingFactor;
/**
* Called when MyGUI tries to retrieve a tag's value. Tags must be denoted in #{tag} notation and will be replaced upon setting a user visible text/property.
* Supported syntax:

@ -5,8 +5,6 @@
namespace MWGui
{
class WindowManager;
class WindowPinnableBase: public WindowBase
{
public:

@ -32,7 +32,6 @@ namespace MWInput
, mMouseManager(mouseManager)
, mJoystickEnabled (Settings::Manager::getBool("enable controller", "Input"))
, mGamepadCursorSpeed(Settings::Manager::getFloat("gamepad cursor speed", "Input"))
, mInvUiScalingFactor(1.f)
, mSneakToggleShortcutTimer(0.f)
, mGamepadZoom(0)
, mGamepadGuiCursorEnabled(true)
@ -69,10 +68,6 @@ namespace MWInput
}
}
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale != 0.f)
mInvUiScalingFactor = 1.f / uiScale;
float deadZoneRadius = Settings::Manager::getFloat("joystick dead zone", "Input");
deadZoneRadius = std::min(std::max(deadZoneRadius, 0.0f), 0.5f);
mBindingsManager->setJoystickDeadZone(deadZoneRadius);
@ -102,8 +97,10 @@ namespace MWInput
// We keep track of our own mouse position, so that moving the mouse while in
// game mode does not move the position of the GUI cursor
float xMove = xAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
float yMove = yAxis * dt * 1500.0f * mInvUiScalingFactor * mGamepadCursorSpeed;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
float xMove = xAxis * dt * 1500.0f / uiScale;
float yMove = yAxis * dt * 1500.0f / uiScale;
float mouseWheelMove = -zAxis * dt * 1500.0f;
if (xMove != 0 || yMove != 0 || mouseWheelMove != 0)
{

@ -52,7 +52,6 @@ namespace MWInput
bool mJoystickEnabled;
float mGamepadCursorSpeed;
float mInvUiScalingFactor;
float mSneakToggleShortcutTimer;
float mGamepadZoom;
bool mGamepadGuiCursorEnabled;

@ -29,22 +29,18 @@ namespace MWInput
, mCameraYMultiplier(Settings::Manager::getFloat("camera y multiplier", "Input"))
, mBindingsManager(bindingsManager)
, mInputWrapper(inputWrapper)
, mInvUiScalingFactor(1.f)
, mGuiCursorX(0)
, mGuiCursorY(0)
, mMouseWheel(0)
, mMouseLookEnabled(false)
, mGuiCursorEnabled(true)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale != 0.f)
mInvUiScalingFactor = 1.f / uiScale;
int w,h;
SDL_GetWindowSize(window, &w, &h);
mGuiCursorX = mInvUiScalingFactor * w / 2.f;
mGuiCursorY = mInvUiScalingFactor * h / 2.f;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mGuiCursorX = w / (2.f * uiScale);
mGuiCursorY = h / (2.f * uiScale);
}
void MouseManager::processChangedSettings(const Settings::CategorySettingVector& changed)
@ -82,8 +78,9 @@ namespace MWInput
// We keep track of our own mouse position, so that moving the mouse while in
// game mode does not move the position of the GUI cursor
mGuiCursorX = static_cast<float>(arg.x) * mInvUiScalingFactor;
mGuiCursorY = static_cast<float>(arg.y) * mInvUiScalingFactor;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mGuiCursorX = static_cast<float>(arg.x) / uiScale;
mGuiCursorY = static_cast<float>(arg.y) / uiScale;
mMouseWheel = static_cast<int>(arg.z);
@ -254,17 +251,14 @@ namespace MWInput
void MouseManager::warpMouse()
{
mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX / mInvUiScalingFactor), static_cast<int>(mGuiCursorY / mInvUiScalingFactor));
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX*uiScale), static_cast<int>(mGuiCursorY*uiScale));
}
void MouseManager::setMousePosition(int x, int y)
{
mGuiCursorX = x * mInvUiScalingFactor;
mGuiCursorY = y * mInvUiScalingFactor;
}
void MouseManager::setGUIScale(float scale)
{
mInvUiScalingFactor = 1.f / scale;
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
mGuiCursorX = x / uiScale;
mGuiCursorY = y / uiScale;
}
}

@ -40,7 +40,6 @@ namespace MWInput
// Used to override mouse position when using controllers not through SDL, such as OpenXR.
void setMousePosition(int x, int y);
void setGUIScale(float scale);
private:
bool mInvertX;
@ -51,7 +50,6 @@ namespace MWInput
BindingsManager* mBindingsManager;
SDLUtil::InputWrapper* mInputWrapper;
float mInvUiScalingFactor;
float mGuiCursorX;
float mGuiCursorY;

@ -432,7 +432,8 @@ namespace MWMechanics
}
void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance)
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,
bool inCombatOrPursue)
{
if (!actor.getRefData().getBaseNode())
return;
@ -453,7 +454,7 @@ namespace MWMechanics
const osg::Vec3f actor2Pos(targetActor.getRefData().getPosition().asVec3());
float sqrDist = (actor1Pos - actor2Pos).length2();
if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance))
if (sqrDist > std::min(maxDistance * maxDistance, sqrHeadTrackDistance) && !inCombatOrPursue)
return;
// stop tracking when target is behind the actor
@ -461,7 +462,7 @@ namespace MWMechanics
osg::Vec3f targetDirection(actor2Pos - actor1Pos);
actorDirection.z() = 0;
targetDirection.z() = 0;
if (actorDirection * targetDirection > 0
if ((actorDirection * targetDirection > 0 || inCombatOrPursue)
&& MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor))
{
@ -2012,28 +2013,25 @@ namespace MWMechanics
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue);
MWWorld::Ptr activePackageTarget;
// 1. Unconsious actor can not track target
// 2. Actors in combat and pursue mode do not bother to headtrack
// 2. Actors in combat and pursue mode do not bother to headtrack anyone except their target
// 3. Player character does not use headtracking in the 1st-person view
if (!stats.getKnockedDown() && !firstPersonPlayer && !inCombatOrPursue)
if (!stats.getKnockedDown() && !firstPersonPlayer)
{
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
if (inCombatOrPursue)
activePackageTarget = stats.getAiSequence().getActivePackage().getTarget();
for (PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{
if (it->first == iter->first)
continue;
updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance);
}
}
if (!stats.getKnockedDown() && !isPlayer && inCombatOrPursue)
{
// Actors in combat and pursue mode always look at their target.
for (const auto& package : stats.getAiSequence())
{
headTrackTarget = package->getTarget();
if (!headTrackTarget.isEmpty())
break;
if (inCombatOrPursue && it->first != activePackageTarget)
continue;
updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance, inCombatOrPursue);
}
}

@ -131,7 +131,8 @@ namespace MWMechanics
void turnActorToFacePlayer(const MWWorld::Ptr& actor, Actor& actorState, const osg::Vec3f& dir);
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance,
bool inCombatOrPursue);
void rest(double hours, bool sleep);
///< Update actors while the player is waiting or sleeping.

@ -23,4 +23,10 @@ namespace MWMechanics
MWBase::World* world = MWBase::Environment::get().getWorld();
return (actor.getClass().canSwim(actor) && world->isSwimming(actor)) || world->isFlying(actor);
}
bool hasWaterWalking(const MWWorld::Ptr& actor)
{
const MWMechanics::MagicEffects& effects = actor.getClass().getCreatureStats(actor).getMagicEffects();
return effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() > 0;
}
}

@ -31,6 +31,7 @@ namespace MWMechanics
MWWorld::Ptr getPlayer();
bool isPlayerInCombat();
bool canActorMoveByZAxis(const MWWorld::Ptr& actor);
bool hasWaterWalking(const MWWorld::Ptr& actor);
template<class T>
void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value)

@ -45,13 +45,13 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
return true; //Door is no longer opening
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
float x = pos.pos[0] - tPos.pos[0];
float y = pos.pos[1] - tPos.pos[1];
float x = pos.pos[1] - tPos.pos[1];
float y = pos.pos[0] - tPos.pos[0];
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
// Turn away from the door and move when turn completed
if (zTurn(actor, std::atan2(x,y) + getAdjustedAngle(), osg::DegreesToRadians(5.f)))
if (zTurn(actor, std::atan2(y,x) + getAdjustedAngle(), osg::DegreesToRadians(5.f)))
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
else
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;

@ -23,7 +23,7 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
actorClass.getMovementSettings(actor).mPosition[1] = 1;
smoothTurn(actor, -osg::PI / 2, 0);
smoothTurn(actor, static_cast<float>(-osg::PI_2), 0);
return false;
}

@ -228,7 +228,6 @@ namespace MWMechanics
const osg::Vec3f vActorPos(pos.asVec3());
const osg::Vec3f vTargetPos(target.getRefData().getPosition().asVec3());
osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target);
float distToTarget = MWBase::Environment::get().getWorld()->getHitDistance(actor, target);
storage.mReadyToAttack = (currentAction->isAttackingOrSpell() && distToTarget <= rangeAttack && storage.mLOS);
@ -236,13 +235,14 @@ namespace MWMechanics
if (isRangedCombat)
{
// rotate actor taking into account target movement direction and projectile speed
vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength);
osg::Vec3f vAimDir = AimDirToMovingTarget(actor, target, storage.mLastTargetPos, AI_REACTION_TIME, (weapon ? weapon->mData.mType : 0), storage.mStrength);
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
storage.mMovement.mRotation[2] = getZAngleToDir(vAimDir);
}
else
{
osg::Vec3f vAimDir = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, false);
storage.mMovement.mRotation[0] = getXAngleToDir(vAimDir);
storage.mMovement.mRotation[2] = getZAngleToDir((vTargetPos-vActorPos)); // using vAimDir results in spastic movements since the head is animated
}
@ -263,8 +263,9 @@ namespace MWMechanics
&& ((!storage.mReadyToAttack && !mPathFinder.isPathConstructed())
|| (storage.mUseCustomDestination && (storage.mCustomDestination - vTargetPos).length() > rangeAttack)))
{
const MWBase::World* world = MWBase::Environment::get().getWorld();
// Try to build path to the target.
const auto halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
const auto halfExtents = world->getPathfindingHalfExtents(actor);
const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor);
const auto pathGridGraph = getPathGridGraph(actor.getCell());
@ -274,11 +275,7 @@ namespace MWMechanics
{
// If there is no path, try to find a point on a line from the actor position to target projected
// on navmesh to attack the target from there.
const MWBase::World* world = MWBase::Environment::get().getWorld();
const auto halfExtents = world->getPathfindingHalfExtents(actor);
const auto navigator = world->getNavigator();
const auto navigatorFlags = getNavigatorFlags(actor);
const auto areaCosts = getAreaCosts(actor);
const auto hit = navigator->raycast(halfExtents, vActorPos, vTargetPos, navigatorFlags);
if (hit.has_value() && (*hit - vTargetPos).length() <= rangeAttack)
@ -534,7 +531,7 @@ namespace MWMechanics
// Otherwise apply a random side step (kind of dodging) with some probability
// if actor is within range of target's weapon.
if (std::abs(angleToTarget) > osg::PI / 4)
moveDuration = 0.2;
moveDuration = 0.2f;
else if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)
moveDuration = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
if (moveDuration > 0)
@ -698,7 +695,7 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t
// idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same
osg::Vec3f vTargetPos = target.getRefData().getPosition().asVec3();
osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target);
osg::Vec3f vDirToTarget = MWBase::Environment::get().getWorld()->aimToTarget(actor, target, true);
float distToTarget = vDirToTarget.length();
osg::Vec3f vTargetMoveDir = vTargetPos - vLastTargetPos;

@ -25,6 +25,14 @@
#include <osg/Quat>
namespace
{
float divOrMax(float dividend, float divisor)
{
return divisor == 0 ? std::numeric_limits<float>::max() * std::numeric_limits<float>::epsilon() : dividend / divisor;
}
}
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
mTypeId(typeId),
mOptions(options),
@ -411,13 +419,20 @@ bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& act
DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld::Ptr& actor) const
{
static const bool allowToFollowOverWaterSurface = Settings::Manager::getBool("allow actors to follow over water surface", "Game");
const MWWorld::Class& actorClass = actor.getClass();
DetourNavigator::Flags result = DetourNavigator::Flag_none;
if (actorClass.isPureWaterCreature(actor) || (getTypeId() != AiPackageTypeId::Wander && actorClass.canSwim(actor)))
if ((actorClass.isPureWaterCreature(actor)
|| (getTypeId() != AiPackageTypeId::Wander
&& ((allowToFollowOverWaterSurface && getTypeId() == AiPackageTypeId::Follow)
|| actorClass.canSwim(actor)
|| hasWaterWalking(actor)))
) && actorClass.getSwimSpeed(actor) > 0)
result |= DetourNavigator::Flag_swim;
if (actorClass.canWalk(actor))
if (actorClass.canWalk(actor) && actor.getClass().getWalkSpeed(actor) > 0)
result |= DetourNavigator::Flag_walk;
if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander)
@ -433,15 +448,15 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P
const MWWorld::Class& actorClass = actor.getClass();
if (flags & DetourNavigator::Flag_swim)
costs.mWater = costs.mWater / actorClass.getSwimSpeed(actor);
costs.mWater = divOrMax(costs.mWater, actorClass.getSwimSpeed(actor));
if (flags & DetourNavigator::Flag_walk)
{
float walkCost;
if (getTypeId() == AiPackageTypeId::Wander)
walkCost = 1.0 / actorClass.getWalkSpeed(actor);
walkCost = divOrMax(1.0, actorClass.getWalkSpeed(actor));
else
walkCost = 1.0 / actorClass.getRunSpeed(actor);
walkCost = divOrMax(1.0, actorClass.getRunSpeed(actor));
costs.mDoor = costs.mDoor * walkCost;
costs.mPathgrid = costs.mPathgrid * walkCost;
costs.mGround = costs.mGround * walkCost;

@ -217,38 +217,48 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle)
if(mHitState == CharState_None)
{
if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0
|| mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0)
&& mAnimation->hasAnimation("knockout"))
|| mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0))
{
mTimeUntilWake = Misc::Rng::rollClosedProbability() * 2 + 1; // Wake up after 1 to 3 seconds
if (isSwimming && mAnimation->hasAnimation("swimknockout"))
{
mHitState = CharState_SwimKnockOut;
mCurrentHit = "swimknockout";
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul);
}
else
else if (!isSwimming && mAnimation->hasAnimation("knockout"))
{
mHitState = CharState_KnockOut;
mCurrentHit = "knockout";
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul);
}
else
{
// Knockout animations are missing. Fall back to idle animation, so target actor still can be killed via HtH.
mCurrentHit.erase();
}
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul);
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true);
}
else if(knockdown && mAnimation->hasAnimation("knockdown"))
else if (knockdown)
{
if (isSwimming && mAnimation->hasAnimation("swimknockdown"))
{
mHitState = CharState_SwimKnockDown;
mCurrentHit = "swimknockdown";
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
}
else
else if (!isSwimming && mAnimation->hasAnimation("knockdown"))
{
mHitState = CharState_KnockDown;
mCurrentHit = "knockdown";
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
}
else
{
// Knockdown animation is missing. Cancel knockdown state.
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false);
}
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
}
else if (recovery)
{
@ -2968,7 +2978,7 @@ void CharacterController::updateHeadTracking(float duration)
}
else
// no head node to look at, fall back to look at center of collision box
direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget);
direction = MWBase::Environment::get().getWorld()->aimToTarget(mPtr, mHeadTrackTarget, false);
}
direction.normalize();

@ -681,7 +681,7 @@ namespace MWMechanics
// Deviating from Morrowind here: it doesn't increase disposition on marginal wins,
// which seems to be a bug (MCP fixes it too).
// Original logic: x = 0, y = -iPerMinChange
x = -iPerMinChange;
x = iPerMinChange;
y = x; // This goes unused.
}
else

@ -153,23 +153,23 @@ namespace MWMechanics
void SpellList::addListener(Spells* spells)
{
for(const auto ptr : mListeners)
{
if(ptr == spells)
return;
}
if (std::find(mListeners.begin(), mListeners.end(), spells) != mListeners.end())
return;
mListeners.push_back(spells);
}
void SpellList::removeListener(Spells* spells)
{
for(auto it = mListeners.begin(); it != mListeners.end(); it++)
{
if(*it == spells)
{
mListeners.erase(it);
break;
}
}
const auto it = std::find(mListeners.begin(), mListeners.end(), spells);
if (it != mListeners.end())
mListeners.erase(it);
}
void SpellList::updateListener(Spells* before, Spells* after)
{
const auto it = std::find(mListeners.begin(), mListeners.end(), before);
if (it == mListeners.end())
return mListeners.push_back(after);
*it = after;
}
}

@ -61,6 +61,8 @@ namespace MWMechanics
void removeListener(Spells* spells);
void updateListener(Spells* before, Spells* after);
const std::vector<std::string> getSpells() const;
};
}

@ -32,6 +32,15 @@ namespace MWMechanics
mSpellList->addListener(this);
}
Spells::Spells(Spells&& spells) : mSpellList(std::move(spells.mSpellList)), mSpells(std::move(spells.mSpells)),
mSelectedSpell(std::move(spells.mSelectedSpell)), mUsedPowers(std::move(spells.mUsedPowers)),
mSpellsChanged(std::move(spells.mSpellsChanged)), mEffects(std::move(spells.mEffects)),
mSourcedEffects(std::move(spells.mSourcedEffects))
{
if (mSpellList)
mSpellList->updateListener(&spells, this);
}
std::map<const ESM::Spell*, SpellParams>::const_iterator Spells::begin() const
{
return mSpells.begin();

@ -59,7 +59,7 @@ namespace MWMechanics
Spells(const Spells&);
Spells(const Spells&&) = delete;
Spells(Spells&& spells);
~Spells();

@ -121,6 +121,7 @@ void Actor::updatePosition()
mPreviousPosition = mWorldPosition;
mPosition = mWorldPosition;
mSimulationPosition = mWorldPosition;
mPositionOffset = osg::Vec3f();
mStandingOnPtr = nullptr;
mSkipSimulation = true;
}
@ -179,9 +180,10 @@ bool Actor::setPosition(const osg::Vec3f& position)
if (mSkipSimulation)
return false;
bool hasChanged = mPosition != position || mPositionOffset.length() != 0 || mWorldPositionChanged;
mPreviousPosition = mPosition + mPositionOffset;
mPosition = position + mPositionOffset;
mPositionOffset = osg::Vec3f();
updateWorldPosition();
applyOffsetChange();
mPreviousPosition = mPosition;
mPosition = position;
return hasChanged;
}
@ -195,9 +197,9 @@ void Actor::applyOffsetChange()
{
if (mPositionOffset.length() == 0)
return;
mWorldPosition += mPositionOffset;
mPosition += mPositionOffset;
mPreviousPosition += mPositionOffset;
mSimulationPosition += mPositionOffset;
mPositionOffset = osg::Vec3f();
mWorldPositionChanged = true;
}

@ -17,10 +17,10 @@ namespace MWPhysics
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
static constexpr int sMaxIterations = 8;
// Allows for more precise movement solving without getting stuck or snagging too easily.
static constexpr float sCollisionMargin = 0.1;
static constexpr float sCollisionMargin = 0.1f;
// Allow for a small amount of penetration to prevent numerical precision issues from causing the "unstuck"ing code to run unnecessarily
// Currently set to 0 because having the "unstuck"ing code run whenever possible prevents some glitchy snagging issues
static constexpr float sAllowedPenetration = 0.0;
static constexpr float sAllowedPenetration = 0.0f;
}
#endif

@ -593,8 +593,7 @@ namespace MWPhysics
const auto verticalHalfExtent = osg::Vec3f(0.0, 0.0, physicActor->getHalfExtents().z());
// use a 3d approximation of the movement vector to better judge player intent
const ESM::Position& refpos = ptr.getRefData().getPosition();
auto velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement;
auto velocity = (osg::Quat(actor.mRefpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(actor.mRefpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement;
// try to pop outside of the world before doing anything else if we're inside of it
if (!physicActor->getOnGround() || physicActor->getOnSlope())
velocity += physicActor->getInertialForce();

@ -9,6 +9,7 @@
#include "components/settings/settings.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/movement.hpp"
#include "../mwrender/bulletdebugdraw.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
@ -137,11 +138,12 @@ namespace
namespace MWPhysics
{
PhysicsTaskScheduler::PhysicsTaskScheduler(float physicsDt, std::shared_ptr<btCollisionWorld> collisionWorld)
PhysicsTaskScheduler::PhysicsTaskScheduler(float physicsDt, btCollisionWorld *collisionWorld, MWRender::DebugDrawer* debugDrawer)
: mDefaultPhysicsDt(physicsDt)
, mPhysicsDt(physicsDt)
, mTimeAccum(0.f)
, mCollisionWorld(std::move(collisionWorld))
, mCollisionWorld(collisionWorld)
, mDebugDrawer(debugDrawer)
, mNumJobs(0)
, mRemainingSteps(0)
, mLOSCacheExpiry(Settings::Manager::getInt("lineofsight keep inactive cache", "Physics"))
@ -185,7 +187,7 @@ namespace MWPhysics
if (data.mActor.lock())
{
std::unique_lock lock(mCollisionWorldMutex);
MovementSolver::unstuck(data, mCollisionWorld.get());
MovementSolver::unstuck(data, mCollisionWorld);
}
});
@ -315,7 +317,7 @@ namespace MWPhysics
// init
for (auto& data : actorsData)
data.updatePosition();
data.updatePosition(mCollisionWorld);
mPrevStepCount = numSteps;
mRemainingSteps = numSteps;
mTimeAccum = timeAccum;
@ -381,7 +383,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback)
{
std::shared_lock lock(mCollisionWorldMutex);
ContactTestWrapper::contactTest(mCollisionWorld.get(), colObj, resultCallback);
ContactTestWrapper::contactTest(mCollisionWorld, colObj, resultCallback);
}
std::optional<btVector3> PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target)
@ -532,7 +534,7 @@ namespace MWPhysics
if(const auto actor = mActorsFrameData[job].mActor.lock())
{
MaybeSharedLock lockColWorld(mCollisionWorldMutex, mThreadSafeBullet);
MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld.get(), *mWorldFrameData);
MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld, *mWorldFrameData);
}
}
@ -594,8 +596,8 @@ namespace MWPhysics
{
for (auto& actorData : mActorsFrameData)
{
MovementSolver::unstuck(actorData, mCollisionWorld.get());
MovementSolver::move(actorData, mPhysicsDt, mCollisionWorld.get(), *mWorldFrameData);
MovementSolver::unstuck(actorData, mCollisionWorld);
MovementSolver::move(actorData, mPhysicsDt, mCollisionWorld, *mWorldFrameData);
}
updateActorsPositions();
@ -626,4 +628,10 @@ namespace MWPhysics
mTimeBegin = mTimer->tick();
mFrameNumber = frameNumber;
}
void PhysicsTaskScheduler::debugDraw()
{
std::shared_lock lock(mCollisionWorldMutex);
mDebugDrawer->step();
}
}

@ -20,12 +20,17 @@ namespace Misc
class Barrier;
}
namespace MWRender
{
class DebugDrawer;
}
namespace MWPhysics
{
class PhysicsTaskScheduler
{
public:
PhysicsTaskScheduler(float physicsDt, std::shared_ptr<btCollisionWorld> collisionWorld);
PhysicsTaskScheduler(float physicsDt, btCollisionWorld* collisionWorld, MWRender::DebugDrawer* debugDrawer);
~PhysicsTaskScheduler();
/// @brief move actors taking into account desired movements and collisions
@ -49,6 +54,7 @@ namespace MWPhysics
void removeCollisionObject(btCollisionObject* collisionObject);
void updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate=false);
bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2);
void debugDraw();
private:
void syncComputation();
@ -67,7 +73,8 @@ namespace MWPhysics
float mDefaultPhysicsDt;
float mPhysicsDt;
float mTimeAccum;
std::shared_ptr<btCollisionWorld> mCollisionWorld;
btCollisionWorld* mCollisionWorld;
MWRender::DebugDrawer* mDebugDrawer;
std::vector<LOSRequest> mLOSCache;
std::set<std::weak_ptr<PtrHolder>, std::owner_less<std::weak_ptr<PtrHolder>>> mUpdateAabb;

@ -60,6 +60,22 @@
#include "movementsolver.hpp"
#include "mtphysics.hpp"
namespace
{
bool canMoveToWaterSurface(const MWPhysics::Actor* physicActor, const float waterlevel, btCollisionWorld* world)
{
if (!physicActor)
return false;
const float halfZ = physicActor->getHalfExtents().z();
const osg::Vec3f actorPosition = physicActor->getPosition();
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
MWPhysics::ActorTracer tracer;
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, world);
return (tracer.mFraction >= 1.0f);
}
}
namespace MWPhysics
{
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
@ -79,7 +95,7 @@ namespace MWPhysics
mDispatcher = std::make_unique<btCollisionDispatcher>(mCollisionConfiguration.get());
mBroadphase = std::make_unique<btDbvtBroadphase>();
mCollisionWorld = std::make_shared<btCollisionWorld>(mDispatcher.get(), mBroadphase.get(), mCollisionConfiguration.get());
mCollisionWorld = std::make_unique<btCollisionWorld>(mDispatcher.get(), mBroadphase.get(), mCollisionConfiguration.get());
// Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this.
// Should a "static" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb.
@ -97,8 +113,8 @@ namespace MWPhysics
}
}
mTaskScheduler = std::make_unique<PhysicsTaskScheduler>(mPhysicsDt, mCollisionWorld);
mDebugDrawer = std::make_unique<MWRender::DebugDrawer>(mParentNode, mCollisionWorld.get(), mDebugDrawEnabled);
mTaskScheduler = std::make_unique<PhysicsTaskScheduler>(mPhysicsDt, mCollisionWorld.get(), mDebugDrawer.get());
}
PhysicsSystem::~PhysicsSystem()
@ -347,16 +363,7 @@ namespace MWPhysics
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
{
const Actor* physicActor = getActor(actor);
if (!physicActor)
return false;
const float halfZ = physicActor->getHalfExtents().z();
const osg::Vec3f actorPosition = physicActor->getPosition();
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
ActorTracer tracer;
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld.get());
return (tracer.mFraction >= 1.0f);
return ::canMoveToWaterSurface(getActor(actor), waterlevel, mCollisionWorld.get());
}
osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const
@ -605,9 +612,6 @@ namespace MWPhysics
return nullptr;
}();
if (caster == nullptr)
Log(Debug::Warning) << "No caster for projectile " << projectileId;
ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile);
resultCallback.m_collisionFilterMask = 0xff;
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
@ -772,16 +776,10 @@ namespace MWPhysics
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
bool waterCollision = false;
bool moveToWaterSurface = false;
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
{
if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
waterCollision = true;
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel))
{
moveToWaterSurface = true;
if (physicActor->getCollisionMode() || !world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
waterCollision = true;
}
}
physicActor->setCanWaterWalk(waterCollision);
@ -794,7 +792,7 @@ namespace MWPhysics
if (!willSimulate)
standingOn = physicActor->getStandingOnPtr();
actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel);
actorsFrameData.emplace_back(std::move(physicActor), standingOn, waterCollision, movement, slowFall, waterlevel);
}
mMovementQueue.clear();
return actorsFrameData;
@ -827,7 +825,7 @@ namespace MWPhysics
void PhysicsSystem::debugDraw()
{
if (mDebugDrawEnabled)
mDebugDrawer->step();
mTaskScheduler->debugDraw();
}
bool PhysicsSystem::isActorStandingOn(const MWWorld::Ptr &actor, const MWWorld::ConstPtr &object) const
@ -937,9 +935,9 @@ namespace MWPhysics
}
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,
bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel)
bool waterCollision, osg::Vec3f movement, float slowFall, float waterlevel)
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface),
mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision),
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
@ -953,7 +951,7 @@ namespace MWPhysics
mWasOnGround = actor->getOnGround();
}
void ActorFrameData::updatePosition()
void ActorFrameData::updatePosition(btCollisionWorld* world)
{
mActorRaw->updateWorldPosition();
// If physics runs "fast enough", position are interpolated without simulation
@ -961,10 +959,10 @@ namespace MWPhysics
// regardless of simulation speed.
mActorRaw->applyOffsetChange();
mPosition = mActorRaw->getPosition();
if (mMoveToWaterSurface)
if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world))
{
mPosition.z() = mWaterlevel;
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z());
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z(), false);
}
mOldHeight = mPosition.z();
mRefpos = mActorRaw->getPtr().getRefData().getPosition();

@ -79,7 +79,7 @@ namespace MWPhysics
struct ActorFrameData
{
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
void updatePosition();
void updatePosition(btCollisionWorld* world);
std::weak_ptr<Actor> mActor;
Actor* mActorRaw;
MWWorld::Ptr mStandingOn;
@ -90,7 +90,7 @@ namespace MWPhysics
bool mDidJump;
bool mFloatToSurface;
bool mNeedLand;
bool mMoveToWaterSurface;
bool mWaterCollision;
float mWaterlevel;
float mSlowFall;
float mOldHeight;
@ -259,7 +259,7 @@ namespace MWPhysics
std::unique_ptr<btBroadphaseInterface> mBroadphase;
std::unique_ptr<btDefaultCollisionConfiguration> mCollisionConfiguration;
std::unique_ptr<btCollisionDispatcher> mDispatcher;
std::shared_ptr<btCollisionWorld> mCollisionWorld;
std::unique_ptr<btCollisionWorld> mCollisionWorld;
std::unique_ptr<PhysicsTaskScheduler> mTaskScheduler;
std::unique_ptr<Resource::BulletShapeManager> mShapeManager;

@ -60,14 +60,14 @@ namespace MWPhysics
// attempt 3: further, less tall fixed distance movement, same as above
// If you're making a full conversion you should purge the logic for attempts 2 and 3. Attempts 2 and 3 just try to work around problems with vanilla Morrowind assets.
int attempt = 0;
float downStepSize;
float downStepSize = 0;
while(attempt < 3)
{
attempt++;
if(attempt == 1)
tracerDest = tracerPos + toMove;
else if (!firstIteration || !sDoExtraStairHacks) // first attempt failed and not on first movement solver iteration, can't retry -- or we have extra hacks disabled
else if (!sDoExtraStairHacks) // early out if we have extra hacks disabled
{
return false;
}

@ -358,6 +358,8 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
}
mScabbard = attachMesh(scabbardName, boneName);
if (mScabbard)
resetControllers(mScabbard->getNode());
osg::Group* weaponNode = getBoneByName("Bip01 Weapon");
if (!weaponNode)

@ -500,6 +500,11 @@ namespace MWRender
mAlpha = alpha;
}
void setLightSource(const osg::ref_ptr<SceneUtil::LightSource>& lightSource)
{
mLightSource = lightSource;
}
protected:
void setDefaults(osg::StateSet* stateset) override
{
@ -522,10 +527,13 @@ namespace MWRender
{
osg::Material* material = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
material->setAlpha(osg::Material::FRONT_AND_BACK, mAlpha);
if (mLightSource)
mLightSource->setActorFade(mAlpha);
}
private:
float mAlpha;
osg::ref_ptr<SceneUtil::LightSource> mLightSource;
};
struct Animation::AnimSource
@ -1667,7 +1675,7 @@ namespace MWRender
{
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
mExtraLightSource = SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
}
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)
@ -1817,6 +1825,7 @@ namespace MWRender
if (mTransparencyUpdater == nullptr)
{
mTransparencyUpdater = new TransparencyUpdater(alpha);
mTransparencyUpdater->setLightSource(mExtraLightSource);
mObjectRoot->addCullCallback(mTransparencyUpdater);
}
else

@ -278,6 +278,7 @@ protected:
osg::ref_ptr<SceneUtil::LightSource> mGlowLight;
osg::ref_ptr<SceneUtil::GlowUpdater> mGlowUpdater;
osg::ref_ptr<TransparencyUpdater> mTransparencyUpdater;
osg::ref_ptr<SceneUtil::LightSource> mExtraLightSource;
float mAlpha;

@ -453,7 +453,7 @@ namespace MWRender
void Camera::setPitch(float angle)
{
const float epsilon = 0.000001f;
float limit = osg::PI_2 - epsilon;
float limit = static_cast<float>(osg::PI_2) - epsilon;
mPitch = osg::clampBetween(angle, -limit, limit);
}

@ -174,7 +174,9 @@ namespace MWRender
mCamera->setNodeMask(Mask_RenderToTexture);
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager;
bool ffp = mResourceSystem->getSceneManager()->getLightingMethod() == SceneUtil::LightingMethod::FFP;
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager(ffp);
lightManager->setStartLight(1);
osg::ref_ptr<osg::StateSet> stateset = lightManager->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
@ -231,12 +233,22 @@ namespace MWRender
float positionZ = std::cos(altitude);
light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0));
light->setDiffuse(osg::Vec4(diffuseR,diffuseG,diffuseB,1));
light->setAmbient(osg::Vec4(ambientR,ambientG,ambientB,1));
osg::Vec4 ambientRGBA = osg::Vec4(ambientR,ambientG,ambientB,1);
if (mResourceSystem->getSceneManager()->getForceShaders())
{
// When using shaders, we now skip the ambient sun calculation as this is the only place it's used.
// Using the scene ambient will give identical results.
lightmodel->setAmbientIntensity(ambientRGBA);
light->setAmbient(osg::Vec4(0,0,0,1));
}
else
light->setAmbient(ambientRGBA);
light->setSpecular(osg::Vec4(0,0,0,0));
light->setLightNum(0);
light->setConstantAttenuation(1.f);
light->setLinearAttenuation(0.f);
light->setQuadraticAttenuation(0.f);
lightManager->setSunlight(light);
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource;
lightSource->setLight(light);
@ -414,7 +426,7 @@ namespace MWRender
visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame());
osg::Node::NodeMask nodeMask = mCamera->getNodeMask();
mCamera->setNodeMask(~0);
mCamera->setNodeMask(~0u);
mCamera->accept(visitor);
mCamera->setNodeMask(nodeMask);

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

Loading…
Cancel
Save