mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-10-05 11:26:31 +00:00
Add OpenMW commits up to 25 Jul 2020
# Conflicts: # .travis.yml
This commit is contained in:
commit
ff10aa816b
52 changed files with 1047 additions and 477 deletions
180
.gitlab-ci.yml
180
.gitlab-ci.yml
|
@ -46,26 +46,184 @@ MacOS:
|
||||||
paths:
|
paths:
|
||||||
- build/OpenMW-*.dmg
|
- build/OpenMW-*.dmg
|
||||||
|
|
||||||
Windows:
|
variables: &engine-targets
|
||||||
|
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
|
||||||
|
|
||||||
|
variables: &cs-targets
|
||||||
|
targets: "openmw-cs,bsatool,esmtool,niftest"
|
||||||
|
|
||||||
|
.Windows_Ninja_Base:
|
||||||
tags:
|
tags:
|
||||||
- windows
|
- windows
|
||||||
|
before_script:
|
||||||
|
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||||
|
- 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
|
stage: build
|
||||||
allow_failure: true
|
|
||||||
script:
|
script:
|
||||||
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
|
- $time = (Get-Date -Format "HH:mm:ss")
|
||||||
- echo ${time}
|
- echo ${time}
|
||||||
- echo "started by ${GITLAB_USER_NAME}"
|
- echo "started by ${GITLAB_USER_NAME}"
|
||||||
# TODO: to anyone wanting to do further work here, we need to figure out how to get the below working
|
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N
|
||||||
# TODO: on gitlab's new shared windows runners. They currently don't have bash or anything else installed
|
- cd MSVC2019_64_Ninja
|
||||||
# TODO: it is currently just a bare windows 10 with powershell.
|
- .\ActivateMSVC.ps1
|
||||||
# - env # turn on for debugging
|
- cmake --build . --config $config --target ($targets.Split(','))
|
||||||
# - sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
|
- cd $config
|
||||||
# - SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
|
- |
|
||||||
# - call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
|
if (Get-ChildItem -Recurse *.pdb) {
|
||||||
# - 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
|
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
|
||||||
|
Get-ChildItem -Recurse *.pdb | Remove-Item
|
||||||
|
}
|
||||||
|
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
|
||||||
cache:
|
cache:
|
||||||
|
key: ninja-v2
|
||||||
paths:
|
paths:
|
||||||
- deps
|
- deps
|
||||||
|
- MSVC2019_64_Ninja/deps/Qt
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
paths:
|
paths:
|
||||||
- "*.zip"
|
- "*.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_CS_RelWithDebInfo:
|
||||||
|
extends:
|
||||||
|
- .Windows_Ninja_Base
|
||||||
|
variables:
|
||||||
|
<<: *cs-targets
|
||||||
|
config: "RelWithDebInfo"
|
||||||
|
|
||||||
|
.Windows_MSBuild_Base:
|
||||||
|
tags:
|
||||||
|
- windows
|
||||||
|
before_script:
|
||||||
|
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||||
|
- 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 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
|
||||||
|
- cd MSVC2019_64
|
||||||
|
- cmake --build . --config $config --target ($targets.Split(','))
|
||||||
|
- cd $config
|
||||||
|
- |
|
||||||
|
if (Get-ChildItem -Recurse *.pdb) {
|
||||||
|
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
|
||||||
|
Get-ChildItem -Recurse *.pdb | Remove-Item
|
||||||
|
}
|
||||||
|
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
|
||||||
|
cache:
|
||||||
|
key: msbuild-v2
|
||||||
|
paths:
|
||||||
|
- deps
|
||||||
|
- MSVC2019_64/deps/Qt
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- "*.zip"
|
||||||
|
- "*.log"
|
||||||
|
- MSVC2019_64/*.log
|
||||||
|
- MSVC2019_64/*/*.log
|
||||||
|
- MSVC2019_64/*/*/*.log
|
||||||
|
- MSVC2019_64/*/*/*/*.log
|
||||||
|
- MSVC2019_64/*/*/*/*/*.log
|
||||||
|
- MSVC2019_64/*/*/*/*/*/*.log
|
||||||
|
- MSVC2019_64/*/*/*/*/*/*/*.log
|
||||||
|
- MSVC2019_64/*/*/*/*/*/*/*/*.log
|
||||||
|
|
||||||
|
Windows_MSBuild_Engine_Release:
|
||||||
|
extends:
|
||||||
|
- .Windows_MSBuild_Base
|
||||||
|
variables:
|
||||||
|
<<: *engine-targets
|
||||||
|
config: "Release"
|
||||||
|
|
||||||
|
Windows_MSBuild_Engine_Debug:
|
||||||
|
extends:
|
||||||
|
- .Windows_MSBuild_Base
|
||||||
|
variables:
|
||||||
|
<<: *engine-targets
|
||||||
|
config: "Debug"
|
||||||
|
|
||||||
|
Windows_MSBuild_Engine_RelWithDebInfo:
|
||||||
|
extends:
|
||||||
|
- .Windows_MSBuild_Base
|
||||||
|
variables:
|
||||||
|
<<: *engine-targets
|
||||||
|
config: "RelWithDebInfo"
|
||||||
|
|
||||||
|
Windows_MSBuild_CS_Release:
|
||||||
|
extends:
|
||||||
|
- .Windows_MSBuild_Base
|
||||||
|
variables:
|
||||||
|
<<: *cs-targets
|
||||||
|
config: "Release"
|
||||||
|
|
||||||
|
Windows_MSBuild_CS_Debug:
|
||||||
|
extends:
|
||||||
|
- .Windows_MSBuild_Base
|
||||||
|
variables:
|
||||||
|
<<: *cs-targets
|
||||||
|
config: "Debug"
|
||||||
|
|
||||||
|
Windows_MSBuild_CS_RelWithDebInfo:
|
||||||
|
extends:
|
||||||
|
- .Windows_MSBuild_Base
|
||||||
|
variables:
|
||||||
|
<<: *cs-targets
|
||||||
|
config: "RelWithDebInfo"
|
|
@ -72,8 +72,8 @@ before_script:
|
||||||
script:
|
script:
|
||||||
- cd ./build
|
- cd ./build
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then ${ANALYZE} make -j3; fi
|
||||||
# - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||||
# - if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||||
- cd "${TRAVIS_BUILD_DIR}"
|
- cd "${TRAVIS_BUILD_DIR}"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||||
Bug #4021: Attributes and skills are not stored as floats
|
Bug #4021: Attributes and skills are not stored as floats
|
||||||
Bug #4623: Corprus implementation is incorrect
|
Bug #4623: Corprus implementation is incorrect
|
||||||
|
Bug #4764: Data race in osg ParticleSystem
|
||||||
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
|
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
|
||||||
Bug #5108: Savegame bloating due to inefficient fog textures format
|
Bug #5108: Savegame bloating due to inefficient fog textures format
|
||||||
Bug #5165: Active spells should use real time intead of timestamps
|
Bug #5165: Active spells should use real time intead of timestamps
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures
|
Bug #5369: Spawnpoint in the Grazelands doesn't produce oversized creatures
|
||||||
Bug #5370: Opening an unlocked but trapped door uses the key
|
Bug #5370: Opening an unlocked but trapped door uses the key
|
||||||
Bug #5384: openmw-cs: deleting an instance requires reload of scene window to show in editor
|
Bug #5384: openmw-cs: deleting an instance requires reload of scene window to show in editor
|
||||||
|
Bug #5387: Move/MoveWorld don't update the object's cell properly
|
||||||
Bug #5397: NPC greeting does not reset if you leave + reenter area
|
Bug #5397: NPC greeting does not reset if you leave + reenter area
|
||||||
Bug #5400: Editor: Verifier checks race of non-skin bodyparts
|
Bug #5400: Editor: Verifier checks race of non-skin bodyparts
|
||||||
Bug #5403: Enchantment effect doesn't show on an enemy during death animation
|
Bug #5403: Enchantment effect doesn't show on an enemy during death animation
|
||||||
|
@ -38,6 +40,7 @@
|
||||||
Bug #5499: Faction advance is available when requirements not met
|
Bug #5499: Faction advance is available when requirements not met
|
||||||
Bug #5502: Dead zone for analogue stick movement is too small
|
Bug #5502: Dead zone for analogue stick movement is too small
|
||||||
Bug #5507: Sound volume is not clamped on ingame settings update
|
Bug #5507: Sound volume is not clamped on ingame settings update
|
||||||
|
Bug #5531: Actors flee using current rotation by axis x
|
||||||
Feature #390: 3rd person look "over the shoulder"
|
Feature #390: 3rd person look "over the shoulder"
|
||||||
Feature #2386: Distant Statics in the form of Object Paging
|
Feature #2386: Distant Statics in the form of Object Paging
|
||||||
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
||||||
|
@ -45,7 +48,10 @@
|
||||||
Feature #5445: Handle NiLines
|
Feature #5445: Handle NiLines
|
||||||
Feature #5457: Realistic diagonal movement
|
Feature #5457: Realistic diagonal movement
|
||||||
Feature #5486: Fixes trainers to choose their training skills based on their base skill points
|
Feature #5486: Fixes trainers to choose their training skills based on their base skill points
|
||||||
|
Feature #5524: Resume failed script execution after reload
|
||||||
|
Feature #5525: Search fields tweaks (utf-8)
|
||||||
Task #5480: Drop Qt4 support
|
Task #5480: Drop Qt4 support
|
||||||
|
Task #5520: Improve cell name autocompleter implementation
|
||||||
|
|
||||||
0.46.0
|
0.46.0
|
||||||
------
|
------
|
||||||
|
|
|
@ -39,6 +39,10 @@ originalIFS="$IFS"
|
||||||
IFS=$'\n\r'
|
IFS=$'\n\r'
|
||||||
for pair in $(cmd //c "set"); do
|
for pair in $(cmd //c "set"); do
|
||||||
IFS='=' read -r -a separatedPair <<< "${pair}"
|
IFS='=' read -r -a separatedPair <<< "${pair}"
|
||||||
|
if [ ${#separatedPair[@]} -ne 2 ]; then
|
||||||
|
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
originalCmdEnv["${separatedPair[0]}"]="${separatedPair[1]}"
|
originalCmdEnv["${separatedPair[0]}"]="${separatedPair[1]}"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -49,6 +53,10 @@ declare -A cmdEnvChanges
|
||||||
for pair in $cmdEnv; do
|
for pair in $cmdEnv; do
|
||||||
if [ -n "$pair" ]; then
|
if [ -n "$pair" ]; then
|
||||||
IFS='=' read -r -a separatedPair <<< "${pair}"
|
IFS='=' read -r -a separatedPair <<< "${pair}"
|
||||||
|
if [ ${#separatedPair[@]} -ne 2 ]; then
|
||||||
|
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
key="${separatedPair[0]}"
|
key="${separatedPair[0]}"
|
||||||
value="${separatedPair[1]}"
|
value="${separatedPair[1]}"
|
||||||
if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then
|
if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then
|
||||||
|
|
|
@ -20,7 +20,7 @@ cmake \
|
||||||
-D CMAKE_BUILD_TYPE=RELEASE \
|
-D CMAKE_BUILD_TYPE=RELEASE \
|
||||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||||
-D BUILD_OPENMW=TRUE \
|
-D BUILD_OPENMW=TRUE \
|
||||||
-D BUILD_OPENCS=FALSE \
|
-D BUILD_OPENCS=TRUE \
|
||||||
-D BUILD_ESMTOOL=TRUE \
|
-D BUILD_ESMTOOL=TRUE \
|
||||||
-D BUILD_BSATOOL=TRUE \
|
-D BUILD_BSATOOL=TRUE \
|
||||||
-D BUILD_ESSIMPORTER=TRUE \
|
-D BUILD_ESSIMPORTER=TRUE \
|
||||||
|
|
|
@ -19,15 +19,15 @@ Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg,
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
mCellNameCompleter.setModel(&mCellNameCompleterModel);
|
||||||
|
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) {
|
void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) {
|
||||||
// Set up an auto-completer for the "Start default character at" field
|
// Update the list of suggestions for the "Start default character at" field
|
||||||
auto *completer = new QCompleter(cellNames);
|
mCellNameCompleterModel.setStringList(cellNames);
|
||||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion);
|
||||||
completer->setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
||||||
startDefaultCharacterAtField->setCompleter(completer);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) {
|
void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#define ADVANCEDPAGE_H
|
#define ADVANCEDPAGE_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QCompleter>
|
||||||
|
#include <QStringListModel>
|
||||||
|
|
||||||
#include "ui_advancedpage.h"
|
#include "ui_advancedpage.h"
|
||||||
|
|
||||||
|
@ -35,6 +37,8 @@ namespace Launcher
|
||||||
Files::ConfigurationManager &mCfgMgr;
|
Files::ConfigurationManager &mCfgMgr;
|
||||||
Config::GameSettings &mGameSettings;
|
Config::GameSettings &mGameSettings;
|
||||||
Settings::Manager &mEngineSettings;
|
Settings::Manager &mEngineSettings;
|
||||||
|
QCompleter mCellNameCompleter;
|
||||||
|
QStringListModel mCellNameCompleterModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the cells associated with the given content files for use in autocomplete
|
* Load the cells associated with the given content files for use in autocomplete
|
||||||
|
|
|
@ -20,7 +20,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||||
add_openmw_dir (mwrender
|
add_openmw_dir (mwrender
|
||||||
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
|
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
|
||||||
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
|
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
|
||||||
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation
|
||||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
|
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1072,7 +1072,14 @@ void OMW::Engine::go()
|
||||||
if (stats)
|
if (stats)
|
||||||
{
|
{
|
||||||
const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
const auto frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
||||||
mViewer->getViewerStats()->report(stats, frameNumber);
|
if (frameNumber >= 2)
|
||||||
|
{
|
||||||
|
mViewer->getViewerStats()->report(stats, frameNumber - 2);
|
||||||
|
osgViewer::Viewer::Cameras cameras;
|
||||||
|
mViewer->getCameras(cameras);
|
||||||
|
for (auto camera : cameras)
|
||||||
|
camera->getStats()->report(stats, frameNumber - 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mEnvironment.limitFrameRate(frameTimer.time_s());
|
mEnvironment.limitFrameRate(frameTimer.time_s());
|
||||||
|
|
|
@ -35,6 +35,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual ~ScriptManager() {}
|
virtual ~ScriptManager() {}
|
||||||
|
|
||||||
|
virtual void clear() = 0;
|
||||||
|
|
||||||
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
|
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
|
||||||
///< Run the script with the given name (compile first, if not compiled yet)
|
///< Run the script with the given name (compile first, if not compiled yet)
|
||||||
|
|
||||||
|
|
|
@ -312,6 +312,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void update (float duration) = 0;
|
virtual void update (float duration) = 0;
|
||||||
|
|
||||||
|
virtual void updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a GMST string from the store, if there is no setting with the given
|
* Fetches a GMST string from the store, if there is no setting with the given
|
||||||
* ID or it is not a string the default string is returned.
|
* ID or it is not a string the default string is returned.
|
||||||
|
|
|
@ -543,6 +543,8 @@ namespace MWBase
|
||||||
virtual void changeVanityModeScale(float factor) = 0;
|
virtual void changeVanityModeScale(float factor) = 0;
|
||||||
virtual bool vanityRotateCamera(float * rot) = 0;
|
virtual bool vanityRotateCamera(float * rot) = 0;
|
||||||
virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0;
|
virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0;
|
||||||
|
virtual void applyDeferredPreviewRotationToPlayer(float dt) = 0;
|
||||||
|
virtual void disableDeferredPreviewRotation() = 0;
|
||||||
|
|
||||||
virtual void setupPlayer() = 0;
|
virtual void setupPlayer() = 0;
|
||||||
virtual void renderPlayer() = 0;
|
virtual void renderPlayer() = 0;
|
||||||
|
|
|
@ -516,6 +516,12 @@ namespace MWGui
|
||||||
setCoord(10,10, width-10, height/2);
|
setCoord(10,10, width-10, height/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Console::updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
|
||||||
|
{
|
||||||
|
if (mPtr == currentPtr)
|
||||||
|
mPtr = newPtr;
|
||||||
|
}
|
||||||
|
|
||||||
void Console::setSelectedObject(const MWWorld::Ptr& object)
|
void Console::setSelectedObject(const MWWorld::Ptr& object)
|
||||||
{
|
{
|
||||||
if (!object.isEmpty())
|
if (!object.isEmpty())
|
||||||
|
|
|
@ -70,6 +70,8 @@ namespace MWGui
|
||||||
|
|
||||||
void executeFile (const std::string& path);
|
void executeFile (const std::string& path);
|
||||||
|
|
||||||
|
void updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
virtual void resetReference ();
|
virtual void resetReference ();
|
||||||
|
|
|
@ -69,8 +69,8 @@ namespace
|
||||||
return compareType(leftName, rightName);
|
return compareType(leftName, rightName);
|
||||||
|
|
||||||
// compare items by name
|
// compare items by name
|
||||||
leftName = Misc::StringUtils::lowerCase(left.mBase.getClass().getName(left.mBase));
|
leftName = Misc::StringUtils::lowerCaseUtf8(left.mBase.getClass().getName(left.mBase));
|
||||||
rightName = Misc::StringUtils::lowerCase(right.mBase.getClass().getName(right.mBase));
|
rightName = Misc::StringUtils::lowerCaseUtf8(right.mBase.getClass().getName(right.mBase));
|
||||||
|
|
||||||
result = leftName.compare(rightName);
|
result = leftName.compare(rightName);
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
|
@ -213,7 +213,7 @@ namespace MWGui
|
||||||
|
|
||||||
if (!mNameFilter.empty())
|
if (!mNameFilter.empty())
|
||||||
{
|
{
|
||||||
const auto itemName = Misc::StringUtils::lowerCase(base.getClass().getName(base));
|
const auto itemName = Misc::StringUtils::lowerCaseUtf8(base.getClass().getName(base));
|
||||||
return itemName.find(mNameFilter) != std::string::npos;
|
return itemName.find(mNameFilter) != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ namespace MWGui
|
||||||
|
|
||||||
for (const auto& effect : effects)
|
for (const auto& effect : effects)
|
||||||
{
|
{
|
||||||
const auto ciEffect = Misc::StringUtils::lowerCase(effect);
|
const auto ciEffect = Misc::StringUtils::lowerCaseUtf8(effect);
|
||||||
|
|
||||||
if (ciEffect.find(mEffectFilter) != std::string::npos)
|
if (ciEffect.find(mEffectFilter) != std::string::npos)
|
||||||
return true;
|
return true;
|
||||||
|
@ -285,7 +285,7 @@ namespace MWGui
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string compare = Misc::StringUtils::lowerCase(item.mBase.getClass().getName(item.mBase));
|
std::string compare = Misc::StringUtils::lowerCaseUtf8(item.mBase.getClass().getName(item.mBase));
|
||||||
if(compare.find(mNameFilter) == std::string::npos)
|
if(compare.find(mNameFilter) == std::string::npos)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -318,12 +318,12 @@ namespace MWGui
|
||||||
|
|
||||||
void SortFilterItemModel::setNameFilter (const std::string& filter)
|
void SortFilterItemModel::setNameFilter (const std::string& filter)
|
||||||
{
|
{
|
||||||
mNameFilter = Misc::StringUtils::lowerCase(filter);
|
mNameFilter = Misc::StringUtils::lowerCaseUtf8(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SortFilterItemModel::setEffectFilter (const std::string& filter)
|
void SortFilterItemModel::setEffectFilter (const std::string& filter)
|
||||||
{
|
{
|
||||||
mEffectFilter = Misc::StringUtils::lowerCase(filter);
|
mEffectFilter = Misc::StringUtils::lowerCaseUtf8(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SortFilterItemModel::update()
|
void SortFilterItemModel::update()
|
||||||
|
|
|
@ -136,27 +136,6 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
mWatchedStatsEmpty = false;
|
mWatchedStatsEmpty = false;
|
||||||
|
|
||||||
// Update the equipped weapon icon
|
|
||||||
MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched);
|
|
||||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
|
||||||
if (weapon == inv.end())
|
|
||||||
winMgr->unsetSelectedWeapon();
|
|
||||||
else
|
|
||||||
winMgr->setSelectedWeapon(*weapon);
|
|
||||||
|
|
||||||
// Update the selected spell icon
|
|
||||||
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
|
||||||
if (enchantItem != inv.end())
|
|
||||||
winMgr->setSelectedEnchantItem(*enchantItem);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const std::string& spell = winMgr->getSelectedSpell();
|
|
||||||
if (!spell.empty())
|
|
||||||
winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, mWatched)));
|
|
||||||
else
|
|
||||||
winMgr->unsetSelectedSpell();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatsWatcher::addListener(StatsListener* listener)
|
void StatsWatcher::addListener(StatsListener* listener)
|
||||||
|
|
|
@ -595,6 +595,11 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
|
||||||
|
{
|
||||||
|
mConsole->updateSelectedObjectPtr(currentPtr, newPtr);
|
||||||
|
}
|
||||||
|
|
||||||
void WindowManager::updateVisible()
|
void WindowManager::updateVisible()
|
||||||
{
|
{
|
||||||
bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper);
|
bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper);
|
||||||
|
|
|
@ -308,6 +308,8 @@ namespace MWGui
|
||||||
virtual void unsetSelectedSpell();
|
virtual void unsetSelectedSpell();
|
||||||
virtual void unsetSelectedWeapon();
|
virtual void unsetSelectedWeapon();
|
||||||
|
|
||||||
|
virtual void updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr);
|
||||||
|
|
||||||
virtual void showCrosshair(bool show);
|
virtual void showCrosshair(bool show);
|
||||||
virtual bool getSubtitlesEnabled();
|
virtual bool getSubtitlesEnabled();
|
||||||
|
|
||||||
|
|
|
@ -121,23 +121,21 @@ namespace MWInput
|
||||||
|
|
||||||
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch"))
|
if (MWBase::Environment::get().getInputManager()->getControlSwitch("playerviewswitch"))
|
||||||
{
|
{
|
||||||
|
const float switchLimit = 0.25;
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
if (mBindingsManager->actionIsActive(A_TogglePOV))
|
if (mBindingsManager->actionIsActive(A_TogglePOV))
|
||||||
{
|
{
|
||||||
if (mPreviewPOVDelay <= 0.5 &&
|
if (world->isFirstPerson() ? mPreviewPOVDelay > switchLimit : mPreviewPOVDelay == 0)
|
||||||
(mPreviewPOVDelay += dt) > 0.5)
|
world->togglePreviewMode(true);
|
||||||
{
|
mPreviewPOVDelay += dt;
|
||||||
mPreviewPOVDelay = 1.f;
|
|
||||||
MWBase::Environment::get().getWorld()->togglePreviewMode(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//disable preview mode
|
//disable preview mode
|
||||||
MWBase::Environment::get().getWorld()->togglePreviewMode(false);
|
if (mPreviewPOVDelay > 0)
|
||||||
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5)
|
world->togglePreviewMode(false);
|
||||||
{
|
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= switchLimit)
|
||||||
MWBase::Environment::get().getWorld()->togglePOV();
|
world->togglePOV();
|
||||||
}
|
|
||||||
mPreviewPOVDelay = 0.f;
|
mPreviewPOVDelay = 0.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
@ -102,6 +103,8 @@ namespace MWInput
|
||||||
mMouseManager->update(dt);
|
mMouseManager->update(dt);
|
||||||
mSensorManager->update(dt);
|
mSensorManager->update(dt);
|
||||||
mActionManager->update(dt, controllerMove);
|
mActionManager->update(dt, controllerMove);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWorld()->applyDeferredPreviewRotationToPlayer(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::setDragDrop(bool dragDrop)
|
void InputManager::setDragDrop(bool dragDrop)
|
||||||
|
|
|
@ -108,6 +108,8 @@ namespace MWInput
|
||||||
player.yaw(x);
|
player.yaw(x);
|
||||||
player.pitch(y);
|
player.pitch(y);
|
||||||
}
|
}
|
||||||
|
else if (!input->getControlSwitch("playerlooking"))
|
||||||
|
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
|
||||||
|
|
||||||
if (arg.zrel && input->getControlSwitch("playerviewswitch") && input->getControlSwitch("playercontrols")) //Check to make sure you are allowed to zoomout and there is a change
|
if (arg.zrel && input->getControlSwitch("playerviewswitch") && input->getControlSwitch("playercontrols")) //Check to make sure you are allowed to zoomout and there is a change
|
||||||
{
|
{
|
||||||
|
@ -207,17 +209,20 @@ namespace MWInput
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float rot[3];
|
float rot[3];
|
||||||
rot[0] = yAxis * dt * 1000.0f * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f;
|
rot[0] = -yAxis * dt * 1000.0f * mCameraSensitivity * (mInvertY ? -1 : 1) * mCameraYMultiplier / 256.f;
|
||||||
rot[1] = 0.0f;
|
rot[1] = 0.0f;
|
||||||
rot[2] = xAxis * dt * 1000.0f * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f;
|
rot[2] = -xAxis * dt * 1000.0f * mCameraSensitivity * (mInvertX ? -1 : 1) / 256.f;
|
||||||
|
|
||||||
// Only actually turn player when we're not in vanity mode
|
// Only actually turn player when we're not in vanity mode
|
||||||
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols"))
|
bool controls = MWBase::Environment::get().getInputManager()->getControlSwitch("playercontrols");
|
||||||
|
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && controls)
|
||||||
{
|
{
|
||||||
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
||||||
player.yaw(rot[2]);
|
player.yaw(-rot[2]);
|
||||||
player.pitch(rot[0]);
|
player.pitch(-rot[0]);
|
||||||
}
|
}
|
||||||
|
else if (!controls)
|
||||||
|
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
|
||||||
|
|
||||||
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,17 +249,20 @@ namespace MWInput
|
||||||
if (!mGuiCursorEnabled)
|
if (!mGuiCursorEnabled)
|
||||||
{
|
{
|
||||||
float rot[3];
|
float rot[3];
|
||||||
rot[0] = mGyroYSpeed * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1);
|
rot[0] = -mGyroYSpeed * dt * mGyroVSensitivity * 4 * (mInvertY ? -1 : 1);
|
||||||
rot[1] = 0.0f;
|
rot[1] = 0.0f;
|
||||||
rot[2] = mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1);
|
rot[2] = -mGyroXSpeed * dt * mGyroHSensitivity * 4 * (mInvertX ? -1 : 1);
|
||||||
|
|
||||||
// Only actually turn player when we're not in vanity mode
|
// Only actually turn player when we're not in vanity mode
|
||||||
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking"))
|
bool playerLooking = MWBase::Environment::get().getInputManager()->getControlSwitch("playerlooking");
|
||||||
|
if (!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot) && playerLooking)
|
||||||
{
|
{
|
||||||
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
||||||
player.yaw(rot[2]);
|
player.yaw(-rot[2]);
|
||||||
player.pitch(rot[0]);
|
player.pitch(-rot[0]);
|
||||||
}
|
}
|
||||||
|
else if (!playerLooking)
|
||||||
|
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
|
||||||
|
|
||||||
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,13 +60,14 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
|
||||||
// Make all nearby actors also avoid the door
|
// Make all nearby actors also avoid the door
|
||||||
std::vector<MWWorld::Ptr> actors;
|
std::vector<MWWorld::Ptr> actors;
|
||||||
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(pos.asVec3(),100,actors);
|
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(pos.asVec3(),100,actors);
|
||||||
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it) {
|
for(auto& actor : actors)
|
||||||
if(*it != getPlayer()) { //Not the player
|
{
|
||||||
MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence();
|
if (actor == getPlayer())
|
||||||
if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) { //Only add it once
|
continue;
|
||||||
seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it);
|
|
||||||
}
|
MWMechanics::AiSequence& seq = actor.getClass().getCreatureStats(actor).getAiSequence();
|
||||||
}
|
if (seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor)
|
||||||
|
seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr), actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -412,6 +412,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
storage.mFleeBlindRunTimer += duration;
|
storage.mFleeBlindRunTimer += duration;
|
||||||
|
|
||||||
|
storage.mMovement.mRotation[0] = -actor.getRefData().getPosition().rot[0];
|
||||||
storage.mMovement.mRotation[2] = osg::PI + getZAngleToDir(target.getRefData().getPosition().asVec3()-actor.getRefData().getPosition().asVec3());
|
storage.mMovement.mRotation[2] = osg::PI + getZAngleToDir(target.getRefData().getPosition().asVec3()-actor.getRefData().getPosition().asVec3());
|
||||||
storage.mMovement.mPosition[1] = 1;
|
storage.mMovement.mPosition[1] = 1;
|
||||||
updateActorsMovement(actor, duration, storage);
|
updateActorsMovement(actor, duration, storage);
|
||||||
|
|
|
@ -135,9 +135,9 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
followDistance = 313;
|
followDistance = 313;
|
||||||
short i = 0;
|
short i = 0;
|
||||||
followers.sort();
|
followers.sort();
|
||||||
for (std::list<int>::iterator it = followers.begin(); it != followers.end(); ++it)
|
for (int followIndex : followers)
|
||||||
{
|
{
|
||||||
if (*it == mFollowIndex)
|
if (followIndex == mFollowIndex)
|
||||||
followDistance += 130 * i;
|
followDistance += 130 * i;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,36 +406,36 @@ void AiSequence::fill(const ESM::AIPackageList &list)
|
||||||
if (!list.mList.empty() && list.mList.begin() != (list.mList.end()-1))
|
if (!list.mList.empty() && list.mList.begin() != (list.mList.end()-1))
|
||||||
mRepeat = true;
|
mRepeat = true;
|
||||||
|
|
||||||
for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
|
for (const auto& esmPackage : list.mList)
|
||||||
{
|
{
|
||||||
std::unique_ptr<MWMechanics::AiPackage> package;
|
std::unique_ptr<MWMechanics::AiPackage> package;
|
||||||
if (it->mType == ESM::AI_Wander)
|
if (esmPackage.mType == ESM::AI_Wander)
|
||||||
{
|
{
|
||||||
ESM::AIWander data = it->mWander;
|
ESM::AIWander data = esmPackage.mWander;
|
||||||
std::vector<unsigned char> idles;
|
std::vector<unsigned char> idles;
|
||||||
idles.reserve(8);
|
idles.reserve(8);
|
||||||
for (int i=0; i<8; ++i)
|
for (int i=0; i<8; ++i)
|
||||||
idles.push_back(data.mIdle[i]);
|
idles.push_back(data.mIdle[i]);
|
||||||
package = std::make_unique<MWMechanics::AiWander>(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0);
|
package = std::make_unique<MWMechanics::AiWander>(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0);
|
||||||
}
|
}
|
||||||
else if (it->mType == ESM::AI_Escort)
|
else if (esmPackage.mType == ESM::AI_Escort)
|
||||||
{
|
{
|
||||||
ESM::AITarget data = it->mTarget;
|
ESM::AITarget data = esmPackage.mTarget;
|
||||||
package = std::make_unique<MWMechanics::AiEscort>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
package = std::make_unique<MWMechanics::AiEscort>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
||||||
}
|
}
|
||||||
else if (it->mType == ESM::AI_Travel)
|
else if (esmPackage.mType == ESM::AI_Travel)
|
||||||
{
|
{
|
||||||
ESM::AITravel data = it->mTravel;
|
ESM::AITravel data = esmPackage.mTravel;
|
||||||
package = std::make_unique<MWMechanics::AiTravel>(data.mX, data.mY, data.mZ);
|
package = std::make_unique<MWMechanics::AiTravel>(data.mX, data.mY, data.mZ);
|
||||||
}
|
}
|
||||||
else if (it->mType == ESM::AI_Activate)
|
else if (esmPackage.mType == ESM::AI_Activate)
|
||||||
{
|
{
|
||||||
ESM::AIActivate data = it->mActivate;
|
ESM::AIActivate data = esmPackage.mActivate;
|
||||||
package = std::make_unique<MWMechanics::AiActivate>(data.mName.toString());
|
package = std::make_unique<MWMechanics::AiActivate>(data.mName.toString());
|
||||||
}
|
}
|
||||||
else //if (it->mType == ESM::AI_Follow)
|
else //if (esmPackage.mType == ESM::AI_Follow)
|
||||||
{
|
{
|
||||||
ESM::AITarget data = it->mTarget;
|
ESM::AITarget data = esmPackage.mTarget;
|
||||||
package = std::make_unique<MWMechanics::AiFollow>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
package = std::make_unique<MWMechanics::AiFollow>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
||||||
}
|
}
|
||||||
mPackages.push_back(std::move(package));
|
mPackages.push_back(std::move(package));
|
||||||
|
@ -457,10 +457,9 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
||||||
|
|
||||||
// If there is more than one non-combat, non-pursue package in the list, enable repeating.
|
// If there is more than one non-combat, non-pursue package in the list, enable repeating.
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin();
|
for (auto& container : sequence.mPackages)
|
||||||
it != sequence.mPackages.end(); ++it)
|
|
||||||
{
|
{
|
||||||
if (isActualAiPackage(static_cast<AiPackageTypeId>(it->mType)))
|
if (isActualAiPackage(static_cast<AiPackageTypeId>(container.mType)))
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,20 +467,19 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
||||||
mRepeat = true;
|
mRepeat = true;
|
||||||
|
|
||||||
// Load packages
|
// Load packages
|
||||||
for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin();
|
for (auto& container : sequence.mPackages)
|
||||||
it != sequence.mPackages.end(); ++it)
|
|
||||||
{
|
{
|
||||||
std::unique_ptr<MWMechanics::AiPackage> package;
|
std::unique_ptr<MWMechanics::AiPackage> package;
|
||||||
switch (it->mType)
|
switch (container.mType)
|
||||||
{
|
{
|
||||||
case ESM::AiSequence::Ai_Wander:
|
case ESM::AiSequence::Ai_Wander:
|
||||||
{
|
{
|
||||||
package.reset(new AiWander(static_cast<ESM::AiSequence::AiWander*>(it->mPackage)));
|
package.reset(new AiWander(static_cast<ESM::AiSequence::AiWander*>(container.mPackage)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::AiSequence::Ai_Travel:
|
case ESM::AiSequence::Ai_Travel:
|
||||||
{
|
{
|
||||||
const auto source = static_cast<const ESM::AiSequence::AiTravel*>(it->mPackage);
|
const auto source = static_cast<const ESM::AiSequence::AiTravel*>(container.mPackage);
|
||||||
if (source->mHidden)
|
if (source->mHidden)
|
||||||
package.reset(new AiInternalTravel(source));
|
package.reset(new AiInternalTravel(source));
|
||||||
else
|
else
|
||||||
|
@ -490,27 +488,27 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
||||||
}
|
}
|
||||||
case ESM::AiSequence::Ai_Escort:
|
case ESM::AiSequence::Ai_Escort:
|
||||||
{
|
{
|
||||||
package.reset(new AiEscort(static_cast<ESM::AiSequence::AiEscort*>(it->mPackage)));
|
package.reset(new AiEscort(static_cast<ESM::AiSequence::AiEscort*>(container.mPackage)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::AiSequence::Ai_Follow:
|
case ESM::AiSequence::Ai_Follow:
|
||||||
{
|
{
|
||||||
package.reset(new AiFollow(static_cast<ESM::AiSequence::AiFollow*>(it->mPackage)));
|
package.reset(new AiFollow(static_cast<ESM::AiSequence::AiFollow*>(container.mPackage)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::AiSequence::Ai_Activate:
|
case ESM::AiSequence::Ai_Activate:
|
||||||
{
|
{
|
||||||
package.reset(new AiActivate(static_cast<ESM::AiSequence::AiActivate*>(it->mPackage)));
|
package.reset(new AiActivate(static_cast<ESM::AiSequence::AiActivate*>(container.mPackage)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::AiSequence::Ai_Combat:
|
case ESM::AiSequence::Ai_Combat:
|
||||||
{
|
{
|
||||||
package.reset(new AiCombat(static_cast<ESM::AiSequence::AiCombat*>(it->mPackage)));
|
package.reset(new AiCombat(static_cast<ESM::AiSequence::AiCombat*>(container.mPackage)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::AiSequence::Ai_Pursue:
|
case ESM::AiSequence::Ai_Pursue:
|
||||||
{
|
{
|
||||||
package.reset(new AiPursue(static_cast<ESM::AiSequence::AiPursue*>(it->mPackage)));
|
package.reset(new AiPursue(static_cast<ESM::AiSequence::AiPursue*>(container.mPackage)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -809,11 +809,11 @@ namespace MWMechanics
|
||||||
void AiWander::AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex, AiWanderStorage& storage)
|
void AiWander::AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex, AiWanderStorage& storage)
|
||||||
{
|
{
|
||||||
storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(npcPos));
|
storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(npcPos));
|
||||||
for (std::vector<ESM::Pathgrid::Edge>::const_iterator it = pathGrid->mEdges.begin(); it != pathGrid->mEdges.end(); ++it)
|
for (auto& edge : pathGrid->mEdges)
|
||||||
{
|
{
|
||||||
if (it->mV0 == pointIndex)
|
if (edge.mV0 == pointIndex)
|
||||||
{
|
{
|
||||||
AddPointBetweenPathGridPoints(pathGrid->mPoints[it->mV0], pathGrid->mPoints[it->mV1], storage);
|
AddPointBetweenPathGridPoints(pathGrid->mPoints[edge.mV0], pathGrid->mPoints[edge.mV1], storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,38 +61,36 @@ namespace MWMechanics
|
||||||
|
|
||||||
// Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the
|
// Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the
|
||||||
// Store must preserve the record ordering as it was in the content files.
|
// Store must preserve the record ordering as it was in the content files.
|
||||||
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
for (const ESM::Spell& spell : spells)
|
||||||
{
|
{
|
||||||
const ESM::Spell* spell = &*iter;
|
if (spell.mData.mType != ESM::Spell::ST_Spell)
|
||||||
|
|
||||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
|
||||||
continue;
|
continue;
|
||||||
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
|
if (!(spell.mData.mFlags & ESM::Spell::F_Autocalc))
|
||||||
continue;
|
continue;
|
||||||
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger();
|
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger();
|
||||||
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
|
if (baseMagicka < iAutoSpellTimesCanCast * spell.mData.mCost)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (race && race->mPowers.exists(spell->mId))
|
if (race && race->mPowers.exists(spell.mId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int school;
|
int school;
|
||||||
float skillTerm;
|
float skillTerm;
|
||||||
calcWeakestSchool(spell, actorSkills, school, skillTerm);
|
calcWeakestSchool(&spell, actorSkills, school, skillTerm);
|
||||||
assert(school >= 0 && school < 6);
|
assert(school >= 0 && school < 6);
|
||||||
SchoolCaps& cap = schoolCaps[school];
|
SchoolCaps& cap = schoolCaps[school];
|
||||||
|
|
||||||
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
|
if (cap.mReachedLimit && spell.mData.mCost <= cap.mMinCost)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat();
|
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat();
|
||||||
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
if (calcAutoCastChance(&spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
selectedSpells.push_back(spell->mId);
|
selectedSpells.push_back(spell.mId);
|
||||||
|
|
||||||
if (cap.mReachedLimit)
|
if (cap.mReachedLimit)
|
||||||
{
|
{
|
||||||
|
@ -101,9 +99,9 @@ namespace MWMechanics
|
||||||
selectedSpells.erase(found);
|
selectedSpells.erase(found);
|
||||||
|
|
||||||
cap.mMinCost = std::numeric_limits<int>::max();
|
cap.mMinCost = std::numeric_limits<int>::max();
|
||||||
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
|
for (const std::string& testSpellName : selectedSpells)
|
||||||
{
|
{
|
||||||
const ESM::Spell* testSpell = spells.find(*weakIt);
|
const ESM::Spell* testSpell = spells.find(testSpellName);
|
||||||
|
|
||||||
//int testSchool;
|
//int testSchool;
|
||||||
//float dummySkillTerm;
|
//float dummySkillTerm;
|
||||||
|
@ -130,10 +128,10 @@ namespace MWMechanics
|
||||||
if (cap.mCount == cap.mLimit)
|
if (cap.mCount == cap.mLimit)
|
||||||
cap.mReachedLimit = true;
|
cap.mReachedLimit = true;
|
||||||
|
|
||||||
if (spell->mData.mCost < cap.mMinCost)
|
if (spell.mData.mCost < cap.mMinCost)
|
||||||
{
|
{
|
||||||
cap.mWeakestSpell = spell->mId;
|
cap.mWeakestSpell = spell.mId;
|
||||||
cap.mMinCost = spell->mData.mCost;
|
cap.mMinCost = spell.mData.mCost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,32 +152,28 @@ namespace MWMechanics
|
||||||
|
|
||||||
std::vector<std::string> selectedSpells;
|
std::vector<std::string> selectedSpells;
|
||||||
|
|
||||||
|
const MWWorld::Store<ESM::Spell> &spells = esmStore.get<ESM::Spell>();
|
||||||
const MWWorld::Store<ESM::Spell> &spells =
|
for (const ESM::Spell& spell : spells)
|
||||||
esmStore.get<ESM::Spell>();
|
|
||||||
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
const ESM::Spell* spell = &*iter;
|
if (spell.mData.mType != ESM::Spell::ST_Spell)
|
||||||
|
|
||||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
|
||||||
continue;
|
continue;
|
||||||
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
|
if (!(spell.mData.mFlags & ESM::Spell::F_PCStart))
|
||||||
continue;
|
continue;
|
||||||
if (reachedLimit && spell->mData.mCost <= minCost)
|
if (reachedLimit && spell.mData.mCost <= minCost)
|
||||||
continue;
|
continue;
|
||||||
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
|
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell.mId) != race->mPowers.mList.end())
|
||||||
continue;
|
continue;
|
||||||
if (baseMagicka < spell->mData.mCost)
|
if (baseMagicka < spell.mData.mCost)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->mValue.getFloat();
|
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->mValue.getFloat();
|
||||||
if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance)
|
if (calcAutoCastChance(&spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
selectedSpells.push_back(spell->mId);
|
selectedSpells.push_back(spell.mId);
|
||||||
|
|
||||||
if (reachedLimit)
|
if (reachedLimit)
|
||||||
{
|
{
|
||||||
|
@ -188,9 +182,9 @@ namespace MWMechanics
|
||||||
selectedSpells.erase(it);
|
selectedSpells.erase(it);
|
||||||
|
|
||||||
minCost = std::numeric_limits<int>::max();
|
minCost = std::numeric_limits<int>::max();
|
||||||
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
|
for (const std::string& testSpellName : selectedSpells)
|
||||||
{
|
{
|
||||||
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt);
|
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(testSpellName);
|
||||||
if (testSpell->mData.mCost < minCost)
|
if (testSpell->mData.mCost < minCost)
|
||||||
{
|
{
|
||||||
minCost = testSpell->mData.mCost;
|
minCost = testSpell->mData.mCost;
|
||||||
|
@ -200,9 +194,9 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (spell->mData.mCost < minCost)
|
if (spell.mData.mCost < minCost)
|
||||||
{
|
{
|
||||||
weakestSpell = spell;
|
weakestSpell = &spell;
|
||||||
minCost = weakestSpell->mData.mCost;
|
minCost = weakestSpell->mData.mCost;
|
||||||
}
|
}
|
||||||
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->mValue.getInteger();
|
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->mValue.getInteger();
|
||||||
|
@ -216,23 +210,22 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
|
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
|
||||||
{
|
{
|
||||||
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;
|
for (const auto& spellEffect : spell->mEffects.mList)
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
|
|
||||||
{
|
{
|
||||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(spellEffect.mEffectID);
|
||||||
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->mValue.getInteger();
|
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->mValue.getInteger();
|
||||||
|
|
||||||
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
|
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
|
||||||
{
|
{
|
||||||
assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length);
|
assert (spellEffect.mSkill >= 0 && spellEffect.mSkill < ESM::Skill::Length);
|
||||||
if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin)
|
if (actorSkills[spellEffect.mSkill] < iAutoSpellAttSkillMin)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
|
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
|
||||||
{
|
{
|
||||||
assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length);
|
assert (spellEffect.mAttribute >= 0 && spellEffect.mAttribute < ESM::Attribute::Length);
|
||||||
if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin)
|
if (actorAttributes[spellEffect.mAttribute] < iAutoSpellAttSkillMin)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,11 +237,8 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
|
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
|
||||||
float minChance = std::numeric_limits<float>::max();
|
float minChance = std::numeric_limits<float>::max();
|
||||||
|
for (const ESM::ENAMstruct& effect : spell->mEffects.mList)
|
||||||
const ESM::EffectList& effects = spell->mEffects;
|
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
|
||||||
{
|
{
|
||||||
const ESM::ENAMstruct& effect = *it;
|
|
||||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||||
|
|
||||||
int minMagn = 1;
|
int minMagn = 1;
|
||||||
|
|
|
@ -2873,11 +2873,11 @@ void CharacterController::updateContinuousVfx()
|
||||||
std::vector<int> effects;
|
std::vector<int> effects;
|
||||||
mAnimation->getLoopingEffects(effects);
|
mAnimation->getLoopingEffects(effects);
|
||||||
|
|
||||||
for (std::vector<int>::iterator it = effects.begin(); it != effects.end(); ++it)
|
for (int effectId : effects)
|
||||||
{
|
{
|
||||||
if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished()
|
if (mPtr.getClass().getCreatureStats(mPtr).isDeathAnimationFinished()
|
||||||
|| mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(*it)).getMagnitude() <= 0)
|
|| mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(effectId)).getMagnitude() <= 0)
|
||||||
mAnimation->removeEffect(*it);
|
mAnimation->removeEffect(effectId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,10 +31,10 @@ namespace MWMechanics
|
||||||
|
|
||||||
std::vector<std::string> candidates;
|
std::vector<std::string> candidates;
|
||||||
int highestLevel = 0;
|
int highestLevel = 0;
|
||||||
for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
for (const auto& levelledItem : items)
|
||||||
{
|
{
|
||||||
if (it->mLevel > highestLevel && it->mLevel <= playerLevel)
|
if (levelledItem.mLevel > highestLevel && levelledItem.mLevel <= playerLevel)
|
||||||
highestLevel = it->mLevel;
|
highestLevel = levelledItem.mLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For levelled creatures, the flags are swapped. This file format just makes so much sense.
|
// For levelled creatures, the flags are swapped. This file format just makes so much sense.
|
||||||
|
@ -43,14 +43,14 @@ namespace MWMechanics
|
||||||
allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels;
|
allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels;
|
||||||
|
|
||||||
std::pair<int, std::string> highest = std::make_pair(-1, "");
|
std::pair<int, std::string> highest = std::make_pair(-1, "");
|
||||||
for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
for (const auto& levelledItem : items)
|
||||||
{
|
{
|
||||||
if (playerLevel >= it->mLevel
|
if (playerLevel >= levelledItem.mLevel
|
||||||
&& (allLevels || it->mLevel == highestLevel))
|
&& (allLevels || levelledItem.mLevel == highestLevel))
|
||||||
{
|
{
|
||||||
candidates.push_back(it->mId);
|
candidates.push_back(levelledItem.mId);
|
||||||
if (it->mLevel >= highest.first)
|
if (levelledItem.mLevel >= highest.first)
|
||||||
highest = std::make_pair(it->mLevel, it->mId);
|
highest = std::make_pair(levelledItem.mLevel, levelledItem.mId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (candidates.empty())
|
if (candidates.empty())
|
||||||
|
|
|
@ -316,13 +316,37 @@ namespace MWMechanics
|
||||||
|
|
||||||
void MechanicsManager::update(float duration, bool paused)
|
void MechanicsManager::update(float duration, bool paused)
|
||||||
{
|
{
|
||||||
|
// Note: we should do it here since game mechanics and world updates use these values
|
||||||
|
MWWorld::Ptr ptr = getPlayer();
|
||||||
|
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
|
||||||
|
|
||||||
|
// Update the equipped weapon icon
|
||||||
|
MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);
|
||||||
|
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
|
if (weapon == inv.end())
|
||||||
|
winMgr->unsetSelectedWeapon();
|
||||||
|
else
|
||||||
|
winMgr->setSelectedWeapon(*weapon);
|
||||||
|
|
||||||
|
// Update the selected spell icon
|
||||||
|
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
||||||
|
if (enchantItem != inv.end())
|
||||||
|
winMgr->setSelectedEnchantItem(*enchantItem);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::string& spell = winMgr->getSelectedSpell();
|
||||||
|
if (!spell.empty())
|
||||||
|
winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, ptr)));
|
||||||
|
else
|
||||||
|
winMgr->unsetSelectedSpell();
|
||||||
|
}
|
||||||
|
|
||||||
if (mUpdatePlayer)
|
if (mUpdatePlayer)
|
||||||
{
|
{
|
||||||
mUpdatePlayer = false;
|
mUpdatePlayer = false;
|
||||||
|
|
||||||
// HACK? The player has been changed, so a new Animation object may
|
// HACK? The player has been changed, so a new Animation object may
|
||||||
// have been made for them. Make sure they're properly updated.
|
// have been made for them. Make sure they're properly updated.
|
||||||
MWWorld::Ptr ptr = getPlayer();
|
|
||||||
mActors.removeActor(ptr);
|
mActors.removeActor(ptr);
|
||||||
mActors.addActor(ptr, true);
|
mActors.addActor(ptr, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,10 @@ Objects::Objects()
|
||||||
|
|
||||||
Objects::~Objects()
|
Objects::~Objects()
|
||||||
{
|
{
|
||||||
PtrControllerMap::iterator it(mObjects.begin());
|
for(auto& object : mObjects)
|
||||||
for (; it != mObjects.end();++it)
|
|
||||||
{
|
{
|
||||||
delete it->second;
|
delete object.second;
|
||||||
it->second = nullptr;
|
object.second = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,8 +76,8 @@ void Objects::update(float duration, bool paused)
|
||||||
{
|
{
|
||||||
if(!paused)
|
if(!paused)
|
||||||
{
|
{
|
||||||
for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter)
|
for(auto& object : mObjects)
|
||||||
iter->second->update(duration);
|
object.second->update(duration);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -87,15 +86,15 @@ void Objects::update(float duration, bool paused)
|
||||||
if(mode != MWGui::GM_Container)
|
if(mode != MWGui::GM_Container)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter)
|
for(auto& object : mObjects)
|
||||||
{
|
{
|
||||||
if (iter->first.getTypeName() != typeid(ESM::Container).name())
|
if (object.first.getTypeName() != typeid(ESM::Container).name())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (iter->second->isAnimPlaying("containeropen"))
|
if (object.second->isAnimPlaying("containeropen"))
|
||||||
{
|
{
|
||||||
iter->second->update(duration);
|
object.second->update(duration);
|
||||||
MWBase::Environment::get().getWorld()->updateAnimatedCollisionShape(iter->first);
|
MWBase::Environment::get().getWorld()->updateAnimatedCollisionShape(object.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,17 +36,13 @@ namespace MWMechanics
|
||||||
|
|
||||||
// Check all the doors in this cell
|
// Check all the doors in this cell
|
||||||
const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors();
|
const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors();
|
||||||
const MWWorld::CellRefList<ESM::Door>::List& refList = doors.mList;
|
|
||||||
MWWorld::CellRefList<ESM::Door>::List::const_iterator it = refList.begin();
|
|
||||||
osg::Vec3f pos(actor.getRefData().getPosition().asVec3());
|
osg::Vec3f pos(actor.getRefData().getPosition().asVec3());
|
||||||
pos.z() = 0;
|
pos.z() = 0;
|
||||||
|
|
||||||
osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0));
|
osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0));
|
||||||
|
|
||||||
for (; it != refList.end(); ++it)
|
for (const auto& ref : doors.mList)
|
||||||
{
|
{
|
||||||
const MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
|
||||||
|
|
||||||
osg::Vec3f doorPos(ref.mData.getPosition().asVec3());
|
osg::Vec3f doorPos(ref.mData.getPosition().asVec3());
|
||||||
|
|
||||||
// FIXME: cast
|
// FIXME: cast
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "../mwworld/refdata.hpp"
|
#include "../mwworld/refdata.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/drawstate.hpp"
|
#include "../mwmechanics/drawstate.hpp"
|
||||||
|
#include "../mwmechanics/movement.hpp"
|
||||||
#include "../mwmechanics/npcstats.hpp"
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
|
||||||
#include "npcanimation.hpp"
|
#include "npcanimation.hpp"
|
||||||
|
@ -52,7 +53,10 @@ namespace MWRender
|
||||||
mCamera(camera),
|
mCamera(camera),
|
||||||
mAnimation(nullptr),
|
mAnimation(nullptr),
|
||||||
mFirstPersonView(true),
|
mFirstPersonView(true),
|
||||||
mPreviewMode(false),
|
mMode(Mode::Normal),
|
||||||
|
mVanityAllowed(true),
|
||||||
|
mStandingPreviewAllowed(Settings::Manager::getBool("preview if stand still", "Camera")),
|
||||||
|
mDeferredRotationAllowed(Settings::Manager::getBool("deferred preview rotation", "Camera")),
|
||||||
mNearest(30.f),
|
mNearest(30.f),
|
||||||
mFurthest(800.f),
|
mFurthest(800.f),
|
||||||
mIsNearest(false),
|
mIsNearest(false),
|
||||||
|
@ -62,20 +66,19 @@ namespace MWRender
|
||||||
mVanityToggleQueuedValue(false),
|
mVanityToggleQueuedValue(false),
|
||||||
mViewModeToggleQueued(false),
|
mViewModeToggleQueued(false),
|
||||||
mCameraDistance(0.f),
|
mCameraDistance(0.f),
|
||||||
mThirdPersonMode(ThirdPersonViewMode::Standard),
|
mMaxNextCameraDistance(800.f),
|
||||||
mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)),
|
mFocalPointCurrentOffset(osg::Vec2d()),
|
||||||
mSmoothTransitionToCombatMode(0.f)
|
mFocalPointTargetOffset(osg::Vec2d()),
|
||||||
|
mFocalPointTransitionSpeedCoef(1.f),
|
||||||
|
mSkipFocalPointTransition(true),
|
||||||
|
mPreviousTransitionInfluence(0.f),
|
||||||
|
mSmoothedSpeed(0.f),
|
||||||
|
mZoomOutWhenMoveCoef(Settings::Manager::getFloat("zoom out when move coef", "Camera")),
|
||||||
|
mDynamicCameraDistanceEnabled(false),
|
||||||
|
mShowCrosshairInThirdPersonMode(false),
|
||||||
|
mDeferredRotation(osg::Vec3f()),
|
||||||
|
mDeferredRotationDisabled(false)
|
||||||
{
|
{
|
||||||
mVanity.enabled = false;
|
|
||||||
mVanity.allowed = true;
|
|
||||||
|
|
||||||
mPreviewCam.pitch = 0.f;
|
|
||||||
mPreviewCam.yaw = 0.f;
|
|
||||||
mPreviewCam.offset = 400.f;
|
|
||||||
mMainCam.pitch = 0.f;
|
|
||||||
mMainCam.yaw = 0.f;
|
|
||||||
mMainCam.offset = 400.f;
|
|
||||||
|
|
||||||
mCameraDistance = mBaseCameraDistance;
|
mCameraDistance = mBaseCameraDistance;
|
||||||
|
|
||||||
mUpdateCallback = new UpdateRenderCameraCallback(this);
|
mUpdateCallback = new UpdateRenderCameraCallback(this);
|
||||||
|
@ -87,17 +90,11 @@ namespace MWRender
|
||||||
mCamera->removeUpdateCallback(mUpdateCallback);
|
mCamera->removeUpdateCallback(mUpdateCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr Camera::getTrackingPtr() const
|
|
||||||
{
|
|
||||||
return mTrackingPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Vec3d Camera::getFocalPoint() const
|
osg::Vec3d Camera::getFocalPoint() const
|
||||||
{
|
{
|
||||||
const osg::Node* trackNode = mTrackingNode;
|
if (!mTrackingNode)
|
||||||
if (!trackNode)
|
|
||||||
return osg::Vec3d();
|
return osg::Vec3d();
|
||||||
osg::NodePathList nodepaths = trackNode->getParentalNodePaths();
|
osg::NodePathList nodepaths = mTrackingNode->getParentalNodePaths();
|
||||||
if (nodepaths.empty())
|
if (nodepaths.empty())
|
||||||
return osg::Vec3d();
|
return osg::Vec3d();
|
||||||
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
||||||
|
@ -119,15 +116,9 @@ namespace MWRender
|
||||||
osg::Vec3d Camera::getFocalPointOffset() const
|
osg::Vec3d Camera::getFocalPointOffset() const
|
||||||
{
|
{
|
||||||
osg::Vec3d offset(0, 0, 10.f);
|
osg::Vec3d offset(0, 0, 10.f);
|
||||||
if (mThirdPersonMode == ThirdPersonViewMode::OverShoulder && !mPreviewMode && !mVanity.enabled)
|
offset.x() += mFocalPointCurrentOffset.x() * cos(getYaw());
|
||||||
{
|
offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw());
|
||||||
float horizontalOffset = mOverShoulderOffset.x() * (1.f - mSmoothTransitionToCombatMode);
|
offset.z() += mFocalPointCurrentOffset.y();
|
||||||
float verticalOffset = mSmoothTransitionToCombatMode * 15.f + (1.f - mSmoothTransitionToCombatMode) * mOverShoulderOffset.y();
|
|
||||||
|
|
||||||
offset.x() += horizontalOffset * cos(getYaw());
|
|
||||||
offset.y() += horizontalOffset * sin(getYaw());
|
|
||||||
offset.z() += verticalOffset;
|
|
||||||
}
|
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,9 +136,6 @@ namespace MWRender
|
||||||
|
|
||||||
void Camera::updateCamera(osg::Camera *cam)
|
void Camera::updateCamera(osg::Camera *cam)
|
||||||
{
|
{
|
||||||
if (mTrackingPtr.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
osg::Vec3d focal, position;
|
osg::Vec3d focal, position;
|
||||||
getPosition(focal, position);
|
getPosition(focal, position);
|
||||||
|
|
||||||
|
@ -177,11 +165,6 @@ namespace MWRender
|
||||||
setPitch(pitch);
|
setPitch(pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::attachTo(const MWWorld::Ptr &ptr)
|
|
||||||
{
|
|
||||||
mTrackingPtr = ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::update(float duration, bool paused)
|
void Camera::update(float duration, bool paused)
|
||||||
{
|
{
|
||||||
if (mAnimation->upperBodyReady())
|
if (mAnimation->upperBodyReady())
|
||||||
|
@ -194,7 +177,6 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
if (mViewModeToggleQueued)
|
if (mViewModeToggleQueued)
|
||||||
{
|
{
|
||||||
|
|
||||||
togglePreviewMode(false);
|
togglePreviewMode(false);
|
||||||
toggleViewMode();
|
toggleViewMode();
|
||||||
mViewModeToggleQueued = false;
|
mViewModeToggleQueued = false;
|
||||||
|
@ -206,36 +188,86 @@ namespace MWRender
|
||||||
|
|
||||||
// only show the crosshair in game mode
|
// only show the crosshair in game mode
|
||||||
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
||||||
wm->showCrosshair(!wm->isGuiMode() && !mVanity.enabled && !mPreviewMode
|
wm->showCrosshair(!wm->isGuiMode() && mMode != Mode::Preview && mMode != Mode::Vanity
|
||||||
&& (mFirstPersonView || mThirdPersonMode != ThirdPersonViewMode::Standard));
|
&& (mFirstPersonView || mShowCrosshairInThirdPersonMode));
|
||||||
|
|
||||||
if(mVanity.enabled)
|
if(mMode == Mode::Vanity)
|
||||||
{
|
|
||||||
rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true);
|
rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true);
|
||||||
|
|
||||||
|
updateFocalPointOffset(duration);
|
||||||
|
|
||||||
|
float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr);
|
||||||
|
speed /= (1.f + speed / 500.f);
|
||||||
|
float maxDelta = 300.f * duration;
|
||||||
|
mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta);
|
||||||
|
|
||||||
|
mMaxNextCameraDistance = mCameraDistance + duration * (100.f + mBaseCameraDistance);
|
||||||
|
updateStandingPreviewMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSmoothTransitionToCombatMode(duration);
|
void Camera::updateStandingPreviewMode()
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::setOverShoulderOffset(float horizontal, float vertical)
|
|
||||||
{
|
{
|
||||||
mOverShoulderOffset = osg::Vec2f(horizontal, vertical);
|
if (!mStandingPreviewAllowed)
|
||||||
|
return;
|
||||||
|
float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr);
|
||||||
|
bool combat = mTrackingPtr.getClass().isActor() &&
|
||||||
|
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;
|
||||||
|
bool standingStill = speed == 0 && !combat && !mFirstPersonView;
|
||||||
|
if (!standingStill && mMode == Mode::StandingPreview)
|
||||||
|
{
|
||||||
|
mMode = Mode::Normal;
|
||||||
|
calculateDeferredRotation();
|
||||||
|
}
|
||||||
|
else if (standingStill && mMode == Mode::Normal)
|
||||||
|
mMode = Mode::StandingPreview;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::updateSmoothTransitionToCombatMode(float duration)
|
void Camera::setFocalPointTargetOffset(osg::Vec2d v)
|
||||||
{
|
{
|
||||||
bool combatMode = true;
|
mFocalPointTargetOffset = v;
|
||||||
if (mTrackingPtr.getClass().isActor())
|
mPreviousTransitionSpeed = mFocalPointTransitionSpeed;
|
||||||
combatMode = mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;
|
mPreviousTransitionInfluence = 1.0f;
|
||||||
float speed = ((combatMode ? 1.f : 0.f) - mSmoothTransitionToCombatMode) * 5;
|
}
|
||||||
if (speed != 0)
|
|
||||||
speed += speed > 0 ? 1 : -1;
|
|
||||||
|
|
||||||
mSmoothTransitionToCombatMode += speed * duration;
|
void Camera::updateFocalPointOffset(float duration)
|
||||||
if (mSmoothTransitionToCombatMode > 1)
|
{
|
||||||
mSmoothTransitionToCombatMode = 1;
|
if (duration <= 0)
|
||||||
if (mSmoothTransitionToCombatMode < 0)
|
return;
|
||||||
mSmoothTransitionToCombatMode = 0;
|
|
||||||
|
if (mSkipFocalPointTransition)
|
||||||
|
{
|
||||||
|
mSkipFocalPointTransition = false;
|
||||||
|
mPreviousExtraOffset = osg::Vec2d();
|
||||||
|
mPreviousTransitionInfluence = 0.f;
|
||||||
|
mFocalPointCurrentOffset = mFocalPointTargetOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Vec2d oldOffset = mFocalPointCurrentOffset;
|
||||||
|
|
||||||
|
if (mPreviousTransitionInfluence > 0)
|
||||||
|
{
|
||||||
|
mFocalPointCurrentOffset -= mPreviousExtraOffset;
|
||||||
|
mPreviousExtraOffset = mPreviousExtraOffset / mPreviousTransitionInfluence + mPreviousTransitionSpeed * duration;
|
||||||
|
mPreviousTransitionInfluence =
|
||||||
|
std::max(0.f, mPreviousTransitionInfluence - duration * mFocalPointTransitionSpeedCoef);
|
||||||
|
mPreviousExtraOffset *= mPreviousTransitionInfluence;
|
||||||
|
mFocalPointCurrentOffset += mPreviousExtraOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Vec2d delta = mFocalPointTargetOffset - mFocalPointCurrentOffset;
|
||||||
|
if (delta.length2() > 0)
|
||||||
|
{
|
||||||
|
float coef = duration * (1.0 + 5.0 / delta.length()) *
|
||||||
|
mFocalPointTransitionSpeedCoef * (1.0f - mPreviousTransitionInfluence);
|
||||||
|
mFocalPointCurrentOffset += delta * std::min(coef, 1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mPreviousExtraOffset = osg::Vec2d();
|
||||||
|
mPreviousTransitionInfluence = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFocalPointTransitionSpeed = (mFocalPointCurrentOffset - oldOffset) / duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::toggleViewMode(bool force)
|
void Camera::toggleViewMode(bool force)
|
||||||
|
@ -254,14 +286,19 @@ namespace MWRender
|
||||||
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0);
|
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0);
|
||||||
|
|
||||||
mFirstPersonView = !mFirstPersonView;
|
mFirstPersonView = !mFirstPersonView;
|
||||||
|
updateStandingPreviewMode();
|
||||||
|
instantTransition();
|
||||||
processViewChange();
|
processViewChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::allowVanityMode(bool allow)
|
void Camera::allowVanityMode(bool allow)
|
||||||
{
|
{
|
||||||
if (!allow && mVanity.enabled)
|
if (!allow && mMode == Mode::Vanity)
|
||||||
|
{
|
||||||
|
disableDeferredPreviewRotation();
|
||||||
toggleVanityMode(false);
|
toggleVanityMode(false);
|
||||||
mVanity.allowed = allow;
|
}
|
||||||
|
mVanityAllowed = allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Camera::toggleVanityMode(bool enable)
|
bool Camera::toggleVanityMode(bool enable)
|
||||||
|
@ -275,26 +312,18 @@ namespace MWRender
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mVanity.allowed && enable)
|
if (!mVanityAllowed && enable)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(mVanity.enabled == enable)
|
if ((mMode == Mode::Vanity) == enable)
|
||||||
return true;
|
return true;
|
||||||
mVanity.enabled = enable;
|
mMode = enable ? Mode::Vanity : Mode::Normal;
|
||||||
|
if (!mDeferredRotationAllowed)
|
||||||
|
disableDeferredPreviewRotation();
|
||||||
|
if (!enable)
|
||||||
|
calculateDeferredRotation();
|
||||||
|
|
||||||
processViewChange();
|
processViewChange();
|
||||||
|
|
||||||
float offset = mPreviewCam.offset;
|
|
||||||
|
|
||||||
if (mVanity.enabled) {
|
|
||||||
setPitch(osg::DegreesToRadians(-30.f));
|
|
||||||
mMainCam.offset = mCameraDistance;
|
|
||||||
} else {
|
|
||||||
offset = mMainCam.offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
mCameraDistance = offset;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,22 +332,21 @@ namespace MWRender
|
||||||
if (mFirstPersonView && !mAnimation->upperBodyReady())
|
if (mFirstPersonView && !mAnimation->upperBodyReady())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(mPreviewMode == enable)
|
if((mMode == Mode::Preview) == enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mPreviewMode = enable;
|
mMode = enable ? Mode::Preview : Mode::Normal;
|
||||||
processViewChange();
|
if (mMode == Mode::Normal)
|
||||||
|
updateStandingPreviewMode();
|
||||||
float offset = mCameraDistance;
|
else if (mFirstPersonView)
|
||||||
if (mPreviewMode) {
|
instantTransition();
|
||||||
mMainCam.offset = offset;
|
if (mMode == Mode::Normal)
|
||||||
offset = mPreviewCam.offset;
|
{
|
||||||
} else {
|
if (!mDeferredRotationAllowed)
|
||||||
mPreviewCam.offset = offset;
|
disableDeferredPreviewRotation();
|
||||||
offset = mMainCam.offset;
|
calculateDeferredRotation();
|
||||||
}
|
}
|
||||||
|
processViewChange();
|
||||||
mCameraDistance = offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::setSneakOffset(float offset)
|
void Camera::setSneakOffset(float offset)
|
||||||
|
@ -326,13 +354,6 @@ namespace MWRender
|
||||||
mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset));
|
mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
float Camera::getYaw() const
|
|
||||||
{
|
|
||||||
if(mVanity.enabled || mPreviewMode)
|
|
||||||
return mPreviewCam.yaw;
|
|
||||||
return mMainCam.yaw;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::setYaw(float angle)
|
void Camera::setYaw(float angle)
|
||||||
{
|
{
|
||||||
if (angle > osg::PI) {
|
if (angle > osg::PI) {
|
||||||
|
@ -340,38 +361,14 @@ namespace MWRender
|
||||||
} else if (angle < -osg::PI) {
|
} else if (angle < -osg::PI) {
|
||||||
angle += osg::PI*2;
|
angle += osg::PI*2;
|
||||||
}
|
}
|
||||||
if (mVanity.enabled || mPreviewMode) {
|
mYaw = angle;
|
||||||
mPreviewCam.yaw = angle;
|
|
||||||
} else {
|
|
||||||
mMainCam.yaw = angle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float Camera::getPitch() const
|
|
||||||
{
|
|
||||||
if (mVanity.enabled || mPreviewMode) {
|
|
||||||
return mPreviewCam.pitch;
|
|
||||||
}
|
|
||||||
return mMainCam.pitch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::setPitch(float angle)
|
void Camera::setPitch(float angle)
|
||||||
{
|
{
|
||||||
const float epsilon = 0.000001f;
|
const float epsilon = 0.000001f;
|
||||||
float limit = osg::PI_2 - epsilon;
|
float limit = osg::PI_2 - epsilon;
|
||||||
if(mPreviewMode)
|
mPitch = osg::clampBetween(angle, -limit, limit);
|
||||||
limit /= 2;
|
|
||||||
|
|
||||||
if(angle > limit)
|
|
||||||
angle = limit;
|
|
||||||
else if(angle < -limit)
|
|
||||||
angle = -limit;
|
|
||||||
|
|
||||||
if (mVanity.enabled || mPreviewMode) {
|
|
||||||
mPreviewCam.pitch = angle;
|
|
||||||
} else {
|
|
||||||
mMainCam.pitch = angle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Camera::getCameraDistance() const
|
float Camera::getCameraDistance() const
|
||||||
|
@ -383,70 +380,53 @@ namespace MWRender
|
||||||
|
|
||||||
void Camera::updateBaseCameraDistance(float dist, bool adjust)
|
void Camera::updateBaseCameraDistance(float dist, bool adjust)
|
||||||
{
|
{
|
||||||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
if (isFirstPerson())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mIsNearest = false;
|
|
||||||
|
|
||||||
if (adjust)
|
if (adjust)
|
||||||
{
|
|
||||||
if (mVanity.enabled || mPreviewMode)
|
|
||||||
dist += mCameraDistance;
|
|
||||||
else
|
|
||||||
dist += std::min(mCameraDistance - getCameraDistanceCorrection(), mBaseCameraDistance);
|
dist += std::min(mCameraDistance - getCameraDistanceCorrection(), mBaseCameraDistance);
|
||||||
}
|
|
||||||
|
|
||||||
|
mIsNearest = dist <= mNearest;
|
||||||
if (dist >= mFurthest)
|
mBaseCameraDistance = osg::clampBetween(dist, mNearest, mFurthest);
|
||||||
dist = mFurthest;
|
Settings::Manager::setFloat("third person camera distance", "Camera", mBaseCameraDistance);
|
||||||
else if (dist <= mNearest)
|
|
||||||
{
|
|
||||||
dist = mNearest;
|
|
||||||
mIsNearest = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mVanity.enabled || mPreviewMode)
|
|
||||||
mPreviewCam.offset = dist;
|
|
||||||
else if (!mFirstPersonView)
|
|
||||||
{
|
|
||||||
mBaseCameraDistance = dist;
|
|
||||||
Settings::Manager::setFloat("third person camera distance", "Camera", dist);
|
|
||||||
}
|
|
||||||
setCameraDistance();
|
setCameraDistance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::setCameraDistance(float dist, bool adjust)
|
void Camera::setCameraDistance(float dist, bool adjust)
|
||||||
{
|
{
|
||||||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
if (isFirstPerson())
|
||||||
return;
|
return;
|
||||||
|
if (adjust)
|
||||||
if (adjust) dist += mCameraDistance;
|
dist += mCameraDistance;
|
||||||
|
mCameraDistance = osg::clampBetween(dist, 10.f, mFurthest);
|
||||||
if (dist >= mFurthest)
|
|
||||||
dist = mFurthest;
|
|
||||||
else if (dist < 10.f)
|
|
||||||
dist = 10.f;
|
|
||||||
mCameraDistance = dist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Camera::getCameraDistanceCorrection() const
|
float Camera::getCameraDistanceCorrection() const
|
||||||
{
|
{
|
||||||
return mThirdPersonMode != ThirdPersonViewMode::Standard ? std::max(-getPitch(), 0.f) * 50.f : 0;
|
if (!mDynamicCameraDistanceEnabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f;
|
||||||
|
|
||||||
|
float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed;
|
||||||
|
float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * mZoomOutWhenMoveCoef;
|
||||||
|
|
||||||
|
return pitchCorrection + speedCorrection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::setCameraDistance()
|
void Camera::setCameraDistance()
|
||||||
{
|
{
|
||||||
if (mVanity.enabled || mPreviewMode)
|
|
||||||
mCameraDistance = mPreviewCam.offset;
|
|
||||||
else if (!mFirstPersonView)
|
|
||||||
mCameraDistance = mBaseCameraDistance + getCameraDistanceCorrection();
|
|
||||||
mFocalPointAdjustment = osg::Vec3d();
|
mFocalPointAdjustment = osg::Vec3d();
|
||||||
|
if (isFirstPerson())
|
||||||
|
return;
|
||||||
|
mCameraDistance = mBaseCameraDistance + getCameraDistanceCorrection();
|
||||||
|
if (mDynamicCameraDistanceEnabled)
|
||||||
|
mCameraDistance = std::min(mCameraDistance, mMaxNextCameraDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::setAnimation(NpcAnimation *anim)
|
void Camera::setAnimation(NpcAnimation *anim)
|
||||||
{
|
{
|
||||||
mAnimation = anim;
|
mAnimation = anim;
|
||||||
|
|
||||||
processViewChange();
|
processViewChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,13 +453,74 @@ namespace MWRender
|
||||||
rotateCamera(getPitch(), getYaw(), false);
|
rotateCamera(getPitch(), getYaw(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Camera::isVanityOrPreviewModeEnabled() const
|
void Camera::applyDeferredPreviewRotationToPlayer(float dt)
|
||||||
{
|
{
|
||||||
return mPreviewMode || mVanity.enabled;
|
if (isVanityOrPreviewModeEnabled() || mTrackingPtr.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
osg::Vec3f rot = mDeferredRotation;
|
||||||
|
float delta = rot.normalize();
|
||||||
|
delta = std::min(delta, (delta + 1.f) * 3 * dt);
|
||||||
|
rot *= delta;
|
||||||
|
mDeferredRotation -= rot;
|
||||||
|
|
||||||
|
if (mDeferredRotationDisabled)
|
||||||
|
{
|
||||||
|
mDeferredRotationDisabled = delta > 0.0001;
|
||||||
|
rotateCameraToTrackingPtr();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Camera::isNearest() const
|
auto& movement = mTrackingPtr.getClass().getMovementSettings(mTrackingPtr);
|
||||||
|
movement.mRotation[0] += rot.x();
|
||||||
|
movement.mRotation[1] += rot.y();
|
||||||
|
movement.mRotation[2] += rot.z();
|
||||||
|
if (std::abs(mDeferredRotation.z()) > 0.0001)
|
||||||
{
|
{
|
||||||
return mIsNearest;
|
float s = std::sin(mDeferredRotation.z());
|
||||||
|
float c = std::cos(mDeferredRotation.z());
|
||||||
|
float x = movement.mPosition[0];
|
||||||
|
float y = movement.mPosition[1];
|
||||||
|
movement.mPosition[0] = x * c + y * s;
|
||||||
|
movement.mPosition[1] = x * -s + y * c;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::rotateCameraToTrackingPtr()
|
||||||
|
{
|
||||||
|
setPitch(-mTrackingPtr.getRefData().getPosition().rot[0] - mDeferredRotation.x());
|
||||||
|
setYaw(-mTrackingPtr.getRefData().getPosition().rot[2] - mDeferredRotation.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::instantTransition()
|
||||||
|
{
|
||||||
|
mSkipFocalPointTransition = true;
|
||||||
|
mDeferredRotationDisabled = false;
|
||||||
|
mDeferredRotation = osg::Vec3f();
|
||||||
|
rotateCameraToTrackingPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::calculateDeferredRotation()
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = mTrackingPtr;
|
||||||
|
if (isVanityOrPreviewModeEnabled() || ptr.isEmpty())
|
||||||
|
return;
|
||||||
|
if (mFirstPersonView)
|
||||||
|
{
|
||||||
|
instantTransition();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDeferredRotation.x() = -ptr.getRefData().getPosition().rot[0] - mPitch;
|
||||||
|
mDeferredRotation.z() = -ptr.getRefData().getPosition().rot[2] - mYaw;
|
||||||
|
if (mDeferredRotation.x() > osg::PI)
|
||||||
|
mDeferredRotation.x() -= 2 * osg::PI;
|
||||||
|
if (mDeferredRotation.x() < -osg::PI)
|
||||||
|
mDeferredRotation.x() += 2 * osg::PI;
|
||||||
|
if (mDeferredRotation.z() > osg::PI)
|
||||||
|
mDeferredRotation.z() -= 2 * osg::PI;
|
||||||
|
if (mDeferredRotation.z() < -osg::PI)
|
||||||
|
mDeferredRotation.z() += 2 * osg::PI;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,9 @@ namespace MWRender
|
||||||
class Camera
|
class Camera
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class ThirdPersonViewMode {Standard, OverShoulder};
|
enum class Mode { Normal, Vanity, Preview, StandingPreview };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CamData {
|
|
||||||
float pitch, yaw, offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
MWWorld::Ptr mTrackingPtr;
|
MWWorld::Ptr mTrackingPtr;
|
||||||
osg::ref_ptr<const osg::Node> mTrackingNode;
|
osg::ref_ptr<const osg::Node> mTrackingNode;
|
||||||
float mHeightScale;
|
float mHeightScale;
|
||||||
|
@ -40,44 +36,66 @@ namespace MWRender
|
||||||
NpcAnimation *mAnimation;
|
NpcAnimation *mAnimation;
|
||||||
|
|
||||||
bool mFirstPersonView;
|
bool mFirstPersonView;
|
||||||
bool mPreviewMode;
|
Mode mMode;
|
||||||
|
bool mVanityAllowed;
|
||||||
|
bool mStandingPreviewAllowed;
|
||||||
|
bool mDeferredRotationAllowed;
|
||||||
|
|
||||||
float mNearest;
|
float mNearest;
|
||||||
float mFurthest;
|
float mFurthest;
|
||||||
bool mIsNearest;
|
bool mIsNearest;
|
||||||
|
|
||||||
struct {
|
|
||||||
bool enabled, allowed;
|
|
||||||
} mVanity;
|
|
||||||
|
|
||||||
float mHeight, mBaseCameraDistance;
|
float mHeight, mBaseCameraDistance;
|
||||||
CamData mMainCam, mPreviewCam;
|
float mPitch, mYaw;
|
||||||
|
|
||||||
bool mVanityToggleQueued;
|
bool mVanityToggleQueued;
|
||||||
bool mVanityToggleQueuedValue;
|
bool mVanityToggleQueuedValue;
|
||||||
bool mViewModeToggleQueued;
|
bool mViewModeToggleQueued;
|
||||||
|
|
||||||
float mCameraDistance;
|
float mCameraDistance;
|
||||||
|
float mMaxNextCameraDistance;
|
||||||
|
|
||||||
ThirdPersonViewMode mThirdPersonMode;
|
|
||||||
osg::Vec2f mOverShoulderOffset;
|
|
||||||
osg::Vec3d mFocalPointAdjustment;
|
osg::Vec3d mFocalPointAdjustment;
|
||||||
|
osg::Vec2d mFocalPointCurrentOffset;
|
||||||
|
osg::Vec2d mFocalPointTargetOffset;
|
||||||
|
float mFocalPointTransitionSpeedCoef;
|
||||||
|
bool mSkipFocalPointTransition;
|
||||||
|
|
||||||
// Makes sense only if mThirdPersonMode is OverShoulder. Can be in range [0, 1].
|
// This fields are used to make focal point transition smooth if previous transition was not finished.
|
||||||
// Used for smooth transition from non-combat camera position (0) to combat camera position (1).
|
float mPreviousTransitionInfluence;
|
||||||
float mSmoothTransitionToCombatMode;
|
osg::Vec2d mFocalPointTransitionSpeed;
|
||||||
void updateSmoothTransitionToCombatMode(float duration);
|
osg::Vec2d mPreviousTransitionSpeed;
|
||||||
|
osg::Vec2d mPreviousExtraOffset;
|
||||||
|
|
||||||
|
float mSmoothedSpeed;
|
||||||
|
float mZoomOutWhenMoveCoef;
|
||||||
|
bool mDynamicCameraDistanceEnabled;
|
||||||
|
bool mShowCrosshairInThirdPersonMode;
|
||||||
|
|
||||||
|
void updateFocalPointOffset(float duration);
|
||||||
float getCameraDistanceCorrection() const;
|
float getCameraDistanceCorrection() const;
|
||||||
|
|
||||||
osg::ref_ptr<osg::NodeCallback> mUpdateCallback;
|
osg::ref_ptr<osg::NodeCallback> mUpdateCallback;
|
||||||
|
|
||||||
|
// Used to rotate player to the direction of view after exiting preview or vanity mode.
|
||||||
|
osg::Vec3f mDeferredRotation;
|
||||||
|
bool mDeferredRotationDisabled;
|
||||||
|
void calculateDeferredRotation();
|
||||||
|
void updateStandingPreviewMode();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Camera(osg::Camera* camera);
|
Camera(osg::Camera* camera);
|
||||||
~Camera();
|
~Camera();
|
||||||
|
|
||||||
MWWorld::Ptr getTrackingPtr() const;
|
/// Attach camera to object
|
||||||
|
void attachTo(const MWWorld::Ptr &ptr) { mTrackingPtr = ptr; }
|
||||||
|
MWWorld::Ptr getTrackingPtr() const { return mTrackingPtr; }
|
||||||
|
|
||||||
void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; }
|
void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; }
|
||||||
void setOverShoulderOffset(float horizontal, float vertical);
|
void setFocalPointTargetOffset(osg::Vec2d v);
|
||||||
|
void instantTransition();
|
||||||
|
void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; }
|
||||||
|
void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; }
|
||||||
|
|
||||||
/// Update the view matrix of \a cam
|
/// Update the view matrix of \a cam
|
||||||
void updateCamera(osg::Camera* cam);
|
void updateCamera(osg::Camera* cam);
|
||||||
|
@ -88,16 +106,14 @@ namespace MWRender
|
||||||
/// Set where the camera is looking at. Uses Morrowind (euler) angles
|
/// Set where the camera is looking at. Uses Morrowind (euler) angles
|
||||||
/// \param rot Rotation angles in radians
|
/// \param rot Rotation angles in radians
|
||||||
void rotateCamera(float pitch, float yaw, bool adjust);
|
void rotateCamera(float pitch, float yaw, bool adjust);
|
||||||
|
void rotateCameraToTrackingPtr();
|
||||||
|
|
||||||
float getYaw() const;
|
float getYaw() const { return mYaw; }
|
||||||
void setYaw(float angle);
|
void setYaw(float angle);
|
||||||
|
|
||||||
float getPitch() const;
|
float getPitch() const { return mPitch; }
|
||||||
void setPitch(float angle);
|
void setPitch(float angle);
|
||||||
|
|
||||||
/// Attach camera to object
|
|
||||||
void attachTo(const MWWorld::Ptr &);
|
|
||||||
|
|
||||||
/// @param Force view mode switch, even if currently not allowed by the animation.
|
/// @param Force view mode switch, even if currently not allowed by the animation.
|
||||||
void toggleViewMode(bool force=false);
|
void toggleViewMode(bool force=false);
|
||||||
|
|
||||||
|
@ -107,11 +123,13 @@ namespace MWRender
|
||||||
/// @note this may be ignored if an important animation is currently playing
|
/// @note this may be ignored if an important animation is currently playing
|
||||||
void togglePreviewMode(bool enable);
|
void togglePreviewMode(bool enable);
|
||||||
|
|
||||||
|
void applyDeferredPreviewRotationToPlayer(float dt);
|
||||||
|
void disableDeferredPreviewRotation() { mDeferredRotationDisabled = true; }
|
||||||
|
|
||||||
/// \brief Lowers the camera for sneak.
|
/// \brief Lowers the camera for sneak.
|
||||||
void setSneakOffset(float offset);
|
void setSneakOffset(float offset);
|
||||||
|
|
||||||
bool isFirstPerson() const
|
bool isFirstPerson() const { return mFirstPersonView && mMode == Mode::Normal; }
|
||||||
{ return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); }
|
|
||||||
|
|
||||||
void processViewChange();
|
void processViewChange();
|
||||||
|
|
||||||
|
@ -140,9 +158,10 @@ namespace MWRender
|
||||||
/// Stores focal and camera world positions in passed arguments
|
/// Stores focal and camera world positions in passed arguments
|
||||||
void getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const;
|
void getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const;
|
||||||
|
|
||||||
bool isVanityOrPreviewModeEnabled() const;
|
bool isVanityOrPreviewModeEnabled() const { return mMode != Mode::Normal; }
|
||||||
|
Mode getMode() const { return mMode; }
|
||||||
|
|
||||||
bool isNearest() const;
|
bool isNearest() const { return mIsNearest; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
#include "vismask.hpp"
|
#include "vismask.hpp"
|
||||||
#include "pathgrid.hpp"
|
#include "pathgrid.hpp"
|
||||||
#include "camera.hpp"
|
#include "camera.hpp"
|
||||||
|
#include "viewovershoulder.hpp"
|
||||||
#include "water.hpp"
|
#include "water.hpp"
|
||||||
#include "terrainstorage.hpp"
|
#include "terrainstorage.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
@ -306,6 +307,8 @@ namespace MWRender
|
||||||
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
||||||
|
|
||||||
mCamera.reset(new Camera(mViewer->getCamera()));
|
mCamera.reset(new Camera(mViewer->getCamera()));
|
||||||
|
if (Settings::Manager::getBool("view over shoulder", "Camera"))
|
||||||
|
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
|
||||||
|
|
||||||
mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
|
mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
|
||||||
|
|
||||||
|
@ -366,7 +369,6 @@ namespace MWRender
|
||||||
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
|
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
|
||||||
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
||||||
mStateUpdater->setFogEnd(mViewDistance);
|
mStateUpdater->setFogEnd(mViewDistance);
|
||||||
updateThirdPersonViewMode();
|
|
||||||
|
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
||||||
|
@ -382,19 +384,6 @@ namespace MWRender
|
||||||
mWorkQueue = nullptr;
|
mWorkQueue = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::updateThirdPersonViewMode()
|
|
||||||
{
|
|
||||||
if (Settings::Manager::getBool("view over shoulder", "Camera"))
|
|
||||||
mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::OverShoulder);
|
|
||||||
else
|
|
||||||
mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::Standard);
|
|
||||||
|
|
||||||
std::stringstream offset(Settings::Manager::getString("view over shoulder offset", "Camera"));
|
|
||||||
float horizontal = 30.f, vertical = -10.f;
|
|
||||||
offset >> horizontal >> vertical;
|
|
||||||
mCamera->setOverShoulderOffset(horizontal, vertical);
|
|
||||||
}
|
|
||||||
|
|
||||||
osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
|
osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
|
||||||
{
|
{
|
||||||
return mViewer->getIncrementalCompileOperation();
|
return mViewer->getIncrementalCompileOperation();
|
||||||
|
@ -630,6 +619,8 @@ namespace MWRender
|
||||||
updateNavMesh();
|
updateNavMesh();
|
||||||
updateRecastMesh();
|
updateRecastMesh();
|
||||||
|
|
||||||
|
if (mViewOverShoulderController)
|
||||||
|
mViewOverShoulderController->update();
|
||||||
mCamera->update(dt, paused);
|
mCamera->update(dt, paused);
|
||||||
|
|
||||||
osg::Vec3d focal, cameraPos;
|
osg::Vec3d focal, cameraPos;
|
||||||
|
@ -662,7 +653,7 @@ namespace MWRender
|
||||||
if(ptr == mCamera->getTrackingPtr() &&
|
if(ptr == mCamera->getTrackingPtr() &&
|
||||||
!mCamera->isVanityOrPreviewModeEnabled())
|
!mCamera->isVanityOrPreviewModeEnabled())
|
||||||
{
|
{
|
||||||
mCamera->rotateCamera(-ptr.getRefData().getPosition().rot[0], -ptr.getRefData().getPosition().rot[2], false);
|
mCamera->rotateCameraToTrackingPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr.getRefData().getBaseNode()->setAttitude(rot);
|
ptr.getRefData().getBaseNode()->setAttitude(rot);
|
||||||
|
|
|
@ -79,6 +79,7 @@ namespace MWRender
|
||||||
class NpcAnimation;
|
class NpcAnimation;
|
||||||
class Pathgrid;
|
class Pathgrid;
|
||||||
class Camera;
|
class Camera;
|
||||||
|
class ViewOverShoulderController;
|
||||||
class Water;
|
class Water;
|
||||||
class TerrainStorage;
|
class TerrainStorage;
|
||||||
class LandManager;
|
class LandManager;
|
||||||
|
@ -294,6 +295,7 @@ namespace MWRender
|
||||||
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
|
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
|
||||||
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
|
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
|
||||||
std::unique_ptr<Camera> mCamera;
|
std::unique_ptr<Camera> mCamera;
|
||||||
|
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
|
||||||
osg::Vec3f mCurrentCameraPos;
|
osg::Vec3f mCurrentCameraPos;
|
||||||
|
|
||||||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||||
|
|
106
apps/openmw/mwrender/viewovershoulder.cpp
Normal file
106
apps/openmw/mwrender/viewovershoulder.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#include "viewovershoulder.hpp"
|
||||||
|
|
||||||
|
#include <osg/Quat>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
#include "../mwworld/refdata.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/drawstate.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
|
||||||
|
ViewOverShoulderController::ViewOverShoulderController(Camera* camera) :
|
||||||
|
mCamera(camera), mMode(Mode::RightShoulder),
|
||||||
|
mAutoSwitchShoulder(Settings::Manager::getBool("auto switch shoulder", "Camera")),
|
||||||
|
mOverShoulderHorizontalOffset(30.f), mOverShoulderVerticalOffset(-10.f)
|
||||||
|
{
|
||||||
|
osg::Vec2f offset = Settings::Manager::getVector2("view over shoulder offset", "Camera");
|
||||||
|
mOverShoulderHorizontalOffset = std::abs(offset.x());
|
||||||
|
mOverShoulderVerticalOffset = offset.y();
|
||||||
|
mDefaultShoulderIsRight = offset.x() >= 0;
|
||||||
|
|
||||||
|
mCamera->enableDynamicCameraDistance(true);
|
||||||
|
mCamera->enableCrosshairInThirdPersonMode(true);
|
||||||
|
mCamera->setFocalPointTargetOffset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewOverShoulderController::update()
|
||||||
|
{
|
||||||
|
if (mCamera->isFirstPerson())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Mode oldMode = mMode;
|
||||||
|
auto ptr = mCamera->getTrackingPtr();
|
||||||
|
bool combat = ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing;
|
||||||
|
if (combat && !mCamera->isVanityOrPreviewModeEnabled())
|
||||||
|
mMode = Mode::Combat;
|
||||||
|
else if (MWBase::Environment::get().getWorld()->isSwimming(ptr))
|
||||||
|
mMode = Mode::Swimming;
|
||||||
|
else if (oldMode == Mode::Combat || oldMode == Mode::Swimming)
|
||||||
|
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
|
||||||
|
if (mAutoSwitchShoulder && (mMode == Mode::LeftShoulder || mMode == Mode::RightShoulder))
|
||||||
|
trySwitchShoulder();
|
||||||
|
|
||||||
|
if (oldMode == mMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mCamera->getMode() == Camera::Mode::Vanity)
|
||||||
|
// Player doesn't touch controls for a long time. Transition should be very slow.
|
||||||
|
mCamera->setFocalPointTransitionSpeed(0.2f);
|
||||||
|
else if ((oldMode == Mode::Combat || mMode == Mode::Combat) && mCamera->getMode() == Camera::Mode::Normal)
|
||||||
|
// Transition to/from combat mode and we are not it preview mode. Should be fast.
|
||||||
|
mCamera->setFocalPointTransitionSpeed(5.f);
|
||||||
|
else
|
||||||
|
mCamera->setFocalPointTransitionSpeed(1.f); // Default transition speed.
|
||||||
|
|
||||||
|
switch (mMode)
|
||||||
|
{
|
||||||
|
case Mode::RightShoulder:
|
||||||
|
mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
|
||||||
|
break;
|
||||||
|
case Mode::LeftShoulder:
|
||||||
|
mCamera->setFocalPointTargetOffset({-mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
|
||||||
|
break;
|
||||||
|
case Mode::Combat:
|
||||||
|
case Mode::Swimming:
|
||||||
|
default:
|
||||||
|
mCamera->setFocalPointTargetOffset({0, 15});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewOverShoulderController::trySwitchShoulder()
|
||||||
|
{
|
||||||
|
if (mCamera->getMode() != Camera::Mode::Normal)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit
|
||||||
|
const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance
|
||||||
|
|
||||||
|
auto orient = osg::Quat(mCamera->getYaw(), osg::Vec3d(0,0,1));
|
||||||
|
osg::Vec3d playerPos = mCamera->getFocalPoint() - mCamera->getFocalPointOffset();
|
||||||
|
|
||||||
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
osg::Vec3d sideOffset = orient * osg::Vec3d(world->getHalfExtents(mCamera->getTrackingPtr()).x() - 1, 0, 0);
|
||||||
|
float rayRight = world->getDistToNearestRayHit(
|
||||||
|
playerPos + sideOffset, orient * osg::Vec3d(1, 1, 0), limitToSwitchBack + 1);
|
||||||
|
float rayLeft = world->getDistToNearestRayHit(
|
||||||
|
playerPos - sideOffset, orient * osg::Vec3d(-1, 1, 0), limitToSwitchBack + 1);
|
||||||
|
float rayForward = world->getDistToNearestRayHit(
|
||||||
|
playerPos, orient * osg::Vec3d(0, 1, 0), limitToSwitchBack + 1);
|
||||||
|
|
||||||
|
if (rayLeft < limitToSwitch && rayRight > limitToSwitchBack)
|
||||||
|
mMode = Mode::RightShoulder;
|
||||||
|
else if (rayRight < limitToSwitch && rayLeft > limitToSwitchBack)
|
||||||
|
mMode = Mode::LeftShoulder;
|
||||||
|
else if (rayLeft > limitToSwitchBack && rayRight > limitToSwitchBack && rayForward > limitToSwitchBack)
|
||||||
|
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal file
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef VIEWOVERSHOULDER_H
|
||||||
|
#define VIEWOVERSHOULDER_H
|
||||||
|
|
||||||
|
#include "camera.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
|
||||||
|
class ViewOverShoulderController
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ViewOverShoulderController(Camera* camera);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void trySwitchShoulder();
|
||||||
|
enum class Mode { RightShoulder, LeftShoulder, Combat, Swimming };
|
||||||
|
|
||||||
|
Camera* mCamera;
|
||||||
|
Mode mMode;
|
||||||
|
bool mAutoSwitchShoulder;
|
||||||
|
float mOverShoulderHorizontalOffset;
|
||||||
|
float mOverShoulderVerticalOffset;
|
||||||
|
bool mDefaultShoulderIsRight;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VIEWOVERSHOULDER_H
|
|
@ -79,8 +79,8 @@ namespace MWScript
|
||||||
if (Success)
|
if (Success)
|
||||||
{
|
{
|
||||||
std::vector<Interpreter::Type_Code> code;
|
std::vector<Interpreter::Type_Code> code;
|
||||||
mParser.getCode (code);
|
mParser.getCode(code);
|
||||||
mScripts.insert (std::make_pair (name, std::make_pair (code, mParser.getLocals())));
|
mScripts.emplace(name, CompiledScript(code, mParser.getLocals()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ namespace MWScript
|
||||||
{
|
{
|
||||||
// failed -> ignore script from now on.
|
// failed -> ignore script from now on.
|
||||||
std::vector<Interpreter::Type_Code> empty;
|
std::vector<Interpreter::Type_Code> empty;
|
||||||
mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals())));
|
mScripts.emplace(name, CompiledScript(empty, Compiler::Locals()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute script
|
// execute script
|
||||||
if (!iter->second.first.empty())
|
if (!iter->second.mByteCode.empty() && iter->second.mActive)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!mOpcodesInstalled)
|
if (!mOpcodesInstalled)
|
||||||
|
@ -118,7 +118,7 @@ namespace MWScript
|
||||||
mOpcodesInstalled = true;
|
mOpcodesInstalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mInterpreter.run (&iter->second.first[0], iter->second.first.size(), interpreterContext);
|
mInterpreter.run (&iter->second.mByteCode[0], iter->second.mByteCode.size(), interpreterContext);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (const MissingImplicitRefError& e)
|
catch (const MissingImplicitRefError& e)
|
||||||
|
@ -129,11 +129,21 @@ namespace MWScript
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
|
Log(Debug::Error) << "Execution of script " << name << " failed: " << e.what();
|
||||||
|
|
||||||
iter->second.first.clear(); // don't execute again.
|
iter->second.mActive = false; // don't execute again.
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptManager::clear()
|
||||||
|
{
|
||||||
|
for (auto& script : mScripts)
|
||||||
|
{
|
||||||
|
script.second.mActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGlobalScripts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<int, int> ScriptManager::compileAll()
|
std::pair<int, int> ScriptManager::compileAll()
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -163,7 +173,7 @@ namespace MWScript
|
||||||
ScriptCollection::iterator iter = mScripts.find (name2);
|
ScriptCollection::iterator iter = mScripts.find (name2);
|
||||||
|
|
||||||
if (iter!=mScripts.end())
|
if (iter!=mScripts.end())
|
||||||
return iter->second.second;
|
return iter->second.mLocals;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,7 +41,20 @@ namespace MWScript
|
||||||
Interpreter::Interpreter mInterpreter;
|
Interpreter::Interpreter mInterpreter;
|
||||||
bool mOpcodesInstalled;
|
bool mOpcodesInstalled;
|
||||||
|
|
||||||
typedef std::pair<std::vector<Interpreter::Type_Code>, Compiler::Locals> CompiledScript;
|
struct CompiledScript
|
||||||
|
{
|
||||||
|
std::vector<Interpreter::Type_Code> mByteCode;
|
||||||
|
Compiler::Locals mLocals;
|
||||||
|
bool mActive;
|
||||||
|
|
||||||
|
CompiledScript(const std::vector<Interpreter::Type_Code>& code, const Compiler::Locals& locals)
|
||||||
|
{
|
||||||
|
mByteCode = code;
|
||||||
|
mLocals = locals;
|
||||||
|
mActive = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
typedef std::map<std::string, CompiledScript> ScriptCollection;
|
typedef std::map<std::string, CompiledScript> ScriptCollection;
|
||||||
|
|
||||||
ScriptCollection mScripts;
|
ScriptCollection mScripts;
|
||||||
|
@ -55,6 +68,8 @@ namespace MWScript
|
||||||
Compiler::Context& compilerContext, int warningsMode,
|
Compiler::Context& compilerContext, int warningsMode,
|
||||||
const std::vector<std::string>& scriptBlacklist);
|
const std::vector<std::string>& scriptBlacklist);
|
||||||
|
|
||||||
|
virtual void clear();
|
||||||
|
|
||||||
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext);
|
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext);
|
||||||
///< Run the script with the given name (compile first, if not compiled yet)
|
///< Run the script with the given name (compile first, if not compiled yet)
|
||||||
|
|
||||||
|
|
|
@ -897,7 +897,8 @@ namespace MWScript
|
||||||
// We should move actors, standing on moving object, too.
|
// We should move actors, standing on moving object, too.
|
||||||
// This approach can be used to create elevators.
|
// This approach can be used to create elevators.
|
||||||
moveStandingActors(ptr, diff);
|
moveStandingActors(ptr, diff);
|
||||||
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z());
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||||
|
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -933,7 +934,8 @@ namespace MWScript
|
||||||
// We should move actors, standing on moving object, too.
|
// We should move actors, standing on moving object, too.
|
||||||
// This approach can be used to create elevators.
|
// This approach can be used to create elevators.
|
||||||
moveStandingActors(ptr, diff);
|
moveStandingActors(ptr, diff);
|
||||||
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z());
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||||
|
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,85 +11,65 @@ namespace MWSound
|
||||||
inline int operator&(int a, PlayMode b) { return a & static_cast<int>(b); }
|
inline int operator&(int a, PlayMode b) { return a & static_cast<int>(b); }
|
||||||
inline int operator&(PlayMode a, PlayMode b) { return static_cast<int>(a) & static_cast<int>(b); }
|
inline int operator&(PlayMode a, PlayMode b) { return static_cast<int>(a) & static_cast<int>(b); }
|
||||||
|
|
||||||
|
struct SoundParams
|
||||||
|
{
|
||||||
|
osg::Vec3f mPos;
|
||||||
|
float mVolume = 1;
|
||||||
|
float mBaseVolume = 1;
|
||||||
|
float mPitch = 1;
|
||||||
|
float mMinDistance = 1;
|
||||||
|
float mMaxDistance = 1000;
|
||||||
|
int mFlags = 0;
|
||||||
|
float mFadeOutTime = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class SoundBase {
|
class SoundBase {
|
||||||
SoundBase& operator=(const SoundBase&) = delete;
|
SoundBase& operator=(const SoundBase&) = delete;
|
||||||
SoundBase(const SoundBase&) = delete;
|
SoundBase(const SoundBase&) = delete;
|
||||||
SoundBase(SoundBase&&) = delete;
|
SoundBase(SoundBase&&) = delete;
|
||||||
|
|
||||||
osg::Vec3f mPos;
|
SoundParams mParams;
|
||||||
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
|
|
||||||
float mBaseVolume;
|
|
||||||
float mPitch;
|
|
||||||
float mMinDistance;
|
|
||||||
float mMaxDistance;
|
|
||||||
int mFlags;
|
|
||||||
|
|
||||||
float mFadeOutTime;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Sound_Instance mHandle;
|
Sound_Instance mHandle = nullptr;
|
||||||
|
|
||||||
friend class OpenAL_Output;
|
friend class OpenAL_Output;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setPosition(const osg::Vec3f &pos) { mPos = pos; }
|
void setPosition(const osg::Vec3f &pos) { mParams.mPos = pos; }
|
||||||
void setVolume(float volume) { mVolume = volume; }
|
void setVolume(float volume) { mParams.mVolume = volume; }
|
||||||
void setBaseVolume(float volume) { mBaseVolume = volume; }
|
void setBaseVolume(float volume) { mParams.mBaseVolume = volume; }
|
||||||
void setFadeout(float duration) { mFadeOutTime = duration; }
|
void setFadeout(float duration) { mParams.mFadeOutTime = duration; }
|
||||||
void updateFade(float duration)
|
void updateFade(float duration)
|
||||||
{
|
{
|
||||||
if(mFadeOutTime > 0.0f)
|
if (mParams.mFadeOutTime > 0.0f)
|
||||||
{
|
{
|
||||||
float soundDuration = std::min(duration, mFadeOutTime);
|
float soundDuration = std::min(duration, mParams.mFadeOutTime);
|
||||||
mVolume *= (mFadeOutTime-soundDuration) / mFadeOutTime;
|
mParams.mVolume *= (mParams.mFadeOutTime - soundDuration) / mParams.mFadeOutTime;
|
||||||
mFadeOutTime -= soundDuration;
|
mParams.mFadeOutTime -= soundDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const osg::Vec3f &getPosition() const { return mPos; }
|
const osg::Vec3f &getPosition() const { return mParams.mPos; }
|
||||||
float getRealVolume() const { return mVolume * mBaseVolume; }
|
float getRealVolume() const { return mParams.mVolume * mParams.mBaseVolume; }
|
||||||
float getPitch() const { return mPitch; }
|
float getPitch() const { return mParams.mPitch; }
|
||||||
float getMinDistance() const { return mMinDistance; }
|
float getMinDistance() const { return mParams.mMinDistance; }
|
||||||
float getMaxDistance() const { return mMaxDistance; }
|
float getMaxDistance() const { return mParams.mMaxDistance; }
|
||||||
|
|
||||||
MWSound::Type getPlayType() const
|
MWSound::Type getPlayType() const
|
||||||
{ return static_cast<MWSound::Type>(mFlags&MWSound::Type::Mask); }
|
{ return static_cast<MWSound::Type>(mParams.mFlags & MWSound::Type::Mask); }
|
||||||
bool getUseEnv() const { return !(mFlags&MWSound::PlayMode::NoEnv); }
|
bool getUseEnv() const { return !(mParams.mFlags & MWSound::PlayMode::NoEnv); }
|
||||||
bool getIsLooping() const { return mFlags&MWSound::PlayMode::Loop; }
|
bool getIsLooping() const { return mParams.mFlags & MWSound::PlayMode::Loop; }
|
||||||
bool getDistanceCull() const { return mFlags&MWSound::PlayMode::RemoveAtDistance; }
|
bool getDistanceCull() const { return mParams.mFlags & MWSound::PlayMode::RemoveAtDistance; }
|
||||||
bool getIs3D() const { return mFlags&Play_3D; }
|
bool getIs3D() const { return mParams.mFlags & Play_3D; }
|
||||||
|
|
||||||
void init(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags)
|
void init(const SoundParams& params)
|
||||||
{
|
{
|
||||||
mPos = pos;
|
mParams = params;
|
||||||
mVolume = vol;
|
|
||||||
mBaseVolume = basevol;
|
|
||||||
mPitch = pitch;
|
|
||||||
mMinDistance = mindist;
|
|
||||||
mMaxDistance = maxdist;
|
|
||||||
mFlags = flags;
|
|
||||||
mFadeOutTime = 0.0f;
|
|
||||||
mHandle = nullptr;
|
mHandle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(float vol, float basevol, float pitch, int flags)
|
SoundBase() = default;
|
||||||
{
|
|
||||||
mPos = osg::Vec3f(0.0f, 0.0f, 0.0f);
|
|
||||||
mVolume = vol;
|
|
||||||
mBaseVolume = basevol;
|
|
||||||
mPitch = pitch;
|
|
||||||
mMinDistance = 1.0f;
|
|
||||||
mMaxDistance = 1000.0f;
|
|
||||||
mFlags = flags;
|
|
||||||
mFadeOutTime = 0.0f;
|
|
||||||
mHandle = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SoundBase()
|
|
||||||
: mPos(0.0f, 0.0f, 0.0f), mVolume(1.0f), mBaseVolume(1.0f), mPitch(1.0f)
|
|
||||||
, mMinDistance(1.0f), mMaxDistance(1000.0f), mFlags(0), mFadeOutTime(0.0f)
|
|
||||||
, mHandle(nullptr)
|
|
||||||
{ }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Sound : public SoundBase {
|
class Sound : public SoundBase {
|
||||||
|
|
|
@ -290,13 +290,25 @@ namespace MWSound
|
||||||
StreamPtr sound = getStreamRef();
|
StreamPtr sound = getStreamRef();
|
||||||
if(playlocal)
|
if(playlocal)
|
||||||
{
|
{
|
||||||
sound->init(1.0f, basevol, 1.0f, PlayMode::NoEnv|Type::Voice|Play_2D);
|
sound->init([&] {
|
||||||
|
SoundParams params;
|
||||||
|
params.mBaseVolume = basevol;
|
||||||
|
params.mFlags = PlayMode::NoEnv | Type::Voice | Play_2D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
played = mOutput->streamSound(decoder, sound.get(), true);
|
played = mOutput->streamSound(decoder, sound.get(), true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sound->init(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance,
|
sound->init([&] {
|
||||||
PlayMode::Normal|Type::Voice|Play_3D);
|
SoundParams params;
|
||||||
|
params.mPos = pos;
|
||||||
|
params.mBaseVolume = basevol;
|
||||||
|
params.mMinDistance = minDistance;
|
||||||
|
params.mMaxDistance = maxDistance;
|
||||||
|
params.mFlags = PlayMode::Normal | Type::Voice | Play_3D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
played = mOutput->streamSound3D(decoder, sound.get(), true);
|
played = mOutput->streamSound3D(decoder, sound.get(), true);
|
||||||
}
|
}
|
||||||
if(!played)
|
if(!played)
|
||||||
|
@ -332,8 +344,12 @@ namespace MWSound
|
||||||
decoder->open(filename);
|
decoder->open(filename);
|
||||||
|
|
||||||
mMusic = getStreamRef();
|
mMusic = getStreamRef();
|
||||||
mMusic->init(1.0f, volumeFromType(Type::Music), 1.0f,
|
mMusic->init([&] {
|
||||||
PlayMode::NoEnv|Type::Music|Play_2D);
|
SoundParams params;
|
||||||
|
params.mBaseVolume = volumeFromType(Type::Music);
|
||||||
|
params.mFlags = PlayMode::NoEnv | Type::Music | Play_2D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
mOutput->streamSound(decoder, mMusic.get());
|
mOutput->streamSound(decoder, mMusic.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +577,12 @@ namespace MWSound
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
StreamPtr track = getStreamRef();
|
StreamPtr track = getStreamRef();
|
||||||
track->init(1.0f, volumeFromType(type), 1.0f, PlayMode::NoEnv|type|Play_2D);
|
track->init([&] {
|
||||||
|
SoundParams params;
|
||||||
|
params.mBaseVolume = volumeFromType(type);
|
||||||
|
params.mFlags = PlayMode::NoEnv | type | Play_2D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
if(!mOutput->streamSound(decoder, track.get()))
|
if(!mOutput->streamSound(decoder, track.get()))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -598,7 +619,14 @@ namespace MWSound
|
||||||
stopSound(sfx, MWWorld::ConstPtr());
|
stopSound(sfx, MWWorld::ConstPtr());
|
||||||
|
|
||||||
SoundPtr sound = getSoundRef();
|
SoundPtr sound = getSoundRef();
|
||||||
sound->init(volume * sfx->mVolume, volumeFromType(type), pitch, mode|type|Play_2D);
|
sound->init([&] {
|
||||||
|
SoundParams params;
|
||||||
|
params.mVolume = volume * sfx->mVolume;
|
||||||
|
params.mBaseVolume = volumeFromType(type);
|
||||||
|
params.mPitch = pitch;
|
||||||
|
params.mFlags = mode | type | Play_2D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
if(!mOutput->playSound(sound.get(), sfx->mHandle, offset))
|
if(!mOutput->playSound(sound.get(), sfx->mHandle, offset))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -635,13 +663,29 @@ namespace MWSound
|
||||||
SoundPtr sound = getSoundRef();
|
SoundPtr sound = getSoundRef();
|
||||||
if(!(mode&PlayMode::NoPlayerLocal) && ptr == MWMechanics::getPlayer())
|
if(!(mode&PlayMode::NoPlayerLocal) && ptr == MWMechanics::getPlayer())
|
||||||
{
|
{
|
||||||
sound->init(volume * sfx->mVolume, volumeFromType(type), pitch, mode|type|Play_2D);
|
sound->init([&] {
|
||||||
|
SoundParams params;
|
||||||
|
params.mVolume = volume * sfx->mVolume;
|
||||||
|
params.mBaseVolume = volumeFromType(type);
|
||||||
|
params.mPitch = pitch;
|
||||||
|
params.mFlags = mode | type | Play_2D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
played = mOutput->playSound(sound.get(), sfx->mHandle, offset);
|
played = mOutput->playSound(sound.get(), sfx->mHandle, offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sound->init(objpos, volume * sfx->mVolume, volumeFromType(type), pitch,
|
sound->init([&] {
|
||||||
sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D);
|
SoundParams params;
|
||||||
|
params.mPos = objpos;
|
||||||
|
params.mVolume = volume * sfx->mVolume;
|
||||||
|
params.mBaseVolume = volumeFromType(type);
|
||||||
|
params.mPitch = pitch;
|
||||||
|
params.mMinDistance = sfx->mMinDist;
|
||||||
|
params.mMaxDistance = sfx->mMaxDist;
|
||||||
|
params.mFlags = mode | type | Play_3D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
played = mOutput->playSound3D(sound.get(), sfx->mHandle, offset);
|
played = mOutput->playSound3D(sound.get(), sfx->mHandle, offset);
|
||||||
}
|
}
|
||||||
if(!played)
|
if(!played)
|
||||||
|
@ -670,8 +714,17 @@ namespace MWSound
|
||||||
if(!sfx) return nullptr;
|
if(!sfx) return nullptr;
|
||||||
|
|
||||||
SoundPtr sound = getSoundRef();
|
SoundPtr sound = getSoundRef();
|
||||||
sound->init(initialPos, volume * sfx->mVolume, volumeFromType(type), pitch,
|
sound->init([&] {
|
||||||
sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D);
|
SoundParams params;
|
||||||
|
params.mPos = initialPos;
|
||||||
|
params.mVolume = volume * sfx->mVolume;
|
||||||
|
params.mBaseVolume = volumeFromType(type);
|
||||||
|
params.mPitch = pitch;
|
||||||
|
params.mMinDistance = sfx->mMinDist;
|
||||||
|
params.mMaxDistance = sfx->mMaxDist;
|
||||||
|
params.mFlags = mode | type | Play_3D;
|
||||||
|
return params;
|
||||||
|
} ());
|
||||||
if(!mOutput->playSound3D(sound.get(), sfx->mHandle, offset))
|
if(!mOutput->playSound3D(sound.get(), sfx->mHandle, offset))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ void MWState::StateManager::cleanup (bool force)
|
||||||
MWBase::Environment::get().getSoundManager()->clear();
|
MWBase::Environment::get().getSoundManager()->clear();
|
||||||
MWBase::Environment::get().getDialogueManager()->clear();
|
MWBase::Environment::get().getDialogueManager()->clear();
|
||||||
MWBase::Environment::get().getJournal()->clear();
|
MWBase::Environment::get().getJournal()->clear();
|
||||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear();
|
MWBase::Environment::get().getScriptManager()->clear();
|
||||||
MWBase::Environment::get().getWorld()->clear();
|
MWBase::Environment::get().getWorld()->clear();
|
||||||
MWBase::Environment::get().getWindowManager()->clear();
|
MWBase::Environment::get().getWindowManager()->clear();
|
||||||
MWBase::Environment::get().getInputManager()->clear();
|
MWBase::Environment::get().getInputManager()->clear();
|
||||||
|
|
|
@ -1062,6 +1062,7 @@ namespace MWWorld
|
||||||
removeContainerScripts(getPlayerPtr());
|
removeContainerScripts(getPlayerPtr());
|
||||||
mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent);
|
mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent);
|
||||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||||
|
mRendering->getCamera()->instantTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
void World::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
||||||
|
@ -1077,6 +1078,7 @@ namespace MWWorld
|
||||||
removeContainerScripts(getPlayerPtr());
|
removeContainerScripts(getPlayerPtr());
|
||||||
mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent);
|
mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent);
|
||||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||||
|
mRendering->getCamera()->instantTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
||||||
|
@ -1335,7 +1337,6 @@ namespace MWWorld
|
||||||
mRendering->updatePtr(ptr, newPtr);
|
mRendering->updatePtr(ptr, newPtr);
|
||||||
MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr);
|
MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr);
|
||||||
mPhysics->updatePtr(ptr, newPtr);
|
mPhysics->updatePtr(ptr, newPtr);
|
||||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
|
|
||||||
|
|
||||||
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
|
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
|
||||||
mechMgr->updateCell(ptr, newPtr);
|
mechMgr->updateCell(ptr, newPtr);
|
||||||
|
@ -1366,6 +1367,9 @@ namespace MWWorld
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->updateConsoleObjectPtr(ptr, newPtr);
|
||||||
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
|
||||||
}
|
}
|
||||||
if (haveToMove && newPtr.getRefData().getBaseNode())
|
if (haveToMove && newPtr.getRefData().getBaseNode())
|
||||||
{
|
{
|
||||||
|
@ -2105,7 +2109,7 @@ namespace MWWorld
|
||||||
std::string enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem);
|
std::string enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem);
|
||||||
if (!enchantId.empty())
|
if (!enchantId.empty())
|
||||||
{
|
{
|
||||||
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem));
|
const ESM::Enchantment* ench = mStore.get<ESM::Enchantment>().search(enchantId);
|
||||||
if (ench)
|
if (ench)
|
||||||
preloadEffects(&ench->mEffects);
|
preloadEffects(&ench->mEffects);
|
||||||
}
|
}
|
||||||
|
@ -2656,6 +2660,16 @@ namespace MWWorld
|
||||||
return mRendering->toggleVanityMode(enable);
|
return mRendering->toggleVanityMode(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::disableDeferredPreviewRotation()
|
||||||
|
{
|
||||||
|
mRendering->getCamera()->disableDeferredPreviewRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::applyDeferredPreviewRotationToPlayer(float dt)
|
||||||
|
{
|
||||||
|
mRendering->getCamera()->applyDeferredPreviewRotationToPlayer(dt);
|
||||||
|
}
|
||||||
|
|
||||||
void World::allowVanityMode(bool allow)
|
void World::allowVanityMode(bool allow)
|
||||||
{
|
{
|
||||||
mRendering->allowVanityMode(allow);
|
mRendering->allowVanityMode(allow);
|
||||||
|
|
|
@ -646,6 +646,9 @@ namespace MWWorld
|
||||||
bool vanityRotateCamera(float * rot) override;
|
bool vanityRotateCamera(float * rot) override;
|
||||||
void setCameraDistance(float dist, bool adjust = false, bool override = true) override;
|
void setCameraDistance(float dist, bool adjust = false, bool override = true) override;
|
||||||
|
|
||||||
|
void applyDeferredPreviewRotationToPlayer(float dt) override;
|
||||||
|
void disableDeferredPreviewRotation() override;
|
||||||
|
|
||||||
void setupPlayer() override;
|
void setupPlayer() override;
|
||||||
void renderPlayer() override;
|
void renderPlayer() override;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ ContentSelectorView::ComboBox::ComboBox(QWidget *parent) :
|
||||||
{
|
{
|
||||||
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
||||||
setValidator(mValidator);
|
setValidator(mValidator);
|
||||||
|
setEditable(true);
|
||||||
setCompleter(0);
|
setCompleter(0);
|
||||||
setEnabled (true);
|
setEnabled (true);
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,28 @@ bool Manager::getBool (const std::string& setting, const std::string& category)
|
||||||
return Misc::StringUtils::ciEqual(string, "true");
|
return Misc::StringUtils::ciEqual(string, "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Vec2f Manager::getVector2 (const std::string& setting, const std::string& category)
|
||||||
|
{
|
||||||
|
const std::string& value = getString(setting, category);
|
||||||
|
std::stringstream stream(value);
|
||||||
|
float x, y;
|
||||||
|
stream >> x >> y;
|
||||||
|
if (stream.fail())
|
||||||
|
throw std::runtime_error(std::string("Can't parse 2d vector: " + value));
|
||||||
|
return osg::Vec2f(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Vec3f Manager::getVector3 (const std::string& setting, const std::string& category)
|
||||||
|
{
|
||||||
|
const std::string& value = getString(setting, category);
|
||||||
|
std::stringstream stream(value);
|
||||||
|
float x, y, z;
|
||||||
|
stream >> x >> y >> z;
|
||||||
|
if (stream.fail())
|
||||||
|
throw std::runtime_error(std::string("Can't parse 3d vector: " + value));
|
||||||
|
return osg::Vec3f(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
void Manager::setString(const std::string &setting, const std::string &category, const std::string &value)
|
void Manager::setString(const std::string &setting, const std::string &category, const std::string &value)
|
||||||
{
|
{
|
||||||
CategorySettingValueMap::key_type key = std::make_pair(category, setting);
|
CategorySettingValueMap::key_type key = std::make_pair(category, setting);
|
||||||
|
@ -111,6 +133,20 @@ void Manager::setBool(const std::string &setting, const std::string &category, c
|
||||||
setString(setting, category, value ? "true" : "false");
|
setString(setting, category, value ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Manager::setVector2 (const std::string &setting, const std::string &category, const osg::Vec2f value)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << value.x() << " " << value.y();
|
||||||
|
setString(setting, category, stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::setVector3 (const std::string &setting, const std::string &category, const osg::Vec3f value)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << value.x() << ' ' << value.y() << ' ' << value.z();
|
||||||
|
setString(setting, category, stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
void Manager::resetPendingChange(const std::string &setting, const std::string &category)
|
void Manager::resetPendingChange(const std::string &setting, const std::string &category)
|
||||||
{
|
{
|
||||||
CategorySettingValueMap::key_type key = std::make_pair(category, setting);
|
CategorySettingValueMap::key_type key = std::make_pair(category, setting);
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <osg/Vec2f>
|
||||||
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
namespace Settings
|
namespace Settings
|
||||||
{
|
{
|
||||||
|
@ -44,11 +46,15 @@ namespace Settings
|
||||||
static float getFloat (const std::string& setting, const std::string& category);
|
static float getFloat (const std::string& setting, const std::string& category);
|
||||||
static std::string getString (const std::string& setting, const std::string& category);
|
static std::string getString (const std::string& setting, const std::string& category);
|
||||||
static bool getBool (const std::string& setting, const std::string& category);
|
static bool getBool (const std::string& setting, const std::string& category);
|
||||||
|
static osg::Vec2f getVector2 (const std::string& setting, const std::string& category);
|
||||||
|
static osg::Vec3f getVector3 (const std::string& setting, const std::string& category);
|
||||||
|
|
||||||
static void setInt (const std::string& setting, const std::string& category, const int value);
|
static void setInt (const std::string& setting, const std::string& category, const int value);
|
||||||
static void setFloat (const std::string& setting, const std::string& category, const float value);
|
static void setFloat (const std::string& setting, const std::string& category, const float value);
|
||||||
static void setString (const std::string& setting, const std::string& category, const std::string& value);
|
static void setString (const std::string& setting, const std::string& category, const std::string& value);
|
||||||
static void setBool (const std::string& setting, const std::string& category, const bool value);
|
static void setBool (const std::string& setting, const std::string& category, const bool value);
|
||||||
|
static void setVector2 (const std::string& setting, const std::string& category, const osg::Vec2f value);
|
||||||
|
static void setVector3 (const std::string& setting, const std::string& category, const osg::Vec3f value);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,3 +150,51 @@ Recommened values: 30 -10 for the right shoulder, -30 -10 for the left shoulder.
|
||||||
|
|
||||||
This setting can only be configured by editing the settings configuration file.
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
|
||||||
|
auto switch shoulder
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: True
|
||||||
|
|
||||||
|
This setting makes difference only in third person mode if 'view over shoulder' is enabled.
|
||||||
|
When player is close to an obstacle, automatically switches camera to the shoulder that is farther away from the obstacle.
|
||||||
|
|
||||||
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
|
||||||
|
zoom out when move coef
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
:Type: floating point
|
||||||
|
:Range: Any
|
||||||
|
:Default: 20
|
||||||
|
|
||||||
|
This setting makes difference only in third person mode if 'view over shoulder' is enabled.
|
||||||
|
Slightly pulls camera away (or closer in case of negative value) when the character moves. To disable set it to zero.
|
||||||
|
|
||||||
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
|
||||||
|
preview if stand still
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: False
|
||||||
|
|
||||||
|
If enabled then the character rotation is not synchonized with the camera rotation while the character doesn't move and not in combat mode.
|
||||||
|
|
||||||
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
|
||||||
|
deferred preview rotation
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
:Type: boolean
|
||||||
|
:Range: True/False
|
||||||
|
:Default: True
|
||||||
|
|
||||||
|
Makes difference only in third person mode.
|
||||||
|
If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode.
|
||||||
|
If disabled then the camera rotates rather than the character.
|
||||||
|
|
||||||
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,18 @@ view over shoulder = false
|
||||||
# Makes sense only if 'view over shoulder' is true. First number is horizontal offset (negative value means offset to the left), second number is vertical offset.
|
# Makes sense only if 'view over shoulder' is true. First number is horizontal offset (negative value means offset to the left), second number is vertical offset.
|
||||||
view over shoulder offset = 30 -10
|
view over shoulder offset = 30 -10
|
||||||
|
|
||||||
|
# Switch shoulder automatically when player is close to an obstacle.
|
||||||
|
auto switch shoulder = true
|
||||||
|
|
||||||
|
# Slightly pulls camera away when the character moves. Works only in 'view over shoulder' mode. Set to 0 to disable.
|
||||||
|
zoom out when move coef = 20
|
||||||
|
|
||||||
|
# Automatically enable preview mode when player doesn't move.
|
||||||
|
preview if stand still = false
|
||||||
|
|
||||||
|
# Rotate the character to the view direction after exiting preview mode.
|
||||||
|
deferred preview rotation = true
|
||||||
|
|
||||||
[Cells]
|
[Cells]
|
||||||
|
|
||||||
# Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled.
|
# Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled.
|
||||||
|
|
Loading…
Reference in a new issue