1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 20:19:57 +00:00

Merge branch 'master' into coverity_scan

This commit is contained in:
Marc Zinnschlag 2018-09-08 11:22:41 +02:00
commit 1900cf822b
227 changed files with 4955 additions and 1980 deletions

View file

@ -24,8 +24,6 @@ addons:
packages: [ packages: [
# Dev # Dev
cmake, clang-3.6, libunshield-dev, libtinyxml-dev, cmake, clang-3.6, libunshield-dev, libtinyxml-dev,
# Tests
libgtest-dev, google-mock,
# Boost # Boost
libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev,
# FFmpeg # FFmpeg

View file

@ -153,6 +153,7 @@ Programmers
Siimacore Siimacore
sir_herrbatka sir_herrbatka
smbas smbas
Sophie Kirschner (pineapplemachine)
spycrab spycrab
Stefan Galowicz (bogglez) Stefan Galowicz (bogglez)
Stanislav Bobrov (Jiub) Stanislav Bobrov (Jiub)
@ -171,7 +172,9 @@ Programmers
viadanna viadanna
Vincent Heuken Vincent Heuken
vocollapse vocollapse
Yohaulticetl
zelurker zelurker
James Carty (MrTopCat)
Documentation Documentation
------------- -------------
@ -180,9 +183,11 @@ Documentation
Alejandro Sanchez (HiPhish) Alejandro Sanchez (HiPhish)
Bodillium Bodillium
Bret Curtis (psi29a) Bret Curtis (psi29a)
David Walley (Loriel)
Cramal Cramal
Ryan Tucker (Ravenwing) Ryan Tucker (Ravenwing)
sir_herrbatka sir_herrbatka
Diego Crespo
Packagers Packagers
--------- ---------

View file

@ -4,6 +4,8 @@
Bug #1990: Sunrise/sunset not set correct Bug #1990: Sunrise/sunset not set correct
Bug #2131: Lustidrike's spell misses the player every time Bug #2131: Lustidrike's spell misses the player every time
Bug #2222: Fatigue's effect on selling price is backwards Bug #2222: Fatigue's effect on selling price is backwards
Bug #2256: Landing sound not playing when jumping immediately after landing
Bug #2274: Thin platform clips through player character instead of lifting
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
Bug #2455: Creatures attacks degrade armor Bug #2455: Creatures attacks degrade armor
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
@ -12,15 +14,25 @@
Bug #2835: Player able to slowly move when overencumbered Bug #2835: Player able to slowly move when overencumbered
Bug #2852: No murder bounty when a player follower commits murder Bug #2852: No murder bounty when a player follower commits murder
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
Bug #2872: Tab completion in console doesn't work with explicit reference
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue
Bug #3059: Unable to hit with marksman weapons when too close to an enemy
Bug #3072: Fatal error on AddItem <item> that has a script containing Equip <item>
Bug #3249: Fixed revert function not updating views properly Bug #3249: Fixed revert function not updating views properly
Bug #3374: Touch spells not hitting kwama foragers Bug #3374: Touch spells not hitting kwama foragers
Bug #3486: [Mod] NPC Commands does not work Bug #3486: [Mod] NPC Commands does not work
Bug #3533: GetSpellEffects should detect effects with zero duration
Bug #3591: Angled hit distance too low Bug #3591: Angled hit distance too low
Bug #3629: DB assassin attack never triggers creature spawning Bug #3629: DB assassin attack never triggers creature spawning
Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers
Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla
Bug #3836: Script fails to compile when command argument contains "\n"
Bug #3876: Landscape texture painting is misaligned Bug #3876: Landscape texture painting is misaligned
Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3897: Have Goodbye give all choices the effects of Goodbye
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
Bug #3920: RemoveSpellEffects doesn't remove constant effects
Bug #3948: AiCombat moving target aiming uses incorrect speed for magic projectiles
Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes
Bug #3993: Terrain texture blending map is not upscaled Bug #3993: Terrain texture blending map is not upscaled
Bug #3997: Almalexia doesn't pace Bug #3997: Almalexia doesn't pace
@ -31,15 +43,20 @@
Bug #4215: OpenMW shows book text after last EOL tag Bug #4215: OpenMW shows book text after last EOL tag
Bug #4221: Characters get stuck in V-shaped terrain Bug #4221: Characters get stuck in V-shaped terrain
Bug #4230: AiTravel package issues break some Tribunal quests Bug #4230: AiTravel package issues break some Tribunal quests
Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality
Bug #4251: Stationary NPCs do not return to their position after combat Bug #4251: Stationary NPCs do not return to their position after combat
Bug #4271: Scamp flickers when attacking
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
Bug #4286: Scripted animations can be interrupted Bug #4286: Scripted animations can be interrupted
Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4291: Non-persistent actors that started the game as dead do not play death animations
Bug #4293: Faction members are not aware of faction ownerships in barter Bug #4293: Faction members are not aware of faction ownerships in barter
Bug #4304: "Follow" not working as a second AI package
Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4307: World cleanup should remove dead bodies only if death animation is finished
Bug #4311: OpenMW does not handle RootCollisionNode correctly
Bug #4327: Missing animations during spell/weapon stance switching Bug #4327: Missing animations during spell/weapon stance switching
Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4358: Running animation is interrupted when magic mode is toggled
Bug #4368: Settings window ok button doesn't have key focus by default Bug #4368: Settings window ok button doesn't have key focus by default
Bug #4378: On-self absorb spells restore stats
Bug #4393: NPCs walk back to where they were after using ResetActors Bug #4393: NPCs walk back to where they were after using ResetActors
Bug #4416: Handle exception if we try to play non-music file Bug #4416: Handle exception if we try to play non-music file
Bug #4419: MRK NiStringExtraData is handled incorrectly Bug #4419: MRK NiStringExtraData is handled incorrectly
@ -59,6 +76,7 @@
Bug #4461: "Open" spell from non-player caster isn't a crime Bug #4461: "Open" spell from non-player caster isn't a crime
Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour
Bug #4474: No fallback when getVampireHead fails Bug #4474: No fallback when getVampireHead fails
Bug #4475: Scripted animations should not cause movement Bug #4475: Scripted animations should not cause movement
Bug #4479: "Game" category on Advanced page is getting too long Bug #4479: "Game" category on Advanced page is getting too long
@ -72,10 +90,39 @@
Bug #4503: Cast and ExplodeSpell commands increase alteration skill Bug #4503: Cast and ExplodeSpell commands increase alteration skill
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4519: Knockdown does not discard movement in the 1st-person mode
Bug #4531: Movement does not reset idle animations
Bug #4539: Paper Doll is affected by GUI scaling
Bug #4545: Creatures flee from werewolves
Bug #4551: Replace 0 sound range with default range separately
Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed
Bug #4557: Topics with reserved names are handled differently from vanilla
Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive
Bug #4560: OpenMW does not update pinned windows properly
Bug #4563: Fast travel price logic checks destination cell instead of service actor cell
Bug #4565: Underwater view distance should be limited
Bug #4573: Player uses headtracking in the 1st-person mode
Bug #4574: Player turning animations are twitchy
Bug #4575: Weird result of attack animation blending with movement animations
Bug #4576: Reset of idle animations when attack can not be started
Bug #4591: Attack strength should be 0 if player did not hold the attack button
Bug #4597: <> operator causes a compile error
Bug #4604: Picking up gold from the ground only makes 1 grabbed
Bug #4607: Scaling for animated collision shapes is applied twice
Bug #4608: Falling damage is applied twice
Bug #4614: Crash due to division by zero when FlipController has no textures
Bug #4615: Flicker effects for light sources are handled incorrectly
Bug #4617: First person sneaking offset is not applied while the character is in air
Bug #4618: Sneaking is possible while the character is flying
Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails
Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type
Feature #1645: Casting effects from objects
Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #2606: Editor: Implemented (optional) case sensitive global search
Feature #3083: Play animation when NPC is casting spell via script Feature #3083: Play animation when NPC is casting spell via script
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results Feature #3103: Provide option for disposition to get increased by successful trade
Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results
Feature #3641: Editor: Limit FPS in 3d preview window Feature #3641: Editor: Limit FPS in 3d preview window
Feature #3703: Ranged sneak attack criticals
Feature #4012: Editor: Write a log file if OpenCS crashes
Feature #4222: 360° screenshots Feature #4222: 360° screenshots
Feature #4256: Implement ToggleBorders (TB) console command Feature #4256: Implement ToggleBorders (TB) console command
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
@ -84,11 +131,23 @@
Feature #4444: Per-group KF-animation files support Feature #4444: Per-group KF-animation files support
Feature #4466: Editor: Add option to ignore "Base" records when running verifier Feature #4466: Editor: Add option to ignore "Base" records when running verifier
Feature #4488: Make water shader rougher during rain Feature #4488: Make water shader rougher during rain
Feature #4012: Editor: Write a log file if OpenCS crashes
Feature #4509: Show count of enchanted items in stack in the spells list Feature #4509: Show count of enchanted items in stack in the spells list
Feature #4512: Editor: Use markers for lights and creatures levelled lists Feature #4512: Editor: Use markers for lights and creatures levelled lists
Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill
Feature #4549: Weapon priority: use the actual damage in weapon rating calculations
Feature #4550: Weapon priority: make ranged weapon bonus more sensible
Feature #4579: Add option for applying Strength into hand to hand damage
Feature #4581: Use proper logging system
Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment
Feature #4625: Weapon priority: use weighted mean for melee damage rating
Feature #4626: Weapon priority: account for weapon speed
Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating
Task #2490: Don't open command prompt window on Release-mode builds automatically Task #2490: Don't open command prompt window on Release-mode builds automatically
Task #4545: Enable is_pod string test Task #4545: Enable is_pod string test
Task #4605: Optimize skinning
Task #4606: Support Rapture3D's OpenAL driver
Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9
Task #4621: Optimize combat AI
0.44.0 0.44.0
------ ------

View file

@ -1,11 +1,3 @@
#!/bin/sh #!/bin/sh
sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang
sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++ sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++
# build libgtest & libgtest_main
sudo mkdir /usr/src/gtest/build
cd /usr/src/gtest/build
sudo cmake .. -DBUILD_SHARED_LIBS=1
sudo make -j4
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so

View file

@ -1,8 +1,21 @@
#!/bin/sh #!/bin/sh -e
free -m free -m
env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh
GOOGLETEST_DIR="$(pwd)/googletest/build"
mkdir build mkdir build
cd build cd build
export CODE_COVERAGE=1 export CODE_COVERAGE=1
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE ${ANALYZE}cmake \
-DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \
-DBUILD_UNITTESTS=1 \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBINDIR=/usr/games \
-DCMAKE_BUILD_TYPE="None" \
-DUSE_SYSTEM_TINYXML=TRUE \
-DGTEST_ROOT="${GOOGLETEST_DIR}" \
-DGMOCK_ROOT="${GOOGLETEST_DIR}" \
..

13
CI/build_googletest.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh -e
git clone https://github.com/google/googletest.git
cd googletest
mkdir build
cd build
cmake \
-D CMAKE_BUILD_TYPE="${CONFIGURATION}" \
-D CMAKE_INSTALL_PREFIX=. \
-G "${GENERATOR}" \
..
cmake --build . --config "${CONFIGURATION}"
cmake --build . --target install --config "${CONFIGURATION}"

View file

@ -624,7 +624,7 @@ if (WIN32)
# Warnings that aren't enabled normally and don't need to be enabled # Warnings that aren't enabled normally and don't need to be enabled
# They're unneeded and sometimes completely retarded warnings that /Wall enables # They're unneeded and sometimes completely retarded warnings that /Wall enables
# Not going to bother commenting them as they tend to warn on every standard library file # Not going to bother commenting them as they tend to warn on every standard library file
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045 4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
# Warnings that are thrown on standard libraries and not OpenMW # Warnings that are thrown on standard libraries and not OpenMW
@ -643,6 +643,7 @@ if (WIN32)
# caused by boost # caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files)
# caused by MyGUI # caused by MyGUI
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception' 4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'

View file

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

View file

@ -1,6 +1,7 @@
set(LAUNCHER set(LAUNCHER
datafilespage.cpp datafilespage.cpp
graphicspage.cpp graphicspage.cpp
sdlinit.cpp
main.cpp main.cpp
maindialog.cpp maindialog.cpp
playpage.cpp playpage.cpp
@ -19,6 +20,7 @@ set(LAUNCHER
set(LAUNCHER_HEADER set(LAUNCHER_HEADER
datafilespage.hpp datafilespage.hpp
graphicspage.hpp graphicspage.hpp
sdlinit.hpp
maindialog.hpp maindialog.hpp
playpage.hpp playpage.hpp
textslotmsgbox.hpp textslotmsgbox.hpp

View file

@ -73,6 +73,12 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game");
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
// Input Settings // Input Settings
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
@ -125,6 +131,12 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();
if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game"))
mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
// Input Settings // Input Settings
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");

View file

@ -12,7 +12,6 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED #endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h>
#include <SDL_video.h> #include <SDL_video.h>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
@ -48,27 +47,15 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings:
} }
bool Launcher::GraphicsPage::connectToSdl() {
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
// Required for determining screen resolution and such on the Graphics tab
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
return false;
}
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
return true;
}
bool Launcher::GraphicsPage::setupSDL() bool Launcher::GraphicsPage::setupSDL()
{ {
bool sdlConnectSuccessful = connectToSdl(); #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
bool sdlConnectSuccessful = initSDL();
if (!sdlConnectSuccessful) if (!sdlConnectSuccessful)
{ {
return false; return false;
} }
#endif
int displays = SDL_GetNumVideoDisplays(); int displays = SDL_GetNumVideoDisplays();
@ -89,8 +76,10 @@ bool Launcher::GraphicsPage::setupSDL()
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
} }
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
// Disconnect from SDL processes // Disconnect from SDL processes
SDL_Quit(); quitSDL();
#endif
return true; return true;
} }

View file

@ -7,6 +7,8 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include "sdlinit.hpp"
namespace Files { struct ConfigurationManager; } namespace Files { struct ConfigurationManager; }
namespace Launcher namespace Launcher
@ -37,11 +39,6 @@ namespace Launcher
QStringList getAvailableResolutions(int screen); QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution(); QRect getMaximumResolution();
/**
* Connect to the SDL so that we can use it to determine graphics
* @return whether or not connecting to SDL is successful
*/
bool connectToSdl();
bool setupSDL(); bool setupSDL();
}; };
} }

View file

@ -12,11 +12,18 @@
#endif // MAC_OS_X_VERSION_MIN_REQUIRED #endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include "maindialog.hpp" #include "maindialog.hpp"
#include "sdlinit.hpp"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
try try
{ {
// Note: we should init SDL2 before Qt4 to avoid crashes on Linux,
// but we should init SDL2 after Qt5 to avoid input issues on MacOS X.
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
initSDL();
#endif
QApplication app(argc, argv); QApplication app(argc, argv);
// Now we make sure the current dir is set to application path // Now we make sure the current dir is set to application path
@ -33,11 +40,18 @@ int main(int argc, char *argv[])
if (result == Launcher::FirstRunDialogResultContinue) if (result == Launcher::FirstRunDialogResultContinue)
mainWin.show(); mainWin.show();
return app.exec(); int exitCode = app.exec();
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
// Disconnect from SDL processes
quitSDL();
#endif
return exitCode;
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << "ERROR: " << e.what() << std::endl; std::cerr << "ERROR: " << e.what() << std::endl;
return 0; return 0;
} }
} }

25
apps/launcher/sdlinit.cpp Normal file
View file

@ -0,0 +1,25 @@
#include <signal.h>
#include <SDL.h>
#include <SDL_video.h>
bool initSDL()
{
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
// Required for determining screen resolution and such on the Graphics tab
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
return false;
}
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
return true;
}
void quitSDL()
{
// Disconnect from SDL processes
SDL_Quit();
}

View file

@ -0,0 +1,8 @@
#ifndef SDLINIT_H
#define SDLINIT_H
bool initSDL();
void quitSDL();
#endif

View file

@ -5,11 +5,8 @@
#include <QLocalSocket> #include <QLocalSocket>
#include <QMessageBox> #include <QMessageBox>
#include <components/debug/debuglog.hpp>
#include <components/crashcatcher/crashcatcher.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
#include <components/nifosg/nifloader.hpp> #include <components/nifosg/nifloader.hpp>
#include "model/doc/document.hpp" #include "model/doc/document.hpp"
@ -27,10 +24,6 @@ CS::Editor::Editor (int argc, char **argv)
mLock(), mMerge (mDocumentManager), mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
{ {
// install the crash handler as soon as possible. note that the log path
// does not depend on config being read.
crashCatcherInstall(argc, argv, (mCfgMgr.getLogPath() / "openmw-cs-crash.log").string());
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig(); std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
setupDataFiles (config.first); setupDataFiles (config.first);
@ -301,7 +294,7 @@ bool CS::Editor::makeIPCServer()
mLock = boost::interprocess::file_lock(mPid.string().c_str()); mLock = boost::interprocess::file_lock(mPid.string().c_str());
if(!mLock.try_lock()) if(!mLock.try_lock())
{ {
std::cerr << "OpenCS already running." << std::endl; Log(Debug::Error) << "Error: OpenMW-CS is already running.";
return false; return false;
} }
@ -324,17 +317,17 @@ bool CS::Editor::makeIPCServer()
if(boost::filesystem::exists(fullPath.toUtf8().constData())) if(boost::filesystem::exists(fullPath.toUtf8().constData()))
{ {
// TODO: compare pid of the current process with that in the file // TODO: compare pid of the current process with that in the file
std::cout << "Detected unclean shutdown." << std::endl; Log(Debug::Info) << "Detected unclean shutdown.";
// delete the stale file // delete the stale file
if(remove(fullPath.toUtf8().constData())) if(remove(fullPath.toUtf8().constData()))
std::cerr << "ERROR removing stale connection file" << std::endl; Log(Debug::Error) << "Error: can not remove stale connection file.";
} }
} }
} }
catch(const std::exception& e) catch(const std::exception& e)
{ {
std::cerr << "ERROR " << e.what() << std::endl; Log(Debug::Error) << "Error: " << e.what();
return false; return false;
} }

View file

@ -1,15 +1,15 @@
#include "editor.hpp" #include "editor.hpp"
#include <exception> #include <exception>
#include <iostream>
#include <string> #include <string>
#include <QApplication> #include <QApplication>
#include <QIcon> #include <QIcon>
#include <QMetaType> #include <QMetaType>
#include "model/doc/messages.hpp" #include <components/debug/debugging.hpp>
#include "model/doc/messages.hpp"
#include "model/world/universalid.hpp" #include "model/world/universalid.hpp"
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
@ -30,7 +30,7 @@ class Application : public QApplication
} }
catch (const std::exception& exception) catch (const std::exception& exception)
{ {
std::cerr << "An exception has been caught: " << exception.what() << std::endl; Log(Debug::Error) << "An exception has been caught: " << exception.what();
} }
return false; return false;
@ -41,45 +41,43 @@ class Application : public QApplication
Application (int& argc, char *argv[]) : QApplication (argc, argv) {} Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
}; };
int main(int argc, char *argv[]) int runApplication(int argc, char *argv[])
{ {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0); setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif #endif
try // To allow background thread drawing in OSG
{ QApplication::setAttribute(Qt::AA_X11InitThreads, true);
// To allow background thread drawing in OSG
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
Q_INIT_RESOURCE (resources); Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string"); qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId"); qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message"); qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
Application application (argc, argv); Application application (argc, argv);
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
QDir::setCurrent(dir.absolutePath()); QDir::setCurrent(dir.absolutePath());
#endif #endif
application.setWindowIcon (QIcon (":./openmw-cs.png")); application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor(argc, argv); CS::Editor editor(argc, argv);
if(!editor.makeIPCServer()) if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
return editor.run();
}
catch (std::exception& e)
{ {
std::cerr << "ERROR: " << e.what() << std::endl; editor.connectToIPCServer();
return 0; return 0;
} }
return editor.run();
}
int main(int argc, char *argv[])
{
return wrapApplication(&runApplication, argc, argv, "OpenMW-CS");
} }

View file

@ -2,7 +2,6 @@
#include <cassert> #include <cassert>
#include <fstream> #include <fstream>
#include <iostream>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
@ -13,6 +12,8 @@
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#endif #endif
#include <components/debug/debuglog.hpp>
void CSMDoc::Document::addGmsts() void CSMDoc::Document::addGmsts()
{ {
for (size_t i=0; i < CSMWorld::DefaultGmsts::FloatCount; ++i) for (size_t i=0; i < CSMWorld::DefaultGmsts::FloatCount; ++i)
@ -435,7 +436,7 @@ void CSMDoc::Document::modificationStateChanged (bool clean)
void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type) void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)
{ {
/// \todo find a better way to get these messages to the user. /// \todo find a better way to get these messages to the user.
std::cout << message.mMessage << std::endl; Log(Debug::Info) << message.mMessage;
} }
void CSMDoc::Document::operationDone2 (int type, bool failed) void CSMDoc::Document::operationDone2 (int type, bool failed)

View file

@ -109,7 +109,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY &&
pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ)
{ {
std::vector<int>::const_iterator it = find(duplList.begin(), duplList.end(), i); std::vector<int>::const_iterator it = find(duplList.begin(), duplList.end(), static_cast<int>(i));
if (it == duplList.end()) if (it == duplList.end())
{ {
std::ostringstream ss; std::ostringstream ss;

View file

@ -1,8 +1,8 @@
#include "refcollection.hpp" #include "refcollection.hpp"
#include <sstream> #include <sstream>
#include <iostream>
#include <components/debug/debuglog.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp> #include <components/esm/loadcell.hpp>
@ -58,10 +58,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
// message // message
if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1])
{ {
std::cerr << "The Position of moved ref " Log(Debug::Warning) << "Warning: the Position of moved ref "
<< ref.mRefID << " does not match the target cell" << std::endl; << ref.mRefID << " does not match the target cell";
std::cerr << "Position: #" << index.first << " " << index.second Log(Debug::Warning) << "Position: #" << index.first << " " << index.second
<<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1];
stream.clear(); stream.clear();
stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1];

View file

@ -1,7 +1,6 @@
#include "object.hpp" #include "object.hpp"
#include <stdexcept> #include <stdexcept>
#include <iostream>
#include <osg/Depth> #include <osg/Depth>
#include <osg/Group> #include <osg/Group>
@ -24,6 +23,7 @@
#include "../../model/world/cellcoordinates.hpp" #include "../../model/world/cellcoordinates.hpp"
#include "../../model/prefs/state.hpp" #include "../../model/prefs/state.hpp"
#include <components/debug/debuglog.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightutil.hpp> #include <components/sceneutil/lightutil.hpp>
#include <components/sceneutil/lightmanager.hpp> #include <components/sceneutil/lightmanager.hpp>
@ -133,7 +133,7 @@ void CSVRender::Object::update()
catch (std::exception& e) catch (std::exception& e)
{ {
// TODO: use error marker mesh // TODO: use error marker mesh
std::cerr << e.what() << std::endl; Log(Debug::Error) << e.what();
} }
} }

View file

@ -1,11 +1,9 @@
#include "coloreditor.hpp" #include "coloreditor.hpp"
#include <QApplication> #include <QApplication>
#include <QColor>
#include <QColorDialog> #include <QColorDialog>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QPainter> #include <QPainter>
#include <QRect>
#include <QShowEvent> #include <QShowEvent>
#include "colorpickerpopup.hpp" #include "colorpickerpopup.hpp"
@ -27,9 +25,7 @@ CSVWidget::ColorEditor::ColorEditor(QWidget *parent, const bool popupOnStart)
mColorPicker(new ColorPickerPopup(this)), mColorPicker(new ColorPickerPopup(this)),
mPopupOnStart(popupOnStart) mPopupOnStart(popupOnStart)
{ {
setCheckable(true);
connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); connect(this, SIGNAL(clicked()), this, SLOT(showPicker()));
connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid()));
connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &)));
} }
@ -85,20 +81,7 @@ void CSVWidget::ColorEditor::setColor(const int colorInt)
void CSVWidget::ColorEditor::showPicker() void CSVWidget::ColorEditor::showPicker()
{ {
if (isChecked()) mColorPicker->showPicker(calculatePopupPosition(), mColor);
{
mColorPicker->showPicker(calculatePopupPosition(), mColor);
}
else
{
mColorPicker->hide();
}
}
void CSVWidget::ColorEditor::pickerHid()
{
setChecked(false);
emit pickingFinished();
} }
void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color)

View file

@ -45,7 +45,6 @@ namespace CSVWidget
private slots: private slots:
void showPicker(); void showPicker();
void pickerHid();
void pickerColorChanged(const QColor &color); void pickerColorChanged(const QColor &color);
signals: signals:

View file

@ -4,7 +4,6 @@
#include <QPushButton> #include <QPushButton>
#include <QEvent> #include <QEvent>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMouseEvent>
#include <QLayout> #include <QLayout>
#include <QStyleOption> #include <QStyleOption>
@ -19,7 +18,6 @@ CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent)
mColorPicker->setWindowFlags(Qt::Widget); mColorPicker->setWindowFlags(Qt::Widget);
mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog);
mColorPicker->installEventFilter(this); mColorPicker->installEventFilter(this);
mColorPicker->open();
connect(mColorPicker, connect(mColorPicker,
SIGNAL(currentColorChanged(const QColor &)), SIGNAL(currentColorChanged(const QColor &)),
this, this,
@ -39,8 +37,9 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo
geometry.moveTo(position); geometry.moveTo(position);
setGeometry(geometry); setGeometry(geometry);
mColorPicker->setCurrentColor(initialColor); // Calling getColor() creates a blocking dialog that will continue execution once the user chooses OK or Cancel
show(); QColor color = mColorPicker->getColor(initialColor);
mColorPicker->setCurrentColor(color);
} }
void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)
@ -63,12 +62,6 @@ void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)
QFrame::mousePressEvent(event); QFrame::mousePressEvent(event);
} }
void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event)
{
QFrame::hideEvent(event);
emit hid();
}
bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event)
{ {
if (object == mColorPicker && event->type() == QEvent::KeyPress) if (object == mColorPicker && event->type() == QEvent::KeyPress)

View file

@ -20,11 +20,9 @@ namespace CSVWidget
protected: protected:
virtual void mousePressEvent(QMouseEvent *event); virtual void mousePressEvent(QMouseEvent *event);
virtual void hideEvent(QHideEvent *event);
virtual bool eventFilter(QObject *object, QEvent *event); virtual bool eventFilter(QObject *object, QEvent *event);
signals: signals:
void hid();
void colorChanged(const QColor &color); void colorChanged(const QColor &color);
}; };
} }

View file

@ -10,6 +10,8 @@
#include <SDL.h> #include <SDL.h>
#include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -61,7 +63,7 @@ namespace
void checkSDLError(int ret) void checkSDLError(int ret)
{ {
if (ret != 0) if (ret != 0)
std::cerr << "SDL error: " << SDL_GetError() << std::endl; Log(Debug::Error) << "SDL error: " << SDL_GetError();
} }
} }
@ -189,7 +191,7 @@ bool OMW::Engine::frame(float frametime)
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
std::cerr << "Error in frame: " << e.what() << std::endl; Log(Debug::Error) << "Error in frame: " << e.what();
} }
return true; return true;
} }
@ -198,6 +200,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mWindow(NULL) : mWindow(NULL)
, mEncoding(ToUTF8::WINDOWS_1252) , mEncoding(ToUTF8::WINDOWS_1252)
, mEncoder(NULL) , mEncoder(NULL)
, mScreenCaptureOperation(nullptr)
, mSkipMenu (false) , mSkipMenu (false)
, mUseSound (true) , mUseSound (true)
, mCompileAll (false) , mCompileAll (false)
@ -363,7 +366,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
// Try with a lower AA // Try with a lower AA
if (antialiasing > 0) if (antialiasing > 0)
{ {
std::cout << "Note: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2 << std::endl; Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2;
antialiasing /= 2; antialiasing /= 2;
Settings::Manager::setInt("antialiasing", "Video", antialiasing); Settings::Manager::setInt("antialiasing", "Video", antialiasing);
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
@ -372,7 +375,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
else else
{ {
std::stringstream error; std::stringstream error;
error << "Failed to create SDL window: " << SDL_GetError() << std::endl; error << "Failed to create SDL window: " << SDL_GetError();
throw std::runtime_error(error.str()); throw std::runtime_error(error.str());
} }
} }
@ -419,16 +422,16 @@ void OMW::Engine::setWindowIcon()
std::string windowIcon = (mResDir / "mygui" / "openmw.png").string(); std::string windowIcon = (mResDir / "mygui" / "openmw.png").string();
windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary); windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary);
if (windowIconStream.fail()) if (windowIconStream.fail())
std::cerr << "Error: Failed to open " << windowIcon << std::endl; Log(Debug::Error) << "Error: Failed to open " << windowIcon;
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png"); osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png");
if (!reader) if (!reader)
{ {
std::cerr << "Error: Failed to read window icon, no png readerwriter found" << std::endl; Log(Debug::Error) << "Error: Failed to read window icon, no png readerwriter found";
return; return;
} }
osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream); osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream);
if (!result.success()) if (!result.success())
std::cerr << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status() << std::endl; Log(Debug::Error) << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status();
else else
{ {
osg::ref_ptr<osg::Image> image = result.getImage(); osg::ref_ptr<osg::Image> image = result.getImage();
@ -563,21 +566,19 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
{ {
std::pair<int, int> result = mEnvironment.getScriptManager()->compileAll(); std::pair<int, int> result = mEnvironment.getScriptManager()->compileAll();
if (result.first) if (result.first)
std::cout Log(Debug::Info)
<< "compiled " << result.second << " of " << result.first << " scripts (" << "compiled " << result.second << " of " << result.first << " scripts ("
<< 100*static_cast<double> (result.second)/result.first << 100*static_cast<double> (result.second)/result.first
<< "%)" << "%)";
<< std::endl;
} }
if (mCompileAllDialogue) if (mCompileAllDialogue)
{ {
std::pair<int, int> result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode); std::pair<int, int> result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode);
if (result.first) if (result.first)
std::cout Log(Debug::Info)
<< "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a(" << "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a("
<< 100*static_cast<double> (result.second)/result.first << 100*static_cast<double> (result.second)/result.first
<< "%)" << "%)";
<< std::endl;
} }
} }
@ -613,14 +614,14 @@ public:
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat);
if (!readerwriter) if (!readerwriter)
{ {
std::cerr << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found" << std::endl; Log(Debug::Error) << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found";
return; return;
} }
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream);
if (!result.success()) if (!result.success())
{ {
std::cerr << "Error: Can't write screenshot: " << result.message() << " code " << result.status() << std::endl; Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status();
} }
} }
@ -635,7 +636,7 @@ void OMW::Engine::go()
{ {
assert (!mContentFiles.empty()); assert (!mContentFiles.empty());
std::cout << "OSG version: " << osgGetVersion() << std::endl; Log(Debug::Info) << "OSG version: " << osgGetVersion();
// Load settings // Load settings
Settings::Manager settings; Settings::Manager settings;
@ -737,7 +738,7 @@ void OMW::Engine::go()
// Save user settings // Save user settings
settings.saveUser(settingspath); settings.saveUser(settingspath);
std::cout << "Quitting peacefully." << std::endl; Log(Debug::Info) << "Quitting peacefully.";
} }
void OMW::Engine::setCompileAll (bool all) void OMW::Engine::setCompileAll (bool all)

View file

@ -1,16 +1,11 @@
#include <iostream>
#include <components/version/version.hpp> #include <components/version/version.hpp>
#include <components/crashcatcher/crashcatcher.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/files/escape.hpp> #include <components/files/escape.hpp>
#include <components/fallback/validate.hpp> #include <components/fallback/validate.hpp>
#include <components/debug/debugging.hpp>
#include <SDL_messagebox.h>
#include "engine.hpp" #include "engine.hpp"
#include <boost/filesystem/fstream.hpp>
#if defined(_WIN32) #if defined(_WIN32)
// For OutputDebugString // For OutputDebugString
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
@ -205,8 +200,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
StringsVector content = variables["content"].as<Files::EscapeStringVector>().toStdStringVector(); StringsVector content = variables["content"].as<Files::EscapeStringVector>().toStdStringVector();
if (content.empty()) if (content.empty())
{ {
std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting...";
return false; return false;
} }
StringsVector::const_iterator it(content.begin()); StringsVector::const_iterator it(content.begin());
@ -220,7 +215,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setCell(variables["start"].as<Files::EscapeHashString>().toStdString()); engine.setCell(variables["start"].as<Files::EscapeHashString>().toStdString());
engine.setSkipMenu (variables["skip-menu"].as<bool>(), variables["new-game"].as<bool>()); engine.setSkipMenu (variables["skip-menu"].as<bool>(), variables["new-game"].as<bool>());
if (!variables["skip-menu"].as<bool>() && variables["new-game"].as<bool>()) if (!variables["skip-menu"].as<bool>() && variables["new-game"].as<bool>())
std::cerr << "Warning: new-game used without skip-menu -> ignoring it" << std::endl; Log(Debug::Warning) << "Warning: new-game used without skip-menu -> ignoring it";
// scripts // scripts
engine.setCompileAll(variables["script-all"].as<bool>()); engine.setCompileAll(variables["script-all"].as<bool>());
@ -241,124 +236,33 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
return true; return true;
} }
#if defined(_WIN32) && defined(_DEBUG) int runApplication(int argc, char *argv[])
class DebugOutput : public boost::iostreams::sink
{ {
public: #ifdef __APPLE__
std::streamsize write(const char *str, std::streamsize size) boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
{ boost::filesystem::current_path(binary_path.parent_path());
// Make a copy for null termination setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
std::string tmp (str, static_cast<unsigned int>(size));
// Write string to Visual Studio Debug output
OutputDebugString (tmp.c_str ());
return size;
}
};
#else
class Tee : public boost::iostreams::sink
{
public:
Tee(std::ostream &stream, std::ostream &stream2)
: out(stream), out2(stream2)
{
}
std::streamsize write(const char *str, std::streamsize size)
{
out.write (str, size);
out.flush();
out2.write (str, size);
out2.flush();
return size;
}
private:
std::ostream &out;
std::ostream &out2;
};
#endif #endif
Files::ConfigurationManager cfgMgr;
std::unique_ptr<OMW::Engine> engine;
engine.reset(new OMW::Engine(cfgMgr));
if (parseOptions(argc, argv, *engine, cfgMgr))
{
engine->go();
}
return 0;
}
#ifdef ANDROID #ifdef ANDROID
extern "C" int SDL_main(int argc, char**argv) extern "C" int SDL_main(int argc, char**argv)
#else #else
int main(int argc, char**argv) int main(int argc, char**argv)
#endif #endif
{ {
#if defined(__APPLE__) return wrapApplication(&runApplication, argc, argv, "OpenMW");
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
#endif
// Some objects used to redirect cout and cerr
// Scope must be here, so this still works inside the catch block for logging exceptions
std::streambuf* cout_rdbuf = std::cout.rdbuf ();
std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();
#if !(defined(_WIN32) && defined(_DEBUG))
boost::iostreams::stream_buffer<Tee> coutsb;
boost::iostreams::stream_buffer<Tee> cerrsb;
#endif
std::ostream oldcout(cout_rdbuf);
std::ostream oldcerr(cerr_rdbuf);
boost::filesystem::ofstream logfile;
std::unique_ptr<OMW::Engine> engine;
int ret = 0;
try
{
Files::ConfigurationManager cfgMgr;
#if defined(_WIN32) && defined(_DEBUG)
// Redirect cout and cerr to VS debug output when running in debug mode
boost::iostreams::stream_buffer<DebugOutput> sb;
sb.open(DebugOutput());
std::cout.rdbuf (&sb);
std::cerr.rdbuf (&sb);
#else
// Redirect cout and cerr to openmw.log
logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw.log"));
coutsb.open (Tee(logfile, oldcout));
cerrsb.open (Tee(logfile, oldcerr));
std::cout.rdbuf (&coutsb);
std::cerr.rdbuf (&cerrsb);
#endif
crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / "crash.log").string());
#ifdef __APPLE__
boost::filesystem::path binary_path = boost::filesystem::system_complete(boost::filesystem::path(argv[0]));
boost::filesystem::current_path(binary_path.parent_path());
#endif
engine.reset(new OMW::Engine(cfgMgr));
if (parseOptions(argc, argv, *engine, cfgMgr))
{
engine->go();
}
}
catch (std::exception &e)
{
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
if (!isatty(fileno(stdin)))
#endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
std::cerr << "\nERROR: " << e.what() << std::endl;
ret = 1;
}
// Restore cout and cerr
std::cout.rdbuf(cout_rdbuf);
std::cerr.rdbuf(cerr_rdbuf);
return ret;
} }
// Platform specific for Windows when there is no console built into the executable. // Platform specific for Windows when there is no console built into the executable.

View file

@ -75,8 +75,8 @@ namespace MWBase
virtual void persuade (int type, ResponseCallback* callback) = 0; virtual void persuade (int type, ResponseCallback* callback) = 0;
virtual int getTemporaryDispositionChange () const = 0; virtual int getTemporaryDispositionChange () const = 0;
/// @note This change is temporary and gets discarded when dialogue ends. /// @note Controlled by an option, gets discarded when dialogue ends by default
virtual void applyDispositionChange (int delta) = 0; virtual void applyBarterDispositionChange (int delta) = 0;
virtual int countSavedGameRecords() const = 0; virtual int countSavedGameRecords() const = 0;

View file

@ -257,7 +257,7 @@ namespace MWBase
virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0; virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0;
virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0; virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0;
virtual bool isAttackPrepairing(const MWWorld::Ptr& ptr) = 0; virtual bool isAttackPreparing(const MWWorld::Ptr& ptr) = 0;
virtual bool isRunning(const MWWorld::Ptr& ptr) = 0; virtual bool isRunning(const MWWorld::Ptr& ptr) = 0;
virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0; virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0;
}; };

View file

@ -412,6 +412,7 @@ namespace MWBase
/// @note throws an exception when invoked on a teleport door /// @note throws an exception when invoked on a teleport door
virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0;
virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) = 0; ///< get a list of actors standing on \a object
virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object
virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object
@ -490,8 +491,8 @@ namespace MWBase
virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0; virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0;
virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0; virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile, virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0; const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0;
virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0; virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0;
@ -536,7 +537,7 @@ namespace MWBase
/// Spawn a blood effect for \a ptr at \a worldPosition /// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0; virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) = 0; virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) = 0;
virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster,
const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id, const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id,
@ -566,6 +567,9 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0; virtual bool isPlayerInJail() const = 0;
virtual void setPlayerTraveling(bool traveling) = 0;
virtual bool isPlayerTraveling() const = 0;
virtual void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) = 0; virtual void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) = 0;
/// Return terrain height at \a worldPos position. /// Return terrain height at \a worldPos position.

View file

@ -142,14 +142,14 @@ namespace MWClass
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
float iWeight = floor(gmst.find(typeGmst)->getFloat()); float iWeight = floor(gmst.find(typeGmst)->mValue.getFloat());
float epsilon = 0.0005f; float epsilon = 0.0005f;
if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->getFloat() + epsilon) if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fLightMaxMod")->mValue.getFloat() + epsilon)
return ESM::Skill::LightArmor; return ESM::Skill::LightArmor;
if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->getFloat() + epsilon) if (ref->mBase->mData.mWeight <= iWeight * gmst.find ("fMedMaxMod")->mValue.getFloat() + epsilon)
return ESM::Skill::MediumArmor; return ESM::Skill::MediumArmor;
else else
@ -285,7 +285,7 @@ namespace MWClass
int armorSkill = actor.getClass().getSkill(actor, armorSkillType); int armorSkill = actor.getClass().getSkill(actor, armorSkillType);
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->getInt(); int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->mValue.getInteger();
if(ref->mBase->mData.mWeight == 0) if(ref->mBase->mData.mWeight == 0)
return ref->mBase->mData.mArmor; return ref->mBase->mData.mArmor;

View file

@ -1,7 +1,7 @@
#include "creature.hpp" #include "creature.hpp"
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/debug/debuglog.hpp>
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -146,7 +146,7 @@ namespace MWClass
if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(*iter)) if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(*iter))
data->mCreatureStats.getSpells().add (spell); data->mCreatureStats.getSpells().add (spell);
else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility
std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'" << std::endl; Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'";
} }
// inventory // inventory
@ -247,13 +247,13 @@ namespace MWClass
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
float dist = gmst.find("fCombatDistance")->getFloat(); float dist = gmst.find("fCombatDistance")->mValue.getFloat();
if (!weapon.isEmpty()) if (!weapon.isEmpty())
dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach; dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
std::vector<MWWorld::Ptr> targetActors; std::vector<MWWorld::Ptr> targetActors;
if (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer()) if (!ptr.isEmpty() && ptr.getClass().isActor())
ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors);
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors); std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);
@ -325,9 +325,6 @@ namespace MWClass
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength)) if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage, attackStrength))
damage = 0; damage = 0;
if (victim == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
damage = 0;
MWMechanics::diseaseContact(victim, ptr); MWMechanics::diseaseContact(victim, ptr);
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true); victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
@ -376,11 +373,6 @@ namespace MWClass
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
} }
bool godmode = object == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
if (godmode)
damage = 0;
if (!successful) if (!successful)
{ {
// Missed // Missed
@ -402,9 +394,9 @@ namespace MWClass
if (!attacker.isEmpty()) if (!attacker.isEmpty())
{ {
// Check for knockdown // Check for knockdown
float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->mValue.getFloat();
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
* getGmst().iKnockDownOddsMult->getInt() * 0.01f + getGmst().iKnockDownOddsBase->getInt(); * getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f + getGmst().iKnockDownOddsBase->mValue.getInteger();
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99())
stats.setKnockedDown(true); stats.setKnockedDown(true);
else else
@ -415,7 +407,7 @@ namespace MWClass
if(ishealth) if(ishealth)
{ {
if (!attacker.isEmpty() && !godmode) if (!attacker.isEmpty())
{ {
damage = scaleDamage(damage, attacker, ptr); damage = scaleDamage(damage, attacker, ptr);
MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition); MWBase::Environment::get().getWorld()->spawnBloodEffect(ptr, hitPosition);
@ -524,8 +516,8 @@ namespace MWClass
const GMST& gmst = getGmst(); const GMST& gmst = getGmst();
float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() float walkSpeed = gmst.fMinWalkSpeedCreature->mValue.getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat()); * (gmst.fMaxWalkSpeedCreature->mValue.getFloat() - gmst.fMinWalkSpeedCreature->mValue.getFloat());
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
@ -544,9 +536,9 @@ namespace MWClass
{ {
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); mageffects.get(ESM::MagicEffect::Levitate).getMagnitude());
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat());
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed); flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
@ -556,8 +548,8 @@ namespace MWClass
if(running) if(running)
swimSpeed = runSpeed; swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
gmst.fSwimRunAthleticsMult->getFloat(); gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed; moveSpeed = swimSpeed;
} }
else if(running) else if(running)
@ -822,8 +814,8 @@ namespace MWClass
return; return;
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat();
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat();
float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay); float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay);

View file

@ -56,8 +56,8 @@ namespace MWClass
else if (creatureStats.isDead()) else if (creatureStats.isDead())
{ {
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat();
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat();
float delay = std::min(fCorpseRespawnDelay, fCorpseClearDelay); float delay = std::min(fCorpseRespawnDelay, fCorpseClearDelay);
if (creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp()) if (creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())

View file

@ -138,7 +138,7 @@ namespace MWClass
int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase();
static const float fWortChanceValue = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();
MWGui::Widgets::SpellEffectList list; MWGui::Widgets::SpellEffectList list;
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)

View file

@ -4,6 +4,7 @@
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/debug/debuglog.hpp>
#include <components/esm/loadmgef.hpp> #include <components/esm/loadmgef.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/npcstate.hpp> #include <components/esm/npcstate.hpp>
@ -365,15 +366,15 @@ namespace MWClass
if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(*iter)) if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(*iter))
data->mNpcStats.getSpells().add (spell); data->mNpcStats.getSpells().add (spell);
else else
std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; Log(Debug::Warning) << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'";
} }
if (!ref->mBase->mFaction.empty()) if (!ref->mBase->mFaction.empty())
{ {
static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepFacMod")->getInt(); .find("iAutoRepFacMod")->mValue.getInteger();
static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepLevMod")->getInt(); .find("iAutoRepLevMod")->mValue.getInteger();
int rank = ref->mBase->getFactionRank(); int rank = ref->mBase->getFactionRank();
data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));
@ -395,7 +396,7 @@ namespace MWClass
else else
{ {
/// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility
std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'";
} }
} }
@ -527,7 +528,7 @@ namespace MWClass
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
return store.find("sWerewolfPopup")->getString(); return store.find("sWerewolfPopup")->mValue.getString();
} }
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
@ -564,10 +565,10 @@ namespace MWClass
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength); MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
const float fCombatDistance = store.find("fCombatDistance")->getFloat(); const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
float dist = fCombatDistance * (!weapon.isEmpty() ? float dist = fCombatDistance * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach : weapon.get<ESM::Weapon>()->mBase->mData.mReach :
store.find("fHandToHandReach")->getFloat()); store.find("fHandToHandReach")->mValue.getFloat());
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
std::vector<MWWorld::Ptr> targetActors; std::vector<MWWorld::Ptr> targetActors;
@ -637,14 +638,14 @@ namespace MWClass
&& !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
if(unaware) if(unaware)
{ {
damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); damage *= store.find("fCombatCriticalStrikeMult")->mValue.getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
} }
} }
if (othercls.getCreatureStats(victim).getKnockedDown()) if (othercls.getCreatureStats(victim).getKnockedDown())
damage *= store.find("fCombatKODamageMult")->getFloat(); damage *= store.find("fCombatKODamageMult")->mValue.getFloat();
// Apply "On hit" enchanted weapons // Apply "On hit" enchanted weapons
MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition); MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);
@ -736,14 +737,14 @@ namespace MWClass
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const GMST& gmst = getGmst(); const GMST& gmst = getGmst();
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt(); int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->mValue.getInteger();
if (Misc::Rng::roll0to99() < chance) if (Misc::Rng::roll0to99() < chance)
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
// Check for knockdown // Check for knockdown
float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->mValue.getFloat();
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
* gmst.iKnockDownOddsMult->getInt() * 0.01f + gmst.iKnockDownOddsBase->getInt(); * gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f + gmst.iKnockDownOddsBase->mValue.getInteger();
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99()) if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99())
stats.setKnockedDown(true); stats.setKnockedDown(true);
else else
@ -771,7 +772,7 @@ namespace MWClass
float unmitigatedDamage = damage; float unmitigatedDamage = damage;
float x = damage / (damage + getArmorRating(ptr)); float x = damage / (damage + getArmorRating(ptr));
damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x); damage *= std::max(gmst.fCombatArmorMinMult->mValue.getFloat(), x);
int damageDiff = static_cast<int>(unmitigatedDamage - damage); int damageDiff = static_cast<int>(unmitigatedDamage - damage);
damage = std::max(1.f, damage); damage = std::max(1.f, damage);
damageDiff = std::max(1, damageDiff); damageDiff = std::max(1, damageDiff);
@ -940,15 +941,15 @@ namespace MWClass
bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run);
float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat()); (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat());
walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed); walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking) if(sneaking)
walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat(); walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat();
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat());
float moveSpeed; float moveSpeed;
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
@ -958,8 +959,8 @@ namespace MWClass
{ {
float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).getMagnitude()); mageffects.get(ESM::MagicEffect::Levitate).getMagnitude());
flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); flySpeed = gmst.fMinFlySpeed->mValue.getFloat() + flySpeed*(gmst.fMaxFlySpeed->mValue.getFloat() - gmst.fMinFlySpeed->mValue.getFloat());
flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->mValue.getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed); flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
@ -969,8 +970,8 @@ namespace MWClass
if(running) if(running)
swimSpeed = runSpeed; swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude();
swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
gmst.fSwimRunAthleticsMult->getFloat(); gmst.fSwimRunAthleticsMult->mValue.getFloat();
moveSpeed = swimSpeed; moveSpeed = swimSpeed;
} }
else if(running && !sneaking) else if(running && !sneaking)
@ -981,7 +982,7 @@ namespace MWClass
moveSpeed *= 0.75f; moveSpeed *= 0.75f;
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing) if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
moveSpeed *= gmst.fWereWolfRunMult->getFloat(); moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat();
return moveSpeed; return moveSpeed;
} }
@ -991,11 +992,15 @@ namespace MWClass
if(getEncumbrance(ptr) > getCapacity(ptr)) if(getEncumbrance(ptr) > getCapacity(ptr))
return 0.f; return 0.f;
const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
return 0.f;
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const GMST& gmst = getGmst(); const GMST& gmst = getGmst();
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() + const float encumbranceTerm = gmst.fJumpEncumbranceBase->mValue.getFloat() +
gmst.fJumpEncumbranceMultiplier->getFloat() * gmst.fJumpEncumbranceMultiplier->mValue.getFloat() *
(1.0f - Npc::getNormalizedEncumbrance(ptr)); (1.0f - Npc::getNormalizedEncumbrance(ptr));
float a = static_cast<float>(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified()); float a = static_cast<float>(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified());
@ -1006,14 +1011,14 @@ namespace MWClass
a = 50.0f; a = 50.0f;
} }
float x = gmst.fJumpAcrobaticsBase->getFloat() + float x = gmst.fJumpAcrobaticsBase->mValue.getFloat() +
std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat()); std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->mValue.getFloat());
x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat(); x += 3.0f * b * gmst.fJumpAcroMultiplier->mValue.getFloat();
x += mageffects.get(ESM::MagicEffect::Jump).getMagnitude() * 64; x += mageffects.get(ESM::MagicEffect::Jump).getMagnitude() * 64;
x *= encumbranceTerm; x *= encumbranceTerm;
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) if(stats.getStance(MWMechanics::CreatureStats::Stance_Run))
x *= gmst.fJumpRunMultiplier->getFloat(); x *= gmst.fJumpRunMultiplier->mValue.getFloat();
x *= npcdata->mNpcStats.getFatigueTerm(); x *= npcdata->mNpcStats.getFatigueTerm();
x -= -627.2f;/*gravity constant*/ x -= -627.2f;/*gravity constant*/
x /= 3.0f; x /= 3.0f;
@ -1078,7 +1083,7 @@ namespace MWClass
float Npc::getCapacity (const MWWorld::Ptr& ptr) const float Npc::getCapacity (const MWWorld::Ptr& ptr) const
{ {
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEncumbranceStrMult")->getFloat(); static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEncumbranceStrMult")->mValue.getFloat();
return stats.getAttribute(ESM::Attribute::Strength).getModified()*fEncumbranceStrMult; return stats.getAttribute(ESM::Attribute::Strength).getModified()*fEncumbranceStrMult;
} }
@ -1121,8 +1126,8 @@ namespace MWClass
MWMechanics::NpcStats &stats = getNpcStats(ptr); MWMechanics::NpcStats &stats = getNpcStats(ptr);
const MWWorld::InventoryStore &invStore = getInventoryStore(ptr); const MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
float ratings[MWWorld::InventoryStore::Slots]; float ratings[MWWorld::InventoryStore::Slots];
@ -1240,11 +1245,10 @@ namespace MWClass
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return "DefaultLandWater"; return "DefaultLandWater";
if(world->isOnGround(ptr))
return "DefaultLand"; return "DefaultLand";
return "";
} }
if(name == "swimleft") if(name == "swimleft")
return "Swim Left"; return "Swim Left";
@ -1363,8 +1367,8 @@ namespace MWClass
return; return;
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat(); static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->mValue.getFloat();
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat(); static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->mValue.getFloat();
float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay); float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay);

View file

@ -268,27 +268,30 @@ namespace MWClass
{ {
text += "\n#{sType} "; text += "\n#{sType} ";
std::map <int, std::pair <std::string, std::string> > mapping; static std::map <int, std::pair <std::string, std::string> > mapping;
mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); if (mapping.empty())
mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); {
mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded");
mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded");
mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded");
mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded");
mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded");
mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded");
mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded");
mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded");
mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", ""); mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", ""); mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", "");
mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", "");
}
std::string type = mapping[ref->mBase->mData.mType].first; const std::string type = mapping[ref->mBase->mData.mType].first;
std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; const std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second;
text += store.get<ESM::GameSetting>().find(type)->getString() + text += store.get<ESM::GameSetting>().find(type)->mValue.getString() +
((oneOrTwoHanded != "") ? ", " + store.get<ESM::GameSetting>().find(oneOrTwoHanded)->getString() : ""); ((oneOrTwoHanded != "") ? ", " + store.get<ESM::GameSetting>().find(oneOrTwoHanded)->mValue.getString() : "");
// weapon damage // weapon damage
if (ref->mBase->mData.mType >= 9) if (ref->mBase->mData.mType >= 9)
@ -326,7 +329,7 @@ namespace MWClass
if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game")) if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game"))
{ {
// 64 game units = 1 yard = 3 ft, display value in feet // 64 game units = 1 yard = 3 ft, display value in feet
const float combatDistance = store.get<ESM::GameSetting>().find("fCombatDistance")->getFloat() * ref->mBase->mData.mReach; const float combatDistance = store.get<ESM::GameSetting>().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach;
text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}"); text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}");
text += " #{sFeet}"; text += " #{sFeet}";

View file

@ -5,7 +5,8 @@
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <list> #include <list>
#include <iostream>
#include <components/debug/debuglog.hpp>
#include <components/esm/loaddial.hpp> #include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp> #include <components/esm/loadinfo.hpp>
@ -22,6 +23,8 @@
#include <components/interpreter/interpreter.hpp> #include <components/interpreter/interpreter.hpp>
#include <components/interpreter/defines.hpp> #include <components/interpreter/defines.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp" #include "../mwbase/journal.hpp"
@ -201,16 +204,14 @@ namespace MWDialogue
} }
catch (const std::exception& error) catch (const std::exception& error)
{ {
std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what();
success = false; success = false;
} }
if (!success) if (!success)
{ {
std::cerr Log(Debug::Warning)
<< "Warning: compiling failed (dialogue script)" << std::endl << "Warning: compiling failed (dialogue script)\n" << cmd << "\n\n";
<< cmd
<< std::endl << std::endl;
} }
return success; return success;
@ -230,7 +231,7 @@ namespace MWDialogue
} }
catch (const std::exception& error) catch (const std::exception& error)
{ {
std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what();
} }
} }
} }
@ -260,7 +261,7 @@ namespace MWDialogue
const MWWorld::Store<ESM::GameSetting>& gmsts = const MWWorld::Store<ESM::GameSetting>& gmsts =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
title = gmsts.find (modifiedTopic)->getString(); title = gmsts.find (modifiedTopic)->mValue.getString();
} }
else else
title = topic; title = topic;
@ -508,9 +509,11 @@ namespace MWDialogue
return static_cast<int>(mTemporaryDispositionChange); return static_cast<int>(mTemporaryDispositionChange);
} }
void DialogueManager::applyDispositionChange(int delta) void DialogueManager::applyBarterDispositionChange(int delta)
{ {
mTemporaryDispositionChange += delta; mTemporaryDispositionChange += delta;
if (Settings::Manager::getBool("barter disposition change is permanent", "Game"))
mPermanentDispositionChange += delta;
} }
bool DialogueManager::checkServiceRefused(ResponseCallback* callback) bool DialogueManager::checkServiceRefused(ResponseCallback* callback)
@ -534,7 +537,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
callback->addResponse(gmsts.find ("sServiceRefusal")->getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); callback->addResponse(gmsts.find ("sServiceRefusal")->mValue.getString(), Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript, mActor); executeScript (info->mResultScript, mActor);
return true; return true;

View file

@ -94,8 +94,8 @@ namespace MWDialogue
virtual void persuade (int type, ResponseCallback* callback); virtual void persuade (int type, ResponseCallback* callback);
virtual int getTemporaryDispositionChange () const; virtual int getTemporaryDispositionChange () const;
/// @note This change is temporary and gets discarded when dialogue ends. /// @note Controlled by an option, gets discarded when dialogue ends by default
virtual void applyDispositionChange (int delta); virtual void applyBarterDispositionChange (int delta);
virtual int countSavedGameRecords() const; virtual int countSavedGameRecords() const;

View file

@ -1,7 +1,5 @@
#include "scripttest.hpp" #include "scripttest.hpp"
#include <iostream>
#include "../mwworld/manualref.hpp" #include "../mwworld/manualref.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -12,6 +10,7 @@
#include "../mwscript/compilercontext.hpp" #include "../mwscript/compilercontext.hpp"
#include <components/debug/debuglog.hpp>
#include <components/compiler/exception.hpp> #include <components/compiler/exception.hpp>
#include <components/compiler/streamerrorhandler.hpp> #include <components/compiler/streamerrorhandler.hpp>
#include <components/compiler/scanner.hpp> #include <components/compiler/scanner.hpp>
@ -80,16 +79,14 @@ void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::
} }
catch (const std::exception& error) catch (const std::exception& error)
{ {
std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what();
success = false; success = false;
} }
if (!success) if (!success)
{ {
std::cerr Log(Debug::Warning)
<< "compiling failed (dialogue script)" << std::endl << "compiling failed (dialogue script)\n" << info->mResultScript << "\n\n";
<< info->mResultScript
<< std::endl << std::endl;
} }
} }
} }

View file

@ -1,5 +1,6 @@
#include "charactercreation.hpp" #include "charactercreation.hpp"
#include <components/debug/debuglog.hpp>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -284,7 +285,7 @@ namespace MWGui
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << "Error: Failed to create chargen window: " << e.what() << std::endl; Log(Debug::Error) << "Error: Failed to create chargen window: " << e.what();
} }
} }
@ -602,7 +603,7 @@ namespace MWGui
mGenerateClass = "Mage"; mGenerateClass = "Mage";
else else
{ {
std::cout << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; Log(Debug::Warning) << "Failed to deduce class from chosen answers in generate class dialog.";
mGenerateClass = "Thief"; mGenerateClass = "Thief";
} }
} }

View file

@ -12,6 +12,8 @@
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include <components/debug/debuglog.hpp>
#include "tooltips.hpp" #include "tooltips.hpp"
namespace namespace
@ -924,7 +926,7 @@ namespace MWGui
std::string classImage = std::string("textures\\levelup\\") + classId + ".dds"; std::string classImage = std::string("textures\\levelup\\") + classId + ".dds";
if (!MWBase::Environment::get().getWindowManager()->textureExists(classImage)) if (!MWBase::Environment::get().getWindowManager()->textureExists(classImage))
{ {
std::cout << "No class image for " << classId << ", falling back to default" << std::endl; Log(Debug::Warning) << "No class image for " << classId << ", falling back to default";
classImage = "textures\\levelup\\warrior.dds"; classImage = "textures\\levelup\\warrior.dds";
} }
imageBox->setImageTexture(classImage); imageBox->setImageTexture(classImage);

View file

@ -303,6 +303,14 @@ namespace MWGui
bool has_front_quote = false; bool has_front_quote = false;
/* Does the input string contain things that don't have to be completed? If yes erase them. */ /* Does the input string contain things that don't have to be completed? If yes erase them. */
/* Erase a possible call to an explicit reference. */
size_t explicitPos = tmp.find("->");
if (explicitPos != std::string::npos)
{
tmp.erase(0, explicitPos+2);
}
/* Are there quotation marks? */ /* Are there quotation marks? */
if( tmp.find('"') != std::string::npos ) { if( tmp.find('"') != std::string::npos ) {
int numquotes=0; int numquotes=0;
@ -339,6 +347,7 @@ namespace MWGui
} }
} }
} }
/* Erase the input from the output string so we can easily append the completed form later. */ /* Erase the input from the output string so we can easily append the completed form later. */
output.erase(output.end()-tmp.length(), output.end()); output.erase(output.end()-tmp.length(), output.end());

View file

@ -52,7 +52,7 @@ namespace MWGui
void ContainerWindow::onItemSelected(int index) void ContainerWindow::onItemSelected(int index)
{ {
if (mDragAndDrop->mIsOnDragAndDrop && mModel) if (mDragAndDrop->mIsOnDragAndDrop)
{ {
dropItem(); dropItem();
return; return;
@ -88,6 +88,9 @@ namespace MWGui
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
{ {
if (!mModel)
return;
if (!onTakeItem(mModel->getItem(mSelectedItem), count)) if (!onTakeItem(mModel->getItem(mSelectedItem), count))
return; return;
@ -96,6 +99,9 @@ namespace MWGui
void ContainerWindow::dropItem() void ContainerWindow::dropItem()
{ {
if (!mModel)
return;
bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount); bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);
if (success) if (success)
@ -104,7 +110,7 @@ namespace MWGui
void ContainerWindow::onBackgroundSelected() void ContainerWindow::onBackgroundSelected()
{ {
if (mDragAndDrop->mIsOnDragAndDrop && mModel) if (mDragAndDrop->mIsOnDragAndDrop)
dropItem(); dropItem();
} }

View file

@ -6,6 +6,7 @@
#include <MyGUI_ScrollBar.h> #include <MyGUI_ScrollBar.h>
#include <MyGUI_Button.h> #include <MyGUI_Button.h>
#include <components/debug/debuglog.hpp>
#include <components/widgets/list.hpp> #include <components/widgets/list.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
@ -362,52 +363,59 @@ namespace MWGui
if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice()) if (mGoodbye || MWBase::Environment::get().getDialogueManager()->isInChoice())
return; return;
int separatorPos = 0; const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
{
if (mTopicsList->getItemNameAt(i) == "")
separatorPos = i;
}
if (id >= separatorPos) const std::string sPersuasion = gmst.find("sPersuasion")->mValue.getString();
const std::string sCompanionShare = gmst.find("sCompanionShare")->mValue.getString();
const std::string sBarter = gmst.find("sBarter")->mValue.getString();
const std::string sSpells = gmst.find("sSpells")->mValue.getString();
const std::string sTravel = gmst.find("sTravel")->mValue.getString();
const std::string sSpellMakingMenuTitle = gmst.find("sSpellMakingMenuTitle")->mValue.getString();
const std::string sEnchanting = gmst.find("sEnchanting")->mValue.getString();
const std::string sServiceTrainingTitle = gmst.find("sServiceTrainingTitle")->mValue.getString();
const std::string sRepair = gmst.find("sRepair")->mValue.getString();
if (topic != sPersuasion && topic != sCompanionShare && topic != sBarter
&& topic != sSpells && topic != sTravel && topic != sSpellMakingMenuTitle
&& topic != sEnchanting && topic != sServiceTrainingTitle && topic != sRepair)
{ {
onTopicActivated(topic); onTopicActivated(topic);
if (mGoodbyeButton->getEnabled()) if (mGoodbyeButton->getEnabled())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mGoodbyeButton);
} }
else else if (topic == sPersuasion)
mPersuasionDialog.setVisible(true);
else if (topic == sCompanionShare)
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr);
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(mCallback.get()))
{ {
const MWWorld::Store<ESM::GameSetting> &gmst = if (topic == sBarter)
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr);
else if (topic == sSpells)
if (topic == gmst.find("sPersuasion")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr);
mPersuasionDialog.setVisible(true); else if (topic == sTravel)
else if (topic == gmst.find("sCompanionShare")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion, mPtr); else if (topic == sSpellMakingMenuTitle)
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused(mCallback.get())) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);
{ else if (topic == sEnchanting)
if (topic == gmst.find("sBarter")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter, mPtr); else if (topic == sServiceTrainingTitle)
else if (topic == gmst.find("sSpells")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying, mPtr); else if (topic == sRepair)
else if (topic == gmst.find("sTravel")->getString()) MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel, mPtr);
else if (topic == gmst.find("sSpellMakingMenuTitle")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation, mPtr);
else if (topic == gmst.find("sEnchanting")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting, mPtr);
else if (topic == gmst.find("sServiceTrainingTitle")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training, mPtr);
else if (topic == gmst.find("sRepair")->getString())
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair, mPtr);
}
else
updateTopics();
} }
else
updateTopics();
} }
void DialogueWindow::setPtr(const MWWorld::Ptr& actor) void DialogueWindow::setPtr(const MWWorld::Ptr& actor)
{ {
if (!actor.getClass().isActor())
{
Log(Debug::Warning) << "Warning: can not talk with non-actor object.";
return;
}
bool sameActor = (mPtr == actor); bool sameActor = (mPtr == actor);
if (!sameActor) if (!sameActor)
{ {
@ -450,7 +458,7 @@ namespace MWGui
void DialogueWindow::restock() void DialogueWindow::restock()
{ {
MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr);
float delay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fBarterGoldResetDelay")->getFloat(); float delay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fBarterGoldResetDelay")->mValue.getFloat();
// Gold is restocked every 24h // Gold is restocked every 24h
if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay)
@ -495,31 +503,31 @@ namespace MWGui
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (mPtr.getTypeName() == typeid(ESM::NPC).name()) if (mPtr.getTypeName() == typeid(ESM::NPC).name())
mTopicsList->addItem(gmst.find("sPersuasion")->getString()); mTopicsList->addItem(gmst.find("sPersuasion")->mValue.getString());
if (services & ESM::NPC::AllItems) if (services & ESM::NPC::AllItems)
mTopicsList->addItem(gmst.find("sBarter")->getString()); mTopicsList->addItem(gmst.find("sBarter")->mValue.getString());
if (services & ESM::NPC::Spells) if (services & ESM::NPC::Spells)
mTopicsList->addItem(gmst.find("sSpells")->getString()); mTopicsList->addItem(gmst.find("sSpells")->mValue.getString());
if (travel) if (travel)
mTopicsList->addItem(gmst.find("sTravel")->getString()); mTopicsList->addItem(gmst.find("sTravel")->mValue.getString());
if (services & ESM::NPC::Spellmaking) if (services & ESM::NPC::Spellmaking)
mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString()); mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->mValue.getString());
if (services & ESM::NPC::Enchanting) if (services & ESM::NPC::Enchanting)
mTopicsList->addItem(gmst.find("sEnchanting")->getString()); mTopicsList->addItem(gmst.find("sEnchanting")->mValue.getString());
if (services & ESM::NPC::Training) if (services & ESM::NPC::Training)
mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString()); mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->mValue.getString());
if (services & ESM::NPC::Repair) if (services & ESM::NPC::Repair)
mTopicsList->addItem(gmst.find("sRepair")->getString()); mTopicsList->addItem(gmst.find("sRepair")->mValue.getString());
if (isCompanion()) if (isCompanion())
mTopicsList->addItem(gmst.find("sCompanionShare")->getString()); mTopicsList->addItem(gmst.find("sCompanionShare")->mValue.getString());
if (mTopicsList->getItemCount() > 0) if (mTopicsList->getItemCount() > 0)
mTopicsList->addSeparator(); mTopicsList->addSeparator();
@ -584,7 +592,7 @@ namespace MWGui
Goodbye* link = new Goodbye(); Goodbye* link = new Goodbye();
link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated); link->eventActivated += MyGUI::newDelegate(this, &DialogueWindow::onGoodbyeActivated);
mLinks.push_back(link); mLinks.push_back(link);
std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString(); std::string goodbye = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->mValue.getString();
BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver, BookTypesetter::Style* questionStyle = typesetter->createHotStyle(body, textColours.answer, textColours.answerOver,
textColours.answerPressed, textColours.answerPressed,
TypesetBook::InteractiveId(link)); TypesetBook::InteractiveId(link));

View file

@ -341,7 +341,7 @@ namespace MWGui
MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem();
if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), mPtr)) if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(), mPtr))
{ {
std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->getString(); std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->mValue.getString();
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
MWBase::Environment::get().getWindowManager()->messageBox(msg); MWBase::Environment::get().getWindowManager()->messageBox(msg);

View file

@ -13,6 +13,7 @@
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <components/debug/debuglog.hpp>
#include <components/interpreter/defines.hpp> #include <components/interpreter/defines.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -300,7 +301,7 @@ namespace MWGui
if (!exists) if (!exists)
{ {
std::cerr << "Warning: Could not find \"" << src << "\" referenced by an <img> tag." << std::endl; Log(Debug::Warning) << "Warning: Could not find \"" << src << "\" referenced by an <img> tag.";
break; break;
} }

View file

@ -611,7 +611,7 @@ namespace MWGui
// Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :)
mEnemyHealth->setProgressPosition(static_cast<size_t>(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100)); mEnemyHealth->setProgressPosition(static_cast<size_t>(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100));
static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarFade")->getFloat(); static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarFade")->mValue.getFloat();
if (fNPCHealthBarFade > 0.f) if (fNPCHealthBarFade > 0.f)
mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade))); mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade)));
@ -620,7 +620,7 @@ namespace MWGui
void HUD::setEnemy(const MWWorld::Ptr &enemy) void HUD::setEnemy(const MWWorld::Ptr &enemy)
{ {
mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId();
mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarTime")->getFloat(); mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarTime")->mValue.getFloat();
if (!mEnemyHealth->getVisible()) if (!mEnemyHealth->getVisible())
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
mEnemyHealth->setVisible(true); mEnemyHealth->setVisible(true);

View file

@ -67,7 +67,13 @@ namespace MWGui
, mLastYSize(0) , mLastYSize(0)
, mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer())) , mPreview(new MWRender::InventoryPreview(parent, resourceSystem, MWMechanics::getPlayer()))
, mTrading(false) , mTrading(false)
, mScaleFactor(1.0f)
, mUpdateTimer(0.f)
{ {
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
if (uiScale > 1.0)
mScaleFactor = uiScale;
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
mPreview->rebuild(); mPreview->rebuild();
@ -431,10 +437,10 @@ namespace MWGui
MyGUI::IntSize size = mAvatarImage->getSize(); MyGUI::IntSize size = mAvatarImage->getSize();
int width = std::min(mPreview->getTextureWidth(), size.width); int width = std::min(mPreview->getTextureWidth(), size.width);
int height = std::min(mPreview->getTextureHeight(), size.height); int height = std::min(mPreview->getTextureHeight(), size.height);
mPreview->setViewport(width, height); mPreview->setViewport(int(width*mScaleFactor), int(height*mScaleFactor));
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,
width/float(mPreview->getTextureWidth()), height/float(mPreview->getTextureHeight()))); width*mScaleFactor/float(mPreview->getTextureWidth()), height*mScaleFactor/float(mPreview->getTextureHeight())));
} }
void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender)
@ -512,7 +518,7 @@ namespace MWGui
// Give the script a chance to run once before we do anything else // Give the script a chance to run once before we do anything else
// this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this) // this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this)
if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) if (!force && !script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled())
{ {
MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr);
MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);
@ -591,6 +597,11 @@ namespace MWGui
{ {
// convert to OpenGL lower-left origin // convert to OpenGL lower-left origin
y = (mAvatarImage->getHeight()-1) - y; y = (mAvatarImage->getHeight()-1) - y;
// Scale coordinates
x = int(x*mScaleFactor);
y = int(y*mScaleFactor);
int slot = mPreview->getSlotSelected (x, y); int slot = mPreview->getSlotSelected (x, y);
if (slot == -1) if (slot == -1)
@ -621,6 +632,22 @@ namespace MWGui
void InventoryWindow::onFrame(float dt) void InventoryWindow::onFrame(float dt)
{ {
updateEncumbranceBar(); updateEncumbranceBar();
if (mPinned)
{
mUpdateTimer += dt;
if (0.1f < mUpdateTimer)
{
mUpdateTimer = 0;
// Update pinned inventory in-game
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
{
mItemView->update();
notifyContentChanged();
}
}
}
} }
void InventoryWindow::setTrading(bool trading) void InventoryWindow::setTrading(bool trading)
@ -671,6 +698,8 @@ namespace MWGui
return; return;
int count = object.getRefData().getCount(); int count = object.getRefData().getCount();
if (object.getClass().isGold(object))
count *= object.getClass().getValue(object);
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
MWBase::Environment::get().getWorld()->breakInvisibility(player); MWBase::Environment::get().getWorld()->breakInvisibility(player);

View file

@ -101,6 +101,8 @@ namespace MWGui
std::unique_ptr<MWRender::InventoryPreview> mPreview; std::unique_ptr<MWRender::InventoryPreview> mPreview;
bool mTrading; bool mTrading;
float mScaleFactor;
float mUpdateTimer;
void onItemSelected(int index); void onItemSelected(int index);
void onItemSelectedFromSourceModel(int index); void onItemSelectedFromSourceModel(int index);

View file

@ -99,9 +99,9 @@ namespace MWGui
std::string message; std::string message;
if (mDays == 1) if (mDays == 1)
message = gmst.find("sNotifyMessage42")->getString(); message = gmst.find("sNotifyMessage42")->mValue.getString();
else else
message = gmst.find("sNotifyMessage43")->getString(); message = gmst.find("sNotifyMessage43")->mValue.getString();
std::stringstream dayStr; std::stringstream dayStr;
dayStr << mDays; dayStr << mDays;
@ -110,12 +110,12 @@ namespace MWGui
for (std::set<int>::iterator it = skills.begin(); it != skills.end(); ++it) for (std::set<int>::iterator it = skills.begin(); it != skills.end(); ++it)
{ {
std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->getString(); std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->mValue.getString();
std::stringstream skillValue; std::stringstream skillValue;
skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase(); skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase();
std::string skillMsg = gmst.find("sNotifyMessage44")->getString(); std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString();
if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security) if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security)
skillMsg = gmst.find("sNotifyMessage39")->getString(); skillMsg = gmst.find("sNotifyMessage39")->mValue.getString();
if (skillMsg.find("%s") != std::string::npos) if (skillMsg.find("%s") != std::string::npos)
skillMsg.replace(skillMsg.find("%s"), 2, skillName); skillMsg.replace(skillMsg.find("%s"), 2, skillName);

View file

@ -10,9 +10,8 @@
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/debug/debuglog.hpp>
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -37,6 +36,7 @@ namespace MWGui
, mLastRenderTime(0.0) , mLastRenderTime(0.0)
, mLoadingOnTime(0.0) , mLoadingOnTime(0.0)
, mImportantLabel(false) , mImportantLabel(false)
, mVisible(false)
, mProgress(0) , mProgress(0)
, mShowWallpaper(true) , mShowWallpaper(true)
{ {
@ -93,7 +93,7 @@ namespace MWGui
++found; ++found;
} }
if (mSplashScreens.empty()) if (mSplashScreens.empty())
std::cerr << "No splash screens found!" << std::endl; Log(Debug::Warning) << "Warning: no splash screens found!";
} }
void LoadingScreen::setLabel(const std::string &label, bool important) void LoadingScreen::setLabel(const std::string &label, bool important)
@ -169,12 +169,18 @@ namespace MWGui
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame);
if (!visible)
{
draw();
return;
}
mVisible = visible; mVisible = visible;
mLoadingBox->setVisible(mVisible); mLoadingBox->setVisible(mVisible);
mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame);
setVisible(true); setVisible(true);
if (mShowWallpaper) if (mShowWallpaper)
@ -183,9 +189,6 @@ namespace MWGui
} }
MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading); MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading);
if (!mVisible)
draw();
} }
void LoadingScreen::loadingOff() void LoadingScreen::loadingOff()

View file

@ -57,7 +57,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor)
int basePrice = iter->getClass().getValue(*iter); int basePrice = iter->getClass().getValue(*iter);
float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() float fRepairMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fRepairMult")->getFloat(); .find("fRepairMult")->mValue.getFloat();
float p = static_cast<float>(std::max(1, basePrice)); float p = static_cast<float>(std::max(1, basePrice));
float r = static_cast<float>(std::max(1, static_cast<int>(maxDurability / p))); float r = static_cast<float>(std::max(1, static_cast<int>(maxDurability / p)));
@ -71,7 +71,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor)
std::string name = iter->getClass().getName(*iter) std::string name = iter->getClass().getName(*iter)
+ " - " + MyGUI::utility::toString(price) + " - " + MyGUI::utility::toString(price)
+ MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("sgp")->getString(); .find("sgp")->mValue.getString();
MyGUI::Button* button = MyGUI::Button* button =

View file

@ -5,6 +5,7 @@
#include <MyGUI_RenderManager.h> #include <MyGUI_RenderManager.h>
#include <MyGUI_Button.h> #include <MyGUI_Button.h>
#include <components/debug/debuglog.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -125,7 +126,7 @@ namespace MWGui
{ {
if (mInterMessageBoxe != NULL) if (mInterMessageBoxe != NULL)
{ {
std::cerr << "Warning: replacing an interactive message box that was not answered yet" << std::endl; Log(Debug::Warning) << "Warning: replacing an interactive message box that was not answered yet";
mInterMessageBoxe->setVisible(false); mInterMessageBoxe->setVisible(false);
delete mInterMessageBoxe; delete mInterMessageBoxe;
mInterMessageBoxe = NULL; mInterMessageBoxe = NULL;

View file

@ -161,6 +161,12 @@ namespace MWGui
} }
} }
assert(index != -1); assert(index != -1);
if (index < 0)
{
mSelected = nullptr;
return;
}
mSelected = &mKey[index]; mSelected = &mKey[index];
// prevent reallocation of zero key from Type_HandToHand // prevent reallocation of zero key from Type_HandToHand

View file

@ -6,6 +6,7 @@
#include <osg/Texture2D> #include <osg/Texture2D>
#include <components/debug/debuglog.hpp>
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
@ -340,7 +341,7 @@ namespace MWGui
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << "Error creating preview: " << e.what() << std::endl; Log(Debug::Error) << "Error creating preview: " << e.what();
} }
} }

View file

@ -169,19 +169,18 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success"); MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
player.getClass().getContainerStore(player).restack(item); player.getClass().getContainerStore(player).restack(item);
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
} }
else else
{ {
MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail"); MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail");
} }
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
gem.getContainerStore()->remove(gem, 1, player); gem.getContainerStore()->remove(gem, 1, player);
if (gem.getRefData().getCount() == 0) if (gem.getRefData().getCount() == 0)
{ {
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->getString(); std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString();
message = boost::str(boost::format(message) % gem.getClass().getName(gem)); message = boost::str(boost::format(message) % gem.getClass().getName(gem));
MWBase::Environment::get().getWindowManager()->messageBox(message); MWBase::Environment::get().getWindowManager()->messageBox(message);

View file

@ -12,6 +12,8 @@
#include <osgDB/ReadFile> #include <osgDB/ReadFile>
#include <osg/Texture2D> #include <osg/Texture2D>
#include <components/debug/debuglog.hpp>
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
@ -438,14 +440,14 @@ namespace MWGui
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg");
if (!readerwriter) if (!readerwriter)
{ {
std::cerr << "Error: Can't open savegame screenshot, no jpg readerwriter found" << std::endl; Log(Debug::Error) << "Error: Can't open savegame screenshot, no jpg readerwriter found";
return; return;
} }
osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(instream); osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(instream);
if (!result.success()) if (!result.success())
{ {
std::cerr << "Error: Failed to read savegame screenshot: " << result.message() << " code " << result.status() << std::endl; Log(Debug::Error) << "Error: Failed to read savegame screenshot: " << result.message() << " code " << result.status();
return; return;
} }

View file

@ -12,6 +12,7 @@
#include <SDL_video.h> #include <SDL_video.h>
#include <components/debug/debuglog.hpp>
#include <components/widgets/sharedstatebutton.hpp> #include <components/widgets/sharedstatebutton.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -31,7 +32,8 @@ namespace
if (val == "linear") return "Trilinear"; if (val == "linear") return "Trilinear";
if (val == "nearest") return "Bilinear"; if (val == "nearest") return "Bilinear";
if (val != "none") if (val != "none")
std::cerr<< "Warning: Invalid texture mipmap option: "<<val <<std::endl; Log(Debug::Warning) << "Warning: Invalid texture mipmap option: "<< val;
return "Other"; return "Other";
} }
@ -398,7 +400,7 @@ namespace MWGui
else if(pos == 1) else if(pos == 1)
Settings::Manager::setString("texture mipmap", "General", "linear"); Settings::Manager::setString("texture mipmap", "General", "linear");
else else
std::cerr<< "Unexpected option pos "<<pos <<std::endl; Log(Debug::Warning) << "Unexpected option pos " << pos;
apply(); apply();
} }

View file

@ -1,9 +1,7 @@
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include <iostream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/debug/debuglog.hpp>
#include <components/esm/loadalch.hpp> #include <components/esm/loadalch.hpp>
#include <components/esm/loadappa.hpp> #include <components/esm/loadappa.hpp>
#include <components/esm/loadarmo.hpp> #include <components/esm/loadarmo.hpp>
@ -245,7 +243,7 @@ namespace MWGui
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchId); const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchId);
if (!ench) if (!ench)
{ {
std::cerr << "Warning: Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId() << std::endl; Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId();
return false; return false;
} }

View file

@ -44,7 +44,7 @@ namespace MWGui
const MWWorld::ESMStore &store = const MWWorld::ESMStore &store =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
int price = static_cast<int>(spell.mData.mCost*store.get<ESM::GameSetting>().find("fSpellValueMult")->getFloat()); int price = static_cast<int>(spell.mData.mCost*store.get<ESM::GameSetting>().find("fSpellValueMult")->mValue.getFloat());
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true);
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();

View file

@ -32,8 +32,8 @@ namespace
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
return gmst.find(ESM::MagicEffect::effectIdToString (id1))->getString() return gmst.find(ESM::MagicEffect::effectIdToString (id1))->mValue.getString()
< gmst.find(ESM::MagicEffect::effectIdToString (id2))->getString(); < gmst.find(ESM::MagicEffect::effectIdToString (id2))->mValue.getString();
} }
void init(ESM::ENAMstruct& effect) void init(ESM::ENAMstruct& effect)
@ -469,7 +469,7 @@ namespace MWGui
mMagickaCost->setCaption(MyGUI::utility::toString(int(y))); mMagickaCost->setCaption(MyGUI::utility::toString(int(y)));
float fSpellMakingValueMult = float fSpellMakingValueMult =
store.get<ESM::GameSetting>().find("fSpellMakingValueMult")->getFloat(); store.get<ESM::GameSetting>().find("fSpellMakingValueMult")->mValue.getFloat();
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, static_cast<int>(y * fSpellMakingValueMult),true); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, static_cast<int>(y * fSpellMakingValueMult),true);
@ -547,7 +547,7 @@ namespace MWGui
for (std::vector<short>::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) for (std::vector<short>::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it)
{ {
mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find( mAvailableEffectsList->addItem(MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find(
ESM::MagicEffect::effectIdToString (*it))->getString()); ESM::MagicEffect::effectIdToString (*it))->mValue.getString());
mButtonMapping[i] = *it; mButtonMapping[i] = *it;
++i; ++i;
} }
@ -557,7 +557,7 @@ namespace MWGui
for (std::vector<short>::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it) for (std::vector<short>::const_iterator it = knownEffects.begin(); it != knownEffects.end(); ++it)
{ {
std::string name = MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find( std::string name = MWBase::Environment::get().getWorld ()->getStore ().get<ESM::GameSetting>().find(
ESM::MagicEffect::effectIdToString (*it))->getString(); ESM::MagicEffect::effectIdToString (*it))->mValue.getString();
MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name); MyGUI::Widget* w = mAvailableEffectsList->getItemWidget(name);
ToolTips::createMagicEffectToolTip (w, *it); ToolTips::createMagicEffectToolTip (w, *it);

View file

@ -75,7 +75,7 @@ namespace MWGui
std::string sourcesDescription; std::string sourcesDescription;
static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fMagicStartIconBlink")->getFloat(); static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fMagicStartIconBlink")->mValue.getFloat();
for (std::vector<MagicEffectInfo>::const_iterator effectIt = it->second.begin(); for (std::vector<MagicEffectInfo>::const_iterator effectIt = it->second.begin();
effectIt != it->second.end(); ++effectIt) effectIt != it->second.end(); ++effectIt)

View file

@ -1,6 +1,6 @@
#include "spellmodel.hpp" #include "spellmodel.hpp"
#include <iostream> #include <components/debug/debuglog.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -94,7 +94,7 @@ namespace MWGui
const ESM::Enchantment* enchant = esmStore.get<ESM::Enchantment>().search(enchantId); const ESM::Enchantment* enchant = esmStore.get<ESM::Enchantment>().search(enchantId);
if (!enchant) if (!enchant)
{ {
std::cerr << "Warning: Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId() << std::endl; Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId();
continue; continue;
} }

View file

@ -26,6 +26,7 @@ namespace MWGui
Spell() Spell()
: mType(Type_Spell) : mType(Type_Spell)
, mCount(0)
, mSelected(false) , mSelected(false)
, mActive(false) , mActive(false)
{ {

View file

@ -86,6 +86,10 @@ namespace MWGui
mUpdateTimer = 0; mUpdateTimer = 0;
mSpellView->incrementalUpdate(); mSpellView->incrementalUpdate();
} }
// Update effects in-game too if the window is pinned
if (mPinned && !MWBase::Environment::get().getWindowManager()->isGuiMode())
mSpellIcons->updateWidgets(mEffectBox, false);
} }
void SpellWindow::updateSpells() void SpellWindow::updateSpells()

View file

@ -61,7 +61,7 @@ namespace MWGui
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
for (int i=0; names[i][0]; ++i) for (int i=0; names[i][0]; ++i)
{ {
setText (names[i][0], store.get<ESM::GameSetting>().find (names[i][1])->getString()); setText (names[i][0], store.get<ESM::GameSetting>().find (names[i][1])->mValue.getString());
} }
getWidget(mSkillView, "SkillView"); getWidget(mSkillView, "SkillView");
@ -306,7 +306,7 @@ namespace MWGui
MyGUI::Widget* levelWidget; MyGUI::Widget* levelWidget;
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)
{ {
int max = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iLevelUpTotal")->getInt(); int max = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iLevelUpTotal")->mValue.getInteger();
getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText");
levelWidget->setUserString("RangePosition_LevelProgress", MyGUI::utility::toString(PCstats.getLevelProgress())); levelWidget->setUserString("RangePosition_LevelProgress", MyGUI::utility::toString(PCstats.getLevelProgress()));
levelWidget->setUserString("Range_LevelProgress", MyGUI::utility::toString(max)); levelWidget->setUserString("Range_LevelProgress", MyGUI::utility::toString(max));

View file

@ -313,7 +313,7 @@ namespace MWGui
{ {
if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), mPtr)) if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(it->mBase.getCellRef().getRefId(), mPtr))
{ {
std::string msg = gmst.find("sNotifyMessage49")->getString(); std::string msg = gmst.find("sNotifyMessage49")->mValue.getString();
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase));
MWBase::Environment::get().getWindowManager()->messageBox(msg); MWBase::Environment::get().getWindowManager()->messageBox(msg);
@ -331,10 +331,10 @@ namespace MWGui
// apply disposition change if merchant is NPC // apply disposition change if merchant is NPC
if ( mPtr.getClass().isNpc() ) { if ( mPtr.getClass().isNpc() ) {
int dispositionDelta = offerAccepted int dispositionDelta = offerAccepted
? gmst.find("iBarterSuccessDisposition")->getInt() ? gmst.find("iBarterSuccessDisposition")->mValue.getInteger()
: gmst.find("iBarterFailDisposition")->getInt(); : gmst.find("iBarterFailDisposition")->mValue.getInteger();
MWBase::Environment::get().getDialogueManager()->applyDispositionChange(dispositionDelta); MWBase::Environment::get().getDialogueManager()->applyBarterDispositionChange(dispositionDelta);
} }
// display message on haggle failure // display message on haggle failure

View file

@ -98,7 +98,7 @@ namespace MWGui
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
{ {
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer
(mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->mValue.getInteger(),true);
MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip
MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default);
@ -136,7 +136,7 @@ namespace MWGui
const MWWorld::ESMStore &store = const MWWorld::ESMStore &store =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
int price = pcStats.getSkill (skillId).getBase() * store.get<ESM::GameSetting>().find("iTrainingMod")->getInt (); int price = pcStats.getSkill (skillId).getBase() * store.get<ESM::GameSetting>().find("iTrainingMod")->mValue.getInteger();
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true);
if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId))

View file

@ -46,7 +46,7 @@ namespace MWGui
mSelect->getHeight()); mSelect->getHeight());
} }
void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior) void TravelWindow::addDestination(const std::string& name, ESM::Position pos, bool interior)
{ {
int price; int price;
@ -56,15 +56,15 @@ namespace MWGui
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
if(interior) if (!mPtr.getCell()->isExterior())
{ {
price = gmst.find("fMagesGuildTravel")->getInt(); price = gmst.find("fMagesGuildTravel")->mValue.getInteger();
} }
else else
{ {
ESM::Position PlayerPos = player.getRefData().getPosition(); ESM::Position PlayerPos = player.getRefData().getPosition();
float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2)); float d = sqrt(pow(pos.pos[0] - PlayerPos.pos[0], 2) + pow(pos.pos[1] - PlayerPos.pos[1], 2) + pow(pos.pos[2] - PlayerPos.pos[2], 2));
price = static_cast<int>(d / gmst.find("fTravelMult")->getFloat()); price = static_cast<int>(d / gmst.find("fTravelMult")->mValue.getFloat());
} }
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);
@ -154,6 +154,10 @@ namespace MWGui
if (playerGold<price) if (playerGold<price)
return; return;
// Set "traveling" flag, so GetPCTraveling can detect teleportation.
// We will reset this flag during next world update.
MWBase::Environment::get().getWorld()->setPlayerTraveling(true);
if (!mPtr.getCell()->isExterior()) if (!mPtr.getCell()->isExterior())
// Interior cell -> mages guild transport // Interior cell -> mages guild transport
MWBase::Environment::get().getWindowManager()->playSound("mysticism cast"); MWBase::Environment::get().getWindowManager()->playSound("mysticism cast");
@ -168,11 +172,11 @@ namespace MWGui
ESM::Position pos = *_sender->getUserData<ESM::Position>(); ESM::Position pos = *_sender->getUserData<ESM::Position>();
std::string cellname = _sender->getUserString("Destination"); std::string cellname = _sender->getUserString("Destination");
bool interior = _sender->getUserString("interior") == "y"; bool interior = _sender->getUserString("interior") == "y";
if (!interior) if (mPtr.getCell()->isExterior())
{ {
ESM::Position playerPos = player.getRefData().getPosition(); ESM::Position playerPos = player.getRefData().getPosition();
float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length(); float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length();
int hours = static_cast<int>(d /MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fTravelTimeMult")->getFloat()); int hours = static_cast<int>(d /MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fTravelTimeMult")->mValue.getFloat());
for(int i = 0;i < hours;i++) for(int i = 0;i < hours;i++)
{ {
MWBase::Environment::get().getMechanicsManager ()->rest (true); MWBase::Environment::get().getMechanicsManager ()->rest (true);

View file

@ -6,6 +6,7 @@
#include <osg/Texture2D> #include <osg/Texture2D>
#include <components/debug/debuglog.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>
@ -21,6 +22,8 @@ VideoWidget::VideoWidget()
setNeedKeyFocus(true); setNeedKeyFocus(true);
} }
VideoWidget::~VideoWidget() = default;
void VideoWidget::setVFS(const VFS::Manager *vfs) void VideoWidget::setVFS(const VFS::Manager *vfs)
{ {
mVFS = vfs; mVFS = vfs;
@ -37,7 +40,7 @@ void VideoWidget::playVideo(const std::string &video)
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << "Failed to open video: " << e.what() << std::endl; Log(Debug::Error) << "Failed to open video: " << e.what();
return; return;
} }

View file

@ -25,6 +25,8 @@ namespace MWGui
MYGUI_RTTI_DERIVED(VideoWidget) MYGUI_RTTI_DERIVED(VideoWidget)
VideoWidget(); VideoWidget();
~VideoWidget();
/// Set the VFS (virtual file system) to find the videos on. /// Set the VFS (virtual file system) to find the videos on.
void setVFS(const VFS::Manager* vfs); void setVFS(const VFS::Manager* vfs);

View file

@ -183,10 +183,10 @@ namespace MWGui
{ {
// figure out if player will be woken while sleeping // figure out if player will be woken while sleeping
int x = Misc::Rng::rollDice(hoursToWait); int x = Misc::Rng::rollDice(hoursToWait);
float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat(); float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->mValue.getFloat();
if (x < fSleepRandMod * hoursToWait) if (x < fSleepRandMod * hoursToWait)
{ {
float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat(); float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->mValue.getFloat();
int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait); int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait);
if (interruptAtHoursRemaining != 0) if (interruptAtHoursRemaining != 0)
{ {
@ -252,7 +252,7 @@ namespace MWGui
// trigger levelup if possible // trigger levelup if possible
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->getInt()) if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->mValue.getInteger())
{ {
MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup);
} }

View file

@ -21,6 +21,8 @@
#include <SDL_keyboard.h> #include <SDL_keyboard.h>
#include <SDL_clipboard.h> #include <SDL_clipboard.h>
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlcursormanager.hpp> #include <components/sdlutil/sdlcursormanager.hpp>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
@ -356,7 +358,7 @@ namespace MWGui
mGuiModeStates[GM_Journal].mCloseSound = "book close"; mGuiModeStates[GM_Journal].mCloseSound = "book close";
mGuiModeStates[GM_Journal].mOpenSound = "book open"; mGuiModeStates[GM_Journal].mOpenSound = "book open";
mMessageBoxManager = new MessageBoxManager(mStore->get<ESM::GameSetting>().find("fMessageTimePerChar")->getFloat()); mMessageBoxManager = new MessageBoxManager(mStore->get<ESM::GameSetting>().find("fMessageTimePerChar")->mValue.getFloat());
SpellBuyingWindow* spellBuyingWindow = new SpellBuyingWindow(); SpellBuyingWindow* spellBuyingWindow = new SpellBuyingWindow();
mWindows.push_back(spellBuyingWindow); mWindows.push_back(spellBuyingWindow);
@ -898,7 +900,8 @@ namespace MWGui
mKeyboardNavigation->onFrame(); mKeyboardNavigation->onFrame();
mMessageBoxManager->onFrame(frameDuration); if (mMessageBoxManager)
mMessageBoxManager->onFrame(frameDuration);
mToolTips->onFrame(frameDuration); mToolTips->onFrame(frameDuration);
@ -1081,7 +1084,7 @@ namespace MWGui
{ {
if (!mStore) if (!mStore)
{ {
std::cerr << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'" << std::endl; Log(Debug::Error) << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'";
return; return;
} }
const ESM::GameSetting *setting = mStore->get<ESM::GameSetting>().find(tag); const ESM::GameSetting *setting = mStore->get<ESM::GameSetting>().find(tag);
@ -1787,7 +1790,7 @@ namespace MWGui
if (found != mCurrentModals.end()) if (found != mCurrentModals.end())
mCurrentModals.erase(found); mCurrentModals.erase(found);
else else
std::cerr << " warning: can't find modal window " << input << std::endl; Log(Debug::Warning) << "Warning: can't find modal window " << input;
} }
} }
if (mCurrentModals.empty()) if (mCurrentModals.empty())

View file

@ -10,6 +10,7 @@
#include <SDL_version.h> #include <SDL_version.h>
#include <components/debug/debuglog.hpp>
#include <components/sdlutil/sdlinputwrapper.hpp> #include <components/sdlutil/sdlinputwrapper.hpp>
#include <components/sdlutil/sdlvideowrapper.hpp> #include <components/sdlutil/sdlvideowrapper.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
@ -120,11 +121,11 @@ namespace MWInput
SDL_ControllerDeviceEvent evt; SDL_ControllerDeviceEvent evt;
evt.which = i; evt.which = i;
controllerAdded(mFakeDeviceID, evt); controllerAdded(mFakeDeviceID, evt);
std::cout << "Detected game controller: " << SDL_GameControllerNameForIndex(i) << std::endl; Log(Debug::Info) << "Detected game controller: " << SDL_GameControllerNameForIndex(i);
} }
else else
{ {
std::cout << "Detected unusable controller: " << SDL_JoystickNameForIndex(i) << std::endl; Log(Debug::Info) << "Detected unusable controller: " << SDL_JoystickNameForIndex(i);
} }
} }
@ -1003,9 +1004,9 @@ namespace MWInput
if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"]) if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"])
return; return;
// We want to interrupt animation only if attack is prepairing, but still is not triggered // We want to interrupt animation only if attack is preparing, but still is not triggered
// Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle Weapon" key twice // Otherwise we will get a "speedshooting" exploit, when player can skip reload animation by hitting "Toggle Weapon" key twice
if (MWBase::Environment::get().getMechanicsManager()->isAttackPrepairing(mPlayer->getPlayer())) if (MWBase::Environment::get().getMechanicsManager()->isAttackPreparing(mPlayer->getPlayer()))
mPlayer->setAttackingOrSpell(false); mPlayer->setAttackingOrSpell(false);
else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer())) else if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
return; return;
@ -1191,7 +1192,7 @@ namespace MWInput
void InputManager::updateIdleTime(float dt) void InputManager::updateIdleTime(float dt)
{ {
static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fVanityDelay")->getFloat(); .find("fVanityDelay")->mValue.getFloat();
if (mTimeIdle >= 0.f) if (mTimeIdle >= 0.f)
mTimeIdle += dt; mTimeIdle += dt;
if (mTimeIdle > vanityDelay) { if (mTimeIdle > vanityDelay) {

View file

@ -197,8 +197,14 @@ namespace MWMechanics
void ActiveSpells::removeEffects(const std::string &id) void ActiveSpells::removeEffects(const std::string &id)
{ {
mSpells.erase(Misc::StringUtils::lowerCase(id)); for (TContainer::iterator spell = mSpells.begin(); spell != mSpells.end(); ++spell)
mSpellsChanged = true; {
if (spell->first == id)
{
spell->second.mEffects.clear();
mSpellsChanged = true;
}
}
} }
void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const

View file

@ -2,13 +2,12 @@
#include <typeinfo> #include <typeinfo>
#include <iostream> #include <iostream>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include <components/debug/debuglog.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
@ -55,22 +54,22 @@ int getBoundItemSlot (const std::string& itemId)
static std::map<std::string, int> boundItemsMap; static std::map<std::string, int> boundItemsMap;
if (boundItemsMap.empty()) if (boundItemsMap.empty())
{ {
std::string boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundBootsID")->getString(); std::string boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundBootsID")->mValue.getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Boots; boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Boots;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundCuirassID")->getString(); boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundCuirassID")->mValue.getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Cuirass; boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Cuirass;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundLeftGauntletID")->getString(); boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundLeftGauntletID")->mValue.getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_LeftGauntlet; boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_LeftGauntlet;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundRightGauntletID")->getString(); boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundRightGauntletID")->mValue.getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_RightGauntlet; boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_RightGauntlet;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundHelmID")->getString(); boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundHelmID")->mValue.getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Helmet; boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Helmet;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundShieldID")->getString(); boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundShieldID")->mValue.getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_CarriedLeft; boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_CarriedLeft;
} }
@ -139,7 +138,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
magicka = 0; magicka = 0;
if (!stunted) if (!stunted)
{ {
float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
} }
} }
@ -180,7 +179,7 @@ namespace MWMechanics
if (caster.isEmpty() || !caster.getClass().isActor()) if (caster.isEmpty() || !caster.getClass().isActor())
return; return;
static const float fSoulgemMult = world->getStore().get<ESM::GameSetting>().find("fSoulgemMult")->getFloat(); static const float fSoulgemMult = world->getStore().get<ESM::GameSetting>().find("fSoulgemMult")->mValue.getFloat();
int creatureSoulValue = mCreature.get<ESM::Creature>()->mBase->mData.mSoul; int creatureSoulValue = mCreature.get<ESM::Creature>()->mBase->mData.mSoul;
if (creatureSoulValue == 0) if (creatureSoulValue == 0)
@ -314,9 +313,9 @@ namespace MWMechanics
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance) MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance)
{ {
static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fMaxHeadTrackDistance")->getFloat(); .find("fMaxHeadTrackDistance")->mValue.getFloat();
static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fInteriorHeadTrackMult")->getFloat(); .find("fInteriorHeadTrackMult")->mValue.getFloat();
float maxDistance = fMaxHeadTrackDistance; float maxDistance = fMaxHeadTrackDistance;
const ESM::Cell* currentCell = actor.getCell()->getCell(); const ESM::Cell* currentCell = actor.getCell()->getCell();
if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx)) if (!currentCell->isExterior() && !(currentCell->mData.mFlags & ESM::Cell::QuasiEx))
@ -404,7 +403,7 @@ namespace MWMechanics
std::set<MWWorld::Ptr> playerAllies; std::set<MWWorld::Ptr> playerAllies;
getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies); getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies);
bool isPlayerFollowerOrEscorter = std::find(playerAllies.begin(), playerAllies.end(), actor1) != playerAllies.end(); bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end();
// If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them
// Doesn't apply for player followers/escorters // Doesn't apply for player followers/escorters
@ -458,7 +457,7 @@ namespace MWMechanics
// Do aggression check if actor2 is the player or a player follower or escorter // Do aggression check if actor2 is the player or a player follower or escorter
if (!aggressive) if (!aggressive)
{ {
if (againstPlayer || std::find(playerAllies.begin(), playerAllies.end(), actor2) != playerAllies.end()) if (againstPlayer || playerAllies.find(actor2) != playerAllies.end())
{ {
// Player followers and escorters with high fight should not initiate combat with the player or with // Player followers and escorters with high fight should not initiate combat with the player or with
// other player followers or escorters // other player followers or escorters
@ -471,7 +470,7 @@ namespace MWMechanics
if (actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc()) if (actor1.getClass().isClass(actor1, "Guard") && !actor2.getClass().isNpc())
{ {
// Check if the creature is too far // Check if the creature is too far
static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fAlarmRadius")->getFloat(); static const float fAlarmRadius = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fAlarmRadius")->mValue.getFloat();
if (sqrDist > fAlarmRadius * fAlarmRadius) if (sqrDist > fAlarmRadius * fAlarmRadius)
return; return;
@ -535,9 +534,9 @@ namespace MWMechanics
float base = 1.f; float base = 1.f;
if (ptr == getPlayer()) if (ptr == getPlayer())
base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat(); base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat();
else else
base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCbaseMagickaMult")->getFloat(); base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCbaseMagickaMult")->mValue.getFloat();
double magickaFactor = base + double magickaFactor = base +
creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1; creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1;
@ -546,7 +545,7 @@ namespace MWMechanics
float diff = (static_cast<int>(magickaFactor*intelligence)) - magicka.getBase(); float diff = (static_cast<int>(magickaFactor*intelligence)) - magicka.getBase();
float currentToBaseRatio = (magicka.getCurrent() / magicka.getBase()); float currentToBaseRatio = (magicka.getCurrent() / magicka.getBase());
magicka.setModified(magicka.getModified() + diff, 0); magicka.setModified(magicka.getModified() + diff, 0);
magicka.setCurrent(magicka.getBase() * currentToBaseRatio); magicka.setCurrent(magicka.getBase() * currentToBaseRatio, false, true);
creatureStats.setMagicka(magicka); creatureStats.setMagicka(magicka);
} }
@ -577,16 +576,21 @@ namespace MWMechanics
float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
if (normalizedEncumbrance > 1) if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1; normalizedEncumbrance = 1;
// Current fatigue can be above base value due to a fortify effect.
// In that case stop here and don't try to restore.
DynamicStat<float> fatigue = stats.getFatigue();
if (fatigue.getCurrent() >= fatigue.getBase())
return;
// restore fatigue // Restore fatigue
float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat ();
float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance);
x *= fEndFatigueMult * endurance; x *= fEndFatigueMult * endurance;
DynamicStat<float> fatigue = stats.getFatigue();
fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); fatigue.setCurrent (fatigue.getCurrent() + 3600 * x);
stats.setFatigue (fatigue); stats.setFatigue (fatigue);
} }
@ -598,16 +602,20 @@ namespace MWMechanics
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); // Current fatigue can be above base value due to a fortify effect.
// In that case stop here and don't try to restore.
DynamicStat<float> fatigue = stats.getFatigue();
if (fatigue.getCurrent() >= fatigue.getBase())
return;
// restore fatigue // Restore fatigue
int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
float x = fFatigueReturnBase + fFatigueReturnMult * endurance; float x = fFatigueReturnBase + fFatigueReturnMult * endurance;
DynamicStat<float> fatigue = stats.getFatigue();
fatigue.setCurrent (fatigue.getCurrent() + duration * x); fatigue.setCurrent (fatigue.getCurrent() + duration * x);
stats.setFatigue (fatigue); stats.setFatigue (fatigue);
} }
@ -688,6 +696,19 @@ namespace MWMechanics
} }
} }
// dynamic stats
for (int i = 0; i < 3; ++i)
{
DynamicStat<float> stat = creatureStats.getDynamic(i);
stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth + i).getMagnitude() -
effects.get(ESM::MagicEffect::DrainHealth + i).getMagnitude(),
// Magicka can be decreased below zero due to a fortify effect wearing off
// Fatigue can be decreased below zero meaning the actor will be knocked out
i == 1 || i == 2);
creatureStats.setDynamic(i, stat);
}
// attributes // attributes
for(int i = 0;i < ESM::Attribute::Length;++i) for(int i = 0;i < ESM::Attribute::Length;++i)
{ {
@ -719,19 +740,6 @@ namespace MWMechanics
} }
} }
// dynamic stats
for(int i = 0;i < 3;++i)
{
DynamicStat<float> stat = creatureStats.getDynamic(i);
stat.setModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() -
effects.get(ESM::MagicEffect::DrainHealth+i).getMagnitude(),
// Magicka can be decreased below zero due to a fortify effect wearing off
// Fatigue can be decreased below zero meaning the actor will be knocked out
i == 1 || i == 2);
creatureStats.setDynamic(i, stat);
}
// AI setting modifiers // AI setting modifiers
int creature = !ptr.getClass().isNpc(); int creature = !ptr.getClass().isNpc();
if (creature && ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Humanoid) if (creature && ptr.get<ESM::Creature>()->mBase->mData.mType == ESM::Creature::Humanoid)
@ -845,14 +853,14 @@ namespace MWMechanics
std::string itemGmst = it->second; std::string itemGmst = it->second;
std::string item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( std::string item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
itemGmst)->getString(); itemGmst)->mValue.getString();
magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);
if (it->first == ESM::MagicEffect::BoundGloves) if (it->first == ESM::MagicEffect::BoundGloves)
{ {
item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"sMagicBoundRightGauntletID")->getString(); "sMagicBoundRightGauntletID")->mValue.getString();
magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);
} }
} }
@ -888,14 +896,14 @@ namespace MWMechanics
} }
} }
bool Actors::isAttackPrepairing(const MWWorld::Ptr& ptr) bool Actors::isAttackPreparing(const MWWorld::Ptr& ptr)
{ {
PtrActorMap::iterator it = mActors.find(ptr); PtrActorMap::iterator it = mActors.find(ptr);
if (it == mActors.end()) if (it == mActors.end())
return false; return false;
CharacterController* ctrl = it->second->getCharacterController(); CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isAttackPrepairing(); return ctrl->isAttackPreparing();
} }
bool Actors::isRunning(const MWWorld::Ptr& ptr) bool Actors::isRunning(const MWWorld::Ptr& ptr)
@ -928,7 +936,7 @@ namespace MWMechanics
NpcStats &stats = ptr.getClass().getNpcStats(ptr); NpcStats &stats = ptr.getClass().getNpcStats(ptr);
// When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->getFloat(); static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->mValue.getFloat();
if (stats.getTimeToStartDrowning() == -1.f) if (stats.getTimeToStartDrowning() == -1.f)
stats.setTimeToStartDrowning(fHoldBreathTime); stats.setTimeToStartDrowning(fHoldBreathTime);
@ -962,7 +970,7 @@ namespace MWMechanics
if(timeLeft == 0.0f && !godmode) if(timeLeft == 0.0f && !godmode)
{ {
// If drowning, apply 3 points of damage per second // If drowning, apply 3 points of damage per second
static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find("fSuffocationDamage")->getFloat(); static const float fSuffocationDamage = world->getStore().get<ESM::GameSetting>().find("fSuffocationDamage")->mValue.getFloat();
DynamicStat<float> health = stats.getHealth(); DynamicStat<float> health = stats.getHealth();
health.setCurrent(health.getCurrent() - fSuffocationDamage*duration); health.setCurrent(health.getCurrent() - fSuffocationDamage*duration);
stats.setHealth(health); stats.setHealth(health);
@ -1088,7 +1096,7 @@ namespace MWMechanics
&& creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0) && creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0)
{ {
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt(); static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->mValue.getInteger();
// Force dialogue on sight if bounty is greater than the cutoff // Force dialogue on sight if bounty is greater than the cutoff
// In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty) // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty)
if ( player.getClass().getNpcStats(player).getBounty() >= cutoff if ( player.getClass().getNpcStats(player).getBounty() >= cutoff
@ -1096,7 +1104,7 @@ namespace MWMechanics
&& MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getWorld()->getLOS(ptr, player)
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
{ {
static const int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->getInt(); static const int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier)
{ {
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
@ -1298,6 +1306,8 @@ namespace MWMechanics
// AI and magic effects update // AI and magic effects update
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{ {
bool isPlayer = iter->first == player;
float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2();
// AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this
// (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not)
@ -1305,7 +1315,7 @@ namespace MWMechanics
// using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876)
bool inProcessingRange = distSqr <= sqrAiProcessingDistance; bool inProcessingRange = distSqr <= sqrAiProcessingDistance;
if (iter->first == player) if (isPlayer)
iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell());
// If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player. // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player.
@ -1337,12 +1347,12 @@ namespace MWMechanics
{ {
if (timerUpdateAITargets == 0) if (timerUpdateAITargets == 0)
{ {
if (iter->first != player) if (!isPlayer)
adjustCommandedActor(iter->first); adjustCommandedActor(iter->first);
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{ {
if (it->first == iter->first || iter->first == player) // player is not AI-controlled if (it->first == iter->first || isPlayer) // player is not AI-controlled
continue; continue;
engageCombat(iter->first, it->first, cachedAllies, it->first == player); engageCombat(iter->first, it->first, cachedAllies, it->first == player);
} }
@ -1353,12 +1363,15 @@ namespace MWMechanics
MWWorld::Ptr headTrackTarget; MWWorld::Ptr headTrackTarget;
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
bool firstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson();
// Unconsious actor can not track target // 1. Unconsious actor can not track target
// Also actors in combat and pursue mode do not bother to headtrack // 2. Actors in combat and pursue mode do not bother to headtrack
// 3. Player character does not use headtracking in the 1st-person view
if (!stats.getKnockedDown() && if (!stats.getKnockedDown() &&
!stats.getAiSequence().isInCombat() && !stats.getAiSequence().isInCombat() &&
!stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue)) !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) &&
!firstPersonPlayer)
{ {
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{ {
@ -1474,9 +1487,9 @@ namespace MWMechanics
static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice" static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice"
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
const int radius = esmStore.get<ESM::GameSetting>().find("fSneakUseDist")->getInt(); const int radius = esmStore.get<ESM::GameSetting>().find("fSneakUseDist")->mValue.getInteger();
static float fSneakUseDelay = esmStore.get<ESM::GameSetting>().find("fSneakUseDelay")->getFloat(); static float fSneakUseDelay = esmStore.get<ESM::GameSetting>().find("fSneakUseDelay")->mValue.getFloat();
if (sneakTimer >= fSneakUseDelay) if (sneakTimer >= fSneakUseDelay)
sneakTimer = 0.f; sneakTimer = 0.f;
@ -1666,7 +1679,7 @@ namespace MWMechanics
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first); MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
if (animation) if (animation)
animation->updateEffects(duration); animation->updateEffects();
} }
@ -1715,7 +1728,7 @@ namespace MWMechanics
} }
else else
{ {
std::cerr<< "Warning: Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; Log(Debug::Warning) << "Warning: Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId();
return false; return false;
} }
} }
@ -1763,38 +1776,36 @@ namespace MWMechanics
std::list<MWWorld::Ptr> Actors::getActorsSidingWith(const MWWorld::Ptr& actor) std::list<MWWorld::Ptr> Actors::getActorsSidingWith(const MWWorld::Ptr& actor)
{ {
std::list<MWWorld::Ptr> list; std::list<MWWorld::Ptr> list;
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) for(PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
{ {
const MWWorld::Class &cls = iter->first.getClass(); const MWWorld::Ptr &iteratedActor = iter->first;
const CreatureStats &stats = cls.getCreatureStats(iter->first); if (iteratedActor == getPlayer())
continue;
const bool sameActor = (iteratedActor == actor);
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
if (stats.isDead()) if (stats.isDead())
continue; continue;
// An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat packages before the Follow/Escort package // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package
for (std::list<MWMechanics::AiPackage*>::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
{
if ((*it)->sideWithTarget() && (*it)->getTarget() == actor)
{
list.push_back(iter->first);
break;
}
else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat)
break;
}
// Actors that are targeted by this actor's Follow or Escort packages also side with them // Actors that are targeted by this actor's Follow or Escort packages also side with them
if (actor != getPlayer()) for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package)
{ {
const CreatureStats &stats2 = actor.getClass().getCreatureStats(actor); if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty())
for (std::list<MWMechanics::AiPackage*>::const_iterator it2 = stats2.getAiSequence().begin(); it2 != stats2.getAiSequence().end(); ++it2)
{ {
if ((*it2)->sideWithTarget() && !(*it2)->getTarget().isEmpty()) if (sameActor)
{ {
list.push_back((*it2)->getTarget()); list.push_back((*package)->getTarget());
break;
} }
else if ((*it2)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) else if ((*package)->getTarget() == actor)
break; {
list.push_back(iteratedActor);
}
break;
} }
else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander)
break;
} }
} }
return list; return list;
@ -1805,17 +1816,21 @@ namespace MWMechanics
std::list<MWWorld::Ptr> list; std::list<MWWorld::Ptr> list;
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{ {
const MWWorld::Class &cls = iter->first.getClass(); const MWWorld::Ptr &iteratedActor = iter->first;
CreatureStats &stats = cls.getCreatureStats(iter->first); if (iteratedActor == getPlayer() || iteratedActor == actor)
continue;
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
if (stats.isDead()) if (stats.isDead())
continue; continue;
// An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package // An actor counts as following if AiFollow is the current AiPackage,
for (std::list<MWMechanics::AiPackage*>::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) // or there are only Combat and Wander packages before the AiFollow package
for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package)
{ {
if ((*it)->followTargetThroughDoors() && (*it)->getTarget() == actor) if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor)
list.push_back(iter->first); list.push_back(iteratedActor);
else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander)
break; break;
} }
} }
@ -1864,24 +1879,24 @@ namespace MWMechanics
std::list<int> list; std::list<int> list;
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{ {
const MWWorld::Class &cls = iter->first.getClass(); const MWWorld::Ptr &iteratedActor = iter->first;
CreatureStats &stats = cls.getCreatureStats(iter->first); if (iteratedActor == getPlayer() || iteratedActor == actor)
continue;
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
if (stats.isDead()) if (stats.isDead())
continue; continue;
// An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package // An actor counts as following if AiFollow is the current AiPackage,
for (std::list<MWMechanics::AiPackage*>::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) // or there are only Combat and Wander packages before the AiFollow package
for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package)
{ {
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor)
{ {
MWWorld::Ptr followTarget = (*it)->getTarget(); list.push_back(static_cast<AiFollow*>(*package)->getFollowIndex());
if (followTarget.isEmpty())
continue;
if (followTarget == actor)
list.push_back(static_cast<MWMechanics::AiFollow*>(*it)->getFollowIndex());
break; break;
} }
else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander)
break; break;
} }
} }
@ -1893,14 +1908,17 @@ namespace MWMechanics
std::vector<MWWorld::Ptr> neighbors; std::vector<MWWorld::Ptr> neighbors;
osg::Vec3f position (actor.getRefData().getPosition().asVec3()); osg::Vec3f position (actor.getRefData().getPosition().asVec3());
getObjectsInRange(position, aiProcessingDistance, neighbors); getObjectsInRange(position, aiProcessingDistance, neighbors);
for(std::vector<MWWorld::Ptr>::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor)
{ {
const MWWorld::Class &cls = iter->getClass(); if (*neighbor == actor)
const CreatureStats &stats = cls.getCreatureStats(*iter);
if (stats.isDead() || *iter == actor)
continue; continue;
const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor);
if (stats.isDead())
continue;
if (stats.getAiSequence().isInCombat(actor)) if (stats.getAiSequence().isInCombat(actor))
list.push_front(*iter); list.push_front(*neighbor);
} }
return list; return list;
} }
@ -1912,15 +1930,18 @@ namespace MWMechanics
osg::Vec3f position (actor.getRefData().getPosition().asVec3()); osg::Vec3f position (actor.getRefData().getPosition().asVec3());
getObjectsInRange(position, aiProcessingDistance, neighbors); getObjectsInRange(position, aiProcessingDistance, neighbors);
std::list<MWWorld::Ptr> followers = getActorsFollowing(actor); std::set<MWWorld::Ptr> followers;
for(std::vector<MWWorld::Ptr>::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) getActorsFollowing(actor, followers);
for (auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor)
{ {
const CreatureStats &stats = iter->getClass().getCreatureStats(*iter); const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor);
if (stats.isDead() || *iter == actor || iter->getClass().isPureWaterCreature(*iter)) if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor))
continue; continue;
const bool isFollower = std::find(followers.begin(), followers.end(), *iter) != followers.end();
if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*iter, actor) && !isFollower)) const bool isFollower = followers.find(*neighbor) != followers.end();
list.push_back(*iter);
if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower))
list.push_back(*neighbor);
} }
return list; return list;
} }

View file

@ -118,7 +118,7 @@ namespace MWMechanics
int countDeaths (const std::string& id) const; int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID. ///< Return the number of deaths for actors with the given ID.
bool isAttackPrepairing(const MWWorld::Ptr& ptr); bool isAttackPreparing(const MWWorld::Ptr& ptr);
bool isRunning(const MWWorld::Ptr& ptr); bool isRunning(const MWWorld::Ptr& ptr);
bool isSneaking(const MWWorld::Ptr& ptr); bool isSneaking(const MWWorld::Ptr& ptr);

View file

@ -19,7 +19,7 @@ MWMechanics::AiBreathe::AiBreathe()
bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
{ {
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->getFloat(); static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->mValue.getFloat();
const MWWorld::Class& actorClass = actor.getClass(); const MWWorld::Class& actorClass = actor.getClass();
if (actorClass.isNpc()) if (actorClass.isNpc())

View file

@ -42,7 +42,19 @@ bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::Charac
return false; return false;
} }
osg::Vec3f dir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3(); osg::Vec3f targetPos = target.getRefData().getPosition().asVec3();
if (target.getClass().isActor())
{
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(target);
targetPos.z() += halfExtents.z() * 2 * 0.75f;
}
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
actorPos.z() += halfExtents.z() * 2 * 0.75f;
osg::Vec3f dir = targetPos - actorPos;
bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f)); bool turned = smoothTurn(actor, getZAngleToDir(dir), 2, osg::DegreesToRadians(3.f));
turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f)); turned &= smoothTurn(actor, getXAngleToDir(dir), 0, osg::DegreesToRadians(3.f));

View file

@ -351,7 +351,7 @@ namespace MWMechanics
case AiCombatStorage::FleeState_RunToDestination: case AiCombatStorage::FleeState_RunToDestination:
{ {
static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fFleeDistance")->getFloat(); static const float fFleeDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fFleeDistance")->mValue.getFloat();
float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length(); float dist = (actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length();
if ((dist > fFleeDistance && !storage.mLOS) if ((dist > fFleeDistance && !storage.mLOS)
@ -372,21 +372,32 @@ namespace MWMechanics
actorMovementSettings.mPosition[1] = storage.mMovement.mPosition[1]; actorMovementSettings.mPosition[1] = storage.mMovement.mPosition[1];
actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2]; actorMovementSettings.mPosition[2] = storage.mMovement.mPosition[2];
rotateActorOnAxis(actor, 2, actorMovementSettings, storage.mMovement); rotateActorOnAxis(actor, 2, actorMovementSettings, storage);
rotateActorOnAxis(actor, 0, actorMovementSettings, storage.mMovement); rotateActorOnAxis(actor, 0, actorMovementSettings, storage);
} }
void AiCombat::rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, void AiCombat::rotateActorOnAxis(const MWWorld::Ptr& actor, int axis,
MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement) MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage)
{ {
actorMovementSettings.mRotation[axis] = 0; actorMovementSettings.mRotation[axis] = 0;
float& targetAngleRadians = desiredMovement.mRotation[axis]; float& targetAngleRadians = storage.mMovement.mRotation[axis];
if (targetAngleRadians != 0) if (targetAngleRadians != 0)
{ {
if (smoothTurn(actor, targetAngleRadians, axis)) // Some attack animations contain small amount of movement.
// Since we use cone shapes for melee, we can use a threshold to avoid jittering
std::shared_ptr<Action>& currentAction = storage.mCurrentAction;
bool isRangedCombat = false;
currentAction->getCombatRange(isRangedCombat);
// Check if the actor now facing desired direction, no need to turn any more
if (isRangedCombat)
{ {
// actor now facing desired direction, no need to turn any more if (smoothTurn(actor, targetAngleRadians, axis))
targetAngleRadians = 0; targetAngleRadians = 0;
}
else
{
if (smoothTurn(actor, targetAngleRadians, axis, osg::DegreesToRadians(3.f)))
targetAngleRadians = 0;
} }
} }
} }
@ -453,7 +464,7 @@ namespace MWMechanics
if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25) if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)
{ {
mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right
mTimerCombatMove = 0.05f + 0.15f * Misc::Rng::rollClosedProbability(); mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
mCombatMove = true; mCombatMove = true;
} }
} }
@ -509,13 +520,13 @@ namespace MWMechanics
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
float baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayCreature")->getFloat(); float baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayCreature")->mValue.getFloat();
if (actor.getClass().isNpc()) if (actor.getClass().isNpc())
{ {
baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayNPC")->getFloat(); baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayNPC")->mValue.getFloat();
//say a provoking combat phrase //say a provoking combat phrase
int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt(); int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->mValue.getInteger();
if (Misc::Rng::roll0to99() < chance) if (Misc::Rng::roll0to99() < chance)
{ {
MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
@ -600,27 +611,26 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t
float duration, int weapType, float strength) float duration, int weapType, float strength)
{ {
float projSpeed; float projSpeed;
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
// get projectile speed (depending on weapon type) // get projectile speed (depending on weapon type)
if (weapType == ESM::Weapon::MarksmanThrown) if (weapType == ESM::Weapon::MarksmanThrown)
{ {
static float fThrownWeaponMinSpeed = static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->mValue.getFloat();
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fThrownWeaponMinSpeed")->getFloat(); static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->mValue.getFloat();
static float fThrownWeaponMaxSpeed =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fThrownWeaponMaxSpeed")->getFloat();
projSpeed = projSpeed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength;
fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength;
} }
else else if (weapType != 0)
{ {
static float fProjectileMinSpeed = static float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->mValue.getFloat();
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fProjectileMinSpeed")->getFloat(); static float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat();
static float fProjectileMaxSpeed =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fProjectileMaxSpeed")->getFloat();
projSpeed = projSpeed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength;
fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; }
else // weapType is 0 ==> it's a target spell projectile
{
projSpeed = gmst.find("fTargetSpellMaxSpeed")->mValue.getFloat();
} }
// idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same

View file

@ -129,7 +129,7 @@ namespace MWMechanics
/// Transfer desired movement (from AiCombatStorage) to Actor /// Transfer desired movement (from AiCombatStorage) to Actor
void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage); void updateActorsMovement(const MWWorld::Ptr& actor, float duration, AiCombatStorage& storage);
void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis, void rotateActorOnAxis(const MWWorld::Ptr& actor, int axis,
MWMechanics::Movement& actorMovementSettings, MWMechanics::Movement& desiredMovement); MWMechanics::Movement& actorMovementSettings, AiCombatStorage& storage);
}; };

View file

@ -23,8 +23,8 @@ namespace MWMechanics
{ {
float suggestCombatRange(int rangeTypes) float suggestCombatRange(int rangeTypes)
{ {
static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat(); static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fCombatDistance")->mValue.getFloat();
static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->getFloat(); static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->mValue.getFloat();
// This distance is a possible distance of melee attack // This distance is a possible distance of melee attack
static float distance = fCombatDistance * std::max(2.f, fHandToHandReach); static float distance = fCombatDistance * std::max(2.f, fHandToHandReach);
@ -114,13 +114,13 @@ namespace MWMechanics
{ {
isRanged = false; isRanged = false;
static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat(); static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fCombatDistance")->mValue.getFloat();
static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fProjectileMaxSpeed")->getFloat(); static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fProjectileMaxSpeed")->mValue.getFloat();
if (mWeapon.isEmpty()) if (mWeapon.isEmpty())
{ {
static float fHandToHandReach = static float fHandToHandReach =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->mValue.getFloat();
return fHandToHandReach * fCombatDistance; return fHandToHandReach * fCombatDistance;
} }
@ -190,11 +190,6 @@ namespace MWMechanics
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{ {
std::vector<int> equipmentSlots = it->getClass().getEquipmentSlots(*it).first;
if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight)
== equipmentSlots.end())
continue;
float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating); float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating);
if (rating > bestActionRating) if (rating > bestActionRating)
{ {
@ -215,14 +210,12 @@ namespace MWMechanics
for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{ {
const ESM::Spell* spell = it->first; float rating = rateSpell(it->first, actor, enemy);
float rating = rateSpell(spell, actor, enemy);
if (rating > bestActionRating) if (rating > bestActionRating)
{ {
bestActionRating = rating; bestActionRating = rating;
bestAction.reset(new ActionSpell(spell->mId)); bestAction.reset(new ActionSpell(it->first->mId));
antiFleeRating = vanillaRateSpell(spell, actor, enemy); antiFleeRating = vanillaRateSpell(it->first, actor, enemy);
} }
} }
@ -265,11 +258,6 @@ namespace MWMechanics
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{ {
std::vector<int> equipmentSlots = it->getClass().getEquipmentSlots(*it).first;
if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight)
== equipmentSlots.end())
continue;
float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating); float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating);
if (rating > bestActionRating) if (rating > bestActionRating)
{ {
@ -280,9 +268,7 @@ namespace MWMechanics
for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{ {
const ESM::Spell* spell = it->first; float rating = rateSpell(it->first, actor, enemy);
float rating = rateSpell(spell, actor, enemy);
if (rating > bestActionRating) if (rating > bestActionRating)
{ {
bestActionRating = rating; bestActionRating = rating;
@ -336,7 +322,7 @@ namespace MWMechanics
float dist = 1.0f; float dist = 1.0f;
if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty()) if (activeWeapon.isEmpty() && !selectedSpellId.empty() && !selectedEnchItem.isEmpty())
{ {
static const float fHandToHandReach = gmst.find("fHandToHandReach")->getFloat(); static const float fHandToHandReach = gmst.find("fHandToHandReach")->mValue.getFloat();
dist = fHandToHandReach; dist = fHandToHandReach;
} }
else if (stats.getDrawState() == MWMechanics::DrawState_Spell) else if (stats.getDrawState() == MWMechanics::DrawState_Spell)
@ -375,7 +361,7 @@ namespace MWMechanics
} }
} }
static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); static const float fTargetSpellMaxSpeed = gmst.find("fTargetSpellMaxSpeed")->mValue.getFloat();
dist *= std::max(1000.0f, fTargetSpellMaxSpeed); dist *= std::max(1000.0f, fTargetSpellMaxSpeed);
} }
else if (!activeWeapon.isEmpty()) else if (!activeWeapon.isEmpty())
@ -383,7 +369,7 @@ namespace MWMechanics
const ESM::Weapon* esmWeap = activeWeapon.get<ESM::Weapon>()->mBase; const ESM::Weapon* esmWeap = activeWeapon.get<ESM::Weapon>()->mBase;
if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow) if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
{ {
static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); static const float fTargetSpellMaxSpeed = gmst.find("fProjectileMaxSpeed")->mValue.getFloat();
dist = fTargetSpellMaxSpeed; dist = fTargetSpellMaxSpeed;
if (!activeAmmo.isEmpty()) if (!activeAmmo.isEmpty())
{ {
@ -399,8 +385,8 @@ namespace MWMechanics
dist = (dist > 0.f) ? dist : 1.0f; dist = (dist > 0.f) ? dist : 1.0f;
static const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); static const float fCombatDistance = gmst.find("fCombatDistance")->mValue.getFloat();
static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->getFloat(); static const float fCombatDistanceWerewolfMod = gmst.find("fCombatDistanceWerewolfMod")->mValue.getFloat();
float combatDistance = fCombatDistance; float combatDistance = fCombatDistance;
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
@ -485,19 +471,22 @@ namespace MWMechanics
if (flee >= 100) if (flee >= 100)
return flee; return flee;
static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->getFloat(); static const float fAIFleeHealthMult = gmst.find("fAIFleeHealthMult")->mValue.getFloat();
static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->getFloat(); static const float fAIFleeFleeMult = gmst.find("fAIFleeFleeMult")->mValue.getFloat();
float healthPercentage = (stats.getHealth().getModified() == 0.0f) float healthPercentage = (stats.getHealth().getModified() == 0.0f)
? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified(); ? 1.0f : stats.getHealth().getCurrent() / stats.getHealth().getModified();
float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult; float rating = (1.0f - healthPercentage) * fAIFleeHealthMult + flee * fAIFleeFleeMult;
static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->getInt(); static const int iWereWolfLevelToAttack = gmst.find("iWereWolfLevelToAttack")->mValue.getInteger();
if (enemy.getClass().isNpc() && enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack) if (actor.getClass().isNpc() && enemy.getClass().isNpc())
{ {
static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->getInt(); if (enemy.getClass().getNpcStats(enemy).isWerewolf() && stats.getLevel() < iWereWolfLevelToAttack)
rating = iWereWolfFleeMod; {
static const int iWereWolfFleeMod = gmst.find("iWereWolfFleeMod")->mValue.getInteger();
rating = iWereWolfFleeMod;
}
} }
if (rating != 0.0f) if (rating != 0.0f)

View file

@ -1,8 +1,8 @@
#include "aisequence.hpp" #include "aisequence.hpp"
#include <limits> #include <limits>
#include <iostream>
#include <components/debug/debuglog.hpp>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -19,6 +19,7 @@
#include "aicombataction.hpp" #include "aicombataction.hpp"
#include "aipursue.hpp" #include "aipursue.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
#include "../mwworld/class.hpp"
namespace MWMechanics namespace MWMechanics
{ {
@ -122,6 +123,20 @@ bool AiSequence::isInCombat() const
return false; return false;
} }
bool AiSequence::isEngagedWithActor() const
{
for (std::list<AiPackage *>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
{
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{
MWWorld::Ptr target2 = (*it)->getTarget();
if (!target2.isEmpty() && target2.getClass().isNpc())
return true;
}
}
return false;
}
bool AiSequence::hasPackage(int typeId) const bool AiSequence::hasPackage(int typeId) const
{ {
for (std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) for (std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
@ -213,7 +228,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
{ {
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
MWWorld::Ptr target = static_cast<const AiCombat *>(*it)->getTarget(); MWWorld::Ptr target = (*it)->getTarget();
// target disappeared (e.g. summoned creatures) // target disappeared (e.g. summoned creatures)
if (target.isEmpty()) if (target.isEmpty())
@ -227,11 +242,11 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
const ESM::Position &targetPos = target.getRefData().getPosition(); const ESM::Position &targetPos = target.getRefData().getPosition();
float distTo = (targetPos.asVec3() - vActorPos).length(); float distTo = (targetPos.asVec3() - vActorPos).length2();
// Small threshold for changing target // Small threshold for changing target
if (it == mPackages.begin()) if (it == mPackages.begin())
distTo = std::max(0.f, distTo - 50.f); distTo = std::max(0.f, distTo - 2500.f);
// if a target has higher priority than current target or has same priority but closer // if a target has higher priority than current target or has same priority but closer
if (rating > bestRating || ((distTo < nearestDist) && rating == bestRating)) if (rating > bestRating || ((distTo < nearestDist) && rating == bestRating))
@ -282,7 +297,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
} }
catch (std::exception& e) catch (std::exception& e)
{ {
std::cerr << "Error during AiSequence::execute: " << e.what() << std::endl; Log(Debug::Error) << "Error during AiSequence::execute: " << e.what();
} }
} }
} }

View file

@ -88,6 +88,9 @@ namespace MWMechanics
/// Is there any combat package? /// Is there any combat package?
bool isInCombat () const; bool isInCombat () const;
/// Are we in combat with any other actor, who's also engaging us?
bool isEngagedWithActor () const;
/// Does this AI sequence have the given package type? /// Does this AI sequence have the given package type?
bool hasPackage(int typeId) const; bool hasPackage(int typeId) const;

View file

@ -1,10 +1,9 @@
#include "aiwander.hpp" #include "aiwander.hpp"
#include <cfloat> #include <cfloat>
#include <iostream>
#include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -24,8 +23,6 @@
#include "coordinateconverter.hpp" #include "coordinateconverter.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
namespace MWMechanics namespace MWMechanics
{ {
static const int COUNT_BEFORE_RESET = 10; static const int COUNT_BEFORE_RESET = 10;
@ -498,7 +495,7 @@ namespace MWMechanics
MWWorld::Ptr player = getPlayer(); MWWorld::Ptr player = getPlayer();
static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat(); .get<ESM::GameSetting>().find("fVoiceIdleOdds")->mValue.getFloat();
float roll = Misc::Rng::rollProbability() * 10000.0f; float roll = Misc::Rng::rollProbability() * 10000.0f;
@ -525,7 +522,7 @@ namespace MWMechanics
int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
float helloDistance = static_cast<float>(hello); float helloDistance = static_cast<float>(hello);
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore() static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt(); .get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
helloDistance *= iGreetDistanceMultiplier; helloDistance *= iGreetDistanceMultiplier;
@ -677,7 +674,7 @@ namespace MWMechanics
} }
else else
{ {
std::cerr<< "Error: Attempted to play out of range idle animation \""<<idleSelect<<"\" for " << actor.getCellRef().getRefId() << std::endl; Log(Debug::Verbose) << "Attempted to play out of range idle animation \"" << idleSelect << "\" for " << actor.getCellRef().getRefId();
return false; return false;
} }
} }
@ -703,7 +700,7 @@ namespace MWMechanics
for(unsigned int counter = 0; counter < mIdle.size(); counter++) for(unsigned int counter = 0; counter < mIdle.size(); counter++)
{ {
static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore() static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fIdleChanceMultiplier")->getFloat(); .get<ESM::GameSetting>().find("fIdleChanceMultiplier")->mValue.getFloat();
unsigned short idleChance = static_cast<unsigned short>(fIdleChanceMultiplier * mIdle[counter]); unsigned short idleChance = static_cast<unsigned short>(fIdleChanceMultiplier * mIdle[counter]);
unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier)); unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier));

View file

@ -140,11 +140,11 @@ void MWMechanics::Alchemy::updateEffects()
float x = getAlchemyFactor(); float x = getAlchemyFactor();
x *= mTools[ESM::Apparatus::MortarPestle].get<ESM::Apparatus>()->mBase->mData.mQuality; x *= mTools[ESM::Apparatus::MortarPestle].get<ESM::Apparatus>()->mBase->mData.mQuality;
x *= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionStrengthMult")->getFloat(); x *= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionStrengthMult")->mValue.getFloat();
// value // value
mValue = static_cast<int> ( mValue = static_cast<int> (
x * MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iAlchemyMod")->getFloat()); x * MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iAlchemyMod")->mValue.getFloat());
// build quantified effect list // build quantified effect list
for (std::set<EffectKey>::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) for (std::set<EffectKey>::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter)
@ -160,13 +160,13 @@ void MWMechanics::Alchemy::updateEffects()
} }
float fPotionT1MagMul = float fPotionT1MagMul =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionT1MagMult")->getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionT1MagMult")->mValue.getFloat();
if (fPotionT1MagMul<=0) if (fPotionT1MagMul<=0)
throw std::runtime_error ("invalid gmst: fPotionT1MagMul"); throw std::runtime_error ("invalid gmst: fPotionT1MagMul");
float fPotionT1DurMult = float fPotionT1DurMult =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionT1DurMult")->getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionT1DurMult")->mValue.getFloat();
if (fPotionT1DurMult<=0) if (fPotionT1DurMult<=0)
throw std::runtime_error ("invalid gmst: fPotionT1DurMult"); throw std::runtime_error ("invalid gmst: fPotionT1DurMult");
@ -449,7 +449,7 @@ bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWW
MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc); MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc);
int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase();
static const float fWortChanceValue = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();
return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue)
|| (potionEffectIndex <= 3 && alchemySkill >= fWortChanceValue*2) || (potionEffectIndex <= 3 && alchemySkill >= fWortChanceValue*2)
|| (potionEffectIndex <= 5 && alchemySkill >= fWortChanceValue*3) || (potionEffectIndex <= 5 && alchemySkill >= fWortChanceValue*3)
@ -503,5 +503,5 @@ std::string MWMechanics::Alchemy::suggestPotionName()
int effectId = effects.begin()->mId; int effectId = effects.begin()->mId;
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
ESM::MagicEffect::effectIdToString(effectId))->getString(); ESM::MagicEffect::effectIdToString(effectId))->mValue.getString();
} }

View file

@ -25,7 +25,7 @@ namespace MWMechanics
std::vector<std::string> autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race) std::vector<std::string> autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race)
{ {
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat(); static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat();
float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];
static const std::string schools[] = { static const std::string schools[] = {
@ -38,7 +38,7 @@ namespace MWMechanics
for (int i=0; i<6; ++i) for (int i=0; i<6; ++i)
{ {
const std::string& gmstName = "iAutoSpell" + schools[i] + "Max"; const std::string& gmstName = "iAutoSpell" + schools[i] + "Max";
iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt(); iAutoSpellSchoolMax[i] = gmst.find(gmstName)->mValue.getInteger();
} }
init = true; init = true;
} }
@ -70,7 +70,7 @@ namespace MWMechanics
continue; continue;
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc)) if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
continue; continue;
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt(); static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->mValue.getInteger();
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost) if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
continue; continue;
@ -89,7 +89,7 @@ namespace MWMechanics
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost) if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
continue; continue;
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat(); static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->mValue.getFloat();
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance) if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
continue; continue;
@ -146,7 +146,7 @@ namespace MWMechanics
{ {
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat(); static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat();
float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];
bool reachedLimit = false; bool reachedLimit = false;
@ -173,7 +173,7 @@ namespace MWMechanics
if (baseMagicka < spell->mData.mCost) if (baseMagicka < spell->mData.mCost)
continue; continue;
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat(); static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->mValue.getFloat();
if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance) if (calcAutoCastChance(spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance)
continue; continue;
@ -206,7 +206,7 @@ namespace MWMechanics
weakestSpell = spell; weakestSpell = spell;
minCost = weakestSpell->mData.mCost; minCost = weakestSpell->mData.mCost;
} }
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt(); static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->mValue.getInteger();
if (selectedSpells.size() == iAutoPCSpellMax) if (selectedSpells.size() == iAutoPCSpellMax)
reachedLimit = true; reachedLimit = true;
} }
@ -221,7 +221,7 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{ {
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID); const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->getInt(); static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->mValue.getInteger();
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)) if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
{ {
@ -278,7 +278,7 @@ namespace MWMechanics
duration = effect.mDuration; duration = effect.mDuration;
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat(); .get<ESM::GameSetting>().find("fEffectCostMult")->mValue.getFloat();
float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn)); float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));
x *= 0.1 * magicEffect->mData.mBaseCost; x *= 0.1 * magicEffect->mData.mBaseCost;

View file

@ -123,16 +123,16 @@ float getFallDamage(const MWWorld::Ptr& ptr, float fallHeight)
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat(); const float fallDistanceMin = store.find("fFallDamageDistanceMin")->mValue.getFloat();
if (fallHeight >= fallDistanceMin) if (fallHeight >= fallDistanceMin)
{ {
const float acrobaticsSkill = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Acrobatics)); const float acrobaticsSkill = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Acrobatics));
const float jumpSpellBonus = ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Jump).getMagnitude(); const float jumpSpellBonus = ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Jump).getMagnitude();
const float fallAcroBase = store.find("fFallAcroBase")->getFloat(); const float fallAcroBase = store.find("fFallAcroBase")->mValue.getFloat();
const float fallAcroMult = store.find("fFallAcroMult")->getFloat(); const float fallAcroMult = store.find("fFallAcroMult")->mValue.getFloat();
const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat(); const float fallDistanceBase = store.find("fFallDistanceBase")->mValue.getFloat();
const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat(); const float fallDistanceMult = store.find("fFallDistanceMult")->mValue.getFloat();
float x = fallHeight - fallDistanceMin; float x = fallHeight - fallDistanceMin;
x -= (1.5f * acrobaticsSkill) + jumpSpellBonus; x -= (1.5f * acrobaticsSkill) + jumpSpellBonus;
@ -411,10 +411,6 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
if(force || movement != mMovementState) if(force || movement != mMovementState)
{ {
mMovementState = movement; mMovementState = movement;
if (movement != CharState_None)
mIdleState = CharState_None;
std::string movementAnimName; std::string movementAnimName;
MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All; MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All;
const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState));
@ -531,7 +527,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character
void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force) void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force)
{ {
if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) if(force || idle != mIdleState || mIdleState == CharState_None || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty()))
{ {
mIdleState = idle; mIdleState = idle;
size_t numLoops = ~0ul; size_t numLoops = ~0ul;
@ -562,14 +558,24 @@ void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterStat
// play until the Loop Stop key 2 to 5 times, then play until the Stop key // play until the Loop Stop key 2 to 5 times, then play until the Stop key
// this replicates original engine behavior for the "Idle1h" 1st-person animation // this replicates original engine behavior for the "Idle1h" 1st-person animation
numLoops = 1 + Misc::Rng::rollDice(4); numLoops = 1 + Misc::Rng::rollDice(4);
} }
} }
mAnimation->disable(mCurrentIdle); // There is no need to restart anim if the new and old anims are the same.
// Just update a number of loops.
float startPoint = 0;
if (!mCurrentIdle.empty() && mCurrentIdle == idleGroup)
{
mAnimation->getInfo(mCurrentIdle, &startPoint);
}
if(!mCurrentIdle.empty())
mAnimation->disable(mCurrentIdle);
mCurrentIdle = idleGroup; mCurrentIdle = idleGroup;
if(!mCurrentIdle.empty()) if(!mCurrentIdle.empty())
mAnimation->play(mCurrentIdle, idlePriority, MWRender::Animation::BlendMask_All, false, mAnimation->play(mCurrentIdle, idlePriority, MWRender::Animation::BlendMask_All, false,
1.0f, "start", "stop", 0.0f, numLoops, true); 1.0f, "start", "stop", startPoint, numLoops, true);
} }
} }
@ -583,7 +589,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
refreshHitRecoilAnims(); refreshHitRecoilAnims();
const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType));
if (!mPtr.getClass().isBipedal(mPtr)) if (!mPtr.getClass().hasInventoryStore(mPtr))
weap = sWeaponTypeListEnd; weap = sWeaponTypeListEnd;
refreshJumpAnims(weap, jump, force); refreshJumpAnims(weap, jump, force);
@ -592,7 +598,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
// idle handled last as it can depend on the other states // idle handled last as it can depend on the other states
// FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
// the idle animation should be displayed // the idle animation should be displayed
if ((mUpperBodyState != UpperCharState_Nothing if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped)
|| (mMovementState != CharState_None && !isTurning()) || (mMovementState != CharState_None && !isTurning())
|| mHitState != CharState_None) || mHitState != CharState_None)
&& !mPtr.getClass().isBipedal(mPtr)) && !mPtr.getClass().isBipedal(mPtr))
@ -773,6 +779,20 @@ void CharacterController::playRandomDeath(float startpoint)
playDeath(startpoint, mDeathState); playDeath(startpoint, mDeathState);
} }
std::string CharacterController::chooseRandomAttackAnimation() const
{
std::string result;
bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
if (isSwimming)
result = chooseRandomGroup("swimattack");
if (!isSwimming || !mAnimation->hasAnimation(result))
result = chooseRandomGroup("attack");
return result;
}
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
: mPtr(ptr) : mPtr(ptr)
, mWeapon(MWWorld::Ptr()) , mWeapon(MWWorld::Ptr())
@ -918,11 +938,14 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
} }
} }
if (soundgen == "land") // Morrowind ignores land soundgen for some reason
return;
std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen);
if(!sound.empty()) if(!sound.empty())
{ {
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) if(soundgen == "left" || soundgen == "right")
{ {
// Don't make foot sounds local for the player, it makes sense to keep them // Don't make foot sounds local for the player, it makes sense to keep them
// positioned on the ground. // positioned on the ground.
@ -1123,16 +1146,10 @@ bool CharacterController::updateCreatureState()
else else
mCurrentWeapon = ""; mCurrentWeapon = "";
} }
if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation
{ {
bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr); mCurrentWeapon = chooseRandomAttackAnimation();
int roll = Misc::Rng::rollDice(3); // [0, 2]
if (roll == 0)
mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack1") ? "swimattack1" : "attack1";
else if (roll == 1)
mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack2") ? "swimattack2" : "attack2";
else
mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack3") ? "swimattack3" : "attack3";
} }
if (!mCurrentWeapon.empty()) if (!mCurrentWeapon.empty())
@ -1212,8 +1229,11 @@ bool CharacterController::updateWeaponState()
mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr(); mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr();
} }
// Use blending only with 3d-person movement animations for bipedal actors
bool firstPersonPlayer = (mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson());
MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);
priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; if (!firstPersonPlayer && mPtr.getClass().isBipedal(mPtr))
priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody;
bool forcestateupdate = false; bool forcestateupdate = false;
@ -1221,11 +1241,11 @@ bool CharacterController::updateWeaponState()
bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell && bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell &&
mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell; mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell;
if(weaptype != mWeaponType && !isKnockedOut() && if(!isKnockedOut() && !isKnockedDown() && !isRecovery())
!isKnockedDown() && !isRecovery())
{ {
std::string weapgroup; std::string weapgroup;
if ((!isWerewolf || mWeaponType != WeapType_Spell) if ((!isWerewolf || mWeaponType != WeapType_Spell)
&& weaptype != mWeaponType
&& mUpperBodyState != UpperCharState_UnEquipingWeap && mUpperBodyState != UpperCharState_UnEquipingWeap
&& !isStillWeapon) && !isStillWeapon)
{ {
@ -1239,6 +1259,10 @@ bool CharacterController::updateWeaponState()
MWRender::Animation::BlendMask_All, false, MWRender::Animation::BlendMask_All, false,
1.0f, "unequip start", "unequip stop", 0.0f, 0); 1.0f, "unequip start", "unequip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_UnEquipingWeap; mUpperBodyState = UpperCharState_UnEquipingWeap;
// If we do not have the "unequip detach" key, hide weapon manually.
if (mAnimation->getTextKeyTime(weapgroup+": unequip detach") < 0)
mAnimation->showWeapons(false);
} }
if(!downSoundId.empty()) if(!downSoundId.empty())
@ -1252,47 +1276,64 @@ bool CharacterController::updateWeaponState()
bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if (!animPlaying || complete >= 1.0f) if (!animPlaying || complete >= 1.0f)
{ {
forcestateupdate = true; // Weapon is changed, no current animation (e.g. unequipping or attack).
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype)); // Start equipping animation now.
if (weaptype != mWeaponType)
getWeaponGroup(weaptype, weapgroup);
mAnimation->setWeaponGroup(weapgroup);
if (!isStillWeapon)
{ {
if (weaptype == WeapType_None) forcestateupdate = true;
mAnimation->showCarriedLeft(updateCarriedLeftVisible(weaptype));
getWeaponGroup(weaptype, weapgroup);
mAnimation->setWeaponGroup(weapgroup);
if (!isStillWeapon)
{ {
// Disable current weapon animation manually
mAnimation->disable(mCurrentWeapon); mAnimation->disable(mCurrentWeapon);
} if (weaptype != WeapType_None)
else {
{ mAnimation->showWeapons(false);
mAnimation->showWeapons(false); mAnimation->play(weapgroup, priorityWeapon,
mAnimation->play(weapgroup, priorityWeapon, MWRender::Animation::BlendMask_All, true,
MWRender::Animation::BlendMask_All, true, 1.0f, "equip start", "equip stop", 0.0f, 0);
1.0f, "equip start", "equip stop", 0.0f, 0); mUpperBodyState = UpperCharState_EquipingWeap;
mUpperBodyState = UpperCharState_EquipingWeap;
}
}
if(isWerewolf) // If we do not have the "equip attach" key, show weapon manually.
{ if (weaptype != WeapType_Spell)
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); {
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip"); if (mAnimation->getTextKeyTime(weapgroup+": equip attach") < 0)
if(sound) mAnimation->showWeapons(true);
}
}
}
if(isWerewolf)
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip");
if(sound)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);
}
}
mWeaponType = weaptype;
getWeaponGroup(mWeaponType, mCurrentWeapon);
if(!upSoundId.empty() && !isStillWeapon)
{ {
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f); sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f);
} }
} }
mWeaponType = weaptype; // Make sure that we disabled unequipping animation
getWeaponGroup(mWeaponType, mCurrentWeapon); if (mUpperBodyState == UpperCharState_UnEquipingWeap)
if(!upSoundId.empty() && !isStillWeapon)
{ {
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); mUpperBodyState = UpperCharState_Nothing;
sndMgr->playSound3D(mPtr, upSoundId, 1.0f, 1.0f); mAnimation->disable(mCurrentWeapon);
mWeaponType = WeapType_None;
getWeaponGroup(mWeaponType, mCurrentWeapon);
} }
} }
} }
@ -1347,18 +1388,12 @@ bool CharacterController::updateWeaponState()
{ {
MWWorld::Ptr player = getPlayer(); MWWorld::Ptr player = getPlayer();
// We should reset player's idle animation in the first-person mode. bool resetIdle = ammunition;
if (mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson())
mIdleState = CharState_None;
// In other cases we should not break swim and sneak animations
if (mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
mIdleState = CharState_None;
if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block)) if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block))
{ {
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
mAttackStrength = 0; mAttackStrength = 0;
if(mWeaponType == WeapType_Spell) if(mWeaponType == WeapType_Spell)
{ {
// Unset casting flag, otherwise pressing the mouse button down would // Unset casting flag, otherwise pressing the mouse button down would
@ -1367,15 +1402,10 @@ bool CharacterController::updateWeaponState()
if (mPtr == player) if (mPtr == player)
{ {
MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);
}
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); // For the player, set the spell we want to cast
// This has to be done at the start of the casting animation,
// For the player, set the spell we want to cast // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)
// This has to be done at the start of the casting animation,
// *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)
if (mPtr == player)
{
std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell(); std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();
stats.getSpells().setSelectedSpell(selectedSpell); stats.getSpells().setSelectedSpell(selectedSpell);
} }
@ -1387,6 +1417,7 @@ bool CharacterController::updateWeaponState()
MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid); cast.playSpellCastingEffects(spellid);
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid); const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.back(); const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.back();
const ESM::MagicEffect *effect; const ESM::MagicEffect *effect;
@ -1406,19 +1437,39 @@ bool CharacterController::updateWeaponState()
const ESM::ENAMstruct &firstEffect = spell->mEffects.mList.at(0); // first effect used for casting animation const ESM::ENAMstruct &firstEffect = spell->mEffects.mList.at(0); // first effect used for casting animation
switch(firstEffect.mRange) std::string startKey;
std::string stopKey;
if (isRandomAttackAnimation(mCurrentWeapon))
{ {
case 0: mAttackType = "self"; break; startKey = "start";
case 1: mAttackType = "touch"; break; stopKey = "stop";
case 2: mAttackType = "target"; break; MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell); // No "release" text key to use, so cast immediately
mCastingManualSpell = false;
}
else
{
switch(firstEffect.mRange)
{
case 0: mAttackType = "self"; break;
case 1: mAttackType = "touch"; break;
case 2: mAttackType = "target"; break;
}
startKey = mAttackType+" start";
stopKey = mAttackType+" stop";
} }
mAnimation->play(mCurrentWeapon, priorityWeapon, mAnimation->play(mCurrentWeapon, priorityWeapon,
MWRender::Animation::BlendMask_All, true, MWRender::Animation::BlendMask_All, true,
weapSpeed, mAttackType+" start", mAttackType+" stop", 1, startKey, stopKey,
0.0f, 0); 0.0f, 0);
mUpperBodyState = UpperCharState_CastingSpell; mUpperBodyState = UpperCharState_CastingSpell;
} }
else
{
resetIdle = false;
}
if (mPtr.getClass().hasInventoryStore(mPtr)) if (mPtr.getClass().hasInventoryStore(mPtr))
{ {
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
@ -1458,16 +1509,26 @@ bool CharacterController::updateWeaponState()
} }
else if (ammunition) else if (ammunition)
{ {
if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || std::string startKey;
mWeaponType == WeapType_Thrown) std::string stopKey;
if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown)
{
mAttackType = "shoot"; mAttackType = "shoot";
startKey = mAttackType+" start";
stopKey = mAttackType+" min attack";
}
else if (isRandomAttackAnimation(mCurrentWeapon))
{
startKey = "start";
stopKey = "stop";
}
else else
{ {
if(mPtr == getPlayer()) if(mPtr == getPlayer())
{ {
if (isWeapon) if (isWeapon)
{ {
if (Settings::Manager::getBool("best attack", "Game")) if (Settings::Manager::getBool("best attack", "Game"))
{ {
MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase); mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
@ -1476,19 +1537,32 @@ bool CharacterController::updateWeaponState()
setAttackTypeBasedOnMovement(); setAttackTypeBasedOnMovement();
} }
else else
setAttackTypeRandomly(mAttackType); {
// There is no "best attack" for Hand-to-Hand
setAttackTypeRandomly(mAttackType);
}
} }
// else if (mPtr != getPlayer()) use mAttackType set by AiCombat // else if (mPtr != getPlayer()) use mAttackType set by AiCombat
startKey = mAttackType+" start";
stopKey = mAttackType+" min attack";
} }
mAnimation->play(mCurrentWeapon, priorityWeapon, mAnimation->play(mCurrentWeapon, priorityWeapon,
MWRender::Animation::BlendMask_All, false, MWRender::Animation::BlendMask_All, false,
weapSpeed, mAttackType+" start", mAttackType+" min attack", weapSpeed, startKey, stopKey,
0.0f, 0); 0.0f, 0);
mUpperBodyState = UpperCharState_StartToMinAttack; mUpperBodyState = UpperCharState_StartToMinAttack;
} }
} }
// We should reset player's idle animation in the first-person mode.
if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson())
mIdleState = CharState_None;
// In other cases we should not break swim and sneak animations
if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
mIdleState = CharState_None;
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
mAttackStrength = complete; mAttackStrength = complete;
@ -1600,16 +1674,11 @@ bool CharacterController::updateWeaponState()
else if(mUpperBodyState == UpperCharState_UnEquipingWeap) else if(mUpperBodyState == UpperCharState_UnEquipingWeap)
mUpperBodyState = UpperCharState_Nothing; mUpperBodyState = UpperCharState_Nothing;
} }
else if(complete >= 1.0f) else if(complete >= 1.0f && !isRandomAttackAnimation(mCurrentWeapon))
{ {
std::string start, stop; std::string start, stop;
switch(mUpperBodyState) switch(mUpperBodyState)
{ {
case UpperCharState_StartToMinAttack:
start = mAttackType+" min attack";
stop = mAttackType+" max attack";
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
break;
case UpperCharState_MinAttackToMaxAttack: case UpperCharState_MinAttackToMaxAttack:
//hack to avoid body pos desync when jumping/sneaking in 'max attack' state //hack to avoid body pos desync when jumping/sneaking in 'max attack' state
if(!mAnimation->isPlaying(mCurrentWeapon)) if(!mAnimation->isPlaying(mCurrentWeapon))
@ -1617,6 +1686,23 @@ bool CharacterController::updateWeaponState()
MWRender::Animation::BlendMask_All, false, MWRender::Animation::BlendMask_All, false,
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0); 0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
break; break;
case UpperCharState_StartToMinAttack:
{
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
// Happens if the player did not hold the attack button.
// Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be 1.
float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
if (mAttackingOrSpell || minAttackTime == maxAttackTime)
{
start = mAttackType+" min attack";
stop = mAttackType+" max attack";
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
break;
}
playSwishSound(0.0f);
}
// Fall-through
case UpperCharState_MaxAttackToMinHit: case UpperCharState_MaxAttackToMinHit:
if(mAttackType == "shoot") if(mAttackType == "shoot")
{ {
@ -1652,21 +1738,30 @@ bool CharacterController::updateWeaponState()
break; break;
} }
// Note: apply reload animations only for upper body since blending with movement animations can give weird result. // Note: apply crossbow reload animation only for upper body
// Especially noticable with crossbow reload animation. // since blending with movement animations can give weird result.
if(!start.empty()) if(!start.empty())
{ {
int mask = MWRender::Animation::BlendMask_All;
if (mWeaponType == WeapType_Crossbow)
mask = MWRender::Animation::BlendMask_UpperBody;
mAnimation->disable(mCurrentWeapon); mAnimation->disable(mCurrentWeapon);
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop) if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
mAnimation->play(mCurrentWeapon, priorityWeapon, mAnimation->play(mCurrentWeapon, priorityWeapon,
MWRender::Animation::BlendMask_UpperBody, true, mask, true,
weapSpeed, start, stop, 0.0f, 0); weapSpeed, start, stop, 0.0f, 0);
else else
mAnimation->play(mCurrentWeapon, priorityWeapon, mAnimation->play(mCurrentWeapon, priorityWeapon,
MWRender::Animation::BlendMask_UpperBody, false, mask, false,
weapSpeed, start, stop, 0.0f, 0); weapSpeed, start, stop, 0.0f, 0);
} }
} }
else if(complete >= 1.0f && isRandomAttackAnimation(mCurrentWeapon))
{
mAnimation->disable(mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped;
}
if (mPtr.getClass().hasInventoryStore(mPtr)) if (mPtr.getClass().hasInventoryStore(mPtr))
{ {
@ -1729,10 +1824,11 @@ void CharacterController::update(float duration)
else if(!cls.getCreatureStats(mPtr).isDead()) else if(!cls.getCreatureStats(mPtr).isDead())
{ {
bool onground = world->isOnGround(mPtr); bool onground = world->isOnGround(mPtr);
bool incapacitated = (cls.getCreatureStats(mPtr).isParalyzed() || cls.getCreatureStats(mPtr).getKnockedDown());
bool inwater = world->isSwimming(mPtr); bool inwater = world->isSwimming(mPtr);
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool flying = world->isFlying(mPtr); bool flying = world->isFlying(mPtr);
// Can't run while flying (see speed formula in Npc/Creature::getSpeed) // Can't run and sneak while flying (see speed formula in Npc/Creature::getSpeed)
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !flying;
bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying; bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying;
CreatureStats &stats = cls.getCreatureStats(mPtr); CreatureStats &stats = cls.getCreatureStats(mPtr);
@ -1811,14 +1907,14 @@ void CharacterController::update(float duration)
// reduce fatigue // reduce fatigue
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
float fatigueLoss = 0; float fatigueLoss = 0;
static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->getFloat(); static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->mValue.getFloat();
static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->getFloat(); static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->mValue.getFloat();
static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->getFloat(); static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->mValue.getFloat();
static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->getFloat(); static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->mValue.getFloat();
static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->getFloat(); static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->mValue.getFloat();
static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->getFloat(); static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->mValue.getFloat();
static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->getFloat(); static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->mValue.getFloat();
static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->getFloat(); static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->mValue.getFloat();
if (cls.getEncumbrance(mPtr) <= cls.getCapacity(mPtr)) if (cls.getEncumbrance(mPtr) <= cls.getCapacity(mPtr))
{ {
@ -1847,7 +1943,7 @@ void CharacterController::update(float duration)
cls.getCreatureStats(mPtr).setFatigue(fatigue); cls.getCreatureStats(mPtr).setFatigue(fatigue);
} }
if(sneak || inwater || flying) if(sneak || inwater || flying || incapacitated)
vec.z() = 0.0f; vec.z() = 0.0f;
bool inJump = true; bool inJump = true;
@ -1858,8 +1954,8 @@ void CharacterController::update(float duration)
forcestateupdate = (mJumpState != JumpState_InAir); forcestateupdate = (mJumpState != JumpState_InAir);
jumpstate = JumpState_InAir; jumpstate = JumpState_InAir;
static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat(); static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->mValue.getFloat();
static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat(); static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->mValue.getFloat();
float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f; float factor = fJumpMoveBase + fJumpMoveMult * mPtr.getClass().getSkill(mPtr, ESM::Skill::Acrobatics)/100.f;
factor = std::min(1.f, factor); factor = std::min(1.f, factor);
vec.x() *= factor; vec.x() *= factor;
@ -1886,8 +1982,8 @@ void CharacterController::update(float duration)
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0);
// decrease fatigue // decrease fatigue
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat(); const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat();
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat(); const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat();
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr); float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
if (normalizedEncumbrance > 1) if (normalizedEncumbrance > 1)
normalizedEncumbrance = 1; normalizedEncumbrance = 1;
@ -1916,17 +2012,15 @@ void CharacterController::update(float duration)
// inflict fall damages // inflict fall damages
if (!godmode) if (!godmode)
{ {
DynamicStat<float> health = cls.getCreatureStats(mPtr).getHealth();
float realHealthLost = static_cast<float>(healthLost * (1.0f - 0.25f * fatigueTerm)); float realHealthLost = static_cast<float>(healthLost * (1.0f - 0.25f * fatigueTerm));
health.setCurrent(health.getCurrent() - realHealthLost);
cls.getCreatureStats(mPtr).setHealth(health);
cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true);
} }
const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics);
if (healthLost > (acrobaticsSkill * fatigueTerm)) if (healthLost > (acrobaticsSkill * fatigueTerm))
{ {
cls.getCreatureStats(mPtr).setKnockedDown(true); if (!godmode)
cls.getCreatureStats(mPtr).setKnockedDown(true);
} }
else else
{ {
@ -1935,6 +2029,12 @@ void CharacterController::update(float duration)
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
} }
} }
// Play landing sound
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
std::string sound = cls.getSoundIdFromSndGen(mPtr, "land");
if (!sound.empty())
sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal);
} }
else else
{ {
@ -1944,6 +2044,12 @@ void CharacterController::update(float duration)
inJump = false; inJump = false;
// Do not play turning animation for player if rotation speed is very slow.
// Actual threshold should take framerate in account.
float rotationThreshold = 0;
if (mPtr == getPlayer())
rotationThreshold = 0.015 * 60 * duration;
if(std::abs(vec.x()/2.0f) > std::abs(vec.y())) if(std::abs(vec.x()/2.0f) > std::abs(vec.y()))
{ {
if(vec.x() > 0.0f) if(vec.x() > 0.0f)
@ -1968,26 +2074,38 @@ void CharacterController::update(float duration)
} }
else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()))
{ {
if(rot.z() > 0.0f) if(rot.z() > rotationThreshold)
{
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
mAnimation->disable(mCurrentJump); else if(rot.z() < -rotationThreshold)
}
else if(rot.z() < 0.0f)
{
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
mAnimation->disable(mCurrentJump);
}
} }
} }
mTurnAnimationThreshold -= duration; // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering
if (isTurning()) if (mPtr == getPlayer())
mTurnAnimationThreshold = 0.05f;
else if (movestate == CharState_None && isTurning()
&& mTurnAnimationThreshold > 0)
{ {
movestate = mMovementState; float threshold = mCurrentMovement.find("swim") == std::string::npos ? 0.4f : 0.8f;
float complete;
bool animPlaying = mAnimation->getInfo(mCurrentMovement, &complete);
if (movestate == CharState_None && isTurning())
{
if (animPlaying && complete < threshold)
movestate = mMovementState;
}
}
else
{
mTurnAnimationThreshold -= duration;
if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft ||
movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft)
{
mTurnAnimationThreshold = 0.05f;
}
else if (movestate == CharState_None && isTurning()
&& mTurnAnimationThreshold > 0)
{
movestate = mMovementState;
}
} }
if(movestate != CharState_None && !isTurning()) if(movestate != CharState_None && !isTurning())
@ -1995,7 +2113,16 @@ void CharacterController::update(float duration)
if(mAnimQueue.empty() || inwater || sneak) if(mAnimQueue.empty() || inwater || sneak)
{ {
idlestate = (inwater ? CharState_IdleSwim : (sneak && !inJump ? CharState_IdleSneak : CharState_Idle)); // Note: turning animations should not interrupt idle ones.
// Also movement should not stop idle animation for spellcasting stance.
if (inwater)
idlestate = CharState_IdleSwim;
else if (sneak && !inJump)
idlestate = CharState_IdleSneak;
else if (movestate != CharState_None && !isTurning() && mWeaponType != WeapType_Spell)
idlestate = CharState_None;
else
idlestate = CharState_Idle;
} }
else else
updateAnimQueue(); updateAnimQueue();
@ -2017,8 +2144,10 @@ void CharacterController::update(float duration)
if (isTurning()) if (isTurning())
{ {
// Adjust animation speed from 1.0 to 1.5 multiplier
float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast<float>(osg::PI));
if (duration > 0) if (duration > 0)
mAnimation->adjustSpeedMult(mCurrentMovement, std::min(1.5f, std::abs(rot.z()) / duration / static_cast<float>(osg::PI))); mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f));
} }
else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed)
{ {
@ -2095,9 +2224,6 @@ void CharacterController::update(float duration)
moved *= (l / newLength); moved *= (l / newLength);
} }
if (mSkipAnim)
mAnimation->updateEffects(duration);
if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr)) if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr))
moved.z() = 1.0; moved.z() = 1.0;
@ -2385,7 +2511,14 @@ void CharacterController::setAttackTypeBasedOnMovement()
mAttackType = "chop"; mAttackType = "chop";
} }
bool CharacterController::isAttackPrepairing() const bool CharacterController::isRandomAttackAnimation(const std::string& group) const
{
return (group == "attack1" || group == "swimattack1" ||
group == "attack2" || group == "swimattack2" ||
group == "attack3" || group == "swimattack3");
}
bool CharacterController::isAttackPreparing() const
{ {
return mUpperBodyState == UpperCharState_StartToMinAttack || return mUpperBodyState == UpperCharState_StartToMinAttack ||
mUpperBodyState == UpperCharState_MinAttackToMaxAttack; mUpperBodyState == UpperCharState_MinAttackToMaxAttack;
@ -2486,7 +2619,7 @@ void CharacterController::setAttackTypeRandomly(std::string& attackType)
bool CharacterController::readyToPrepareAttack() const bool CharacterController::readyToPrepareAttack() const
{ {
return (mHitState == CharState_None || mHitState == CharState_Block) return (mHitState == CharState_None || mHitState == CharState_Block)
&& mUpperBodyState <= UpperCharState_WeapEquiped; && mUpperBodyState <= UpperCharState_WeapEquiped;
} }
bool CharacterController::readyToStartAttack() const bool CharacterController::readyToStartAttack() const

View file

@ -31,8 +31,10 @@ enum Priority {
Priority_WeaponLowerBody, Priority_WeaponLowerBody,
Priority_SneakIdleLowerBody, Priority_SneakIdleLowerBody,
Priority_SwimIdle, Priority_SwimIdle,
Priority_Jump,
Priority_Movement, Priority_Movement,
// Note: in vanilla movement anims have higher priority than jump ones.
// It causes issues with landing animations during movement.
Priority_Jump,
Priority_Hit, Priority_Hit,
Priority_Weapon, Priority_Weapon,
Priority_Block, Priority_Block,
@ -222,6 +224,9 @@ class CharacterController : public MWRender::Animation::TextKeyListener
bool updateCreatureState(); bool updateCreatureState();
void updateIdleStormState(bool inwater); void updateIdleStormState(bool inwater);
std::string chooseRandomAttackAnimation() const;
bool isRandomAttackAnimation(const std::string& group) const;
bool isPersistentAnimPlaying(); bool isPersistentAnimPlaying();
void updateAnimQueue(); void updateAnimQueue();
@ -276,7 +281,7 @@ public:
void forceStateUpdate(); void forceStateUpdate();
bool isAttackPrepairing() const; bool isAttackPreparing() const;
bool isCastingSpell() const; bool isCastingSpell() const;
bool isReadyToBlock() const; bool isReadyToBlock() const;
bool isKnockedDown() const; bool isKnockedDown() const;

View file

@ -82,9 +82,9 @@ namespace MWMechanics
osg::Vec3f(0,0,1))); osg::Vec3f(0,0,1)));
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (angleDegrees < gmst.find("fCombatBlockLeftAngle")->getFloat()) if (angleDegrees < gmst.find("fCombatBlockLeftAngle")->mValue.getFloat())
return false; return false;
if (angleDegrees > gmst.find("fCombatBlockRightAngle")->getFloat()) if (angleDegrees > gmst.find("fCombatBlockRightAngle")->mValue.getFloat())
return false; return false;
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
@ -92,11 +92,11 @@ namespace MWMechanics
float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2f * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2f * blockerStats.getAttribute(ESM::Attribute::Agility).getModified()
+ 0.1f * blockerStats.getAttribute(ESM::Attribute::Luck).getModified(); + 0.1f * blockerStats.getAttribute(ESM::Attribute::Luck).getModified();
float enemySwing = attackStrength; float enemySwing = attackStrength;
float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->getFloat() + gmst.find("fSwingBlockBase")->getFloat(); float swingTerm = enemySwing * gmst.find("fSwingBlockMult")->mValue.getFloat() + gmst.find("fSwingBlockBase")->mValue.getFloat();
float blockerTerm = blockTerm * swingTerm; float blockerTerm = blockTerm * swingTerm;
if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0) if (blocker.getClass().getMovementSettings(blocker).mPosition[1] <= 0)
blockerTerm *= gmst.find("fBlockStillBonus")->getFloat(); blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat();
blockerTerm *= blockerStats.getFatigueTerm(); blockerTerm *= blockerStats.getFatigueTerm();
int attackerSkill = 0; int attackerSkill = 0;
@ -109,8 +109,8 @@ namespace MWMechanics
attackerTerm *= attackerStats.getFatigueTerm(); attackerTerm *= attackerStats.getFatigueTerm();
int x = int(blockerTerm - attackerTerm); int x = int(blockerTerm - attackerTerm);
int iBlockMaxChance = gmst.find("iBlockMaxChance")->getInt(); int iBlockMaxChance = gmst.find("iBlockMaxChance")->mValue.getInteger();
int iBlockMinChance = gmst.find("iBlockMinChance")->getInt(); int iBlockMinChance = gmst.find("iBlockMinChance")->mValue.getInteger();
x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x)); x = std::min(iBlockMaxChance, std::max(iBlockMinChance, x));
if (Misc::Rng::roll0to99() < x) if (Misc::Rng::roll0to99() < x)
@ -126,9 +126,9 @@ namespace MWMechanics
inv.unequipItem(*shield, blocker); inv.unequipItem(*shield, blocker);
} }
// Reduce blocker fatigue // Reduce blocker fatigue
const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat(); const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->mValue.getFloat();
const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->mValue.getFloat();
const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->mValue.getFloat();
MWMechanics::DynamicStat<float> fatigue = blockerStats.getFatigue(); MWMechanics::DynamicStat<float> fatigue = blockerStats.getFatigue();
float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker); float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker);
normalizedEncumbrance = std::min(1.f, normalizedEncumbrance); normalizedEncumbrance = std::min(1.f, normalizedEncumbrance);
@ -166,7 +166,7 @@ namespace MWMechanics
if ((weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver) if ((weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver)
&& actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) && actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
damage *= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWereWolfSilverWeaponDamageMult")->getFloat(); damage *= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWereWolfSilverWeaponDamageMult")->mValue.getFloat();
if (damage == 0 && attacker == getPlayer()) if (damage == 0 && attacker == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}");
@ -209,11 +209,24 @@ namespace MWMechanics
adjustWeaponDamage(damage, weapon, attacker); adjustWeaponDamage(damage, weapon, attacker);
if(attacker == getPlayer()) if (attacker == getPlayer())
{
attacker.getClass().skillUsageSucceeded(attacker, weaponSkill, 0); attacker.getClass().skillUsageSucceeded(attacker, weaponSkill, 0);
const MWMechanics::AiSequence& sequence = victim.getClass().getCreatureStats(victim).getAiSequence();
bool unaware = !sequence.isInCombat()
&& !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim);
if (unaware)
{
damage *= gmst.find("fCombatCriticalStrikeMult")->mValue.getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
}
if (victim.getClass().getCreatureStats(victim).getKnockedDown()) if (victim.getClass().getCreatureStats(victim).getKnockedDown())
damage *= gmst.find("fCombatKODamageMult")->getFloat(); damage *= gmst.find("fCombatKODamageMult")->mValue.getFloat();
} }
reduceWeaponCondition(damage, validVictim, weapon, attacker); reduceWeaponCondition(damage, validVictim, weapon, attacker);
@ -228,7 +241,7 @@ namespace MWMechanics
// Non-enchanted arrows shot at enemies have a chance to turn up in their inventory // Non-enchanted arrows shot at enemies have a chance to turn up in their inventory
if (victim != getPlayer() && !appliedEnchantment) if (victim != getPlayer() && !appliedEnchantment)
{ {
float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->mValue.getFloat();
if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f) if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f)
victim.getClass().getContainerStore(victim).add(projectile, 1, victim); victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
} }
@ -260,10 +273,10 @@ namespace MWMechanics
defenseTerm = victimStats.getEvasion(); defenseTerm = victimStats.getEvasion();
} }
defenseTerm += std::min(100.f, defenseTerm += std::min(100.f,
gmst.find("fCombatInvisoMult")->getFloat() * gmst.find("fCombatInvisoMult")->mValue.getFloat() *
victimStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude()); victimStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude());
defenseTerm += std::min(100.f, defenseTerm += std::min(100.f,
gmst.find("fCombatInvisoMult")->getFloat() * gmst.find("fCombatInvisoMult")->mValue.getFloat() *
victimStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude()); victimStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude());
} }
float attackTerm = skillValue + float attackTerm = skillValue +
@ -309,7 +322,7 @@ namespace MWMechanics
x = std::min(100.f, x + elementResistance); x = std::min(100.f, x + elementResistance);
static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fElementalShieldMult")->getFloat(); static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fElementalShieldMult")->mValue.getFloat();
x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); x = fElementalShieldMult * magnitude * (1.f - 0.01f * x);
// Note swapped victim and attacker, since the attacker takes the damage here. // Note swapped victim and attacker, since the attacker takes the damage here.
@ -339,7 +352,7 @@ namespace MWMechanics
// weapon condition does not degrade when godmode is on // weapon condition does not degrade when godmode is on
if (!godmode) if (!godmode)
{ {
const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWeaponDamageMult")->getFloat(); const float fWeaponDamageMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWeaponDamageMult")->mValue.getFloat();
float x = std::max(1.f, fWeaponDamageMult * damage); float x = std::max(1.f, fWeaponDamageMult * damage);
weaphealth -= std::min(int(x), weaphealth); weaphealth -= std::min(int(x), weaphealth);
@ -366,21 +379,18 @@ namespace MWMechanics
} }
static const float fDamageStrengthBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float fDamageStrengthBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fDamageStrengthBase")->getFloat(); .find("fDamageStrengthBase")->mValue.getFloat();
static const float fDamageStrengthMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float fDamageStrengthMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fDamageStrengthMult")->getFloat(); .find("fDamageStrengthMult")->mValue.getFloat();
damage *= fDamageStrengthBase + damage *= fDamageStrengthBase +
(attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1f); (attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1f);
} }
void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength) void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength)
{ {
// Note: MCP contains an option to include Strength in hand-to-hand damage
// calculations. Some mods recommend using it, so we may want to include an
// option for it.
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
float minstrike = store.get<ESM::GameSetting>().find("fMinHandToHandMult")->getFloat(); float minstrike = store.get<ESM::GameSetting>().find("fMinHandToHandMult")->mValue.getFloat();
float maxstrike = store.get<ESM::GameSetting>().find("fMaxHandToHandMult")->getFloat(); float maxstrike = store.get<ESM::GameSetting>().find("fMaxHandToHandMult")->mValue.getFloat();
damage = static_cast<float>(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage = static_cast<float>(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand));
damage *= minstrike + ((maxstrike-minstrike)*attackStrength); damage *= minstrike + ((maxstrike-minstrike)*attackStrength);
@ -388,6 +398,16 @@ namespace MWMechanics
healthdmg = otherstats.isParalyzed() healthdmg = otherstats.isParalyzed()
|| otherstats.getKnockedDown(); || otherstats.getKnockedDown();
bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()); bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf());
// Options in the launcher's combo box: unarmedFactorsStrengthComboBox
// 0 = Do not factor strength into hand-to-hand combat.
// 1 = Factor into werewolf hand-to-hand combat.
// 2 = Ignore werewolves.
int factorStrength = Settings::Manager::getInt("strength influences hand to hand", "Game");
if (factorStrength == 1 || (factorStrength == 2 && !isWerewolf)) {
damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f;
}
if(isWerewolf) if(isWerewolf)
{ {
healthdmg = true; healthdmg = true;
@ -395,7 +415,7 @@ namespace MWMechanics
damage *= MWBase::Environment::get().getWorld()->getGlobalFloat("werewolfclawmult"); damage *= MWBase::Environment::get().getWorld()->getGlobalFloat("werewolfclawmult");
} }
if(healthdmg) if(healthdmg)
damage *= store.get<ESM::GameSetting>().find("fHandtoHandHealthPer")->getFloat(); damage *= store.get<ESM::GameSetting>().find("fHandtoHandHealthPer")->mValue.getFloat();
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(isWerewolf) if(isWerewolf)
@ -412,9 +432,9 @@ namespace MWMechanics
{ {
// somewhat of a guess, but using the weapon weight makes sense // somewhat of a guess, but using the weapon weight makes sense
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat(); const float fFatigueAttackBase = store.find("fFatigueAttackBase")->mValue.getFloat();
const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat(); const float fFatigueAttackMult = store.find("fFatigueAttackMult")->mValue.getFloat();
const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat(); const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->mValue.getFloat();
CreatureStats& stats = attacker.getClass().getCreatureStats(attacker); CreatureStats& stats = attacker.getClass().getCreatureStats(attacker);
MWMechanics::DynamicStat<float> fatigue = stats.getFatigue(); MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();
const float normalizedEncumbrance = attacker.getClass().getNormalizedEncumbrance(attacker); const float normalizedEncumbrance = attacker.getClass().getNormalizedEncumbrance(attacker);
@ -439,9 +459,9 @@ namespace MWMechanics
float d = (pos1 - pos2).length(); float d = (pos1 - pos2).length();
static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( static const int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"iFightDistanceBase")->getInt(); "iFightDistanceBase")->mValue.getInteger();
static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( static const float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDistanceMultiplier")->getFloat(); "fFightDistanceMultiplier")->mValue.getFloat();
return (iFightDistanceBase - fFightDistanceMultiplier * d); return (iFightDistanceBase - fFightDistanceMultiplier * d);
} }

View file

@ -48,8 +48,8 @@ namespace MWMechanics
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fFatigueBase = gmst.find("fFatigueBase")->getFloat(); static const float fFatigueBase = gmst.find("fFatigueBase")->mValue.getFloat();
static const float fFatigueMult = gmst.find("fFatigueMult")->getFloat(); static const float fFatigueMult = gmst.find("fFatigueMult")->mValue.getFloat();
return fFatigueBase - fFatigueMult * (1-normalised); return fFatigueBase - fFatigueMult * (1-normalised);
} }
@ -195,6 +195,7 @@ namespace MWMechanics
mDead = true; mDead = true;
mDynamic[index].setModifier(0); mDynamic[index].setModifier(0);
mDynamic[index].setCurrentModifier(0);
mDynamic[index].setCurrent(0); mDynamic[index].setCurrent(0);
if (MWBase::Environment::get().getWorld()->getGodModeState()) if (MWBase::Environment::get().getWorld()->getGodModeState())

View file

@ -12,10 +12,12 @@ float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr
{ {
const MWWorld::Ptr& player = MWMechanics::getPlayer(); const MWWorld::Ptr& player = MWMechanics::getPlayer();
// [-100, 100] // [-500, 500]
int difficultySetting = Settings::Manager::getInt("difficulty", "Game"); int difficultySetting = Settings::Manager::getInt("difficulty", "Game");
difficultySetting = std::min(difficultySetting, 500);
difficultySetting = std::max(difficultySetting, -500);
static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDifficultyMult")->getFloat(); static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDifficultyMult")->mValue.getFloat();
float difficultyTerm = 0.01f * difficultySetting; float difficultyTerm = 0.01f * difficultySetting;

View file

@ -28,7 +28,7 @@ namespace MWMechanics
float fDiseaseXferChance = float fDiseaseXferChance =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fDiseaseXferChance")->getFloat(); "fDiseaseXferChance")->mValue.getFloat();
MagicEffects& actorEffects = actor.getClass().getCreatureStats(actor).getMagicEffects(); MagicEffects& actorEffects = actor.getClass().getCreatureStats(actor).getMagicEffects();
@ -59,7 +59,7 @@ namespace MWMechanics
actor.getClass().getCreatureStats(actor).getSpells().add(it->first); actor.getClass().getCreatureStats(actor).getSpells().add(it->first);
std::string msg = "sMagicContractDisease"; std::string msg = "sMagicContractDisease";
msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(msg)->getString(); msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(msg)->mValue.getString();
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, spell->mName); msg.replace(msg.find("%s"), 2, spell->mName);
MWBase::Environment::get().getWindowManager()->messageBox(msg); MWBase::Environment::get().getWindowManager()->messageBox(msg);

View file

@ -107,7 +107,7 @@ namespace MWMechanics
} }
const bool powerfulSoul = getGemCharge() >= \ const bool powerfulSoul = getGemCharge() >= \
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->mValue.getInteger();
if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name())) if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name()))
{ // Armor or Clothing { // Armor or Clothing
switch(mCastStyle) switch(mCastStyle)
@ -184,7 +184,7 @@ namespace MWMechanics
float magnitudeCost = (magMin + magMax) * baseCost * 0.05f; float magnitudeCost = (magMin + magMax) * baseCost * 0.05f;
if (mCastStyle == ESM::Enchantment::ConstantEffect) if (mCastStyle == ESM::Enchantment::ConstantEffect)
{ {
magnitudeCost *= store.get<ESM::GameSetting>().find("fEnchantmentConstantDurationMult")->getFloat(); magnitudeCost *= store.get<ESM::GameSetting>().find("fEnchantmentConstantDurationMult")->mValue.getFloat();
} }
else else
{ {
@ -193,7 +193,7 @@ namespace MWMechanics
float areaCost = area * 0.05f * baseCost; float areaCost = area * 0.05f * baseCost;
const float fEffectCostMult = store.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat(); const float fEffectCostMult = store.get<ESM::GameSetting>().find("fEffectCostMult")->mValue.getFloat();
cost += (magnitudeCost + areaCost) * fEffectCostMult; cost += (magnitudeCost + areaCost) * fEffectCostMult;
@ -230,7 +230,7 @@ namespace MWMechanics
if(mEnchanter.isEmpty()) if(mEnchanter.isEmpty())
return 0; return 0;
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->getFloat(); float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->mValue.getFloat();
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true);
return price; return price;
} }
@ -256,7 +256,7 @@ namespace MWMechanics
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
return static_cast<int>(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get<ESM::GameSetting>().find("fEnchantmentMult")->getFloat()); return static_cast<int>(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get<ESM::GameSetting>().find("fEnchantmentMult")->mValue.getFloat());
} }
bool Enchanting::soulEmpty() const bool Enchanting::soulEmpty() const
{ {
@ -288,8 +288,8 @@ namespace MWMechanics
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
float chance2 = 7.5f / (gmst.find("fEnchantmentChanceMult")->getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ? float chance2 = 7.5f / (gmst.find("fEnchantmentChanceMult")->mValue.getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ?
gmst.find("fEnchantmentConstantChanceMult")->getFloat() : 1.0f )) gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat() : 1.0f ))
* getEnchantPoints(); * getEnchantPoints();
return (chance1-chance2); return (chance1-chance2);

View file

@ -1,8 +1,7 @@
#ifndef OPENMW_MECHANICS_LEVELLEDLIST_H #ifndef OPENMW_MECHANICS_LEVELLEDLIST_H
#define OPENMW_MECHANICS_LEVELLEDLIST_H #define OPENMW_MECHANICS_LEVELLEDLIST_H
#include <iostream> #include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -63,7 +62,7 @@ namespace MWMechanics
// Vanilla doesn't fail on nonexistent items in levelled lists // Vanilla doesn't fail on nonexistent items in levelled lists
if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item))) if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item)))
{ {
std::cerr << "Warning: ignoring nonexistent item '" << item << "' in levelled list '" << levItem->mId << "'" << std::endl; Log(Debug::Warning) << "Warning: ignoring nonexistent item '" << item << "' in levelled list '" << levItem->mId << "'";
return std::string(); return std::string();
} }

View file

@ -1,6 +1,7 @@
#include "mechanicsmanagerimp.hpp" #include "mechanicsmanagerimp.hpp"
#include <limits.h> #include <limits.h>
#include <set>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
@ -35,7 +36,7 @@ namespace
float getFightDispositionBias(float disposition) float getFightDispositionBias(float disposition)
{ {
static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find( static const float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fFightDispMult")->getFloat(); "fFightDispMult")->mValue.getFloat();
return ((50.f - disposition) * fFightDispMult); return ((50.f - disposition) * fFightDispMult);
} }
@ -44,11 +45,11 @@ namespace
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->getFloat(); float persTerm = stats.getAttribute(ESM::Attribute::Personality).getModified() / gmst.find("fPersonalityMod")->mValue.getFloat();
float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->getFloat(); float luckTerm = stats.getAttribute(ESM::Attribute::Luck).getModified() / gmst.find("fLuckMod")->mValue.getFloat();
float repTerm = stats.getReputation() * gmst.find("fReputationMod")->getFloat(); float repTerm = stats.getReputation() * gmst.find("fReputationMod")->mValue.getFloat();
float fatigueTerm = stats.getFatigueTerm(); float fatigueTerm = stats.getFatigueTerm();
float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->getFloat(); float levelTerm = stats.getLevel() * gmst.find("fLevelMod")->mValue.getFloat();
rating1 = (repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; rating1 = (repTerm + luckTerm + persTerm + stats.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm;
@ -341,7 +342,7 @@ namespace MWMechanics
if(timeToDrown != mWatchedTimeToStartDrowning) if(timeToDrown != mWatchedTimeToStartDrowning)
{ {
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fHoldBreathTime")->getFloat(); .find("fHoldBreathTime")->mValue.getFloat();
mWatchedTimeToStartDrowning = timeToDrown; mWatchedTimeToStartDrowning = timeToDrown;
@ -435,9 +436,9 @@ namespace MWMechanics
return mActors.isActorDetected(actor, observer); return mActors.isActorDetected(actor, observer);
} }
bool MechanicsManager::isAttackPrepairing(const MWWorld::Ptr& ptr) bool MechanicsManager::isAttackPreparing(const MWWorld::Ptr& ptr)
{ {
return mActors.isAttackPrepairing(ptr); return mActors.isAttackPreparing(ptr);
} }
bool MechanicsManager::isRunning(const MWWorld::Ptr& ptr) bool MechanicsManager::isRunning(const MWWorld::Ptr& ptr)
@ -542,12 +543,12 @@ namespace MWMechanics
const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr); const MWMechanics::NpcStats &playerStats = playerPtr.getClass().getNpcStats(playerPtr);
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fDispRaceMod = gmst.find("fDispRaceMod")->getFloat(); static const float fDispRaceMod = gmst.find("fDispRaceMod")->mValue.getFloat();
if (Misc::StringUtils::ciEqual(npc->mBase->mRace, player->mBase->mRace)) if (Misc::StringUtils::ciEqual(npc->mBase->mRace, player->mBase->mRace))
x += fDispRaceMod; x += fDispRaceMod;
static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->getFloat(); static const float fDispPersonalityMult = gmst.find("fDispPersonalityMult")->mValue.getFloat();
static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->getFloat(); static const float fDispPersonalityBase = gmst.find("fDispPersonalityBase")->mValue.getFloat();
x += fDispPersonalityMult * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - fDispPersonalityBase); x += fDispPersonalityMult * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - fDispPersonalityBase);
float reaction = 0; float reaction = 0;
@ -591,20 +592,20 @@ namespace MWMechanics
rank = 0; rank = 0;
} }
static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->getFloat(); static const float fDispFactionRankMult = gmst.find("fDispFactionRankMult")->mValue.getFloat();
static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->getFloat(); static const float fDispFactionRankBase = gmst.find("fDispFactionRankBase")->mValue.getFloat();
static const float fDispFactionMod = gmst.find("fDispFactionMod")->getFloat(); static const float fDispFactionMod = gmst.find("fDispFactionMod")->mValue.getFloat();
x += (fDispFactionRankMult * rank x += (fDispFactionRankMult * rank
+ fDispFactionRankBase) + fDispFactionRankBase)
* fDispFactionMod * reaction; * fDispFactionMod * reaction;
static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->getFloat(); static const float fDispCrimeMod = gmst.find("fDispCrimeMod")->mValue.getFloat();
static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->getFloat(); static const float fDispDiseaseMod = gmst.find("fDispDiseaseMod")->mValue.getFloat();
x -= fDispCrimeMod * playerStats.getBounty(); x -= fDispCrimeMod * playerStats.getBounty();
if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease()) if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease())
x += fDispDiseaseMod; x += fDispDiseaseMod;
static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->getFloat(); static const float fDispWeaponDrawn = gmst.find("fDispWeaponDrawn")->mValue.getFloat();
if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon)
x += fDispWeaponDrawn; x += fDispWeaponDrawn;
@ -672,16 +673,16 @@ namespace MWMechanics
float target2 = d * (playerRating2 - npcRating2 + 50); float target2 = d * (playerRating2 - npcRating2 + 50);
float bribeMod; float bribeMod;
if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->mValue.getFloat();
else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->mValue.getFloat();
else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); else bribeMod = gmst.find("fBribe1000Mod")->mValue.getFloat();
float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod;
float iPerMinChance = floor(gmst.find("iPerMinChance")->getFloat()); float iPerMinChance = floor(gmst.find("iPerMinChance")->mValue.getFloat());
float iPerMinChange = floor(gmst.find("iPerMinChange")->getFloat()); float iPerMinChange = floor(gmst.find("iPerMinChange")->mValue.getFloat());
float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat(); float fPerDieRollMult = gmst.find("fPerDieRollMult")->mValue.getFloat();
float fPerTempMult = gmst.find("fPerTempMult")->getFloat(); float fPerTempMult = gmst.find("fPerTempMult")->mValue.getFloat();
float x = 0; float x = 0;
float y = 0; float y = 0;
@ -866,7 +867,7 @@ namespace MWMechanics
continue; continue;
// All sMagicBound* GMST's should be of type string // All sMagicBound* GMST's should be of type string
std::string currentGMSTValue = currentSetting.getString(); std::string currentGMSTValue = currentSetting.mValue.getString();
Misc::StringUtils::lowerCaseInPlace(currentGMSTValue); Misc::StringUtils::lowerCaseInPlace(currentGMSTValue);
boundItemIDCache.insert(currentGMSTValue); boundItemIDCache.insert(currentGMSTValue);
@ -1162,7 +1163,7 @@ namespace MWMechanics
osg::Vec3f from (player.getRefData().getPosition().asVec3()); osg::Vec3f from (player.getRefData().getPosition().asVec3());
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat(); float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->mValue.getFloat();
mActors.getObjectsInRange(from, radius, neighbors); mActors.getObjectsInRange(from, radius, neighbors);
@ -1260,29 +1261,29 @@ namespace MWMechanics
float disp = 0.f, dispVictim = 0.f; float disp = 0.f, dispVictim = 0.f;
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
{ {
arg = store.find("iCrimeTresspass")->getInt(); arg = store.find("iCrimeTresspass")->mValue.getInteger();
disp = dispVictim = store.find("iDispTresspass")->getFloat(); disp = dispVictim = store.find("iDispTresspass")->mValue.getFloat();
} }
else if (type == OT_Pickpocket) else if (type == OT_Pickpocket)
{ {
arg = store.find("iCrimePickPocket")->getInt(); arg = store.find("iCrimePickPocket")->mValue.getInteger();
disp = dispVictim = store.find("fDispPickPocketMod")->getFloat(); disp = dispVictim = store.find("fDispPickPocketMod")->mValue.getFloat();
} }
else if (type == OT_Assault) else if (type == OT_Assault)
{ {
arg = store.find("iCrimeAttack")->getInt(); arg = store.find("iCrimeAttack")->mValue.getInteger();
disp = store.find("iDispAttackMod")->getFloat(); disp = store.find("iDispAttackMod")->mValue.getFloat();
dispVictim = store.find("fDispAttacking")->getFloat(); dispVictim = store.find("fDispAttacking")->mValue.getFloat();
} }
else if (type == OT_Murder) else if (type == OT_Murder)
{ {
arg = store.find("iCrimeKilling")->getInt(); arg = store.find("iCrimeKilling")->mValue.getInteger();
disp = dispVictim = store.find("iDispKilling")->getFloat(); disp = dispVictim = store.find("iDispKilling")->mValue.getFloat();
} }
else if (type == OT_Theft) else if (type == OT_Theft)
{ {
disp = dispVictim = store.find("fDispStealing")->getFloat() * arg; disp = dispVictim = store.find("fDispStealing")->mValue.getFloat() * arg;
arg = static_cast<int>(arg * store.find("fCrimeStealing")->getFloat()); arg = static_cast<int>(arg * store.find("fCrimeStealing")->mValue.getFloat());
arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen
} }
@ -1292,7 +1293,7 @@ namespace MWMechanics
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
osg::Vec3f from (player.getRefData().getPosition().asVec3()); osg::Vec3f from (player.getRefData().getPosition().asVec3());
float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->getFloat(); float radius = esmStore.get<ESM::GameSetting>().find("fAlarmRadius")->mValue.getFloat();
mActors.getObjectsInRange(from, radius, neighbors); mActors.getObjectsInRange(from, radius, neighbors);
@ -1306,21 +1307,21 @@ namespace MWMechanics
// Controls whether witnesses will engage combat with the criminal. // Controls whether witnesses will engage combat with the criminal.
int fight = 0, fightVictim = 0; int fight = 0, fightVictim = 0;
if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) if (type == OT_Trespassing || type == OT_SleepingInOwnedBed)
fight = fightVictim = esmStore.get<ESM::GameSetting>().find("iFightTrespass")->getInt(); fight = fightVictim = esmStore.get<ESM::GameSetting>().find("iFightTrespass")->mValue.getInteger();
else if (type == OT_Pickpocket) else if (type == OT_Pickpocket)
{ {
fight = esmStore.get<ESM::GameSetting>().find("iFightPickpocket")->getInt(); fight = esmStore.get<ESM::GameSetting>().find("iFightPickpocket")->mValue.getInteger();
fightVictim = esmStore.get<ESM::GameSetting>().find("iFightPickpocket")->getInt() * 4; // *4 according to research wiki fightVictim = esmStore.get<ESM::GameSetting>().find("iFightPickpocket")->mValue.getInteger() * 4; // *4 according to research wiki
} }
else if (type == OT_Assault) else if (type == OT_Assault)
{ {
fight = esmStore.get<ESM::GameSetting>().find("iFightAttacking")->getInt(); fight = esmStore.get<ESM::GameSetting>().find("iFightAttacking")->mValue.getInteger();
fightVictim = esmStore.get<ESM::GameSetting>().find("iFightAttack")->getInt(); fightVictim = esmStore.get<ESM::GameSetting>().find("iFightAttack")->mValue.getInteger();
} }
else if (type == OT_Murder) else if (type == OT_Murder)
fight = fightVictim = esmStore.get<ESM::GameSetting>().find("iFightKilling")->getInt(); fight = fightVictim = esmStore.get<ESM::GameSetting>().find("iFightKilling")->mValue.getInteger();
else if (type == OT_Theft) else if (type == OT_Theft)
fight = fightVictim = esmStore.get<ESM::GameSetting>().find("fFightStealing")->getInt(); fight = fightVictim = esmStore.get<ESM::GameSetting>().find("fFightStealing")->mValue.getInteger();
bool reported = false; bool reported = false;
@ -1445,11 +1446,12 @@ namespace MWMechanics
if (target == getPlayer() || !attacker.getClass().isActor()) if (target == getPlayer() || !attacker.getClass().isActor())
return false; return false;
std::list<MWWorld::Ptr> followersAttacker = getActorsSidingWith(attacker); std::set<MWWorld::Ptr> followersAttacker;
getActorsSidingWith(attacker, followersAttacker);
MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target); MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target);
if (std::find(followersAttacker.begin(), followersAttacker.end(), target) != followersAttacker.end()) if (followersAttacker.find(target) != followersAttacker.end())
{ {
statsTarget.friendlyHit(); statsTarget.friendlyHit();
@ -1460,24 +1462,11 @@ namespace MWMechanics
} }
} }
// Attacking an NPC that is already in combat with any other NPC is not a crime if (canCommitCrimeAgainst(target, attacker))
AiSequence& seq = statsTarget.getAiSequence();
bool isFightingNpc = false;
for (std::list<AiPackage*>::const_iterator it = seq.begin(); it != seq.end(); ++it)
{
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{
MWWorld::Ptr target2 = (*it)->getTarget();
if (!target2.isEmpty() && target2.getClass().isNpc())
isFightingNpc = true;
}
}
if (target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker)
&& !isAggressive(target, attacker) && !isFightingNpc
&& !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue))
commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault); commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault);
AiSequence& seq = statsTarget.getAiSequence();
if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target)
|| attacker == getPlayer()) || attacker == getPlayer())
&& !seq.isInCombat(attacker)) && !seq.isInCombat(attacker))
@ -1504,6 +1493,14 @@ namespace MWMechanics
return true; return true;
} }
bool MechanicsManager::canCommitCrimeAgainst(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker)
{
MWMechanics::AiSequence seq = target.getClass().getCreatureStats(target).getAiSequence();
return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker)
&& !isAggressive(target, attacker) && !seq.isEngagedWithActor()
&& !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue);
}
void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker) void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker)
{ {
if (attacker.isEmpty() || victim.isEmpty()) if (attacker.isEmpty() || victim.isEmpty())
@ -1516,11 +1513,10 @@ namespace MWMechanics
return; // TODO: implement animal rights return; // TODO: implement animal rights
const MWMechanics::NpcStats& victimStats = victim.getClass().getNpcStats(victim); const MWMechanics::NpcStats& victimStats = victim.getClass().getNpcStats(victim);
if (victimStats.getCrimeId() == -1) const MWWorld::Ptr &player = getPlayer();
return; bool canCommit = attacker == player && canCommitCrimeAgainst(victim, attacker);
// For now we report only about crimes of player and player's followers // For now we report only about crimes of player and player's followers
const MWWorld::Ptr &player = getPlayer();
if (attacker != player) if (attacker != player)
{ {
std::set<MWWorld::Ptr> playerFollowers; std::set<MWWorld::Ptr> playerFollowers;
@ -1529,6 +1525,9 @@ namespace MWMechanics
return; return;
} }
if (!canCommit && victimStats.getCrimeId() == -1)
return;
// Simple check for who attacked first: if the player attacked first, a crimeId should be set // Simple check for who attacked first: if the player attacked first, a crimeId should be set
// Doesn't handle possible edge case where no one reported the assault, but in such a case, // Doesn't handle possible edge case where no one reported the assault, but in such a case,
// for bystanders it is not possible to tell who attacked first, anyway. // for bystanders it is not possible to tell who attacked first, anyway.
@ -1553,8 +1552,8 @@ namespace MWMechanics
&& !MWBase::Environment::get().getWorld()->isSwimming(ptr) && !MWBase::Environment::get().getWorld()->isSwimming(ptr)
&& MWBase::Environment::get().getWorld()->isOnGround(ptr)) && MWBase::Environment::get().getWorld()->isOnGround(ptr))
{ {
static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat();
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
@ -1569,8 +1568,8 @@ namespace MWMechanics
sneakTerm = fSneakSkillMult * sneak + 0.2f * agility + 0.1f * luck + bootWeight * fSneakBootMult; sneakTerm = fSneakSkillMult * sneak + 0.2f * agility + 0.1f * luck + bootWeight * fSneakBootMult;
} }
static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat(); static float fSneakDistBase = store.find("fSneakDistanceBase")->mValue.getFloat();
static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat(); static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->mValue.getFloat();
osg::Vec3f pos1 (ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos1 (ptr.getRefData().getPosition().asVec3());
osg::Vec3f pos2 (observer.getRefData().getPosition().asVec3()); osg::Vec3f pos2 (observer.getRefData().getPosition().asVec3());
@ -1588,8 +1587,8 @@ namespace MWMechanics
float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind;
// is ptr behind the observer? // is ptr behind the observer?
static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat(); static float fSneakNoViewMult = store.find("fSneakNoViewMult")->mValue.getFloat();
static float fSneakViewMult = store.find("fSneakViewMult")->getFloat(); static float fSneakViewMult = store.find("fSneakViewMult")->mValue.getFloat();
float y = 0; float y = 0;
osg::Vec3f vec = pos1 - pos2; osg::Vec3f vec = pos1 - pos2;
if (observer.getRefData().getBaseNode()) if (observer.getRefData().getBaseNode())
@ -1749,7 +1748,7 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->getGlobalInt("pcknownwerewolf"))) MWBase::Environment::get().getWorld()->getGlobalInt("pcknownwerewolf")))
{ {
const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iWerewolfFightMod"); const ESM::GameSetting * iWerewolfFightMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iWerewolfFightMod");
fight += iWerewolfFightMod->getInt(); fight += iWerewolfFightMod->mValue.getInteger();
} }
} }
@ -1842,7 +1841,7 @@ namespace MWMechanics
// Witnesses of the player's transformation will make them a globally known werewolf // Witnesses of the player's transformation will make them a globally known werewolf
std::vector<MWWorld::Ptr> closeActors; std::vector<MWWorld::Ptr> closeActors;
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->getFloat(), closeActors); getActorsInRange(actor.getRefData().getPosition().asVec3(), gmst.find("fAlarmRadius")->mValue.getFloat(), closeActors);
bool detected = false, reported = false; bool detected = false, reported = false;
for (std::vector<MWWorld::Ptr>::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it) for (std::vector<MWWorld::Ptr>::const_iterator it = closeActors.begin(); it != closeActors.end(); ++it)
@ -1867,7 +1866,7 @@ namespace MWMechanics
if (reported) if (reported)
{ {
npcStats.setBounty(npcStats.getBounty()+ npcStats.setBounty(npcStats.getBounty()+
gmst.find("iWereWolfBounty")->getInt()); gmst.find("iWereWolfBounty")->mValue.getInteger());
windowManager->messageBox("#{sCrimeMessage}"); windowManager->messageBox("#{sCrimeMessage}");
} }
} }
@ -1879,7 +1878,7 @@ namespace MWMechanics
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor); MWMechanics::NpcStats &stats = actor.getClass().getNpcStats(actor);
stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getInt()); stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->mValue.getInteger());
} }
void MechanicsManager::cleanupSummonedCreature(const MWWorld::Ptr &caster, int creatureActorId) void MechanicsManager::cleanupSummonedCreature(const MWWorld::Ptr &caster, int creatureActorId)

View file

@ -132,6 +132,12 @@ namespace MWMechanics
/// @note No-op for non-player attackers /// @note No-op for non-player attackers
virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
/// Checks if commiting a crime is currently valid
/// @param victim The actor being attacked
/// @param attacker The actor commiting the crime
/// @return true if the victim is a valid target for crime
virtual bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
/// Utility to check if taking this item is illegal and calling commitCrime if so /// Utility to check if taking this item is illegal and calling commitCrime if so
/// @param container The container the item is in; may be empty for an item in the world /// @param container The container the item is in; may be empty for an item in the world
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container,
@ -223,7 +229,7 @@ namespace MWMechanics
virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count); virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count);
virtual bool isAttackPrepairing(const MWWorld::Ptr& ptr); virtual bool isAttackPreparing(const MWWorld::Ptr& ptr);
virtual bool isRunning(const MWWorld::Ptr& ptr); virtual bool isRunning(const MWWorld::Ptr& ptr);
virtual bool isSneaking(const MWWorld::Ptr& ptr); virtual bool isSneaking(const MWWorld::Ptr& ptr);

View file

@ -149,12 +149,12 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
float typeFactor = gmst.find ("fMiscSkillBonus")->getFloat(); float typeFactor = gmst.find ("fMiscSkillBonus")->mValue.getFloat();
for (int i=0; i<5; ++i) for (int i=0; i<5; ++i)
if (class_.mData.mSkills[i][0]==skillIndex) if (class_.mData.mSkills[i][0]==skillIndex)
{ {
typeFactor = gmst.find ("fMinorSkillBonus")->getFloat(); typeFactor = gmst.find ("fMinorSkillBonus")->mValue.getFloat();
break; break;
} }
@ -162,7 +162,7 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const
for (int i=0; i<5; ++i) for (int i=0; i<5; ++i)
if (class_.mData.mSkills[i][1]==skillIndex) if (class_.mData.mSkills[i][1]==skillIndex)
{ {
typeFactor = gmst.find ("fMajorSkillBonus")->getFloat(); typeFactor = gmst.find ("fMajorSkillBonus")->mValue.getFloat();
break; break;
} }
@ -178,7 +178,7 @@ float MWMechanics::NpcStats::getSkillProgressRequirement (int skillIndex, const
MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find (skillIndex); MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find (skillIndex);
if (skill->mData.mSpecialization==class_.mData.mSpecialization) if (skill->mData.mSpecialization==class_.mData.mSpecialization)
{ {
specialisationFactor = gmst.find ("fSpecialSkillBonus")->getFloat(); specialisationFactor = gmst.find ("fSpecialSkillBonus")->mValue.getFloat();
if (specialisationFactor<=0) if (specialisationFactor<=0)
throw std::runtime_error ("invalid skill specialisation factor"); throw std::runtime_error ("invalid skill specialisation factor");
@ -227,21 +227,21 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
// is this a minor or major skill? // is this a minor or major skill?
int increase = gmst.find("iLevelupMiscMultAttriubte")->getInt(); // Note: GMST has a typo int increase = gmst.find("iLevelupMiscMultAttriubte")->mValue.getInteger(); // Note: GMST has a typo
for (int k=0; k<5; ++k) for (int k=0; k<5; ++k)
{ {
if (class_.mData.mSkills[k][0] == skillIndex) if (class_.mData.mSkills[k][0] == skillIndex)
{ {
mLevelProgress += gmst.find("iLevelUpMinorMult")->getInt(); mLevelProgress += gmst.find("iLevelUpMinorMult")->mValue.getInteger();
increase = gmst.find("iLevelUpMajorMultAttribute")->getInt(); increase = gmst.find("iLevelUpMajorMultAttribute")->mValue.getInteger();
} }
} }
for (int k=0; k<5; ++k) for (int k=0; k<5; ++k)
{ {
if (class_.mData.mSkills[k][1] == skillIndex) if (class_.mData.mSkills[k][1] == skillIndex)
{ {
mLevelProgress += gmst.find("iLevelUpMajorMult")->getInt(); mLevelProgress += gmst.find("iLevelUpMajorMult")->mValue.getInteger();
increase = gmst.find("iLevelUpMinorMultAttribute")->getInt(); increase = gmst.find("iLevelUpMinorMultAttribute")->mValue.getInteger();
} }
} }
@ -249,7 +249,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::Skill>().find(skillIndex); MWBase::Environment::get().getWorld ()->getStore ().get<ESM::Skill>().find(skillIndex);
mSkillIncreases[skill->mData.mAttribute] += increase; mSkillIncreases[skill->mData.mAttribute] += increase;
mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->getInt(); mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger();
// Play sound & skill progress notification // Play sound & skill progress notification
/// \todo check if character is the player, if levelling is ever implemented for NPCs /// \todo check if character is the player, if levelling is ever implemented for NPCs
@ -266,7 +266,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never); MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never);
if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) if (mLevelProgress >= gmst.find("iLevelUpTotal")->mValue.getInteger())
{ {
// levelup is possible now // levelup is possible now
MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never); MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never);
@ -287,7 +287,7 @@ void MWMechanics::NpcStats::levelUp()
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
mLevelProgress -= gmst.find("iLevelUpTotal")->getInt(); mLevelProgress -= gmst.find("iLevelUpTotal")->mValue.getInteger();
mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
@ -298,7 +298,7 @@ void MWMechanics::NpcStats::levelUp()
// "When you gain a level, in addition to increasing three primary attributes, your Health // "When you gain a level, in addition to increasing three primary attributes, your Health
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
// the Health increase is calculated from the increased Endurance" // the Health increase is calculated from the increased Endurance"
setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->getFloat()); setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat());
setLevel(getLevel()+1); setLevel(getLevel()+1);
} }
@ -324,7 +324,7 @@ int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const
std::stringstream gmst; std::stringstream gmst;
gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult"; gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult";
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(gmst.str())->getInt(); return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(gmst.str())->mValue.getInteger();
} }
int MWMechanics::NpcStats::getSkillIncreasesForSpecialization(int spec) const int MWMechanics::NpcStats::getSkillIncreasesForSpecialization(int spec) const

View file

@ -1,6 +1,6 @@
#include "objects.hpp" #include "objects.hpp"
#include <iostream> #include <components/debug/debuglog.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -88,7 +88,7 @@ bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& gro
} }
else else
{ {
std::cerr<< "Warning: Objects::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; Log(Debug::Warning) << "Warning: Objects::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId();
return false; return false;
} }
} }

View file

@ -37,9 +37,9 @@ namespace MWMechanics
float pcSneak = static_cast<float>(mThief.getClass().getSkill(mThief, ESM::Skill::Sneak)); float pcSneak = static_cast<float>(mThief.getClass().getSkill(mThief, ESM::Skill::Sneak));
int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iPickMinChance")->getInt(); .find("iPickMinChance")->mValue.getInteger();
int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iPickMaxChance")->getInt(); .find("iPickMaxChance")->mValue.getInteger();
int roll = Misc::Rng::roll0to99(); int roll = Misc::Rng::roll0to99();
if (t < pcSneak / iPickMinChance) if (t < pcSneak / iPickMinChance)
@ -57,7 +57,7 @@ namespace MWMechanics
{ {
float stackValue = static_cast<float>(item.getClass().getValue(item) * count); float stackValue = static_cast<float>(item.getClass().getValue(item) * count);
float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fPickPocketMod")->getFloat(); .find("fPickPocketMod")->mValue.getFloat();
float valueTerm = 10 * fPickPocketMod * stackValue; float valueTerm = 10 * fPickPocketMod * stackValue;
return getDetected(valueTerm); return getDetected(valueTerm);

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