From 673b10207948598535444ae1afbb153f824a4298 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Jul 2014 12:17:24 +0200 Subject: [PATCH 001/707] increased version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9de8efeb5..cb9a54a6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 30) +set(OPENMW_VERSION_MINOR 31) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/readme.txt b/readme.txt index 92cb35f31..90f223a64 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.30.0 +Version: 0.31.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 6ba4a5335cdbdcbd8683989fb4fcf86c53fd0f2b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 4 Jul 2014 08:58:54 +0200 Subject: [PATCH 002/707] updated changelog --- readme.txt | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/readme.txt b/readme.txt index 90f223a64..bafca48a4 100644 --- a/readme.txt +++ b/readme.txt @@ -96,6 +96,188 @@ Allowed options: CHANGELOG +0.31.0 + +Bug #245: Cloud direction and weather systems differ from Morrowind +Bug #275: Local Map does not always show objects that span multiple cells +Bug #538: Update CenterOnCell (COC) function behavior +Bug #618: Local and World Map Textures are sometimes Black +Bug #640: Water behaviour at night +Bug #668: OpenMW doesn't support non-latin paths on Windows +Bug #746: OpenMW doesn't check if the background music was already played +Bug #747: Door is stuck if cell is left before animation finishes +Bug #772: Disabled statics are visible on map +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #869: Dead bodies don't collide with anything +Bug #894: Various character creation issues +Bug #897/#1369: opencs Segmentation Fault after "new" or "load" +Bug #899: Various jumping issues +Bug #952: Reflection effects are one frame delayed +Bug #993: Able to interact with world during Wait/Rest dialog +Bug #995: Dropped items can be placed inside the wall +Bug #1008: Corpses always face up upon reentering the cell +Bug #1035: Random colour patterns appearing in automap +Bug #1037: Footstep volume issues +Bug #1047: Creation of wrong links in dialogue window +Bug #1129: Summoned creature time life duration seems infinite +Bug #1134: Crimes can be committed against hostile NPCs +Bug #1136: Creature run speed formula is incorrect +Bug #1150: Weakness to Fire doesn't apply to Fire Damage in the same spell +Bug #1155: NPCs killing each other +Bug #1166: Bittercup script still does not work +Bug #1178: .bsa file names are case sensitive. +Bug #1179: Crash after trying to load game after being killed +Bug #1180: Changing footstep sound location +Bug #1196: Jumping not disabled when showing messageboxes +Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works +Bug #1217: Container content changes based on the current position of the mouse +Bug #1234: Loading/saving issues with dynamic records +Bug #1277: Text pasted into the console appears twice +Bug #1284: Crash on New Game +Bug #1303: It's possible to skip the chargen +Bug #1304: Slaughterfish should not detect the player unless the player is in the water +Bug #1311: Editor: deleting Record Filter line does not reset the filter +Bug #1324: ERROR: ESM Error: String table overflow when loading Animated Morrowind.esp +Bug #1328: Editor: Bogus Filter created when dragging multiple records to filter bar of non-applicable table +Bug #1331: Walking/running sound persist after killing NPC`s that are walking/running. +Bug #1334: Previously equipped items not shown as unequipped after attempting to sell them. +Bug #1335: Actors ignore vertical axis when deciding to attack +Bug #1338: Unknown toggle option for shadows +Bug #1339: "Ashlands Region" is visible when beginning new game during "Loading Area" process +Bug #1340: Guards prompt Player with punishment options after resisting arrest with another guard. +Bug #1348: Regression: Bug #1098 has returned with a vengeance +Bug #1349: [TR] TR_Data mesh tr_ex_imp_gatejamb01 cannot be activated +Bug #1352: Disabling an ESX file does not disable dependent ESX files +Bug #1355: CppCat Checks OpenMW +Bug #1356: Incorrect voice type filtering for sleep interrupts +Bug #1357: Restarting the game clears saves +Bug #1360: Seyda Neen silk rider dialog problem +Bug #1361: Some lights don't work +Bug #1364: It is difficult to bind "Mouse 1" to an action in the options menu +Bug #1370: Animation compilation mod does not work properly +Bug #1371: SL_Pick01.nif from third party fails to load in openmw, but works in Vanilla +Bug #1373: When stealing in front of Sellus Gravius cannot exit the dialog +Bug #1378: Installs to /usr/local are not working +Bug #1380: Loading a save file fail if one of the content files is disabled +Bug #1382: "getHExact() size mismatch" crash on loading official plugin "Siege at Firemoth.esp" +Bug #1386: Arkngthand door will not open +Bug #1388: Segfault when modifying View Distance in Menu options +Bug #1389: Crash when loading a save after dying +Bug #1390: Apostrophe characters not displayed [French version] +Bug #1391: Custom made icon background texture for magical weapons and stuff isn't scaled properly on GUI. +Bug #1393: Coin icon during the level up dialogue are off of the background +Bug #1394: Alt+F4 doesn't work on Win version +Bug #1395: Changing rings switches only the last one put on +Bug #1396: Pauldron parts aren't showing when the robe is equipped +Bug #1402: Dialogue of some shrines have wrong button orientation +Bug #1403: Items are floating in the air when they're dropped onto dead bodies. +Bug #1404: Forearms are not rendered on Argonian females +Bug #1407: Alchemy allows making potions from two of the same item +Bug #1408: "Max sale" button gives you all the items AND all the trader's gold +Bug #1409: Rest "Until Healed" broken for characters with stunted magicka. +Bug #1412: Empty travel window opens while playing through start game +Bug #1413: Save game ignores missing writing permission +Bug #1414: The Underground 2 ESM Error +Bug #1416: Not all splash screens in the Splash directory are used +Bug #1417: Loading saved game does not terminate +Bug #1419: Skyrim: Home of the Nords error +Bug #1422: ClearInfoActor +Bug #1423: ForceGreeting closes existing dialogue windows +Bug #1425: Cannot load save game +Bug #1426: Read skill books aren't stored in savegame +Bug #1427: Useless items can be set under hotkeys +Bug #1429: Text variables in journal +Bug #1432: When attacking friendly NPC, the crime is reported and bounty is raised after each swing +Bug #1435: Stealing priceless items is without punishment +Bug #1437: Door marker at Jobasha's Rare Books is spawning PC in the air +Bug #1440: Topic selection menu should be wider +Bug #1441: Dropping items on the rug makes them inaccessible +Bug #1442: When dropping and taking some looted items, bystanders consider that as a crime +Bug #1444: Arrows and bolts are not dropped where the cursor points +Bug #1445: Security trainers offering acrobatics instead +Bug #1447: Character dash not displayed, French edition +Bug #1448: When the player is killed by the guard while having a bounty on his head, the guard dialogue opens over and over instead of loading dialogue +Bug #1454: Script error in SkipTutorial +Bug #1456: Bad lighting when using certain Morrowind.ini generated by MGE +Bug #1457: Heart of Lorkan comes after you when attacking it +Bug #1458: Modified Keybindings are not remembered +Bug #1459: Dura Gra-Bol doesn't respond to PC attack +Bug #1462: Interior cells not loaded with Morrowind Patch active +Bug #1469: Item tooltip should show the base value, not real value +Bug #1477: Death count is not stored in savegame +Bug #1478: AiActivate does not trigger activate scripts +Bug #1481: Weapon not rendered when partially submerged in water +Bug #1483: Enemies are attacking even while dying +Bug #1486: ESM Error: Don't know what to do with INFO +Bug #1490: Arrows shot at PC can end up in inventory +Bug #1492: Monsters respawn on top of one another +Bug #1493: Dialogue box opens with follower NPC even if NPC is dead +Bug #1494: Paralysed cliffracers remain airbourne +Bug #1495: Dialogue box opens with follower NPC even the game is paused +Bug #1496: GUI messages are not cleared when loading another saved game +Bug #1499: Underwater sound sometimes plays when transitioning from interior. +Bug #1500: Targetted spells and water. +Bug #1502: Console error message on info refusal +Bug #1507: Bloodmoon MQ The Ritual of Beasts: Can't remove the arrow +Bug #1508: Bloodmoon: Fort Frostmoth, cant talk with Carnius Magius +Bug #1516: PositionCell doesn't move actors to current cell +Bug #1518: ForceGreeting broken for explicit references +Bug #1522: Crash after attempting to play non-music file +Bug #1523: World map empty after loading interior save +Bug #1524: Arrows in waiting/resting dialog act like minimum and maximum buttons +Bug #1525: Werewolf: Killed NPC's don't fill werewolfs hunger for blood +Bug #1527: Werewolf: Detect life detects wrong type of actor +Bug #1529: OpenMW crash during "the shrine of the dead" mission (tribunal) +Bug #1530: Selected text in the console has the same color as the background +Bug #1539: Barilzar's Mazed Band: Tribunal +Bug #1542: Looping taunts from NPC`s after death: Tribunal +Bug #1543: OpenCS crash when using drag&drop in script editor +Bug #1547: Bamz-Amschend: Centurion Archers combat problem +Bug #1548: The Missing Hand: Tribunal +Bug #1549: The Mad God: Tribunal, Dome of Serlyn +Bug #1557: A bounty is calculated from actual item cost +Bug #1562: Invisible terrain on top of Red Mountain +Bug #1564: Cave of the hidden music: Bloodmoon +Bug #1567: Editor: Deleting of referenceables does not work +Bug #1568: Picking up a stack of items and holding the enter key and moving your mouse around paints a bunch of garbage on screen. +Bug #1574: Solstheim: Drauger cant inflict damage on player +Bug #1578: Solstheim: Bonewolf running animation not working +Bug #1585: Particle effects on PC are stopped when paralyzed +Bug #1589: Tribunal: Crimson Plague quest does not update when Gedna Relvel is killed +Bug #1590: Failed to save game: compile error +Bug #1598: Segfault when making Drain/Fortify Skill spells +Bug #1599: Unable to switch to fullscreen +Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed +Feature #32: Periodic Cleanup/Refill +Feature #41: Precipitation and weather particles +Feature #568: Editor: Configuration setup +Feature #649: Editor: Threaded loading +Feature #930: Editor: Cell record saving +Feature #934: Editor: Body part table +Feature #935: Editor: Enchantment effect table +Feature #1162: Dialogue merging +Feature #1174: Saved Game: add missing creature state +Feature #1177: Saved Game: fog of war state +Feature #1312: Editor: Combat/Magic/Stealth values for creatures are not displayed +Feature #1314: Make NPCs and creatures fight each other +Feature #1315: Crime: Murder +Feature #1321: Sneak skill enhancements +Feature #1323: Handle restocking items +Feature #1332: Saved Game: levelled creatures +Feature #1347: modFactionReaction script instruction +Feature #1362: Animated main menu support +Feature #1433: Store walk/run toggle +Feature #1449: Use names instead of numbers for saved game files and folders +Feature #1453: Adding Delete button to the load menu +Feature #1460: Enable Journal screen while in dialogue +Feature #1480: Play Battle music when in combat +Feature #1501: Followers unable to fast travel with you +Feature #1520: Disposition and distance-based aggression/ShouldAttack +Feature #1595: Editor: Object rendering in cells +Task #940: Move license to locations where applicable +Task #1333: Remove cmake git tag reading +Task #1566: Editor: Object rendering refactoring + 0.30.0 Bug #416: Extreme shaking can occur during cell transitions while moving From f27e3067a39a2ba005ae621fdb6a77ff9a045ea1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 4 Jul 2014 23:19:40 +0200 Subject: [PATCH 003/707] Use explicit C locale in Misc::StringUtils (Fixes #1216) --- components/misc/stringops.cpp | 55 +---------------------------------- components/misc/stringops.hpp | 32 ++++++-------------- 2 files changed, 10 insertions(+), 77 deletions(-) diff --git a/components/misc/stringops.cpp b/components/misc/stringops.cpp index 0bc8e290a..0f801e554 100644 --- a/components/misc/stringops.cpp +++ b/components/misc/stringops.cpp @@ -12,59 +12,6 @@ namespace Misc { -bool begins(const char* str1, const char* str2) -{ - while(*str2) - { - if(*str1 == 0 || *str1 != *str2) return false; - - str1++; - str2++; - } - return true; -} - -bool ends(const char* str1, const char* str2) -{ - int len1 = strlen(str1); - int len2 = strlen(str2); - - if(len1 < len2) return false; - - return strcmp(str2, str1+len1-len2) == 0; -} - -// True if the given chars match, case insensitive -static bool icmp(char a, char b) -{ - if(a >= 'A' && a <= 'Z') - a += 'a' - 'A'; - if(b >= 'A' && b <= 'Z') - b += 'a' - 'A'; - - return a == b; -} - -bool ibegins(const char* str1, const char* str2) -{ - while(*str2) - { - if(*str1 == 0 || !icmp(*str1,*str2)) return false; - - str1++; - str2++; - } - return true; -} - -bool iends(const char* str1, const char* str2) -{ - int len1 = strlen(str1); - int len2 = strlen(str2); - - if(len1 < len2) return false; - - return strcasecmp(str2, str1+len1-len2) == 0; -} +std::locale StringUtils::mLocale = std::locale::classic(); } diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index d41463cfc..04dedb072 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -4,15 +4,18 @@ #include #include #include +#include namespace Misc { class StringUtils { + + static std::locale mLocale; struct ci { - bool operator()(int x, int y) const { - return std::tolower(x) < std::tolower(y); + bool operator()(char x, char y) const { + return std::tolower(x, StringUtils::mLocale) < std::tolower(y, StringUtils::mLocale); } }; @@ -28,7 +31,7 @@ public: std::string::const_iterator xit = x.begin(); std::string::const_iterator yit = y.begin(); for (; xit != x.end(); ++xit, ++yit) { - if (std::tolower(*xit) != std::tolower(*yit)) { + if (std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale)) { return false; } } @@ -42,7 +45,7 @@ public: for(;xit != x.end() && yit != y.end() && len > 0;++xit,++yit,--len) { int res = *xit - *yit; - if(res != 0 && std::tolower(*xit) != std::tolower(*yit)) + if(res != 0 && std::tolower(*xit, mLocale) != std::tolower(*yit, mLocale)) return (res > 0) ? 1 : -1; } if(len > 0) @@ -57,12 +60,8 @@ public: /// Transforms input string to lower case w/o copy static std::string &toLower(std::string &inout) { - std::transform( - inout.begin(), - inout.end(), - inout.begin(), - (int (*)(int)) std::tolower - ); + for (unsigned int i=0; i Date: Sat, 5 Jul 2014 07:24:30 +0200 Subject: [PATCH 004/707] Fix travis --- .../components/misc/test_stringops.cpp | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/apps/openmw_test_suite/components/misc/test_stringops.cpp b/apps/openmw_test_suite/components/misc/test_stringops.cpp index 44587c445..55fe0e0c2 100644 --- a/apps/openmw_test_suite/components/misc/test_stringops.cpp +++ b/apps/openmw_test_suite/components/misc/test_stringops.cpp @@ -12,68 +12,3 @@ struct StringOpsTest : public ::testing::Test { } }; - -TEST_F(StringOpsTest, begins_matching) -{ - ASSERT_TRUE(Misc::begins("abc", "a")); - ASSERT_TRUE(Misc::begins("abc", "ab")); - ASSERT_TRUE(Misc::begins("abc", "abc")); - ASSERT_TRUE(Misc::begins("abcd", "abc")); -} - -TEST_F(StringOpsTest, begins_not_matching) -{ - ASSERT_FALSE(Misc::begins("abc", "b")); - ASSERT_FALSE(Misc::begins("abc", "bc")); - ASSERT_FALSE(Misc::begins("abc", "bcd")); - ASSERT_FALSE(Misc::begins("abc", "abcd")); -} - -TEST_F(StringOpsTest, ibegins_matching) -{ - ASSERT_TRUE(Misc::ibegins("Abc", "a")); - ASSERT_TRUE(Misc::ibegins("aBc", "ab")); - ASSERT_TRUE(Misc::ibegins("abC", "abc")); - ASSERT_TRUE(Misc::ibegins("abcD", "abc")); -} - -TEST_F(StringOpsTest, ibegins_not_matching) -{ - ASSERT_FALSE(Misc::ibegins("abc", "b")); - ASSERT_FALSE(Misc::ibegins("abc", "bc")); - ASSERT_FALSE(Misc::ibegins("abc", "bcd")); - ASSERT_FALSE(Misc::ibegins("abc", "abcd")); -} - -TEST_F(StringOpsTest, ends_matching) -{ - ASSERT_TRUE(Misc::ends("abc", "c")); - ASSERT_TRUE(Misc::ends("abc", "bc")); - ASSERT_TRUE(Misc::ends("abc", "abc")); - ASSERT_TRUE(Misc::ends("abcd", "abcd")); -} - -TEST_F(StringOpsTest, ends_not_matching) -{ - ASSERT_FALSE(Misc::ends("abc", "b")); - ASSERT_FALSE(Misc::ends("abc", "ab")); - ASSERT_FALSE(Misc::ends("abc", "bcd")); - ASSERT_FALSE(Misc::ends("abc", "abcd")); -} - -TEST_F(StringOpsTest, iends_matching) -{ - ASSERT_TRUE(Misc::iends("Abc", "c")); - ASSERT_TRUE(Misc::iends("aBc", "bc")); - ASSERT_TRUE(Misc::iends("abC", "abc")); - ASSERT_TRUE(Misc::iends("abcD", "abcd")); -} - -TEST_F(StringOpsTest, iends_not_matching) -{ - ASSERT_FALSE(Misc::iends("abc", "b")); - ASSERT_FALSE(Misc::iends("abc", "ab")); - ASSERT_FALSE(Misc::iends("abc", "bcd")); - ASSERT_FALSE(Misc::iends("abc", "abcd")); -} - From 05cb22f5b79acabf181ef540d57a2de8d8d7ce6c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Jul 2014 10:36:29 +0200 Subject: [PATCH 005/707] updated changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index bafca48a4..23fe99f71 100644 --- a/readme.txt +++ b/readme.txt @@ -130,6 +130,7 @@ Bug #1179: Crash after trying to load game after being killed Bug #1180: Changing footstep sound location Bug #1196: Jumping not disabled when showing messageboxes Bug #1202: "strange" keys are not shown in binding menu, and are not saved either, but works +Bug #1216: Broken dialog topics in russian Morrowind Bug #1217: Container content changes based on the current position of the mouse Bug #1234: Loading/saving issues with dynamic records Bug #1277: Text pasted into the console appears twice From 0295673869888ee3075b12e7e9681c5e1f167f71 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 7 Jul 2014 23:37:59 +0200 Subject: [PATCH 006/707] Reset item model when reference is reset (Fixes #1628) This caused crashes when the window was resized after the reference no longer exists (e.g. when a savegame is loaded) --- apps/openmw/mwgui/companionwindow.cpp | 5 +++++ apps/openmw/mwgui/companionwindow.hpp | 2 ++ apps/openmw/mwgui/container.cpp | 6 ++++++ apps/openmw/mwgui/container.hpp | 2 ++ apps/openmw/mwgui/tradewindow.cpp | 6 ++++++ apps/openmw/mwgui/tradewindow.hpp | 1 + 6 files changed, 22 insertions(+) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index d0ac3e7c3..8d199e727 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -153,6 +153,11 @@ void CompanionWindow::onReferenceUnavailable() MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); } +void CompanionWindow::resetReference() +{ + ReferenceInterface::resetReference(); + mItemView->setModel(NULL); +} } diff --git a/apps/openmw/mwgui/companionwindow.hpp b/apps/openmw/mwgui/companionwindow.hpp index 006d0a3c3..dc460e2fc 100644 --- a/apps/openmw/mwgui/companionwindow.hpp +++ b/apps/openmw/mwgui/companionwindow.hpp @@ -20,6 +20,8 @@ namespace MWGui virtual void exit(); + virtual void resetReference(); + void open(const MWWorld::Ptr& npc); void onFrame (); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 011feb4d3..8da3def5f 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -258,6 +258,12 @@ namespace MWGui onTakeAllButtonClicked(mTakeButton); } + void ContainerWindow::resetReference() + { + ReferenceInterface::resetReference(); + mItemView->setModel(NULL); + } + void ContainerWindow::close() { WindowBase::close(); diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 5446a4ab7..79951f70e 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -54,6 +54,8 @@ namespace MWGui void open(const MWWorld::Ptr& container, bool loot=false); virtual void close(); + virtual void resetReference(); + virtual void exit(); private: diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index c0a51311f..19187cde1 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -531,4 +531,10 @@ namespace MWGui sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); } } + + void TradeWindow::resetReference() + { + ReferenceInterface::resetReference(); + mItemView->setModel(NULL); + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index cc70f1ae9..b487a8870 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -37,6 +37,7 @@ namespace MWGui virtual void exit(); + virtual void resetReference(); private: ItemView* mItemView; From 4a844ef79bbbfe23d9a9c2220aa0675a187bf7d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 5 Jul 2014 18:24:55 +0200 Subject: [PATCH 007/707] Fix compile error for OPENMW_USE_FFMPEG=0 --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 03e74697c..409e27388 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1088,7 +1088,7 @@ public: void close() { } - bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) + bool update() { return false; } }; From ef9eb8351498118c32888345c68688cec30df5db Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jul 2014 20:28:52 +0200 Subject: [PATCH 008/707] Fix initializing CharacterController with fists or spell equipped --- apps/openmw/mwmechanics/character.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2c5d68ceb..6549442ed 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -570,10 +570,14 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if (cls.hasInventoryStore(mPtr)) { getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); - if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) + if (mWeaponType != WeapType_None) { - getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; + getWeaponGroup(mWeaponType, mCurrentWeapon); + } + + if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) + { mAnimation->showWeapons(true); mAnimation->setWeaponGroup(mCurrentWeapon); } From 3042a9da7331e7908faaea50112c6e59ffbcd19c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jul 2014 19:37:12 +0200 Subject: [PATCH 009/707] Cancel queued view mode switch when switching view mode (Fixes #1618) --- apps/openmw/mwrender/camera.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9e683cc15..4580bae70 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -149,6 +149,8 @@ namespace MWRender mViewModeToggleQueued = true; return; } + else + mViewModeToggleQueued = false; mFirstPersonView = !mFirstPersonView; processViewChange(); From 05d92f0a7b3df74c48bfc4d89870a8c3f4f2e4d8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 8 Jul 2014 08:07:13 +0200 Subject: [PATCH 010/707] updated changelog --- readme.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.txt b/readme.txt index 23fe99f71..a02cc4ebc 100644 --- a/readme.txt +++ b/readme.txt @@ -249,6 +249,8 @@ Bug #1590: Failed to save game: compile error Bug #1598: Segfault when making Drain/Fortify Skill spells Bug #1599: Unable to switch to fullscreen Bug #1613: Morrowind Rebirth duplicate objects / vanilla objects not removed +Bug #1618: Death notice fails to show up +Bug #1628: Alt+Tab Segfault Feature #32: Periodic Cleanup/Refill Feature #41: Precipitation and weather particles Feature #568: Editor: Configuration setup From 8b404ee255d816bcbede0c0d5b51100ddb7b8b60 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 06:42:24 +0200 Subject: [PATCH 011/707] Fix not being able to exit dialogue using Escape when a Goodbye link appears --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 ++- apps/openmw/mwgui/dialogue.cpp | 9 +++++++-- apps/openmw/mwinput/inputmanagerimp.cpp | 8 -------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 8d9dc670f..5d55a7a6f 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -505,9 +505,10 @@ namespace MWDialogue void DialogueManager::askQuestion (const std::string& question, int choice) { + mIsInChoice = true; + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->addChoice(question, choice); - mIsInChoice = true; } void DialogueManager::goodbye() diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index a6ab1f122..2283e0cbe 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -273,8 +273,13 @@ namespace MWGui { if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice()) && !mGoodbye) - return; - MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + { + // in choice, not allowed to escape, but give access to main menu to allow loading other saves + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx); + } + else + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); } void DialogueWindow::onWindowResize(MyGUI::Window* _sender) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8d4c53921..067ad72bb 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -686,14 +686,6 @@ namespace MWInput return; } - if(MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue) { //Give access to the main menu when at a choice in dialogue - if(MWBase::Environment::get().getDialogueManager()->isInChoice()) { - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx); - return; - } - } - if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); From c9150c9680d430335203a791d00d8044b4d85acb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 10:35:11 +0200 Subject: [PATCH 012/707] Initialize character skeleton to a suitable pose (Fixes #1473) --- apps/openmw/mwmechanics/character.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d4ddf53cd..638feeef3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -607,6 +607,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if(mDeathState == CharState_None) refreshCurrentAnims(mIdleState, mMovementState, true); + + mAnimation->runAnimation(0.f); } CharacterController::~CharacterController() From 7c7c71428afc4a6290d4f0972175b898819754d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 14:26:59 +0200 Subject: [PATCH 013/707] Don't crash if certain class doesn't exist --- apps/openmw/mwgui/levelupdialog.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index eaa822722..5c698258a 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -132,22 +132,23 @@ namespace MWGui const ESM::Class *cls = world->getStore().get().find(playerData->mClass); - // Vanilla uses thief.dds for custom classes. A player with a custom class - // doesn't have mId set, see mwworld/esmstore.hpp where it is initialised as - // "$dynamic0". This check should resolve bug #1260. - // Choosing Stealth specialization and Speed/Agility as attributes. if(world->getStore().get().isDynamic(cls->mId)) { + // Vanilla uses thief.dds for custom classes. + // Choosing Stealth specialization and Speed/Agility as attributes, if possible. Otherwise fall back to first class found. MWWorld::SharedIterator it = world->getStore().get().begin(); for(; it != world->getStore().get().end(); it++) { if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3) break; } - mClassImage->setImageTexture ("textures\\levelup\\" + it->mId + ".dds"); + if (it == world->getStore().get().end()) + it = world->getStore().get().begin(); + if (it != world->getStore().get().end()) + cls = &*it; } - else - mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); + + mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); From f31a5490f34383492e4153f7e39e1c7128b11c55 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 13 Jul 2014 19:35:26 +0200 Subject: [PATCH 014/707] Create shaders on first render in shader based MyGUI manager --- libs/openengine/gui/manager.cpp | 48 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 383d37640..028192e9f 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -135,28 +135,6 @@ public: setRenderWindow(_window); setSceneManager(_scene); - // ADDED - sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); - mVertexProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getVertexProgram()->_getBindingDelegate(); - - mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); - mVertexProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getVertexProgram()->_getBindingDelegate(); - - mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); - mFragmentProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getFragmentProgram()->_getBindingDelegate(); - - mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); - sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); - mFragmentProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) - ->getFragmentProgram()->_getBindingDelegate(); - - MYGUI_PLATFORM_LOG(Info, getClassTypeName() << " successfully initialized"); mIsInitialise = true; @@ -359,6 +337,30 @@ public: } } + void initShaders() + { + // ADDED + sh::MaterialInstance* mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mVertexProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mVertexProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getVertexProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/NoTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/NoTexture", "Default"); + mFragmentProgramNoTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + + mat = sh::Factory::getInstance().getMaterialInstance("MyGUI/OneTexture"); + sh::Factory::getInstance()._ensureMaterial("MyGUI/OneTexture", "Default"); + mFragmentProgramOneTexture = static_cast(mat->getMaterial())->getOgreTechniqueForConfiguration("Default")->getPass(0) + ->getFragmentProgram()->_getBindingDelegate(); + } + void doRender(IVertexBuffer* _buffer, ITexture* _texture, size_t _count) { if (getManualRender()) @@ -368,6 +370,8 @@ public: } // ADDED + if (!mVertexProgramNoTexture) + initShaders(); if (_texture) { From 6cd739678af152c7f47958e1b65bb2d411125e43 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 14 Jul 2014 16:53:58 +0200 Subject: [PATCH 015/707] Fix dangling model pointer after reference cleanup (Fixes #1653) --- apps/openmw/mwgui/companionwindow.cpp | 2 ++ apps/openmw/mwgui/container.cpp | 2 ++ apps/openmw/mwgui/tradewindow.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index 8d199e727..6ac8c9d18 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -157,6 +157,8 @@ void CompanionWindow::resetReference() { ReferenceInterface::resetReference(); mItemView->setModel(NULL); + mModel = NULL; + mSortModel = NULL; } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 8da3def5f..65a9e07c4 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -262,6 +262,8 @@ namespace MWGui { ReferenceInterface::resetReference(); mItemView->setModel(NULL); + mModel = NULL; + mSortModel = NULL; } void ContainerWindow::close() diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 19187cde1..c56c2ee94 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -536,5 +536,7 @@ namespace MWGui { ReferenceInterface::resetReference(); mItemView->setModel(NULL); + mTradeModel = NULL; + mSortModel = NULL; } } From 58396915305aa69b3d968b49853e2494e48ff1da Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 14 Jul 2014 17:27:36 +0200 Subject: [PATCH 016/707] Check for container organic flag before checking weight (Fixes #1654) --- apps/openmw/mwgui/container.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 65a9e07c4..f0212031e 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -195,15 +195,6 @@ namespace MWGui { if (mPtr.getTypeName() == typeid(ESM::Container).name()) { - // check that we don't exceed container capacity - MWWorld::Ptr item = mDragAndDrop->mItem.mBase; - float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount; - if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); - return; - } - // check container organic flag MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->mBase->mFlags & ESM::Container::Organic) @@ -212,6 +203,15 @@ namespace MWGui messageBox("#{sContentsMessage2}"); return; } + + // check that we don't exceed container capacity + MWWorld::Ptr item = mDragAndDrop->mItem.mBase; + float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount; + if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); + return; + } } mDragAndDrop->drop(mModel, mItemView); From 563c2e57309d33d2ef6156045b4202344b0540c9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 15 Jul 2014 10:39:11 +0200 Subject: [PATCH 017/707] be a bit more relaxed about allowing - in names (Fixes #1593) --- components/compiler/scanner.cpp | 19 +++++++++++++------ components/compiler/scanner.hpp | 2 ++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 46e50a2e9..dd8fb431b 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -343,17 +343,13 @@ namespace Compiler } else if (!(c=='"' && name.empty())) { - if (!(std::isalpha (c) || std::isdigit (c) || c=='_' || c=='`' || - /// \todo add an option to disable the following hack. Also, find out who is - /// responsible for allowing it in the first place and meet up with that person in - /// a dark alley. - (c=='-' && !name.empty() && std::isalpha (mStream.peek())))) + if (!isStringCharacter (c)) { putback (c); break; } - if (first && std::isdigit (c)) + if (first && (std::isdigit (c) || c=='`' || c=='-')) error = true; } @@ -499,6 +495,17 @@ namespace Compiler return true; } + bool Scanner::isStringCharacter (char c, bool lookAhead) + { + return std::isalpha (c) || std::isdigit (c) || c=='_' || + /// \todo disable this when doing more stricter compiling + c=='`' || + /// \todo disable this when doing more stricter compiling. Also, find out who is + /// responsible for allowing it in the first place and meet up with that person in + /// a dark alley. + (c=='-' && (!lookAhead || isStringCharacter (mStream.peek(), false))); + } + bool Scanner::isWhitespace (char c) { return c==' ' || c=='\t'; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 344ae0582..7f6609f76 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -92,6 +92,8 @@ namespace Compiler bool scanSpecial (char c, Parser& parser, bool& cont); + bool isStringCharacter (char c, bool lookAhead = true); + static bool isWhitespace (char c); public: From 8241ee59c3e0a9519200f9af64acabc18291bf85 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 15 Jul 2014 12:59:02 +0200 Subject: [PATCH 018/707] modified GlobalScripts data structures to accommodate targeted script data --- apps/openmw/mwscript/globalscripts.cpp | 60 +++++++++++++------------- apps/openmw/mwscript/globalscripts.hpp | 11 ++++- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 8e118e2f8..411a0ec13 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -15,6 +15,9 @@ namespace MWScript { + GlobalScriptDesc::GlobalScriptDesc() : mRunning (false) {} + + GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) { @@ -23,53 +26,53 @@ namespace MWScript void GlobalScripts::addScript (const std::string& name) { - std::map >::iterator iter = + std::map::iterator iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) { if (const ESM::Script *script = mStore.get().find (name)) { - Locals locals; - - locals.configure (*script); + GlobalScriptDesc desc; + desc.mRunning = true; + desc.mLocals.configure (*script); - mScripts.insert (std::make_pair (name, std::make_pair (true, locals))); + mScripts.insert (std::make_pair (name, desc)); } } else - iter->second.first = true; + iter->second.mRunning = true; } void GlobalScripts::removeScript (const std::string& name) { - std::map >::iterator iter = + std::map::iterator iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter!=mScripts.end()) - iter->second.first = false; + iter->second.mRunning = false; } bool GlobalScripts::isRunning (const std::string& name) const { - std::map >::const_iterator iter = + std::map::const_iterator iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) return false; - return iter->second.first; + return iter->second.mRunning; } void GlobalScripts::run() { - for (std::map >::iterator iter (mScripts.begin()); + for (std::map::iterator iter (mScripts.begin()); iter!=mScripts.end(); ++iter) { - if (iter->second.first) + if (iter->second.mRunning) { MWScript::InterpreterContext interpreterContext ( - &iter->second.second, MWWorld::Ptr()); + &iter->second.mLocals, MWWorld::Ptr()); MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); } } @@ -99,16 +102,16 @@ namespace MWScript void GlobalScripts::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { - for (std::map >::const_iterator iter (mScripts.begin()); + for (std::map::const_iterator iter (mScripts.begin()); iter!=mScripts.end(); ++iter) { ESM::GlobalScript script; script.mId = iter->first; - iter->second.second.write (script.mLocals, iter->first); + iter->second.mLocals.write (script.mLocals, iter->first); - script.mRunning = iter->second.first ? 1 : 0; + script.mRunning = iter->second.mRunning ? 1 : 0; writer.startRecord (ESM::REC_GSCR); script.save (writer); @@ -124,25 +127,24 @@ namespace MWScript ESM::GlobalScript script; script.load (reader); - std::map >::iterator iter = + std::map::iterator iter = mScripts.find (script.mId); if (iter==mScripts.end()) { if (const ESM::Script *scriptRecord = mStore.get().search (script.mId)) { - std::pair data (false, Locals()); + GlobalScriptDesc desc; + desc.mLocals.configure (*scriptRecord); - data.second.configure (*scriptRecord); - - iter = mScripts.insert (std::make_pair (script.mId, data)).first; + iter = mScripts.insert (std::make_pair (script.mId, desc)).first; } else // script does not exist anymore return true; } - iter->second.first = script.mRunning!=0; - iter->second.second.read (script.mLocals, script.mId); + iter->second.mRunning = script.mRunning!=0; + iter->second.mLocals.read (script.mLocals, script.mId); return true; } @@ -153,21 +155,19 @@ namespace MWScript Locals& GlobalScripts::getLocals (const std::string& name) { std::string name2 = ::Misc::StringUtils::lowerCase (name); - std::map >::iterator iter = - mScripts.find (name2); + std::map::iterator iter = mScripts.find (name2); if (iter==mScripts.end()) { if (const ESM::Script *script = mStore.get().find (name)) { - Locals locals; - - locals.configure (*script); + GlobalScriptDesc desc; + desc.mLocals.configure (*script); - iter = mScripts.insert (std::make_pair (name, std::make_pair (false, locals))).first; + iter = mScripts.insert (std::make_pair (name, desc)).first; } } - return iter->second.second; + return iter->second.mLocals; } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 97584a5b8..9ad9361b6 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -26,10 +26,19 @@ namespace MWWorld namespace MWScript { + struct GlobalScriptDesc + { + bool mRunning; + Locals mLocals; + std::string mId; // ID used to start targeted script (empty if not a targeted script) + + GlobalScriptDesc(); + }; + class GlobalScripts { const MWWorld::ESMStore& mStore; - std::map > mScripts; // running, local variables + std::map mScripts; public: From e9377ad5c41abdfaecb6e12c79d91750cd0cfd12 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 15 Jul 2014 13:05:38 +0200 Subject: [PATCH 019/707] include targeted script data in saved games --- apps/openmw/mwscript/globalscripts.cpp | 3 +++ components/esm/globalscript.cpp | 4 ++++ components/esm/globalscript.hpp | 1 + 3 files changed, 8 insertions(+) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 411a0ec13..3335f4415 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -113,6 +113,8 @@ namespace MWScript script.mRunning = iter->second.mRunning ? 1 : 0; + script.mTargetId = iter->second.mId; + writer.startRecord (ESM::REC_GSCR); script.save (writer); writer.endRecord (ESM::REC_GSCR); @@ -145,6 +147,7 @@ namespace MWScript iter->second.mRunning = script.mRunning!=0; iter->second.mLocals.read (script.mLocals, script.mId); + iter->second.mId = script.mTargetId; return true; } diff --git a/components/esm/globalscript.cpp b/components/esm/globalscript.cpp index dcbd91140..467fe54a1 100644 --- a/components/esm/globalscript.cpp +++ b/components/esm/globalscript.cpp @@ -12,6 +12,8 @@ void ESM::GlobalScript::load (ESMReader &esm) mRunning = 0; esm.getHNOT (mRunning, "RUN_"); + + mTargetId = esm.getHNOString ("TARG"); } void ESM::GlobalScript::save (ESMWriter &esm) const @@ -22,4 +24,6 @@ void ESM::GlobalScript::save (ESMWriter &esm) const if (mRunning) esm.writeHNT ("RUN_", mRunning); + + esm.writeHNOString ("TARG", mTargetId); } \ No newline at end of file diff --git a/components/esm/globalscript.hpp b/components/esm/globalscript.hpp index 4fb8b7c48..43c859e09 100644 --- a/components/esm/globalscript.hpp +++ b/components/esm/globalscript.hpp @@ -15,6 +15,7 @@ namespace ESM std::string mId; Locals mLocals; int mRunning; + std::string mTargetId; // for targeted scripts void load (ESMReader &esm); void save (ESMWriter &esm) const; From 75ab8de3d200e24b934a86727f65f37cd787fe25 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 15 Jul 2014 13:26:04 +0200 Subject: [PATCH 020/707] added opcode for running scripts with explicit references (targeted scripts) --- apps/openmw/mwscript/globalscripts.cpp | 8 ++++-- apps/openmw/mwscript/globalscripts.hpp | 2 +- apps/openmw/mwscript/interpretercontext.cpp | 4 +-- apps/openmw/mwscript/interpretercontext.hpp | 2 +- components/interpreter/context.hpp | 2 +- components/interpreter/docs/vmformat.txt | 3 ++- components/interpreter/installopcodes.cpp | 1 + components/interpreter/scriptopcodes.hpp | 28 ++++++++++++++++----- 8 files changed, 36 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 3335f4415..83c6560f0 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -24,7 +24,7 @@ namespace MWScript addStartup(); } - void GlobalScripts::addScript (const std::string& name) + void GlobalScripts::addScript (const std::string& name, const std::string& targetId) { std::map::iterator iter = mScripts.find (::Misc::StringUtils::lowerCase (name)); @@ -36,12 +36,16 @@ namespace MWScript GlobalScriptDesc desc; desc.mRunning = true; desc.mLocals.configure (*script); + desc.mId = targetId; mScripts.insert (std::make_pair (name, desc)); } } - else + else if (!iter->second.mRunning) + { iter->second.mRunning = true; + iter->second.mId = targetId; + } } void GlobalScripts::removeScript (const std::string& name) diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 9ad9361b6..55c2e9321 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -44,7 +44,7 @@ namespace MWScript GlobalScripts (const MWWorld::ESMStore& store); - void addScript (const std::string& name); + void addScript (const std::string& name, const std::string& targetId = ""); void removeScript (const std::string& name); diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 028f83a61..0b474510d 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -366,9 +366,9 @@ namespace MWScript return MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name); } - void InterpreterContext::startScript (const std::string& name) + void InterpreterContext::startScript (const std::string& name, const std::string& targetId) { - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, targetId); } void InterpreterContext::stopScript (const std::string& name) diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 99026392e..0f4eefeeb 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -113,7 +113,7 @@ namespace MWScript virtual bool isScriptRunning (const std::string& name) const; - virtual void startScript (const std::string& name); + virtual void startScript (const std::string& name, const std::string& targetId = ""); virtual void stopScript (const std::string& name); diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 97e4fad4f..25a3ab30d 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -81,7 +81,7 @@ namespace Interpreter virtual bool isScriptRunning (const std::string& name) const = 0; - virtual void startScript (const std::string& name) = 0; + virtual void startScript (const std::string& name, const std::string& targetId = "") = 0; virtual void stopScript (const std::string& name) = 0; diff --git a/components/interpreter/docs/vmformat.txt b/components/interpreter/docs/vmformat.txt index 990762268..5d1eba088 100644 --- a/components/interpreter/docs/vmformat.txt +++ b/components/interpreter/docs/vmformat.txt @@ -133,5 +133,6 @@ op 67: store stack[0] in member float stack[2] of global script with ID stack[1] op 68: replace stack[0] with member short stack[1] of global script with ID stack[0] op 69: replace stack[0] with member short stack[1] of global script with ID stack[0] op 70: replace stack[0] with member short stack[1] of global script with ID stack[0] -opcodes 71-33554431 unused +op 71: explicit reference (target) = stack[0]; pop; start script stack[0] and pop +opcodes 72-33554431 unused opcodes 33554432-67108863 reserved for extensions diff --git a/components/interpreter/installopcodes.cpp b/components/interpreter/installopcodes.cpp index 721cde3d8..d705a109c 100644 --- a/components/interpreter/installopcodes.cpp +++ b/components/interpreter/installopcodes.cpp @@ -113,6 +113,7 @@ namespace Interpreter interpreter.installSegment5 (46, new OpScriptRunning); interpreter.installSegment5 (47, new OpStartScript); interpreter.installSegment5 (48, new OpStopScript); + interpreter.installSegment5 (71, new OpStartScriptExplicit); // spacial interpreter.installSegment5 (49, new OpGetDistance); diff --git a/components/interpreter/scriptopcodes.hpp b/components/interpreter/scriptopcodes.hpp index 56502d510..c98bcd23e 100644 --- a/components/interpreter/scriptopcodes.hpp +++ b/components/interpreter/scriptopcodes.hpp @@ -10,36 +10,52 @@ namespace Interpreter class OpScriptRunning : public Opcode0 { public: - + virtual void execute (Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime[0].mInteger = runtime.getContext().isScriptRunning (name); - } + } }; class OpStartScript : public Opcode0 { public: - + virtual void execute (Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); runtime.getContext().startScript (name); - } + } + }; + + class OpStartScriptExplicit : public Opcode0 + { + public: + + virtual void execute (Runtime& runtime) + { + std::string targetId = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + runtime.getContext().startScript (name, targetId); + } }; class OpStopScript : public Opcode0 { public: - + virtual void execute (Runtime& runtime) { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); runtime.getContext().stopScript (name); - } + } }; } From f43a10b7b99c6520873a8b36eefe4a8209281aee Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 15 Jul 2014 21:10:30 +0200 Subject: [PATCH 021/707] Adjust disease contraction according to Hrnchamd's research --- apps/openmw/mwmechanics/disease.hpp | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp index 5f73e8acd..05ce1c7ae 100644 --- a/apps/openmw/mwmechanics/disease.hpp +++ b/apps/openmw/mwmechanics/disease.hpp @@ -14,9 +14,11 @@ namespace MWMechanics { /// Call when \a actor has got in contact with \a carrier (e.g. hit by him, or loots him) + /// @param actor The actor that will potentially catch diseases. Currently only the player can catch diseases. + /// @param carrier The disease carrier. inline void diseaseContact (MWWorld::Ptr actor, MWWorld::Ptr carrier) { - if (!carrier.getClass().isActor()) + if (!carrier.getClass().isActor() || actor != MWBase::Environment::get().getWorld()->getPlayerPtr()) return; float fDiseaseXferChance = @@ -27,25 +29,47 @@ namespace MWMechanics for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); - if (spell->mData.mType == ESM::Spell::ST_Disease - || spell->mData.mType == ESM::Spell::ST_Blight) + + if (actor.getClass().getCreatureStats(actor).getSpells().hasSpell(spell->mId)) + continue; + + bool hasCorprusEffect = false; + for (std::vector::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) { - float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - if (roll < fDiseaseXferChance) + if (effectIt->mEffectID == ESM::MagicEffect::Corprus) { - // Contracted disease! - actor.getClass().getCreatureStats(actor).getSpells().add(it->first); - - if (actor.getRefData().getHandle() == "player") - { - std::string msg = "sMagicContractDisease"; - msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); - if (msg.find("%s") != std::string::npos) - msg.replace(msg.find("%s"), 2, spell->mName); - MWBase::Environment::get().getWindowManager()->messageBox(msg); - } + hasCorprusEffect = true; + break; } } + + float resist = 0.f; + if (hasCorprusEffect) + resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCorprusDisease).mMagnitude + - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCorprusDisease).mMagnitude); + else if (spell->mData.mType == ESM::Spell::ST_Disease) + resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCommonDisease).mMagnitude + - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCommonDisease).mMagnitude); + else if (spell->mData.mType == ESM::Spell::ST_Blight) + resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistBlightDisease).mMagnitude + - actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToBlightDisease).mMagnitude); + else + continue; + + int x = fDiseaseXferChance * 100 * resist; + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 10000; // [0, 9999] + + if (roll < x) + { + // Contracted disease! + actor.getClass().getCreatureStats(actor).getSpells().add(it->first); + + std::string msg = "sMagicContractDisease"; + msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, spell->mName); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + } } } } From 123157b216919f58a6d72fd5b0c8366e2e85a486 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 15 Jul 2014 21:53:11 +0200 Subject: [PATCH 022/707] Implement elemental shield damage to attacker (Feature #1121) --- apps/openmw/mwclass/creature.cpp | 2 ++ apps/openmw/mwclass/npc.cpp | 2 ++ apps/openmw/mwmechanics/combat.cpp | 48 ++++++++++++++++++++++++++++++ apps/openmw/mwmechanics/combat.hpp | 3 ++ 4 files changed, 55 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index feb6a2714..e78134802 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -330,6 +330,8 @@ namespace MWClass } } + MWMechanics::applyElementalShields(ptr, victim); + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) damage = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e2cb8ba7a..a5175c1f6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -630,6 +630,8 @@ namespace MWClass } } + MWMechanics::applyElementalShields(ptr, victim); + if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage)) damage = 0; diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 6feb5879b..19f00e81b 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -256,4 +256,52 @@ namespace MWMechanics return hitchance; } + void applyElementalShields(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim) + { + for (int i=0; i<3; ++i) + { + float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).mMagnitude; + + if (!magnitude) + continue; + + CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); + float saveTerm = attacker.getClass().getSkill(attacker, ESM::Skill::Destruction) + + 0.2f * attackerStats.getAttribute(ESM::Attribute::Willpower).getModified() + + 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified(); + + int fatigueMax = attackerStats.getFatigue().getModified(); + int fatigueCurrent = attackerStats.getFatigue().getCurrent(); + + float normalisedFatigue = fatigueMax==0 ? 1 : std::max (0.0f, static_cast (fatigueCurrent)/fatigueMax); + + saveTerm *= 1.25f * normalisedFatigue; + + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + float x = std::max(0.f, saveTerm - roll); + + int element = ESM::MagicEffect::FireDamage; + if (i == 1) + element = ESM::MagicEffect::ShockDamage; + if (i == 2) + element = ESM::MagicEffect::FrostDamage; + + short resistanceEffect = ESM::MagicEffect::getResistanceEffect(element); + short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(element); + float elementResistance = 0; + if (resistanceEffect != -1) + elementResistance += attackerStats.getMagicEffects().get(resistanceEffect).mMagnitude; + if (weaknessEffect != -1) + elementResistance -= attackerStats.getMagicEffects().get(weaknessEffect).mMagnitude; + + x = std::min(100.f, x + elementResistance); + + static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->getFloat(); + x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); + MWMechanics::DynamicStat health = attackerStats.getHealth(); + health.setCurrent(health.getCurrent() - x); + attackerStats.setHealth(health); + } + } + } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index bc58227bf..eb89cb820 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -19,6 +19,9 @@ void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MW /// Get the chance (in percent) for \a attacker to successfully hit \a victim with a given weapon skill value float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue); +/// Applies damage to attacker based on the victim's elemental shields. +void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); + } #endif From 20a0040bdb88084846c5da633f82635318428cc5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 15 Jul 2014 22:06:46 +0200 Subject: [PATCH 023/707] Apply elemental shield magnitude to element resistance (Closes #1121) --- apps/openmw/mwmechanics/combat.cpp | 8 +----- apps/openmw/mwmechanics/spellcasting.cpp | 32 ++++++++++++++++-------- apps/openmw/mwmechanics/spellcasting.hpp | 5 ++++ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 19f00e81b..5908e79cd 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -286,13 +286,7 @@ namespace MWMechanics if (i == 2) element = ESM::MagicEffect::FrostDamage; - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(element); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(element); - float elementResistance = 0; - if (resistanceEffect != -1) - elementResistance += attackerStats.getMagicEffects().get(resistanceEffect).mMagnitude; - if (weaknessEffect != -1) - elementResistance -= attackerStats.getMagicEffects().get(weaknessEffect).mMagnitude; + float elementResistance = MWMechanics::getEffectResistanceAttribute(element, &attackerStats.getMagicEffects()); x = std::min(100.f, x + elementResistance); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 1b0c444ab..9253a9453 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -148,6 +148,27 @@ namespace MWMechanics return school; } + float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects) + { + short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); + short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); + + float resistance = 0; + if (resistanceEffect != -1) + resistance += actorEffects->get(resistanceEffect).mMagnitude; + if (weaknessEffect != -1) + resistance -= actorEffects->get(weaknessEffect).mMagnitude; + + if (effectId == ESM::MagicEffect::FireDamage) + resistance += actorEffects->get(ESM::MagicEffect::FireShield).mMagnitude; + if (effectId == ESM::MagicEffect::ShockDamage) + resistance += actorEffects->get(ESM::MagicEffect::LightningShield).mMagnitude; + if (effectId == ESM::MagicEffect::FrostDamage) + resistance += actorEffects->get(ESM::MagicEffect::FrostShield).mMagnitude; + + return resistance; + } + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell, const MagicEffects* effects) { @@ -163,16 +184,7 @@ namespace MWMechanics float resisted = 0; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) { - - short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId); - short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId); - - float resistance = 0; - if (resistanceEffect != -1) - resistance += magicEffects->get(resistanceEffect).mMagnitude; - if (weaknessEffect != -1) - resistance -= magicEffects->get(weaknessEffect).mMagnitude; - + float resistance = getEffectResistanceAttribute(effectId, magicEffects); float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index b526a4353..2de187ae4 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -35,6 +35,11 @@ namespace MWMechanics int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); + /// Get the resistance attribute against an effect for a given actor. This will add together + /// ResistX and Weakness to X effects relevant against the given effect. + float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects); + + /// Get the effective resistance against an effect casted by the given actor in the given spell (optional). /// @return >=100 for fully resisted. can also return negative value for damage amplification. /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. From e8322da6631ecf49a6761210df1e9f89ac26725a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 17 Jul 2014 08:35:28 +0200 Subject: [PATCH 024/707] added support for targeted scripts to script compiler --- components/compiler/generator.cpp | 15 +++++++++++---- components/compiler/generator.hpp | 2 +- components/compiler/lineparser.cpp | 14 +++++++------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index c67e51e57..235b163ad 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -300,9 +300,9 @@ namespace code.push_back (Compiler::Generator::segment5 (46)); } - void opStartScript (Compiler::Generator::CodeContainer& code) + void opStartScript (Compiler::Generator::CodeContainer& code, bool targeted) { - code.push_back (Compiler::Generator::segment5 (47)); + code.push_back (Compiler::Generator::segment5 (targeted ? 71 : 47)); } void opStopScript (Compiler::Generator::CodeContainer& code) @@ -830,9 +830,16 @@ namespace Compiler opScriptRunning (code); } - void startScript (CodeContainer& code) + void startScript (CodeContainer& code, Literals& literals, const std::string& id) { - opStartScript (code); + if (id.empty()) + opStartScript (code, false); + else + { + int index = literals.addString (id); + opPushInt (code, index); + opStartScript (code, true); + } } void stopScript (CodeContainer& code) diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp index b51116122..2619033c8 100644 --- a/components/compiler/generator.hpp +++ b/components/compiler/generator.hpp @@ -113,7 +113,7 @@ namespace Compiler void scriptRunning (CodeContainer& code); - void startScript (CodeContainer& code); + void startScript (CodeContainer& code, Literals& literals, const std::string& id); void stopScript (CodeContainer& code); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index f7d2726e3..b1b831bc2 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -262,6 +262,13 @@ namespace Compiler Generator::disable (mCode, mLiterals, mExplicit); mState = PotentialEndState; return true; + + case Scanner::K_startscript: + + mExprParser.parseArguments ("c", scanner, mCode); + Generator::startScript (mCode, mLiterals, mExplicit); + mState = EndState; + return true; } // check for custom extensions @@ -361,13 +368,6 @@ namespace Compiler mState = EndState; return true; - case Scanner::K_startscript: - - mExprParser.parseArguments ("c", scanner, mCode); - Generator::startScript (mCode); - mState = EndState; - return true; - case Scanner::K_stopscript: mExprParser.parseArguments ("c", scanner, mCode); From dba6a9ebff4ba8c3ad8b4d2cee94a5b3bf3d7212 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 17 Jul 2014 09:15:41 +0200 Subject: [PATCH 025/707] run targeted scripts with an implicit reference based on the ID given --- apps/openmw/mwscript/globalscripts.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 83c6560f0..332431b16 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -75,8 +75,15 @@ namespace MWScript { if (iter->second.mRunning) { + MWWorld::Ptr ptr; + + if (!iter->second.mId.empty()) + ptr = MWBase::Environment::get().getWorld()->getPtr ( + iter->second.mId, false); + MWScript::InterpreterContext interpreterContext ( - &iter->second.mLocals, MWWorld::Ptr()); + &iter->second.mLocals, ptr); + MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); } } From 35b27ea8cb480e07ddffb24e91e7255f79d4de87 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 17 Jul 2014 11:29:04 +0200 Subject: [PATCH 026/707] ignore stray string argument after GetDisabled --- components/compiler/exprparser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index e54b2e2a8..d0cda0ec2 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -386,6 +386,9 @@ namespace Compiler mExplicit.clear(); mRefOp = false; + std::vector ignore; + parseArguments ("x", scanner, ignore); + mNextOperand = false; return true; } @@ -527,6 +530,9 @@ namespace Compiler Generator::getDisabled (mCode, mLiterals, ""); mOperands.push_back ('l'); + std::vector ignore; + parseArguments ("x", scanner, ignore); + mNextOperand = false; return true; } From 27c84d6cb7130204334af3575edabfe589337762 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 17 Jul 2014 13:36:55 +0200 Subject: [PATCH 027/707] the reference for a targeted script is now determined when needed instead of at the start of the script execution --- apps/openmw/mwscript/globalscripts.cpp | 6 +-- apps/openmw/mwscript/interpretercontext.cpp | 49 +++++++++++++-------- apps/openmw/mwscript/interpretercontext.hpp | 19 ++++++-- components/interpreter/context.hpp | 2 + 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 332431b16..0c94f503b 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -77,12 +77,8 @@ namespace MWScript { MWWorld::Ptr ptr; - if (!iter->second.mId.empty()) - ptr = MWBase::Environment::get().getWorld()->getPtr ( - iter->second.mId, false); - MWScript::InterpreterContext interpreterContext ( - &iter->second.mLocals, ptr); + &iter->second.mLocals, MWWorld::Ptr(), iter->second.mId); MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 0b474510d..c64b4c0d3 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -22,7 +22,7 @@ namespace MWScript { - MWWorld::Ptr InterpreterContext::getReference ( + MWWorld::Ptr InterpreterContext::getReferenceImp ( const std::string& id, bool activeOnly, bool doThrow) { if (!id.empty()) @@ -31,6 +31,10 @@ namespace MWScript } else { + if (mReference.isEmpty() && !mTargetId.empty()) + mReference = + MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); + if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); @@ -38,7 +42,7 @@ namespace MWScript } } - const MWWorld::Ptr InterpreterContext::getReference ( + const MWWorld::Ptr InterpreterContext::getReferenceImp ( const std::string& id, bool activeOnly, bool doThrow) const { if (!id.empty()) @@ -47,6 +51,10 @@ namespace MWScript } else { + if (mReference.isEmpty() && !mTargetId.empty()) + mReference = + MWBase::Environment::get().getWorld()->searchPtr (mTargetId, false); + if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); @@ -64,7 +72,7 @@ namespace MWScript } else { - const MWWorld::Ptr ptr = getReference (id, false); + const MWWorld::Ptr ptr = getReferenceImp (id, false); id = ptr.getClass().getScript (ptr); @@ -84,7 +92,7 @@ namespace MWScript } else { - const MWWorld::Ptr ptr = getReference (id, false); + const MWWorld::Ptr ptr = getReferenceImp (id, false); id = ptr.getClass().getScript (ptr); @@ -96,9 +104,9 @@ namespace MWScript } InterpreterContext::InterpreterContext ( - MWScript::Locals *locals, MWWorld::Ptr reference) + MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId) : mLocals (locals), mReference (reference), - mActivationHandled (false) + mActivationHandled (false), mTargetId (targetId) {} int InterpreterContext::getLocalShort (int index) const @@ -236,34 +244,34 @@ namespace MWScript std::string InterpreterContext::getNPCName() const { - ESM::NPC npc = *mReference.get()->mBase; + ESM::NPC npc = *getReferenceImp().get()->mBase; return npc.mName; } std::string InterpreterContext::getNPCRace() const { - ESM::NPC npc = *mReference.get()->mBase; + ESM::NPC npc = *getReferenceImp().get()->mBase; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mRace); return race->mName; } std::string InterpreterContext::getNPCClass() const { - ESM::NPC npc = *mReference.get()->mBase; + ESM::NPC npc = *getReferenceImp().get()->mBase; const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mClass); return class_->mName; } std::string InterpreterContext::getNPCFaction() const { - ESM::NPC npc = *mReference.get()->mBase; + ESM::NPC npc = *getReferenceImp().get()->mBase; const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(npc.mFaction); return faction->mName; } std::string InterpreterContext::getNPCRank() const { - std::map ranks = mReference.getClass().getNpcStats (mReference).getFactionRanks(); + std::map ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks(); std::map::const_iterator it = ranks.begin(); MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -299,7 +307,7 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); - std::string factionId = mReference.getClass().getNpcStats (mReference).getFactionRanks().begin()->first; + std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first; std::map ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map::const_iterator it = ranks.find(factionId); @@ -326,7 +334,7 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); - std::string factionId = mReference.getClass().getNpcStats (mReference).getFactionRanks().begin()->first; + std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first; std::map ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map::const_iterator it = ranks.find(factionId); @@ -383,7 +391,7 @@ namespace MWScript MWWorld::Ptr ref2; if (id.empty()) - ref2 = getReference("", true, true); + ref2 = getReferenceImp(); else ref2 = MWBase::Environment::get().getWorld()->searchPtr(id, true); @@ -448,19 +456,19 @@ namespace MWScript bool InterpreterContext::isDisabled (const std::string& id) const { - const MWWorld::Ptr ref = getReference (id, false); + const MWWorld::Ptr ref = getReferenceImp (id, false); return !ref.getRefData().isEnabled(); } void InterpreterContext::enable (const std::string& id) { - MWWorld::Ptr ref = getReference (id, false); + MWWorld::Ptr ref = getReferenceImp (id, false); MWBase::Environment::get().getWorld()->enable (ref); } void InterpreterContext::disable (const std::string& id) { - MWWorld::Ptr ref = getReference (id, false); + MWWorld::Ptr ref = getReferenceImp (id, false); MWBase::Environment::get().getWorld()->disable (ref); } @@ -542,6 +550,11 @@ namespace MWScript MWWorld::Ptr InterpreterContext::getReference(bool required) { - return getReference ("", true, required); + return getReferenceImp ("", true, required); + } + + std::string InterpreterContext::getTargetId() const + { + return mTargetId; } } diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 0f4eefeeb..f897282e2 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -27,14 +27,22 @@ namespace MWScript class InterpreterContext : public Interpreter::Context { Locals *mLocals; - MWWorld::Ptr mReference; + mutable MWWorld::Ptr mReference; MWWorld::Ptr mActivated; bool mActivationHandled; - MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true); + std::string mTargetId; - const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const; + /// If \a id is empty, a reference the script is run from is returned or in case + /// of a non-local script the reference derived from the target ID. + MWWorld::Ptr getReferenceImp (const std::string& id = "", bool activeOnly = false, + bool doThrow=true); + + /// If \a id is empty, a reference the script is run from is returned or in case + /// of a non-local script the reference derived from the target ID. + const MWWorld::Ptr getReferenceImp (const std::string& id = "", + bool activeOnly = false, bool doThrow=true) const; const Locals& getMemberLocals (std::string& id, bool global) const; ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before @@ -44,7 +52,8 @@ namespace MWScript public: - InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference); + InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference, + const std::string& targetId = ""); ///< The ownership of \a locals is not transferred. 0-pointer allowed. virtual int getLocalShort (int index) const; @@ -158,6 +167,8 @@ namespace MWScript MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) + + virtual std::string getTargetId() const; }; } diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 25a3ab30d..881687366 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -108,6 +108,8 @@ namespace Interpreter virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) = 0; + + virtual std::string getTargetId() const = 0; }; } From 8952154488789989e7721678c1bfc5672590a1ff Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 17 Jul 2014 13:37:57 +0200 Subject: [PATCH 028/707] inherit target ID when starting a script from another script --- components/interpreter/scriptopcodes.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/interpreter/scriptopcodes.hpp b/components/interpreter/scriptopcodes.hpp index c98bcd23e..976390eb5 100644 --- a/components/interpreter/scriptopcodes.hpp +++ b/components/interpreter/scriptopcodes.hpp @@ -26,7 +26,7 @@ namespace Interpreter { std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - runtime.getContext().startScript (name); + runtime.getContext().startScript (name, runtime.getContext().getTargetId()); } }; From 08ce6ed7fba940a89f95c294b87dfab43f557e22 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 16 Jul 2014 15:30:06 +0200 Subject: [PATCH 029/707] Attempt to restack item after repair or recharge (Fixes #1656) --- apps/openmw/mwgui/merchantrepair.cpp | 5 ++- apps/openmw/mwgui/recharge.cpp | 2 ++ apps/openmw/mwmechanics/repair.cpp | 7 +++-- apps/openmw/mwworld/containerstore.cpp | 28 +++++++++++++++++ apps/openmw/mwworld/containerstore.hpp | 5 +++ apps/openmw/mwworld/inventorystore.cpp | 42 +++++++++++++------------- 6 files changed, 65 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 49cc60d8a..0d1a57000 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -117,15 +117,18 @@ void MerchantRepair::exit() void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + // repair MWWorld::Ptr item = *sender->getUserData(); item.getCellRef().setCharge(item.getClass().getItemMaxHealth(item)); + player.getClass().getContainerStore(player).restack(item); + MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1); int price = boost::lexical_cast(sender->getUserString("Price")); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); startRepair(mActor); diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 079564273..9c43b1416 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -165,6 +165,8 @@ void Recharge::onItemClicked(MyGUI::Widget *sender) item.getCellRef().setEnchantmentCharge( std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast(enchantment->mData.mCharge))); + player.getClass().getContainerStore(player).restack(item); + player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0); } diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 9f2c851cf..6d6f889ed 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -57,10 +57,13 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) charge = std::min(charge + y, itemToRepair.getClass().getItemMaxHealth(itemToRepair)); itemToRepair.getCellRef().setCharge(charge); + // attempt to re-stack item, in case it was fully repaired + MWWorld::ContainerStoreIterator stacked = player.getClass().getContainerStore(player).restack(itemToRepair); + // set the OnPCRepair variable on the item's script - std::string script = itemToRepair.getClass().getScript(itemToRepair); + std::string script = stacked->getClass().getScript(itemToRepair); if(script != "") - itemToRepair.getRefData().getLocals().setVarByInt(script, "onpcrepair", 1); + stacked->getRefData().getLocals().setVarByInt(script, "onpcrepair", 1); // increase skill player.getClass().skillUsageSucceeded(player, ESM::Skill::Armorer, 0); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index e330ddaee..18ebd82db 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -141,6 +141,34 @@ void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) remove(ptr, ptr.getRefData().getCount()-1, container); } +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item) +{ + MWWorld::ContainerStoreIterator retval = end(); + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (item == *iter) + { + retval = iter; + break; + } + } + + if (retval == end()) + throw std::runtime_error("item is not from this container"); + + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (stacks(*iter, item)) + { + iter->getRefData().setCount(iter->getRefData().getCount() + item.getRefData().getCount()); + item.getRefData().setCount(0); + retval = iter; + break; + } + } + return retval; +} + bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) { const MWWorld::Class& cls1 = ptr1.getClass(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 7c81bdb6e..6d9d7a6bb 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -134,6 +134,11 @@ namespace MWWorld void unstack (const Ptr& ptr, const Ptr& container); ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). + MWWorld::ContainerStoreIterator restack (const MWWorld::Ptr& item); + ///< Attempt to re-stack an item in this container. + /// If a compatible stack is found, the item's count is added to that stack, then the original is deleted. + /// @return If the item was stacked, return the stack, otherwise return the old (untouched) item. + /// @return How many items with refID \a id are in this container? int count (const std::string& id); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2eb8aeb46..891440023 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -512,28 +512,21 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c // empty this slot mSlots[slot] = end(); - // restack the previously equipped item with other (non-equipped) items - for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + if (it->getRefData().getCount()) { - if (stacks(*iter, *it)) - { - iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); - it->getRefData().setCount(0); - retval = iter; - break; - } - } - - if (actor.getRefData().getHandle() == "player") - { - // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared - const std::string& script = it->getClass().getScript(*it); - if (script != "") - (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + retval = restack(*it); - if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) + if (actor.getRefData().getHandle() == "player") { - mSelectedEnchantItem = end(); + // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared + const std::string& script = it->getClass().getScript(*it); + if (script != "") + (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + + if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) + { + mSelectedEnchantItem = end(); + } } } @@ -633,8 +626,15 @@ void MWWorld::InventoryStore::rechargeItems(float duration) static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get().find( "fMagicItemRechargePerSecond")->getFloat(); - it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration, - it->second)); + if (it->first->getCellRef().getEnchantmentCharge() <= it->second) + { + it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration, + it->second)); + + // attempt to restack when fully recharged + if (it->first->getCellRef().getEnchantmentCharge() == it->second) + it->first = restack(*it->first); + } } } From 876af8f52961cd03b65d8d2ca76d38fc72269175 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 16 Jul 2014 20:09:42 +0200 Subject: [PATCH 030/707] Fix not saved Attacked flag (Fixes #1657) --- components/esm/creaturestats.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 0a9a36109..37f0cc63c 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -33,6 +33,9 @@ void ESM::CreatureStats::load (ESMReader &esm) mAlarmed = false; esm.getHNOT (mAlarmed, "ALRM"); + mAttacked = false; + esm.getHNOT (mAttacked, "ATKD"); + mHostile = false; esm.getHNOT (mHostile, "HOST"); @@ -142,6 +145,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mAlarmed) esm.writeHNT ("ALRM", mAlarmed); + if (mAttacked) + esm.writeHNT ("ATKD", mAttacked); + if (mHostile) esm.writeHNT ("HOST", mHostile); From 41c17bccb6f168448a80389a471b836837fb0470 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 14:39:17 +0200 Subject: [PATCH 031/707] Exit if an unknown encoding option is specified --- components/to_utf8/to_utf8.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 59a9aff80..c53cf62b5 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -4,6 +4,7 @@ #include #include #include +#include /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library @@ -329,8 +330,10 @@ ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) return ToUTF8::WINDOWS_1250; else if (encodingName == "win1251") return ToUTF8::WINDOWS_1251; - else + else if (encodingName == "win1252") return ToUTF8::WINDOWS_1252; + else + throw std::runtime_error(std::string("Unknown encoding '") + encodingName + std::string("', see openmw --help for available options.")); } std::string ToUTF8::encodingUsingMessage(const std::string& encodingName) @@ -339,6 +342,8 @@ std::string ToUTF8::encodingUsingMessage(const std::string& encodingName) return "Using Central and Eastern European font encoding."; else if (encodingName == "win1251") return "Using Cyrillic font encoding."; - else + else if (encodingName == "win1252") return "Using default (English) font encoding."; + else + throw std::runtime_error(std::string("Unknown encoding '") + encodingName + std::string("', see openmw --help for available options.")); } From 2a510573b8460ba860e880c829d42be7919c4e00 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 15:27:56 +0200 Subject: [PATCH 032/707] Remove unused implementation file --- components/CMakeLists.txt | 2 +- components/terrain/backgroundloader.cpp | 0 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 components/terrain/backgroundloader.cpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index b8ebb84b1..8f9fb17af 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -72,7 +72,7 @@ add_component_dir (translation add_definitions(-DTERRAIN_USE_SHADER=1) add_component_dir (terrain - quadtreenode chunk world defaultworld storage material buffercache defs backgroundloader + quadtreenode chunk world defaultworld storage material buffercache defs ) add_component_dir (loadinglistener diff --git a/components/terrain/backgroundloader.cpp b/components/terrain/backgroundloader.cpp deleted file mode 100644 index e69de29bb..000000000 From f9a39138ccca492445631543fc0ca333b49551d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 16:15:25 +0200 Subject: [PATCH 033/707] Add missing endline --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 5d55a7a6f..6c801f755 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -266,7 +266,7 @@ namespace MWDialogue } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); + std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; } } } From 4d39d77eaa39f2c904591eb3b3e1d82494f32d07 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 20:40:40 +0200 Subject: [PATCH 034/707] Fix incompatible encoding names in ContentModel (bug uncovered by 41c17bccb6f16) --- components/contentselector/model/contentmodel.cpp | 3 ++- components/contentselector/model/contentmodel.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index f5bc2369a..e76930e81 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -23,6 +23,7 @@ ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) { + mEncoding = encoding; if (encoding == QLatin1String("win1252")) mCodec = QTextCodec::codecForName("windows-1252"); @@ -449,7 +450,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) try { ESM::ESMReader fileReader; ToUTF8::Utf8Encoder encoder = - ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString()); + ToUTF8::calculateEncoding(mEncoding.toStdString()); fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 6d147bce9..7b2000b51 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -64,6 +64,7 @@ namespace ContentSelectorModel ContentFileList mFiles; QHash mCheckStates; QTextCodec *mCodec; + QString mEncoding; public: From d4ffd30f98fe50e1810236b26a6925781717f50d Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 20:57:37 +0200 Subject: [PATCH 035/707] Implement Rank filter with no faction given (Fixes #1660) --- apps/openmw/mwdialogue/filter.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 08cdb1d00..daa9a684a 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -76,8 +76,18 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const } else if (info.mData.mRank != -1) { - // if there is a rank condition, but the NPC is not in a faction, always fail - return false; + if (isCreature) + return false; + + // Rank requirement, but no faction given. Use the actor's faction, if there is one. + MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor); + + if (!stats.getFactionRanks().size()) + return false; + + // check rank + if (stats.getFactionRanks().begin()->second < info.mData.mRank) + return false; } // Gender From 192d5ca08f4ef6bbe5a31f71857a11366203b273 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 17 Jul 2014 22:20:37 +0200 Subject: [PATCH 036/707] Allow overriding OGRE_PLUGIN_DIR in cmake command line --- CMakeLists.txt | 6 ++++-- components/ogreinit/ogreinit.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb9a54a6c..ee3850ddb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,12 +325,14 @@ else () add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") endif() -add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") -add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") if (APPLE AND OPENMW_OSX_DEPLOYMENT) # make it empty so plugin loading code can check this and try to find plugins inside app bundle add_definitions(-DOGRE_PLUGIN_DIR="") else() + if (NOT DEFINED ${OGRE_PLUGIN_DIR}) + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + endif() + add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") endif() diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index 77dbcb1ee..515c0875a 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -193,7 +193,7 @@ namespace OgreInit pluginDir = Ogre::macFrameworksPath(); #endif #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX - pluginDir = OGRE_PLUGIN_DIR_REL; + pluginDir = OGRE_PLUGIN_DIR; #endif } Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); From 4fb897f2f8ea61f3f40a133c9a908d0db8092863 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 18 Jul 2014 09:56:58 +0200 Subject: [PATCH 037/707] added missing getId functions to classes derived from MWWorld::Class --- apps/openmw/mwclass/activator.cpp | 5 +++++ apps/openmw/mwclass/activator.hpp | 3 +++ apps/openmw/mwclass/apparatus.cpp | 5 +++++ apps/openmw/mwclass/apparatus.hpp | 3 +++ apps/openmw/mwclass/armor.cpp | 5 +++++ apps/openmw/mwclass/armor.hpp | 3 +++ apps/openmw/mwclass/book.cpp | 5 +++++ apps/openmw/mwclass/book.hpp | 3 +++ apps/openmw/mwclass/clothing.cpp | 5 +++++ apps/openmw/mwclass/clothing.hpp | 3 +++ apps/openmw/mwclass/container.cpp | 5 +++++ apps/openmw/mwclass/container.hpp | 3 +++ apps/openmw/mwclass/creaturelevlist.cpp | 5 +++++ apps/openmw/mwclass/creaturelevlist.hpp | 3 +++ apps/openmw/mwclass/door.cpp | 5 +++++ apps/openmw/mwclass/door.hpp | 3 +++ apps/openmw/mwclass/itemlevlist.cpp | 5 +++++ apps/openmw/mwclass/itemlevlist.hpp | 3 +++ apps/openmw/mwclass/light.cpp | 5 +++++ apps/openmw/mwclass/light.hpp | 3 +++ apps/openmw/mwclass/lockpick.cpp | 5 +++++ apps/openmw/mwclass/lockpick.hpp | 3 +++ apps/openmw/mwclass/misc.cpp | 5 +++++ apps/openmw/mwclass/misc.hpp | 3 +++ apps/openmw/mwclass/potion.cpp | 5 +++++ apps/openmw/mwclass/potion.hpp | 3 +++ apps/openmw/mwclass/probe.cpp | 5 +++++ apps/openmw/mwclass/probe.hpp | 3 +++ apps/openmw/mwclass/repair.cpp | 5 +++++ apps/openmw/mwclass/repair.hpp | 3 +++ apps/openmw/mwclass/static.cpp | 5 +++++ apps/openmw/mwclass/static.hpp | 3 +++ 32 files changed, 128 insertions(+) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 043aadd35..0585ced51 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -25,6 +25,11 @@ namespace MWClass { + std::string Activator::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 1e772ef4f..3e4bc3de4 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -13,6 +13,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index d61ba038a..32f2e0bb7 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -21,6 +21,11 @@ namespace MWClass { + std::string Apparatus::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 17b8b9254..5cdda8f26 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -13,6 +13,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index b29bf36b2..79766e9ec 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -25,6 +25,11 @@ namespace MWClass { + std::string Armor::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index e9164f920..8b7804c63 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 0adee57e3..91b570783 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -22,6 +22,11 @@ namespace MWClass { + std::string Book::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index b60ef41d6..49d21e8bf 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index dc98e323e..1a9a497d7 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -22,6 +22,11 @@ namespace MWClass { + std::string Clothing::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 052928238..99ce61ece 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 53add4274..044d67536 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -43,6 +43,11 @@ namespace namespace MWClass { + std::string Container::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Container::ensureCustomData (const MWWorld::Ptr& ptr) const { if (!ptr.getRefData().getCustomData()) diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 9fc013e45..e926a71fe 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -15,6 +15,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 784304804..754780b1d 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -27,6 +27,11 @@ namespace namespace MWClass { + std::string CreatureLevList::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const { return ""; diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index 6c51a3189..7016524eb 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -11,6 +11,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 677ad462e..889268a9a 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -42,6 +42,11 @@ namespace namespace MWClass { + std::string Door::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 12b360aa8..23e11d336 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -16,6 +16,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/itemlevlist.cpp b/apps/openmw/mwclass/itemlevlist.cpp index 6ed9ab2e5..d31080bb2 100644 --- a/apps/openmw/mwclass/itemlevlist.cpp +++ b/apps/openmw/mwclass/itemlevlist.cpp @@ -5,6 +5,11 @@ namespace MWClass { + std::string ItemLevList::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + std::string ItemLevList::getName (const MWWorld::Ptr& ptr) const { return ""; diff --git a/apps/openmw/mwclass/itemlevlist.hpp b/apps/openmw/mwclass/itemlevlist.hpp index 0b71b072c..2b507135f 100644 --- a/apps/openmw/mwclass/itemlevlist.hpp +++ b/apps/openmw/mwclass/itemlevlist.hpp @@ -9,6 +9,9 @@ namespace MWClass { public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual std::string getName (const MWWorld::Ptr& ptr) const; ///< \return name (the one that is to be presented to the user; not the internal one); /// can return an empty string. diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 8a2c20f69..6178984cf 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -47,6 +47,11 @@ namespace namespace MWClass { + std::string Light::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 5568e1727..584039336 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -14,6 +14,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index bc6855129..9cce5694d 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -22,6 +22,11 @@ namespace MWClass { + std::string Lockpick::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 8f5a699d9..d4bdf3fa6 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1044fb01d..7bd2f9b67 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -38,6 +38,11 @@ bool isGold (const MWWorld::Ptr& ptr) namespace MWClass { + std::string Miscellaneous::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 16e9ca10b..53a8e050b 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 440121d35..b0b15193d 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -24,6 +24,11 @@ namespace MWClass { + std::string Potion::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 0f0578ca0..4c407d161 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index ed8625eec..030426dee 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -22,6 +22,11 @@ namespace MWClass { + std::string Probe::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index a959e6c42..047cb8ed4 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index d7a080534..f62d5f6cf 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -21,6 +21,11 @@ namespace MWClass { + std::string Repair::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 28ca5ad4c..17730d6ec 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 8768bde06..c241935ab 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -12,6 +12,11 @@ namespace MWClass { + std::string Static::getId (const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mId; + } + void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index e36b3d142..2ac2e8682 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + /// Return ID of \a ptr + virtual std::string getId (const MWWorld::Ptr& ptr) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering From 6a3ff211b18cbf5505dfb6f06a554aac4cf8355d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 18 Jul 2014 09:57:47 +0200 Subject: [PATCH 038/707] automatically get target ID at InterpreterContext construction, if a reference is available --- apps/openmw/mwscript/interpretercontext.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index c64b4c0d3..e1dc6273c 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -107,7 +107,13 @@ namespace MWScript MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId) : mLocals (locals), mReference (reference), mActivationHandled (false), mTargetId (targetId) - {} + { + // If we run on a reference (local script, dialogue script or console with object + // selected), store the ID of that reference store it so it can be inherited by + // targeted scripts started from this one. + if (targetId.empty() && !reference.isEmpty()) + mTargetId = reference.getClass().getId (reference); + } int InterpreterContext::getLocalShort (int index) const { From e33ee52b917c2e9a295b89b20c6cd117db32a439 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 18 Jul 2014 12:29:20 +0200 Subject: [PATCH 039/707] make stray names in the begin line a warning instead of an error --- components/compiler/fileparser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp index 185af4a51..e90e9a8f6 100644 --- a/components/compiler/fileparser.cpp +++ b/components/compiler/fileparser.cpp @@ -51,6 +51,12 @@ namespace Compiler /// \todo allow this workaround to be disabled for newer scripts } + if (mState==BeginCompleteState) + { + reportWarning ("Stray string (" + name + ") after begin statement", loc); + return true; + } + return Parser::parseName (name, loc, scanner); } From 1320ac6983912ce0ec7fdaadf2eca5768f684d55 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 18 Jul 2014 15:58:25 +0200 Subject: [PATCH 040/707] Ensure non-negative particle life time, add size assertion --- components/nifogre/ogrenifloader.cpp | 4 ++-- components/nifogre/particles.cpp | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index eed320756..81b2e55d2 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -762,8 +762,8 @@ class NIFObjectLoader else emitter->setEmissionRate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); - emitter->setTimeToLive(partctrl->lifetime, - partctrl->lifetime + partctrl->lifetimeRandom); + emitter->setTimeToLive(std::max(0.f, partctrl->lifetime), + std::max(0.f, partctrl->lifetime + partctrl->lifetimeRandom)); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index a1433a669..47dce774f 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -448,12 +448,14 @@ public: if(life_time-particle_time < mGrowTime) { Ogre::Real scale = (life_time-particle_time) / mGrowTime; + assert (scale >= 0); width *= scale; height *= scale; } if(particle_time < mFadeTime) { Ogre::Real scale = particle_time / mFadeTime; + assert (scale >= 0); width *= scale; height *= scale; } @@ -479,12 +481,14 @@ public: if(life_time-particle_time < mGrowTime) { Ogre::Real scale = (life_time-particle_time) / mGrowTime; + assert (scale >= 0); width *= scale; height *= scale; } if(particle_time < mFadeTime) { Ogre::Real scale = particle_time / mFadeTime; + assert (scale >= 0); width *= scale; height *= scale; } From 63fd04882a7ee406a667ebad905a8b2334911b81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 19 Jul 2014 23:49:54 +0200 Subject: [PATCH 041/707] Make sure crime gold discounts don't reduce price to zero --- apps/openmw/mwworld/worldimp.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 386834882..80ff0fdfa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2586,9 +2586,15 @@ namespace MWWorld float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->getFloat(); float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->getFloat(); - int discount = bounty*fCrimeGoldDiscountMult; + int discount = bounty * fCrimeGoldDiscountMult; int turnIn = bounty * fCrimeGoldTurnInMult; + if (bounty > 0) + { + discount = std::max(1, discount); + turnIn = std::max(1, turnIn); + } + mGlobalVariables["pchascrimegold"].setInteger((bounty <= playerGold) ? 1 : 0); mGlobalVariables["pchasgolddiscount"].setInteger((discount <= playerGold) ? 1 : 0); From 691ba02115d49f36a3dae68bcb9b649f7f200544 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 14:12:27 +0200 Subject: [PATCH 042/707] Reduce wepaon condition even if attack misses --- apps/openmw/mwclass/creature.cpp | 17 +++++++++++++++++ apps/openmw/mwclass/npc.cpp | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index e78134802..d521b1b33 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -254,6 +254,23 @@ namespace MWClass if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false); + + // Weapon health is reduced by 1 even if the attack misses + const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon); + if(weaphashealth) + { + int weaphealth = weapon.getClass().getItemHealth(weapon); + + if (!MWBase::Environment::get().getWorld()->getGodModeState()) + { + weaphealth -= std::min(1, weaphealth); + weapon.getCellRef().setCharge(weaphealth); + } + + // Weapon broken? unequip it + if (weapon.getCellRef().getCharge() == 0) + weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr); + } return; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a5175c1f6..0dc679e1c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -527,6 +527,24 @@ namespace MWClass if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { othercls.onHit(victim, 0.0f, false, weapon, ptr, false); + + // Weapon health is reduced by 1 even if the attack misses + const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon); + if(weaphashealth) + { + int weaphealth = weapon.getClass().getItemHealth(weapon); + + if (!MWBase::Environment::get().getWorld()->getGodModeState()) + { + weaphealth -= std::min(1, weaphealth); + weapon.getCellRef().setCharge(weaphealth); + } + + // Weapon broken? unequip it + if (weaphealth == 0) + weapon = *inv.unequipItem(weapon, ptr); + } + return; } From 5bbf07976f7f3f070d87c9c80b71283cacda1401 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 14:41:39 +0200 Subject: [PATCH 043/707] Consider Shield spell effect for creature armor rating --- apps/openmw/mwclass/creature.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d521b1b33..737ed002f 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -637,8 +637,7 @@ namespace MWClass float Creature::getArmorRating (const MWWorld::Ptr& ptr) const { - /// \todo add Shield magic effect magnitude here, controlled by a GMST (Vanilla vs MCP behaviour) - return 0.f; + return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; } float Creature::getCapacity (const MWWorld::Ptr& ptr) const From 28a0899d2ba6c731bdb3a361b4d51da77595eab1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 16:22:52 +0200 Subject: [PATCH 044/707] Implement difficulty scaling (Fixes #1505) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/creature.cpp | 4 + apps/openmw/mwclass/npc.cpp | 4 + apps/openmw/mwgui/settingswindow.cpp | 13 ++ apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwmechanics/combat.cpp | 5 + apps/openmw/mwmechanics/difficultyscaling.cpp | 38 +++++ apps/openmw/mwmechanics/difficultyscaling.hpp | 12 ++ files/mygui/openmw_settings_window.layout | 137 ++++++++++-------- files/settings-default.cfg | 2 + 10 files changed, 157 insertions(+), 61 deletions(-) create mode 100644 apps/openmw/mwmechanics/difficultyscaling.cpp create mode 100644 apps/openmw/mwmechanics/difficultyscaling.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 23ba78dba..0dda1131b 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -69,7 +69,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting - disease pickpocket levelledlist combat steering obstacle autocalcspell + disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 737ed002f..c93fcc298 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -9,6 +9,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/disease.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/difficultyscaling.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -412,6 +413,9 @@ namespace MWClass if(ishealth) { + if (!attacker.isEmpty()) + damage = scaleDamage(damage, attacker, ptr); + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; setActorHealth(ptr, health, attacker); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0dc679e1c..f81c37c10 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -23,6 +23,7 @@ #include "../mwmechanics/disease.hpp" #include "../mwmechanics/combat.hpp" #include "../mwmechanics/autocalcspell.hpp" +#include "../mwmechanics/difficultyscaling.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -788,6 +789,9 @@ namespace MWClass if(ishealth) { + if (!attacker.isEmpty()) + damage = scaleDamage(damage, attacker, ptr); + if(damage > 0.0f) sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f4602b064..09d2fc19c 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -157,6 +157,8 @@ namespace MWGui { configureWidgets(mMainWidget); + setTitle("#{sOptions}"); + getWidget(mOkButton, "OkButton"); getWidget(mResolutionList, "ResolutionList"); getWidget(mFullscreenButton, "FullscreenButton"); @@ -174,6 +176,7 @@ namespace MWGui getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mRefractionButton, "RefractionButton"); + getWidget(mDifficultySlider, "DifficultySlider"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mShaderModeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShaderModeToggled); @@ -227,6 +230,10 @@ namespace MWGui MyGUI::TextBox* fovText; getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + boost::lexical_cast(int(Settings::Manager::getInt("field of view", "General"))) + ")"); + + MyGUI::TextBox* diffText; + getWidget(diffText, "DifficultyText"); + diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast(int(Settings::Manager::getInt("difficulty", "Game"))) + ")"); } void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) @@ -406,6 +413,12 @@ namespace MWGui getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + boost::lexical_cast(int(value)) + ")"); } + if (scroller == mDifficultySlider) + { + MyGUI::TextBox* diffText; + getWidget(diffText, "DifficultyText"); + diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast(int(value)) + ")"); + } } else { diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 37f2c8af0..cbfb5cfe1 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -30,6 +30,7 @@ namespace MWGui MyGUI::Button* mVSyncButton; MyGUI::Button* mFPSButton; MyGUI::ScrollBar* mFOVSlider; + MyGUI::ScrollBar* mDifficultySlider; MyGUI::ScrollBar* mAnisotropySlider; MyGUI::ComboBox* mTextureFilteringButton; MyGUI::TextBox* mAnisotropyLabel; diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 5908e79cd..6e7bc9b94 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -10,6 +10,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/difficultyscaling.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -292,6 +293,10 @@ namespace MWMechanics static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get().find("fElementalShieldMult")->getFloat(); x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); + + // Note swapped victim and attacker, since the attacker takes the damage here. + x = scaleDamage(x, victim, attacker); + MWMechanics::DynamicStat health = attackerStats.getHealth(); health.setCurrent(health.getCurrent() - x); attackerStats.setHealth(health); diff --git a/apps/openmw/mwmechanics/difficultyscaling.cpp b/apps/openmw/mwmechanics/difficultyscaling.cpp new file mode 100644 index 000000000..05ab12ccd --- /dev/null +++ b/apps/openmw/mwmechanics/difficultyscaling.cpp @@ -0,0 +1,38 @@ +#include "difficultyscaling.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwworld/esmstore.hpp" + +#include + +float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim) +{ + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + // [-100, 100] + int difficultySetting = Settings::Manager::getInt("difficulty", "Game"); + + static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get().find("fDifficultyMult")->getFloat(); + + float difficultyTerm = 0.01f * difficultySetting; + + float x = 0; + if (victim == player) + { + if (difficultyTerm > 0) + x = fDifficultyMult * difficultyTerm; + else + x = difficultyTerm / fDifficultyMult; + } + else if (attacker == player) + { + if (difficultyTerm > 0) + x = -difficultyTerm / fDifficultyMult; + else + x = fDifficultyMult * (-difficultyTerm); + } + + damage *= 1 + x; + return damage; +} diff --git a/apps/openmw/mwmechanics/difficultyscaling.hpp b/apps/openmw/mwmechanics/difficultyscaling.hpp new file mode 100644 index 000000000..168cf1055 --- /dev/null +++ b/apps/openmw/mwmechanics/difficultyscaling.hpp @@ -0,0 +1,12 @@ +#ifndef OPENMW_MWMECHANICS_DIFFICULTYSCALING_H +#define OPENMW_MWMECHANICS_DIFFICULTYSCALING_H + +namespace MWWorld +{ + class Ptr; +} + +/// Scales damage dealt to an actor based on difficulty setting +float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim); + +#endif diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 9ecae465c..5945c68d7 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,7 +1,7 @@ - + @@ -12,98 +12,115 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + - - + + + + - - + + + + + + + - - - + + + - + - - - + + + - + - - - + + + - + - - - - + + + + - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index fac2a0f97..ab9de47a4 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -170,6 +170,8 @@ always run = false # Always use the most powerful attack when striking with a weapon (chop, slash or thrust) best attack = false +difficulty = 0 + [Saves] character = # Save when resting From 9897f14c3c3e2e5d8587a4850431690ed0a538d3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 16:52:57 +0200 Subject: [PATCH 045/707] Don't block when actor is paralyzed --- apps/openmw/mwmechanics/combat.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 6e7bc9b94..1f26e8f7d 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -55,11 +55,24 @@ namespace MWMechanics if (!blocker.getClass().hasInventoryStore(blocker)) return false; - if (blocker.getClass().getCreatureStats(blocker).getKnockedDown() - || blocker.getClass().getCreatureStats(blocker).getHitRecovery()) + MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); + + if (blockerStats.getKnockedDown() // Used for both knockout or knockdown + || blockerStats.getHitRecovery() + || blockerStats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0) + return false; + + // Don't block when in spellcasting state (shield is equipped, but not visible) + if (blockerStats.getDrawState() == DrawState_Spell) return false; MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker); + + // Don't block when in hand-to-hand combat (shield is equipped, but not visible) + if (blockerStats.getDrawState() == DrawState_Weapon && + inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end()) + return false; + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name()) return false; @@ -73,17 +86,6 @@ namespace MWMechanics if (angle.valueDegrees() > gmst.find("fCombatBlockRightAngle")->getFloat()) return false; - MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); - - // Don't block when in spellcasting state (shield is equipped, but not visible) - if (blockerStats.getDrawState() == DrawState_Spell) - return false; - - // Don't block when in hand-to-hand combat (shield is equipped, but not visible) - if (blockerStats.getDrawState() == DrawState_Weapon && - inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end()) - return false; - MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2 * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() From 6d794dac6905e28e61c170c10dc47e6c7da6ffc7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 17:09:55 +0200 Subject: [PATCH 046/707] Add missing messagebox for spell lock and unlock success --- apps/openmw/mwmechanics/spellcasting.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 9253a9453..67da99200 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -496,7 +496,11 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::Lock) { if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude + { + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}"); target.getCellRef().setLockLevel(magnitude); + } } else if (effectId == ESM::MagicEffect::Open) { @@ -504,12 +508,14 @@ namespace MWMechanics { if (target.getCellRef().getLockLevel() > 0) { - //Door not already unlocked MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); if (!caster.isEmpty() && caster.getClass().isActor()) MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); + + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}"); } - target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel())); //unlocks the door + target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel())); } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); From 1636fd66dba12330c122f60b1ab5432b5d65f88d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 22:26:26 +0200 Subject: [PATCH 047/707] Don't add creature base damage to weapon damage (UESP was wrong) --- apps/openmw/mwclass/creature.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c93fcc298..ffd9eda26 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -308,13 +308,13 @@ namespace MWClass attack = weapon.get()->mBase->mData.mThrust; if(attack) { - float weaponDamage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); - weaponDamage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); + damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); if(weaphashealth) { int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); int weaphealth = weapon.getClass().getItemHealth(weapon); - weaponDamage *= float(weaphealth) / weapmaxhealth; + damage *= float(weaphealth) / weapmaxhealth; if (!MWBase::Environment::get().getWorld()->getGodModeState()) { @@ -329,8 +329,6 @@ namespace MWClass if (weapon.getCellRef().getCharge() == 0) weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr); } - - damage += weaponDamage; } // Apply "On hit" enchanted weapons From 90a96cd7d81c8c6b6397d8a8ed7cf54486381dee Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 22:34:20 +0200 Subject: [PATCH 048/707] Fix bug where actors in combat with multiple other actors where not regarded as in combat with a specific actor --- apps/openmw/mwmechanics/actors.cpp | 8 ++------ apps/openmw/mwmechanics/aisequence.cpp | 24 ++++++++++++++++++++++++ apps/openmw/mwmechanics/aisequence.hpp | 6 ++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6ea65275b..9a4cd4450 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1281,12 +1281,8 @@ namespace MWMechanics { const MWWorld::Class &cls = iter->getClass(); CreatureStats &stats = cls.getCreatureStats(*iter); - if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat) - { - MWMechanics::AiCombat* package = static_cast(stats.getAiSequence().getActivePackage()); - if(package->getTarget() == actor) - list.push_front(*iter); - } + if (!stats.isDead() && stats.getAiSequence().isInCombat(actor)) + list.push_front(*iter); } return list; } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 3aeeee65a..02f00dfc6 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -72,6 +72,30 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const return true; } +bool AiSequence::isInCombat() const +{ + for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + return true; + } + return false; +} + +bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const +{ + for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + { + const AiCombat *combat = static_cast(*it); + if (combat->getTarget() == actor) + return true; + } + } + return false; +} + bool AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) const { bool firstCombatFound = false; diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index b789d33cd..bc20dc61b 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -63,6 +63,12 @@ namespace MWMechanics /// Return true and assign target if combat package is currently active, return false otherwise bool getCombatTarget (MWWorld::Ptr &targetActor) const; + /// Is there any combat package? + bool isInCombat () const; + + /// Are we in combat with this particular actor? + bool isInCombat (const MWWorld::Ptr& actor) const; + bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const; ///< Function assumes that actor can have only 1 target apart player From 804f1a5e59769187d4ddcdc8f20cffab4167815e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 22:35:35 +0200 Subject: [PATCH 049/707] Adjust combat mechanics - Projectiles can not trigger critical hits - Critical hits are only possible if the target is not in combat (Fixes #1669) - Hand-to-hand deals damage to health during entire duration of knockdown animation (including standing up) --- apps/openmw/mwclass/npc.cpp | 22 ++++++++++++++-------- apps/openmw/mwmechanics/combat.cpp | 14 ++------------ apps/openmw/mwmechanics/creaturestats.hpp | 3 ++- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f81c37c10..67f955d1b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -600,8 +600,8 @@ namespace MWClass damage = stats.getSkill(weapskill).getModified(); damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); - healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f) - || (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0); + healthdmg = (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0) + || otherstats.getKnockedDown(); if(stats.isWerewolf()) { healthdmg = true; @@ -623,15 +623,21 @@ namespace MWClass sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f); } if(ptr.getRefData().getHandle() == "player") + { skillUsageSucceeded(ptr, weapskill, 0); - bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); - if(!detected) - { - damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); - MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); - MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence(); + + bool unaware = !seq.isInCombat() + && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); + if(unaware) + { + damage *= store.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } } + if (othercls.getCreatureStats(victim).getKnockedDown()) damage *= store.find("fCombatKODamageMult")->getFloat(); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 1f26e8f7d..c13eac98c 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -147,11 +147,7 @@ namespace MWMechanics float resistance = std::min(100.f, stats.getMagicEffects().get(ESM::MagicEffect::ResistNormalWeapons).mMagnitude - stats.getMagicEffects().get(ESM::MagicEffect::WeaknessToNormalWeapons).mMagnitude); - float multiplier = 0; - if (resistance >= 0) - multiplier = 1 - resistance / 100.f; - else - multiplier = -(resistance-100) / 100.f; + float multiplier = 1.f - resistance / 100.f; if (!(weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver || weapon.get()->mBase->mData.mFlags & ESM::Weapon::Magical)) @@ -213,16 +209,10 @@ namespace MWMechanics damage *= fDamageStrengthBase + (attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1); + if(attacker.getRefData().getHandle() == "player") attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0); - bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim); - if(!detected) - { - damage *= gmst.find("fCombatCriticalStrikeMult")->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()) damage *= gmst.find("fCombatKODamageMult")->getFloat(); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index b365a0b89..a83da4249 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -208,7 +208,8 @@ namespace MWMechanics float getEvasion() const; void setKnockedDown(bool value); - ///Returns true for the entire duration of the actor being knocked down + /// Returns true for the entire duration of the actor being knocked down or knocked out, + /// including transition animations (falling down & standing up) bool getKnockedDown() const; void setKnockedDownOneFrame(bool value); ///Returns true only for the first frame of the actor being knocked out; used for "onKnockedOut" command From 5629803a084a5621c23db440e97f77dec7c8499c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Jul 2014 23:08:22 +0200 Subject: [PATCH 050/707] Implement correct armor mitigation mechanics --- apps/openmw/mwclass/creature.cpp | 6 ++++++ apps/openmw/mwclass/npc.cpp | 19 +++++++++++++------ apps/openmw/mwclass/npc.hpp | 1 + 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index ffd9eda26..a74ee6fd6 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -394,6 +394,9 @@ namespace MWClass if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if (damage < 0.001f) + damage = 0; + if (damage > 0.f) { // Check for knockdown @@ -409,6 +412,8 @@ namespace MWClass else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + damage = std::max(1.f, damage); + if(ishealth) { if (!attacker.isEmpty()) @@ -639,6 +644,7 @@ namespace MWClass float Creature::getArmorRating (const MWWorld::Ptr& ptr) const { + // Note this is currently unused. Creatures do not use armor mitigation. return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 67f955d1b..b9c049987 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -280,6 +280,7 @@ namespace MWClass gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); gmst.fDamageStrengthBase = store.find("fDamageStrengthBase"); gmst.fDamageStrengthMult = store.find("fDamageStrengthMult"); + gmst.fCombatArmorMinMult = store.find("fCombatArmorMinMult"); inited = true; } @@ -706,6 +707,9 @@ namespace MWClass if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); + if (damage < 0.001f) + damage = 0; + if(damage > 0.0f) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying @@ -734,7 +738,7 @@ namespace MWClass else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? - if(ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage + if(damage > 0 && ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage { // Hit percentages: // cuirass = 30% @@ -754,9 +758,12 @@ namespace MWClass }; int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; - float damagediff = damage; - damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f); - damagediff -= damage; + float unmitigatedDamage = damage; + float x = damage / (damage + getArmorRating(ptr)); + damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x); + int damageDiff = unmitigatedDamage - damage; + if (damage < 1) + damage = 1; MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); @@ -764,13 +771,13 @@ namespace MWClass if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) { int armorhealth = armor.getClass().getItemHealth(armor); - armorhealth -= std::min(std::max(1, (int)damagediff), + armorhealth -= std::min(std::max(1, damageDiff), armorhealth); armor.getCellRef().setCharge(armorhealth); // Armor broken? unequip it if (armorhealth == 0) - inv.unequipItem(armor, ptr); + armor = *inv.unequipItem(armor, ptr); if (ptr.getRefData().getHandle() == "player") skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index d6b1f8c26..b0ca0a658 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -40,6 +40,7 @@ namespace MWClass const ESM::GameSetting *iKnockDownOddsBase; const ESM::GameSetting *fDamageStrengthBase; const ESM::GameSetting *fDamageStrengthMult; + const ESM::GameSetting *fCombatArmorMinMult; }; static const GMST& getGmst(); From 10d4da79944de255bc536aeb085c6259740b000e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 01:17:01 +0200 Subject: [PATCH 051/707] Fix date time in journal entries being off by one day (Fixes #1668) --- apps/openmw/mwgui/journalviewmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index b9ddb7daa..b71e64a19 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -259,7 +259,7 @@ struct JournalViewModelImpl : JournalViewModel os << itr->mDayOfMonth << ' ' << MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth) - << " (" << dayStr << " " << (itr->mDay + 1) << ')'; + << " (" << dayStr << " " << (itr->mDay) << ')'; timestamp_buffer = os.str (); } From d87630b41a0c5dc5deada97c2b3f363361f9554f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Jul 2014 09:34:10 +0200 Subject: [PATCH 052/707] blacklisting for scripts in OpenMW --- apps/openmw/engine.cpp | 14 ++++++++++++- apps/openmw/engine.hpp | 6 ++++++ apps/openmw/main.cpp | 20 ++++++++++++++----- apps/openmw/mwscript/scriptmanagerimp.cpp | 24 ++++++++++++++++++----- apps/openmw/mwscript/scriptmanagerimp.hpp | 4 +++- files/openmw.cfg | 1 + files/openmw.cfg.local | 1 + 7 files changed, 58 insertions(+), 12 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f8b4c9856..66eae5f5d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -180,6 +180,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mEncoder(NULL) , mActivationDistanceOverride(-1) , mGrab(true) + , mScriptBlacklistUse (true) { std::srand ( std::time(NULL) ); @@ -406,7 +407,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mScriptContext->setExtensions (&mExtensions); mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(), - mVerboseScripts, *mScriptContext, mWarningsMode)); + mVerboseScripts, *mScriptContext, mWarningsMode, + mScriptBlacklistUse ? mScriptBlacklist : std::vector())); // Create game mechanics system MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager; @@ -565,3 +567,13 @@ void OMW::Engine::setWarningsMode (int mode) { mWarningsMode = mode; } + +void OMW::Engine::setScriptBlacklist (const std::vector& list) +{ + mScriptBlacklist = list; +} + +void OMW::Engine::setScriptBlacklistUse (bool use) +{ + mScriptBlacklistUse = use; +} \ No newline at end of file diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index e0f51d0dc..203379a93 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -89,6 +89,8 @@ namespace OMW Files::Collections mFileCollections; bool mFSStrict; Translation::Storage mTranslationDataStorage; + std::vector mScriptBlacklist; + bool mScriptBlacklistUse; // not implemented Engine (const Engine&); @@ -181,6 +183,10 @@ namespace OMW void setWarningsMode (int mode); + void setScriptBlacklist (const std::vector& list); + + void setScriptBlacklistUse (bool use); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index adde408b9..8cdab74d8 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -144,6 +144,12 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat "\t1 - show warning but consider script as correctly compiled anyway\n" "\t2 - treat warnings as errors") + ("script-blacklist", bpo::value()->default_value(StringsVector(), "") + ->multitoken(), "ignore the specified script (if the use of the blacklist is enabled)") + + ("script-blacklist-use", bpo::value()->implicit_value(true) + ->default_value(true), "enable script blacklisting") + ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") @@ -241,15 +247,19 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setCell(variables["start"].as()); engine.setSkipMenu (variables["skip-menu"].as()); - // other settings - engine.setSoundUsage(!variables["no-sound"].as()); - engine.setScriptsVerbosity(variables["script-verbose"].as()); + // scripts engine.setCompileAll(variables["script-all"].as()); - engine.setFallbackValues(variables["fallback"].as().mMap); + engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); - engine.setActivationDistanceOverride (variables["activate-dist"].as()); engine.setWarningsMode (variables["script-warn"].as()); + engine.setScriptBlacklist (variables["script-blacklist"].as()); + engine.setScriptBlacklistUse (variables["script-blacklist-use"].as()); + + // other settings + engine.setSoundUsage(!variables["no-sound"].as()); + engine.setFallbackValues(variables["fallback"].as().mMap); + engine.setActivationDistanceOverride (variables["activate-dist"].as()); return true; } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 7b858dacf..781c16299 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -22,12 +23,19 @@ namespace MWScript { ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose, - Compiler::Context& compilerContext, int warningsMode) + Compiler::Context& compilerContext, int warningsMode, + const std::vector& scriptBlacklist) : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), mOpcodesInstalled (false), mGlobalScripts (store) { mErrorHandler.setWarningsMode (warningsMode); + + mScriptBlacklist.resize (scriptBlacklist.size()); + + std::transform (scriptBlacklist.begin(), scriptBlacklist.end(), + mScriptBlacklist.begin(), Misc::StringUtils::lowerCase); + std::sort (mScriptBlacklist.begin(), mScriptBlacklist.end()); } bool ScriptManager::compile (const std::string& name) @@ -133,11 +141,17 @@ namespace MWScript int success = 0; const MWWorld::Store& scripts = mStore.get(); - MWWorld::Store::iterator it = scripts.begin(); - for (; it != scripts.end(); ++it, ++count) - if (compile (it->mId)) - ++success; + for (MWWorld::Store::iterator iter = scripts.begin(); + iter != scripts.end(); ++iter) + if (!std::binary_search (mScriptBlacklist.begin(), mScriptBlacklist.end(), + Misc::StringUtils::lowerCase (iter->mId))) + { + ++count; + + if (compile (iter->mId)) + ++success; + } return std::make_pair (count, success); } diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index da3abc60b..4edc09eca 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -48,11 +48,13 @@ namespace MWScript ScriptCollection mScripts; GlobalScripts mGlobalScripts; std::map mOtherLocals; + std::vector mScriptBlacklist; public: ScriptManager (const MWWorld::ESMStore& store, bool verbose, - Compiler::Context& compilerContext, int warningsMode); + Compiler::Context& compilerContext, int warningsMode, + const std::vector& scriptBlacklist); virtual void run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) diff --git a/files/openmw.cfg b/files/openmw.cfg index b67b79a96..4ebe287d5 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -2,3 +2,4 @@ data="?global?data" data="?mw?Data Files" data-local="?userdata?data" resources=${OPENMW_RESOURCE_FILES} +script-blacklist=Museum diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index 6a578542d..4ae51382e 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -3,3 +3,4 @@ data="?mw?Data Files" data=./data data-local="?userdata?data" resources=./resources +script-blacklist=Museum From a9f9dec923927934c3adc9772b963cedd28e3874 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Jul 2014 12:15:21 +0200 Subject: [PATCH 053/707] consider script blacklist in OpenCS verifier --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/editor.cpp | 10 +++++++- apps/opencs/model/doc/blacklist.cpp | 31 +++++++++++++++++++++++ apps/opencs/model/doc/blacklist.hpp | 25 ++++++++++++++++++ apps/opencs/model/doc/document.cpp | 14 ++++++++-- apps/opencs/model/doc/document.hpp | 7 ++++- apps/opencs/model/doc/documentmanager.cpp | 7 ++++- apps/opencs/model/doc/documentmanager.hpp | 3 +++ apps/opencs/model/tools/scriptcheck.cpp | 21 ++++++++++----- apps/opencs/model/tools/scriptcheck.hpp | 9 +++++-- apps/opencs/model/tools/tools.cpp | 6 +++-- apps/opencs/model/tools/tools.hpp | 4 ++- 12 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 apps/opencs/model/doc/blacklist.cpp create mode 100644 apps/opencs/model/doc/blacklist.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index c03cc3138..e1e467889 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -9,7 +9,7 @@ opencs_units (model/doc ) opencs_units_noqt (model/doc - stage savingstate savingstages + stage savingstate savingstages blacklist ) opencs_hdrs_noqt (model/doc diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index b3513a7f1..44d66b236 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -86,7 +86,11 @@ std::pair > CS::Editor::readConfi ("encoding", boost::program_options::value()->default_value("win1252")) ("resources", boost::program_options::value()->default_value("resources")) ("fallback-archive", boost::program_options::value >()-> - default_value(std::vector(), "fallback-archive")->multitoken()); + default_value(std::vector(), "fallback-archive")->multitoken()) + ("script-blacklist", boost::program_options::value >()->default_value(std::vector(), "") + ->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)") + ("script-blacklist-use", boost::program_options::value()->implicit_value(true) + ->default_value(true), "enable script blacklisting"); boost::program_options::notify(variables); @@ -97,6 +101,10 @@ std::pair > CS::Editor::readConfi mDocumentManager.setResourceDir (mResources = variables["resources"].as()); + if (variables["script-blacklist-use"].as()) + mDocumentManager.setBlacklistedScripts ( + variables["script-blacklist"].as >()); + mFsStrict = variables["fs-strict"].as(); Files::PathContainer dataDirs, dataLocal; diff --git a/apps/opencs/model/doc/blacklist.cpp b/apps/opencs/model/doc/blacklist.cpp new file mode 100644 index 000000000..9b37a4302 --- /dev/null +++ b/apps/opencs/model/doc/blacklist.cpp @@ -0,0 +1,31 @@ + +#include "blacklist.hpp" + +#include + +#include + +bool CSMDoc::Blacklist::isBlacklisted (const CSMWorld::UniversalId& id) const +{ + std::map >::const_iterator iter = + mIds.find (id.getType()); + + if (iter==mIds.end()) + return false; + + return std::binary_search (iter->second.begin(), iter->second.end(), + Misc::StringUtils::lowerCase (id.getId())); +} + +void CSMDoc::Blacklist::add (CSMWorld::UniversalId::Type type, + const std::vector& ids) +{ + std::vector& list = mIds[type]; + + int size = list.size(); + + list.resize (size+ids.size()); + + std::transform (ids.begin(), ids.end(), list.begin()+size, Misc::StringUtils::lowerCase); + std::sort (list.begin(), list.end()); +} \ No newline at end of file diff --git a/apps/opencs/model/doc/blacklist.hpp b/apps/opencs/model/doc/blacklist.hpp new file mode 100644 index 000000000..9bf7f1d86 --- /dev/null +++ b/apps/opencs/model/doc/blacklist.hpp @@ -0,0 +1,25 @@ +#ifndef CSM_DOC_BLACKLIST_H +#define CSM_DOC_BLACKLIST_H + +#include +#include +#include + +#include "../world/universalid.hpp" + +namespace CSMDoc +{ + /// \brief ID blacklist sorted by UniversalId type + class Blacklist + { + std::map > mIds; + + public: + + bool isBlacklisted (const CSMWorld::UniversalId& id) const; + + void add (CSMWorld::UniversalId::Type type, const std::vector& ids); + }; +} + +#endif diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 23a47b313..c608757e0 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2205,9 +2205,10 @@ void CSMDoc::Document::createBase() CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, bool new_, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, - ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager) + ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, + const std::vector& blacklistedScripts) : mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager), - mTools (mData), mResDir(resDir), + mTools (*this), mResDir(resDir), mProjectPath ((configuration.getUserDataPath() / "projects") / (savePath.filename().string() + ".project")), mSaving (*this, mProjectPath, encoding) @@ -2239,6 +2240,8 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, createBase(); } + mBlacklist.add (CSMWorld::UniversalId::Type_Script, blacklistedScripts); + addOptionalGmsts(); addOptionalGlobals(); @@ -2358,6 +2361,13 @@ CSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId& return mTools.getReport (id); } +bool CSMDoc::Document::isBlacklisted (const CSMWorld::UniversalId& id) + const +{ + return mBlacklist.isBlacklisted (id); +} + + void CSMDoc::Document::progress (int current, int max, int type) { emit progress (current, max, type, 1, this); diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index d092b47c8..d0e94d5e0 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -17,6 +17,7 @@ #include "state.hpp" #include "saving.hpp" +#include "blacklist.hpp" class QAbstractItemModel; @@ -52,6 +53,7 @@ namespace CSMDoc boost::filesystem::path mProjectPath; Saving mSaving; boost::filesystem::path mResDir; + Blacklist mBlacklist; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. @@ -78,7 +80,8 @@ namespace CSMDoc Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, bool new_, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, - ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager); + ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager, + const std::vector& blacklistedScripts); ~Document(); @@ -110,6 +113,8 @@ namespace CSMDoc CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id); ///< The ownership of the returned report is not transferred. + bool isBlacklisted (const CSMWorld::UniversalId& id) const; + signals: void stateChanged (int state, CSMDoc::Document *document); diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 6953db0ed..9b807225c 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -52,7 +52,7 @@ CSMDoc::DocumentManager::~DocumentManager() void CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { - Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager); + Document *document = new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mResourcesManager, mBlacklistedScripts); mDocuments.push_back (document); @@ -85,6 +85,11 @@ void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding) mEncoding = encoding; } +void CSMDoc::DocumentManager::setBlacklistedScripts (const std::vector& scriptIds) +{ + mBlacklistedScripts = scriptIds; +} + void CSMDoc::DocumentManager::listResources() { mResourcesManager.listResources(); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index cebae6f6d..c545b9a9f 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -34,6 +34,7 @@ namespace CSMDoc Loader mLoader; ToUTF8::FromType mEncoding; CSMWorld::ResourcesManager mResourcesManager; + std::vector mBlacklistedScripts; DocumentManager (const DocumentManager&); DocumentManager& operator= (const DocumentManager&); @@ -53,6 +54,8 @@ namespace CSMDoc void setEncoding (ToUTF8::FromType encoding); + void setBlacklistedScripts (const std::vector& scriptIds); + /// Ask OGRE for a list of available resources. void listResources(); diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index b989e22a2..d2c647bda 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -7,6 +7,8 @@ #include #include +#include "../doc/document.hpp" + #include "../world/data.hpp" void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc, @@ -37,8 +39,8 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) (type==ErrorMessage ? "error: " : "warning: ") + message)); } -CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMWorld::Data& data) -: mData (data), mContext (data), mMessages (0) +CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document) +: mDocument (document), mContext (document.getData()), mMessages (0) { /// \todo add an option to configure warning mode setWarningsMode (0); @@ -53,18 +55,25 @@ int CSMTools::ScriptCheckStage::setup() mMessages = 0; mId.clear(); - return mData.getScripts().getSize(); + return mDocument.getData().getScripts().getSize(); } void CSMTools::ScriptCheckStage::perform (int stage, Messages& messages) { + mId = mDocument.getData().getScripts().getId (stage); + + if (mDocument.isBlacklisted ( + CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId))) + return; + mMessages = &messages; - mId = mData.getScripts().getId (stage); try { - mFile = mData.getScripts().getRecord (stage).get().mId; - std::istringstream input (mData.getScripts().getRecord (stage).get().mScriptText); + const CSMWorld::Data& data = mDocument.getData(); + + mFile = data.getScripts().getRecord (stage).get().mId; + std::istringstream input (data.getScripts().getRecord (stage).get().mScriptText); Compiler::Scanner scanner (*this, input, mContext.getExtensions()); diff --git a/apps/opencs/model/tools/scriptcheck.hpp b/apps/opencs/model/tools/scriptcheck.hpp index ecf8d61b7..75f11b9d4 100644 --- a/apps/opencs/model/tools/scriptcheck.hpp +++ b/apps/opencs/model/tools/scriptcheck.hpp @@ -8,12 +8,17 @@ #include "../world/scriptcontext.hpp" +namespace CSMDoc +{ + class Document; +} + namespace CSMTools { /// \brief VerifyStage: make sure that scripts compile class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler { - const CSMWorld::Data& mData; + const CSMDoc::Document& mDocument; Compiler::Extensions mExtensions; CSMWorld::ScriptContext mContext; std::string mId; @@ -28,7 +33,7 @@ namespace CSMTools public: - ScriptCheckStage (const CSMWorld::Data& data); + ScriptCheckStage (const CSMDoc::Document& document); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 2f93db48e..fcf98868a 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -5,6 +5,7 @@ #include "../doc/state.hpp" #include "../doc/operation.hpp" +#include "../doc/document.hpp" #include "../world/data.hpp" #include "../world/universalid.hpp" @@ -80,13 +81,14 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); - mVerifier->appendStage (new ScriptCheckStage (mData)); + mVerifier->appendStage (new ScriptCheckStage (mDocument)); } return mVerifier; } -CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0) +CSMTools::Tools::Tools (CSMDoc::Document& document) +: mDocument (document), mData (document.getData()), mVerifier (0), mNextReportNumber (0) { // index 0: load error log mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 3394d3f62..4d677142d 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -14,6 +14,7 @@ namespace CSMWorld namespace CSMDoc { class Operation; + class Document; } namespace CSMTools @@ -24,6 +25,7 @@ namespace CSMTools { Q_OBJECT + CSMDoc::Document& mDocument; CSMWorld::Data& mData; CSMDoc::Operation *mVerifier; std::map mReports; @@ -44,7 +46,7 @@ namespace CSMTools public: - Tools (CSMWorld::Data& data); + Tools (CSMDoc::Document& document); virtual ~Tools(); From 11a2c767cc474a3a8c1f3b831d5a67b7e7449c72 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Jul 2014 12:50:29 +0200 Subject: [PATCH 054/707] some argument parsing cleanup --- components/compiler/exprparser.cpp | 30 +++++++++++++++++++----------- components/compiler/extensions.hpp | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index d0cda0ec2..6e29626b1 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -777,11 +777,22 @@ namespace Compiler ++optionalCount; } } + else if (*iter=='X') + { + parser.reset(); + + parser.setOptional (true); + + scanner.scan (parser); + + if (optional && parser.isEmpty()) + break; + } else { parser.reset(); - if (optional || *iter == 'X') + if (optional) parser.setOptional (true); scanner.scan (parser); @@ -789,20 +800,17 @@ namespace Compiler if (optional && parser.isEmpty()) break; - if (*iter != 'X') - { - std::vector tmp; + std::vector tmp; - char type = parser.append (tmp); + char type = parser.append (tmp); - if (type!=*iter) - Generator::convert (tmp, type, *iter); + if (type!=*iter) + Generator::convert (tmp, type, *iter); - stack.push (tmp); + stack.push (tmp); - if (optional) - ++optionalCount; - } + if (optional) + ++optionalCount; } } diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index d229751de..aa62fd0eb 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -21,7 +21,7 @@ namespace Compiler s - Short
S - String, case preserved
x - Optional, ignored string argument - X - Optional, ignored integer argument + X - Optional, ignored float argument **/ typedef std::string ScriptArgs; From 127add7623e92ccbff375fbf676e615ebf09ad72 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 18:19:45 +0200 Subject: [PATCH 055/707] Don't allow dialogue with werewolf NPCs --- apps/openmw/mwclass/npc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b9c049987..9042d4231 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -885,6 +885,7 @@ namespace MWClass if(ptr.getRefData().getHandle() == "player") return boost::shared_ptr(new MWWorld::ActionTalk(actor)); + // Werewolfs can't activate NPCs if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -895,6 +896,7 @@ namespace MWClass return action; } + if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); if(ptr.getClass().getCreatureStats(ptr).isHostile()) @@ -902,8 +904,10 @@ namespace MWClass if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)) return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing + // Can't talk to werewolfs + if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf()) + return boost::shared_ptr (new MWWorld::FailedAction("")); return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); - } MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) From d43c21a9fc73532c06c1f99e31ecf90486af6e41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 20:36:26 +0200 Subject: [PATCH 056/707] Make NPCs react to being attacked by other actors (Fixes #1588) --- apps/openmw/mwclass/npc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9042d4231..49e016d4f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -680,6 +680,14 @@ namespace MWClass !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + if (!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr) + && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)) + { + // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. + // Note: accidental or collateral damage attacks are ignored. + MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); + } + bool wasDead = getCreatureStats(ptr).isDead(); getCreatureStats(ptr).setAttacked(true); From c0645d4978fcf71f7449df6d1a39b1c408780d10 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 20:37:14 +0200 Subject: [PATCH 057/707] Increase death count immediately on death (Bug #1588) This is required for scripts using getDeadCount as reaction to onDeath that rely on the increased value. --- apps/openmw/mwbase/mechanicsmanager.hpp | 4 + apps/openmw/mwmechanics/actors.cpp | 137 ++++++++++-------- apps/openmw/mwmechanics/actors.hpp | 3 + apps/openmw/mwmechanics/creaturestats.cpp | 7 + .../mwmechanics/mechanicsmanagerimp.cpp | 4 + .../mwmechanics/mechanicsmanagerimp.hpp | 4 + 6 files changed, 100 insertions(+), 59 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index f2b71bd4c..f718972b9 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -196,6 +196,10 @@ namespace MWBase /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0; + + /// Usually done once a frame, but can be invoked manually in time-critical situations. + /// This will increase the death count for any actors that were killed. + virtual void killDeadActors() = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9a4cd4450..809e948e7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1029,10 +1029,6 @@ namespace MWMechanics iter->second->update(duration); } - // Kill dead actors, update some variables - - int hostilesCount = 0; // need to know this to play Battle music - for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const MWWorld::Class &cls = iter->first.getClass(); @@ -1048,68 +1044,23 @@ namespace MWMechanics stats.setKnockedDownOneFrame(false); stats.setKnockedDownOverOneFrame(true); } + } - if(!stats.isDead()) - { - if (stats.isHostile()) hostilesCount++; - - if(iter->second->isDead()) - { - // Actor has been resurrected. Notify the CharacterController and re-enable collision. - MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); - iter->second->resurrect(); - } - - if(!stats.isDead()) - continue; - } - - // If it's the player and God Mode is turned on, keep it alive - if (iter->first.getRefData().getHandle()=="player" && - MWBase::Environment::get().getWorld()->getGodModeState()) - { - MWMechanics::DynamicStat stat (stats.getHealth()); + int hostilesCount = 0; // need to know this to play Battle music - if (stat.getModified()<1) - { - stat.setModified(1, 0); - stats.setHealth(stat); - } - stats.resurrect(); - continue; - } + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + const MWWorld::Class &cls = iter->first.getClass(); + CreatureStats &stats = cls.getCreatureStats(iter->first); - if (iter->second->kill()) + if(!stats.isDead()) { - ++mDeathCount[cls.getId(iter->first)]; - - // Make sure spell effects with CasterLinked flag are removed - for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) - { - MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); - spells.purge(stats.getActorId()); - } - - // Apply soultrap - if (iter->first.getTypeName() == typeid(ESM::Creature).name()) - { - SoulTrap soulTrap (iter->first); - stats.getActiveSpells().visitEffectSources(soulTrap); - } - - // Reset magic effects and recalculate derived effects - // One case where we need this is to make sure bound items are removed upon death - stats.setMagicEffects(MWMechanics::MagicEffects()); - stats.getActiveSpells().clear(); - calculateCreatureStatModifiers(iter->first, 0); - - MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); - - if (cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + if (stats.isHostile()) hostilesCount++; } } + killDeadActors(); + // check if we still have any player enemies to switch music static bool isBattleMusic = false; @@ -1184,6 +1135,74 @@ namespace MWMechanics } } } + + void Actors::killDeadActors() + { + for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + { + const MWWorld::Class &cls = iter->first.getClass(); + CreatureStats &stats = cls.getCreatureStats(iter->first); + + if(!stats.isDead()) + { + if(iter->second->isDead()) + { + // Actor has been resurrected. Notify the CharacterController and re-enable collision. + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true); + iter->second->resurrect(); + } + + if(!stats.isDead()) + continue; + } + + // If it's the player and God Mode is turned on, keep it alive + if (iter->first.getRefData().getHandle()=="player" && + MWBase::Environment::get().getWorld()->getGodModeState()) + { + MWMechanics::DynamicStat stat (stats.getHealth()); + + if (stat.getModified()<1) + { + stat.setModified(1, 0); + stats.setHealth(stat); + } + stats.resurrect(); + continue; + } + + if (iter->second->kill()) + { + ++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())]; + + // Make sure spell effects with CasterLinked flag are removed + for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) + { + MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); + spells.purge(stats.getActorId()); + } + + // Apply soultrap + if (iter->first.getTypeName() == typeid(ESM::Creature).name()) + { + SoulTrap soulTrap (iter->first); + stats.getActiveSpells().visitEffectSources(soulTrap); + } + + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.setMagicEffects(MWMechanics::MagicEffects()); + stats.getActiveSpells().clear(); + calculateCreatureStatModifiers(iter->first, 0); + + MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); + + if (cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } + } + } + void Actors::restoreDynamicStats(bool sleep) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 4784162f4..cda6abaea 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -96,6 +96,9 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. + ///@see MechanicsManager::killDeadActors + void killDeadActors (); + void forceStateUpdate(const MWWorld::Ptr &ptr); void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index af35a109a..71217dbb9 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" namespace MWMechanics { @@ -191,6 +192,12 @@ namespace MWMechanics mDied = true; mDead = true; + + if (mDied) + // Must increase death count immediately. There are scripts that use getDeadCount as reaction to onDeath + // and rely on the increased value. + // Would be more appropriate to use a killActor(actor) function, but we don't have access to the Ptr in CreatureStats. + MWBase::Environment::get().getMechanicsManager()->killDeadActors(); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d7c3e2f56..2d3bc066f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -653,6 +653,10 @@ namespace MWMechanics return mActors.countDeaths (id); } + void MechanicsManager::killDeadActors() + { + mActors.killDeadActors(); + } void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 596e6887e..365e24067 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -160,6 +160,10 @@ namespace MWMechanics /// @param bias Can be used to add an additional aggression bias towards the target, /// making it more likely for the function to return true. virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false); + + /// Usually done once a frame, but can be invoked manually in time-critical situations. + /// This will increase the death count for any actors that were killed. + virtual void killDeadActors(); }; } From 038a811f16797148519d62112d263db2c34c178e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Jul 2014 23:44:33 +0200 Subject: [PATCH 058/707] Rename travel window Ok button to Cancel (Fixes #1676) --- files/mygui/openmw_travel_window.layout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/mygui/openmw_travel_window.layout b/files/mygui/openmw_travel_window.layout index 683d47fe7..30b470e08 100644 --- a/files/mygui/openmw_travel_window.layout +++ b/files/mygui/openmw_travel_window.layout @@ -27,7 +27,7 @@
- +
From 2b57c7fa6788199d9ea1bd3e36b84a8e499790ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 17:05:05 +0200 Subject: [PATCH 059/707] Use CellRef's global variable to disable owner (Fixes #1677) --- apps/esmtool/esmtool.cpp | 1 + apps/openmw/mwclass/activator.cpp | 1 + apps/openmw/mwclass/apparatus.cpp | 1 + apps/openmw/mwclass/armor.cpp | 1 + apps/openmw/mwclass/book.cpp | 1 + apps/openmw/mwclass/clothing.cpp | 1 + apps/openmw/mwclass/container.cpp | 1 + apps/openmw/mwclass/door.cpp | 1 + apps/openmw/mwclass/ingredient.cpp | 1 + apps/openmw/mwclass/light.cpp | 1 + apps/openmw/mwclass/lockpick.cpp | 1 + apps/openmw/mwclass/misc.cpp | 1 + apps/openmw/mwclass/potion.cpp | 1 + apps/openmw/mwclass/probe.cpp | 1 + apps/openmw/mwclass/repair.cpp | 1 + apps/openmw/mwclass/weapon.cpp | 1 + apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 7 +++++++ apps/openmw/mwworld/cellref.cpp | 5 +++++ apps/openmw/mwworld/cellref.hpp | 5 +++++ components/esm/cellref.cpp | 6 +++--- components/esm/cellref.hpp | 9 +++++---- 21 files changed, 41 insertions(+), 7 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index eef96c8c9..a09b87ad4 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -249,6 +249,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl; std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; + std::cout << " Global: '" << ref.mGlobalVariable << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 043aadd35..37189e05f 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -99,6 +99,7 @@ namespace MWClass { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index d61ba038a..a27762a57 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -129,6 +129,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index b29bf36b2..71426c723 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -249,6 +249,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 0adee57e3..e7b811936 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -141,6 +141,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index dc98e323e..bf3d0c12c 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -195,6 +195,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 53add4274..aba3b8466 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -237,6 +237,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 677ad462e..e75eda726 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -254,6 +254,7 @@ namespace MWClass text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); } info.text = text; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 683092923..17ae61c0d 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -149,6 +149,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 8a2c20f69..611c86f67 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -189,6 +189,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index bc6855129..5dc7180b2 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -143,6 +143,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1044fb01d..c5796ddbd 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -184,6 +184,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 440121d35..66555444f 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -153,6 +153,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index ed8625eec..61609bde0 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -142,6 +142,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index d7a080534..f1f947d77 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -146,6 +146,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 66affa599..2208d717a 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -353,6 +353,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); + text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2d3bc066f..1f81eda7d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -38,6 +38,13 @@ namespace isFactionOwned = true; } + const std::string& globalVariable = item.getCellRef().getGlobalVariable(); + if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1) + { + isOwned = false; + isFactionOwned = false; + } + if (!item.getCellRef().getOwner().empty()) victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().getOwner(), true); diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index f16d8e3d1..056737be6 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -88,6 +88,11 @@ namespace MWWorld return mCellRef.mOwner; } + std::string CellRef::getGlobalVariable() const + { + return mCellRef.mGlobalVariable; + } + void CellRef::setOwner(const std::string &owner) { if (owner != mCellRef.mOwner) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 689671c02..e9ef343fc 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -61,6 +61,11 @@ namespace MWWorld std::string getOwner() const; void setOwner(const std::string& owner); + // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed + // even if it has an Owner field. + // Used by bed rent scripts to allow the player to use the bed for the duration of the rent. + std::string getGlobalVariable() const; + // ID of creature trapped in this soul gem std::string getSoul() const; void setSoul(const std::string& soul); diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 84c638d9c..409ae02d0 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -27,7 +27,7 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHNOT (mScale, "XSCL"); mOwner = esm.getHNOString ("ANAM"); - mGlob = esm.getHNOString ("BNAM"); + mGlobalVariable = esm.getHNOString ("BNAM"); mSoul = esm.getHNOString ("XSOL"); mFaction = esm.getHNOString ("CNAM"); @@ -90,7 +90,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons } esm.writeHNOCString("ANAM", mOwner); - esm.writeHNOCString("BNAM", mGlob); + esm.writeHNOCString("BNAM", mGlobalVariable); esm.writeHNOCString("XSOL", mSoul); esm.writeHNOCString("CNAM", mFaction); @@ -144,7 +144,7 @@ void ESM::CellRef::blank() mRefID.clear(); mScale = 1; mOwner.clear(); - mGlob.clear(); + mGlobalVariable.clear(); mSoul.clear(); mFaction.clear(); mFactIndex = -1; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index b87578120..25ea243ef 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -35,12 +35,13 @@ namespace ESM float mScale; // Scale applied to mesh - // The NPC that owns this object (and will get angry if you steal - // it) + // The NPC that owns this object (and will get angry if you steal it) std::string mOwner; - // I have no idea, looks like a link to a global variable? - std::string mGlob; + // Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed + // even if it has an Owner field. + // Used by bed rent scripts to allow the player to use the bed for the duration of the rent. + std::string mGlobalVariable; // ID of creature trapped in this soul gem std::string mSoul; From ca45a63cf79d262048972c5a3eec44f9288baeb7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 17:55:54 +0200 Subject: [PATCH 060/707] Close doors instantly when using Lock instruction --- apps/openmw/mwbase/world.hpp | 5 +++-- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 8 +++++++ apps/openmw/mwworld/worldimp.cpp | 28 ++++++++++++++----------- apps/openmw/mwworld/worldimp.hpp | 5 +++-- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 49ac5bc15..29f326a1b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -398,8 +398,9 @@ namespace MWBase /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door) = 0; - /// open or close a non-teleport door as specified - virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0; + /// update movement state of a non-teleport door as specified + /// @param state see MWClass::setDoorState + virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0; virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index e75eda726..5e7534660 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -62,7 +62,7 @@ namespace MWClass const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); if (customData.mDoorState > 0) { - MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false); + MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState); } } } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a4c74be6b..4e0257d82 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -147,6 +147,14 @@ namespace MWScript } ptr.getClass().lock (ptr, lockLevel); + + // Instantly reset door to closed state + // This is done when using Lock in scripts, but not when using Lock spells. + if (ptr.getTypeName() == typeid(ESM::Door).name()) + { + MWBase::Environment::get().getWorld()->activateDoor(ptr, 0); + MWBase::Environment::get().getWorld()->localRotateObject(ptr, 0, 0, 0); + } } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 80ff0fdfa..fb520dae4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1123,6 +1123,9 @@ namespace MWWorld ptr.getRefData().setPosition(pos); + if(ptr.getRefData().getBaseNode() == 0) + return; + if (ptr.getClass().isActor()) mWorldScene->updateObjectRotation(ptr); else @@ -1131,19 +1134,19 @@ namespace MWWorld void World::localRotateObject (const Ptr& ptr, float x, float y, float z) { - if (ptr.getRefData().getBaseNode() != 0) - { - LocalRotation rot = ptr.getRefData().getLocalRotation(); - rot.rot[0]=Ogre::Degree(x).valueRadians(); - rot.rot[1]=Ogre::Degree(y).valueRadians(); - rot.rot[2]=Ogre::Degree(z).valueRadians(); + LocalRotation rot = ptr.getRefData().getLocalRotation(); + rot.rot[0]=Ogre::Degree(x).valueRadians(); + rot.rot[1]=Ogre::Degree(y).valueRadians(); + rot.rot[2]=Ogre::Degree(z).valueRadians(); - wrap(rot.rot[0]); - wrap(rot.rot[1]); - wrap(rot.rot[2]); + wrap(rot.rot[0]); + wrap(rot.rot[1]); + wrap(rot.rot[2]); - ptr.getRefData().setLocalRotation(rot); + ptr.getRefData().setLocalRotation(rot); + if (ptr.getRefData().getBaseNode() != 0) + { mWorldScene->updateObjectLocalRotation(ptr); } } @@ -1983,11 +1986,12 @@ namespace MWWorld mDoorStates[door] = state; } - void World::activateDoor(const Ptr &door, bool open) + void World::activateDoor(const Ptr &door, int state) { - int state = open ? 1 : 2; door.getClass().setDoorState(door, state); mDoorStates[door] = state; + if (state == 0) + mDoorStates.erase(door); } bool World::getPlayerStandingOn (const MWWorld::Ptr& object) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 08d7eb42d..dda442963 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -473,8 +473,9 @@ namespace MWWorld /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door); - /// open or close a non-teleport door as specified - virtual void activateDoor(const MWWorld::Ptr& door, bool open); + /// update movement state of a non-teleport door as specified + /// @param state see MWClass::setDoorState + virtual void activateDoor(const MWWorld::Ptr& door, int state); virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object From aa8c0bccb4b8c304bcdef0cbdda0f808aa31d437 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 23 Jul 2014 09:44:29 +0200 Subject: [PATCH 061/707] added new argument type: z (optional, any) --- components/CMakeLists.txt | 2 +- components/compiler/discardparser.cpp | 70 +++++++++++++++++++++++++++ components/compiler/discardparser.hpp | 45 +++++++++++++++++ components/compiler/exprparser.cpp | 14 +++++- components/compiler/extensions.hpp | 3 +- 5 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 components/compiler/discardparser.cpp create mode 100644 components/compiler/discardparser.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index b8ebb84b1..a9bd376a9 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -58,7 +58,7 @@ add_component_dir (compiler context controlparser errorhandler exception exprparser extensions fileparser generator lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser - quickfileparser + quickfileparser discardparser ) add_component_dir (interpreter diff --git a/components/compiler/discardparser.cpp b/components/compiler/discardparser.cpp new file mode 100644 index 000000000..6028968bb --- /dev/null +++ b/components/compiler/discardparser.cpp @@ -0,0 +1,70 @@ + +#include "discardparser.hpp" + +#include "scanner.hpp" + +namespace Compiler +{ + DiscardParser::DiscardParser (ErrorHandler& errorHandler, const Context& context) + : Parser (errorHandler, context), mState (StartState) + { + + } + + bool DiscardParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) + { + if (mState==StartState || mState==CommaState || mState==MinusState) + { + start(); + return false; + } + + return Parser::parseInt (value, loc, scanner); + } + + bool DiscardParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) + { + if (mState==StartState || mState==CommaState || mState==MinusState) + { + start(); + return false; + } + + return Parser::parseFloat (value, loc, scanner); + } + + bool DiscardParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + if (mState==StartState || mState==CommaState) + { + start(); + return false; + } + + return Parser::parseName (name, loc, scanner); + } + + bool DiscardParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_comma && mState==StartState) + { + mState = CommaState; + return true; + } + + if (code==Scanner::S_minus && (mState==StartState || mState==CommaState)) + { + mState = MinusState; + return true; + } + + return Parser::parseSpecial (code, loc, scanner); + } + + void DiscardParser::reset() + { + mState = StartState; + Parser::reset(); + } +} diff --git a/components/compiler/discardparser.hpp b/components/compiler/discardparser.hpp new file mode 100644 index 000000000..bee8a87bb --- /dev/null +++ b/components/compiler/discardparser.hpp @@ -0,0 +1,45 @@ +#ifndef COMPILER_DISCARDPARSER_H_INCLUDED +#define COMPILER_DISCARDPARSER_H_INCLUDED + +#include "parser.hpp" + +namespace Compiler +{ + /// \brief Parse a single optional numeric value or string and discard it + class DiscardParser : public Parser + { + enum State + { + StartState, CommaState, MinusState + }; + + State mState; + + public: + + DiscardParser (ErrorHandler& errorHandler, const Context& context); + + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + + virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + + virtual void reset(); + ///< Reset parser to clean state. + }; +} + +#endif + diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 6e29626b1..d94f6c436 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -16,6 +16,7 @@ #include "stringparser.hpp" #include "extensions.hpp" #include "context.hpp" +#include "discardparser.hpp" namespace Compiler { @@ -743,6 +744,7 @@ namespace Compiler ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true); StringParser stringParser (getErrorHandler(), getContext(), mLiterals); + DiscardParser discardParser (getErrorHandler(), getContext()); std::stack > stack; @@ -785,7 +787,17 @@ namespace Compiler scanner.scan (parser); - if (optional && parser.isEmpty()) + if (parser.isEmpty()) + break; + } + else if (*iter=='z') + { + discardParser.reset(); + discardParser.setOptional (true); + + scanner.scan (discardParser); + + if (discardParser.isEmpty()) break; } else diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index aa62fd0eb..a15218d99 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -21,7 +21,8 @@ namespace Compiler s - Short
S - String, case preserved
x - Optional, ignored string argument - X - Optional, ignored float argument + X - Optional, ignored numeric expression + z - Optional, ignored string or numeric argument **/ typedef std::string ScriptArgs; From 19f4c46fe4eac193e0423dca48803f7b48af301e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 23 Jul 2014 12:33:35 +0200 Subject: [PATCH 062/707] alllow (and ignore) explicit references for StopScript and ScriptRunning --- components/compiler/exprparser.cpp | 15 +++++++++++++++ components/compiler/lineparser.cpp | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index d94f6c436..6dcca08df 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -408,6 +408,21 @@ namespace Compiler mNextOperand = false; return true; } + else if (keyword==Scanner::K_scriptrunning) + { + start(); + + mTokenLoc = loc; + parseArguments ("c", scanner); + + Generator::scriptRunning (mCode); + mOperands.push_back ('l'); + + mExplicit.clear(); + mRefOp = false; + mNextOperand = false; + return true; + } // check for custom extensions if (const Extensions *extensions = getContext().getExtensions()) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index b1b831bc2..cdbfaa04a 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -269,6 +269,13 @@ namespace Compiler Generator::startScript (mCode, mLiterals, mExplicit); mState = EndState; return true; + + case Scanner::K_stopscript: + + mExprParser.parseArguments ("c", scanner, mCode); + Generator::stopScript (mCode); + mState = EndState; + return true; } // check for custom extensions From f55084463b47cb3bee1d420f7badee83de0f710f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 23 Jul 2014 12:35:15 +0200 Subject: [PATCH 063/707] added MockChangeScript to script blacklist --- files/openmw.cfg | 1 + files/openmw.cfg.local | 1 + 2 files changed, 2 insertions(+) diff --git a/files/openmw.cfg b/files/openmw.cfg index 4ebe287d5..de1f22e50 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -3,3 +3,4 @@ data="?mw?Data Files" data-local="?userdata?data" resources=${OPENMW_RESOURCE_FILES} script-blacklist=Museum +script-blacklist=MockChangeScript \ No newline at end of file diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index 4ae51382e..b2970fb19 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -4,3 +4,4 @@ data=./data data-local="?userdata?data" resources=./resources script-blacklist=Museum +script-blacklist=MockChangeScript From 432fb751bf505eb6e6c724b2aacf1c5e4d09617d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aciubid=C5=82o?= Date: Wed, 23 Jul 2014 21:25:28 +0100 Subject: [PATCH 064/707] Implement fatigue restoration to match Morrowind. Fatigue restoration doesn't depend on encuberance or EndFatigueMult. --- apps/openmw/mwmechanics/actors.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6ea65275b..92d5ac68e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -326,11 +326,9 @@ namespace MWMechanics // restore fatigue float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); - float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); - - float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); - x *= fEndFatigueMult * endurance; + float x = fFatigueReturnBase + fFatigueReturnMult * endurance; + DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); From 75366b2e37bb1db5ae69644a552c944d8a21f1e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 20:03:35 +0200 Subject: [PATCH 065/707] Implement CellRef's faction rank (rank requirement to use item) --- apps/esmtool/esmtool.cpp | 2 ++ apps/opencs/model/world/columnimp.hpp | 4 ++-- apps/openmw/mwclass/activator.cpp | 4 +--- apps/openmw/mwclass/apparatus.cpp | 4 +--- apps/openmw/mwclass/armor.cpp | 4 +--- apps/openmw/mwclass/book.cpp | 4 +--- apps/openmw/mwclass/clothing.cpp | 4 +--- apps/openmw/mwclass/container.cpp | 4 +--- apps/openmw/mwclass/door.cpp | 4 +--- apps/openmw/mwclass/ingredient.cpp | 4 +--- apps/openmw/mwclass/light.cpp | 4 +--- apps/openmw/mwclass/lockpick.cpp | 4 +--- apps/openmw/mwclass/misc.cpp | 4 +--- apps/openmw/mwclass/potion.cpp | 4 +--- apps/openmw/mwclass/probe.cpp | 4 +--- apps/openmw/mwclass/repair.cpp | 4 +--- apps/openmw/mwclass/weapon.cpp | 4 +--- apps/openmw/mwgui/tooltips.cpp | 11 +++++++++++ apps/openmw/mwgui/tooltips.hpp | 3 +++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 +++- apps/openmw/mwworld/cellref.cpp | 5 +++++ apps/openmw/mwworld/cellref.hpp | 3 +++ apps/openmw/mwworld/manualref.hpp | 2 +- components/esm/cellref.cpp | 10 +++++----- components/esm/cellref.hpp | 5 ++--- 25 files changed, 52 insertions(+), 57 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a09b87ad4..ea908590a 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -250,6 +250,8 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Global: '" << ref.mGlobalVariable << "'" << std::endl; + std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; + std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 0c033593c..3fad05f65 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -977,13 +977,13 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return record.get().mFactIndex; + return record.get().mFactionRank; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mFactIndex = data.toInt(); + record2.mFactionRank = data.toInt(); record.setModified (record2); } diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 37189e05f..5bd144a73 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -97,9 +97,7 @@ namespace MWClass std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index a27762a57..a162c3edd 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -127,9 +127,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 71426c723..e102e7cf1 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -247,9 +247,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index e7b811936..824b69daf 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -139,9 +139,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index bf3d0c12c..bbe5f60bf 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -193,9 +193,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index aba3b8466..362b97902 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -235,9 +235,7 @@ namespace MWClass text += "\n#{sTrapped}"; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 5e7534660..d0ba37f0b 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -251,10 +251,8 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); } info.text = text; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 17ae61c0d..fa03f23ff 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -147,9 +147,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 611c86f67..8237b07f2 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -187,9 +187,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 5dc7180b2..b0129f403 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -141,9 +141,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index c5796ddbd..74b10ee3d 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -182,9 +182,7 @@ namespace MWClass } if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 66555444f..734385a6a 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -151,9 +151,7 @@ namespace MWClass info.isPotion = true; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 61609bde0..5df7da2b0 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -140,9 +140,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index f1f947d77..ed09a06da 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -144,9 +144,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 2208d717a..6f75095ff 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -351,9 +351,7 @@ namespace MWClass info.remainingEnchantCharge = ptr.getCellRef().getEnchantmentCharge(); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction"); - text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getGlobalVariable(), "Global"); + text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 676a9ee63..e09ff2487 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -547,6 +547,17 @@ namespace MWGui return " (" + boost::lexical_cast(value) + ")"; } + std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref) + { + std::string ret; + ret += getMiscString(cellref.getOwner(), "Owner"); + ret += getMiscString(cellref.getFaction(), "Faction"); + if (cellref.getFactionRank() > 0) + ret += getValueString(cellref.getFactionRank(), "Rank"); + ret += getMiscString(cellref.getGlobalVariable(), "Global"); + return ret; + } + bool ToolTips::toggleFullHelp() { mFullHelp = !mFullHelp; diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 4e73cc555..8b6174b87 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -66,6 +66,9 @@ namespace MWGui static std::string getCountString(const int value); ///< @return blank string if count is 1, or else " (value)" + static std::string getCellRefString(const MWWorld::CellRef& cellref); + ///< Returns a string containing debug tooltip information about the given cellref. + // these do not create an actual tooltip, but they fill in the data that is required so the tooltip // system knows what to show in case this widget is hovered static void createSkillToolTip(MyGUI::Widget* widget, int skillId); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1f81eda7d..861d5e110 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -34,7 +34,9 @@ namespace if (!faction.empty() && ptr.getClass().isNpc()) { const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); - if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) + std::map::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction)); + if (found == factions.end() + || found->second < item.getCellRef().getFactionRank()) isFactionOwned = true; } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 056737be6..cdf08e6ed 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -93,6 +93,11 @@ namespace MWWorld return mCellRef.mGlobalVariable; } + int CellRef::getFactionRank() const + { + return mCellRef.mFactionRank; + } + void CellRef::setOwner(const std::string &owner) { if (owner != mCellRef.mOwner) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index e9ef343fc..577655739 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -75,6 +75,9 @@ namespace MWWorld std::string getFaction() const; void setFaction (const std::string& faction); + // PC faction rank required to use the item. Sometimes is -1, which means "any rank". + int getFactionRank() const; + // Lock level for doors and containers // Positive for a locked door. 0 for a door that was never locked. // For an unlocked door, it is set to -(previous locklevel) diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index b77257e47..fc8329815 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -28,7 +28,7 @@ namespace MWWorld cellRef.mRefNum.mContentFile = -1; cellRef.mRefID = name; cellRef.mScale = 1; - cellRef.mFactIndex = 0; + cellRef.mFactionRank = 0; cellRef.mCharge = -1; cellRef.mGoldValue = 1; cellRef.mEnchantmentCharge = -1; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 409ae02d0..1560c0d8b 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -31,8 +31,8 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mSoul = esm.getHNOString ("XSOL"); mFaction = esm.getHNOString ("CNAM"); - mFactIndex = -2; - esm.getHNOT (mFactIndex, "INDX"); + mFactionRank = -2; + esm.getHNOT (mFactionRank, "INDX"); mGoldValue = 1; mCharge = -1; @@ -94,8 +94,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons esm.writeHNOCString("XSOL", mSoul); esm.writeHNOCString("CNAM", mFaction); - if (mFactIndex != -2) { - esm.writeHNT("INDX", mFactIndex); + if (mFactionRank != -2) { + esm.writeHNT("INDX", mFactionRank); } if (mEnchantmentCharge != -1) @@ -147,7 +147,7 @@ void ESM::CellRef::blank() mGlobalVariable.clear(); mSoul.clear(); mFaction.clear(); - mFactIndex = -1; + mFactionRank = -2; mCharge = 0; mEnchantmentCharge = 0; mGoldValue = 0; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 25ea243ef..de1a5bedc 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -50,9 +50,8 @@ namespace ESM // you take it and are not a faction member) std::string mFaction; - // INDX might be PC faction rank required to use the item? Sometimes - // is -1, which I assume means "any rank". - int mFactIndex; + // PC faction rank required to use the item. Sometimes is -1, which means "any rank". + int mFactionRank; // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. From 07cfa2abd50ca161a793ee797f4e87e2bad6c8c0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 22 Jul 2014 23:05:40 +0200 Subject: [PATCH 066/707] Set texture unit indices for GLSL ES --- extern/shiny/Main/MaterialInstance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny/Main/MaterialInstance.cpp b/extern/shiny/Main/MaterialInstance.cpp index b8032d681..128cc593b 100644 --- a/extern/shiny/Main/MaterialInstance.cpp +++ b/extern/shiny/Main/MaterialInstance.cpp @@ -163,7 +163,7 @@ namespace sh mTexUnits.push_back(texUnit); // set texture unit indices (required by GLSL) - if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) + if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && (mFactory->getCurrentLanguage () == Language_GLSL || mFactory->getCurrentLanguage() == Language_GLSLES)) { pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); From 103325bfb8430f5a8065b919da070345f5e2b9ad Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 23 Jul 2014 22:04:18 +0200 Subject: [PATCH 067/707] Change HUD enemy health bar to use actorId --- apps/openmw/mwgui/hud.cpp | 12 ++++++++---- apps/openmw/mwgui/hud.hpp | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index b39fd0db7..3e753b7f9 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -92,6 +92,7 @@ namespace MWGui , mSpellVisible(true) , mWorldMouseOver(false) , mEnemyHealthTimer(-1) + , mEnemyActorId(-1) , mIsDrowning(false) , mWeaponSpellTimer(0.f) , mDrowningFlashTheta(0.f) @@ -609,7 +610,10 @@ namespace MWGui void HUD::updateEnemyHealthBar() { - MWMechanics::CreatureStats& stats = mEnemy.getClass().getCreatureStats(mEnemy); + MWWorld::Ptr enemy = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mEnemyActorId); + if (enemy.isEmpty()) + return; + MWMechanics::CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); mEnemyHealth->setProgressRange(100); // Health is usually cast to int before displaying. Actors die whenever they are < 1 health. // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) @@ -620,7 +624,7 @@ namespace MWGui { mSpellIcons->updateWidgets(mEffectBox, true); - if (!mEnemy.isEmpty() && mEnemyHealth->getVisible()) + if (mEnemyActorId != -1 && mEnemyHealth->getVisible()) { updateEnemyHealthBar(); } @@ -634,7 +638,7 @@ namespace MWGui void HUD::setEnemy(const MWWorld::Ptr &enemy) { - mEnemy = enemy; + mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId(); mEnemyHealthTimer = 5; if (!mEnemyHealth->getVisible()) mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); @@ -644,7 +648,7 @@ namespace MWGui void HUD::resetEnemy() { - mEnemy = MWWorld::Ptr(); + mEnemyActorId = -1; mEnemyHealthTimer = -1; } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index f451ea4d2..56b0ef7b5 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -104,7 +104,7 @@ namespace MWGui SpellIcons* mSpellIcons; - MWWorld::Ptr mEnemy; + int mEnemyActorId; float mEnemyHealthTimer; bool mIsDrowning; From fc618cb3ea956e5bb4e626e962c2c36518113bd9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 00:25:02 +0200 Subject: [PATCH 068/707] Use separate function for fatigue restoration during resting/waiting --- apps/openmw/mwmechanics/actors.cpp | 28 ++++++++++++++++++++++++---- apps/openmw/mwmechanics/actors.hpp | 4 +++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 809e948e7..acca71333 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -184,7 +184,7 @@ namespace MWMechanics calculateCreatureStatModifiers (ptr, duration); // fatigue restoration - calculateRestoration(ptr, duration, false); + calculateRestoration(ptr, duration); } void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) @@ -293,7 +293,7 @@ namespace MWMechanics creatureStats.setFatigue(fatigue); } - void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep) + void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep) { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; @@ -332,9 +332,29 @@ namespace MWMechanics x *= fEndFatigueMult * endurance; DynamicStat fatigue = stats.getFatigue(); - fatigue.setCurrent (fatigue.getCurrent() + duration * x); + fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); stats.setFatigue (fatigue); + } + + void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) + { + if (ptr.getClass().getCreatureStats(ptr).isDead()) + return; + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); + + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + // restore fatigue + float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); + float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); + + float x = fFatigueReturnBase + fFatigueReturnMult * endurance; + + DynamicStat fatigue = stats.getFatigue(); + fatigue.setCurrent (fatigue.getCurrent() + duration * x); + stats.setFatigue (fatigue); } void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration) @@ -1206,7 +1226,7 @@ namespace MWMechanics void Actors::restoreDynamicStats(bool sleep) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - calculateRestoration(iter->first, 3600, sleep); + restoreDynamicStats(iter->first, sleep); } int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index cda6abaea..4b5d77a7f 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -36,7 +36,7 @@ namespace MWMechanics void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration); void calculateNpcStatModifiers (const MWWorld::Ptr& ptr); - void calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep); + void calculateRestoration (const MWWorld::Ptr& ptr, float duration); void updateDrowning (const MWWorld::Ptr& ptr, float duration); @@ -90,6 +90,8 @@ namespace MWMechanics void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. + void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep); + int getHoursToRest(const MWWorld::Ptr& ptr) const; ///< Calculate how many hours the given actor needs to rest in order to be fully healed From 7ae9bbb1a5d8db85d09f572109884e23c6eef442 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 00:59:58 +0200 Subject: [PATCH 069/707] Implement iCrimeThresholdMultiplier --- apps/openmw/mwmechanics/actors.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index acca71333..8f1209827 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -860,7 +860,7 @@ namespace MWMechanics if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile()) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()); + int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); // 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) if ( player.getClass().getNpcStats(player).getBounty() >= cutoff @@ -868,7 +868,11 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiPursue(player), ptr); + static int iCrimeThresholdMultiplier = esmStore.get().find("iCrimeThresholdMultiplier")->getInt(); + if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier) + MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); + else + creatureStats.getAiSequence().stack(AiPursue(player), ptr); creatureStats.setAlarmed(true); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); } From 4e0c133fb35d1ef9d31336e25b11c0266daee4a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 02:19:36 +0200 Subject: [PATCH 070/707] Add comment to Cell NAM0 (it's a RefId counter) --- apps/esmtool/record.cpp | 2 +- components/esm/loadcell.cpp | 10 +++++----- components/esm/loadcell.hpp | 5 ++++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index e4ada7d18..f8bc2af61 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -513,7 +513,7 @@ void Record::print() else std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl; std::cout << " Water Level Int: " << mData.mWaterInt << std::endl; - std::cout << " NAM0: " << mData.mNAM0 << std::endl; + std::cout << " RefId counter: " << mData.mRefIdCounter << std::endl; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 83864569f..0a25fce84 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -58,7 +58,7 @@ void Cell::load(ESMReader &esm, bool saveContext) void Cell::loadCell(ESMReader &esm, bool saveContext) { - mNAM0 = 0; + mRefIdCounter = 0; if (mData.mFlags & Interior) { @@ -92,7 +92,7 @@ void Cell::loadCell(ESMReader &esm, bool saveContext) esm.getHNOT(mMapColor, "NAM5"); } if (esm.isNextSub("NAM0")) { - esm.getHT(mNAM0); + esm.getHT(mRefIdCounter); } if (saveContext) { @@ -150,8 +150,8 @@ void Cell::save(ESMWriter &esm) const esm.writeHNT("NAM5", mMapColor); } - if (mNAM0 != 0) - esm.writeHNT("NAM0", mNAM0); + if (mRefIdCounter != 0) + esm.writeHNT("NAM0", mRefIdCounter); } void Cell::restore(ESMReader &esm, int iCtx) const @@ -220,7 +220,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mWater = 0; mWaterInt = false; mMapColor = 0; - mNAM0 = 0; + mRefIdCounter = 0; mData.mFlags = 0; mData.mX = 0; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index fb4b6b28a..5f889a55b 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -94,7 +94,10 @@ struct Cell float mWater; // Water level bool mWaterInt; int mMapColor; - int mNAM0; + // Counter for RefIds. This is only used during content file editing and has no impact on gameplay. + // It prevents overwriting previous refIDs, even if they were deleted. + // as that would collide with refs when a content file is upgraded. + int mRefIdCounter; // References "leased" from another cell (i.e. a different cell // introduced this ref, and it has been moved here by a plugin) From d4ff17f1c5bf1641bbf8fcdb2ae2984664eb04f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 02:20:16 +0200 Subject: [PATCH 071/707] Remove unused FLTV and NAM0 from CellRef --- apps/openmw/mwworld/manualref.hpp | 2 -- components/esm/cellref.cpp | 28 +++++++--------------------- components/esm/cellref.hpp | 6 ------ 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index fc8329815..0becd7524 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -35,8 +35,6 @@ namespace MWWorld cellRef.mTeleport = false; cellRef.mLockLevel = 0; cellRef.mReferenceBlocked = 0; - cellRef.mFltv = 0; - cellRef.mNam0 = 0; LiveCellRef ref(cellRef, base); diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 1560c0d8b..7947cdba4 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -6,10 +6,12 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) { - // NAM0 sometimes appears here, sometimes further on - mNam0 = 0; + // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that + // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. + // Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway, + // because any item can theoretically be moved by a script. if (esm.isNextSub ("NAM0")) - esm.getHT (mNam0); + esm.skipHSub(); if (wideRefNum) esm.getHNT (mRefNum, "FRMR", 8); @@ -60,20 +62,12 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mKey = esm.getHNOString ("KNAM"); mTrap = esm.getHNOString ("TNAM"); - mFltv = 0; esm.getHNOT (mReferenceBlocked, "UNAM"); - esm.getHNOT (mFltv, "FLTV"); esm.getHNOT(mPos, "DATA", 24); - // Number of references in the cell? Maximum once in each cell, - // but not always at the beginning, and not always right. In other - // words, completely useless. - // Update: Well, maybe not completely useless. This might actually be - // number_of_references + number_of_references_moved_here_Across_boundaries, - // and could be helpful for collecting these weird moved references. - if (esm.isNextSub ("NAM0")) - esm.getHT (mNam0); + if (esm.isNextSub("NAM0")) + esm.skipHSub(); } void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const @@ -127,14 +121,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked); - if (!inInventory && mFltv != 0) - esm.writeHNT("FLTV", mFltv); - if (!inInventory) esm.writeHNT("DATA", mPos, 24); - - if (!inInventory && mNam0 != 0) - esm.writeHNT("NAM0", mNam0); } void ESM::CellRef::blank() @@ -156,8 +144,6 @@ void ESM::CellRef::blank() mKey.clear(); mTrap.clear(); mReferenceBlocked = 0; - mFltv = 0; - mNam0 = 0; mTeleport = false; for (int i=0; i<3; ++i) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index de1a5bedc..9c57061b0 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -82,12 +82,6 @@ namespace ESM // -1 is not blocked, otherwise it is blocked. signed char mReferenceBlocked; - // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza - // Brindisi Dorom", where it has the value 100. Also only for - // activators. - int mFltv; - int mNam0; - // Position and rotation of this object within the cell Position mPos; From 4b3e12bfb4fe5450a3ad22bdb12e04760ed8e558 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 18:03:11 +0200 Subject: [PATCH 072/707] Fix pinned widgets receiving mouse clicks in gamemode (Fixes #1686) --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 668211db2..9ebafc90c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -268,7 +268,7 @@ namespace MWGui mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); trackWindow(mCompanionWindow, "companion"); - mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows"); + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Overlay"); mHud->setVisible(mHudEnabled); From 4ec51b386a9d4000295263cee221d37ac7d37b59 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Jul 2014 19:00:57 +0200 Subject: [PATCH 073/707] Add ManualResourceLoader for fog of war textures (Fixes #1675) --- apps/openmw/mwrender/localmap.cpp | 28 +++++++++++++++++++++++++++- apps/openmw/mwrender/localmap.hpp | 5 ++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 62907fcc3..271cbbfe1 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -340,7 +340,9 @@ Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName) sFogOfWarResolution, sFogOfWarResolution, 0, PF_A8R8G8B8, - TU_DYNAMIC_WRITE_ONLY); + TU_DYNAMIC_WRITE_ONLY, + this // ManualResourceLoader required if the texture contents are lost (due to lost devices nonsense that can occur with D3D) + ); } return tex; @@ -457,6 +459,30 @@ bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interi return alpha < 200; } +void LocalMap::loadResource(Ogre::Resource* resource) +{ + std::string resourceName = resource->getName(); + size_t pos = resourceName.find("_fog"); + if (pos != std::string::npos) + resourceName = resourceName.substr(0, pos); + if (mBuffers.find(resourceName) == mBuffers.end()) + { + // create a buffer to use for dynamic operations + std::vector buffer; + + // initialize to (0, 0, 0, 1) + buffer.resize(sFogOfWarResolution*sFogOfWarResolution, 0xFF000000); + mBuffers[resourceName] = buffer; + } + + std::vector& buffer = mBuffers[resourceName]; + + Ogre::Texture* tex = dynamic_cast(resource); + tex->createInternalResources(); + memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); + tex->getBuffer()->unlock(); +} + void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation) { if (sFogOfWarSkip != 0) diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index babf7224e..1572800e5 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace MWWorld { @@ -23,12 +24,14 @@ namespace MWRender /// /// \brief Local map rendering /// - class LocalMap + class LocalMap : public Ogre::ManualResourceLoader { public: LocalMap(OEngine::Render::OgreRenderer*, MWRender::RenderingManager* rendering); ~LocalMap(); + virtual void loadResource(Ogre::Resource* resource); + /** * Clear all savegame-specific data (i.e. fog of war textures) */ From 919632a505547a6a58548ac09313a62b77cc34c2 Mon Sep 17 00:00:00 2001 From: crysthala Date: Thu, 24 Jul 2014 13:12:44 -0500 Subject: [PATCH 074/707] render lighting icons --- files/opencs/Lightbulb-48.png | Bin 0 -> 8092 bytes files/opencs/Moon-48.png | Bin 0 -> 7186 bytes files/opencs/Sun-48.png | Bin 0 -> 8364 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 files/opencs/Lightbulb-48.png create mode 100644 files/opencs/Moon-48.png create mode 100644 files/opencs/Sun-48.png diff --git a/files/opencs/Lightbulb-48.png b/files/opencs/Lightbulb-48.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a45528ef258d5680f8dd717d013544be5c80fa GIT binary patch literal 8092 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 U^IfI7vj6}907*qoM6N<$f+3E*%K!iX literal 0 HcmV?d00001 diff --git a/files/opencs/Sun-48.png b/files/opencs/Sun-48.png new file mode 100644 index 0000000000000000000000000000000000000000..dbdede349a4417d6d67e5e22c2192c50b00e71ad GIT binary patch literal 8364 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 Date: Thu, 24 Jul 2014 20:52:14 +0200 Subject: [PATCH 075/707] Ignore FLTV from old savegames --- components/esm/cellref.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 7947cdba4..29d26d013 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -63,6 +63,8 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mTrap = esm.getHNOString ("TNAM"); esm.getHNOT (mReferenceBlocked, "UNAM"); + if (esm.isNextSub("FLTV")) // no longer used + esm.skipHSub(); esm.getHNOT(mPos, "DATA", 24); From 2e355df8b31a60a1c72b0b837c895caf0b0c8714 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 07:59:50 +0200 Subject: [PATCH 076/707] removed function ScriptManager::getLocalIndex (was redundant and was also depending on precompiled scripts) --- apps/openmw/mwbase/scriptmanager.hpp | 8 +-- apps/openmw/mwscript/interpretercontext.cpp | 60 ++++++++++++--------- apps/openmw/mwscript/interpretercontext.hpp | 4 ++ apps/openmw/mwscript/scriptmanagerimp.cpp | 42 --------------- apps/openmw/mwscript/scriptmanagerimp.hpp | 5 -- components/compiler/locals.hpp | 6 ++- 6 files changed, 45 insertions(+), 80 deletions(-) diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index ae146e064..1a9eee456 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -50,13 +50,7 @@ namespace MWBase ///< Return locals for script \a name. virtual MWScript::GlobalScripts& getGlobalScripts() = 0; - - virtual int getLocalIndex (const std::string& scriptId, const std::string& variable, - char type) = 0; - ///< Return index of the variable of the given name and type in the given script. Will - /// throw an exception, if there is no such script or variable or the type does not match. - - }; + }; } #endif diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index e1dc6273c..121b07e34 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -3,8 +3,12 @@ #include #include +#include #include + +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -103,6 +107,32 @@ namespace MWScript } } + int InterpreterContext::findLocalVariableIndex (const std::string& scriptId, + const std::string& name, char type) const + { + int index = MWBase::Environment::get().getScriptManager()->getLocals (scriptId). + search (type, name); + + if (index!=-1) + return index; + + std::ostringstream stream; + + stream << "Failed to access "; + + switch (type) + { + case 's': stream << "short"; break; + case 'l': stream << "long"; break; + case 'f': stream << "float"; break; + } + + stream << " member variable " << name << " in script " << scriptId; + + throw std::runtime_error (stream.str().c_str()); + } + + InterpreterContext::InterpreterContext ( MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId) : mLocals (locals), mReference (reference), @@ -485,10 +515,7 @@ namespace MWScript const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( - scriptId, name, 's'); - - return locals.mShorts[index]; + return locals.mShorts[findLocalVariableIndex (scriptId, name, 's')]; } int InterpreterContext::getMemberLong (const std::string& id, const std::string& name, @@ -498,10 +525,7 @@ namespace MWScript const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( - scriptId, name, 'l'); - - return locals.mLongs[index]; + return locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')]; } float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name, @@ -511,10 +535,7 @@ namespace MWScript const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( - scriptId, name, 'f'); - - return locals.mFloats[index]; + return locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')]; } void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, @@ -524,10 +545,7 @@ namespace MWScript Locals& locals = getMemberLocals (scriptId, global); - int index = - MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's'); - - locals.mShorts[index] = value; + locals.mShorts[findLocalVariableIndex (scriptId, name, 's')] = value; } void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global) @@ -536,10 +554,7 @@ namespace MWScript Locals& locals = getMemberLocals (scriptId, global); - int index = - MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l'); - - locals.mLongs[index] = value; + locals.mLongs[findLocalVariableIndex (scriptId, name, 'l')] = value; } void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global) @@ -548,10 +563,7 @@ namespace MWScript Locals& locals = getMemberLocals (scriptId, global); - int index = - MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f'); - - locals.mFloats[index] = value; + locals.mFloats[findLocalVariableIndex (scriptId, name, 'f')] = value; } MWWorld::Ptr InterpreterContext::getReference(bool required) diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index f897282e2..bc43f3e44 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -50,6 +50,10 @@ namespace MWScript Locals& getMemberLocals (std::string& id, bool global); ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before + /// Throws an exception if local variable can't be found. + int findLocalVariableIndex (const std::string& scriptId, const std::string& name, + char type) const; + public: InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference, diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 781c16299..289fd9a70 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -196,46 +196,4 @@ namespace MWScript { return mGlobalScripts; } - - int ScriptManager::getLocalIndex (const std::string& scriptId, const std::string& variable, - char type) - { - const ESM::Script *script = mStore.get().find (scriptId); - - int offset = 0; - int size = 0; - - switch (type) - { - case 's': - - offset = 0; - size = script->mData.mNumShorts; - break; - - case 'l': - - offset = script->mData.mNumShorts; - size = script->mData.mNumLongs; - break; - - case 'f': - - offset = script->mData.mNumShorts+script->mData.mNumLongs; - size = script->mData.mNumFloats; - break; - - default: - - throw std::runtime_error ("invalid variable type"); - } - - std::string variable2 = Misc::StringUtils::lowerCase (variable); - - for (int i=0; imVarNames.at (i+offset))==variable2) - return i; - - throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId); - } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index 4edc09eca..7b1121aca 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -71,11 +71,6 @@ namespace MWScript ///< Return locals for script \a name. virtual GlobalScripts& getGlobalScripts(); - - virtual int getLocalIndex (const std::string& scriptId, const std::string& variable, - char type); - ///< Return index of the variable of the given name and type in the given script. Will - /// throw an exception, if there is no such script or variable or the type does not match. }; } diff --git a/components/compiler/locals.hpp b/components/compiler/locals.hpp index d5bf05253..cf7899b5c 100644 --- a/components/compiler/locals.hpp +++ b/components/compiler/locals.hpp @@ -17,8 +17,6 @@ namespace Compiler int searchIndex (char type, const std::string& name) const; - bool search (char type, const std::string& name) const; - std::vector& get (char type); public: @@ -29,6 +27,10 @@ namespace Compiler int getIndex (const std::string& name) const; ///< return index for local variable \a name (-1: does not exist). + /// Return index for local variable \a name of type \a type (-1: variable does not + /// exit). + bool search (char type, const std::string& name) const; + const std::vector& get (char type) const; void write (std::ostream& localFile) const; From 9f69db0d69538add19a714391e825c01a7254da2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 08:12:53 +0200 Subject: [PATCH 077/707] added missing const to ScriptManager::getLocals --- apps/openmw/mwbase/scriptmanager.hpp | 2 +- apps/openmw/mwscript/scriptmanagerimp.cpp | 2 +- apps/openmw/mwscript/scriptmanagerimp.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index 1a9eee456..7bdeba132 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -46,7 +46,7 @@ namespace MWBase ///< Compile all scripts /// \return count, success - virtual Compiler::Locals& getLocals (const std::string& name) = 0; + virtual const Compiler::Locals& getLocals (const std::string& name) = 0; ///< Return locals for script \a name. virtual MWScript::GlobalScripts& getGlobalScripts() = 0; diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 289fd9a70..5af214268 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -156,7 +156,7 @@ namespace MWScript return std::make_pair (count, success); } - Compiler::Locals& ScriptManager::getLocals (const std::string& name) + const Compiler::Locals& ScriptManager::getLocals (const std::string& name) { std::string name2 = Misc::StringUtils::lowerCase (name); diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index 7b1121aca..6026f6aba 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -67,7 +67,7 @@ namespace MWScript ///< Compile all scripts /// \return count, success - virtual Compiler::Locals& getLocals (const std::string& name); + virtual const Compiler::Locals& getLocals (const std::string& name); ///< Return locals for script \a name. virtual GlobalScripts& getGlobalScripts(); From f6b502b195f508e12db8491a0c29b0db72820081 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 09:05:17 +0200 Subject: [PATCH 078/707] rewrote dialgoue filter access to local variables --- apps/openmw/mwdialogue/filter.cpp | 52 ++++++++++++------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 08cdb1d00..b7f91613a 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -1,11 +1,14 @@ #include "filter.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -187,33 +190,28 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c if (scriptName.empty()) return false; // no script - const ESM::Script *script = - MWBase::Environment::get().getWorld()->getStore().get().find (scriptName); + std::string name = Misc::StringUtils::lowerCase (select.getName()); - std::string name = select.getName(); + const Compiler::Locals& localDefs = + MWBase::Environment::get().getScriptManager()->getLocals (scriptName); - int i = 0; + char type = localDefs.getType (name); - for (; i (script->mVarNames.size()); ++i) - if (Misc::StringUtils::ciEqual(script->mVarNames[i], name)) - break; + if (type==' ') + return false; // script does not have a variable of this name. - if (i>=static_cast (script->mVarNames.size())) - return false; // script does not have a variable of this name + int index = localDefs.getIndex (name); const MWScript::Locals& locals = mActor.getRefData().getLocals(); - if (imData.mNumShorts) - return select.selectCompare (static_cast (locals.mShorts[i])); - - i -= script->mData.mNumShorts; - - if (imData.mNumLongs) - return select.selectCompare (locals.mLongs[i]); - - i -= script->mData.mNumLongs; + switch (type) + { + case 's': return select.selectCompare (static_cast (locals.mShorts[index])); + case 'l': return select.selectCompare (locals.mLongs[index]); + case 'f': return select.selectCompare (locals.mFloats[index]); + } - return select.selectCompare (locals.mFloats.at (i)); + throw std::logic_error ("unknown local variable type in dialogue filter"); } case SelectWrapper::Function_PcHealthPercent: @@ -453,20 +451,10 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co // This actor has no attached script, so there is no local variable return true; - const ESM::Script *script = - MWBase::Environment::get().getWorld()->getStore().get().find (scriptName); - - std::string name = select.getName(); - - int i = 0; - for (; i < static_cast (script->mVarNames.size()); ++i) - if (Misc::StringUtils::ciEqual(script->mVarNames[i], name)) - break; - - if (i >= static_cast (script->mVarNames.size())) - return true; // script does not have a variable of this name + const Compiler::Locals& localDefs = + MWBase::Environment::get().getScriptManager()->getLocals (scriptName); - return false; + return localDefs.getIndex (Misc::StringUtils::lowerCase (select.getName()))==-1; } case SelectWrapper::Function_SameGender: From 87c54adb2460fa1028aae89c3a7cca52a154ebbe Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 09:26:30 +0200 Subject: [PATCH 079/707] some cleanup --- apps/openmw/mwscript/locals.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 57584ac30..6a8a857d8 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -29,7 +29,7 @@ namespace MWScript int Locals::getIntVar(const std::string &script, const std::string &var) { - Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); + const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); int index = locals.getIndex(var); char type = locals.getType(var); if(index != -1) @@ -53,7 +53,7 @@ namespace MWScript bool Locals::setVarByInt(const std::string& script, const std::string& var, int val) { - Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); + const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); int index = locals.getIndex(var); char type = locals.getType(var); if(index != -1) From a3c4000198b1b970d52275ff8cdb67515158fc6a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 09:36:02 +0200 Subject: [PATCH 080/707] moved call to Globalscripts::addStartup out of the constructor because at the time of construction the environment may not be set up yet to perform this operation --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwscript/globalscripts.cpp | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 66eae5f5d..6a48789a5 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -421,6 +421,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mOgre->getRoot()->addFrameListener (this); // scripts + mEnvironment.getScriptManager()->getGlobalScripts().addStartup(); + if (mCompileAll) { std::pair result = MWBase::Environment::get().getScriptManager()->compileAll(); diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 0c94f503b..8270c4e6b 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -20,9 +20,7 @@ namespace MWScript GlobalScripts::GlobalScripts (const MWWorld::ESMStore& store) : mStore (store) - { - addStartup(); - } + {} void GlobalScripts::addScript (const std::string& name, const std::string& targetId) { From 1ca0cc4988caccd1ccc848dce3a2b2c8a200b606 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 09:37:21 +0200 Subject: [PATCH 081/707] rewrote MWScript::Locals::configure to be independent of precompiled script data --- apps/openmw/mwscript/locals.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 6a8a857d8..a1ee48ae6 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -14,12 +14,15 @@ namespace MWScript { void Locals::configure (const ESM::Script& script) { + const Compiler::Locals& locals = + MWBase::Environment::get().getScriptManager()->getLocals (script.mId); + mShorts.clear(); - mShorts.resize (script.mData.mNumShorts, 0); + mShorts.resize (locals.get ('s').size(), 0); mLongs.clear(); - mLongs.resize (script.mData.mNumLongs, 0); + mLongs.resize (locals.get ('l').size(), 0); mFloats.clear(); - mFloats.resize (script.mData.mNumFloats, 0); + mFloats.resize (locals.get ('f').size(), 0); } bool Locals::isEmpty() const From acb728195f84f199bfa5bdd704d79819e6439f60 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 10:37:34 +0200 Subject: [PATCH 082/707] improved documentation of ESM::Script member variables --- components/esm/loadscpt.hpp | 38 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index d5200d4c1..38160e7f4 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -23,29 +23,8 @@ public: struct SCHDstruct { - /* Script name. - - NOTE: You should handle the name "Main" (case insensitive) with - care. With tribunal, modders got the ability to add 'start - scripts' to their mods, which is a script that is run at - startup and which runs throughout the game (I think.) - - However, before Tribunal, there was only one startup script, - called "Main". If mods wanted to make their own start scripts, - they had to overwrite Main. This is obviously problem if - multiple mods to this at the same time. - - Although most mods have switched to using Trib-style startup - scripts, some legacy mods might still overwrite Main, and this - can cause problems if several mods do it. I think the best - course of action is to NEVER overwrite main, but instead add - each with a separate unique name and add them to the start - script list. But there might be other problems with this - approach though. - */ - - // These describe the sizes we need to allocate for the script - // data. + /// Data from script-precompling in the editor. + /// \warning Do not use them. OpenCS currently does not precompile scripts. int mNumShorts, mNumLongs, mNumFloats, mScriptDataSize, mStringTableSize; }; // 52 bytes @@ -53,9 +32,16 @@ public: SCHDstruct mData; - std::vector mVarNames; // Variable names - std::vector mScriptData; // Compiled bytecode - std::string mScriptText; // Uncompiled script + /// Variable names generated by script-precompiling in the editor. + /// \warning Do not use this field. OpenCS currently does not precompile scripts. + std::vector mVarNames; + + /// Bytecode generated from script-precompiling in the editor. + /// \warning Do not use this field. OpenCS currently does not precompile scripts. + std::vector mScriptData; + + /// Script source code + std::string mScriptText; void load(ESMReader &esm); void save(ESMWriter &esm) const; From 151ef14b67839c63d9937bc74de09ff11a2f906c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 11:10:12 +0200 Subject: [PATCH 083/707] some cleanup --- files/opencs/resources.qrc | 134 +++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 66 deletions(-) diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index 2031e54cc..aa6996c9b 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -2,71 +2,73 @@ opencs.png activator.png - added.png - apparatus.png - armor.png - attribute.png - base.png - birthsign.png - body-part.png - book.png - cell.png - class.png - clothing.png - container.png - creature.png - dialogoue-info.png - dialogoue-journal.png - dialogoue-regular.png - dialogue-greeting.png - dialogue-persuasion.png - dialogue-speech.png - door.png - enchantment.png - faction.png - filter.png - globvar.png - GMST.png - Info.png - ingredient.png - landpaint.png - land.png - LandTexture.png - leveled-creature.png - light.png - lockpick.png - magic-effect.png - magicrabbit.png - map.png - miscellaneous.png - modified.png - npc.png - PathGrid.png - potion.png - probe.png - race.png - random-item.png - random.png - removed.png - repair.png - script.png - skill.png - soundgen.png - sound.png - spell.png - static.png - weapon.png - multitype.png - go-next.png - go-previous.png - edit-delete.png - edit-undo.png - edit-preview.png - edit-clone.png - add.png - raster/startup/big/create-addon.png - raster/startup/big/new-game.png - raster/startup/big/edit-content.png - raster/startup/small/configure.png + added.png + apparatus.png + armor.png + attribute.png + base.png + birthsign.png + body-part.png + book.png + cell.png + class.png + clothing.png + container.png + creature.png + dialogoue-info.png + dialogoue-journal.png + dialogoue-regular.png + dialogue-greeting.png + dialogue-persuasion.png + dialogue-speech.png + door.png + enchantment.png + faction.png + filter.png + globvar.png + GMST.png + Info.png + ingredient.png + landpaint.png + land.png + LandTexture.png + leveled-creature.png + light.png + lockpick.png + magic-effect.png + magicrabbit.png + map.png + miscellaneous.png + modified.png + npc.png + PathGrid.png + potion.png + probe.png + race.png + random-item.png + random.png + removed.png + repair.png + script.png + skill.png + soundgen.png + sound.png + spell.png + static.png + weapon.png + multitype.png + go-next.png + go-previous.png + edit-delete.png + edit-undo.png + edit-preview.png + edit-clone.png + add.png + + + raster/startup/big/create-addon.png + raster/startup/big/new-game.png + raster/startup/big/edit-content.png + raster/startup/small/configure.png From 4421e7a5cc88eef8f5a122dea989c35d76812e95 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 11:15:04 +0200 Subject: [PATCH 084/707] added lighting mode toolbar icons --- apps/opencs/view/render/scenewidget.cpp | 6 +++--- files/opencs/resources.qrc | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index ebd3eb764..494229600 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -64,19 +64,19 @@ namespace CSVRender CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Lighting Mode"); /// \todo replace icons - tool->addButton (":door.png", "day", + tool->addButton (":scenetoolbar/day", "day", "Day" "
  • Cell specific ambient in interiors
  • " "
  • Low ambient in exteriors
  • " "
  • Strong directional light source/lir>" "
  • This mode closely resembles day time in-game
"); - tool->addButton (":GMST.png", "night", + tool->addButton (":scenetoolbar/night", "night", "Night" "
  • Cell specific ambient in interiors
  • " "
  • Low ambient in exteriors
  • " "
  • Weak directional light source
  • " "
  • This mode closely resembles night time in-game
"); - tool->addButton (":Info.png", "bright", + tool->addButton (":scenetoolbar/bright", "bright", "Bright" "
  • Maximum ambient
  • " "
  • Strong directional light source
"); diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index aa6996c9b..659734826 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -71,4 +71,9 @@ raster/startup/big/edit-content.png raster/startup/small/configure.png + + Moon-48.png + Sun-48.png + Lightbulb-48.png + From 5e543ac8065cbeaa7625ab838176d3e76ea6d2f4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 11:53:30 +0200 Subject: [PATCH 085/707] added script doortestwarp to blacklist --- files/openmw.cfg | 3 ++- files/openmw.cfg.local | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/files/openmw.cfg b/files/openmw.cfg index de1f22e50..e105b6304 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -3,4 +3,5 @@ data="?mw?Data Files" data-local="?userdata?data" resources=${OPENMW_RESOURCE_FILES} script-blacklist=Museum -script-blacklist=MockChangeScript \ No newline at end of file +script-blacklist=MockChangeScript +script-blacklist=doortestwarp diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index b2970fb19..9d86174d9 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -5,3 +5,4 @@ data-local="?userdata?data" resources=./resources script-blacklist=Museum script-blacklist=MockChangeScript +script-blacklist=doortestwarp From d0654f3adefc86a82641a7435e4b84682e606600 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 12:17:15 +0200 Subject: [PATCH 086/707] move starting of startup scripts from engine startup to new game start; also restart all startup scripts on saved game load --- apps/openmw/engine.cpp | 2 -- apps/openmw/mwstate/statemanagerimp.cpp | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 6a48789a5..66eae5f5d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -421,8 +421,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mOgre->getRoot()->addFrameListener (this); // scripts - mEnvironment.getScriptManager()->getGlobalScripts().addStartup(); - if (mCompileAll) { std::pair result = MWBase::Environment::get().getScriptManager()->compileAll(); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a3604cc66..b69e6b908 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -139,6 +139,9 @@ void MWState::StateManager::newGame (bool bypass) else MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + mState = State_Running; } @@ -401,6 +404,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl // Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + // Do not trigger erroneous cellChanged events MWBase::Environment::get().getWorld()->markCellAsUnchanged(); } From af54bb96237e17066ed81ce56712fbada9353372 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Jul 2014 12:23:18 +0200 Subject: [PATCH 087/707] removed some redundancies; some more general cleanup --- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++----- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index b69e6b908..96f14d7e9 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -132,16 +132,13 @@ void MWState::StateManager::newGame (bool bypass) { cleanup(); - MWBase::Environment::get().getWorld()->startNewGame (bypass); - if (!bypass) MWBase::Environment::get().getWindowManager()->setNewGame (true); - else - MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + MWBase::Environment::get().getWorld()->startNewGame (bypass); + mState = State_Running; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 386834882..98025b00e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -211,9 +211,9 @@ namespace MWWorld // set new game mark mGlobalVariables["chargenstate"].setInteger (1); mGlobalVariables["pcrace"].setInteger (3); - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); } + else + mGlobalVariables["chargenstate"].setInteger (-1); if (bypass && !mStartCell.empty()) { From 60499eff83ffd6528e4b285784dae8d8eb232710 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 25 Jul 2014 19:48:37 +0200 Subject: [PATCH 088/707] Hotfix for message boxes locking up the game --- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9ebafc90c..5cd8899d8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -785,6 +785,7 @@ namespace MWGui } else { mMessageBoxManager->createInteractiveMessageBox(message, buttons); MWBase::Environment::get().getInputManager()->changeInputMode(isGuiMode()); + updateVisible(); } } From 90b2709d6c0fa1997faf64127b3c85990e3610fd Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 25 Jul 2014 19:56:06 +0200 Subject: [PATCH 089/707] Allow absorption of non-harmful spells (Fixes #1693) Also fix absorption being calculated for each effect rather than the whole spell. --- apps/openmw/mwmechanics/spellcasting.cpp | 48 ++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 67da99200..2566bf189 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -287,6 +287,27 @@ namespace MWMechanics bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player"); + // Try absorbing if it's a spell + // NOTE: Vanilla does this once per effect source instead of adding the % from all sources together, not sure + // if that is worth replicating. + bool absorbed = false; + if (spell && caster != target && target.getClass().isActor()) + { + int absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).mMagnitude; + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + absorbed = (roll < absorb); + if (absorbed) + { + const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); + MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( + "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); + // Magicka is increased by cost of spell + DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); + magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); + target.getClass().getCreatureStats(target).setMagicka(magicka); + } + } + for (std::vector::const_iterator effectIt (effects.mList.begin()); effectIt!=effects.mList.end(); ++effectIt) { @@ -326,31 +347,13 @@ namespace MWMechanics { anyHarmfulEffect = true; + if (absorbed) // Absorbed, and we know there was a harmful effect (figuring that out is the only reason we are in this loop) + break; + // If player is attempting to cast a harmful spell, show the target's HP bar if (castByPlayer && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); - // Try absorbing if it's a spell - // NOTE: Vanilla does this once per effect source instead of adding the % from all sources together, not sure - // if that is worth replicating. - if (spell && caster != target) - { - int absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).mMagnitude; - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - bool isAbsorbed = (roll < absorb); - if (isAbsorbed) - { - const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); - MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( - "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::Reflect, false, ""); - // Magicka is increased by cost of spell - DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); - magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); - target.getClass().getCreatureStats(target).setMagicka(magicka); - magnitudeMult = 0; - } - } - // Try reflecting if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) { @@ -382,8 +385,7 @@ namespace MWMechanics } } - - if (magnitudeMult > 0) + if (magnitudeMult > 0 && !absorbed) { float random = std::rand() / static_cast(RAND_MAX); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; From 6a745c014fec66622bdda9759c3c6e181067d5e7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 27 Jul 2014 11:51:53 +0200 Subject: [PATCH 090/707] workaround for incorrect argument order for PositionCell instruction --- components/compiler/errorhandler.cpp | 30 +++++++++++++++++++++++----- components/compiler/errorhandler.hpp | 21 +++++++++++++++++++ components/compiler/lineparser.cpp | 27 +++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/components/compiler/errorhandler.cpp b/components/compiler/errorhandler.cpp index fe58836cc..bcd30ef2d 100644 --- a/components/compiler/errorhandler.cpp +++ b/components/compiler/errorhandler.cpp @@ -3,11 +3,8 @@ namespace Compiler { - // constructor - - ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0), mWarningsMode (1) {} - - // destructor + ErrorHandler::ErrorHandler() + : mWarnings (0), mErrors (0), mWarningsMode (1), mDowngradeErrors (false) {} ErrorHandler::~ErrorHandler() {} @@ -49,6 +46,12 @@ namespace Compiler void ErrorHandler::error (const std::string& message, const TokenLoc& loc) { + if (mDowngradeErrors) + { + warning (message, loc); + return; + } + ++mErrors; report (message, loc, ErrorMessage); } @@ -72,4 +75,21 @@ namespace Compiler { mWarningsMode = mode; } + + void ErrorHandler::downgradeErrors (bool downgrade) + { + mDowngradeErrors = downgrade; + } + + + ErrorDowngrade::ErrorDowngrade (ErrorHandler& handler) : mHandler (handler) + { + mHandler.downgradeErrors (true); + } + + ErrorDowngrade::~ErrorDowngrade() + { + mHandler.downgradeErrors (false); + } + } diff --git a/components/compiler/errorhandler.hpp b/components/compiler/errorhandler.hpp index e5922a6be..c92e7bb8d 100644 --- a/components/compiler/errorhandler.hpp +++ b/components/compiler/errorhandler.hpp @@ -17,6 +17,7 @@ namespace Compiler int mWarnings; int mErrors; int mWarningsMode; + bool mDowngradeErrors; protected: @@ -66,6 +67,26 @@ namespace Compiler void setWarningsMode (int mode); ///< // 0 ignore, 1 rate as warning, 2 rate as error + + /// Treat errors as warnings. + void downgradeErrors (bool downgrade); + }; + + class ErrorDowngrade + { + ErrorHandler& mHandler; + + /// not implemented + ErrorDowngrade (const ErrorDowngrade&); + + /// not implemented + ErrorDowngrade& operator= (const ErrorDowngrade&); + + public: + + ErrorDowngrade (ErrorHandler& handler); + + ~ErrorDowngrade(); }; } diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index cdbfaa04a..2226f5845 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -11,6 +11,7 @@ #include "generator.hpp" #include "extensions.hpp" #include "declarationparser.hpp" +#include "exception.hpp" namespace Compiler { @@ -292,9 +293,31 @@ namespace Compiler mExplicit.clear(); } - int optionals = mExprParser.parseArguments (argumentType, scanner, mCode); + int optionals = 0; + + try + { + ErrorDowngrade errorDowngrade (getErrorHandler()); + std::vector code; + optionals = mExprParser.parseArguments (argumentType, scanner, code); + mCode.insert (mCode.begin(), code.begin(), code.end()); + extensions->generateInstructionCode (keyword, mCode, mLiterals, + mExplicit, optionals); + } + catch (const SourceException& exception) + { + // Ignore argument exceptions for positioncell. + /// \todo add option to disable this + if (Misc::StringUtils::lowerCase (loc.mLiteral)=="positioncell") + { + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } + + throw; + } - extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals); mState = EndState; return true; } From 4a1e5610153eb1be6f72dcd730084b609b83753c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 25 Jul 2014 23:03:34 +0200 Subject: [PATCH 091/707] Properly assign effect attribute/skill in potion creation (Fixes #1704) --- apps/openmw/mwmechanics/alchemy.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 2e03122d5..a097446a0 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -181,7 +181,13 @@ void MWMechanics::Alchemy::updateEffects() ESM::ENAMstruct effect; effect.mEffectID = iter->mId; - effect.mSkill = effect.mAttribute = iter->mArg; // somewhat hack-ish, but should work + effect.mAttribute = -1; + effect.mSkill = -1; + + if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) + effect.mSkill = iter->mArg; + else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) + effect.mAttribute = iter->mArg; effect.mRange = 0; effect.mArea = 0; From b370c0f7b5c29a45fca9f923d78649651ba96fbd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 00:57:49 +0200 Subject: [PATCH 092/707] Enchanting: Don't check price on self-enchanting (Bug #1701) --- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 92221977b..145602eb0 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -299,7 +299,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if (mEnchanting.getEnchantPrice() > playerGold) + if (mPtr != player && mEnchanting.getEnchantPrice() > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; From 09607f992ea2f725e886dc193813f88b3bbe7fd9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 00:59:35 +0200 Subject: [PATCH 093/707] Enchanting: fix inverted self-enchant success chance (Fixes #1701) --- apps/openmw/mwmechanics/enchanting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index f3f6795db..18732514b 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -62,7 +62,7 @@ namespace MWMechanics if(mSelfEnchanting) { - if(getEnchantChance() (RAND_MAX)*100) + if(std::rand()/static_cast (RAND_MAX)*100 < getEnchantChance()) return false; mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2); From 9c60e4d8265c0e1dd54499790de491c7e3929e7b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 01:03:27 +0200 Subject: [PATCH 094/707] Change button caption from "Buy" to "Create" when self-enchanting --- apps/openmw/mwgui/enchantingdialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 145602eb0..1fef34371 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -144,6 +144,8 @@ namespace MWGui mEnchanting.setSelfEnchanting(false); mEnchanting.setEnchanter(actor); + mBuyButton->setCaptionWithReplacing("#{sBuy}"); + mPtr = actor; startEditing (); @@ -156,6 +158,8 @@ namespace MWGui mEnchanting.setSelfEnchanting(true); mEnchanting.setEnchanter(player); + mBuyButton->setCaptionWithReplacing("#{sCreate}"); + mPtr = player; startEditing(); mEnchanting.setSoulGem(soulgem); From 16b089cdc833e332bba1243cfb7ee9328aa75324 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 01:07:32 +0200 Subject: [PATCH 095/707] Fix invisible enchanting price when self-enchanting was previously used. --- apps/openmw/mwgui/enchantingdialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 1fef34371..d7e79e7a2 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -149,6 +149,9 @@ namespace MWGui mPtr = actor; startEditing (); + mPrice->setVisible(true); + mPriceText->setVisible(true); + updateLabels(); } void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) @@ -162,7 +165,6 @@ namespace MWGui mPtr = player; startEditing(); - mEnchanting.setSoulGem(soulgem); setSoulGem(soulgem); From 31d058b98cf7a17a9081768d36b60309afabbd6b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 02:23:42 +0200 Subject: [PATCH 096/707] Add workaround for ScrollView messing up canvas size (Fixes #1700) TODO: Create fixed ScrollView widget? --- apps/openmw/mwgui/itemview.cpp | 6 ++++++ apps/openmw/mwgui/journalwindow.cpp | 3 +++ apps/openmw/mwgui/list.cpp | 4 ++++ apps/openmw/mwgui/merchantrepair.cpp | 3 +++ apps/openmw/mwgui/quickkeysmenu.cpp | 5 +++-- apps/openmw/mwgui/recharge.cpp | 4 ++++ apps/openmw/mwgui/repair.cpp | 3 +++ apps/openmw/mwgui/review.cpp | 3 +++ apps/openmw/mwgui/scrollwindow.cpp | 3 +++ apps/openmw/mwgui/settingswindow.cpp | 3 +++ apps/openmw/mwgui/spellbuyingwindow.cpp | 5 +++++ apps/openmw/mwgui/spellcreationdialog.cpp | 3 +++ apps/openmw/mwgui/spellwindow.cpp | 3 +++ apps/openmw/mwgui/statswindow.cpp | 6 ++++++ apps/openmw/mwgui/travelwindow.cpp | 3 +++ 15 files changed, 55 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index b86f61034..c4c6c6545 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -115,7 +115,13 @@ void ItemView::update() } x += 42; MyGUI::IntSize size = MyGUI::IntSize(std::max(mScrollView->getSize().width, x), mScrollView->getSize().height); + + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mScrollView->setVisibleVScroll(false); + mScrollView->setVisibleHScroll(false); mScrollView->setCanvasSize(size); + mScrollView->setVisibleVScroll(true); + mScrollView->setVisibleHScroll(true); dragArea->setSize(size); } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index fa27b4ef0..b588ba3e1 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -400,7 +400,10 @@ namespace getPage (pageId)->showPage (book, 0); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + getWidget (listId)->setVisibleVScroll(false); getWidget (listId)->setCanvasSize (size.first, size.second); + getWidget (listId)->setVisibleVScroll(true); } void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character) diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp index b0c514b9d..e88d43534 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -93,7 +93,11 @@ namespace MWGui } ++i; } + + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mScrollView->setVisibleVScroll(false); mScrollView->setCanvasSize(mClient->getSize().width, std::max(mItemHeight, mClient->getSize().height)); + mScrollView->setVisibleVScroll(true); if (!scrollbarShown && mItemHeight > mClient->getSize().height) redraw(true); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 0d1a57000..013a40017 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -91,7 +91,10 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); } } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mList->setVisibleVScroll(false); mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); + mList->setVisibleVScroll(true); mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 328ef21fb..84150c322 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -654,9 +654,10 @@ namespace MWGui mHeight += spellHeight; } - + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mMagicList->setVisibleVScroll(false); mMagicList->setCanvasSize (mWidth, std::max(mMagicList->getHeight(), mHeight)); - + mMagicList->setVisibleVScroll(true); } void MagicSelectionDialog::addGroup(const std::string &label, const std::string& label2) diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index 9c43b1416..1b994d584 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -119,7 +119,11 @@ void Recharge::updateView() currentY += 32 + 4; } + + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mView->setVisibleVScroll(false); mView->setCanvasSize (MyGUI::IntSize(mView->getWidth(), std::max(mView->getHeight(), currentY))); + mView->setVisibleVScroll(true); } void Recharge::onCancel(MyGUI::Widget *sender) diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 986e27243..019f341b5 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -126,7 +126,10 @@ void Repair::updateRepairView() currentY += 32 + 4; } } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mRepairView->setVisibleVScroll(false); mRepairView->setCanvasSize (MyGUI::IntSize(mRepairView->getWidth(), std::max(mRepairView->getHeight(), currentY))); + mRepairView->setVisibleVScroll(true); } void Repair::onCancel(MyGUI::Widget *sender) diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index e27e40ae6..072411707 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -320,7 +320,10 @@ namespace MWGui if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); + mSkillView->setVisibleVScroll(true); } // widget controls diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 3d0751114..867bef0d7 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -57,10 +57,13 @@ namespace MWGui BookTextParser parser; MyGUI::IntSize size = parser.parseScroll(ref->mBase->mText, mTextView, 390); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mTextView->setVisibleVScroll(false); if (size.height > mTextView->getSize().height) mTextView->setCanvasSize(MyGUI::IntSize(410, size.height)); else mTextView->setCanvasSize(410, mTextView->getSize().height); + mTextView->setVisibleVScroll(true); mTextView->setViewOffset(MyGUI::IntPoint(0,0)); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 09d2fc19c..abeb6b86c 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -473,7 +473,10 @@ namespace MWGui curH += h; } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mControlsBox->setVisibleVScroll(false); mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); + mControlsBox->setVisibleVScroll(true); } void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 8d9f35daa..d6c716453 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -50,6 +50,8 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + // TODO: refactor to use MyGUI::ListBox + MyGUI::Button* toAdd = mSpellsView->createWidget( "SandTextButton", @@ -106,7 +108,10 @@ namespace MWGui updateLabels(); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSpellsView->setVisibleVScroll(false); mSpellsView->setCanvasSize (MyGUI::IntSize(mSpellsView->getWidth(), std::max(mSpellsView->getHeight(), mCurrentY))); + mSpellsView->setVisibleVScroll(true); } bool SpellBuyingWindow::playerHasSpell(const std::string &id) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 030b8bf37..f1ca3c802 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -637,7 +637,10 @@ namespace MWGui ++i; } + // Canvas size must be expressed with HScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mUsedEffectsView->setVisibleHScroll(false); mUsedEffectsView->setCanvasSize(size); + mUsedEffectsView->setVisibleHScroll(true); notifyEffectsChanged(); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 77da56fa4..15b5bf280 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -261,7 +261,10 @@ namespace MWGui mHeight += spellHeight; } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSpellView->setVisibleVScroll(false); mSpellView->setCanvasSize(mSpellView->getWidth(), std::max(mSpellView->getHeight(), mHeight)); + mSpellView->setVisibleVScroll(true); } void SpellWindow::addGroup(const std::string &label, const std::string& label2) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index b11258f1c..7f3b66d83 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -82,7 +82,10 @@ namespace MWGui { mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), mSkillView->getCanvasSize().height); + mSkillView->setVisibleVScroll(true); } void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) @@ -578,7 +581,10 @@ namespace MWGui mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); } + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mSkillView->setVisibleVScroll(false); mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), coord1.top)); + mSkillView->setVisibleVScroll(true); } void StatsWindow::onPinToggled() diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 9aa75173a..d874e8c0c 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -126,7 +126,10 @@ namespace MWGui } updateLabels(); + // Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden + mDestinationsView->setVisibleVScroll(false); mDestinationsView->setCanvasSize (MyGUI::IntSize(mDestinationsView->getWidth(), std::max(mDestinationsView->getHeight(), mCurrentY))); + mDestinationsView->setVisibleVScroll(true); } void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender) From fdc6dd6985959a24b4c4f0b29e16e7717201f79a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 15:34:42 +0200 Subject: [PATCH 097/707] Shader compatibility fixes for GLSL ES --- files/materials/atmosphere.shader | 2 +- files/materials/clouds.shader | 2 +- files/materials/mygui.shader | 2 +- files/materials/objects.shader | 16 ++++++++-------- files/materials/terrain.shader | 24 ++++++++++++------------ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index f02c5766f..c8a8e0220 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -33,7 +33,7 @@ SH_START_PROGRAM { - shOutputColour(0) = alphaFade * atmosphereColour + (1.f - alphaFade) * horizonColour; + shOutputColour(0) = alphaFade * atmosphereColour + (1.0 - alphaFade) * horizonColour; } #endif diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index d3e5f083c..9e9b10256 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -25,7 +25,7 @@ shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition)); UV = uv0; - alphaFade = (shInputPosition.z <= 200.f) ? ((shInputPosition.z <= 100.f) ? 0.0 : 0.25) : 1.0; + alphaFade = (shInputPosition.z <= 200.0) ? ((shInputPosition.z <= 100.0) ? 0.0 : 0.25) : 1.0; } #else diff --git a/files/materials/mygui.shader b/files/materials/mygui.shader index 014558d97..4d12eba90 100644 --- a/files/materials/mygui.shader +++ b/files/materials/mygui.shader @@ -14,7 +14,7 @@ SH_START_PROGRAM { - shOutputPosition = float4(shInputPosition.xyz, 1.f); + shOutputPosition = float4(shInputPosition.xyz, 1.0); #if TEXTURE UV.xy = uv0; #endif diff --git a/files/materials/objects.shader b/files/materials/objects.shader index ed75babdd..bacf3f912 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -210,11 +210,11 @@ #if VERTEXCOLOR_MODE == 2 lightResult.xyz += colour.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #else lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #endif #if @shIterator == 0 @@ -432,11 +432,11 @@ #if VERTEXCOLOR_MODE == 2 lightResult.xyz += colourPassthrough.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #else lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(viewNormal.xyz, lightDir), 0); + * max(dot(viewNormal.xyz, lightDir), 0.0); #endif #if @shIterator == 0 @@ -504,8 +504,8 @@ #if ENV_MAP // Everything looks better with fresnel - float facing = 1.0 - max(abs(dot(-eyeDir, normal)), 0); - float envFactor = shSaturate(0.25 + 0.75 * pow(facing, 1)); + float facing = 1.0 - max(abs(dot(-eyeDir, normal)), 0.0); + float envFactor = shSaturate(0.25 + 0.75 * pow(facing, 1.0)); shOutputColour(0).xyz += shSample(envMap, UV.zw).xyz * envFactor * env_map_color; #endif @@ -513,7 +513,7 @@ #if SPECULAR float3 light0Dir = normalize(lightPosObjSpace0.xyz); - float NdotL = max(dot(normal, light0Dir), 0); + float NdotL = max(dot(normal, light0Dir), 0.0); float3 halfVec = normalize (light0Dir + eyeDir); float shininess = matShininess; @@ -522,7 +522,7 @@ shininess *= (specTex.a); #endif - float3 specular = pow(max(dot(normal, halfVec), 0), shininess) * lightSpec0 * matSpec; + float3 specular = pow(max(dot(normal, halfVec), 0.0), shininess) * lightSpec0 * matSpec; #if SPEC_MAP specular *= specTex.xyz; #else diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 1436de0c3..8384588e4 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -175,7 +175,7 @@ lightResult.xyz += lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normal.xyz, lightDir), 0); + * max(dot(normal.xyz, lightDir), 0.0); #if @shIterator == 0 directionalResult = lightResult.xyz; @@ -310,7 +310,7 @@ shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) #if !IS_FIRST_PASS // Opacity the previous passes should have, i.e. 1 - (opacity of this pass) -float previousAlpha = 1.f; +float previousAlpha = 1.0; #endif @@ -334,7 +334,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; float4 albedo = float4(0,0,0,1); - float2 layerUV = float2(UV.x, 1.f-UV.y) * 16; // Reverse Y, required to get proper tangents + float2 layerUV = float2(UV.x, 1.0-UV.y) * 16.0; // Reverse Y, required to get proper tangents float2 thisLayerUV; float4 normalTex; float4 diffuseTex; @@ -349,9 +349,9 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; #if @shPropertyBool(use_normal_map_@shIterator) normalTex = shSample(normalMap@shIterator, thisLayerUV); #if @shIterator == 0 && IS_FIRST_PASS - TSnormal = normalize(normalTex.xyz * 2 - 1); + TSnormal = normalize(normalTex.xyz * 2.0 - 1.0); #else - TSnormal = shLerp(TSnormal, normalTex.xyz * 2 - 1, blendValues@shPropertyString(blendmap_component_@shIterator)); + TSnormal = shLerp(TSnormal, normalTex.xyz * 2.0 - 1.0, blendValues@shPropertyString(blendmap_component_@shIterator)); #endif #endif @@ -361,7 +361,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; diffuseTex = shSample(diffuseMap@shIterator, layerUV); #if !@shPropertyBool(use_specular_@shIterator) - diffuseTex.a = 0; + diffuseTex.a = 0.0; #endif #if @shIterator == 0 @@ -371,7 +371,7 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon #endif #if !IS_FIRST_PASS - previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); + previousAlpha *= 1.0-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @@ -404,7 +404,7 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon lightResult.xyz += lightDiffuse[@shIterator].xyz * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normal.xyz, lightDir), 0); + * max(dot(normal.xyz, lightDir), 0.0); #if @shIterator == 0 float3 directionalResult = lightResult.xyz; #endif @@ -444,10 +444,10 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon // Specular float3 light0Dir = normalize(lightPos0.xyz); - float NdotL = max(dot(normal, light0Dir), 0); + float NdotL = max(dot(normal, light0Dir), 0.0); float3 halfVec = normalize (light0Dir + eyeDir); - float3 specular = pow(max(dot(normal, halfVec), 0), 32) * lightSpec0; + float3 specular = pow(max(dot(normal, halfVec), 0.0), 32.0) * lightSpec0; shOutputColour(0).xyz += specular * (albedo.a) * shadow; #endif @@ -465,9 +465,9 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); #if IS_FIRST_PASS - shOutputColour(0).a = 1; + shOutputColour(0).a = 1.0; #else - shOutputColour(0).a = 1.f-previousAlpha; + shOutputColour(0).a = 1.0-previousAlpha; #endif } From 8c81e22f3e3c92905e5ec21c30d439ef225822c3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 18:01:53 +0200 Subject: [PATCH 098/707] Determine target for On Touch effects for non-player actors --- apps/openmw/mwworld/worldimp.cpp | 86 ++++++++++++++++++++++--------- apps/openmw/mwworld/worldimp.hpp | 2 +- libs/openengine/bullet/physic.cpp | 4 +- libs/openengine/bullet/physic.hpp | 4 +- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9b747582f..10c2e4974 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -148,7 +148,7 @@ namespace MWWorld mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (activationDistanceOverride), mFallback(fallbackMap), mTeleportEnabled(true), mLevitationEnabled(true), - mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles), + mGodMode(false), mContentFiles (contentFiles), mGoToJail(false), mStartCell (startCell), mStartupScript(startupScript) { @@ -290,7 +290,6 @@ namespace MWWorld mGodMode = false; mSky = true; mTeleportEnabled = true; - mFacedDistance = FLT_MAX; mGlobalVariables.fill (mStore); } @@ -1462,30 +1461,22 @@ namespace MWWorld updateFacedHandle (); } - void World::updateFacedHandle () + void World::getFacedHandle(std::string& facedHandle, float maxDistance) { - float telekinesisRangeBonus = - mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() - .get(ESM::MagicEffect::Telekinesis).mMagnitude; - telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); + maxDistance += mRendering->getCameraDistance(); - float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; - activationDistance += mRendering->getCameraDistance(); - - // send new query - // figure out which object we want to test against std::vector < std::pair < float, std::string > > results; if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedHandles(x, y, activationDistance); + results = mPhysics->getFacedHandles(x, y, maxDistance); if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()*50); } else { - results = mPhysics->getFacedHandles(activationDistance); + results = mPhysics->getFacedHandles(maxDistance); } // ignore the player and other things we're not interested in @@ -1508,15 +1499,21 @@ namespace MWWorld if (results.empty() || results.front().second.find("HeightField") != std::string::npos) // Blocked by terrain - { - mFacedHandle = ""; - mFacedDistance = FLT_MAX; - } + facedHandle = ""; else - { - mFacedHandle = results.front().second; - mFacedDistance = results.front().first; - } + facedHandle = results.front().second; + } + + void World::updateFacedHandle () + { + float telekinesisRangeBonus = + mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() + .get(ESM::MagicEffect::Telekinesis).mMagnitude; + telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); + + float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + + getFacedHandle(mFacedHandle, activationDistance); } bool World::isCellExterior() const @@ -2344,8 +2341,49 @@ namespace MWWorld { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - // TODO: this only works for the player - MWWorld::Ptr target = getFacedObject(); + // Get the target to use for "on touch" effects + MWWorld::Ptr target; + float distance = 192.f; // ?? + + if (actor == getPlayerPtr()) + { + // For the player, use camera to aim + std::string facedHandle; + getFacedHandle(facedHandle, distance); + if (!facedHandle.empty()) + target = getPtrViaHandle(facedHandle); + } + else + { + // For NPCs use facing direction from Head node + Ogre::Vector3 origin(actor.getRefData().getPosition().pos); + MWRender::Animation *anim = mRendering->getAnimation(actor); + if(anim != NULL) + { + Ogre::Node *node = anim->getNode("Head"); + if (node == NULL) + node = anim->getNode("Bip01 Head"); + if(node != NULL) + origin += node->_getDerivedPosition(); + } + Ogre::Quaternion orient; + orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + Ogre::Vector3 direction = orient.yAxis(); + Ogre::Vector3 dest = origin + direction * distance; + + + std::vector > collisions = mPhysEngine->rayTest2(btVector3(origin.x, origin.y, origin.z), btVector3(dest.x, dest.y, dest.z)); + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end(); ++cIt) + { + MWWorld::Ptr collided = getPtrViaHandle(cIt->second); + if (collided != actor) + { + target = collided; + break; + } + } + } std::string selectedSpell = stats.getSpells().getSelectedSpell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index dda442963..5c44388ca 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -92,7 +92,6 @@ namespace MWWorld int mActivationDistanceOverride; std::string mFacedHandle; - float mFacedDistance; std::string mStartupScript; @@ -114,6 +113,7 @@ namespace MWWorld void updateWindowManager (); void performUpdateSceneQueries (); void updateFacedHandle (); + void getFacedHandle(std::string& facedHandle, float maxDistance); float getMaxActivationDistance (); float getNpcActivationDistance (); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 6bc544af4..954d7c283 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -737,7 +737,7 @@ namespace Physic { } - std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap, Ogre::Vector3* normal) + std::pair PhysicEngine::rayTest(const btVector3 &from, const btVector3 &to, bool raycastingObjectOnly, bool ignoreHeightMap, Ogre::Vector3* normal) { std::string name = ""; float d = -1; @@ -801,7 +801,7 @@ namespace Physic return std::make_pair(false, 1); } - std::vector< std::pair > PhysicEngine::rayTest2(btVector3& from, btVector3& to) + std::vector< std::pair > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to) { MyRayResultCallback resultCallback1; resultCallback1.m_collisionFilterGroup = 0xff; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index e37caee38..09bff4b04 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -302,13 +302,13 @@ namespace Physic * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). * If \a normal is non-NULL, the hit normal will be written there (if there is a hit) */ - std::pair rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly = true, + std::pair rayTest(const btVector3& from,const btVector3& to,bool raycastingObjectOnly = true, bool ignoreHeightMap = false, Ogre::Vector3* normal = NULL); /** * Return all objects hit by a ray. */ - std::vector< std::pair > rayTest2(btVector3& from, btVector3& to); + std::vector< std::pair > rayTest2(const btVector3 &from, const btVector3 &to); std::pair sphereCast (float radius, btVector3& from, btVector3& to); ///< @return (hit, relative distance) From d7acb7fc7dfc0f462232732f9d3fcf4d0f7793ee Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 22:24:05 +0200 Subject: [PATCH 099/707] Ignore invalid shader cache index (Bug #1664) --- extern/shiny/Main/Factory.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/extern/shiny/Main/Factory.cpp b/extern/shiny/Main/Factory.cpp index 72af07d58..eab5c8dfc 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -51,24 +51,32 @@ namespace sh { assert(mCurrentLanguage != Language_None); - if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt")) + try { - std::ifstream file; - file.open(std::string(mPlatform->getCacheFolder () + "/lastModified.txt").c_str()); - - std::string line; - while (getline(file, line)) + if (boost::filesystem::exists (mPlatform->getCacheFolder () + "/lastModified.txt")) { - std::string sourceFile = line; + std::ifstream file; + file.open(std::string(mPlatform->getCacheFolder () + "/lastModified.txt").c_str()); + + std::string line; + while (getline(file, line)) + { + std::string sourceFile = line; - if (!getline(file, line)) - assert(0); + if (!getline(file, line)) + assert(0); - int modified = boost::lexical_cast(line); + int modified = boost::lexical_cast(line); - mShadersLastModified[sourceFile] = modified; + mShadersLastModified[sourceFile] = modified; + } } } + catch (std::exception& e) + { + std::cerr << "Failed to load shader modification index: " << e.what() << std::endl; + mShadersLastModified.clear(); + } // load configurations { From 47e42d4fda6bdadb67b6b1eadcd893da172cf3e0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Jul 2014 22:29:04 +0200 Subject: [PATCH 100/707] Destroy Engine after exception is logged In cases where OpenMW throws an exception, then crashes in the Engine destructor (ideally should not happen, but keeps happening), we will at least see what the exception was about. --- apps/openmw/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index adde408b9..f91d7de7a 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -309,6 +309,8 @@ int main(int argc, char**argv) boost::filesystem::ofstream logfile; + std::auto_ptr engine; + int ret = 0; try { @@ -350,11 +352,11 @@ int main(int argc, char**argv) boost::filesystem::current_path(bundlePath); #endif - OMW::Engine engine(cfgMgr); + engine.reset(new OMW::Engine(cfgMgr)); - if (parseOptions(argc, argv, engine, cfgMgr)) + if (parseOptions(argc, argv, *engine, cfgMgr)) { - engine.go(); + engine->go(); } } catch (std::exception &e) From 1a04501951c6dce2f7b762e619bc3daa24cfcabe Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 27 Jul 2014 18:53:54 +0200 Subject: [PATCH 101/707] Handle faction save/load properly when player has faction reputation in a faction he is not a member of (Fixes #1573) --- components/esm/npcstats.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 3fa954182..26d8f9596 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -4,7 +4,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -ESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (0), mReputation (0) {} +ESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (-1), mReputation (0) {} void ESM::NpcStats::load (ESMReader &esm) { @@ -98,7 +98,7 @@ void ESM::NpcStats::save (ESMWriter &esm) const esm.writeHNT ("FAEX", expelled); } - if (iter->second.mRank) + if (iter->second.mRank >= 0) esm.writeHNT ("FARA", iter->second.mRank); if (iter->second.mReputation) From d81e9cfefd179c8498325baf3bb231ec9fd10a07 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 27 Jul 2014 20:30:52 +0200 Subject: [PATCH 102/707] Implement actors fighting for the actor they are following (Fixes #1141) --- apps/openmw/mwclass/creature.cpp | 13 ++- apps/openmw/mwmechanics/actors.cpp | 84 +++++++++---------- apps/openmw/mwmechanics/aicombat.cpp | 2 + apps/openmw/mwmechanics/aifollow.cpp | 5 ++ apps/openmw/mwmechanics/aifollow.hpp | 2 + apps/openmw/mwmechanics/aisequence.cpp | 10 +++ apps/openmw/mwmechanics/aisequence.hpp | 4 + .../mwmechanics/mechanicsmanagerimp.cpp | 2 + apps/openmw/mwworld/class.hpp | 6 -- 9 files changed, 77 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a74ee6fd6..c175ce3eb 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -366,10 +366,17 @@ namespace MWClass getCreatureStats(ptr).setAttacked(true); // Self defense - if (!attacker.isEmpty() && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker) - && (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures - // (they have no movement or attacks anyway) + if ( ((!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr)) + || attacker == MWBase::Environment::get().getWorld()->getPlayerPtr()) + && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker) + && (canWalk(ptr) || canFly(ptr) || canSwim(ptr)) // No retaliation for totally static creatures + // (they have no movement or attacks anyway) + ) + { + // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. + // Note: accidental or collateral damage attacks are ignored. MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); + } if(!successful) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8f1209827..36397ac09 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -192,6 +192,15 @@ namespace MWMechanics CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1); if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player + if (actor2.getClass().getCreatureStats(actor2).isDead() + || actor1.getClass().getCreatureStats(actor1).isDead()) + return; + + const ESM::Position& actor1Pos = actor2.getRefData().getPosition(); + const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); + float sqrDist = Ogre::Vector3(actor1Pos.pos).squaredDistance(Ogre::Vector3(actor2Pos.pos)); + if (sqrDist > 7168*7168) + return; // pure water creatures won't try to fight with the target on the ground // except that creature is already hostile @@ -208,9 +217,9 @@ namespace MWMechanics else { aggressive = false; - // if one of actors is creature then we should make a decision to start combat or not - // NOTE: function doesn't take into account combat between 2 creatures - if (!actor1.getClass().isNpc()) + + // Make guards fight aggressive creatures + if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) { // if creature is hostile then it is necessarily to start combat if (creatureStats.isHostile()) aggressive = true; @@ -218,24 +227,34 @@ namespace MWMechanics } } + // start combat if we are in combat with any followers of this actor + const std::list& followers = getActorsFollowing(actor2); + for (std::list::const_iterator it = followers.begin(); it != followers.end(); ++it) + { + if (creatureStats.getAiSequence().isInCombat(*it)) + aggressive = true; + } + // start combat if we are in combat with someone this actor is following + const CreatureStats& creatureStats2 = actor2.getClass().getCreatureStats(actor2); + for (std::list::const_iterator it = creatureStats2.getAiSequence().begin(); it != creatureStats2.getAiSequence().end(); ++it) + { + if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && + creatureStats.getAiSequence().isInCombat(dynamic_cast(*it)->getTarget())) + aggressive = true; + } + if(aggressive) { - const ESM::Position& actor1Pos = actor2.getRefData().getPosition(); - const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); - float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos)); - if (againstPlayer || actor2.getClass().getCreatureStats(actor2).getAiSequence().canAddTarget(actor2Pos, d)) - { - bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); + bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor1, actor2); - if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); + if (againstPlayer) LOS &= MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor2, actor1); - if (LOS) + if (LOS) + { + MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); + if (!againstPlayer) // start combat between each other { - MWBase::Environment::get().getMechanicsManager()->startCombat(actor1, actor2); - if (!againstPlayer) // start combat between each other - { - MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1); - } + MWBase::Environment::get().getMechanicsManager()->startCombat(actor2, actor1); } } } @@ -964,19 +983,10 @@ namespace MWMechanics } } - static Ogre::Vector3 sBasePoint; - bool comparePtrDist (const MWWorld::Ptr& ptr1, const MWWorld::Ptr& ptr2) - { - return (sBasePoint.squaredDistance(Ogre::Vector3(ptr1.getRefData().getPosition().pos)) - < sBasePoint.squaredDistance(Ogre::Vector3(ptr2.getRefData().getPosition().pos))); - } - void Actors::update (float duration, bool paused) { if(!paused) - { - std::list listGuards; // at the moment only guards certainly will fight with creatures - + { static float timerUpdateAITargets = 0; // target lists get updated once every 1.0 sec @@ -989,16 +999,10 @@ namespace MWMechanics // Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation // (below) iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string()); - - // add guards to list to later make them fight with creatures - if (timerUpdateAITargets == 0 && iter->first.getClass().isClass(iter->first, "Guard")) - listGuards.push_back(iter->first); } MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - listGuards.push_back(player); - // AI and magic effects update for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1008,20 +1012,16 @@ namespace MWMechanics if (MWBase::Environment::get().getMechanicsManager()->isAIActive()) { - // make guards and creatures fight each other - if (timerUpdateAITargets == 0 && iter->first.getTypeName() == typeid(ESM::Creature).name() && !listGuards.empty()) - { - sBasePoint = Ogre::Vector3(iter->first.getRefData().getPosition().pos); - listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest guard - - for (std::list::iterator it = listGuards.begin(); it != listGuards.end(); ++it) + if (timerUpdateAITargets == 0) + { + for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { - engageCombat(iter->first, *it, *it == player); + if (it->first == iter->first || iter->first == player) // player is not AI-controlled + continue; + engageCombat(iter->first, it->first, it->first == player); } } - if (iter->first != player) engageCombat(iter->first, player, true); - if (iter->first.getClass().isNpc() && iter->first != player) updateCrimePersuit(iter->first, duration); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c0a97a1b1..491954495 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -174,6 +174,8 @@ namespace MWMechanics return true; MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); + if (target.isEmpty()) + return false; if(target.getClass().getCreatureStats(target).isDead()) return true; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 5ab7e1730..c2bb0f2f4 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -121,3 +121,8 @@ MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) { } + +MWWorld::Ptr MWMechanics::AiFollow::getTarget() const +{ + return MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); +} diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index e9587b36e..744a9c505 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -31,6 +31,8 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); + MWWorld::Ptr getTarget() const; + virtual AiFollow *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 02f00dfc6..1d0065679 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -72,6 +72,16 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const return true; } +std::list::const_iterator AiSequence::begin() const +{ + return mPackages.begin(); +} + +std::list::const_iterator AiSequence::end() const +{ + return mPackages.end(); +} + bool AiSequence::isInCombat() const { for(std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index bc20dc61b..af14cce59 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -50,6 +50,10 @@ namespace MWMechanics virtual ~AiSequence(); + /// Iterator may be invalidated by any function calls other than begin() or end(). + std::list::const_iterator begin() const; + std::list::const_iterator end() const; + /// Returns currently executing AiPackage type /** \see enum AiPackage::TypeId **/ int getTypeId() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 861d5e110..fd4269930 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1171,6 +1171,8 @@ namespace MWMechanics void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) { + if (ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(target)) + return; ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr); if (target == MWBase::Environment::get().getWorld()->getPlayerPtr()) { diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index c3f94d7f1..e880030f9 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -72,12 +72,6 @@ namespace MWWorld public: - /// NPC-stances. - enum Stance - { - Run, Sneak - }; - virtual ~Class(); const std::string& getTypeName() const { From 2abc4e42c8610d97901db3c863c50cabfa15c0a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 27 Jul 2014 20:49:57 +0200 Subject: [PATCH 103/707] end parsing of line after parsing a declaration --- components/compiler/lineparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 2226f5845..0a3629912 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -386,7 +386,7 @@ namespace Compiler if (declaration.parseKeyword (keyword, loc, scanner)) scanner.scan (declaration); - return true; + return false; } case Scanner::K_set: mState = SetState; return true; From 17bd094afd9fabc19dc3a86a6a542996805db2c8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 27 Jul 2014 20:55:54 +0200 Subject: [PATCH 104/707] allow a few more stray arguments --- components/compiler/extensions0.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index ef4fe4fbd..3945b73cc 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -59,7 +59,7 @@ namespace Compiler extensions.registerInstruction ("modfight", "l", opcodeModFight, opcodeModFightExplicit); extensions.registerInstruction ("modflee", "l", opcodeModFlee, opcodeModFleeExplicit); extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); - extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); + extensions.registerInstruction ("toggleai", "X", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit); extensions.registerInstruction("stopcombat", "x", opcodeStopCombat, opcodeStopCombatExplicit); @@ -113,12 +113,12 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { - extensions.registerInstruction ("additem", "cl", opcodeAddItem, opcodeAddItemExplicit); + extensions.registerInstruction ("additem", "clxx", opcodeAddItem, opcodeAddItemExplicit); extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount, opcodeGetItemCountExplicit); extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, opcodeRemoveItemExplicit); - extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); + extensions.registerInstruction ("equip", "cX", opcodeEquip, opcodeEquipExplicit); extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit); @@ -163,7 +163,7 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { - extensions.registerInstruction ("journal", "cl", opcodeJournal); + extensions.registerInstruction ("journal", "clxxX", opcodeJournal); extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex); extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex); extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic); @@ -406,7 +406,7 @@ namespace Compiler extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); - extensions.registerInstruction ("addspell", "cxX", opcodeAddSpell, opcodeAddSpellExplicit); + extensions.registerInstruction ("addspell", "cz", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, From 6262d6c9644b5459078126b389dc218db8eb680a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 27 Jul 2014 23:10:58 +0200 Subject: [PATCH 105/707] Don't leave stale player CharacterController in Actors when loading game (Fixes #1713) --- apps/openmw/mwmechanics/actors.cpp | 14 ++++++++------ apps/openmw/mwworld/worldimp.cpp | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 36397ac09..a215a71a3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -924,12 +924,7 @@ namespace MWMechanics Actors::~Actors() { - PtrControllerMap::iterator it(mActors.begin()); - for (; it != mActors.end(); ++it) - { - delete it->second; - it->second = NULL; - } + clear(); } void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately) @@ -1359,6 +1354,13 @@ namespace MWMechanics void Actors::clear() { + PtrControllerMap::iterator it(mActors.begin()); + for (; it != mActors.end(); ++it) + { + delete it->second; + it->second = NULL; + } + mActors.clear(); mDeathCount.clear(); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 10c2e4974..14746526b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2111,8 +2111,8 @@ namespace MWWorld void World::enableActorCollision(const MWWorld::Ptr& actor, bool enable) { OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle()); - - physicActor->enableCollisionBody(enable); + if (physicActor) + physicActor->enableCollisionBody(enable); } bool World::findInteriorPosition(const std::string &name, ESM::Position &pos) From b4ba18afe7ba0c4a91a24d99d21f6ff9439e896f Mon Sep 17 00:00:00 2001 From: MiroslavR Date: Sun, 27 Jul 2014 23:36:40 +0200 Subject: [PATCH 106/707] Include revision number in the "version" command line option (Closes #1711) --- apps/openmw/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index adde408b9..0aa763407 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -184,6 +184,14 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat if (variables.count ("version")) { std::cout << "OpenMW version " << OPENMW_VERSION << std::endl; + + std::string rev = OPENMW_VERSION_COMMITHASH; + std::string tag = OPENMW_VERSION_TAGHASH; + if (!rev.empty() && !tag.empty()) + { + rev = rev.substr(0, 10); + std::cout << "Revision " << rev << std::endl; + } run = false; } From 45206bc3f6c973d899f70e367921206bf1a53715 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 00:32:59 +0200 Subject: [PATCH 107/707] Savegame: write and read dynamic Store before Cells --- apps/openmw/mwworld/worldimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 14746526b..ebbc16c23 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -319,8 +319,9 @@ namespace MWWorld MWMechanics::CreatureStats::writeActorIdCounter(writer); progress.increaseProgress(); + mStore.write (writer, progress); // dynamic Store must be written (and read) before Cells, so that + // references to custom made records will be recognized mCells.write (writer, progress); - mStore.write (writer, progress); mGlobalVariables.write (writer, progress); mPlayer->write (writer, progress); mWeatherManager->write (writer, progress); From 315b022d2d01a1887cd5a76f108b14a2d160b103 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 00:55:57 +0200 Subject: [PATCH 108/707] Add transfer gold from all services to NPC trade gold pool --- apps/openmw/mwgui/merchantrepair.cpp | 6 ++++++ apps/openmw/mwgui/spellbuyingwindow.cpp | 5 +++++ apps/openmw/mwgui/spellcreationdialog.cpp | 8 +++++++- apps/openmw/mwgui/trainingwindow.cpp | 3 +++ apps/openmw/mwgui/travelwindow.cpp | 6 ++++++ apps/openmw/mwmechanics/enchanting.cpp | 4 ++++ 6 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 013a40017..b6f9936a8 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -10,6 +10,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" @@ -134,6 +136,10 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& actorStats = mActor.getClass().getCreatureStats(mActor); + actorStats.setGoldPool(actorStats.getGoldPool() + price); + startRepair(mActor); } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index d6c716453..cd378aadf 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -135,6 +135,11 @@ namespace MWGui MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); + startSpellBuying(mPtr); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index f1ca3c802..63cbdb567 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -352,7 +352,13 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, boost::lexical_cast(mPriceLabel->getCaption()), player); + int price = boost::lexical_cast(mPriceLabel->getCaption()); + + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 6463db3d7..babfe099f 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -159,6 +159,9 @@ namespace MWGui // remove gold player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + // add gold to NPC trading gold pool + npcStats.setGoldPool(npcStats.getGoldPool() + price); + // go back to game mode MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Training); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index d874e8c0c..ae191e764 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -12,6 +12,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" @@ -150,6 +152,10 @@ namespace MWGui player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + // add gold to NPC trading gold pool + MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr); + npcStats.setGoldPool(npcStats.getGoldPool() + price); + MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); ESM::Position pos = *_sender->getUserData(); std::string cellname = _sender->getUserString("Destination"); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 18732514b..9663a2d51 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -292,5 +292,9 @@ namespace MWMechanics MWWorld::ContainerStore& store = player.getClass().getContainerStore(player); store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player); + + // add gold to NPC trading gold pool + CreatureStats& enchanterStats = mEnchanter.getClass().getCreatureStats(mEnchanter); + enchanterStats.setGoldPool(enchanterStats.getGoldPool() + getEnchantPrice()); } } From c6d3b0b70b27d7bf6c590dbaa08ae1e60e1d38fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 02:27:48 +0200 Subject: [PATCH 109/707] Moved merchant restock from trade start to dialogue start, since other services also interact with it. --- apps/openmw/mwgui/dialogue.cpp | 25 +++++++++++++++++++++++++ apps/openmw/mwgui/dialogue.hpp | 1 + apps/openmw/mwgui/tradewindow.cpp | 25 ------------------------- apps/openmw/mwgui/tradewindow.hpp | 2 -- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 2283e0cbe..23098bf42 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -396,6 +396,31 @@ namespace MWGui mLinks.clear(); updateOptions(); + + restock(); + } + + void DialogueWindow::restock() + { + MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); + float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); + + if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) + { + sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr)); + + mPtr.getClass().restock(mPtr); + + // Also restock any containers owned by this merchant, which are also available to buy in the trade window + std::vector itemSources; + MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources); + for (std::vector::iterator it = itemSources.begin(); it != itemSources.end(); ++it) + { + it->getClass().restock(*it); + } + + sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); + } } void DialogueWindow::setKeywords(std::list keyWords) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 516c04942..71935dfaf 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -152,6 +152,7 @@ namespace MWGui private: void updateOptions(); + void restock(); int mServices; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index c56c2ee94..f94e39b2f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -99,8 +99,6 @@ namespace MWGui mCurrentBalance = 0; mCurrentMerchantOffer = 0; - restock(); - std::vector itemSources; MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); @@ -509,29 +507,6 @@ namespace MWGui return merchantGold; } - void TradeWindow::restock() - { - MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); - float delay = MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getFloat(); - - if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay) - { - sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr)); - - mPtr.getClass().restock(mPtr); - - // Also restock any containers owned by this merchant, which are also available to buy in the trade window - std::vector itemSources; - MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources); - for (std::vector::iterator it = itemSources.begin(); it != itemSources.end(); ++it) - { - it->getClass().restock(*it); - } - - sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp()); - } - } - void TradeWindow::resetReference() { ReferenceInterface::resetReference(); diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index b487a8870..7baf0ce8e 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -104,8 +104,6 @@ namespace MWGui virtual void onReferenceUnavailable(); int getMerchantGold(); - - void restock(); }; } From ad50b926f5a667fed85b2ef4a6b44ccad091eeb8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 28 Jul 2014 09:01:44 +0200 Subject: [PATCH 110/707] reducing some stray arguments again --- components/compiler/extensions0.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 3945b73cc..0181fae96 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -59,7 +59,7 @@ namespace Compiler extensions.registerInstruction ("modfight", "l", opcodeModFight, opcodeModFightExplicit); extensions.registerInstruction ("modflee", "l", opcodeModFlee, opcodeModFleeExplicit); extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); - extensions.registerInstruction ("toggleai", "X", opcodeToggleAI, opcodeToggleAI); + extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit); extensions.registerInstruction("stopcombat", "x", opcodeStopCombat, opcodeStopCombatExplicit); @@ -113,7 +113,7 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { - extensions.registerInstruction ("additem", "clxx", opcodeAddItem, opcodeAddItemExplicit); + extensions.registerInstruction ("additem", "cl", opcodeAddItem, opcodeAddItemExplicit); extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount, opcodeGetItemCountExplicit); extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, @@ -163,7 +163,7 @@ namespace Compiler { void registerExtensions (Extensions& extensions) { - extensions.registerInstruction ("journal", "clxxX", opcodeJournal); + extensions.registerInstruction ("journal", "cl", opcodeJournal); extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex); extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex); extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic); From 3067082534c3b2247ebf11b58818ab5cc0b47ad9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 14:53:53 +0200 Subject: [PATCH 111/707] Make base_anim.nif take priority for biped creatures Fixes the skeletal minion's WalkForward1h animation. --- apps/openmw/mwrender/creatureanimation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index f2447cb70..e4806072a 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -27,9 +27,9 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); + addAnimSource(model); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\base_anim.nif"); - addAnimSource(model); } } @@ -47,9 +47,9 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); + addAnimSource(model); if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\base_anim.nif"); - addAnimSource(model); mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); From d69ed78ccd995a0bb4f3601216951f6b0aa8d7ea Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 15:40:52 +0200 Subject: [PATCH 112/707] Don't add combat AI to player --- apps/openmw/mwclass/npc.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 49e016d4f..13b61d920 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -675,17 +675,20 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. - // Attacking peaceful NPCs is a crime - if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && - !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) - MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); - - if (!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr) - && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)) + if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr()) { - // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. - // Note: accidental or collateral damage attacks are ignored. - MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); + // Attacking peaceful NPCs is a crime + if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && + !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) + MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + + if (!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr) + && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker)) + { + // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. + // Note: accidental or collateral damage attacks are ignored. + MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); + } } bool wasDead = getCreatureStats(ptr).isDead(); From 0077296c917db940d6d1fc473cd74731d3b2e71b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 15:57:16 +0200 Subject: [PATCH 113/707] Take actor's speed into account in stuck check The Winged Twilight's walking animation was so slow that it incorrectly detects being stuck. --- apps/openmw/mwmechanics/aipackage.cpp | 14 +++++--------- apps/openmw/mwmechanics/aipackage.hpp | 2 -- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 7790942b2..ff0329341 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -16,7 +16,7 @@ MWMechanics::AiPackage::~AiPackage() {} -MWMechanics::AiPackage::AiPackage() : mLastDoorChecked(MWWorld::Ptr()), mTimer(.26), mStuckTimer(0) { //mTimer starts at .26 to force initial pathbuild +MWMechanics::AiPackage::AiPackage() : mTimer(.26), mStuckTimer(0) { //mTimer starts at .26 to force initial pathbuild } @@ -92,22 +92,19 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po { /// TODO (tluppi#1#): Use ObstacleCheck here. Not working for some reason //if(mObstacleCheck.check(actor, duration)) { - if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < 10 && distance(dest, start) > 20) { //Actually stuck, and far enough away from destination to care + if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < actor.getClass().getSpeed(actor)*0.05 && distance(dest, start) > 20) { //Actually stuck, and far enough away from destination to care // first check if we're walking into a door MWWorld::Ptr door = getNearbyDoor(actor); if(door != MWWorld::Ptr()) // NOTE: checks interior cells only { - if(door.getCellRef().getTrap().empty() && mLastDoorChecked != door) { //Open the door if untrapped - door.getClass().activate(door, actor).get()->execute(actor); - mLastDoorChecked = door; + if(door.getCellRef().getTrap().empty() && door.getClass().getDoorState(door) == 0) { //Open the door if untrapped + MWBase::Environment::get().getWorld()->activateDoor(door, 1); } } else // probably walking into another NPC { - // TODO: diagonal should have same animation as walk forward - // but doesn't seem to do that? actor.getClass().getMovementSettings(actor).mPosition[0] = 1; - actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; // change the angle a bit, too zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); } @@ -115,7 +112,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po else { //Not stuck, so reset things mStuckTimer = 0; mStuckPos = pos; - mLastDoorChecked = MWWorld::Ptr(); //Resets it, in case he gets stuck behind the door again actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward } } diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 983777c0a..0a5e1c545 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -73,8 +73,6 @@ namespace MWMechanics float mTimer; float mStuckTimer; - MWWorld::Ptr mLastDoorChecked; //Used to ensure we don't try to CONSTANTLY open a door - ESM::Position mStuckPos; ESM::Pathgrid::Point mPrevDest; }; From 4773d754c67dba844821d4fd85446744a9b2d155 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 16:41:12 +0200 Subject: [PATCH 114/707] Remove redundant isHostile flag (Fixes #1652) --- apps/openmw/mwclass/npc.cpp | 6 +++--- apps/openmw/mwmechanics/actors.cpp | 12 +++++------- apps/openmw/mwmechanics/aicombat.cpp | 2 -- apps/openmw/mwmechanics/creaturestats.cpp | 14 +------------- apps/openmw/mwmechanics/creaturestats.hpp | 3 --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 -- apps/openmw/mwscript/aiextensions.cpp | 2 -- components/esm/creaturestats.cpp | 7 ++----- components/esm/creaturestats.hpp | 1 - 9 files changed, 11 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 13b61d920..d087febd0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -678,8 +678,8 @@ namespace MWClass if (ptr != MWBase::Environment::get().getWorld()->getPlayerPtr()) { // Attacking peaceful NPCs is a crime - if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && - !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) + if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker) + && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); if (!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr) @@ -910,7 +910,7 @@ namespace MWClass if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); - if(ptr.getClass().getCreatureStats(ptr).isHostile()) + if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat()) return boost::shared_ptr(new MWWorld::FailedAction("#{sActorInCombat}")); if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)) return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a215a71a3..67e80ec67 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -191,7 +191,6 @@ namespace MWMechanics { CreatureStats& creatureStats = actor1.getClass().getCreatureStats(actor1); - if (againstPlayer && creatureStats.isHostile()) return; // already fighting against player if (actor2.getClass().getCreatureStats(actor2).isDead() || actor1.getClass().getCreatureStats(actor1).isDead()) return; @@ -204,7 +203,7 @@ namespace MWMechanics // pure water creatures won't try to fight with the target on the ground // except that creature is already hostile - if ((againstPlayer || !creatureStats.isHostile()) + if ((againstPlayer || !creatureStats.getAiSequence().isInCombat()) && ((actor1.getClass().canSwim(actor1) && !actor1.getClass().canWalk(actor1) // pure water creature && !MWBase::Environment::get().getWorld()->isSwimming(actor2)) || (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target @@ -222,7 +221,7 @@ namespace MWMechanics if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) { // if creature is hostile then it is necessarily to start combat - if (creatureStats.isHostile()) aggressive = true; + if (creatureStats.getAiSequence().isInCombat()) aggressive = true; else aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); } } @@ -796,7 +795,7 @@ namespace MWMechanics { if (torch != inventoryStore.end()) { - if (!ptr.getClass().getCreatureStats (ptr).isHostile()) + if (!ptr.getClass().getCreatureStats (ptr).getAiSequence().isInCombat()) { // For non-hostile NPCs, unequip whatever is in the left slot in favor of a light. if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) @@ -876,7 +875,7 @@ namespace MWMechanics CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); NpcStats& npcStats = ptr.getClass().getNpcStats(ptr); - if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile()) + if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat()) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); int cutoff = esmStore.get().find("iCrimeThreshold")->getInt(); @@ -909,7 +908,6 @@ namespace MWMechanics creatureStats.getAiSequence().stopCombat(); // Reset factors to attack - creatureStats.setHostile(false); creatureStats.setAttacked(false); creatureStats.setAlarmed(false); @@ -1074,7 +1072,7 @@ namespace MWMechanics if(!stats.isDead()) { - if (stats.isHostile()) hostilesCount++; + if (stats.getAiSequence().isInCombat()) hostilesCount++; } } diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 491954495..d59b0a3ec 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -190,8 +190,6 @@ namespace MWMechanics // 2. creature can't swim to target || (!actorClass.canSwim(actor) && world->isSwimming(target)))) { - if (target == world->getPlayerPtr()) - actorClass.getCreatureStats(actor).setHostile(false); actorClass.getCreatureStats(actor).setAttackingOrSpell(false); return true; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 71217dbb9..fc7481410 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -17,7 +17,7 @@ namespace MWMechanics CreatureStats::CreatureStats() : mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), - mAttacked (false), mHostile (false), + mAttacked (false), mAttackingOrSpell(false), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), @@ -331,16 +331,6 @@ namespace MWMechanics mAttacked = attacked; } - bool CreatureStats::isHostile() const - { - return mHostile; - } - - void CreatureStats::setHostile (bool hostile) - { - mHostile = hostile; - } - bool CreatureStats::getCreatureTargetted() const { MWWorld::Ptr targetPtr; @@ -506,7 +496,6 @@ namespace MWMechanics state.mTalkedTo = mTalkedTo; state.mAlarmed = mAlarmed; state.mAttacked = mAttacked; - state.mHostile = mHostile; state.mAttackingOrSpell = mAttackingOrSpell; // TODO: rewrite. does this really need 3 separate bools? state.mKnockdown = mKnockdown; @@ -555,7 +544,6 @@ namespace MWMechanics mTalkedTo = state.mTalkedTo; mAlarmed = state.mAlarmed; mAttacked = state.mAttacked; - mHostile = state.mHostile; mAttackingOrSpell = state.mAttackingOrSpell; // TODO: rewrite. does this really need 3 separate bools? mKnockdown = state.mKnockdown; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index a83da4249..4ee994de5 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -200,9 +200,6 @@ namespace MWMechanics bool getAttacked() const; void setAttacked (bool attacked); - bool isHostile() const; - void setHostile (bool hostile); - bool getCreatureTargetted() const; float getEvasion() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fd4269930..199dc488d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1176,8 +1176,6 @@ namespace MWMechanics ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr); if (target == MWBase::Environment::get().getWorld()->getPlayerPtr()) { - ptr.getClass().getCreatureStats(ptr).setHostile(true); - // if guard starts combat with player, guards pursuing player should do the same if (ptr.getClass().isClass(ptr, "Guard")) { diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index d844138d6..a3f935487 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -420,7 +420,6 @@ namespace MWScript runtime.pop(); const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - std::string currentTargetId; bool targetsAreEqual = false; MWWorld::Ptr targetPtr; @@ -457,7 +456,6 @@ namespace MWScript MWWorld::Ptr actor = R()(runtime); MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); creatureStats.getAiSequence().stopCombat(); - creatureStats.setHostile(false); } }; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 37f0cc63c..5326b7f77 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -36,8 +36,8 @@ void ESM::CreatureStats::load (ESMReader &esm) mAttacked = false; esm.getHNOT (mAttacked, "ATKD"); - mHostile = false; - esm.getHNOT (mHostile, "HOST"); + if (esm.isNextSub("HOST")) + esm.skipHSub(); // Hostile, no longer used mAttackingOrSpell = false; esm.getHNOT (mAttackingOrSpell, "ATCK"); @@ -148,9 +148,6 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mAttacked) esm.writeHNT ("ATKD", mAttacked); - if (mHostile) - esm.writeHNT ("HOST", mHostile); - if (mAttackingOrSpell) esm.writeHNT ("ATCK", mAttackingOrSpell); diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 7ae57da16..610be0246 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -43,7 +43,6 @@ namespace ESM bool mTalkedTo; bool mAlarmed; bool mAttacked; - bool mHostile; bool mAttackingOrSpell; bool mKnockdown; bool mKnockdownOneFrame; From 18b3e71be54f2246d4f6de43ba05524a6488f879 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 16:59:46 +0200 Subject: [PATCH 115/707] Reset player position when spawning in fallback cell --- apps/openmw/mwworld/player.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 12908ca9d..280761215 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -253,6 +253,10 @@ namespace MWWorld catch (...) { // Cell no longer exists. Place the player in a default cell. + ESM::Position pos = mPlayer.mData.getPosition(); + MWBase::Environment::get().getWorld()->indexToPosition(0, 0, pos.pos[0], pos.pos[1], true); + pos.pos[2] = 0; + mPlayer.mData.setPosition(pos); mCellStore = world.getExterior(0,0); } From d9a6515fe3fea5f43102290171e647dd5dc2444e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 17:11:46 +0200 Subject: [PATCH 116/707] Adjust AiFollow running threshold to more closely match vanilla MW --- apps/openmw/mwmechanics/aifollow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index c2bb0f2f4..13c4a9891 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -74,9 +74,9 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) } //Check if you're far away - if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 1000) + if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 450) actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run - else if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 800) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold + else if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk return false; From d956df83e48d2b7ff472bb6a4341328cbb456f82 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 17:19:20 +0200 Subject: [PATCH 117/707] Don't make guards fight non-aggressive creatures that are in combat Ex. summoned creature that is helping in a fight. --- apps/openmw/mwmechanics/actors.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 67e80ec67..bd87f8b4b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -220,9 +220,8 @@ namespace MWMechanics // Make guards fight aggressive creatures if (!actor1.getClass().isNpc() && actor2.getClass().isClass(actor2, "Guard")) { - // if creature is hostile then it is necessarily to start combat - if (creatureStats.getAiSequence().isInCombat()) aggressive = true; - else aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); + if (creatureStats.getAiSequence().isInCombat() && MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2)) + aggressive = true; } } From f67b7dae913352d15cff4dfc75ddcac2f82dfd06 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 17:28:00 +0200 Subject: [PATCH 118/707] Sheath weapon in all Ai packages except for AiCombat --- apps/openmw/mwmechanics/aiactivate.cpp | 4 ++++ apps/openmw/mwmechanics/aiescort.cpp | 4 ++++ apps/openmw/mwmechanics/aifollow.cpp | 2 ++ apps/openmw/mwmechanics/aipursue.cpp | 2 ++ apps/openmw/mwmechanics/aitravel.cpp | 2 ++ 5 files changed, 14 insertions(+) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 9e01c3fe7..7310b27ab 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -5,6 +5,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -24,6 +26,8 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) ESM::Position pos = actor.getRefData().getPosition(); //position of the actor const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + if(target == MWWorld::Ptr()) return true; //Target doesn't exist diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 3f5724077..cc8dead8a 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -9,6 +9,8 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "steering.hpp" #include "movement.hpp" @@ -72,6 +74,8 @@ namespace MWMechanics return true; } + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 13c4a9891..27d944657 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -35,6 +35,8 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) if(target == MWWorld::Ptr()) return true; //Target doesn't exist + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + ESM::Position pos = actor.getRefData().getPosition(); //position of the actor if(!mAlwaysFollow) //Update if you only follow for a bit diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 60f671c12..2995a8c36 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -38,6 +38,8 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, float duration) if(target == MWWorld::Ptr()) return true; //Target doesn't exist + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + //Set the target desition from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index db137037d..3fd4704d9 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -44,6 +44,8 @@ namespace MWMechanics actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); + MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { From b56cb7e5eec16a5b59102df953db9ee7e86b2a16 Mon Sep 17 00:00:00 2001 From: bogglez Date: Mon, 28 Jul 2014 17:46:41 +0200 Subject: [PATCH 119/707] Remove defunct option for building without FFmpeg - Added REQUIRED to find_package(FFmpeg) - Removed USE_FFMPEG option from CMakeLists.txt - Always use FFmpeg for sound input - Removed SOUND_DEFINE from CMakeLists.txt - Removed #else branch from videoplayer.cpp with dummy VideoState code (FFmpeg is now guaranteed to exist and the code was incomplete) - Remove #ifdef OPENMW_USE_FFMPEG in ffmpeg_decoder.cpp, it is guaranteed to be used - Remove #ifdef OPENMW_USE_FFMPEG from soundmanagerimp.cpp, it is guaranteed to be used --- CMakeLists.txt | 33 +++---------------------- apps/openmw/CMakeLists.txt | 1 - apps/openmw/mwrender/videoplayer.cpp | 23 ----------------- apps/openmw/mwsound/ffmpeg_decoder.cpp | 5 ---- apps/openmw/mwsound/soundmanagerimp.cpp | 5 ---- 5 files changed, 4 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ee3850ddb..2f5e21c61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,9 +74,6 @@ option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF) -# Sound source selection -option(USE_FFMPEG "use ffmpeg for sound" ON) - # OS X deployment option(OPENMW_OSX_DEPLOYMENT OFF) @@ -138,32 +135,10 @@ set(OPENMW_LIBS ${OENGINE_ALL}) set(OPENMW_LIBS_HEADER) # Sound setup -set(GOT_SOUND_INPUT 0) -set(SOUND_INPUT_INCLUDES "") -set(SOUND_INPUT_LIBRARY "") -set(SOUND_DEFINE "") -if (USE_FFMPEG) - set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) - find_package(FFmpeg) - if (FFMPEG_FOUND) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES}) - set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) - set(GOT_SOUND_INPUT 1) - endif (FFMPEG_FOUND) -endif (USE_FFMPEG) - -if (NOT GOT_SOUND_INPUT) - message(WARNING "--------------------") - message(WARNING "Failed to find any sound input packages") - message(WARNING "--------------------") -endif (NOT GOT_SOUND_INPUT) - -if (NOT FFMPEG_FOUND) - message(WARNING "--------------------") - message(WARNING "FFmpeg not found, video playback will be disabled") - message(WARNING "--------------------") -endif (NOT FFMPEG_FOUND) +set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) +find_package(FFmpeg REQUIRED) +set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIRS}) +set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES}) # TinyXML option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0dda1131b..c5ff2a83c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -99,7 +99,6 @@ add_executable(openmw # Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING # when we change the backend. include_directories(${SOUND_INPUT_INCLUDES} ${BULLET_INCLUDE_DIRS}) -add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw ${OGRE_LIBRARIES} diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 409e27388..2d05f2770 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -26,8 +26,6 @@ typedef SSIZE_T ssize_t; namespace MWRender { -#ifdef OPENMW_USE_FFMPEG - extern "C" { #include @@ -1073,27 +1071,6 @@ void VideoState::deinit() } } -#else // defined OPENMW_USE_FFMPEG - -class VideoState -{ -public: - VideoState() { } - - void init(const std::string& resourceName) - { - throw std::runtime_error("FFmpeg not supported, cannot play \""+resourceName+"\""); - } - void deinit() { } - - void close() { } - - bool update() - { return false; } -}; - -#endif // defined OPENMW_USE_FFMPEG - VideoPlayer::VideoPlayer() : mState(NULL) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 10a782b96..982d0c5ff 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -1,6 +1,3 @@ -#ifdef OPENMW_USE_FFMPEG - - #include "ffmpeg_decoder.hpp" // auto_ptr @@ -375,5 +372,3 @@ FFmpeg_Decoder::~FFmpeg_Decoder() } } - -#endif diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index ba7b4f3ba..453a19905 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -17,15 +17,10 @@ #include "openal_output.hpp" #define SOUND_OUT "OpenAL" -/* Set up the sound manager to use FFMPEG for input. - * The OPENMW_USE_x macros are set in CMakeLists.txt. -*/ -#ifdef OPENMW_USE_FFMPEG #include "ffmpeg_decoder.hpp" #ifndef SOUND_IN #define SOUND_IN "FFmpeg" #endif -#endif namespace MWSound From 53496991723a0860da90088ad969596f1767cc64 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 17:54:14 +0200 Subject: [PATCH 120/707] Only show "target resists magic" message for spells cast by player --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 2566bf189..94b479007 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -379,7 +379,7 @@ namespace MWMechanics // Fully resisted, show message if (target.getRefData().getHandle() == "player") MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}"); - else + else if (castByPlayer) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); } } From 8455ed62799491a1f12332dd453af4b29e2475c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 28 Jul 2014 19:40:56 +0200 Subject: [PATCH 121/707] Cancel upper body animations when knocked down --- apps/openmw/mwmechanics/character.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 638feeef3..0809cd640 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -221,6 +221,21 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mCurrentHit = "shield"; mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "block start", "block stop", 0.0f, 0); } + + // Cancel upper body animations + if (mHitState == CharState_KnockDown || mHitState == CharState_KnockOut) + { + if (mUpperBodyState > UpperCharState_WeapEquiped) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } + else if (mUpperBodyState > UpperCharState_Nothing && mUpperBodyState < UpperCharState_WeapEquiped) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_Nothing; + } + } } else if(!mAnimation->isPlaying(mCurrentHit)) { From 0032426817376061cb6d68624e900933c6e734c9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 28 Jul 2014 20:39:37 +0200 Subject: [PATCH 122/707] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 5c757f957..2c8d86f49 100644 --- a/credits.txt +++ b/credits.txt @@ -72,6 +72,7 @@ Sandy Carter (bwrsandman) Sebastian Wick (swick) Sergey Shambir sir_herrbatka +Stefan Galowicz (bogglez) Sylvain Thesnieres (Garvek) Thomas Luppi (Digmaster) Tom Mason (wheybags) From c006393178a359a95a3f089e8e2b734ee6020fff Mon Sep 17 00:00:00 2001 From: bogglez Date: Mon, 28 Jul 2014 21:52:34 +0200 Subject: [PATCH 123/707] Fix http://bugs.openmw.org/issues/768 Changes application_name in configuration manager to OpenMW instead of openmw, if on windows. This shouldn't break anything since Windows uses case-insensitive filesystems. Strictly speaking the installation directory must be OpenMW now though (not sure whether this is the case) --- components/files/configurationmanager.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 58d75d1fd..942f47d4e 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -16,13 +16,19 @@ namespace Files static const char* const openmwCfgFile = "openmw.cfg"; +#if defined(_WIN32) || defined(__WINDOWS__) +static const char* const applicationName = "OpenMW"; +#else +static const char* const applicationName = "openmw"; +#endif + const char* const mwToken = "?mw?"; const char* const localToken = "?local?"; const char* const userDataToken = "?userdata?"; const char* const globalToken = "?global?"; ConfigurationManager::ConfigurationManager() - : mFixedPath("openmw") + : mFixedPath(applicationName) { setupTokensMapping(); From 7dfb624ee2837ed8d6181bc11bffe682a7ba2a8f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 00:23:00 +0200 Subject: [PATCH 124/707] Support loading text keys for objects without a skeleton This means we can no longer map them to bone IDs, but they are unused anyway. Required to load text keys from the default head models (such as Talk: Start/Stop and Blink: Start/Stop) --- components/nifogre/ogrenifloader.cpp | 6 +----- components/nifogre/ogrenifloader.hpp | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 81b2e55d2..c39c62b67 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1038,11 +1038,7 @@ class NIFObjectLoader { const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - if (scene->mSkelBase) - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); - extractTextKeys(tk, scene->mTextKeys[trgtid]); - } + extractTextKeys(tk, scene->mTextKeys); } else if(e->recType == Nif::RC_NiStringExtraData) { diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index badb6ccd3..011245bea 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -69,7 +69,7 @@ struct ObjectScene { // The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length. float mMaxControllerLength; - std::map mTextKeys; + TextKeyMap mTextKeys; MaterialControllerManager mMaterialControllerMgr; From 20efeea5d9ed9590c3c8a0f703a7dbf37e5dc6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aciubid=C5=82o?= Date: Mon, 28 Jul 2014 20:39:13 +0100 Subject: [PATCH 125/707] AI tell their greatings even when player is moving (like morrowind). Greating reset distance was greatly inflated comparing with morrowind. --- apps/openmw/mwmechanics/aiwander.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index b97554e2a..f1a815d30 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -454,9 +454,7 @@ namespace MWMechanics if (!mSaidGreeting) { // TODO: check if actor is aware / has line of sight - if (playerDistSqr <= helloDistance*helloDistance - // Only play a greeting if the player is not moving - && Ogre::Vector3(player.getClass().getMovementSettings(player).mPosition).squaredLength() == 0) + if (playerDistSqr <= helloDistance*helloDistance) { mSaidGreeting = true; MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); @@ -467,7 +465,7 @@ namespace MWMechanics static float fGreetDistanceReset = MWBase::Environment::get().getWorld()->getStore() .get().find("fGreetDistanceReset")->getFloat(); - if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset * iGreetDistanceMultiplier*iGreetDistanceMultiplier) + if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset) mSaidGreeting = false; } From d47bfbe69c9c4ac81d4140445c0451a3dd96eec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aciubid=C5=82o?= Date: Mon, 28 Jul 2014 23:16:24 +0100 Subject: [PATCH 126/707] Implement AI greeting states. Greeting consist of 3 phases: - none - default one, greeting state can only change to "in progress" when near enough and some time passes - in progress - NPC says his greating and rotates toward player, state can only change to "done" after some time - done - rotation is stoped, after idling can go away from player, state can only change to "none" when player and NPC are faraway --- apps/openmw/mwmechanics/aiwander.cpp | 37 +++++++++++++++------------- apps/openmw/mwmechanics/aiwander.hpp | 7 +++++- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f1a815d30..519cc8a61 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -43,7 +43,7 @@ namespace MWMechanics mReaction = 0; mRotate = false; mTargetAngle = 0; - mSaidGreeting = false; + mSaidGreeting = Greet_None; mHasReturnPosition = false; mReturnPosition = Ogre::Vector3(0,0,0); @@ -407,7 +407,7 @@ namespace MWMechanics } // Allow interrupting a walking actor to trigger a greeting - if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState() && mDistance)) + if(mIdleNow || mWalking) { // Play a random voice greeting if the player gets too close int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); @@ -421,8 +421,18 @@ namespace MWMechanics Ogre::Vector3 playerPos(player.getRefData().getPosition().pos); Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); float playerDistSqr = playerPos.squaredDistance(actorPos); - - if(playerDistSqr <= helloDistance*helloDistance) + + if (mSaidGreeting == Greet_None) + { + // TODO: check if actor is aware / has line of sight + if (playerDistSqr <= helloDistance*helloDistance) + { + mSaidGreeting = Greet_InProgress; + MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); + } + } + + if(mSaidGreeting == Greet_InProgress) { if(mWalking) { @@ -449,29 +459,22 @@ namespace MWMechanics mRotate = true; } } + + mSaidGreeting = Greet_Done; } - - if (!mSaidGreeting) - { - // TODO: check if actor is aware / has line of sight - if (playerDistSqr <= helloDistance*helloDistance) - { - mSaidGreeting = true; - MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); - } - } - else + + if (mSaidGreeting == MWMechanics::AiWander::Greet_Done) { static float fGreetDistanceReset = MWBase::Environment::get().getWorld()->getStore() .get().find("fGreetDistanceReset")->getFloat(); if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset) - mSaidGreeting = false; + mSaidGreeting = Greet_None; } // Check if idle animation finished // FIXME: don't stay forever - if(!checkIdle(actor, mPlayedIdle) && playerDistSqr > helloDistance*helloDistance) + if(!checkIdle(actor, mPlayedIdle) && (playerDistSqr > helloDistance*helloDistance || mSaidGreeting == MWMechanics::AiWander::Greet_Done)) { mPlayedIdle = 0; mIdleNow = false; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 7abd19e27..fdfd6f42e 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -62,7 +62,12 @@ namespace MWMechanics std::vector mIdle; bool mRepeat; - bool mSaidGreeting; + enum GreetingState { + Greet_None, + Greet_InProgress, + Greet_Done + }; + GreetingState mSaidGreeting; bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, // if we had the actor in the AiWander constructor... From bd3729a6cbbf2067b6c1f46a37c749ddc8f18daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aciubid=C5=82o?= Date: Mon, 28 Jul 2014 23:41:37 +0100 Subject: [PATCH 127/707] Add grace periods for player greeting. Add delay for NPC greating. NPC ignores greeted player after some time. Fixes bug 1503. --- apps/openmw/mwmechanics/aiwander.cpp | 23 ++++++++++++++++++----- apps/openmw/mwmechanics/aiwander.hpp | 1 + 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 519cc8a61..27dad88cd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -22,6 +22,9 @@ namespace MWMechanics { static const int COUNT_BEFORE_RESET = 200; // TODO: maybe no longer needed static const float DOOR_CHECK_INTERVAL = 1.5f; + static const float REACTION_INTERVAL = 0.25f; + static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player + static const int GREETING_SHOULD_END = 10; AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) @@ -44,6 +47,7 @@ namespace MWMechanics mRotate = false; mTargetAngle = 0; mSaidGreeting = Greet_None; + greetingTimer = 0; mHasReturnPosition = false; mReturnPosition = Ogre::Vector3(0,0,0); @@ -221,14 +225,14 @@ namespace MWMechanics } mReaction += duration; - if(mReaction < 0.25f) // FIXME: hard coded constant + if(mReaction < REACTION_INTERVAL) { return false; } else mReaction = 0; - // NOTE: everything below get updated every 0.25 seconds + // NOTE: everything below get updated every REACTION_INTERVAL seconds MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) @@ -424,16 +428,22 @@ namespace MWMechanics if (mSaidGreeting == Greet_None) { - // TODO: check if actor is aware / has line of sight if (playerDistSqr <= helloDistance*helloDistance) + greetingTimer++; + + // TODO: check if actor is aware / has line of sight + if (greetingTimer >= GREETING_SHOULD_START) { mSaidGreeting = Greet_InProgress; MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); + greetingTimer = 0; } } if(mSaidGreeting == Greet_InProgress) { + greetingTimer++; + if(mWalking) { stopWalking(actor); @@ -460,7 +470,11 @@ namespace MWMechanics } } - mSaidGreeting = Greet_Done; + if (greetingTimer >= GREETING_SHOULD_END) + { + mSaidGreeting = Greet_Done; + greetingTimer = 0; + } } if (mSaidGreeting == MWMechanics::AiWander::Greet_Done) @@ -473,7 +487,6 @@ namespace MWMechanics } // Check if idle animation finished - // FIXME: don't stay forever if(!checkIdle(actor, mPlayedIdle) && (playerDistSqr > helloDistance*helloDistance || mSaidGreeting == MWMechanics::AiWander::Greet_Done)) { mPlayedIdle = 0; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index fdfd6f42e..9b579b24a 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -68,6 +68,7 @@ namespace MWMechanics Greet_Done }; GreetingState mSaidGreeting; + int greetingTimer; bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, // if we had the actor in the AiWander constructor... From 598c0c4ae7137718a65afed676fa715203329aec Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 00:26:26 +0200 Subject: [PATCH 128/707] Implement mouth animation for NPCs based on say sound (Fixes #642) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/soundmanager.hpp | 5 ++ apps/openmw/mwrender/npcanimation.cpp | 47 ++++++++++++-- apps/openmw/mwrender/npcanimation.hpp | 11 +++- apps/openmw/mwsound/loudness.cpp | 53 +++++++++++++++ apps/openmw/mwsound/loudness.hpp | 20 ++++++ apps/openmw/mwsound/openal_output.cpp | 86 +++++++++++++++---------- apps/openmw/mwsound/openal_output.hpp | 4 +- apps/openmw/mwsound/sound.cpp | 23 +++++++ apps/openmw/mwsound/sound.hpp | 10 +++ apps/openmw/mwsound/sound_output.hpp | 2 +- apps/openmw/mwsound/soundmanagerimp.cpp | 17 ++++- apps/openmw/mwsound/soundmanagerimp.hpp | 5 ++ 13 files changed, 241 insertions(+), 44 deletions(-) create mode 100644 apps/openmw/mwsound/loudness.cpp create mode 100644 apps/openmw/mwsound/loudness.hpp create mode 100644 apps/openmw/mwsound/sound.cpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 0dda1131b..25cf48b09 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -48,7 +48,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwsound - soundmanagerimp openal_output ffmpeg_decoder sound + soundmanagerimp openal_output ffmpeg_decoder sound sound_decoder sound_output loudness ) add_openmw_dir (mwworld diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 15739730b..a02a463dd 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -101,6 +101,11 @@ namespace MWBase virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()) = 0; ///< Stop an actor speaking + virtual float getSaySoundLoudness(const MWWorld::Ptr& reference) const = 0; + ///< Check the currently playing say sound for this actor + /// and get an average loudness value (scale [0,1]) at the current time position. + /// If the actor is not saying anything, returns 0. + virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; ///< Play a 2D audio track, using a custom decoder diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index be2b262fc..597d0c2df 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -66,15 +66,40 @@ std::string getVampireHead(const std::string& race, bool female) namespace MWRender { +HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference) + : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0) +{ +} + float HeadAnimationTime::getValue() const { - // TODO use time from text keys (Talk Start/Stop, Blink Start/Stop) // TODO: Handle eye blinking if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) - return 0; + return mBlinkStop; else - // TODO: Use the loudness of the currently playing sound - return 1; + return mTalkStart + + (mTalkStop - mTalkStart) * + std::min(1.f, MWBase::Environment::get().getSoundManager()->getSaySoundLoudness(mReference)*4); // Rescale a bit (most voices are not very loud) +} + +void HeadAnimationTime::setTalkStart(float value) +{ + mTalkStart = value; +} + +void HeadAnimationTime::setTalkStop(float value) +{ + mTalkStop = value; +} + +void HeadAnimationTime::setBlinkStart(float value) +{ + mBlinkStart = value; +} + +void HeadAnimationTime::setBlinkStop(float value) +{ + mBlinkStop = value; } static NpcAnimation::PartBoneMap createPartListMap() @@ -620,7 +645,21 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g ctrl->setSource(mNullAnimationTimePtr); if (type == ESM::PRT_Head) + { ctrl->setSource(mHeadAnimationTime); + const NifOgre::TextKeyMap& keys = mObjectParts[type]->mTextKeys; + for (NifOgre::TextKeyMap::const_iterator it = keys.begin(); it != keys.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->second, "talk: start")) + mHeadAnimationTime->setTalkStart(it->first); + if (Misc::StringUtils::ciEqual(it->second, "talk: stop")) + mHeadAnimationTime->setTalkStop(it->first); + if (Misc::StringUtils::ciEqual(it->second, "blink: start")) + mHeadAnimationTime->setBlinkStart(it->first); + if (Misc::StringUtils::ciEqual(it->second, "blink: stop")) + mHeadAnimationTime->setBlinkStop(it->first); + } + } else if (type == ESM::PRT_Weapon) ctrl->setSource(mWeaponAnimationTime); } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 8ec46facd..057f67e2f 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -19,8 +19,17 @@ class HeadAnimationTime : public Ogre::ControllerValue { private: MWWorld::Ptr mReference; + float mTalkStart; + float mTalkStop; + float mBlinkStart; + float mBlinkStop; public: - HeadAnimationTime(MWWorld::Ptr reference) : mReference(reference) {} + HeadAnimationTime(MWWorld::Ptr reference); + + void setTalkStart(float value); + void setTalkStop(float value); + void setBlinkStart(float value); + void setBlinkStop(float value); virtual Ogre::Real getValue() const; virtual void setValue(Ogre::Real value) diff --git a/apps/openmw/mwsound/loudness.cpp b/apps/openmw/mwsound/loudness.cpp new file mode 100644 index 000000000..88c706a91 --- /dev/null +++ b/apps/openmw/mwsound/loudness.cpp @@ -0,0 +1,53 @@ +#include "loudness.hpp" + +#include "soundmanagerimp.hpp" + +namespace MWSound +{ + + void analyzeLoudness(const std::vector &data, int sampleRate, ChannelConfig chans, + SampleType type, std::vector &out, float valuesPerSecond) + { + int samplesPerSegment = sampleRate / valuesPerSecond; + int numSamples = bytesToFrames(data.size(), chans, type); + int advance = framesToBytes(1, chans, type); + + out.reserve(numSamples/samplesPerSegment); + + int segment=0; + int sample=0; + while (segment < numSamples/samplesPerSegment) + { + float sum=0; + int samplesAdded = 0; + while (sample < numSamples && sample < (segment+1)*samplesPerSegment) + { + // get sample on a scale from -1 to 1 + float value = 0; + if (type == SampleType_UInt8) + value = data[sample*advance]/128.f; + else if (type == SampleType_Int16) + { + value = *reinterpret_cast(&data[sample*advance]); + value /= float(std::numeric_limits().max()); + } + else if (type == SampleType_Float32) + { + value = *reinterpret_cast(&data[sample*advance]); + value /= std::numeric_limits().max(); + } + + sum += value*value; + ++samplesAdded; + ++sample; + } + + float rms = 0; // root mean square + if (samplesAdded > 0) + rms = std::sqrt(sum / samplesAdded); + out.push_back(rms); + ++segment; + } + } + +} diff --git a/apps/openmw/mwsound/loudness.hpp b/apps/openmw/mwsound/loudness.hpp new file mode 100644 index 000000000..df727bd0b --- /dev/null +++ b/apps/openmw/mwsound/loudness.hpp @@ -0,0 +1,20 @@ +#include "sound_decoder.hpp" + +namespace MWSound +{ + +/** + * Analyzes the energy (closely related to loudness) of a sound buffer. + * The buffer will be divided into segments according to \a valuesPerSecond, + * and for each segment a loudness value in the range of [0,1] will be computed. + * @param data the sound buffer to analyze, containing raw samples + * @param sampleRate the sample rate of the sound buffer + * @param chans channel layout of the buffer + * @param type sample type of the buffer + * @param out Will contain the output loudness values. + * @param valuesPerSecond How many loudness values per second of audio to compute. + */ +void analyzeLoudness (const std::vector& data, int sampleRate, ChannelConfig chans, SampleType type, + std::vector& out, float valuesPerSecond); + +} diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index b245b9241..b20bd4f7e 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -11,11 +11,16 @@ #include "sound_decoder.hpp" #include "sound.hpp" #include "soundmanagerimp.hpp" +#include "loudness.hpp" #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 #endif +namespace +{ + const int loudnessFPS = 20; // loudness values per second of audio +} namespace MWSound { @@ -750,7 +755,7 @@ void OpenAL_Output::deinit() } -ALuint OpenAL_Output::getBuffer(const std::string &fname) +ALuint OpenAL_Output::getBuffer(const std::string &fname, std::vector* loudnessBuffer) { ALuint buf = 0; @@ -765,11 +770,12 @@ ALuint OpenAL_Output::getBuffer(const std::string &fname) if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } - - return buf; } throwALerror(); + if (buf != 0 && loudnessBuffer == NULL) + return buf; + std::vector data; ChannelConfig chans; SampleType type; @@ -795,42 +801,50 @@ ALuint OpenAL_Output::getBuffer(const std::string &fname) decoder->readAll(data); decoder->close(); - alGenBuffers(1, &buf); - throwALerror(); + if (loudnessBuffer != NULL) + { + analyzeLoudness(data, srate, chans, type, *loudnessBuffer, loudnessFPS); + } + + if (buf == 0) + { + alGenBuffers(1, &buf); + throwALerror(); - alBufferData(buf, format, &data[0], data.size(), srate); - mBufferCache[fname] = buf; - mBufferRefs[buf] = 1; + alBufferData(buf, format, &data[0], data.size(), srate); + mBufferCache[fname] = buf; + mBufferRefs[buf] = 1; - ALint bufsize = 0; - alGetBufferi(buf, AL_SIZE, &bufsize); - mBufferCacheMemSize += bufsize; + ALint bufsize = 0; + alGetBufferi(buf, AL_SIZE, &bufsize); + mBufferCacheMemSize += bufsize; - // NOTE: Max buffer cache: 15MB - while(mBufferCacheMemSize > 15*1024*1024) - { - if(mUnusedBuffers.empty()) + // NOTE: Max buffer cache: 15MB + while(mBufferCacheMemSize > 15*1024*1024) { - std::cout <<"No more unused buffers to clear!"<< std::endl; - break; - } + if(mUnusedBuffers.empty()) + { + std::cout <<"No more unused buffers to clear!"<< std::endl; + break; + } - ALuint oldbuf = mUnusedBuffers.front(); - mUnusedBuffers.pop_front(); + ALuint oldbuf = mUnusedBuffers.front(); + mUnusedBuffers.pop_front(); - NameMap::iterator nameiter = mBufferCache.begin(); - while(nameiter != mBufferCache.end()) - { - if(nameiter->second == oldbuf) - mBufferCache.erase(nameiter++); - else - ++nameiter; - } + NameMap::iterator nameiter = mBufferCache.begin(); + while(nameiter != mBufferCache.end()) + { + if(nameiter->second == oldbuf) + mBufferCache.erase(nameiter++); + else + ++nameiter; + } - bufsize = 0; - alGetBufferi(oldbuf, AL_SIZE, &bufsize); - alDeleteBuffers(1, &oldbuf); - mBufferCacheMemSize -= bufsize; + bufsize = 0; + alGetBufferi(oldbuf, AL_SIZE, &bufsize); + alDeleteBuffers(1, &oldbuf); + mBufferCacheMemSize -= bufsize; + } } return buf; } @@ -883,7 +897,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, f } MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float vol, float basevol, float pitch, - float min, float max, int flags, float offset) + float min, float max, int flags, float offset, bool extractLoudness) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -895,8 +909,12 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre try { - buf = getBuffer(fname); + std::vector loudnessVector; + + buf = getBuffer(fname, extractLoudness ? &loudnessVector : NULL); + sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags)); + sound->setLoudnessVector(loudnessVector, loudnessFPS); } catch(std::exception&) { diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 31edf7359..f96c588cf 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -36,7 +36,7 @@ namespace MWSound typedef std::vector SoundVec; SoundVec mActiveSounds; - ALuint getBuffer(const std::string &fname); + ALuint getBuffer(const std::string &fname, std::vector* loudnessBuffer=NULL); void bufferFinished(ALuint buffer); Environment mLastEnvironment; @@ -49,7 +49,7 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset); /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float vol, float basevol, float pitch, float min, float max, int flags, float offset); + float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false); virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound.cpp b/apps/openmw/mwsound/sound.cpp new file mode 100644 index 000000000..b3105a82c --- /dev/null +++ b/apps/openmw/mwsound/sound.cpp @@ -0,0 +1,23 @@ +#include "sound.hpp" + +namespace MWSound +{ + + float Sound::getCurrentLoudness() + { + if (mLoudnessVector.empty()) + return 0.f; + int index = getTimeOffset() * mLoudnessFPS; + + index = std::max(0, std::min(index, int(mLoudnessVector.size()-1))); + + return mLoudnessVector[index]; + } + + void Sound::setLoudnessVector(const std::vector &loudnessVector, float loudnessFPS) + { + mLoudnessVector = loudnessVector; + mLoudnessFPS = loudnessFPS; + } + +} diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 670002a30..1b5c00196 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -24,6 +24,9 @@ namespace MWSound int mFlags; float mFadeOutTime; + std::vector mLoudnessVector; + float mLoudnessFPS; + public: virtual void stop() = 0; virtual bool isPlaying() = 0; @@ -31,6 +34,12 @@ namespace MWSound void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } void setFadeout(float duration) { mFadeOutTime=duration; } + void setLoudnessVector(const std::vector& loudnessVector, float loudnessFPS); + + /// Get loudness at the current time position on a [0,1] scale. + /// Requires that loudnessVector was filled in by the user. + float getCurrentLoudness(); + MWBase::SoundManager::PlayType getPlayType() const { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); } @@ -44,6 +53,7 @@ namespace MWSound , mMaxDistance(maxdist) , mFlags(flags) , mFadeOutTime(0) + , mLoudnessFPS(20) { } virtual ~Sound() { } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 91e25db52..a9a999a5c 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -28,7 +28,7 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset) = 0; /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float vol, float basevol, float pitch, float min, float max, int flags, float offset) = 0; + float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false) = 0; virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index ba7b4f3ba..fced81f7b 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -256,7 +256,7 @@ namespace MWSound const Ogre::Vector3 objpos(pos.pos); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - 20.0f, 1500.0f, Play_Normal|Play_TypeVoice, 0); + 20.0f, 1500.0f, Play_Normal|Play_TypeVoice, 0, true); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) @@ -265,6 +265,21 @@ namespace MWSound } } + float SoundManager::getSaySoundLoudness(const MWWorld::Ptr &ptr) const + { + SoundMap::const_iterator snditer = mActiveSounds.begin(); + while(snditer != mActiveSounds.end()) + { + if(snditer->second.first == ptr && snditer->second.second == "_say_sound") + break; + ++snditer; + } + if (snditer == mActiveSounds.end()) + return 0.f; + + return snditer->first->getCurrentLoudness(); + } + void SoundManager::say(const std::string& filename) { if(!mOutput->isInitialized()) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 380cfe255..250cb0d51 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -110,6 +110,11 @@ namespace MWSound virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()); ///< Stop an actor speaking + virtual float getSaySoundLoudness(const MWWorld::Ptr& reference) const; + ///< Check the currently playing say sound for this actor + /// and get an average loudness value (scale [0,1]) at the current time position. + /// If the actor is not saying anything, returns 0. + virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder From 625f9a35e607459180860201e34c29ae1bd0b5a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 01:16:08 +0200 Subject: [PATCH 129/707] Implement NPC eye blinking (Fixes #1721) --- apps/openmw/mwrender/npcanimation.cpp | 38 +++++++++++++++++++++++---- apps/openmw/mwrender/npcanimation.hpp | 8 ++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 597d0c2df..e2a01f723 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -67,19 +67,45 @@ namespace MWRender { HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference) - : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0) + : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mValue(0) { + resetBlinkTimer(); } -float HeadAnimationTime::getValue() const +void HeadAnimationTime::resetBlinkTimer() +{ + mBlinkTimer = -(2 + (std::rand() / double(RAND_MAX*1.0)) * 6); +} + +void HeadAnimationTime::update(float dt) { - // TODO: Handle eye blinking if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) - return mBlinkStop; + { + mBlinkTimer += dt; + + float duration = mBlinkStop - mBlinkStart; + + if (mBlinkTimer >= 0 && mBlinkTimer <= duration) + { + mValue = mBlinkStart + mBlinkTimer; + } + else + mValue = mBlinkStop; + + if (mBlinkTimer > duration) + resetBlinkTimer(); + } else - return mTalkStart + + { + mValue = mTalkStart + (mTalkStop - mTalkStart) * std::min(1.f, MWBase::Environment::get().getSoundManager()->getSaySoundLoudness(mReference)*4); // Rescale a bit (most voices are not very loud) + } +} + +float HeadAnimationTime::getValue() const +{ + return mValue; } void HeadAnimationTime::setTalkStart(float value) @@ -541,6 +567,8 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) { Ogre::Vector3 ret = Animation::runAnimation(timepassed); + mHeadAnimationTime->update(timepassed); + Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); if(mViewMode == VM_FirstPerson) { diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 057f67e2f..95cd35ddb 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -23,9 +23,17 @@ private: float mTalkStop; float mBlinkStart; float mBlinkStop; + + float mBlinkTimer; + + float mValue; +private: + void resetBlinkTimer(); public: HeadAnimationTime(MWWorld::Ptr reference); + void update(float dt); + void setTalkStart(float value); void setTalkStop(float value); void setBlinkStart(float value); From 0943ff08863aca78c0ac22ce68b2bf5468dc2782 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 14:19:12 +0200 Subject: [PATCH 130/707] Fix normalizing sample values --- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwsound/loudness.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index e2a01f723..0a9b56b33 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -99,7 +99,7 @@ void HeadAnimationTime::update(float dt) { mValue = mTalkStart + (mTalkStop - mTalkStart) * - std::min(1.f, MWBase::Environment::get().getSoundManager()->getSaySoundLoudness(mReference)*4); // Rescale a bit (most voices are not very loud) + std::min(1.f, MWBase::Environment::get().getSoundManager()->getSaySoundLoudness(mReference)*2); // Rescale a bit (most voices are not very loud) } } diff --git a/apps/openmw/mwsound/loudness.cpp b/apps/openmw/mwsound/loudness.cpp index 88c706a91..ebe3bf1ec 100644 --- a/apps/openmw/mwsound/loudness.cpp +++ b/apps/openmw/mwsound/loudness.cpp @@ -25,16 +25,16 @@ namespace MWSound // get sample on a scale from -1 to 1 float value = 0; if (type == SampleType_UInt8) - value = data[sample*advance]/128.f; + value = ((char)(data[sample*advance]^0x80))/128.f; else if (type == SampleType_Int16) { value = *reinterpret_cast(&data[sample*advance]); - value /= float(std::numeric_limits().max()); + value /= float(std::numeric_limits().max()); } else if (type == SampleType_Float32) { value = *reinterpret_cast(&data[sample*advance]); - value /= std::numeric_limits().max(); + value = std::max(-1.f, std::min(1.f, value)); // Float samples *should* be scaled to [-1,1] already. } sum += value*value; From a59620f643ee54d428e25237d427cc4116179065 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 14:32:44 +0200 Subject: [PATCH 131/707] Cache loudness vector in the buffer cache --- apps/openmw/mwsound/openal_output.cpp | 92 +++++++++++++-------------- apps/openmw/mwsound/openal_output.hpp | 10 ++- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index b20bd4f7e..3d2795ce1 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -739,7 +739,7 @@ void OpenAL_Output::deinit() mUnusedBuffers.clear(); while(!mBufferCache.empty()) { - alDeleteBuffers(1, &mBufferCache.begin()->second); + alDeleteBuffers(1, &mBufferCache.begin()->second.mALBuffer); mBufferCache.erase(mBufferCache.begin()); } @@ -755,14 +755,14 @@ void OpenAL_Output::deinit() } -ALuint OpenAL_Output::getBuffer(const std::string &fname, std::vector* loudnessBuffer) +const CachedSound& OpenAL_Output::getBuffer(const std::string &fname) { ALuint buf = 0; NameMap::iterator iditer = mBufferCache.find(fname); if(iditer != mBufferCache.end()) { - buf = iditer->second; + buf = iditer->second.mALBuffer; if(mBufferRefs[buf]++ == 0) { IDDq::iterator iter = std::find(mUnusedBuffers.begin(), @@ -770,12 +770,11 @@ ALuint OpenAL_Output::getBuffer(const std::string &fname, std::vector* lo if(iter != mUnusedBuffers.end()) mUnusedBuffers.erase(iter); } + + return iditer->second; } throwALerror(); - if (buf != 0 && loudnessBuffer == NULL) - return buf; - std::vector data; ChannelConfig chans; SampleType type; @@ -801,52 +800,49 @@ ALuint OpenAL_Output::getBuffer(const std::string &fname, std::vector* lo decoder->readAll(data); decoder->close(); - if (loudnessBuffer != NULL) - { - analyzeLoudness(data, srate, chans, type, *loudnessBuffer, loudnessFPS); - } + CachedSound cached; + analyzeLoudness(data, srate, chans, type, cached.mLoudnessVector, loudnessFPS); - if (buf == 0) - { - alGenBuffers(1, &buf); - throwALerror(); + alGenBuffers(1, &buf); + throwALerror(); - alBufferData(buf, format, &data[0], data.size(), srate); - mBufferCache[fname] = buf; - mBufferRefs[buf] = 1; + alBufferData(buf, format, &data[0], data.size(), srate); + mBufferRefs[buf] = 1; + cached.mALBuffer = buf; + mBufferCache[fname] = cached; - ALint bufsize = 0; - alGetBufferi(buf, AL_SIZE, &bufsize); - mBufferCacheMemSize += bufsize; + ALint bufsize = 0; + alGetBufferi(buf, AL_SIZE, &bufsize); + mBufferCacheMemSize += bufsize; - // NOTE: Max buffer cache: 15MB - while(mBufferCacheMemSize > 15*1024*1024) + // NOTE: Max buffer cache: 15MB + while(mBufferCacheMemSize > 15*1024*1024) + { + if(mUnusedBuffers.empty()) { - if(mUnusedBuffers.empty()) - { - std::cout <<"No more unused buffers to clear!"<< std::endl; - break; - } - - ALuint oldbuf = mUnusedBuffers.front(); - mUnusedBuffers.pop_front(); + std::cout <<"No more unused buffers to clear!"<< std::endl; + break; + } - NameMap::iterator nameiter = mBufferCache.begin(); - while(nameiter != mBufferCache.end()) - { - if(nameiter->second == oldbuf) - mBufferCache.erase(nameiter++); - else - ++nameiter; - } + ALuint oldbuf = mUnusedBuffers.front(); + mUnusedBuffers.pop_front(); - bufsize = 0; - alGetBufferi(oldbuf, AL_SIZE, &bufsize); - alDeleteBuffers(1, &oldbuf); - mBufferCacheMemSize -= bufsize; + NameMap::iterator nameiter = mBufferCache.begin(); + while(nameiter != mBufferCache.end()) + { + if(nameiter->second.mALBuffer == oldbuf) + mBufferCache.erase(nameiter++); + else + ++nameiter; } + + bufsize = 0; + alGetBufferi(oldbuf, AL_SIZE, &bufsize); + alDeleteBuffers(1, &oldbuf); + mBufferCacheMemSize -= bufsize; } - return buf; + + return mBufferCache[fname]; } void OpenAL_Output::bufferFinished(ALuint buf) @@ -870,7 +866,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, f try { - buf = getBuffer(fname); + buf = getBuffer(fname).mALBuffer; sound.reset(new OpenAL_Sound(*this, src, buf, Ogre::Vector3(0.0f), vol, basevol, pitch, 1.0f, 1000.0f, flags)); } catch(std::exception&) @@ -909,12 +905,12 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre try { - std::vector loudnessVector; - - buf = getBuffer(fname, extractLoudness ? &loudnessVector : NULL); + const CachedSound& cached = getBuffer(fname); + buf = cached.mALBuffer; sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags)); - sound->setLoudnessVector(loudnessVector, loudnessFPS); + if (extractLoudness) + sound->setLoudnessVector(cached.mLoudnessVector, loudnessFPS); } catch(std::exception&) { diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index f96c588cf..be12bfbec 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -16,6 +16,12 @@ namespace MWSound class SoundManager; class Sound; + struct CachedSound + { + ALuint mALBuffer; + std::vector mLoudnessVector; + }; + class OpenAL_Output : public Sound_Output { ALCdevice *mDevice; @@ -25,7 +31,7 @@ namespace MWSound IDDq mFreeSources; IDDq mUnusedBuffers; - typedef std::map NameMap; + typedef std::map NameMap; NameMap mBufferCache; typedef std::map IDRefMap; @@ -36,7 +42,7 @@ namespace MWSound typedef std::vector SoundVec; SoundVec mActiveSounds; - ALuint getBuffer(const std::string &fname, std::vector* loudnessBuffer=NULL); + const CachedSound& getBuffer(const std::string &fname); void bufferFinished(ALuint buffer); Environment mLastEnvironment; From 1a6db097edd224b5a4a8b70b9de46e52f34f7d96 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 15:32:22 +0200 Subject: [PATCH 132/707] Fix dangling MWWorld::Ptrs in enchanting dialog after loading save game (Fixes #1722) --- apps/openmw/mwgui/enchantingdialog.cpp | 23 +++++++++++++++++------ apps/openmw/mwgui/enchantingdialog.hpp | 2 ++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index d7e79e7a2..e89777bee 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -58,9 +58,6 @@ namespace MWGui void EnchantingDialog::open() { center(); - - setSoulGem(MWWorld::Ptr()); - setItem(MWWorld::Ptr()); } void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem) @@ -78,7 +75,6 @@ namespace MWGui mSoulBox->setUserData(gem); mEnchanting.setSoulGem(gem); } - updateLabels(); } void EnchantingDialog::setItem(const MWWorld::Ptr &item) @@ -96,7 +92,6 @@ namespace MWGui mItemBox->setUserData(item); mEnchanting.setOldItem(item); } - updateLabels(); } void EnchantingDialog::exit() @@ -148,6 +143,9 @@ namespace MWGui mPtr = actor; + setSoulGem(MWWorld::Ptr()); + setItem(MWWorld::Ptr()); + startEditing (); mPrice->setVisible(true); mPriceText->setVisible(true); @@ -167,6 +165,7 @@ namespace MWGui startEditing(); setSoulGem(soulgem); + setItem(MWWorld::Ptr()); mPrice->setVisible(false); mPriceText->setVisible(false); @@ -177,6 +176,16 @@ namespace MWGui { MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); + resetReference(); + } + + void EnchantingDialog::resetReference() + { + ReferenceInterface::resetReference(); + setItem(MWWorld::Ptr()); + setSoulGem(MWWorld::Ptr()); + mPtr = MWWorld::Ptr(); + mEnchanting.setEnchanter(MWWorld::Ptr()); } void EnchantingDialog::onCancelButtonClicked(MyGUI::Widget* sender) @@ -219,8 +228,8 @@ namespace MWGui void EnchantingDialog::onSoulSelected(MWWorld::Ptr item) { mItemSelectionDialog->setVisible(false); - mEnchanting.setSoulGem(item); + mEnchanting.setSoulGem(item); if(mEnchanting.getGemCharge()==0) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage32}"); @@ -228,6 +237,7 @@ namespace MWGui } setSoulGem(item); + updateLabels(); } void EnchantingDialog::onSoulCancel() @@ -252,6 +262,7 @@ namespace MWGui else { setSoulGem(MWWorld::Ptr()); + updateLabels(); } } diff --git a/apps/openmw/mwgui/enchantingdialog.hpp b/apps/openmw/mwgui/enchantingdialog.hpp index b75ae8280..5b67d199b 100644 --- a/apps/openmw/mwgui/enchantingdialog.hpp +++ b/apps/openmw/mwgui/enchantingdialog.hpp @@ -29,6 +29,8 @@ namespace MWGui void startEnchanting(MWWorld::Ptr actor); void startSelfEnchanting(MWWorld::Ptr soulgem); + virtual void resetReference(); + protected: virtual void onReferenceUnavailable(); virtual void notifyEffectsChanged (); From 9e48d56244e84cdcc9c0177c8bd226ba4396a929 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 15:55:58 +0200 Subject: [PATCH 133/707] Reset RefNum when copying an object (Fixes #1723) --- apps/openmw/mwworld/cellref.cpp | 6 ++++++ apps/openmw/mwworld/cellref.hpp | 3 +++ apps/openmw/mwworld/containerstore.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 1 + 4 files changed, 11 insertions(+) diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index cdf08e6ed..c0b3bb6af 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -10,6 +10,12 @@ namespace MWWorld return mCellRef.mRefNum; } + void CellRef::unsetRefNum() + { + mCellRef.mRefNum.mContentFile = -1; + mCellRef.mRefNum.mIndex = 0; + } + std::string CellRef::getRefId() const { return mCellRef.mRefID; diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 577655739..d2e4fdf1c 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -25,6 +25,9 @@ namespace MWWorld // Note: Currently unused for items in containers ESM::RefNum getRefNum() const; + // Set RefNum to its default state. + void unsetRefNum(); + // Id of object being referenced std::string getRefId() const; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 18ebd82db..3188c1cd5 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -255,6 +255,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr pos.pos[1] = 0; pos.pos[2] = 0; item.getCellRef().setPosition(pos); + item.getCellRef().unsetRefNum(); // destroy link to content file std::string script = item.getClass().getScript(item); if(script != "") diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ebbc16c23..d1207dd8b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1718,6 +1718,7 @@ namespace MWWorld localRotation.rot[2] = 0; dropped.getRefData().setLocalRotation(localRotation); dropped.getCellRef().setPosition(pos); + dropped.getCellRef().unsetRefNum(); if (mWorldScene->isCellActive(*cell)) { if (dropped.getRefData().isEnabled()) { From ccde462308e6ad956c9f217123873923459250b2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 19:01:14 +0200 Subject: [PATCH 134/707] Fix typo (RefId -> RefNum) --- apps/esmtool/record.cpp | 2 +- components/esm/loadcell.cpp | 10 +++++----- components/esm/loadcell.hpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index f8bc2af61..caf67e619 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -513,7 +513,7 @@ void Record::print() else std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl; std::cout << " Water Level Int: " << mData.mWaterInt << std::endl; - std::cout << " RefId counter: " << mData.mRefIdCounter << std::endl; + std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 0a25fce84..91da936a4 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -58,7 +58,7 @@ void Cell::load(ESMReader &esm, bool saveContext) void Cell::loadCell(ESMReader &esm, bool saveContext) { - mRefIdCounter = 0; + mRefNumCounter = 0; if (mData.mFlags & Interior) { @@ -92,7 +92,7 @@ void Cell::loadCell(ESMReader &esm, bool saveContext) esm.getHNOT(mMapColor, "NAM5"); } if (esm.isNextSub("NAM0")) { - esm.getHT(mRefIdCounter); + esm.getHT(mRefNumCounter); } if (saveContext) { @@ -150,8 +150,8 @@ void Cell::save(ESMWriter &esm) const esm.writeHNT("NAM5", mMapColor); } - if (mRefIdCounter != 0) - esm.writeHNT("NAM0", mRefIdCounter); + if (mRefNumCounter != 0) + esm.writeHNT("NAM0", mRefNumCounter); } void Cell::restore(ESMReader &esm, int iCtx) const @@ -220,7 +220,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mWater = 0; mWaterInt = false; mMapColor = 0; - mRefIdCounter = 0; + mRefNumCounter = 0; mData.mFlags = 0; mData.mX = 0; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 5f889a55b..f40b3db61 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -94,10 +94,10 @@ struct Cell float mWater; // Water level bool mWaterInt; int mMapColor; - // Counter for RefIds. This is only used during content file editing and has no impact on gameplay. - // It prevents overwriting previous refIDs, even if they were deleted. + // Counter for RefNums. This is only used during content file editing and has no impact on gameplay. + // It prevents overwriting previous refNums, even if they were deleted. // as that would collide with refs when a content file is upgraded. - int mRefIdCounter; + int mRefNumCounter; // References "leased" from another cell (i.e. a different cell // introduced this ref, and it has been moved here by a plugin) From 543bb22e8f4b8a0c31c1920b610c7a0206916f74 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 29 Jul 2014 19:01:40 +0200 Subject: [PATCH 135/707] Implement collision script instructions (Fixes #1111) --- apps/openmw/mwbase/world.hpp | 9 +++ apps/openmw/mwscript/docs/vmformat.txt | 10 +++- apps/openmw/mwscript/miscextensions.cpp | 62 ++++++++++++++++++++ apps/openmw/mwworld/physicssystem.cpp | 76 ++++++++++++++++++++++++- apps/openmw/mwworld/physicssystem.hpp | 22 +++++++ apps/openmw/mwworld/worldimp.cpp | 60 ++++++++++++++++--- apps/openmw/mwworld/worldimp.hpp | 9 +++ components/compiler/extensions0.cpp | 4 ++ components/compiler/opcodes.hpp | 8 +++ libs/openengine/bullet/physic.cpp | 16 ------ libs/openengine/bullet/physic.hpp | 2 - libs/openengine/bullet/trace.cpp | 2 + libs/openengine/bullet/trace.h | 1 + 13 files changed, 252 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 29f326a1b..62da2682d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -404,6 +404,15 @@ namespace MWBase virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object + virtual bool getPlayerCollidingWith(const MWWorld::Ptr& object) = 0; ///< @return true if the player is colliding with \a object + virtual bool getActorCollidingWith (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is colliding with \a object + virtual void hurtStandingActors (const MWWorld::Ptr& object, float dmgPerSecond) = 0; + ///< Apply a health difference to any actors standing on \a object. + /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. + virtual void hurtCollidingActors (const MWWorld::Ptr& object, float dmgPerSecond) = 0; + ///< Apply a health difference to any actors colliding with \a object. + /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. + virtual float getWindSpeed() = 0; virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 8c9d6e480..4b501b20f 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -405,5 +405,13 @@ op 0x200024c: Face op 0x200024d: Face, explicit op 0x200024e: GetStat (dummy function) op 0x200024f: GetStat (dummy function), explicit +op 0x2000250: GetCollidingPC +op 0x2000251: GetCollidingPC, explicit +op 0x2000252: GetCollidingActor +op 0x2000253: GetCollidingActor, explicit +op 0x2000254: HurtStandingActor +op 0x2000255: HurtStandingActor, explicit +op 0x2000256: HurtCollidingActor +op 0x2000257: HurtCollidingActor, explicit -opcodes 0x2000250-0x3ffffff unused +opcodes 0x2000258-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 4e0257d82..36848d44c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -611,6 +611,60 @@ namespace MWScript } }; + template + class OpGetCollidingPc : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (MWBase::Environment::get().getWorld()->getPlayerStandingOn(ptr)); + } + }; + + template + class OpGetCollidingActor : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push (MWBase::Environment::get().getWorld()->getActorStandingOn(ptr)); + } + }; + + template + class OpHurtStandingActor : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + float healthDiffPerSecond = runtime[0].mFloat; + runtime.pop(); + + MWBase::Environment::get().getWorld()->hurtStandingActors(ptr, healthDiffPerSecond); + } + }; + + template + class OpHurtCollidingActor : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + float healthDiffPerSecond = runtime[0].mFloat; + runtime.pop(); + + MWBase::Environment::get().getWorld()->hurtCollidingActors(ptr, healthDiffPerSecond); + } + }; + class OpGetWindSpeed : public Interpreter::Opcode0 { public: @@ -967,6 +1021,14 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPcExplicit, new OpGetStandingPc); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActor, new OpGetStandingActor); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActorExplicit, new OpGetStandingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPc, new OpGetCollidingPc); + interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPcExplicit, new OpGetCollidingPc); + interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActor, new OpGetCollidingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActorExplicit, new OpGetCollidingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActor, new OpHurtStandingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActorExplicit, new OpHurtStandingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActor, new OpHurtCollidingActor); + interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActorExplicit, new OpHurtCollidingActor); interpreter.installSegment5 (Compiler::Misc::opcodeGetWindSpeed, new OpGetWindSpeed); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMe, new OpHitOnMe); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMeExplicit, new OpHitOnMe); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index daad5b0e6..02a198f3d 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -241,7 +241,9 @@ namespace MWWorld } static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, - bool isFlying, float waterlevel, float slowFall, OEngine::Physic::PhysicEngine *engine) + bool isFlying, float waterlevel, float slowFall, OEngine::Physic::PhysicEngine *engine + , std::map& collisionTracker + , std::map& standingCollisionTracker) { const ESM::Position &refpos = ptr.getRefData().getPosition(); Ogre::Vector3 position(refpos.pos); @@ -318,6 +320,11 @@ namespace MWWorld tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine); // check if down 2 possible if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) { + const btCollisionObject* standingOn = tracer.mHitObject; + if (const OEngine::Physic::RigidBody* body = dynamic_cast(standingOn)) + { + standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName; + } isOnGround = true; // if we're on the ground, don't try to fall any more velocity.z = std::max(0.0f, velocity.z); @@ -376,6 +383,14 @@ namespace MWWorld remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it? break; } + else + { + const btCollisionObject* standingOn = tracer.mHitObject; + if (const OEngine::Physic::RigidBody* body = dynamic_cast(standingOn)) + { + collisionTracker[ptr.getRefData().getHandle()] = body->mName; + } + } } else { @@ -771,6 +786,10 @@ namespace MWWorld const PtrVelocityList& PhysicsSystem::applyQueuedMovement(float dt) { + // Collision events are only tracked for a single frame, so reset first + mCollisions.clear(); + mStandingCollisions.clear(); + mMovementResults.clear(); mTimeAccum += dt; @@ -810,7 +829,7 @@ namespace MWWorld Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), - waterlevel, slowFall, mEngine); + waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions); if (waterCollision) mEngine->mDynamicsWorld->removeCollisionObject(&object); @@ -837,4 +856,57 @@ namespace MWWorld mEngine->stepSimulation(dt); } + + bool PhysicsSystem::isActorStandingOn(const Ptr &actor, const Ptr &object) const + { + const std::string& actorHandle = actor.getRefData().getHandle(); + const std::string& objectHandle = object.getRefData().getHandle(); + + for (std::map::const_iterator it = mStandingCollisions.begin(); + it != mStandingCollisions.end(); ++it) + { + if (it->first == actorHandle && it->second == objectHandle) + return true; + } + return false; + } + + void PhysicsSystem::getActorsStandingOn(const Ptr &object, std::vector &out) const + { + const std::string& objectHandle = object.getRefData().getHandle(); + + for (std::map::const_iterator it = mStandingCollisions.begin(); + it != mStandingCollisions.end(); ++it) + { + if (it->second == objectHandle) + out.push_back(it->first); + } + } + + bool PhysicsSystem::isActorCollidingWith(const Ptr &actor, const Ptr &object) const + { + const std::string& actorHandle = actor.getRefData().getHandle(); + const std::string& objectHandle = object.getRefData().getHandle(); + + for (std::map::const_iterator it = mCollisions.begin(); + it != mCollisions.end(); ++it) + { + if (it->first == actorHandle && it->second == objectHandle) + return true; + } + return false; + } + + void PhysicsSystem::getActorsCollidingWith(const Ptr &object, std::vector &out) const + { + const std::string& objectHandle = object.getRefData().getHandle(); + + for (std::map::const_iterator it = mCollisions.begin(); + it != mCollisions.end(); ++it) + { + if (it->second == objectHandle) + out.push_back(it->first); + } + } + } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 8e0be95d5..ac60a80b5 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -87,12 +87,34 @@ namespace MWWorld const PtrVelocityList& applyQueuedMovement(float dt); + /// Return true if \a actor has been standing on \a object in this frame + /// This will trigger whenever the object is directly below the actor. + /// It doesn't matter if the actor is stationary or moving. + bool isActorStandingOn(const MWWorld::Ptr& actor, const MWWorld::Ptr& object) const; + + /// Get the handle of all actors standing on \a object in this frame. + void getActorsStandingOn(const MWWorld::Ptr& object, std::vector& out) const; + + /// Return true if \a actor has collided with \a object in this frame. + /// This will detect running into objects, but will not detect climbing stairs, stepping up a small object, etc. + bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::Ptr& object) const; + + /// Get the handle of all actors colliding with \a object in this frame. + void getActorsCollidingWith(const MWWorld::Ptr& object, std::vector& out) const; + private: OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; std::map handleToMesh; + // Tracks all movement collisions happening during a single frame. + // This will detect e.g. running against a vertical wall. It will not detect climbing up stairs, + // stepping up small objects, etc. + std::map mCollisions; + + std::map mStandingCollisions; + PtrVelocityList mMovementQueue; PtrVelocityList mMovementResults; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d1207dd8b..1c9faa4a7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1995,18 +1995,62 @@ namespace MWWorld bool World::getPlayerStandingOn (const MWWorld::Ptr& object) { - MWWorld::Ptr player = mPlayer->getPlayer(); - if (!mPhysEngine->getCharacter("player")->getOnGround()) - return false; - btVector3 from (player.getRefData().getPosition().pos[0], player.getRefData().getPosition().pos[1], player.getRefData().getPosition().pos[2]); - btVector3 to = from - btVector3(0,0,5); - std::pair result = mPhysEngine->rayTest(from, to); - return result.first == object.getRefData().getBaseNode()->getName(); + MWWorld::Ptr player = getPlayerPtr(); + return mPhysics->isActorStandingOn(player, object); } bool World::getActorStandingOn (const MWWorld::Ptr& object) { - return mPhysEngine->isAnyActorStandingOn(object.getRefData().getBaseNode()->getName()); + std::vector actors; + mPhysics->getActorsStandingOn(object, actors); + return !actors.empty(); + } + + bool World::getPlayerCollidingWith (const MWWorld::Ptr& object) + { + MWWorld::Ptr player = getPlayerPtr(); + return mPhysics->isActorCollidingWith(player, object); + } + + bool World::getActorCollidingWith (const MWWorld::Ptr& object) + { + std::vector actors; + mPhysics->getActorsCollidingWith(object, actors); + return !actors.empty(); + } + + void World::hurtStandingActors(const Ptr &object, float healthPerSecond) + { + std::vector actors; + mPhysics->getActorsStandingOn(object, actors); + for (std::vector::iterator it = actors.begin(); it != actors.end(); ++it) + { + MWWorld::Ptr actor = searchPtrViaHandle(*it); // Collision events are from the last frame, actor might no longer exist + if (actor.isEmpty()) + continue; + + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + MWMechanics::DynamicStat health = stats.getHealth(); + health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration()); + stats.setHealth(health); + } + } + + void World::hurtCollidingActors(const Ptr &object, float healthPerSecond) + { + std::vector actors; + mPhysics->getActorsCollidingWith(object, actors); + for (std::vector::iterator it = actors.begin(); it != actors.end(); ++it) + { + MWWorld::Ptr actor = searchPtrViaHandle(*it); // Collision events are from the last frame, actor might no longer exist + if (actor.isEmpty()) + continue; + + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + MWMechanics::DynamicStat health = stats.getHealth(); + health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration()); + stats.setHealth(health); + } } float World::getWindSpeed() diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5c44388ca..c4c8b588e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -479,6 +479,15 @@ namespace MWWorld virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object + virtual bool getPlayerCollidingWith(const MWWorld::Ptr& object); ///< @return true if the player is colliding with \a object + virtual bool getActorCollidingWith (const MWWorld::Ptr& object); ///< @return true if any actor is colliding with \a object + virtual void hurtStandingActors (const MWWorld::Ptr& object, float dmgPerSecond); + ///< Apply a health difference to any actors standing on \a object. + /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. + virtual void hurtCollidingActors (const MWWorld::Ptr& object, float dmgPerSecond); + ///< Apply a health difference to any actors colliding with \a object. + /// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed. + virtual float getWindSpeed(); virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index ef4fe4fbd..40246d1dc 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -272,6 +272,10 @@ namespace Compiler extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit); extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit); extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit); + extensions.registerFunction ("getcollidingpc", 'l', "", opcodeGetCollidingPc, opcodeGetCollidingPcExplicit); + extensions.registerFunction ("getcollidingactor", 'l', "", opcodeGetCollidingActor, opcodeGetCollidingActorExplicit); + extensions.registerInstruction ("hurtstandingactor", "f", opcodeHurtStandingActor, opcodeHurtStandingActorExplicit); + extensions.registerInstruction ("hurtcollidingactor", "f", opcodeHurtCollidingActor, opcodeHurtCollidingActorExplicit); extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit); extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 9f3ed3574..828d736c4 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -238,6 +238,14 @@ namespace Compiler const int opcodeGetStandingPcExplicit = 0x200020d; const int opcodeGetStandingActor = 0x200020e; const int opcodeGetStandingActorExplicit = 0x200020f; + const int opcodeGetCollidingPc = 0x2000250; + const int opcodeGetCollidingPcExplicit = 0x2000251; + const int opcodeGetCollidingActor = 0x2000252; + const int opcodeGetCollidingActorExplicit = 0x2000253; + const int opcodeHurtStandingActor = 0x2000254; + const int opcodeHurtStandingActorExplicit = 0x2000255; + const int opcodeHurtCollidingActor = 0x2000256; + const int opcodeHurtCollidingActorExplicit = 0x2000257; const int opcodeGetWindSpeed = 0x2000212; const int opcodePlayBink = 0x20001f7; const int opcodeGoToJail = 0x2000235; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 954d7c283..c95552cb7 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -846,21 +846,5 @@ namespace Physic } } - bool PhysicEngine::isAnyActorStandingOn (const std::string& objectName) - { - for (PhysicActorContainer::iterator it = mActorMap.begin(); it != mActorMap.end(); ++it) - { - if (!it->second->getOnGround()) - continue; - Ogre::Vector3 pos = it->second->getPosition(); - btVector3 from (pos.x, pos.y, pos.z); - btVector3 to = from - btVector3(0,0,5); - std::pair result = rayTest(from, to); - if (result.first == objectName) - return true; - } - return false; - } - } } diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 09bff4b04..590b56c01 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -296,8 +296,6 @@ namespace Physic void setSceneManager(Ogre::SceneManager* sceneMgr); - bool isAnyActorStandingOn (const std::string& objectName); - /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). * If \a normal is non-NULL, the hit normal will be written there (if there is a hit) diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index ad140f1f7..de5fecfca 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -81,12 +81,14 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, mFraction = newTraceCallback.m_closestHitFraction; mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); mEndPos = (end-start)*mFraction + start; + mHitObject = newTraceCallback.m_hitCollisionObject; } else { mEndPos = end; mPlaneNormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); mFraction = 1.0f; + mHitObject = NULL; } } diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index b9fbce64d..f499f4a27 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -18,6 +18,7 @@ namespace Physic { Ogre::Vector3 mEndPos; Ogre::Vector3 mPlaneNormal; + const btCollisionObject* mHitObject; float mFraction; From f754e06be955499596be03c2c5817670d805431a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=9Aciubid=C5=82o?= Date: Wed, 30 Jul 2014 07:47:09 +0100 Subject: [PATCH 136/707] Fix for bug 1685 --- apps/openmw/mwmechanics/aiwander.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 27dad88cd..3b093d6f6 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -428,10 +428,10 @@ namespace MWMechanics if (mSaidGreeting == Greet_None) { - if (playerDistSqr <= helloDistance*helloDistance) + if ((playerDistSqr <= helloDistance*helloDistance) && MWBase::Environment::get().getWorld()->getLOS(player, actor) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor)) greetingTimer++; - // TODO: check if actor is aware / has line of sight if (greetingTimer >= GREETING_SHOULD_START) { mSaidGreeting = Greet_InProgress; From f25e0b13b24a643de6233d38805666fed4d9d6bf Mon Sep 17 00:00:00 2001 From: crysthala Date: Wed, 30 Jul 2014 07:55:21 -0500 Subject: [PATCH 137/707] render window controls icons eyeball dude: first person flying eye: free cam orbit2: orbiting cam --- files/opencs/eyeballdude.png | Bin 0 -> 7350 bytes files/opencs/flying eye.png | Bin 0 -> 8123 bytes files/opencs/orbit2.png | Bin 0 -> 8016 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 files/opencs/eyeballdude.png create mode 100644 files/opencs/flying eye.png create mode 100644 files/opencs/orbit2.png diff --git a/files/opencs/eyeballdude.png b/files/opencs/eyeballdude.png new file mode 100644 index 0000000000000000000000000000000000000000..c782880f321d907c75b0a60df8fef7eda7bee33a GIT binary patch literal 7350 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 literal 0 HcmV?d00001 diff --git a/files/opencs/flying eye.png b/files/opencs/flying eye.png new file mode 100644 index 0000000000000000000000000000000000000000..e379d7fad047497b3041321ab90fc1d1fe467fc4 GIT binary patch literal 8123 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 SIr3Kk0000 Date: Wed, 30 Jul 2014 02:32:05 +0200 Subject: [PATCH 138/707] Show sMagicInvalidTarget for an invalid soultrap target (Fixes #1728) --- apps/openmw/mwmechanics/spellcasting.cpp | 55 +++++++++++++++++------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 94b479007..4d05e5446 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -230,6 +230,44 @@ namespace MWMechanics return -(resistance-100) / 100.f; } + /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure. + bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, bool castByPlayer) + { + switch (effectId) + { + case ESM::MagicEffect::Levitate: + if (!MWBase::Environment::get().getWorld()->isLevitationEnabled()) + { + if (castByPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); + return false; + } + break; + case ESM::MagicEffect::Soultrap: + if ((target.getClass().isActor() && target.getClass().isNpc()) + || (target.getTypeName() == typeid(ESM::Creature).name() && target.get()->mBase->mData.mSoul == 0)) + { + if (castByPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidTarget}"); + return false; + } + break; + case ESM::MagicEffect::AlmsiviIntervention: + case ESM::MagicEffect::DivineIntervention: + case ESM::MagicEffect::Mark: + case ESM::MagicEffect::Recall: + if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) + { + if (castByPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); + return false; + } + break; + } + + return true; + } + CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target) : mCaster(caster) , mTarget(target) @@ -318,23 +356,8 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); - if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate) - { - if (castByPlayer) - MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); + if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer)) continue; - } - - if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled() && - (effectIt->mEffectID == ESM::MagicEffect::AlmsiviIntervention || - effectIt->mEffectID == ESM::MagicEffect::DivineIntervention || - effectIt->mEffectID == ESM::MagicEffect::Mark || - effectIt->mEffectID == ESM::MagicEffect::Recall)) - { - if (castByPlayer) - MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); - continue; - } // If player is healing someone, show the target's HP bar if (castByPlayer && target != caster From d1feb9ef0261b6ee6f4a0d4ca3c1d5a6cd6d3c36 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 30 Jul 2014 03:39:43 +0200 Subject: [PATCH 139/707] Fix # in book text being interpreted as MyGUI color code --- apps/openmw/mwgui/formatting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 4d3d04ced..1dded94fe 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -406,7 +406,7 @@ namespace MWGui box->setTextAlign(mTextStyle.mTextAlign); box->setTextColour(mTextStyle.mColour); box->setFontName(mTextStyle.mFont); - box->setCaption(realText); + box->setCaption(MyGUI::TextIterator::toTagsString(realText)); box->setSize(box->getSize().width, box->getTextSize().height); mHeight += box->getTextSize().height; From 261e755e73e7534d3b3dd59a3f291c4b992d36f2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 30 Jul 2014 04:43:47 +0200 Subject: [PATCH 140/707] Font hacking again (Fixes #1506) --- apps/openmw/mwgui/fontloader.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp index 92d9a25b6..13f4e41f2 100644 --- a/apps/openmw/mwgui/fontloader.cpp +++ b/apps/openmw/mwgui/fontloader.cpp @@ -262,11 +262,32 @@ namespace MWGui // in the cp437 encoding of the font. Fall back to similar available characters. if (mEncoding == ToUTF8::CP437) { - std::multimap additional; + std::multimap additional; // additional.insert(std::make_pair(39, 0x2019)); // apostrophe additional.insert(std::make_pair(45, 0x2013)); // dash + additional.insert(std::make_pair(45, 0x2014)); // dash additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark + additional.insert(std::make_pair(44, 0x201A)); + additional.insert(std::make_pair(44, 0x201E)); + additional.insert(std::make_pair(43, 0x2020)); + additional.insert(std::make_pair(94, 0x02C6)); + additional.insert(std::make_pair(37, 0x2030)); + additional.insert(std::make_pair(83, 0x0160)); + additional.insert(std::make_pair(60, 0x2039)); + additional.insert(std::make_pair(79, 0x0152)); + additional.insert(std::make_pair(90, 0x017D)); + additional.insert(std::make_pair(39, 0x2019)); + additional.insert(std::make_pair(126, 0x02DC)); + additional.insert(std::make_pair(84, 0x2122)); + additional.insert(std::make_pair(83, 0x0161)); + additional.insert(std::make_pair(62, 0x203A)); + additional.insert(std::make_pair(111, 0x0153)); + additional.insert(std::make_pair(122, 0x017E)); + additional.insert(std::make_pair(89, 0x0178)); + additional.insert(std::make_pair(156, 0x00A2)); + additional.insert(std::make_pair(46, 0x2026)); + for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it) { if (it->first != i) From 7a5f73de9e03fa3799a5f46d46ea4215bc70502b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 30 Jul 2014 17:02:23 +0200 Subject: [PATCH 141/707] added navigation mode icons to buttons --- apps/opencs/view/render/worldspacewidget.cpp | 6 +++--- files/opencs/resources.qrc | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index d3413a29d..57ed06212 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -61,7 +61,7 @@ CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( /// \todo replace icons /// \todo consider user-defined button-mapping - tool->addButton (":door.png", "1st", + tool->addButton (":scenetoolbar/1st-person", "1st", "First Person" "
  • Mouse-Look while holding the left button
  • " "
  • WASD movement keys
  • " @@ -70,7 +70,7 @@ CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( "
  • Camera is held upright
  • " "
  • Hold shift to speed up movement
  • " "
"); - tool->addButton (":GMST.png", "free", + tool->addButton (":scenetoolbar/free-camera", "free", "Free Camera" "
  • Mouse-Look while holding the left button
  • " "
  • Stafing (also vertically) via WASD or by holding the left mouse button and control
  • " @@ -78,7 +78,7 @@ CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( "
  • Roll camera with Q and E keys
  • " "
  • Hold shift to speed up movement
  • " "
"); - tool->addButton (":Info.png", "orbit", + tool->addButton (":scenetoolbar/orbiting-camera", "orbit", "Orbiting Camera" "