mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 05:45:34 +00:00
Add OpenMW commits up to 25 Jul 2020
# Conflicts: # .travis.yml
This commit is contained in:
commit
ff10aa816b
52 changed files with 1047 additions and 477 deletions
180
.gitlab-ci.yml
180
.gitlab-ci.yml
|
@ -46,26 +46,184 @@ MacOS:
|
|||
paths:
|
||||
- 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:
|
||||
- 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
|
||||
allow_failure: true
|
||||
script:
|
||||
- Set-Variable -Name "time" -Value (date -Format "%H:%m")
|
||||
- $time = (Get-Date -Format "HH:mm:ss")
|
||||
- echo ${time}
|
||||
- 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
|
||||
# TODO: on gitlab's new shared windows runners. They currently don't have bash or anything else installed
|
||||
# TODO: it is currently just a bare windows 10 with powershell.
|
||||
# - env # turn on for debugging
|
||||
# - sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
|
||||
# - 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%
|
||||
# - 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N
|
||||
- cd MSVC2019_64_Ninja
|
||||
- .\ActivateMSVC.ps1
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
- cd $config
|
||||
- |
|
||||
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: ninja-v2
|
||||
paths:
|
||||
- deps
|
||||
- MSVC2019_64_Ninja/deps/Qt
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- "*.zip"
|
||||
- "*.log"
|
||||
- MSVC2019_64_Ninja/*.log
|
||||
- MSVC2019_64_Ninja/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*/*/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*/*/*/*/*.log
|
||||
- MSVC2019_64_Ninja/*/*/*/*/*/*/*/*.log
|
||||
|
||||
Windows_Ninja_Engine_Release:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Release"
|
||||
|
||||
Windows_Ninja_Engine_Debug:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_Ninja_Engine_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "RelWithDebInfo"
|
||||
|
||||
Windows_Ninja_CS_Release:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Release"
|
||||
|
||||
Windows_Ninja_CS_Debug:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_Ninja_CS_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "RelWithDebInfo"
|
||||
|
||||
.Windows_MSBuild_Base:
|
||||
tags:
|
||||
- windows
|
||||
before_script:
|
||||
- Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
|
||||
- choco install 7zip -y
|
||||
- choco install cmake.install --installargs 'ADD_CMAKE_TO_PATH=System' -y
|
||||
- choco install vswhere -y
|
||||
- choco install python -y
|
||||
- refreshenv
|
||||
stage: build
|
||||
script:
|
||||
- $time = (Get-Date -Format "HH:mm:ss")
|
||||
- echo ${time}
|
||||
- echo "started by ${GITLAB_USER_NAME}"
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V
|
||||
- cd MSVC2019_64
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
- cd $config
|
||||
- |
|
||||
if (Get-ChildItem -Recurse *.pdb) {
|
||||
7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip '*.pdb'
|
||||
Get-ChildItem -Recurse *.pdb | Remove-Item
|
||||
}
|
||||
- 7z a -tzip ..\..\OpenMW_MSVC2019_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}.zip '*'
|
||||
cache:
|
||||
key: msbuild-v2
|
||||
paths:
|
||||
- deps
|
||||
- MSVC2019_64/deps/Qt
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- "*.zip"
|
||||
- "*.log"
|
||||
- MSVC2019_64/*.log
|
||||
- MSVC2019_64/*/*.log
|
||||
- MSVC2019_64/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*/*/*/*.log
|
||||
- MSVC2019_64/*/*/*/*/*/*/*/*.log
|
||||
|
||||
Windows_MSBuild_Engine_Release:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Release"
|
||||
|
||||
Windows_MSBuild_Engine_Debug:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_MSBuild_Engine_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
config: "RelWithDebInfo"
|
||||
|
||||
Windows_MSBuild_CS_Release:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Release"
|
||||
|
||||
Windows_MSBuild_CS_Debug:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "Debug"
|
||||
|
||||
Windows_MSBuild_CS_RelWithDebInfo:
|
||||
extends:
|
||||
- .Windows_MSBuild_Base
|
||||
variables:
|
||||
<<: *cs-targets
|
||||
config: "RelWithDebInfo"
|
|
@ -72,8 +72,8 @@ before_script:
|
|||
script:
|
||||
- cd ./build
|
||||
- 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 ../CI/check_package.osx.sh; 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}" = "linux" ]; then ./openmw_test_suite; fi
|
||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||
- cd "${TRAVIS_BUILD_DIR}"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
|
||||
Bug #4021: Attributes and skills are not stored as floats
|
||||
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 #5108: Savegame bloating due to inefficient fog textures format
|
||||
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 #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 #5387: Move/MoveWorld don't update the object's cell properly
|
||||
Bug #5397: NPC greeting does not reset if you leave + reenter area
|
||||
Bug #5400: Editor: Verifier checks race of non-skin bodyparts
|
||||
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 #5502: Dead zone for analogue stick movement is too small
|
||||
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 #2386: Distant Statics in the form of Object Paging
|
||||
Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher
|
||||
|
@ -45,7 +48,10 @@
|
|||
Feature #5445: Handle NiLines
|
||||
Feature #5457: Realistic diagonal movement
|
||||
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 #5520: Improve cell name autocompleter implementation
|
||||
|
||||
0.46.0
|
||||
------
|
||||
|
|
|
@ -39,6 +39,10 @@ originalIFS="$IFS"
|
|||
IFS=$'\n\r'
|
||||
for pair in $(cmd //c "set"); do
|
||||
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]}"
|
||||
done
|
||||
|
||||
|
@ -49,6 +53,10 @@ declare -A cmdEnvChanges
|
|||
for pair in $cmdEnv; do
|
||||
if [ -n "$pair" ]; then
|
||||
IFS='=' read -r -a separatedPair <<< "${pair}"
|
||||
if [ ${#separatedPair[@]} -ne 2 ]; then
|
||||
echo "Parsed '$pair' as ${#separatedPair[@]} parts, expected 2."
|
||||
continue
|
||||
fi
|
||||
key="${separatedPair[0]}"
|
||||
value="${separatedPair[1]}"
|
||||
if ! [ ${originalCmdEnv[$key]+_} ] || [ "${originalCmdEnv[$key]}" != "$value" ]; then
|
||||
|
|
|
@ -20,7 +20,7 @@ cmake \
|
|||
-D CMAKE_BUILD_TYPE=RELEASE \
|
||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||
-D BUILD_OPENMW=TRUE \
|
||||
-D BUILD_OPENCS=FALSE \
|
||||
-D BUILD_OPENCS=TRUE \
|
||||
-D BUILD_ESMTOOL=TRUE \
|
||||
-D BUILD_BSATOOL=TRUE \
|
||||
-D BUILD_ESSIMPORTER=TRUE \
|
||||
|
|
|
@ -19,15 +19,15 @@ Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg,
|
|||
setupUi(this);
|
||||
|
||||
loadSettings();
|
||||
mCellNameCompleter.setModel(&mCellNameCompleterModel);
|
||||
startDefaultCharacterAtField->setCompleter(&mCellNameCompleter);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) {
|
||||
// Set up an auto-completer for the "Start default character at" field
|
||||
auto *completer = new QCompleter(cellNames);
|
||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
||||
completer->setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
||||
startDefaultCharacterAtField->setCompleter(completer);
|
||||
|
||||
// Update the list of suggestions for the "Start default character at" field
|
||||
mCellNameCompleterModel.setStringList(cellNames);
|
||||
mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion);
|
||||
mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
||||
}
|
||||
|
||||
void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) {
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#define ADVANCEDPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QCompleter>
|
||||
#include <QStringListModel>
|
||||
|
||||
#include "ui_advancedpage.h"
|
||||
|
||||
|
@ -35,6 +37,8 @@ namespace Launcher
|
|||
Files::ConfigurationManager &mCfgMgr;
|
||||
Config::GameSettings &mGameSettings;
|
||||
Settings::Manager &mEngineSettings;
|
||||
QCompleter mCellNameCompleter;
|
||||
QStringListModel mCellNameCompleterModel;
|
||||
|
||||
/**
|
||||
* Load the cells associated with the given content files for use in autocomplete
|
||||
|
|
|
@ -20,7 +20,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
|
|||
add_openmw_dir (mwrender
|
||||
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
|
||||
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
|
||||
)
|
||||
|
||||
|
|
|
@ -1072,7 +1072,14 @@ void OMW::Engine::go()
|
|||
if (stats)
|
||||
{
|
||||
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());
|
||||
|
|
|
@ -35,6 +35,8 @@ namespace MWBase
|
|||
|
||||
virtual ~ScriptManager() {}
|
||||
|
||||
virtual void clear() = 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)
|
||||
|
||||
|
|
|
@ -312,6 +312,8 @@ namespace MWBase
|
|||
|
||||
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
|
||||
* ID or it is not a string the default string is returned.
|
||||
|
|
|
@ -543,6 +543,8 @@ namespace MWBase
|
|||
virtual void changeVanityModeScale(float factor) = 0;
|
||||
virtual bool vanityRotateCamera(float * rot) = 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 renderPlayer() = 0;
|
||||
|
|
|
@ -516,6 +516,12 @@ namespace MWGui
|
|||
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)
|
||||
{
|
||||
if (!object.isEmpty())
|
||||
|
|
|
@ -70,6 +70,8 @@ namespace MWGui
|
|||
|
||||
void executeFile (const std::string& path);
|
||||
|
||||
void updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr);
|
||||
|
||||
void clear();
|
||||
|
||||
virtual void resetReference ();
|
||||
|
|
|
@ -69,8 +69,8 @@ namespace
|
|||
return compareType(leftName, rightName);
|
||||
|
||||
// compare items by name
|
||||
leftName = Misc::StringUtils::lowerCase(left.mBase.getClass().getName(left.mBase));
|
||||
rightName = Misc::StringUtils::lowerCase(right.mBase.getClass().getName(right.mBase));
|
||||
leftName = Misc::StringUtils::lowerCaseUtf8(left.mBase.getClass().getName(left.mBase));
|
||||
rightName = Misc::StringUtils::lowerCaseUtf8(right.mBase.getClass().getName(right.mBase));
|
||||
|
||||
result = leftName.compare(rightName);
|
||||
if (result != 0)
|
||||
|
@ -213,7 +213,7 @@ namespace MWGui
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ namespace MWGui
|
|||
|
||||
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)
|
||||
return true;
|
||||
|
@ -285,7 +285,7 @@ namespace MWGui
|
|||
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)
|
||||
return false;
|
||||
|
||||
|
@ -318,12 +318,12 @@ namespace MWGui
|
|||
|
||||
void SortFilterItemModel::setNameFilter (const std::string& filter)
|
||||
{
|
||||
mNameFilter = Misc::StringUtils::lowerCase(filter);
|
||||
mNameFilter = Misc::StringUtils::lowerCaseUtf8(filter);
|
||||
}
|
||||
|
||||
void SortFilterItemModel::setEffectFilter (const std::string& filter)
|
||||
{
|
||||
mEffectFilter = Misc::StringUtils::lowerCase(filter);
|
||||
mEffectFilter = Misc::StringUtils::lowerCaseUtf8(filter);
|
||||
}
|
||||
|
||||
void SortFilterItemModel::update()
|
||||
|
|
|
@ -136,27 +136,6 @@ namespace MWGui
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -595,6 +595,11 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void WindowManager::updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
|
||||
{
|
||||
mConsole->updateSelectedObjectPtr(currentPtr, newPtr);
|
||||
}
|
||||
|
||||
void WindowManager::updateVisible()
|
||||
{
|
||||
bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper);
|
||||
|
|
|
@ -308,6 +308,8 @@ namespace MWGui
|
|||
virtual void unsetSelectedSpell();
|
||||
virtual void unsetSelectedWeapon();
|
||||
|
||||
virtual void updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr);
|
||||
|
||||
virtual void showCrosshair(bool show);
|
||||
virtual bool getSubtitlesEnabled();
|
||||
|
||||
|
|
|
@ -121,23 +121,21 @@ namespace MWInput
|
|||
|
||||
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 (mPreviewPOVDelay <= 0.5 &&
|
||||
(mPreviewPOVDelay += dt) > 0.5)
|
||||
{
|
||||
mPreviewPOVDelay = 1.f;
|
||||
MWBase::Environment::get().getWorld()->togglePreviewMode(true);
|
||||
}
|
||||
if (world->isFirstPerson() ? mPreviewPOVDelay > switchLimit : mPreviewPOVDelay == 0)
|
||||
world->togglePreviewMode(true);
|
||||
mPreviewPOVDelay += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
//disable preview mode
|
||||
MWBase::Environment::get().getWorld()->togglePreviewMode(false);
|
||||
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->togglePOV();
|
||||
}
|
||||
if (mPreviewPOVDelay > 0)
|
||||
world->togglePreviewMode(false);
|
||||
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= switchLimit)
|
||||
world->togglePOV();
|
||||
mPreviewPOVDelay = 0.f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
|
@ -102,6 +103,8 @@ namespace MWInput
|
|||
mMouseManager->update(dt);
|
||||
mSensorManager->update(dt);
|
||||
mActionManager->update(dt, controllerMove);
|
||||
|
||||
MWBase::Environment::get().getWorld()->applyDeferredPreviewRotationToPlayer(dt);
|
||||
}
|
||||
|
||||
void InputManager::setDragDrop(bool dragDrop)
|
||||
|
|
|
@ -108,6 +108,8 @@ namespace MWInput
|
|||
player.yaw(x);
|
||||
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
|
||||
{
|
||||
|
@ -207,17 +209,20 @@ namespace MWInput
|
|||
return;
|
||||
|
||||
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[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
|
||||
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();
|
||||
player.yaw(rot[2]);
|
||||
player.pitch(rot[0]);
|
||||
player.yaw(-rot[2]);
|
||||
player.pitch(-rot[0]);
|
||||
}
|
||||
else if (!controls)
|
||||
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
|
||||
|
||||
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
||||
}
|
||||
|
|
|
@ -249,17 +249,20 @@ namespace MWInput
|
|||
if (!mGuiCursorEnabled)
|
||||
{
|
||||
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[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
|
||||
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();
|
||||
player.yaw(rot[2]);
|
||||
player.pitch(rot[0]);
|
||||
player.yaw(-rot[2]);
|
||||
player.pitch(-rot[0]);
|
||||
}
|
||||
else if (!playerLooking)
|
||||
MWBase::Environment::get().getWorld()->disableDeferredPreviewRotation();
|
||||
|
||||
MWBase::Environment::get().getInputManager()->resetIdleTime();
|
||||
}
|
||||
|
|
|
@ -60,13 +60,14 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
|
|||
// Make all nearby actors also avoid the door
|
||||
std::vector<MWWorld::Ptr> actors;
|
||||
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(pos.asVec3(),100,actors);
|
||||
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it) {
|
||||
if(*it != getPlayer()) { //Not the player
|
||||
MWMechanics::AiSequence& seq = it->getClass().getCreatureStats(*it).getAiSequence();
|
||||
if(seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor) { //Only add it once
|
||||
seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it);
|
||||
}
|
||||
}
|
||||
for(auto& actor : actors)
|
||||
{
|
||||
if (actor == getPlayer())
|
||||
continue;
|
||||
|
||||
MWMechanics::AiSequence& seq = actor.getClass().getCreatureStats(actor).getAiSequence();
|
||||
if (seq.getTypeId() != MWMechanics::AiPackageTypeId::AvoidDoor)
|
||||
seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr), actor);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -412,6 +412,7 @@ namespace MWMechanics
|
|||
{
|
||||
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.mPosition[1] = 1;
|
||||
updateActorsMovement(actor, duration, storage);
|
||||
|
|
|
@ -135,9 +135,9 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
|||
followDistance = 313;
|
||||
short i = 0;
|
||||
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;
|
||||
++i;
|
||||
}
|
||||
|
|
|
@ -406,36 +406,36 @@ void AiSequence::fill(const ESM::AIPackageList &list)
|
|||
if (!list.mList.empty() && list.mList.begin() != (list.mList.end()-1))
|
||||
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;
|
||||
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;
|
||||
idles.reserve(8);
|
||||
for (int i=0; i<8; ++i)
|
||||
idles.push_back(data.mIdle[i]);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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());
|
||||
}
|
||||
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);
|
||||
}
|
||||
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.
|
||||
int count = 0;
|
||||
for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin();
|
||||
it != sequence.mPackages.end(); ++it)
|
||||
for (auto& container : sequence.mPackages)
|
||||
{
|
||||
if (isActualAiPackage(static_cast<AiPackageTypeId>(it->mType)))
|
||||
if (isActualAiPackage(static_cast<AiPackageTypeId>(container.mType)))
|
||||
count++;
|
||||
}
|
||||
|
||||
|
@ -468,20 +467,19 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
mRepeat = true;
|
||||
|
||||
// Load packages
|
||||
for (std::vector<ESM::AiSequence::AiPackageContainer>::const_iterator it = sequence.mPackages.begin();
|
||||
it != sequence.mPackages.end(); ++it)
|
||||
for (auto& container : sequence.mPackages)
|
||||
{
|
||||
std::unique_ptr<MWMechanics::AiPackage> package;
|
||||
switch (it->mType)
|
||||
switch (container.mType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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)
|
||||
package.reset(new AiInternalTravel(source));
|
||||
else
|
||||
|
@ -490,27 +488,27 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -809,11 +809,11 @@ namespace MWMechanics
|
|||
void AiWander::AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex, AiWanderStorage& storage)
|
||||
{
|
||||
storage.mAllowedNodes.push_back(PathFinder::makePathgridPoint(npcPos));
|
||||
for (std::vector<ESM::Pathgrid::Edge>::const_iterator it = pathGrid->mEdges.begin(); it != pathGrid->mEdges.end(); ++it)
|
||||
for (auto& edge : pathGrid->mEdges)
|
||||
{
|
||||
if (it->mV0 == pointIndex)
|
||||
if (edge.mV0 == pointIndex)
|
||||
{
|
||||
AddPointBetweenPathGridPoints(pathGrid->mPoints[it->mV0], pathGrid->mPoints[it->mV1], storage);
|
||||
AddPointBetweenPathGridPoints(pathGrid->mPoints[edge.mV0], pathGrid->mPoints[edge.mV1], storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,38 +61,36 @@ namespace MWMechanics
|
|||
|
||||
// Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the
|
||||
// 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;
|
||||
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
|
||||
if (!(spell.mData.mFlags & ESM::Spell::F_Autocalc))
|
||||
continue;
|
||||
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger();
|
||||
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
|
||||
if (baseMagicka < iAutoSpellTimesCanCast * spell.mData.mCost)
|
||||
continue;
|
||||
|
||||
if (race && race->mPowers.exists(spell->mId))
|
||||
if (race && race->mPowers.exists(spell.mId))
|
||||
continue;
|
||||
|
||||
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
||||
if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
|
||||
continue;
|
||||
|
||||
int school;
|
||||
float skillTerm;
|
||||
calcWeakestSchool(spell, actorSkills, school, skillTerm);
|
||||
calcWeakestSchool(&spell, actorSkills, school, skillTerm);
|
||||
assert(school >= 0 && school < 6);
|
||||
SchoolCaps& cap = schoolCaps[school];
|
||||
|
||||
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
|
||||
if (cap.mReachedLimit && spell.mData.mCost <= cap.mMinCost)
|
||||
continue;
|
||||
|
||||
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat();
|
||||
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
||||
if (calcAutoCastChance(&spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
||||
continue;
|
||||
|
||||
selectedSpells.push_back(spell->mId);
|
||||
selectedSpells.push_back(spell.mId);
|
||||
|
||||
if (cap.mReachedLimit)
|
||||
{
|
||||
|
@ -101,9 +99,9 @@ namespace MWMechanics
|
|||
selectedSpells.erase(found);
|
||||
|
||||
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;
|
||||
//float dummySkillTerm;
|
||||
|
@ -130,10 +128,10 @@ namespace MWMechanics
|
|||
if (cap.mCount == cap.mLimit)
|
||||
cap.mReachedLimit = true;
|
||||
|
||||
if (spell->mData.mCost < cap.mMinCost)
|
||||
if (spell.mData.mCost < cap.mMinCost)
|
||||
{
|
||||
cap.mWeakestSpell = spell->mId;
|
||||
cap.mMinCost = spell->mData.mCost;
|
||||
cap.mWeakestSpell = spell.mId;
|
||||
cap.mMinCost = spell.mData.mCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,32 +152,28 @@ namespace MWMechanics
|
|||
|
||||
std::vector<std::string> selectedSpells;
|
||||
|
||||
|
||||
const MWWorld::Store<ESM::Spell> &spells =
|
||||
esmStore.get<ESM::Spell>();
|
||||
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
||||
const MWWorld::Store<ESM::Spell> &spells = esmStore.get<ESM::Spell>();
|
||||
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;
|
||||
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
|
||||
if (!(spell.mData.mFlags & ESM::Spell::F_PCStart))
|
||||
continue;
|
||||
if (reachedLimit && spell->mData.mCost <= minCost)
|
||||
if (reachedLimit && spell.mData.mCost <= minCost)
|
||||
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;
|
||||
if (baseMagicka < spell->mData.mCost)
|
||||
if (baseMagicka < spell.mData.mCost)
|
||||
continue;
|
||||
|
||||
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;
|
||||
|
||||
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
||||
if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
|
||||
continue;
|
||||
|
||||
selectedSpells.push_back(spell->mId);
|
||||
selectedSpells.push_back(spell.mId);
|
||||
|
||||
if (reachedLimit)
|
||||
{
|
||||
|
@ -188,9 +182,9 @@ namespace MWMechanics
|
|||
selectedSpells.erase(it);
|
||||
|
||||
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)
|
||||
{
|
||||
minCost = testSpell->mData.mCost;
|
||||
|
@ -200,9 +194,9 @@ namespace MWMechanics
|
|||
}
|
||||
else
|
||||
{
|
||||
if (spell->mData.mCost < minCost)
|
||||
if (spell.mData.mCost < minCost)
|
||||
{
|
||||
weakestSpell = spell;
|
||||
weakestSpell = &spell;
|
||||
minCost = weakestSpell->mData.mCost;
|
||||
}
|
||||
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)
|
||||
{
|
||||
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
|
||||
for (const auto& spellEffect : spell->mEffects.mList)
|
||||
{
|
||||
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();
|
||||
|
||||
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
|
||||
{
|
||||
assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length);
|
||||
if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin)
|
||||
assert (spellEffect.mSkill >= 0 && spellEffect.mSkill < ESM::Skill::Length);
|
||||
if (actorSkills[spellEffect.mSkill] < iAutoSpellAttSkillMin)
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
|
||||
{
|
||||
assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length);
|
||||
if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin)
|
||||
assert (spellEffect.mAttribute >= 0 && spellEffect.mAttribute < ESM::Attribute::Length);
|
||||
if (actorAttributes[spellEffect.mAttribute] < iAutoSpellAttSkillMin)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -244,11 +237,8 @@ namespace MWMechanics
|
|||
{
|
||||
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
|
||||
float minChance = std::numeric_limits<float>::max();
|
||||
|
||||
const ESM::EffectList& effects = spell->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
||||
for (const ESM::ENAMstruct& effect : spell->mEffects.mList)
|
||||
{
|
||||
const ESM::ENAMstruct& effect = *it;
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||
|
||||
int minMagn = 1;
|
||||
|
|
|
@ -2873,11 +2873,11 @@ void CharacterController::updateContinuousVfx()
|
|||
std::vector<int> 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()
|
||||
|| mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(*it)).getMagnitude() <= 0)
|
||||
mAnimation->removeEffect(*it);
|
||||
|| mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(MWMechanics::EffectKey(effectId)).getMagnitude() <= 0)
|
||||
mAnimation->removeEffect(effectId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ namespace MWMechanics
|
|||
|
||||
std::vector<std::string> candidates;
|
||||
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)
|
||||
highestLevel = it->mLevel;
|
||||
if (levelledItem.mLevel > highestLevel && levelledItem.mLevel <= playerLevel)
|
||||
highestLevel = levelledItem.mLevel;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
&& (allLevels || it->mLevel == highestLevel))
|
||||
if (playerLevel >= levelledItem.mLevel
|
||||
&& (allLevels || levelledItem.mLevel == highestLevel))
|
||||
{
|
||||
candidates.push_back(it->mId);
|
||||
if (it->mLevel >= highest.first)
|
||||
highest = std::make_pair(it->mLevel, it->mId);
|
||||
candidates.push_back(levelledItem.mId);
|
||||
if (levelledItem.mLevel >= highest.first)
|
||||
highest = std::make_pair(levelledItem.mLevel, levelledItem.mId);
|
||||
}
|
||||
}
|
||||
if (candidates.empty())
|
||||
|
|
|
@ -316,13 +316,37 @@ namespace MWMechanics
|
|||
|
||||
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)
|
||||
{
|
||||
mUpdatePlayer = false;
|
||||
|
||||
// HACK? The player has been changed, so a new Animation object may
|
||||
// have been made for them. Make sure they're properly updated.
|
||||
MWWorld::Ptr ptr = getPlayer();
|
||||
mActors.removeActor(ptr);
|
||||
mActors.addActor(ptr, true);
|
||||
}
|
||||
|
|
|
@ -19,11 +19,10 @@ Objects::Objects()
|
|||
|
||||
Objects::~Objects()
|
||||
{
|
||||
PtrControllerMap::iterator it(mObjects.begin());
|
||||
for (; it != mObjects.end();++it)
|
||||
for(auto& object : mObjects)
|
||||
{
|
||||
delete it->second;
|
||||
it->second = nullptr;
|
||||
delete object.second;
|
||||
object.second = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,8 +76,8 @@ void Objects::update(float duration, bool paused)
|
|||
{
|
||||
if(!paused)
|
||||
{
|
||||
for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter)
|
||||
iter->second->update(duration);
|
||||
for(auto& object : mObjects)
|
||||
object.second->update(duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -87,15 +86,15 @@ void Objects::update(float duration, bool paused)
|
|||
if(mode != MWGui::GM_Container)
|
||||
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;
|
||||
|
||||
if (iter->second->isAnimPlaying("containeropen"))
|
||||
if (object.second->isAnimPlaying("containeropen"))
|
||||
{
|
||||
iter->second->update(duration);
|
||||
MWBase::Environment::get().getWorld()->updateAnimatedCollisionShape(iter->first);
|
||||
object.second->update(duration);
|
||||
MWBase::Environment::get().getWorld()->updateAnimatedCollisionShape(object.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,17 +36,13 @@ namespace MWMechanics
|
|||
|
||||
// Check all the doors in this cell
|
||||
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());
|
||||
pos.z() = 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());
|
||||
|
||||
// FIXME: cast
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../mwworld/refdata.hpp"
|
||||
|
||||
#include "../mwmechanics/drawstate.hpp"
|
||||
#include "../mwmechanics/movement.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
#include "npcanimation.hpp"
|
||||
|
@ -52,7 +53,10 @@ namespace MWRender
|
|||
mCamera(camera),
|
||||
mAnimation(nullptr),
|
||||
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),
|
||||
mFurthest(800.f),
|
||||
mIsNearest(false),
|
||||
|
@ -62,20 +66,19 @@ namespace MWRender
|
|||
mVanityToggleQueuedValue(false),
|
||||
mViewModeToggleQueued(false),
|
||||
mCameraDistance(0.f),
|
||||
mThirdPersonMode(ThirdPersonViewMode::Standard),
|
||||
mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)),
|
||||
mSmoothTransitionToCombatMode(0.f)
|
||||
mMaxNextCameraDistance(800.f),
|
||||
mFocalPointCurrentOffset(osg::Vec2d()),
|
||||
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;
|
||||
|
||||
mUpdateCallback = new UpdateRenderCameraCallback(this);
|
||||
|
@ -87,17 +90,11 @@ namespace MWRender
|
|||
mCamera->removeUpdateCallback(mUpdateCallback);
|
||||
}
|
||||
|
||||
MWWorld::Ptr Camera::getTrackingPtr() const
|
||||
{
|
||||
return mTrackingPtr;
|
||||
}
|
||||
|
||||
osg::Vec3d Camera::getFocalPoint() const
|
||||
{
|
||||
const osg::Node* trackNode = mTrackingNode;
|
||||
if (!trackNode)
|
||||
if (!mTrackingNode)
|
||||
return osg::Vec3d();
|
||||
osg::NodePathList nodepaths = trackNode->getParentalNodePaths();
|
||||
osg::NodePathList nodepaths = mTrackingNode->getParentalNodePaths();
|
||||
if (nodepaths.empty())
|
||||
return osg::Vec3d();
|
||||
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
||||
|
@ -119,15 +116,9 @@ namespace MWRender
|
|||
osg::Vec3d Camera::getFocalPointOffset() const
|
||||
{
|
||||
osg::Vec3d offset(0, 0, 10.f);
|
||||
if (mThirdPersonMode == ThirdPersonViewMode::OverShoulder && !mPreviewMode && !mVanity.enabled)
|
||||
{
|
||||
float horizontalOffset = mOverShoulderOffset.x() * (1.f - mSmoothTransitionToCombatMode);
|
||||
float verticalOffset = mSmoothTransitionToCombatMode * 15.f + (1.f - mSmoothTransitionToCombatMode) * mOverShoulderOffset.y();
|
||||
|
||||
offset.x() += horizontalOffset * cos(getYaw());
|
||||
offset.y() += horizontalOffset * sin(getYaw());
|
||||
offset.z() += verticalOffset;
|
||||
}
|
||||
offset.x() += mFocalPointCurrentOffset.x() * cos(getYaw());
|
||||
offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw());
|
||||
offset.z() += mFocalPointCurrentOffset.y();
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -145,9 +136,6 @@ namespace MWRender
|
|||
|
||||
void Camera::updateCamera(osg::Camera *cam)
|
||||
{
|
||||
if (mTrackingPtr.isEmpty())
|
||||
return;
|
||||
|
||||
osg::Vec3d focal, position;
|
||||
getPosition(focal, position);
|
||||
|
||||
|
@ -177,11 +165,6 @@ namespace MWRender
|
|||
setPitch(pitch);
|
||||
}
|
||||
|
||||
void Camera::attachTo(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
mTrackingPtr = ptr;
|
||||
}
|
||||
|
||||
void Camera::update(float duration, bool paused)
|
||||
{
|
||||
if (mAnimation->upperBodyReady())
|
||||
|
@ -194,7 +177,6 @@ namespace MWRender
|
|||
}
|
||||
if (mViewModeToggleQueued)
|
||||
{
|
||||
|
||||
togglePreviewMode(false);
|
||||
toggleViewMode();
|
||||
mViewModeToggleQueued = false;
|
||||
|
@ -206,36 +188,86 @@ namespace MWRender
|
|||
|
||||
// only show the crosshair in game mode
|
||||
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
||||
wm->showCrosshair(!wm->isGuiMode() && !mVanity.enabled && !mPreviewMode
|
||||
&& (mFirstPersonView || mThirdPersonMode != ThirdPersonViewMode::Standard));
|
||||
wm->showCrosshair(!wm->isGuiMode() && mMode != Mode::Preview && mMode != Mode::Vanity
|
||||
&& (mFirstPersonView || mShowCrosshairInThirdPersonMode));
|
||||
|
||||
if(mVanity.enabled)
|
||||
{
|
||||
if(mMode == Mode::Vanity)
|
||||
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();
|
||||
}
|
||||
|
||||
void Camera::updateStandingPreviewMode()
|
||||
{
|
||||
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::setFocalPointTargetOffset(osg::Vec2d v)
|
||||
{
|
||||
mFocalPointTargetOffset = v;
|
||||
mPreviousTransitionSpeed = mFocalPointTransitionSpeed;
|
||||
mPreviousTransitionInfluence = 1.0f;
|
||||
}
|
||||
|
||||
void Camera::updateFocalPointOffset(float duration)
|
||||
{
|
||||
if (duration <= 0)
|
||||
return;
|
||||
|
||||
if (mSkipFocalPointTransition)
|
||||
{
|
||||
mSkipFocalPointTransition = false;
|
||||
mPreviousExtraOffset = osg::Vec2d();
|
||||
mPreviousTransitionInfluence = 0.f;
|
||||
mFocalPointCurrentOffset = mFocalPointTargetOffset;
|
||||
}
|
||||
|
||||
updateSmoothTransitionToCombatMode(duration);
|
||||
}
|
||||
osg::Vec2d oldOffset = mFocalPointCurrentOffset;
|
||||
|
||||
void Camera::setOverShoulderOffset(float horizontal, float vertical)
|
||||
{
|
||||
mOverShoulderOffset = osg::Vec2f(horizontal, vertical);
|
||||
}
|
||||
if (mPreviousTransitionInfluence > 0)
|
||||
{
|
||||
mFocalPointCurrentOffset -= mPreviousExtraOffset;
|
||||
mPreviousExtraOffset = mPreviousExtraOffset / mPreviousTransitionInfluence + mPreviousTransitionSpeed * duration;
|
||||
mPreviousTransitionInfluence =
|
||||
std::max(0.f, mPreviousTransitionInfluence - duration * mFocalPointTransitionSpeedCoef);
|
||||
mPreviousExtraOffset *= mPreviousTransitionInfluence;
|
||||
mFocalPointCurrentOffset += mPreviousExtraOffset;
|
||||
}
|
||||
|
||||
void Camera::updateSmoothTransitionToCombatMode(float duration)
|
||||
{
|
||||
bool combatMode = true;
|
||||
if (mTrackingPtr.getClass().isActor())
|
||||
combatMode = mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;
|
||||
float speed = ((combatMode ? 1.f : 0.f) - mSmoothTransitionToCombatMode) * 5;
|
||||
if (speed != 0)
|
||||
speed += speed > 0 ? 1 : -1;
|
||||
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;
|
||||
}
|
||||
|
||||
mSmoothTransitionToCombatMode += speed * duration;
|
||||
if (mSmoothTransitionToCombatMode > 1)
|
||||
mSmoothTransitionToCombatMode = 1;
|
||||
if (mSmoothTransitionToCombatMode < 0)
|
||||
mSmoothTransitionToCombatMode = 0;
|
||||
mFocalPointTransitionSpeed = (mFocalPointCurrentOffset - oldOffset) / duration;
|
||||
}
|
||||
|
||||
void Camera::toggleViewMode(bool force)
|
||||
|
@ -254,14 +286,19 @@ namespace MWRender
|
|||
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0);
|
||||
|
||||
mFirstPersonView = !mFirstPersonView;
|
||||
updateStandingPreviewMode();
|
||||
instantTransition();
|
||||
processViewChange();
|
||||
}
|
||||
|
||||
|
||||
void Camera::allowVanityMode(bool allow)
|
||||
{
|
||||
if (!allow && mVanity.enabled)
|
||||
if (!allow && mMode == Mode::Vanity)
|
||||
{
|
||||
disableDeferredPreviewRotation();
|
||||
toggleVanityMode(false);
|
||||
mVanity.allowed = allow;
|
||||
}
|
||||
mVanityAllowed = allow;
|
||||
}
|
||||
|
||||
bool Camera::toggleVanityMode(bool enable)
|
||||
|
@ -275,26 +312,18 @@ namespace MWRender
|
|||
return false;
|
||||
}
|
||||
|
||||
if(!mVanity.allowed && enable)
|
||||
if (!mVanityAllowed && enable)
|
||||
return false;
|
||||
|
||||
if(mVanity.enabled == enable)
|
||||
if ((mMode == Mode::Vanity) == enable)
|
||||
return true;
|
||||
mVanity.enabled = enable;
|
||||
mMode = enable ? Mode::Vanity : Mode::Normal;
|
||||
if (!mDeferredRotationAllowed)
|
||||
disableDeferredPreviewRotation();
|
||||
if (!enable)
|
||||
calculateDeferredRotation();
|
||||
|
||||
processViewChange();
|
||||
|
||||
float offset = mPreviewCam.offset;
|
||||
|
||||
if (mVanity.enabled) {
|
||||
setPitch(osg::DegreesToRadians(-30.f));
|
||||
mMainCam.offset = mCameraDistance;
|
||||
} else {
|
||||
offset = mMainCam.offset;
|
||||
}
|
||||
|
||||
mCameraDistance = offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -303,22 +332,21 @@ namespace MWRender
|
|||
if (mFirstPersonView && !mAnimation->upperBodyReady())
|
||||
return;
|
||||
|
||||
if(mPreviewMode == enable)
|
||||
if((mMode == Mode::Preview) == enable)
|
||||
return;
|
||||
|
||||
mPreviewMode = enable;
|
||||
processViewChange();
|
||||
|
||||
float offset = mCameraDistance;
|
||||
if (mPreviewMode) {
|
||||
mMainCam.offset = offset;
|
||||
offset = mPreviewCam.offset;
|
||||
} else {
|
||||
mPreviewCam.offset = offset;
|
||||
offset = mMainCam.offset;
|
||||
mMode = enable ? Mode::Preview : Mode::Normal;
|
||||
if (mMode == Mode::Normal)
|
||||
updateStandingPreviewMode();
|
||||
else if (mFirstPersonView)
|
||||
instantTransition();
|
||||
if (mMode == Mode::Normal)
|
||||
{
|
||||
if (!mDeferredRotationAllowed)
|
||||
disableDeferredPreviewRotation();
|
||||
calculateDeferredRotation();
|
||||
}
|
||||
|
||||
mCameraDistance = offset;
|
||||
processViewChange();
|
||||
}
|
||||
|
||||
void Camera::setSneakOffset(float offset)
|
||||
|
@ -326,13 +354,6 @@ namespace MWRender
|
|||
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)
|
||||
{
|
||||
if (angle > osg::PI) {
|
||||
|
@ -340,38 +361,14 @@ namespace MWRender
|
|||
} else if (angle < -osg::PI) {
|
||||
angle += osg::PI*2;
|
||||
}
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
mPreviewCam.yaw = angle;
|
||||
} else {
|
||||
mMainCam.yaw = angle;
|
||||
}
|
||||
}
|
||||
|
||||
float Camera::getPitch() const
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
return mPreviewCam.pitch;
|
||||
}
|
||||
return mMainCam.pitch;
|
||||
mYaw = angle;
|
||||
}
|
||||
|
||||
void Camera::setPitch(float angle)
|
||||
{
|
||||
const float epsilon = 0.000001f;
|
||||
float limit = osg::PI_2 - epsilon;
|
||||
if(mPreviewMode)
|
||||
limit /= 2;
|
||||
|
||||
if(angle > limit)
|
||||
angle = limit;
|
||||
else if(angle < -limit)
|
||||
angle = -limit;
|
||||
|
||||
if (mVanity.enabled || mPreviewMode) {
|
||||
mPreviewCam.pitch = angle;
|
||||
} else {
|
||||
mMainCam.pitch = angle;
|
||||
}
|
||||
mPitch = osg::clampBetween(angle, -limit, limit);
|
||||
}
|
||||
|
||||
float Camera::getCameraDistance() const
|
||||
|
@ -383,70 +380,53 @@ namespace MWRender
|
|||
|
||||
void Camera::updateBaseCameraDistance(float dist, bool adjust)
|
||||
{
|
||||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
||||
if (isFirstPerson())
|
||||
return;
|
||||
|
||||
mIsNearest = false;
|
||||
|
||||
if (adjust)
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode)
|
||||
dist += mCameraDistance;
|
||||
else
|
||||
dist += std::min(mCameraDistance - getCameraDistanceCorrection(), mBaseCameraDistance);
|
||||
}
|
||||
dist += std::min(mCameraDistance - getCameraDistanceCorrection(), mBaseCameraDistance);
|
||||
|
||||
|
||||
if (dist >= mFurthest)
|
||||
dist = mFurthest;
|
||||
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);
|
||||
}
|
||||
mIsNearest = dist <= mNearest;
|
||||
mBaseCameraDistance = osg::clampBetween(dist, mNearest, mFurthest);
|
||||
Settings::Manager::setFloat("third person camera distance", "Camera", mBaseCameraDistance);
|
||||
setCameraDistance();
|
||||
}
|
||||
|
||||
void Camera::setCameraDistance(float dist, bool adjust)
|
||||
{
|
||||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
||||
if (isFirstPerson())
|
||||
return;
|
||||
|
||||
if (adjust) dist += mCameraDistance;
|
||||
|
||||
if (dist >= mFurthest)
|
||||
dist = mFurthest;
|
||||
else if (dist < 10.f)
|
||||
dist = 10.f;
|
||||
mCameraDistance = dist;
|
||||
if (adjust)
|
||||
dist += mCameraDistance;
|
||||
mCameraDistance = osg::clampBetween(dist, 10.f, mFurthest);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (mVanity.enabled || mPreviewMode)
|
||||
mCameraDistance = mPreviewCam.offset;
|
||||
else if (!mFirstPersonView)
|
||||
mCameraDistance = mBaseCameraDistance + getCameraDistanceCorrection();
|
||||
mFocalPointAdjustment = osg::Vec3d();
|
||||
if (isFirstPerson())
|
||||
return;
|
||||
mCameraDistance = mBaseCameraDistance + getCameraDistanceCorrection();
|
||||
if (mDynamicCameraDistanceEnabled)
|
||||
mCameraDistance = std::min(mCameraDistance, mMaxNextCameraDistance);
|
||||
}
|
||||
|
||||
void Camera::setAnimation(NpcAnimation *anim)
|
||||
{
|
||||
mAnimation = anim;
|
||||
|
||||
processViewChange();
|
||||
}
|
||||
|
||||
|
@ -473,13 +453,74 @@ namespace MWRender
|
|||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool Camera::isNearest() const
|
||||
void Camera::rotateCameraToTrackingPtr()
|
||||
{
|
||||
return mIsNearest;
|
||||
setPitch(-mTrackingPtr.getRefData().getPosition().rot[0] - mDeferredRotation.x());
|
||||
setYaw(-mTrackingPtr.getRefData().getPosition().rot[2] - mDeferredRotation.z());
|
||||
}
|
||||
|
||||
void Camera::instantTransition()
|
||||
{
|
||||
mSkipFocalPointTransition = true;
|
||||
mDeferredRotationDisabled = false;
|
||||
mDeferredRotation = osg::Vec3f();
|
||||
rotateCameraToTrackingPtr();
|
||||
}
|
||||
|
||||
void Camera::calculateDeferredRotation()
|
||||
{
|
||||
MWWorld::Ptr ptr = mTrackingPtr;
|
||||
if (isVanityOrPreviewModeEnabled() || ptr.isEmpty())
|
||||
return;
|
||||
if (mFirstPersonView)
|
||||
{
|
||||
instantTransition();
|
||||
return;
|
||||
}
|
||||
|
||||
mDeferredRotation.x() = -ptr.getRefData().getPosition().rot[0] - mPitch;
|
||||
mDeferredRotation.z() = -ptr.getRefData().getPosition().rot[2] - mYaw;
|
||||
if (mDeferredRotation.x() > osg::PI)
|
||||
mDeferredRotation.x() -= 2 * osg::PI;
|
||||
if (mDeferredRotation.x() < -osg::PI)
|
||||
mDeferredRotation.x() += 2 * osg::PI;
|
||||
if (mDeferredRotation.z() > osg::PI)
|
||||
mDeferredRotation.z() -= 2 * osg::PI;
|
||||
if (mDeferredRotation.z() < -osg::PI)
|
||||
mDeferredRotation.z() += 2 * osg::PI;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,13 +24,9 @@ namespace MWRender
|
|||
class Camera
|
||||
{
|
||||
public:
|
||||
enum class ThirdPersonViewMode {Standard, OverShoulder};
|
||||
enum class Mode { Normal, Vanity, Preview, StandingPreview };
|
||||
|
||||
private:
|
||||
struct CamData {
|
||||
float pitch, yaw, offset;
|
||||
};
|
||||
|
||||
MWWorld::Ptr mTrackingPtr;
|
||||
osg::ref_ptr<const osg::Node> mTrackingNode;
|
||||
float mHeightScale;
|
||||
|
@ -40,44 +36,66 @@ namespace MWRender
|
|||
NpcAnimation *mAnimation;
|
||||
|
||||
bool mFirstPersonView;
|
||||
bool mPreviewMode;
|
||||
Mode mMode;
|
||||
bool mVanityAllowed;
|
||||
bool mStandingPreviewAllowed;
|
||||
bool mDeferredRotationAllowed;
|
||||
|
||||
float mNearest;
|
||||
float mFurthest;
|
||||
bool mIsNearest;
|
||||
|
||||
struct {
|
||||
bool enabled, allowed;
|
||||
} mVanity;
|
||||
|
||||
float mHeight, mBaseCameraDistance;
|
||||
CamData mMainCam, mPreviewCam;
|
||||
float mPitch, mYaw;
|
||||
|
||||
bool mVanityToggleQueued;
|
||||
bool mVanityToggleQueuedValue;
|
||||
bool mViewModeToggleQueued;
|
||||
|
||||
float mCameraDistance;
|
||||
float mMaxNextCameraDistance;
|
||||
|
||||
ThirdPersonViewMode mThirdPersonMode;
|
||||
osg::Vec2f mOverShoulderOffset;
|
||||
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].
|
||||
// Used for smooth transition from non-combat camera position (0) to combat camera position (1).
|
||||
float mSmoothTransitionToCombatMode;
|
||||
void updateSmoothTransitionToCombatMode(float duration);
|
||||
// This fields are used to make focal point transition smooth if previous transition was not finished.
|
||||
float mPreviousTransitionInfluence;
|
||||
osg::Vec2d mFocalPointTransitionSpeed;
|
||||
osg::Vec2d mPreviousTransitionSpeed;
|
||||
osg::Vec2d mPreviousExtraOffset;
|
||||
|
||||
float mSmoothedSpeed;
|
||||
float mZoomOutWhenMoveCoef;
|
||||
bool mDynamicCameraDistanceEnabled;
|
||||
bool mShowCrosshairInThirdPersonMode;
|
||||
|
||||
void updateFocalPointOffset(float duration);
|
||||
float getCameraDistanceCorrection() const;
|
||||
|
||||
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:
|
||||
Camera(osg::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 setOverShoulderOffset(float horizontal, float vertical);
|
||||
void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; }
|
||||
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
|
||||
void updateCamera(osg::Camera* cam);
|
||||
|
@ -88,16 +106,14 @@ namespace MWRender
|
|||
/// Set where the camera is looking at. Uses Morrowind (euler) angles
|
||||
/// \param rot Rotation angles in radians
|
||||
void rotateCamera(float pitch, float yaw, bool adjust);
|
||||
void rotateCameraToTrackingPtr();
|
||||
|
||||
float getYaw() const;
|
||||
float getYaw() const { return mYaw; }
|
||||
void setYaw(float angle);
|
||||
|
||||
float getPitch() const;
|
||||
float getPitch() const { return mPitch; }
|
||||
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.
|
||||
void toggleViewMode(bool force=false);
|
||||
|
||||
|
@ -107,11 +123,13 @@ namespace MWRender
|
|||
/// @note this may be ignored if an important animation is currently playing
|
||||
void togglePreviewMode(bool enable);
|
||||
|
||||
void applyDeferredPreviewRotationToPlayer(float dt);
|
||||
void disableDeferredPreviewRotation() { mDeferredRotationDisabled = true; }
|
||||
|
||||
/// \brief Lowers the camera for sneak.
|
||||
void setSneakOffset(float offset);
|
||||
|
||||
bool isFirstPerson() const
|
||||
{ return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); }
|
||||
bool isFirstPerson() const { return mFirstPersonView && mMode == Mode::Normal; }
|
||||
|
||||
void processViewChange();
|
||||
|
||||
|
@ -140,9 +158,10 @@ namespace MWRender
|
|||
/// Stores focal and camera world positions in passed arguments
|
||||
void getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const;
|
||||
|
||||
bool isVanityOrPreviewModeEnabled() const;
|
||||
bool isVanityOrPreviewModeEnabled() const { return mMode != Mode::Normal; }
|
||||
Mode getMode() const { return mMode; }
|
||||
|
||||
bool isNearest() const;
|
||||
bool isNearest() const { return mIsNearest; }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "vismask.hpp"
|
||||
#include "pathgrid.hpp"
|
||||
#include "camera.hpp"
|
||||
#include "viewovershoulder.hpp"
|
||||
#include "water.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
#include "util.hpp"
|
||||
|
@ -306,6 +307,8 @@ namespace MWRender
|
|||
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
||||
|
||||
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);
|
||||
|
||||
|
@ -366,7 +369,6 @@ namespace MWRender
|
|||
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
|
||||
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
||||
mStateUpdater->setFogEnd(mViewDistance);
|
||||
updateThirdPersonViewMode();
|
||||
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
||||
|
@ -382,19 +384,6 @@ namespace MWRender
|
|||
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()
|
||||
{
|
||||
return mViewer->getIncrementalCompileOperation();
|
||||
|
@ -630,6 +619,8 @@ namespace MWRender
|
|||
updateNavMesh();
|
||||
updateRecastMesh();
|
||||
|
||||
if (mViewOverShoulderController)
|
||||
mViewOverShoulderController->update();
|
||||
mCamera->update(dt, paused);
|
||||
|
||||
osg::Vec3d focal, cameraPos;
|
||||
|
@ -662,7 +653,7 @@ namespace MWRender
|
|||
if(ptr == mCamera->getTrackingPtr() &&
|
||||
!mCamera->isVanityOrPreviewModeEnabled())
|
||||
{
|
||||
mCamera->rotateCamera(-ptr.getRefData().getPosition().rot[0], -ptr.getRefData().getPosition().rot[2], false);
|
||||
mCamera->rotateCameraToTrackingPtr();
|
||||
}
|
||||
|
||||
ptr.getRefData().getBaseNode()->setAttitude(rot);
|
||||
|
|
|
@ -79,6 +79,7 @@ namespace MWRender
|
|||
class NpcAnimation;
|
||||
class Pathgrid;
|
||||
class Camera;
|
||||
class ViewOverShoulderController;
|
||||
class Water;
|
||||
class TerrainStorage;
|
||||
class LandManager;
|
||||
|
@ -294,6 +295,7 @@ namespace MWRender
|
|||
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
|
||||
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
|
||||
std::unique_ptr<Camera> mCamera;
|
||||
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
|
||||
osg::Vec3f mCurrentCameraPos;
|
||||
|
||||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||
|
|
106
apps/openmw/mwrender/viewovershoulder.cpp
Normal file
106
apps/openmw/mwrender/viewovershoulder.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "viewovershoulder.hpp"
|
||||
|
||||
#include <osg/Quat>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/refdata.hpp"
|
||||
|
||||
#include "../mwmechanics/drawstate.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
ViewOverShoulderController::ViewOverShoulderController(Camera* camera) :
|
||||
mCamera(camera), mMode(Mode::RightShoulder),
|
||||
mAutoSwitchShoulder(Settings::Manager::getBool("auto switch shoulder", "Camera")),
|
||||
mOverShoulderHorizontalOffset(30.f), mOverShoulderVerticalOffset(-10.f)
|
||||
{
|
||||
osg::Vec2f offset = Settings::Manager::getVector2("view over shoulder offset", "Camera");
|
||||
mOverShoulderHorizontalOffset = std::abs(offset.x());
|
||||
mOverShoulderVerticalOffset = offset.y();
|
||||
mDefaultShoulderIsRight = offset.x() >= 0;
|
||||
|
||||
mCamera->enableDynamicCameraDistance(true);
|
||||
mCamera->enableCrosshairInThirdPersonMode(true);
|
||||
mCamera->setFocalPointTargetOffset(offset);
|
||||
}
|
||||
|
||||
void ViewOverShoulderController::update()
|
||||
{
|
||||
if (mCamera->isFirstPerson())
|
||||
return;
|
||||
|
||||
Mode oldMode = mMode;
|
||||
auto ptr = mCamera->getTrackingPtr();
|
||||
bool combat = ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing;
|
||||
if (combat && !mCamera->isVanityOrPreviewModeEnabled())
|
||||
mMode = Mode::Combat;
|
||||
else if (MWBase::Environment::get().getWorld()->isSwimming(ptr))
|
||||
mMode = Mode::Swimming;
|
||||
else if (oldMode == Mode::Combat || oldMode == Mode::Swimming)
|
||||
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
|
||||
if (mAutoSwitchShoulder && (mMode == Mode::LeftShoulder || mMode == Mode::RightShoulder))
|
||||
trySwitchShoulder();
|
||||
|
||||
if (oldMode == mMode)
|
||||
return;
|
||||
|
||||
if (mCamera->getMode() == Camera::Mode::Vanity)
|
||||
// Player doesn't touch controls for a long time. Transition should be very slow.
|
||||
mCamera->setFocalPointTransitionSpeed(0.2f);
|
||||
else if ((oldMode == Mode::Combat || mMode == Mode::Combat) && mCamera->getMode() == Camera::Mode::Normal)
|
||||
// Transition to/from combat mode and we are not it preview mode. Should be fast.
|
||||
mCamera->setFocalPointTransitionSpeed(5.f);
|
||||
else
|
||||
mCamera->setFocalPointTransitionSpeed(1.f); // Default transition speed.
|
||||
|
||||
switch (mMode)
|
||||
{
|
||||
case Mode::RightShoulder:
|
||||
mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
|
||||
break;
|
||||
case Mode::LeftShoulder:
|
||||
mCamera->setFocalPointTargetOffset({-mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
|
||||
break;
|
||||
case Mode::Combat:
|
||||
case Mode::Swimming:
|
||||
default:
|
||||
mCamera->setFocalPointTargetOffset({0, 15});
|
||||
}
|
||||
}
|
||||
|
||||
void ViewOverShoulderController::trySwitchShoulder()
|
||||
{
|
||||
if (mCamera->getMode() != Camera::Mode::Normal)
|
||||
return;
|
||||
|
||||
const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit
|
||||
const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance
|
||||
|
||||
auto orient = osg::Quat(mCamera->getYaw(), osg::Vec3d(0,0,1));
|
||||
osg::Vec3d playerPos = mCamera->getFocalPoint() - mCamera->getFocalPointOffset();
|
||||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
osg::Vec3d sideOffset = orient * osg::Vec3d(world->getHalfExtents(mCamera->getTrackingPtr()).x() - 1, 0, 0);
|
||||
float rayRight = world->getDistToNearestRayHit(
|
||||
playerPos + sideOffset, orient * osg::Vec3d(1, 1, 0), limitToSwitchBack + 1);
|
||||
float rayLeft = world->getDistToNearestRayHit(
|
||||
playerPos - sideOffset, orient * osg::Vec3d(-1, 1, 0), limitToSwitchBack + 1);
|
||||
float rayForward = world->getDistToNearestRayHit(
|
||||
playerPos, orient * osg::Vec3d(0, 1, 0), limitToSwitchBack + 1);
|
||||
|
||||
if (rayLeft < limitToSwitch && rayRight > limitToSwitchBack)
|
||||
mMode = Mode::RightShoulder;
|
||||
else if (rayRight < limitToSwitch && rayLeft > limitToSwitchBack)
|
||||
mMode = Mode::LeftShoulder;
|
||||
else if (rayLeft > limitToSwitchBack && rayRight > limitToSwitchBack && rayForward > limitToSwitchBack)
|
||||
mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder;
|
||||
}
|
||||
|
||||
}
|
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal file
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef VIEWOVERSHOULDER_H
|
||||
#define VIEWOVERSHOULDER_H
|
||||
|
||||
#include "camera.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class ViewOverShoulderController
|
||||
{
|
||||
public:
|
||||
ViewOverShoulderController(Camera* camera);
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
void trySwitchShoulder();
|
||||
enum class Mode { RightShoulder, LeftShoulder, Combat, Swimming };
|
||||
|
||||
Camera* mCamera;
|
||||
Mode mMode;
|
||||
bool mAutoSwitchShoulder;
|
||||
float mOverShoulderHorizontalOffset;
|
||||
float mOverShoulderVerticalOffset;
|
||||
bool mDefaultShoulderIsRight;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // VIEWOVERSHOULDER_H
|
|
@ -79,8 +79,8 @@ namespace MWScript
|
|||
if (Success)
|
||||
{
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
mParser.getCode (code);
|
||||
mScripts.insert (std::make_pair (name, std::make_pair (code, mParser.getLocals())));
|
||||
mParser.getCode(code);
|
||||
mScripts.emplace(name, CompiledScript(code, mParser.getLocals()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ namespace MWScript
|
|||
{
|
||||
// failed -> ignore script from now on.
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ namespace MWScript
|
|||
}
|
||||
|
||||
// execute script
|
||||
if (!iter->second.first.empty())
|
||||
if (!iter->second.mByteCode.empty() && iter->second.mActive)
|
||||
try
|
||||
{
|
||||
if (!mOpcodesInstalled)
|
||||
|
@ -118,7 +118,7 @@ namespace MWScript
|
|||
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;
|
||||
}
|
||||
catch (const MissingImplicitRefError& e)
|
||||
|
@ -129,11 +129,21 @@ namespace MWScript
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void ScriptManager::clear()
|
||||
{
|
||||
for (auto& script : mScripts)
|
||||
{
|
||||
script.second.mActive = true;
|
||||
}
|
||||
|
||||
mGlobalScripts.clear();
|
||||
}
|
||||
|
||||
std::pair<int, int> ScriptManager::compileAll()
|
||||
{
|
||||
int count = 0;
|
||||
|
@ -163,7 +173,7 @@ namespace MWScript
|
|||
ScriptCollection::iterator iter = mScripts.find (name2);
|
||||
|
||||
if (iter!=mScripts.end())
|
||||
return iter->second.second;
|
||||
return iter->second.mLocals;
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -41,7 +41,20 @@ namespace MWScript
|
|||
Interpreter::Interpreter mInterpreter;
|
||||
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;
|
||||
|
||||
ScriptCollection mScripts;
|
||||
|
@ -55,6 +68,8 @@ namespace MWScript
|
|||
Compiler::Context& compilerContext, int warningsMode,
|
||||
const std::vector<std::string>& scriptBlacklist);
|
||||
|
||||
virtual void clear();
|
||||
|
||||
virtual bool run (const std::string& name, Interpreter::Context& interpreterContext);
|
||||
///< Run the script with the given name (compile first, if not compiled yet)
|
||||
|
||||
|
|
|
@ -897,7 +897,8 @@ namespace MWScript
|
|||
// We should move actors, standing on moving object, too.
|
||||
// This approach can be used to create elevators.
|
||||
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.
|
||||
// This approach can be used to create elevators.
|
||||
moveStandingActors(ptr, diff);
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z());
|
||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(ptr,
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z()));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,85 +11,65 @@ namespace MWSound
|
|||
inline int operator&(int a, PlayMode b) { return a & static_cast<int>(b); }
|
||||
inline int operator&(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 {
|
||||
SoundBase& operator=(const SoundBase&) = delete;
|
||||
SoundBase(const SoundBase&) = delete;
|
||||
SoundBase(SoundBase&&) = delete;
|
||||
|
||||
osg::Vec3f mPos;
|
||||
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
|
||||
float mBaseVolume;
|
||||
float mPitch;
|
||||
float mMinDistance;
|
||||
float mMaxDistance;
|
||||
int mFlags;
|
||||
|
||||
float mFadeOutTime;
|
||||
SoundParams mParams;
|
||||
|
||||
protected:
|
||||
Sound_Instance mHandle;
|
||||
Sound_Instance mHandle = nullptr;
|
||||
|
||||
friend class OpenAL_Output;
|
||||
|
||||
public:
|
||||
void setPosition(const osg::Vec3f &pos) { mPos = pos; }
|
||||
void setVolume(float volume) { mVolume = volume; }
|
||||
void setBaseVolume(float volume) { mBaseVolume = volume; }
|
||||
void setFadeout(float duration) { mFadeOutTime = duration; }
|
||||
void setPosition(const osg::Vec3f &pos) { mParams.mPos = pos; }
|
||||
void setVolume(float volume) { mParams.mVolume = volume; }
|
||||
void setBaseVolume(float volume) { mParams.mBaseVolume = volume; }
|
||||
void setFadeout(float duration) { mParams.mFadeOutTime = duration; }
|
||||
void updateFade(float duration)
|
||||
{
|
||||
if(mFadeOutTime > 0.0f)
|
||||
if (mParams.mFadeOutTime > 0.0f)
|
||||
{
|
||||
float soundDuration = std::min(duration, mFadeOutTime);
|
||||
mVolume *= (mFadeOutTime-soundDuration) / mFadeOutTime;
|
||||
mFadeOutTime -= soundDuration;
|
||||
float soundDuration = std::min(duration, mParams.mFadeOutTime);
|
||||
mParams.mVolume *= (mParams.mFadeOutTime - soundDuration) / mParams.mFadeOutTime;
|
||||
mParams.mFadeOutTime -= soundDuration;
|
||||
}
|
||||
}
|
||||
|
||||
const osg::Vec3f &getPosition() const { return mPos; }
|
||||
float getRealVolume() const { return mVolume * mBaseVolume; }
|
||||
float getPitch() const { return mPitch; }
|
||||
float getMinDistance() const { return mMinDistance; }
|
||||
float getMaxDistance() const { return mMaxDistance; }
|
||||
const osg::Vec3f &getPosition() const { return mParams.mPos; }
|
||||
float getRealVolume() const { return mParams.mVolume * mParams.mBaseVolume; }
|
||||
float getPitch() const { return mParams.mPitch; }
|
||||
float getMinDistance() const { return mParams.mMinDistance; }
|
||||
float getMaxDistance() const { return mParams.mMaxDistance; }
|
||||
|
||||
MWSound::Type getPlayType() const
|
||||
{ return static_cast<MWSound::Type>(mFlags&MWSound::Type::Mask); }
|
||||
bool getUseEnv() const { return !(mFlags&MWSound::PlayMode::NoEnv); }
|
||||
bool getIsLooping() const { return mFlags&MWSound::PlayMode::Loop; }
|
||||
bool getDistanceCull() const { return mFlags&MWSound::PlayMode::RemoveAtDistance; }
|
||||
bool getIs3D() const { return mFlags&Play_3D; }
|
||||
{ return static_cast<MWSound::Type>(mParams.mFlags & MWSound::Type::Mask); }
|
||||
bool getUseEnv() const { return !(mParams.mFlags & MWSound::PlayMode::NoEnv); }
|
||||
bool getIsLooping() const { return mParams.mFlags & MWSound::PlayMode::Loop; }
|
||||
bool getDistanceCull() const { return mParams.mFlags & MWSound::PlayMode::RemoveAtDistance; }
|
||||
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;
|
||||
mVolume = vol;
|
||||
mBaseVolume = basevol;
|
||||
mPitch = pitch;
|
||||
mMinDistance = mindist;
|
||||
mMaxDistance = maxdist;
|
||||
mFlags = flags;
|
||||
mFadeOutTime = 0.0f;
|
||||
mParams = params;
|
||||
mHandle = nullptr;
|
||||
}
|
||||
|
||||
void init(float vol, float basevol, float pitch, int flags)
|
||||
{
|
||||
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)
|
||||
{ }
|
||||
SoundBase() = default;
|
||||
};
|
||||
|
||||
class Sound : public SoundBase {
|
||||
|
|
|
@ -290,13 +290,25 @@ namespace MWSound
|
|||
StreamPtr sound = getStreamRef();
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
sound->init(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance,
|
||||
PlayMode::Normal|Type::Voice|Play_3D);
|
||||
sound->init([&] {
|
||||
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);
|
||||
}
|
||||
if(!played)
|
||||
|
@ -332,8 +344,12 @@ namespace MWSound
|
|||
decoder->open(filename);
|
||||
|
||||
mMusic = getStreamRef();
|
||||
mMusic->init(1.0f, volumeFromType(Type::Music), 1.0f,
|
||||
PlayMode::NoEnv|Type::Music|Play_2D);
|
||||
mMusic->init([&] {
|
||||
SoundParams params;
|
||||
params.mBaseVolume = volumeFromType(Type::Music);
|
||||
params.mFlags = PlayMode::NoEnv | Type::Music | Play_2D;
|
||||
return params;
|
||||
} ());
|
||||
mOutput->streamSound(decoder, mMusic.get());
|
||||
}
|
||||
|
||||
|
@ -561,7 +577,12 @@ namespace MWSound
|
|||
return nullptr;
|
||||
|
||||
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()))
|
||||
return nullptr;
|
||||
|
||||
|
@ -598,7 +619,14 @@ namespace MWSound
|
|||
stopSound(sfx, MWWorld::ConstPtr());
|
||||
|
||||
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))
|
||||
return nullptr;
|
||||
|
||||
|
@ -635,13 +663,29 @@ namespace MWSound
|
|||
SoundPtr sound = getSoundRef();
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
sound->init(objpos, volume * sfx->mVolume, volumeFromType(type), pitch,
|
||||
sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D);
|
||||
sound->init([&] {
|
||||
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);
|
||||
}
|
||||
if(!played)
|
||||
|
@ -670,8 +714,17 @@ namespace MWSound
|
|||
if(!sfx) return nullptr;
|
||||
|
||||
SoundPtr sound = getSoundRef();
|
||||
sound->init(initialPos, volume * sfx->mVolume, volumeFromType(type), pitch,
|
||||
sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D);
|
||||
sound->init([&] {
|
||||
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))
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ void MWState::StateManager::cleanup (bool force)
|
|||
MWBase::Environment::get().getSoundManager()->clear();
|
||||
MWBase::Environment::get().getDialogueManager()->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().getWindowManager()->clear();
|
||||
MWBase::Environment::get().getInputManager()->clear();
|
||||
|
|
|
@ -1062,6 +1062,7 @@ namespace MWWorld
|
|||
removeContainerScripts(getPlayerPtr());
|
||||
mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent);
|
||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||
mRendering->getCamera()->instantTransition();
|
||||
}
|
||||
|
||||
void World::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
||||
|
@ -1077,6 +1078,7 @@ namespace MWWorld
|
|||
removeContainerScripts(getPlayerPtr());
|
||||
mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent);
|
||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||
mRendering->getCamera()->instantTransition();
|
||||
}
|
||||
|
||||
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);
|
||||
MWBase::Environment::get().getSoundManager()->updatePtr (ptr, newPtr);
|
||||
mPhysics->updatePtr(ptr, newPtr);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
|
||||
|
||||
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
|
||||
mechMgr->updateCell(ptr, newPtr);
|
||||
|
@ -1366,6 +1367,9 @@ namespace MWWorld
|
|||
End of tes3mp addition
|
||||
*/
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->updateConsoleObjectPtr(ptr, newPtr);
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().updatePtrs(ptr, newPtr);
|
||||
}
|
||||
if (haveToMove && newPtr.getRefData().getBaseNode())
|
||||
{
|
||||
|
@ -2105,7 +2109,7 @@ namespace MWWorld
|
|||
std::string enchantId = selectedEnchantItem.getClass().getEnchantment(selectedEnchantItem);
|
||||
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)
|
||||
preloadEffects(&ench->mEffects);
|
||||
}
|
||||
|
@ -2656,6 +2660,16 @@ namespace MWWorld
|
|||
return mRendering->toggleVanityMode(enable);
|
||||
}
|
||||
|
||||
void World::disableDeferredPreviewRotation()
|
||||
{
|
||||
mRendering->getCamera()->disableDeferredPreviewRotation();
|
||||
}
|
||||
|
||||
void World::applyDeferredPreviewRotationToPlayer(float dt)
|
||||
{
|
||||
mRendering->getCamera()->applyDeferredPreviewRotationToPlayer(dt);
|
||||
}
|
||||
|
||||
void World::allowVanityMode(bool allow)
|
||||
{
|
||||
mRendering->allowVanityMode(allow);
|
||||
|
|
|
@ -646,6 +646,9 @@ namespace MWWorld
|
|||
bool vanityRotateCamera(float * rot) override;
|
||||
void setCameraDistance(float dist, bool adjust = false, bool override = true) override;
|
||||
|
||||
void applyDeferredPreviewRotationToPlayer(float dt) override;
|
||||
void disableDeferredPreviewRotation() override;
|
||||
|
||||
void setupPlayer() override;
|
||||
void renderPlayer() override;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ ContentSelectorView::ComboBox::ComboBox(QWidget *parent) :
|
|||
{
|
||||
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
||||
setValidator(mValidator);
|
||||
setEditable(true);
|
||||
setCompleter(0);
|
||||
setEnabled (true);
|
||||
|
||||
|
|
|
@ -76,6 +76,28 @@ bool Manager::getBool (const std::string& setting, const std::string& category)
|
|||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
CategorySettingValueMap::key_type key = std::make_pair(category, setting);
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <osg/Vec2f>
|
||||
#include <osg/Vec3f>
|
||||
|
||||
namespace Settings
|
||||
{
|
||||
|
@ -44,11 +46,15 @@ namespace Settings
|
|||
static float getFloat (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 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 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 setBool (const std::string& setting, const std::string& category, const bool value);
|
||||
static void setVector2 (const std::string& setting, const std::string& category, const osg::Vec2f value);
|
||||
static void setVector3 (const std::string& setting, const std::string& category, const osg::Vec3f value);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -150,3 +150,51 @@ Recommened values: 30 -10 for the right shoulder, -30 -10 for the left shoulder.
|
|||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
auto switch shoulder
|
||||
--------------------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
:Default: True
|
||||
|
||||
This setting makes difference only in third person mode if 'view over shoulder' is enabled.
|
||||
When player is close to an obstacle, automatically switches camera to the shoulder that is farther away from the obstacle.
|
||||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
zoom out when move coef
|
||||
-----------------------
|
||||
|
||||
:Type: floating point
|
||||
:Range: Any
|
||||
:Default: 20
|
||||
|
||||
This setting makes difference only in third person mode if 'view over shoulder' is enabled.
|
||||
Slightly pulls camera away (or closer in case of negative value) when the character moves. To disable set it to zero.
|
||||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
preview if stand still
|
||||
----------------------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
:Default: False
|
||||
|
||||
If enabled then the character rotation is not synchonized with the camera rotation while the character doesn't move and not in combat mode.
|
||||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
deferred preview rotation
|
||||
-------------------------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
:Default: True
|
||||
|
||||
Makes difference only in third person mode.
|
||||
If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode.
|
||||
If disabled then the camera rotates rather than the character.
|
||||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
|
|
|
@ -42,6 +42,18 @@ view over shoulder = false
|
|||
# Makes sense only if 'view over shoulder' is true. First number is horizontal offset (negative value means offset to the left), second number is vertical offset.
|
||||
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]
|
||||
|
||||
# Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled.
|
||||
|
|
Loading…
Reference in a new issue