1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-10-05 09:26:30 +00:00

Add OpenMW commits up to 25 Jul 2020

# Conflicts:
#   .travis.yml
This commit is contained in:
David Cernat 2020-07-25 19:44:01 +02:00
commit ff10aa816b
52 changed files with 1047 additions and 477 deletions

View file

@ -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"

View file

@ -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}"

View file

@ -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
------ ------

View file

@ -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

View file

@ -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 \

View file

@ -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) {

View file

@ -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

View file

@ -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
) )

View file

@ -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());

View file

@ -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)

View file

@ -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.

View file

@ -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;

View file

@ -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())

View file

@ -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 ();

View file

@ -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()

View file

@ -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)

View file

@ -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);

View file

@ -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();

View file

@ -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;
} }
} }

View file

@ -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)

View file

@ -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();
} }

View file

@ -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();
} }

View file

@ -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;

View file

@ -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);

View file

@ -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;
} }

View file

@ -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:

View file

@ -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);
} }
} }
} }

View file

@ -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;

View file

@ -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);
} }
} }

View file

@ -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())

View file

@ -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);
} }

View file

@ -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);
} }
} }
} }

View file

@ -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

View file

@ -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;
}
}

View file

@ -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; }
}; };
} }

View file

@ -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);

View file

@ -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;

View 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;
}
}

View 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

View file

@ -80,7 +80,7 @@ namespace MWScript
{ {
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;
} }
{ {

View file

@ -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)

View file

@ -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()));
} }
}; };

View file

@ -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 {

View file

@ -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;

View file

@ -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();

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);
}; };
} }

View file

@ -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.

View 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.