From 76bf774485d7da4285a9af3b9605b558465af50e Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 6 Jun 2014 03:52:41 +1000 Subject: [PATCH 001/226] Small changes for compiling with MSVC 2013. --- extern/oics/ICSPrerequisites.h | 4 ++++ extern/sdl4ogre/sdlinputwrapper.hpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h index 52daea3f4..5fe58a63d 100644 --- a/extern/oics/ICSPrerequisites.h +++ b/extern/oics/ICSPrerequisites.h @@ -37,6 +37,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#if defined(_WIN32) && _MSC_VER >= 1800 +#include /* std::min and std::max */ +#endif + #include "tinyxml.h" #include "SDL_keyboard.h" diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp index f08e3eff6..e4c97066d 100644 --- a/extern/sdl4ogre/sdlinputwrapper.hpp +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -1,6 +1,11 @@ #ifndef SDL4OGRE_SDLINPUTWRAPPER_H #define SDL4OGRE_SDLINPUTWRAPPER_H +#if defined(_WIN32) && _MSC_VER >= 1800 +#include +#define NOMINMAX +#endif + #include #include From fd758bacd3d2773906416b3dd12f09563d1054d2 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 6 Jun 2014 19:58:05 +1000 Subject: [PATCH 002/226] Remove #ifdef guards. --- extern/oics/ICSPrerequisites.h | 7 ++----- extern/sdl4ogre/sdlinputwrapper.hpp | 3 --- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h index 5fe58a63d..6e6cd814b 100644 --- a/extern/oics/ICSPrerequisites.h +++ b/extern/oics/ICSPrerequisites.h @@ -36,10 +36,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include - -#if defined(_WIN32) && _MSC_VER >= 1800 -#include /* std::min and std::max */ -#endif +#include /* std::min and std::max for MSVC 2013 */ #include "tinyxml.h" @@ -94,7 +91,7 @@ namespace ICS // from http://www.cplusplus.com/forum/articles/9645/ template - T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a + T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a { //character array as argument std::stringstream ss(Text); T result; diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp index e4c97066d..275701814 100644 --- a/extern/sdl4ogre/sdlinputwrapper.hpp +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -1,10 +1,7 @@ #ifndef SDL4OGRE_SDLINPUTWRAPPER_H #define SDL4OGRE_SDLINPUTWRAPPER_H -#if defined(_WIN32) && _MSC_VER >= 1800 -#include #define NOMINMAX -#endif #include From 927ae00454d9e91db30040764df639938b486709 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 6 Jun 2014 21:15:23 +0200 Subject: [PATCH 003/226] Fix code that stopped animation immediately after starting it, due to thinking it has completed (Fixes #1370) --- 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 db4e59929..67d506e43 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -832,6 +832,8 @@ bool CharacterController::updateWeaponState() MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 1.0f-complete, 0); + + complete = 0.f; mUpperBodyState = UpperCharState_MaxAttackToMinHit; } else if (mHitState == CharState_KnockDown) From 2ec324c80bb834ea543786725fffe60e2110034a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Jun 2014 17:10:02 +0200 Subject: [PATCH 004/226] Consider all splash screens in the Splash folder (Fixes #1416) --- apps/openmw/mwgui/loadingscreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index f1bbc68cd..f6d6b8135 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -136,7 +136,7 @@ namespace MWGui Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) { - Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "Splash_*.tga"); + Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "Splash/*.tga"); mResources.insert(mResources.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); } } From b470596206c21ece3fd77e0159d15fc4d68197e5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Jun 2014 17:48:40 +0200 Subject: [PATCH 005/226] Handle failed savegame file operations (Fixes #1413) --- apps/openmw/mwstate/statemanagerimp.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 0e56365d6..7a70944dc 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -233,6 +233,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.close(); + if (stream.fail()) + throw std::runtime_error("Write operation failed"); + Settings::Manager::setString ("character", "Saves", slot->mPath.parent_path().filename().string()); } @@ -246,6 +249,10 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::vector buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->messageBox(error.str(), buttons); + + // If no file was written, clean up the slot + if (slot && !boost::filesystem::exists(slot->mPath)) + mCharacterManager.getCurrentCharacter()->deleteSlot(slot); } } From a0bff03560b4413500161c5d45ba38bf41d25341 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Jun 2014 17:58:03 +0200 Subject: [PATCH 006/226] Fix not handling failbit/badbit in ifstream (Bug #1355) --- components/translation/translation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 423c3971a..976ae926d 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -32,6 +32,9 @@ namespace Translation boost::filesystem::ifstream stream ( dataFileCollections.getCollection (extension).getPath (fileName)); + // Configure the stream to throw exception upon error + stream.exceptions ( boost::filesystem::ifstream::failbit | boost::filesystem::ifstream::badbit ); + if (!stream.is_open()) throw std::runtime_error ("failed to open translation file: " + fileName); @@ -41,6 +44,7 @@ namespace Translation void Storage::loadDataFromStream(ContainerType& container, std::istream& stream) { + // NOTE: does not handle failbit/badbit. stream must be set up beforehand to throw in these cases. std::string line; while (!stream.eof()) { From 69855097cac627c679587f983d97f11634c37502 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Jun 2014 18:00:39 +0200 Subject: [PATCH 007/226] Fix an always true condition (Bug #1355) --- apps/openmw/mwmechanics/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 67d506e43..2d4bd8744 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -977,7 +977,8 @@ bool CharacterController::updateWeaponState() } //if playing combat animation and lowerbody is not busy switch to whole body animation - if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) + if((weaptype != WeapType_None || mUpperBodyState == UpperCharState_UnEquipingWeap + || mUpperBodyState == UpperCharState_EquipingWeap) && animPlaying) { if( mMovementState != CharState_None || mJumpState != JumpState_None || From 823ccb1b3d7bcdbc3b864e937c667cfe456a6e7c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Jun 2014 18:48:12 +0200 Subject: [PATCH 008/226] Don't batch statics that have "references persist" set (temporary fix for Arkngthand door - Fixes #1386) --- apps/openmw/mwclass/static.cpp | 5 ++++- apps/openmw/mwrender/objects.cpp | 4 ++-- apps/openmw/mwrender/objects.hpp | 2 +- components/esm/loadstat.cpp | 1 + components/esm/loadstat.hpp | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 4ac41350f..8768bde06 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -14,9 +14,12 @@ namespace MWClass { void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { + MWWorld::LiveCellRef *ref = + ptr.get(); + const std::string model = getModel(ptr); if (!model.empty()) { - renderingInterface.getObjects().insertModel(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model, !ref->mBase->mPersistent); } } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 7953a3117..d9e20e1f8 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -73,7 +73,7 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr) ptr.getRefData().setBaseNode(insert); } -void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) +void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch) { insertBegin(ptr); @@ -99,7 +99,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; mBounds[ptr.getCell()].merge(bounds); - if(ptr.getTypeName() == typeid(ESM::Static).name() && + if(batch && Settings::Manager::getBool("use static geometry", "Objects") && anim->canBatch()) { diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 665a1e657..02e974e2d 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -41,7 +41,7 @@ public: , mRootNode(NULL) {} ~Objects(){} - void insertModel(const MWWorld::Ptr& ptr, const std::string &model); + void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false); ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr); diff --git a/components/esm/loadstat.cpp b/components/esm/loadstat.cpp index a71f22dc2..53d1b4bb5 100644 --- a/components/esm/loadstat.cpp +++ b/components/esm/loadstat.cpp @@ -10,6 +10,7 @@ namespace ESM void Static::load(ESMReader &esm) { + mPersistent = esm.getRecordFlags() & 0x0400; mModel = esm.getHNString("MODL"); } void Static::save(ESMWriter &esm) const diff --git a/components/esm/loadstat.hpp b/components/esm/loadstat.hpp index d912d1058..45b05136a 100644 --- a/components/esm/loadstat.hpp +++ b/components/esm/loadstat.hpp @@ -26,6 +26,8 @@ struct Static std::string mId, mModel; + bool mPersistent; + void load(ESMReader &esm); void save(ESMWriter &esm) const; From b9dadff5a378ae818f80c1a6250d48c9368bfe29 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Jun 2014 19:21:37 +0200 Subject: [PATCH 009/226] Recognize DELE subrecords at the end of the record (Fixes #1414) --- apps/openmw/mwworld/esmstore.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index cd6cc4a16..12831e7dc 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -99,6 +99,13 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) } it->second->load(esm, id); + // DELE can also occur after the usual subrecords + if (esm.isNextSub("DELE")) { + esm.skipRecord(); + it->second->eraseStatic(id); + continue; + } + if (n.val==ESM::REC_DIAL) { dialogue = const_cast(mDialogs.find(id)); } else { From 98d7b6672a894f345ab2a13de4a6138ae769d649 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Jun 2014 01:42:43 +0200 Subject: [PATCH 010/226] Make MODL subrecord optional for potions (Fixes #1419) --- components/esm/loadalch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index f6bfc6a11..aac88482f 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -10,7 +10,7 @@ namespace ESM void Potion::load(ESMReader &esm) { - mModel = esm.getHNString("MODL"); + mModel = esm.getHNOString("MODL"); mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason mScript = esm.getHNOString("SCRI"); mName = esm.getHNOString("FNAM"); From d2dca270678502169036fd44ce6d4c436c500509 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Jun 2014 11:25:10 +0200 Subject: [PATCH 011/226] Correct wrong assertions (Fixes #1425) --- apps/openmw/mwgui/quickkeysmenu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index e14217177..eda9daeff 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -162,7 +162,7 @@ namespace MWGui void QuickKeysMenu::onAssignItem(MWWorld::Ptr item) { - assert (mSelectedIndex > 0); + assert (mSelectedIndex >= 0); ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; while (button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); @@ -184,7 +184,7 @@ namespace MWGui void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) { - assert (mSelectedIndex > 0); + assert (mSelectedIndex >= 0); ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; while (button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); @@ -203,7 +203,7 @@ namespace MWGui void QuickKeysMenu::onAssignMagic (const std::string& spellId) { - assert (mSelectedIndex > 0); + assert (mSelectedIndex >= 0); ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; while (button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); From 67abc60264a6aec4d5b9080150bb22e76cde02f5 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 8 Jun 2014 20:59:26 +0400 Subject: [PATCH 012/226] aiming to moving target in ranged combat ai 1) Taking into account target move vector and speed. However aiming is not ideal, since attack strength can't be controlled directly. I did achieve almost 100% accuracy updating it everyframe but then thought it would be unfair, cause AI should mimic human targetting. 2) Also added in this commit func to measure real attack durations for weapon. --- apps/openmw/mwmechanics/aicombat.cpp | 346 +++++++++++++++++------ apps/openmw/mwmechanics/aicombat.hpp | 9 +- apps/openmw/mwrender/animation.cpp | 19 +- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/weaponanimation.cpp | 2 +- 5 files changed, 278 insertions(+), 100 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8f8188559..7a846e00d 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -1,8 +1,6 @@ #include "aicombat.hpp" #include -#include - #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" @@ -14,6 +12,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwrender/animation.hpp" + #include "creaturestats.hpp" #include "steering.hpp" @@ -30,7 +30,12 @@ namespace } //chooses an attack depending on probability to avoid uniformity - void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); + ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); + + void getMinMaxAttackDuration(const MWWorld::Ptr& actor, float (*fMinMaxDurations)[2]); + + Ogre::Vector3 AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const Ogre::Vector3& vLastTargetPos, + float duration, int weapType, float strength); float getZAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f) { @@ -76,18 +81,20 @@ namespace namespace MWMechanics { - static const float MAX_ATTACK_DURATION = 0.35f; static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp AiCombat::AiCombat(const MWWorld::Ptr& actor) : mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()), + mLastTargetPos(actor.getRefData().getPosition().pos), mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), mFollowTarget(false), mReadyToAttack(false), mAttack(false), + mStrength(0), + mMinMaxAttackDuration(), mCombatMove(false), mMovement(), mForceNoShortcut(false), @@ -158,9 +165,9 @@ namespace MWMechanics return true; if (!actor.getClass().isNpc() && target == MWBase::Environment::get().getWorld()->getPlayerPtr() && - (actor.getClass().canSwim(actor) && !actor.getClass().canWalk(actor) // pure water creature - && !MWBase::Environment::get().getWorld()->isSwimming(target)) // Player moved out of water - || (!actor.getClass().canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(target))) // creature can't swim to Player + (actor.getClass().canSwim(actor) && !actor.getClass().canWalk(actor) // 1. pure water creature and Player moved out of water + && !MWBase::Environment::get().getWorld()->isSwimming(target)) + || (!actor.getClass().canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(target))) // 2. creature can't swim to Player { actor.getClass().getCreatureStats(actor).setHostile(false); actor.getClass().getCreatureStats(actor).setAttackingOrSpell(false); @@ -194,6 +201,27 @@ namespace MWMechanics } mTimerAttack -= duration; + + //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f + float attacksPeriod = 1.0f; + + ESM::Weapon::AttackType attackType; + + if(mReadyToAttack) + { + if (mMinMaxAttackDuration[0][0] == 0) + { + getMinMaxAttackDuration(actor, mMinMaxAttackDuration); + } + + if (mTimerAttack <= 0) mAttack = false; + } + else + { + mTimerAttack = -attacksPeriod; + mAttack = false; + } + actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mAttack); float tReaction = 0.25f; @@ -213,40 +241,6 @@ namespace MWMechanics mCell = actor.getCell(); } - //actual attacking logic - //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f - float attacksPeriod = 1.0f; - if(mReadyToAttack) - { - if(mTimerAttack <= -attacksPeriod) - { - //TODO: should depend on time between 'start' to 'min attack' - //for better controlling of NPCs' attack strength. - //Also it seems that this time is different for slash/thrust/chop - mTimerAttack = MAX_ATTACK_DURATION * static_cast(rand())/RAND_MAX; - mAttack = true; - - //say a provoking combat phrase - if (actor.getClass().isNpc()) - { - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int chance = store.get().find("iVoiceAttackOdds")->getInt(); - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - if (roll < chance) - { - MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); - } - } - } - else if (mTimerAttack <= 0) - mAttack = false; - } - else - { - mTimerAttack = -attacksPeriod; - mAttack = false; - } - const MWWorld::Class &actorCls = actor.getClass(); const ESM::Weapon *weapon = NULL; MWMechanics::WeaponType weaptype; @@ -270,9 +264,9 @@ namespace MWMechanics if (weaptype == WeapType_HandToHand) { - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - weapRange = gmst.find("fHandToHandReach")->getFloat(); + static float fHandToHandReach = + MWBase::Environment::get().getWorld()->getStore().get().find("fHandToHandReach")->getFloat(); + weapRange = fHandToHandReach; } else if (weaptype != WeapType_PickProbe && weaptype != WeapType_Spell) { @@ -289,6 +283,49 @@ namespace MWMechanics weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) } + float rangeAttack; + float rangeFollow; + bool distantCombat = false; + if (weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow || weaptype == WeapType_Thrown) + { + rangeAttack = 1000; // TODO: should depend on archer skill + rangeFollow = 0; // not needed in ranged combat + distantCombat = true; + } + else + { + rangeAttack = weapRange; + rangeFollow = 300; + } + + // start new attack + if(mReadyToAttack) + { + if(mTimerAttack <= -attacksPeriod) + { + mAttack = true; // attack starts just now + + if (!distantCombat) attackType = chooseBestAttack(weapon, mMovement); + else attackType = ESM::Weapon::AT_Chop; // cause it's =0 + + mStrength = static_cast(rand()) / RAND_MAX; + mTimerAttack = mMinMaxAttackDuration[attackType][0] + + (mMinMaxAttackDuration[attackType][1] - mMinMaxAttackDuration[attackType][0]) * mStrength; + + //say a provoking combat phrase + if (actor.getClass().isNpc()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceAttackOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + { + MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); + } + } + } + } + /* * Some notes on meanings of variables: @@ -319,21 +356,6 @@ namespace MWMechanics * target even if LOS is not achieved) */ - float rangeAttack; - float rangeFollow; - bool distantCombat = false; - if (weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow || weaptype == WeapType_Thrown) - { - rangeAttack = 1000; // TODO: should depend on archer skill - rangeFollow = 0; // not needed in ranged combat - distantCombat = true; - } - else - { - rangeAttack = weapRange; - rangeFollow = 300; - } - ESM::Position pos = actor.getRefData().getPosition(); Ogre::Vector3 vActorPos(pos.pos); Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos); @@ -342,40 +364,46 @@ namespace MWMechanics bool isStuck = false; float speed = 0.0f; - if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = actorCls.getSpeed(actor)) * tReaction / 2) + if(mMovement.mPosition[1] && (mLastActorPos - vActorPos).length() < (speed = actorCls.getSpeed(actor)) * tReaction / 2) isStuck = true; - mLastPos = pos; + mLastActorPos = vActorPos; // check if actor can move along z-axis bool canMoveByZ = (actorCls.canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(actor)) || MWBase::Environment::get().getWorld()->isFlying(actor); - // determine vertical angle to target - // if actor can move along z-axis it will control movement dir - // if can't - it will control correct aiming - mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget); - - // (within strike dist) || (not quite strike dist while following) + // (within attack dist) || (not quite attack dist while following) if(distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck) ) { //Melee and Close-up combat - // if we preserve dir.z then horizontal angle can be inaccurate - mMovement.mRotation[2] = getZAngleToDir(Ogre::Vector3(vDirToTarget.x, vDirToTarget.y, 0)); + // getXAngleToDir determines vertical angle to target: + // if actor can move along z-axis it will control movement dir + // if can't - it will control correct aiming. + // note: in getZAngleToDir if we preserve dir.z then horizontal angle can be inaccurate + if (distantCombat) + { + Ogre::Vector3 vAimDir = AimDirToMovingTarget(actor, target, mLastTargetPos, tReaction, weaptype, mStrength); + mLastTargetPos = vTargetPos; + mMovement.mRotation[0] = getXAngleToDir(vAimDir); + mMovement.mRotation[2] = getZAngleToDir(Ogre::Vector3(vAimDir.x, vAimDir.y, 0)); + } + else + { + mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget); + mMovement.mRotation[2] = getZAngleToDir(Ogre::Vector3(vDirToTarget.x, vDirToTarget.y, 0)); + } - // (not quite strike dist while following) + // (not quite attack dist while following) if (mFollowTarget && distToTarget > rangeAttack) { //Close-up combat: just run up on target mMovement.mPosition[1] = 1; } - else // (within strike dist) + else // (within attack dist) { - mMovement.mPosition[1] = 0; - - // set slash/thrust/chop attack - if (mAttack && !distantCombat) chooseBestAttack(weapon, mMovement); + if (!mAttack) mMovement.mPosition[1] = 0; if(mMovement.mPosition[0] || mMovement.mPosition[1]) { @@ -479,9 +507,9 @@ namespace MWMechanics //special run attack; it shouldn't affect melee combat tactics if(actorCls.getMovementSettings(actor).mPosition[1] == 1) { - //check if actor can overcome the distance = distToTarget - attackerWeapRange - //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) - //then start attacking + /* check if actor can overcome the distance = distToTarget - attackerWeapRange + less than in time of swinging with weapon (t_swing), then start attacking + */ float speed1 = actorCls.getSpeed(actor); float speed2 = target.getClass().getSpeed(target); if(target.getClass().getMovementSettings(target).mPosition[0] == 0 @@ -491,13 +519,16 @@ namespace MWMechanics float s1 = distToTarget - weapRange; float t = s1/speed1; float s2 = speed2 * t; - float t_swing = (MAX_ATTACK_DURATION/2) / weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags + float t_swing = + mMinMaxAttackDuration[ESM::Weapon::AT_Thrust][0] + + (mMinMaxAttackDuration[ESM::Weapon::AT_Thrust][1] - mMinMaxAttackDuration[ESM::Weapon::AT_Thrust][0]) * static_cast(rand()) / RAND_MAX; + if (t + s2/speed1 <= t_swing) { mReadyToAttack = true; if(mTimerAttack <= -attacksPeriod) { - mTimerAttack = MAX_ATTACK_DURATION * static_cast(rand())/RAND_MAX; + mTimerAttack = t_swing; mAttack = true; } } @@ -650,8 +681,10 @@ namespace MWMechanics namespace { -void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) +ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement) { + ESM::Weapon::AttackType attackType; + if (weapon == NULL) { //hand-to-hand deal equal damage for each type @@ -660,34 +693,161 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement { movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; movement.mPosition[1] = 0; + attackType = ESM::Weapon::AT_Slash; } else if(roll <= 0.666f) //forward punch + { movement.mPosition[1] = 1; + attackType = ESM::Weapon::AT_Thrust; + } else { movement.mPosition[1] = movement.mPosition[0] = 0; + attackType = ESM::Weapon::AT_Chop; } + } + else + { + //the more damage attackType deals the more probability it has + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + + float total = slash + chop + thrust; + + float roll = static_cast(rand())/RAND_MAX; + if(roll <= static_cast(slash)/total) + { + movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; + movement.mPosition[1] = 0; + attackType = ESM::Weapon::AT_Slash; + } + else if(roll <= (static_cast(slash) + static_cast(thrust))/total) + { + movement.mPosition[1] = 1; + attackType = ESM::Weapon::AT_Thrust; + } + else + { + movement.mPosition[1] = movement.mPosition[0] = 0; + attackType = ESM::Weapon::AT_Chop; + } + } + + return attackType; +} + +void getMinMaxAttackDuration(const MWWorld::Ptr& actor, float (*fMinMaxDurations)[2]) +{ + if (!actor.getClass().hasInventoryStore(actor)) // creatures + { + fMinMaxDurations[0][0] = fMinMaxDurations[0][1] = 0.1f; + fMinMaxDurations[1][0] = fMinMaxDurations[1][1] = 0.1f; + fMinMaxDurations[2][0] = fMinMaxDurations[2][1] = 0.1f; return; } - //the more damage attackType deals the more probability it has - int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; - int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; - int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + // get weapon information: type and speed + const ESM::Weapon *weapon = NULL; + MWMechanics::WeaponType weaptype; - float total = slash + chop + thrust; + MWWorld::ContainerStoreIterator weaponSlot = + MWMechanics::getActiveWeapon(actor.getClass().getCreatureStats(actor), actor.getClass().getInventoryStore(actor), &weaptype); - float roll = static_cast(rand())/RAND_MAX; - if(roll <= static_cast(slash)/total) + float weapSpeed; + if (weaptype != MWMechanics::WeapType_HandToHand) { - movement.mPosition[0] = (static_cast(rand())/RAND_MAX < 0.5f)? 1: -1; - movement.mPosition[1] = 0; + weapon = weaponSlot->get()->mBase; + weapSpeed = weapon->mData.mSpeed; + } + else weapSpeed = 1.0f; + + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(actor); + + std::string weapGroup; + MWMechanics::getWeaponGroup(weaptype, weapGroup); + weapGroup = weapGroup + ": "; + + bool bRangedWeap = (weaptype >= MWMechanics::WeapType_BowAndArrow && weaptype <= MWMechanics::WeapType_Thrown); + + const char *attackType[] = {"chop ", "slash ", "thrust ", "shoot "}; + + std::string textKey = "start"; + std::string textKey2; + + // get durations for each attack type + for (int i = 0; i < (bRangedWeap ? 1 : 3); i++) + { + float start1 = anim->getStartTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey, false); + + textKey2 = "min attack"; + float start2 = anim->getStartTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2, false); + + fMinMaxDurations[i][0] = (start2 - start1) / weapSpeed; + + textKey2 = "max attack"; + start1 = anim->getStartTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2, false); + + fMinMaxDurations[i][1] = fMinMaxDurations[i][0] + (start1 - start2) / weapSpeed; + } + +} + +Ogre::Vector3 AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const Ogre::Vector3& vLastTargetPos, + float duration, int weapType, float strength) +{ + float projSpeed; + + // get projectile speed (depending on weapon type) + if (weapType == ESM::Weapon::MarksmanThrown) + { + static float fThrownWeaponMinSpeed = + MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMinSpeed")->getFloat(); + static float fThrownWeaponMaxSpeed = + MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMaxSpeed")->getFloat(); + + projSpeed = + fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; } - else if(roll <= (static_cast(slash) + static_cast(thrust))/total) - movement.mPosition[1] = 1; else - movement.mPosition[1] = movement.mPosition[0] = 0; + { + static float fProjectileMinSpeed = + MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMinSpeed")->getFloat(); + static float fProjectileMaxSpeed = + MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->getFloat(); + + projSpeed = + fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; + } + + // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same + + Ogre::Vector3 vActorPos = Ogre::Vector3(actor.getRefData().getPosition().pos); + Ogre::Vector3 vTargetPos = Ogre::Vector3(target.getRefData().getPosition().pos); + Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos; + float distToTarget = vDirToTarget.length(); + + Ogre::Vector3 vTargetMoveDir = vTargetPos - vLastTargetPos; + vTargetMoveDir /= duration; // |vTargetMoveDir| is target real speed in units/sec now + + Ogre::Vector3 vPerpToDir = vDirToTarget.crossProduct(Ogre::Vector3::UNIT_Z); + + float velPerp = vTargetMoveDir.dotProduct(vPerpToDir.normalisedCopy()); + float velDir = vTargetMoveDir.dotProduct(vDirToTarget.normalisedCopy()); + + // time to collision between target and projectile + float t_collision; + + float projVelDirSquared = projSpeed * projSpeed - velPerp * velPerp; + float projDistDiff = vDirToTarget.dotProduct(vTargetMoveDir.normalisedCopy()); + projDistDiff = sqrt(distToTarget * distToTarget - projDistDiff * projDistDiff); + + if (projVelDirSquared > 0) + t_collision = projDistDiff / (sqrt(projVelDirSquared) - velDir); + else t_collision = 0; // speed of projectile is not enough to reach moving target + + return vTargetPos + vTargetMoveDir * t_collision - vActorPos; } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 4b728ff22..e168dfc95 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -8,6 +8,8 @@ #include "movement.hpp" #include "obstacle.hpp" +#include + #include "../mwworld/cellstore.hpp" // for Doors #include "../mwbase/world.hpp" @@ -48,12 +50,17 @@ namespace MWMechanics bool mCombatMove; bool mBackOffDoor; + float mStrength; // this is actually make sense only in ranged combat + float mMinMaxAttackDuration[3][2]; // slash, thrust, chop has different durations + bool mForceNoShortcut; ESM::Position mShortcutFailPos; - ESM::Position mLastPos; + Ogre::Vector3 mLastActorPos; MWMechanics::Movement mMovement; + int mTargetActorId; + Ogre::Vector3 mLastTargetPos; const MWWorld::CellStore* mCell; ObstacleCheck mObstacleCheck; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3b8b91b0e..641ddb43e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -896,15 +896,26 @@ bool Animation::getInfo(const std::string &groupname, float *complete, float *sp return true; } -float Animation::getStartTime(const std::string &groupname) const +float Animation::getStartTime(const std::string &groupname, bool onlyGroup) const { AnimSourceList::const_iterator iter(mAnimSources.begin()); for(;iter != mAnimSources.end();iter++) { const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; - NifOgre::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); - if(found != keys.end()) - return found->first; + if (onlyGroup) + { + NifOgre::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); + if(found != keys.end()) + return found->first; + } + else + { + for(NifOgre::TextKeyMap::const_iterator iter(keys.begin()); iter != keys.end(); ++iter) + { + if(iter->second.compare(0, groupname.size(), groupname) == 0) + return iter->first; + } + } } return -1.f; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 564bb73ef..2fc57f0e9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -275,7 +275,7 @@ public: bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; /// Get the absolute position in the animation track of the first text key with the given group. - float getStartTime(const std::string &groupname) const; + float getStartTime(const std::string &groupname, bool onlyGroup) const; /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. float getCurrentTime(const std::string& groupname) const; diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 5f953bd83..3d4886cea 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -32,7 +32,7 @@ float WeaponAnimationTime::getValue() const void WeaponAnimationTime::setGroup(const std::string &group) { mWeaponGroup = group; - mStartTime = mAnimation->getStartTime(mWeaponGroup); + mStartTime = mAnimation->getStartTime(mWeaponGroup, true); } void WeaponAnimationTime::updateStartTime() From a3752da79f3785ed814d0ca75fe201269449c2cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Jun 2014 19:50:39 +0200 Subject: [PATCH 013/226] Store Always Run control state in settings --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +++- files/settings-default.cfg | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 943453a39..f75dc7ff0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -117,7 +117,7 @@ namespace MWInput , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) , mOverencumberedMessageDelay(0.f) - , mAlwaysRunActive(false) + , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input")) , mControlsDisabled(false) { @@ -819,6 +819,8 @@ namespace MWInput { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; mAlwaysRunActive = !mAlwaysRunActive; + + Settings::Manager::setBool("always run", "Input", mAlwaysRunActive); } void InputManager::resetIdleTime() diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 9eed2c7d9..2ed3d6cb9 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -168,6 +168,8 @@ camera y multiplier = 1.0 ui y multiplier = 1.0 +always run = false + [Game] # Always use the most powerful attack when striking with a weapon (chop, slash or thrust) best attack = false From cdd53862cbda2677bfed35477ccf6b4e518047e3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 8 Jun 2014 21:37:50 +0200 Subject: [PATCH 014/226] Increase width of dialogue topic list (Fixes #1440) --- files/mygui/openmw_dialogue_window.layout | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/files/mygui/openmw_dialogue_window.layout b/files/mygui/openmw_dialogue_window.layout index 78daa0705..5a7cd772d 100644 --- a/files/mygui/openmw_dialogue_window.layout +++ b/files/mygui/openmw_dialogue_window.layout @@ -4,28 +4,28 @@ - + - - + + - + - - + - + From 5488fe1ab3bacdffed8aa934882ef4b6b525ebe3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 9 Jun 2014 03:40:14 +0200 Subject: [PATCH 015/226] Change npc training skills to prefer skills with lowest ID if skill values are the same (Fixes #1445) --- apps/openmw/mwgui/trainingwindow.cpp | 46 ++++++++++++++++------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 9a2c3b805..6463db3d7 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -16,6 +16,24 @@ #include "tooltips.hpp" +namespace +{ +// Sorts a container descending by skill value. If skill value is equal, sorts ascending by skill ID. +// pair +bool sortSkills (const std::pair& left, const std::pair& right) +{ + if (left == right) + return false; + + if (left.second > right.second) + return true; + else if (left.second < right.second) + return false; + + return left.first < right.first; +} +} + namespace MWGui { @@ -52,29 +70,17 @@ namespace MWGui MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); // NPC can train you in his best 3 skills - std::vector< std::pair > bestSkills; - bestSkills.push_back (std::make_pair(-1, -1)); - bestSkills.push_back (std::make_pair(-1, -1)); - bestSkills.push_back (std::make_pair(-1, -1)); + std::vector< std::pair > skills; for (int i=0; i bestSkills[j].second) - { - if (j<2) - { - bestSkills[j+1] = bestSkills[j]; - } - bestSkills[j] = std::make_pair(i, value); - break; - } - } + skills.push_back(std::make_pair(i, value)); } + std::sort(skills.begin(), skills.end(), sortSkills); + MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator (); MyGUI::Gui::getInstance ().destroyWidgets (widgets); @@ -86,20 +92,20 @@ namespace MWGui for (int i=0; i<3; ++i) { int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer - (mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); + (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); MyGUI::Button* button = mTrainingOptions->createWidget("SandTextButton", MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); button->setEnabled(price <= playerGold); - button->setUserData(bestSkills[i].first); + button->setUserData(skills[i].first); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); - button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[bestSkills[i].first] + "} - " + boost::lexical_cast(price)); + button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[skills[i].first] + "} - " + boost::lexical_cast(price)); button->setSize(button->getTextSize ().width+12, button->getSize().height); - ToolTips::createSkillToolTip (button, bestSkills[i].first); + ToolTips::createSkillToolTip (button, skills[i].first); } center(); From 1bab74a98dd7448ab70d7d2ab1cdcd8a12a22b9c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 9 Jun 2014 03:42:29 +0200 Subject: [PATCH 016/226] Fix punishment for stealing 0 value items (Fixes #1435) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 900ea72ca..f39a4b961 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -929,7 +929,10 @@ namespace MWMechanics else if (type == OT_Murder) arg = store.find("iCrimeKilling")->getInt(); else if (type == OT_Theft) + { arg *= store.find("fCrimeStealing")->getFloat(); + arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen + } MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() From 698cbba6ef6bb3e139da218b001686f0583d0a54 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 9 Jun 2014 23:02:06 +0400 Subject: [PATCH 017/226] old bug + comment fix --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5727996d9..76a84cc9e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -928,7 +928,7 @@ namespace MWMechanics 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 creature + listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest guard for (std::list::iterator it = listGuards.begin(); it != listGuards.end(); ++it) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index db4e59929..b487ffb21 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -975,7 +975,7 @@ bool CharacterController::updateWeaponState() } //if playing combat animation and lowerbody is not busy switch to whole body animation - if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) + if((weaptype != WeapType_None || mUpperBodyState == UpperCharState_UnEquipingWeap) && animPlaying) { if( mMovementState != CharState_None || mJumpState != JumpState_None || From e796fa23130a4476839ee862454f1561766dae01 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 9 Jun 2014 22:18:53 +0200 Subject: [PATCH 018/226] Add another french morrowind font workaround (Fixes #1447) --- apps/openmw/mwgui/fontloader.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp index 9d47bc38d..b7c2007b4 100644 --- a/apps/openmw/mwgui/fontloader.cpp +++ b/apps/openmw/mwgui/fontloader.cpp @@ -258,12 +258,16 @@ namespace MWGui code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + MyGUI::utility::toString((fontSize-data[i].ascent))); - // More hacks! The french game uses U+2019, which is nowhere to be found in - // the CP437 encoding of the font. Fall back to 39 (regular apostrophe) - if (i == 39 && mEncoding == ToUTF8::CP437) + // More hacks! The french game uses several win1252 characters that are not included + // in the cp437 encoding of the font. Fall back to similar available characters. + // Same for U+2013 + std::map additional; + additional[39] = 0x2019; // apostrophe + additional[45] = 0x2013; // dash + if (additional.find(i) != additional.end() && mEncoding == ToUTF8::CP437) { MyGUI::xml::ElementPtr code = codes->createChild("Code"); - code->addAttribute("index", 0x2019); + code->addAttribute("index", additional[i]); code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " " + MyGUI::utility::toString(w) + " " From 7721e541910063ea2b74462168bbdc05e1eaaa60 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 00:22:00 +0200 Subject: [PATCH 019/226] Use descriptive names for save files and character folders (Fixes #1449) --- apps/openmw/mwstate/character.cpp | 30 ++++++++++++++------ apps/openmw/mwstate/character.hpp | 1 - apps/openmw/mwstate/charactermanager.cpp | 35 ++++++++++++++++-------- apps/openmw/mwstate/charactermanager.hpp | 7 +++-- apps/openmw/mwstate/statemanagerimp.cpp | 16 +++++++---- 5 files changed, 58 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 5fe80ce0c..6f569f078 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -54,9 +54,28 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) Slot slot; std::ostringstream stream; - stream << mNext++; + + // The profile description is user-supplied, so we need to escape the path + for (std::string::const_iterator it = profile.mDescription.begin(); it != profile.mDescription.end(); ++it) + { + if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters + stream << *it; + else + stream << "_"; + } slot.mPath = mPath / stream.str(); + + // Append an index if necessary to ensure a unique file + int i=0; + while (boost::filesystem::exists(slot.mPath)) + { + std::ostringstream test; + test << stream.str(); + test << " - " << ++i; + slot.mPath = mPath / test.str(); + } + slot.mProfile = profile; slot.mTimeStamp = std::time (0); @@ -64,7 +83,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) } MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game) -: mPath (saves), mNext (0) +: mPath (saves) { if (!boost::filesystem::is_directory (mPath)) { @@ -82,13 +101,6 @@ MWState::Character::Character (const boost::filesystem::path& saves, const std:: addSlot (slotPath, game); } catch (...) {} // ignoring bad saved game files for now - - std::istringstream stream (slotPath.filename().string()); - - int index = 0; - - if ((stream >> index) && index>=mNext) - mNext = index+1; } std::sort (mSlots.begin(), mSlots.end()); diff --git a/apps/openmw/mwstate/character.hpp b/apps/openmw/mwstate/character.hpp index 874533289..4703f0cca 100644 --- a/apps/openmw/mwstate/character.hpp +++ b/apps/openmw/mwstate/character.hpp @@ -26,7 +26,6 @@ namespace MWState boost::filesystem::path mPath; std::vector mSlots; - int mNext; void addSlot (const boost::filesystem::path& path, const std::string& game); diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index d773904db..91d728ae0 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -8,7 +8,7 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves, const std::string& game) -: mPath (saves), mNext (0), mCurrent (0), mGame (game) +: mPath (saves), mCurrent (0), mGame (game) { if (!boost::filesystem::is_directory (mPath)) { @@ -28,21 +28,14 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save if (character.begin()!=character.end()) mCharacters.push_back (character); } - - std::istringstream stream (characterDir.filename().string()); - - int index = 0; - - if ((stream >> index) && index>=mNext) - mNext = index+1; } } } -MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create) +MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create, const std::string& name) { if (!mCurrent && create) - createCharacter(); + createCharacter(name); return mCurrent; } @@ -63,13 +56,31 @@ void MWState::CharacterManager::deleteSlot(const MWState::Character *character, } } -void MWState::CharacterManager::createCharacter() +void MWState::CharacterManager::createCharacter(const std::string& name) { std::ostringstream stream; - stream << mNext++; + + // The character name is user-supplied, so we need to escape the path + for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) + { + if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters + stream << *it; + else + stream << "_"; + } boost::filesystem::path path = mPath / stream.str(); + // Append an index if necessary to ensure a unique directory + int i=0; + while (boost::filesystem::exists(path)) + { + std::ostringstream test; + test << stream.str(); + test << " - " << ++i; + path = mPath / test.str(); + } + mCharacters.push_back (Character (path, mGame)); mCurrent = &mCharacters.back(); diff --git a/apps/openmw/mwstate/charactermanager.hpp b/apps/openmw/mwstate/charactermanager.hpp index adf9d2ef4..c44c10b5a 100644 --- a/apps/openmw/mwstate/charactermanager.hpp +++ b/apps/openmw/mwstate/charactermanager.hpp @@ -10,7 +10,6 @@ namespace MWState class CharacterManager { boost::filesystem::path mPath; - int mNext; // Uses std::list, so that mCurrent stays valid when characters are deleted std::list mCharacters; @@ -32,13 +31,15 @@ namespace MWState CharacterManager (const boost::filesystem::path& saves, const std::string& game); - Character *getCurrentCharacter (bool create = true); + Character *getCurrentCharacter (bool create, const std::string& name); ///< \param create Create a new character, if there is no current character. + /// \param name The character name to use in case a new character is created. void deleteSlot(const MWState::Character *character, const MWState::Slot *slot); - void createCharacter(); + void createCharacter(const std::string& name); ///< Create new character within saved game management + /// \param name Name for the character (does not need to be unique) void setCurrentCharacter (const Character *character); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 7a70944dc..68cb91eb9 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -184,9 +184,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot encoded->read(&profile.mScreenshot[0], encoded->size()); if (!slot) - slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); + slot = getCurrentCharacter()->createSlot (profile); else - slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + slot = getCurrentCharacter()->updateSlot (slot, profile); boost::filesystem::ofstream stream (slot->mPath, std::ios::binary); @@ -252,7 +252,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot // If no file was written, clean up the slot if (slot && !boost::filesystem::exists(slot->mPath)) - mCharacterManager.getCurrentCharacter()->deleteSlot(slot); + getCurrentCharacter()->deleteSlot(slot); } } @@ -419,7 +419,10 @@ void MWState::StateManager::deleteGame(const MWState::Character *character, cons MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) { - return mCharacterManager.getCurrentCharacter (create); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + std::string name = player.getClass().getName(player); + + return mCharacterManager.getCurrentCharacter (create, name); } MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() @@ -440,11 +443,12 @@ void MWState::StateManager::update (float duration) if (mAskLoadRecent) { int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); - if(iButton==0) + MWState::Character *curCharacter = getCurrentCharacter(false); + if(iButton==0 && curCharacter) { mAskLoadRecent = false; //Load last saved game for current character - MWState::Character *curCharacter = getCurrentCharacter(); + MWState::Slot lastSave = *curCharacter->begin(); loadGame(curCharacter, &lastSave); } From e0d6670ac452162b096b883edf806a91e3e60d5e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 01:57:54 +0200 Subject: [PATCH 020/226] Move video skip detection to WindowManager Fixes a bug where skipping using Esc would not work if a mouse button had been pressed previously --- apps/openmw/mwgui/confirmationdialog.cpp | 8 ++++---- apps/openmw/mwgui/mainmenu.cpp | 4 ++-- apps/openmw/mwgui/videowidget.cpp | 15 ++------------- apps/openmw/mwgui/videowidget.hpp | 12 ++++-------- apps/openmw/mwgui/windowmanagerimp.cpp | 24 +++++++++++++++++++++--- apps/openmw/mwgui/windowmanagerimp.hpp | 6 ++++++ 6 files changed, 39 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 89f477598..57c88bfa2 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -30,9 +30,9 @@ namespace MWGui void ConfirmationDialog::exit() { - eventCancelClicked(); - setVisible(false); + + eventCancelClicked(); } void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender) @@ -42,8 +42,8 @@ namespace MWGui void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender) { - eventOkClicked(); - setVisible(false); + + eventOkClicked(); } } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index b5cd61f59..1d47d3b76 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -167,7 +167,7 @@ namespace MWGui mVideo = mVideoBackground->createWidget("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); - mVideo->playVideo("video\\menu_background.bik", false); + mVideo->playVideo("video\\menu_background.bik"); } MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); @@ -204,7 +204,7 @@ namespace MWGui if (!mVideo->update()) { // If finished playing, start again - mVideo->playVideo("video\\menu_background.bik", 0); + mVideo->playVideo("video\\menu_background.bik"); } } } diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 8430c1c7b..cfd837a95 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -4,17 +4,12 @@ namespace MWGui { VideoWidget::VideoWidget() - : mAllowSkipping(true) { - eventKeyButtonPressed += MyGUI::newDelegate(this, &VideoWidget::onKeyPressed); - setNeedKeyFocus(true); } -void VideoWidget::playVideo(const std::string &video, bool allowSkipping) +void VideoWidget::playVideo(const std::string &video) { - mAllowSkipping = allowSkipping; - mPlayer.playVideo(video); setImageTexture(mPlayer.getTextureName()); @@ -30,19 +25,13 @@ int VideoWidget::getVideoHeight() return mPlayer.getVideoHeight(); } -void VideoWidget::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) -{ - if (_key == MyGUI::KeyCode::Escape && mAllowSkipping) - mPlayer.stopVideo(); -} - bool VideoWidget::update() { mPlayer.update(); return mPlayer.isPlaying(); } -void VideoWidget::cleanup() +void VideoWidget::stop() { mPlayer.close(); } diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp index 9360c8359..ad3d4d5e3 100644 --- a/apps/openmw/mwgui/videowidget.hpp +++ b/apps/openmw/mwgui/videowidget.hpp @@ -9,7 +9,7 @@ namespace MWGui { /** - * Widget that plays a video. Can be skipped by pressing Esc. + * Widget that plays a video. */ class VideoWidget : public MyGUI::ImageBox { @@ -18,7 +18,7 @@ namespace MWGui VideoWidget(); - void playVideo (const std::string& video, bool allowSkipping); + void playVideo (const std::string& video); int getVideoWidth(); int getVideoHeight(); @@ -26,15 +26,11 @@ namespace MWGui /// @return Is the video still playing? bool update(); - /// Free video player resources (done automatically on destruction) - void cleanup(); + /// Stop video and free resources (done automatically on destruction) + void stop(); private: - bool mAllowSkipping; - MWRender::VideoPlayer mPlayer; - - void onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 35ee2adc9..7ebdb6a83 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -202,8 +202,12 @@ namespace MWGui MyGUI::Align::Default, "Overlay"); mVideoBackground->setImageTexture("black.png"); mVideoBackground->setVisible(false); + mVideoBackground->setNeedMouseFocus(true); + mVideoBackground->setNeedKeyFocus(true); mVideoWidget = mVideoBackground->createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Default); + mVideoWidget->setNeedMouseFocus(true); + mVideoWidget->setNeedKeyFocus(true); } void WindowManager::initUI() @@ -263,7 +267,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,"Windows"); mHud->setVisible(mHudEnabled); @@ -1559,7 +1563,15 @@ namespace MWGui void WindowManager::playVideo(const std::string &name, bool allowSkipping) { - mVideoWidget->playVideo("video\\" + name, allowSkipping); + mVideoWidget->playVideo("video\\" + name); + + mVideoWidget->eventKeyButtonPressed.clear(); + mVideoBackground->eventKeyButtonPressed.clear(); + if (allowSkipping) + { + mVideoWidget->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed); + mVideoBackground->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed); + } // Turn off all rendering except for the GUI mRendering->getScene()->clearSpecialCaseRenderQueues(); @@ -1587,7 +1599,7 @@ namespace MWGui mRendering->getWindow()->update(); } - mVideoWidget->cleanup(); + mVideoWidget->stop(); setCursorVisible(cursorWasVisible); @@ -1628,4 +1640,10 @@ namespace MWGui if(input == mCurrentModals.top()) mCurrentModals.pop(); } + + void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) + { + if (_key == MyGUI::KeyCode::Escape) + mVideoWidget->stop(); + } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index b1dbf3a24..aee6cef47 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -12,6 +12,9 @@ #include "../mwbase/windowmanager.hpp" +#include +#include + namespace MyGUI { class Gui; @@ -424,6 +427,9 @@ namespace MWGui void onCursorChange(const std::string& name); void onKeyFocusChanged(MyGUI::Widget* widget); + // Key pressed while playing a video + void onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char); + void sizeVideo(int screenWidth, int screenHeight); }; } From 881ae33b74d374a444c37e9a2114213c771d98ac Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 02:15:09 +0200 Subject: [PATCH 021/226] Don't allow Creatures with no movement abilities to move (Fixes #1457) --- apps/openmw/mwclass/creature.cpp | 4 +++- apps/openmw/mwworld/physicssystem.cpp | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1a6e4e321..5744ebb9b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -345,7 +345,9 @@ namespace MWClass getCreatureStats(ptr).setAttacked(true); // Self defense - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) + if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80 + && (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures + // (they have no movement or attacks anyway) MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); if(!successful) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index b0b00c6db..85d41a478 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -193,6 +193,11 @@ namespace MWWorld const ESM::Position &refpos = ptr.getRefData().getPosition(); Ogre::Vector3 position(refpos.pos); + // Early-out for totally static creatures + // (Not sure if gravity should still apply?) + if (!ptr.getClass().canWalk(ptr) && !isFlying && !ptr.getClass().canSwim(ptr)) + return position; + /* Anything to collide with? */ OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); if(!physicActor || !physicActor->getCollisionMode()) From 311acfa8ff7a875373adf9a731b8239b3b596ae3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 02:27:38 +0200 Subject: [PATCH 022/226] Add delete button to save load menu (Fixes #1453) --- apps/openmw/mwgui/savegamedialog.cpp | 28 +++++++++++++++++------ apps/openmw/mwgui/savegamedialog.hpp | 4 ++++ files/mygui/openmw_savegame_dialog.layout | 10 +++++--- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index ef5d5858b..e5390d5a6 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -30,11 +30,13 @@ namespace MWGui getWidget(mInfoText, "InfoText"); getWidget(mOkButton, "OkButton"); getWidget(mCancelButton, "CancelButton"); + getWidget(mDeleteButton, "DeleteButton"); getWidget(mSaveList, "SaveList"); getWidget(mSaveNameEdit, "SaveNameEdit"); getWidget(mSpacer, "Spacer"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); + mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked); mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick); @@ -54,13 +56,16 @@ namespace MWGui onSlotSelected(sender, pos); if (pos != MyGUI::ITEM_NONE && MyGUI::InputManager::getInstance().isShiftPressed()) - { - ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); - dialog->open("#{sMessage3}"); - dialog->eventOkClicked.clear(); - dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed); - dialog->eventCancelClicked.clear(); - } + confirmDeleteSave(); + } + + void SaveGameDialog::confirmDeleteSave() + { + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); + dialog->open("#{sMessage3}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed); + dialog->eventCancelClicked.clear(); } void SaveGameDialog::onDeleteSlotConfirmed() @@ -175,6 +180,9 @@ namespace MWGui mCharacterSelection->setVisible(load); mSpacer->setUserString("Hidden", load ? "false" : "true"); + mDeleteButton->setUserString("Hidden", load ? "false" : "true"); + mDeleteButton->setVisible(load); + if (!load) { mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false); @@ -188,6 +196,12 @@ namespace MWGui exit(); } + void SaveGameDialog::onDeleteButtonClicked(MyGUI::Widget *sender) + { + if (mCurrentSlot) + confirmDeleteSave(); + } + void SaveGameDialog::onConfirmationGiven() { accept(true); diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 9f44d5370..80cfad279 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -24,8 +24,11 @@ namespace MWGui void setLoadOrSave(bool load); private: + void confirmDeleteSave(); + void onCancelButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender); + void onDeleteButtonClicked (MyGUI::Widget* sender); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); // Slot selected (mouse click or arrow keys) void onSlotSelected (MyGUI::ListBox* sender, size_t pos); @@ -51,6 +54,7 @@ namespace MWGui MyGUI::EditBox* mInfoText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; + MyGUI::Button* mDeleteButton; MyGUI::ListBox* mSaveList; MyGUI::EditBox* mSaveNameEdit; MyGUI::Widget* mSpacer; diff --git a/files/mygui/openmw_savegame_dialog.layout b/files/mygui/openmw_savegame_dialog.layout index ceb1a8428..701533636 100644 --- a/files/mygui/openmw_savegame_dialog.layout +++ b/files/mygui/openmw_savegame_dialog.layout @@ -2,8 +2,8 @@ - - + + @@ -49,10 +49,14 @@ - + + + + + From 271aac3fcc2b317996c829076e492a41f7ebde72 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 02:47:02 +0200 Subject: [PATCH 023/226] Savegame dialog: Grey out buttons if no save is selected --- apps/openmw/mwgui/savegamedialog.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index e5390d5a6..caa0cbb36 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -112,6 +112,7 @@ namespace MWGui mCurrentCharacter = NULL; mCurrentSlot = NULL; mSaveList->removeAllItems(); + onSlotSelected(mSaveList, MyGUI::ITEM_NONE); MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); if (mgr->characterBegin() == mgr->characterEnd()) @@ -239,10 +240,8 @@ namespace MWGui } else { - if (mCurrentCharacter && mCurrentSlot) - { - MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot); - } + assert (mCurrentCharacter && mCurrentSlot); + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot); } } @@ -292,6 +291,9 @@ namespace MWGui void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos) { + mOkButton->setEnabled(pos != MyGUI::ITEM_NONE || mSaving); + mDeleteButton->setEnabled(pos != MyGUI::ITEM_NONE); + if (pos == MyGUI::ITEM_NONE) { mCurrentSlot = NULL; From c0c1db44907a4719773c7f734fefeb599a32eeda Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 03:28:21 +0200 Subject: [PATCH 024/226] Don't list non-usable items in QuickKeysMenu selection (Fixes #1427) --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 ++ apps/openmw/mwgui/sortfilteritemmodel.cpp | 5 +++++ apps/openmw/mwgui/sortfilteritemmodel.hpp | 1 + 3 files changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index eda9daeff..f52a4b941 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -24,6 +24,7 @@ #include "spellwindow.hpp" #include "itemwidget.hpp" +#include "sortfilteritemmodel.hpp" namespace MWGui @@ -134,6 +135,7 @@ namespace MWGui } mItemSelectionDialog->setVisible(true); mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); + mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems); mAssignDialog->setVisible (false); } diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index b8dcbcbbb..93e5432ca 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -14,6 +14,7 @@ #include #include "../mwworld/class.hpp" +#include "../mwworld/nullaction.hpp" namespace { @@ -126,6 +127,10 @@ namespace MWGui && !base.get()->mBase->mData.mIsScroll) return false; + if ((mFilter & Filter_OnlyUsableItems) && typeid(*base.getClass().use(base)) == typeid(MWWorld::NullAction) + && base.getClass().getScript(base).empty()) + return false; + return true; } diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index c7feaa3b9..4af35e7a8 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -36,6 +36,7 @@ namespace MWGui static const int Filter_OnlyEnchanted = (1<<1); static const int Filter_OnlyEnchantable = (1<<2); static const int Filter_OnlyChargedSoulstones = (1<<3); + static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action private: From 65d5311037f821098580a92c9577c025f59fdb84 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 04:10:34 +0200 Subject: [PATCH 025/226] Revert "Don't try to show exceptions in a message box if SDL was not initialized" According to SDL docs, "This function may be called at any time, even before SDL_Init()". Also fixes an issue where message boxes weren't working due to SDL_Quit already having been called by ~Engine. This reverts commit 39eea24dc3866760cc40b79b6d57ebbc6799fc73. Conflicts: apps/openmw/main.cpp --- apps/openmw/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 3098d953e..302a9cf64 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -4,7 +4,8 @@ #include #include -#include +#include +#include #include "engine.hpp" #if defined(_WIN32) && !defined(_CONSOLE) @@ -286,7 +287,7 @@ int main(int argc, char**argv) catch (std::exception &e) { #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE - if (isatty(fileno(stdin)) || !SDL_WasInit(SDL_INIT_VIDEO)) + if (isatty(fileno(stdin))) std::cerr << "\nERROR: " << e.what() << std::endl; else #endif From 8419002393d89bd20222773a1872efb858f7ea32 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 10 Jun 2014 11:25:39 +0200 Subject: [PATCH 026/226] Task #940: Move licenses to appropriate place in docs. Additional cleanup. --- .gitignore | 2 +- CMakeLists.txt | 2 +- credits.txt | 4 ++-- {Docs => docs}/Doxyfile | 2 +- {Docs => docs}/DoxyfilePages | 2 +- .../license/DejaVu Font License.txt | 0 GPL3.txt => docs/license/GPL3.txt | 0 {Docs => docs}/mainpage.hpp.cmake | 0 8 files changed, 6 insertions(+), 6 deletions(-) rename {Docs => docs}/Doxyfile (99%) rename {Docs => docs}/DoxyfilePages (99%) rename DejaVu Font License.txt => docs/license/DejaVu Font License.txt (100%) rename GPL3.txt => docs/license/GPL3.txt (100%) rename {Docs => docs}/mainpage.hpp.cmake (100%) diff --git a/.gitignore b/.gitignore index 3975c4521..08bf0bad6 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,7 @@ resources ## generated objects apps/openmw/config.hpp components/version/version.hpp -Docs/mainpage.hpp +docs/mainpage.hpp moc_*.cxx *.cxx_parameters *qrc_launcher.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 3632f1e08..7038e05ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ include(OpenMWMacros) # doxygen main page -configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") +configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/docs/mainpage.hpp") option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE) option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE) diff --git a/credits.txt b/credits.txt index 092f5c15e..791db0433 100644 --- a/credits.txt +++ b/credits.txt @@ -19,8 +19,8 @@ Alexander Olofsson (Ace) Artem Kotsynyak (greye) Arthur Moore (EmperorArthur) athile +Bret Curtis (psi29a) Britt Mathis (galdor557) -BrotherBrick cc9cii Chris Boyce (slothlife) Chris Robinson (KittyCat) @@ -79,7 +79,7 @@ Torben Leif Carrington (TorbenC) Packagers: Alexander Olofsson (Ace) - Windows -BrotherBrick - Ubuntu Linux +Bret Curtis (psi29a) - Ubuntu Linux Edmondo Tommasina (edmondo) - Gentoo Linux Julian Ospald (hasufell) - Gentoo Linux Karl-Felix Glatzer (k1ll) - Linux Binaries diff --git a/Docs/Doxyfile b/docs/Doxyfile similarity index 99% rename from Docs/Doxyfile rename to docs/Doxyfile index 43c3312ad..156e23abd 100644 --- a/Docs/Doxyfile +++ b/docs/Doxyfile @@ -576,7 +576,7 @@ WARN_LOGFILE = INPUT = apps \ components \ libs \ - Docs + docs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/Docs/DoxyfilePages b/docs/DoxyfilePages similarity index 99% rename from Docs/DoxyfilePages rename to docs/DoxyfilePages index 5ce82a7c2..dca9d7a12 100644 --- a/Docs/DoxyfilePages +++ b/docs/DoxyfilePages @@ -576,7 +576,7 @@ WARN_LOGFILE = INPUT = apps \ components \ libs \ - Docs + docs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/DejaVu Font License.txt b/docs/license/DejaVu Font License.txt similarity index 100% rename from DejaVu Font License.txt rename to docs/license/DejaVu Font License.txt diff --git a/GPL3.txt b/docs/license/GPL3.txt similarity index 100% rename from GPL3.txt rename to docs/license/GPL3.txt diff --git a/Docs/mainpage.hpp.cmake b/docs/mainpage.hpp.cmake similarity index 100% rename from Docs/mainpage.hpp.cmake rename to docs/mainpage.hpp.cmake From 979128b2c55f3c38a5160cf82222aa5325824916 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 10 Jun 2014 14:20:29 +0400 Subject: [PATCH 027/226] Combat music; some minor combat fixes --- apps/openmw/mwmechanics/actors.cpp | 19 +++++++++++++++++++ apps/openmw/mwmechanics/aicombat.cpp | 8 ++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 76a84cc9e..7102fc0ee 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -970,6 +970,9 @@ namespace MWMechanics } // 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(); @@ -988,6 +991,8 @@ namespace MWMechanics if(!stats.isDead()) { + if (stats.isHostile()) hostilesCount++; + if(iter->second->isDead()) { // Actor has been resurrected. Notify the CharacterController and re-enable collision. @@ -1045,6 +1050,20 @@ namespace MWMechanics } } + // check if we still have any player enemies to switch music + static bool isBattleMusic = false; + + if (isBattleMusic && hostilesCount == 0) + { + MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); + isBattleMusic = false; + } + else if (!isBattleMusic && hostilesCount > 0) + { + MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle")); + isBattleMusic = true; + } + // if player is in sneak state see if anyone detects him if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak)) { diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 5e2847ed2..2218623da 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -405,8 +405,6 @@ namespace MWMechanics } else // (within attack dist) { - if (!mAttack) mMovement.mPosition[1] = 0; - if(mMovement.mPosition[0] || mMovement.mPosition[1]) { mTimerCombatMove = 0.1f + 0.1f * static_cast(rand())/RAND_MAX; @@ -501,6 +499,12 @@ namespace MWMechanics } mMovement.mPosition[1] = 1; + if (mReadyToAttack) + { + // to stop possible sideway moving after target moved out of attack range + mCombatMove = true; + mTimerCombatMove = 0; + } mReadyToAttack = false; } From 4119038f1d772bdd9afde02ad1e7b965005ead1d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 14:46:44 +0200 Subject: [PATCH 028/226] Remove an old workaround (Fixes #1458) --- apps/openmw/mwinput/inputmanagerimp.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f75dc7ff0..279f7c2af 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -919,11 +919,6 @@ namespace MWInput mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } } - - // Printscreen key should not be allowed because it's captured by system screenshot function - // We check this explicitely here to fix up pre-0.26 config files. Can be removed after a few versions - if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Screenshot), ICS::Control::INCREASE) == SDLK_PRINTSCREEN) - mInputBinder->addKeyBinding(mInputBinder->getControl(A_Screenshot), SDLK_F12, ICS::Control::INCREASE); } std::string InputManager::getActionDescription (int action) From e28888df394739094e93e9b98576392cb56c3234 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 10 Jun 2014 14:51:51 +0200 Subject: [PATCH 029/226] fix for dejavu location --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7038e05ba..d67b724ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,7 +432,7 @@ IF(NOT WIN32 AND NOT APPLE) ENDIF(BUILD_OPENCS) # Install licenses - INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) + INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) ENDIF (DPKG_PROGRAM) From 3bf599248ec8103e4226d7d88efe73aa62f47734 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 14:58:09 +0200 Subject: [PATCH 030/226] CMake cleanup: Remove distribution-specific install stuff (unused) --- CMakeLists.txt | 71 ++++++++++++------------------- apps/launcher/CMakeLists.txt | 4 -- apps/mwiniimporter/CMakeLists.txt | 5 --- apps/opencs/CMakeLists.txt | 4 -- apps/openmw/CMakeLists.txt | 4 -- 5 files changed, 27 insertions(+), 61 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3632f1e08..8e5ba6cd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,13 +80,6 @@ option(USE_FFMPEG "use ffmpeg for sound" ON) # OS X deployment option(OPENMW_OSX_DEPLOYMENT OFF) -if(UNIX AND NOT APPLE) - option(BUILD_WITH_DPKG "enable dpkg-based install for debian and debian derivatives" OFF) - if(BUILD_WITH_DPKG) - find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems") - endif(BUILD_WITH_DPKG) -endif(UNIX AND NOT APPLE) - # Location of morrowind data files if (APPLE) set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") @@ -395,46 +388,36 @@ if (CMAKE_COMPILER_IS_GNUCC) endif (CMAKE_COMPILER_IS_GNUCC) IF(NOT WIN32 AND NOT APPLE) - ## Debian and non debian Linux building + # Linux building # Paths - IF (DPKG_PROGRAM) - ## Debian specific - SET(CMAKE_INSTALL_PREFIX "/usr") - SET(DATAROOTDIR "share" CACHE PATH "Sets the root of data directories to a non-default location") - SET(DATADIR "share/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") - SET(ICONDIR "share/pixmaps" CACHE PATH "Set icon dir") - SET(SYSCONFDIR "../etc/openmw" CACHE PATH "Set config dir") - ELSE () - ## Non debian specific - SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") - SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") - SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") - SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") - SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.") - SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir") + SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") + SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") + SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") + SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") + SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.") + SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir") - # Install binaries - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) - IF(BUILD_LAUNCHER) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) - ENDIF(BUILD_LAUNCHER) - IF(BUILD_BSATOOL) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) - ENDIF(BUILD_BSATOOL) - IF(BUILD_ESMTOOL) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) - ENDIF(BUILD_ESMTOOL) - IF(BUILD_MWINIIMPORTER) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) - ENDIF(BUILD_MWINIIMPORTER) - IF(BUILD_OPENCS) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) - ENDIF(BUILD_OPENCS) + # Install binaries + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) + IF(BUILD_LAUNCHER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_LAUNCHER) + IF(BUILD_BSATOOL) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_BSATOOL) + IF(BUILD_ESMTOOL) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_ESMTOOL) + IF(BUILD_MWINIIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_OPENCS) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_OPENCS) - # Install licenses - INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) - INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) - ENDIF (DPKG_PROGRAM) + # Install licenses + INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) + INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) # Install icon and desktop file INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index ec721a5e5..d7733ba0e 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -119,10 +119,6 @@ endif(NOT WIN32) -if(DPKG_PROGRAM) - INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher) -endif() - if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(omwlauncher gcov) diff --git a/apps/mwiniimporter/CMakeLists.txt b/apps/mwiniimporter/CMakeLists.txt index 702f66513..deab88ce2 100644 --- a/apps/mwiniimporter/CMakeLists.txt +++ b/apps/mwiniimporter/CMakeLists.txt @@ -22,8 +22,3 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(mwiniimport gcov) endif() - -if(DPKG_PROGRAM) - INSTALL(TARGETS mwiniimport RUNTIME DESTINATION games COMPONENT mwiniimport) -endif() - diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f18ac0bca..dd31aa81b 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -199,10 +199,6 @@ target_link_libraries(opencs components ) -if(DPKG_PROGRAM) - INSTALL(TARGETS opencs RUNTIME DESTINATION games COMPONENT opencs) -endif() - if(APPLE) INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) endif() diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8310e8abf..7c047adef 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -142,10 +142,6 @@ if(APPLE) endif() endif(APPLE) -if(DPKG_PROGRAM) - INSTALL(TARGETS openmw RUNTIME DESTINATION games COMPONENT openmw) -endif(DPKG_PROGRAM) - if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw gcov) From 1ed3f092c142488072d4915d9edd47f6660fa4ba Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 15:42:50 +0200 Subject: [PATCH 031/226] Implement text replacement for journal topic responses (Fixes #1429) --- apps/openmw/mwbase/journal.hpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 +-- apps/openmw/mwdialogue/journalentry.cpp | 28 ++++++++++++++----- apps/openmw/mwdialogue/journalentry.hpp | 10 +++++-- apps/openmw/mwdialogue/journalimp.cpp | 7 +++-- apps/openmw/mwdialogue/journalimp.hpp | 2 +- apps/openmw/mwdialogue/topic.cpp | 5 ---- apps/openmw/mwdialogue/topic.hpp | 2 -- 8 files changed, 37 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index a49ebb9bc..c43ebeb8f 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -59,7 +59,7 @@ namespace MWBase virtual int getJournalIndex (const std::string& id) const = 0; ///< Get the journal index. - virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) = 0; + virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) = 0; virtual TEntryIter begin() const = 0; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 57681b7fb..c1a623418 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -294,7 +294,7 @@ namespace MWDialogue { if (iter->mId == info->mId) { - MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor)); + MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor); break; } } @@ -472,7 +472,7 @@ namespace MWDialogue { if (iter->mId == info->mId) { - MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor)); + MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor); break; } } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 55574bf3e..b92d7cace 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -5,16 +5,21 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwscript/interpretercontext.hpp" + + namespace MWDialogue { Entry::Entry() {} - Entry::Entry (const std::string& topic, const std::string& infoId) + Entry::Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor) : mInfoId (infoId) { const ESM::Dialogue *dialogue = @@ -24,8 +29,17 @@ namespace MWDialogue iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == mInfoId) { - /// \todo text replacement - mText = iter->mResponse; + if (actor.isEmpty()) + { + MWScript::InterpreterContext interpreterContext(NULL,MWWorld::Ptr()); + mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext); + } + else + { + MWScript::InterpreterContext interpreterContext(&actor.getRefData().getLocals(),actor); + mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext); + } + return; } @@ -49,8 +63,8 @@ namespace MWDialogue JournalEntry::JournalEntry() {} - JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) - : Entry (topic, infoId), mTopic (topic) + JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor) + : Entry (topic, infoId, actor), mTopic (topic) {} JournalEntry::JournalEntry (const ESM::JournalEntry& record) @@ -65,7 +79,7 @@ namespace MWDialogue JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { - return JournalEntry (topic, idFromIndex (topic, index)); + return JournalEntry (topic, idFromIndex (topic, index), MWWorld::Ptr()); } std::string JournalEntry::idFromIndex (const std::string& topic, int index) @@ -90,7 +104,7 @@ namespace MWDialogue StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId, int day, int month, int dayOfMonth) - : JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) + : JournalEntry (topic, infoId, MWWorld::Ptr()), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) {} StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record) diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index a77ba4f7c..3ae3efcc8 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -8,6 +8,11 @@ namespace ESM struct JournalEntry; } +namespace MWWorld +{ + class Ptr; +} + namespace MWDialogue { /// \brief Basic quest/dialogue/topic entry @@ -19,7 +24,8 @@ namespace MWDialogue Entry(); - Entry (const std::string& topic, const std::string& infoId); + /// actor is optional + Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor); Entry (const ESM::JournalEntry& record); @@ -37,7 +43,7 @@ namespace MWDialogue JournalEntry(); - JournalEntry (const std::string& topic, const std::string& infoId); + JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor); JournalEntry (const ESM::JournalEntry& record); diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 04aa0534d..1429415e5 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -9,6 +9,7 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/class.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -103,12 +104,12 @@ namespace MWDialogue quest.setIndex (index); } - void Journal::addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) + void Journal::addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) { Topic& topic = getTopic (topicId); - JournalEntry entry(topicId, infoId); - entry.mActorName = actorName; + JournalEntry entry(topicId, infoId, actor); + entry.mActorName = actor.getClass().getName(actor); topic.addEntry (entry); } diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 00511f47c..17b764436 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -38,7 +38,7 @@ namespace MWDialogue virtual int getJournalIndex (const std::string& id) const; ///< Get the journal index. - virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName); + virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor); virtual TEntryIter begin() const; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index f7df305c7..ea39174b8 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -58,9 +58,4 @@ namespace MWDialogue { return mEntries.end(); } - - JournalEntry Topic::getEntry (const std::string& infoId) const - { - return JournalEntry (mTopic, infoId); - } } diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index 02fa6d524..cbff2296c 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -53,8 +53,6 @@ namespace MWDialogue TEntryIter end() const; ///< Iterator pointing past the end of the journal for this topic. - - JournalEntry getEntry (const std::string& infoId) const; }; } From a90245147bfc40994cc5bf7a0785077762f21755 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 16:00:02 +0200 Subject: [PATCH 032/226] Don't reset history when ForceGreeting is used and a dialogue window was already open for the same actor (Fixes #1423) --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 6 +++++- apps/openmw/mwgui/dialogue.cpp | 12 ++++++++---- apps/openmw/mwgui/dialogue.hpp | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index c1a623418..aca287854 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -140,7 +140,11 @@ namespace MWDialogue mActorKnownTopics.clear(); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->startDialogue(actor, actor.getClass().getName (actor)); + + // If the dialogue window was already open, keep the existing history + bool resetHistory = (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue)); + + win->startDialogue(actor, actor.getClass().getName (actor), resetHistory); //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI updateTopics(); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 441e31e32..e7dd74eee 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -366,10 +366,11 @@ namespace MWGui } } - void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) + void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory) { mGoodbye = false; mEnabled = true; + bool sameActor = (mPtr == actor); mPtr = actor; mTopicsList->setEnabled(true); setTitle(npcName); @@ -378,9 +379,12 @@ namespace MWGui mTopicsList->clear(); - for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) - delete (*it); - mHistoryContents.clear(); + if (resetHistory || !sameActor) + { + for (std::vector::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) + delete (*it); + mHistoryContents.clear(); + } for (std::vector::iterator it = mLinks.begin(); it != mLinks.end(); ++it) delete (*it); diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index fcb1338b5..4e0ca5dde 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -111,7 +111,7 @@ namespace MWGui void notifyLinkClicked (TypesetBook::InteractiveId link); - void startDialogue(MWWorld::Ptr actor, std::string npcName); + void startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory); void setKeywords(std::list keyWord); void addResponse (const std::string& text, const std::string& title=""); From 2dd54dbcfcef832ce2c3fe49f31cecf741e316f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 16:36:22 +0200 Subject: [PATCH 033/226] Implement ClearInfoActor script instruction (Fixes #1422) --- apps/openmw/mwbase/dialoguemanager.hpp | 3 +++ apps/openmw/mwbase/journal.hpp | 5 +++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 9 +++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 5 ++++- apps/openmw/mwdialogue/journalimp.cpp | 10 ++++++++++ apps/openmw/mwdialogue/journalimp.hpp | 5 +++++ apps/openmw/mwdialogue/topic.cpp | 13 +++++++++++++ apps/openmw/mwdialogue/topic.hpp | 2 ++ apps/openmw/mwscript/dialogueextensions.cpp | 14 ++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 4 +++- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 12 files changed, 71 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index dfb002cfc..d0e64b23c 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -76,6 +76,9 @@ namespace MWBase /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0; + + /// Removes the last added topic response for the given actor from the journal + virtual void clearInfoActor (const MWWorld::Ptr& actor) const = 0; }; } diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index c43ebeb8f..938cec74b 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -60,6 +60,11 @@ namespace MWBase ///< Get the journal index. virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) = 0; + /// \note topicId must be lowercase + + virtual void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName) = 0; + ///< Removes the last topic response added for the given topicId and actor name. + /// \note topicId must be lowercase virtual TEntryIter begin() const = 0; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index aca287854..aa7df1fd4 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -698,6 +698,15 @@ namespace MWDialogue return diff; } + void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const + { + if (actor == mActor && !mLastTopic.empty()) + { + MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse( + mLastTopic, actor.getClass().getName(actor)); + } + } + std::vector ParseHyperText(const std::string& text) { std::vector result; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 94f8f3ec1..6553ddc01 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -40,7 +40,7 @@ namespace MWDialogue bool mTalkedTo; int mChoice; - std::string mLastTopic; + std::string mLastTopic; // last topic ID, lowercase bool mIsInChoice; float mTemporaryDispositionChange; @@ -99,6 +99,9 @@ namespace MWDialogue /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const; + + /// Removes the last added topic response for the given actor from the journal + virtual void clearInfoActor (const MWWorld::Ptr& actor) const; }; diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 1429415e5..9497347e3 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -113,6 +113,16 @@ namespace MWDialogue topic.addEntry (entry); } + void Journal::removeLastAddedTopicResponse(const std::string &topicId, const std::string &actorName) + { + Topic& topic = getTopic (topicId); + + topic.removeLastAddedResponse(actorName); + + if (topic.begin() == topic.end()) + mTopics.erase(mTopics.find(topicId)); // All responses removed -> remove topic + } + int Journal::getJournalIndex (const std::string& id) const { TQuestContainer::const_iterator iter = mQuests.find (id); diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 17b764436..d15b909fa 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -39,6 +39,11 @@ namespace MWDialogue ///< Get the journal index. virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor); + /// \note topicId must be lowercase + + virtual void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName); + ///< Removes the last topic response added for the given topicId and actor name. + /// \note topicId must be lowercase virtual TEntryIter begin() const; ///< Iterator pointing to the begin of the main journal. diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index ea39174b8..c1a45f841 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -58,4 +58,17 @@ namespace MWDialogue { return mEntries.end(); } + + void Topic::removeLastAddedResponse (const std::string& actorName) + { + for (std::vector::reverse_iterator it = mEntries.rbegin(); + it != mEntries.rend(); ++it) + { + if (it->mActorName == actorName) + { + mEntries.erase( (++it).base() ); // erase doesn't take a reverse_iterator + return; + } + } + } } diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index cbff2296c..72486ef8a 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -48,6 +48,8 @@ namespace MWDialogue virtual std::string getName() const; + void removeLastAddedResponse (const std::string& actorName); + TEntryIter begin() const; ///< Iterator pointing to the begin of the journal for this topic. diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 9dde65ab2..45eeccf18 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -235,6 +235,18 @@ namespace MWScript } }; + template + class OpClearInfoActor : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + MWBase::Environment::get().getDialogueManager()->clearInfoActor(ptr); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -256,6 +268,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction); + interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor); + interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor); } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 24b0b6f7a..91d0f031f 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -393,5 +393,7 @@ op 0x2000241: onKnockoutExplicit op 0x2000242: ModFactionReaction op 0x2000243: GetFactionReaction op 0x2000244: Activate, explicit +op 0x2000245: ClearInfoActor +op 0x2000246: ClearInfoActor, explicit -opcodes 0x2000245-0x3ffffff unused +opcodes 0x2000247-0x3ffffff unused diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 0f726a52d..61464e5c2 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -181,6 +181,7 @@ namespace Compiler opcodeSameFactionExplicit); extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction); extensions.registerFunction("getfactionreaction", 'l', "ccl", opcodeGetFactionReaction); + extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 8796c53c5..4b22e4b9c 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -154,6 +154,8 @@ namespace Compiler const int opcodeSameFactionExplicit = 0x20001b6; const int opcodeModFactionReaction = 0x2000242; const int opcodeGetFactionReaction = 0x2000243; + const int opcodeClearInfoActor = 0x2000245; + const int opcodeClearInfoActorExplicit = 0x2000246; } namespace Gui From 6ba112619ae3362c91ddfc180ae034f49f044eaa Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 16:46:13 +0200 Subject: [PATCH 034/226] Fix dropped items ending up inaccessible when standing in objects with no collision (Fixes #1441) --- apps/openmw/mwworld/worldimp.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ebdac7ba1..bf752734f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1703,14 +1703,15 @@ namespace MWWorld Ogre::Vector3 orig = Ogre::Vector3(pos.pos); + orig.z += 20; Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); - float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; - len += 100.0; + float len = 100.0; std::pair hit = mPhysics->castRay(orig, dir, len); - pos.pos[2] = hit.second.z; + if (hit.first) + pos.pos[2] = hit.second.z; // copy the object and set its count int origCount = object.getRefData().getCount(); From 3788fb042e4b739892c347183a7d148fb0bf47be Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 17:47:59 +0200 Subject: [PATCH 035/226] Implement MenuTest script instruction (Fixes #1454) --- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 23 +++++++++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ apps/openmw/mwgui/windowpinnablebase.cpp | 6 ++++ apps/openmw/mwgui/windowpinnablebase.hpp | 1 + apps/openmw/mwscript/docs/vmformat.txt | 3 +- apps/openmw/mwscript/guiextensions.cpp | 42 ++++++++++++++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 1 + 9 files changed, 80 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 2dfa50eb5..0f45542ba 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -328,6 +328,8 @@ namespace MWBase /** Used when one Modal adds another Modal \param input Pointer to the current modal, to ensure proper modal is removed **/ virtual void removeCurrentModal(MWGui::WindowModal* input) = 0; + + virtual void pinWindow (MWGui::GuiWindow window) = 0; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7ebdb6a83..b49bfbfa6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1646,4 +1646,27 @@ namespace MWGui if (_key == MyGUI::KeyCode::Escape) mVideoWidget->stop(); } + + void WindowManager::pinWindow(GuiWindow window) + { + switch (window) + { + case GW_Inventory: + mInventoryWindow->setPinned(true); + break; + case GW_Map: + mMap->setPinned(true); + break; + case GW_Magic: + mSpellWindow->setPinned(true); + break; + case GW_Stats: + mStatsWindow->setPinned(true); + break; + default: + break; + } + + updateVisible(); + } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index aee6cef47..dd41635ad 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -319,6 +319,8 @@ namespace MWGui \param input Pointer to the current modal, to ensure proper modal is removed **/ virtual void removeCurrentModal(WindowModal* input); + virtual void pinWindow (MWGui::GuiWindow window); + private: bool mConsoleOnlyScripts; diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp index 47364337c..919d315f2 100644 --- a/apps/openmw/mwgui/windowpinnablebase.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -25,6 +25,12 @@ namespace MWGui onPinToggled(); } + void WindowPinnableBase::setPinned(bool pinned) + { + if (pinned != mPinned) + onPinButtonClicked(mPinButton); + } + void WindowPinnableBase::setPinButtonVisible(bool visible) { mPinButton->setVisible(visible); diff --git a/apps/openmw/mwgui/windowpinnablebase.hpp b/apps/openmw/mwgui/windowpinnablebase.hpp index cd393f918..3aad60988 100644 --- a/apps/openmw/mwgui/windowpinnablebase.hpp +++ b/apps/openmw/mwgui/windowpinnablebase.hpp @@ -12,6 +12,7 @@ namespace MWGui public: WindowPinnableBase(const std::string& parLayout); bool pinned() { return mPinned; } + void setPinned (bool pinned); void setPinButtonVisible(bool visible); private: diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 91d0f031f..1576bf0af 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -57,7 +57,8 @@ op 0x20028: RemoveSoulGem, explicit reference op 0x20029: PCRaiseRank, explicit reference op 0x2002a: PCLowerRank, explicit reference op 0x2002b: PCJoinFaction, explicit reference -opcodes 0x2002c-0x3ffff unused +op 0x2002c: MenuTest +opcodes 0x2002d-0x3ffff unused Segment 4: (not implemented yet) diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index be241a564..333be5be6 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -162,6 +162,47 @@ namespace MWScript } }; + class OpMenuTest : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + int arg=0; + if(arg0>0) + { + arg = runtime[0].mInteger; + runtime.pop(); + } + + + if (arg == 0) + { + MWGui::GuiMode modes[] = { MWGui::GM_Inventory, MWGui::GM_Container }; + + for (int i=0; i<2; ++i) + { + if (MWBase::Environment::get().getWindowManager()->containsMode(modes[i])) + MWBase::Environment::get().getWindowManager()->removeGuiMode(modes[i]); + } + } + else + { + MWGui::GuiWindow gw = MWGui::GW_None; + if (arg == 3) + gw = MWGui::GW_Stats; + if (arg == 4) + gw = MWGui::GW_Inventory; + if (arg == 5) + gw = MWGui::GW_Magic; + if (arg == 6) + gw = MWGui::GW_Map; + + MWBase::Environment::get().getWindowManager()->pinWindow(gw); + } + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -200,6 +241,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap); interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap); + interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 61464e5c2..b9e36db80 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -216,6 +216,7 @@ namespace Compiler extensions.registerInstruction ("showmap", "S", opcodeShowMap); extensions.registerInstruction ("fillmap", "", opcodeFillMap); + extensions.registerInstruction ("menutest", "/l", opcodeMenuTest); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 4b22e4b9c..14f4db060 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -177,6 +177,7 @@ namespace Compiler const int opcodeToggleFullHelp = 0x2000151; const int opcodeShowMap = 0x20001a0; const int opcodeFillMap = 0x20001a1; + const int opcodeMenuTest = 0x2002c; } namespace Misc From 5a955279bb3b3f2f9e3bbadf67454cb91a040589 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 18:29:20 +0200 Subject: [PATCH 036/226] Fix main menu background showing when resizing window during load --- apps/openmw/mwgui/mainmenu.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 1d47d3b76..ca7e877cc 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -68,10 +68,10 @@ namespace MWGui { if (visible) updateMenu(); - else - showBackground( - MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && - MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); + + showBackground( + MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && + MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); OEngine::GUI::Layout::setVisible (visible); } @@ -220,7 +220,6 @@ namespace MWGui MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); - showBackground(state == MWBase::StateManager::State_NoGame); mVersionText->setVisible(state == MWBase::StateManager::State_NoGame); std::vector buttons; From 7b5482f25bad75275e14be4b67422303e7cf9da9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 18:31:39 +0200 Subject: [PATCH 037/226] Fix character selection caption when there is no character in settings.cfg --- apps/openmw/mwgui/savegamedialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index caa0cbb36..a35415e75 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -163,6 +163,8 @@ namespace MWGui } mCharacterSelection->setIndexSelected(selectedIndex); + if (selectedIndex == MyGUI::ITEM_NONE) + mCharacterSelection->setCaption("Select Character ..."); fillSaveList(); From 47172fb8a2945b8703175c5a8b2bdd74d0b525ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 19:23:42 +0200 Subject: [PATCH 038/226] ContentModel: Don't confuse file path with file name (Fixes #1352) --- .../contentselector/model/contentmodel.cpp | 31 ++++++++++--------- .../contentselector/model/contentmodel.hpp | 4 +-- components/contentselector/model/esmfile.hpp | 2 ++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 0d274474c..f5bc2369a 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -239,7 +239,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const return false; EsmFile *file = item(index.row()); - QString fileName = file->filePath(); + QString fileName = file->fileName(); bool success = false; switch(role) @@ -266,7 +266,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const if (success) { - success = setCheckState(fileName, value.toBool()); + success = setCheckState(file->filePath(), value.toBool()); emit dataChanged(index, index); } } @@ -277,19 +277,19 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const int checkValue = value.toInt(); bool success = false; bool setState = false; - if ((checkValue==Qt::Checked) && !isChecked(fileName)) + if ((checkValue==Qt::Checked) && !isChecked(file->filePath())) { setState = true; success = true; } - else if ((checkValue == Qt::Checked) && isChecked (fileName)) + else if ((checkValue == Qt::Checked) && isChecked (file->filePath())) setState = true; else if (checkValue == Qt::Unchecked) setState = true; if (setState) { - setCheckState(fileName, success); + setCheckState(file->filePath(), success); emit dataChanged(index, index); } @@ -517,10 +517,10 @@ void ContentSelectorModel::ContentModel::sortFiles() } } -bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const +bool ContentSelectorModel::ContentModel::isChecked(const QString& filepath) const { - if (mCheckStates.contains(name)) - return (mCheckStates[name] == Qt::Checked); + if (mCheckStates.contains(filepath)) + return (mCheckStates[filepath] == Qt::Checked); return false; } @@ -543,12 +543,12 @@ void ContentSelectorModel::ContentModel::refreshModel() emit dataChanged (index(0,0), index(rowCount()-1,0)); } -bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) +bool ContentSelectorModel::ContentModel::setCheckState(const QString &filepath, bool checkState) { - if (name.isEmpty()) + if (filepath.isEmpty()) return false; - const EsmFile *file = item(name); + const EsmFile *file = item(filepath); if (!file) return false; @@ -558,8 +558,8 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool if (checkState) state = Qt::Checked; - mCheckStates[name] = state; - emit dataChanged(indexFromItem(item(name)), indexFromItem(item(name))); + mCheckStates[filepath] = state; + emit dataChanged(indexFromItem(item(filepath)), indexFromItem(item(filepath))); if (file->isGameFile()) refreshModel(); @@ -586,7 +586,10 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool { foreach (const EsmFile *downstreamFile, mFiles) { - if (downstreamFile->gameFiles().contains(name)) + QFileInfo fileInfo(filepath); + QString filename = fileInfo.fileName(); + + if (downstreamFile->gameFiles().contains(filename)) { if (mCheckStates.contains(downstreamFile->filePath())) mCheckStates[downstreamFile->filePath()] = Qt::Unchecked; diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 8c8c2124b..6d147bce9 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -45,8 +45,8 @@ namespace ContentSelectorModel const EsmFile *item(const QString &name) const; bool isEnabled (QModelIndex index) const; - bool isChecked(const QString &name) const; - bool setCheckState(const QString &name, bool isChecked); + bool isChecked(const QString &filepath) const; + bool setCheckState(const QString &filepath, bool isChecked); void setCheckStates (const QStringList &fileList, bool isChecked); ContentFileList checkedItems() const; void uncheckAll(); diff --git a/components/contentselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp index ca24b52d1..7e1edcaba 100644 --- a/components/contentselector/model/esmfile.hpp +++ b/components/contentselector/model/esmfile.hpp @@ -53,6 +53,8 @@ namespace ContentSelectorModel inline QDateTime modified() const { return mModified; } inline float format() const { return mFormat; } inline QString filePath() const { return mPath; } + + /// @note Contains file names, not paths. inline const QStringList &gameFiles() const { return mGameFiles; } inline QString description() const { return mDescription; } inline QString toolTip() const { return sToolTip.arg(mAuthor) From 07d20c212bcd7dbe162e63de662f2928e95bfbeb Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jun 2014 21:34:47 +0200 Subject: [PATCH 039/226] Fix crash activating quick key 1 --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index f52a4b941..328ef21fb 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -247,7 +247,7 @@ namespace MWGui void QuickKeysMenu::activateQuickKey(int index) { - assert (index-1 > 0); + assert (index-1 >= 0); ItemWidget* button = mQuickKeyButtons[index-1]; QuickKeyType type = mAssigned[index-1]; From d6d9df6cec4c3c5e1dc02bc756f4174243caff56 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 11 Jun 2014 00:20:46 +0400 Subject: [PATCH 040/226] split getStartTime --- apps/openmw/mwmechanics/aicombat.cpp | 12 ++++++-- apps/openmw/mwrender/animation.cpp | 35 ++++++++++++++---------- apps/openmw/mwrender/animation.hpp | 5 +++- apps/openmw/mwrender/weaponanimation.cpp | 2 +- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 2218623da..4a3c724d5 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -785,15 +785,21 @@ void getMinMaxAttackDuration(const MWWorld::Ptr& actor, float (*fMinMaxDurations // get durations for each attack type for (int i = 0; i < (bRangedWeap ? 1 : 3); i++) { - float start1 = anim->getStartTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey, false); + float start1 = anim->getTextKeyTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey); + + if (start1 < 0) + { + fMinMaxDurations[i][0] = fMinMaxDurations[i][1] = 0.1f; + continue; + } textKey2 = "min attack"; - float start2 = anim->getStartTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2, false); + float start2 = anim->getTextKeyTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2); fMinMaxDurations[i][0] = (start2 - start1) / weapSpeed; textKey2 = "max attack"; - start1 = anim->getStartTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2, false); + start1 = anim->getTextKeyTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2); fMinMaxDurations[i][1] = fMinMaxDurations[i][0] + (start1 - start2) / weapSpeed; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 6c2746557..1c86fab89 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -903,27 +903,32 @@ bool Animation::getInfo(const std::string &groupname, float *complete, float *sp return true; } -float Animation::getStartTime(const std::string &groupname, bool onlyGroup) const +float Animation::getStartTime(const std::string &groupname) const { - AnimSourceList::const_iterator iter(mAnimSources.begin()); - for(;iter != mAnimSources.end();iter++) + for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) { const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; - if (onlyGroup) + + NifOgre::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); + if(found != keys.end()) + return found->first; + } + return -1.f; +} + +float Animation::getTextKeyTime(const std::string &textKey) const +{ + for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) + { + const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; + + for(NifOgre::TextKeyMap::const_iterator iterKey(keys.begin()); iterKey != keys.end(); ++iterKey) { - NifOgre::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); - if(found != keys.end()) - return found->first; - } - else - { - for(NifOgre::TextKeyMap::const_iterator iter(keys.begin()); iter != keys.end(); ++iter) - { - if(iter->second.compare(0, groupname.size(), groupname) == 0) - return iter->first; - } + if(iterKey->second.compare(0, textKey.size(), textKey) == 0) + return iterKey->first; } } + return -1.f; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2fc57f0e9..b2d69b79a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -275,7 +275,10 @@ public: bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; /// Get the absolute position in the animation track of the first text key with the given group. - float getStartTime(const std::string &groupname, bool onlyGroup) const; + float getStartTime(const std::string &groupname) const; + + /// Get the absolute position in the animation track of the text key + float getTextKeyTime(const std::string &textKey) const; /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. float getCurrentTime(const std::string& groupname) const; diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 3d4886cea..5f953bd83 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -32,7 +32,7 @@ float WeaponAnimationTime::getValue() const void WeaponAnimationTime::setGroup(const std::string &group) { mWeaponGroup = group; - mStartTime = mAnimation->getStartTime(mWeaponGroup, true); + mStartTime = mAnimation->getStartTime(mWeaponGroup); } void WeaponAnimationTime::updateStartTime() From a41339da1e9b4ff0f18090d6376b2563e1a7a0cc Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 10 Jun 2014 02:22:58 +0200 Subject: [PATCH 041/226] Write logs in log directory The crash.log file was created in the working directory, requiring users that had installed the game to run it with augmented privileges to be able to create the file. --- apps/openmw/main.cpp | 168 ++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 80 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 3098d953e..06db76fcf 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -7,16 +7,15 @@ #include #include "engine.hpp" -#if defined(_WIN32) && !defined(_CONSOLE) #include #include +#if defined(_WIN32) // For OutputDebugString #define WIN32_LEAN_AND_MEAN #include // makes __argc and __argv available on windows #include - #endif @@ -253,29 +252,92 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat return true; } +#if defined(_WIN32) && defined(_DEBUG) +class DebugOutput : public boost::iostreams::sink +{ +public: + std::streamsize write(const char *str, std::streamsize size) + { + // Make a copy for null termination + std::string tmp (str, size); + // Write string to Visual Studio Debug output + OutputDebugString (tmp.c_str ()); + return size; + } +}; +#else +class Tee : public boost::iostreams::sink +{ +public: + Tee(std::ostream &stream, std::ostream &stream2) + : out(stream), out2(stream2) + { + } + + std::streamsize write(const char *str, std::streamsize size) + { + out.write (str, size); + out.flush(); + out2.write (str, size); + out2.flush(); + return size; + } + +private: + std::ostream &out; + std::ostream &out2; +}; +#endif + int main(int argc, char**argv) { -#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE - // Unix crash catcher - if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached()) - { - int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; - cc_install_handlers(argc, argv, 5, s, "crash.log", NULL); - std::cout << "Installing crash catcher" << std::endl; - } - else - std::cout << "Running in a debugger, not installing crash catcher" << std::endl; -#endif - -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - // set current dir to bundle path - boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path(); - boost::filesystem::current_path(bundlePath); -#endif + std::streambuf* cout_rdbuf = std::cout.rdbuf (); + std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); + int ret = 0; try { Files::ConfigurationManager cfgMgr; + +#if defined(_WIN32) && defined(_DEBUG) + // Redirect cout and cerr to VS debug output when running in debug mode + boost::iostreams::stream_buffer sb; + sb.open(DebugOutput()); + std::cout.rdbuf (&sb); + std::cerr.rdbuf (&sb); +#else + // Redirect cout and cerr to openmw.log + std::ofstream logfile (std::string(cfgMgr.getLogPath().string() + "/openmw.log").c_str()); + + boost::iostreams::stream_buffer coutsb; + std::ostream oldcout(cout_rdbuf); + coutsb.open (Tee(logfile, oldcout)); + std::cout.rdbuf (&coutsb); + + boost::iostreams::stream_buffer cerrsb; + std::ostream oldcerr(cerr_rdbuf); + cerrsb.open (Tee(logfile, oldcerr)); + std::cerr.rdbuf (&cerrsb); +#endif + +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE + // Unix crash catcher + if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached()) + { + int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; + cc_install_handlers(argc, argv, 5, s, std::string(cfgMgr.getLogPath().string() + "/crash.log").c_str(), NULL); + std::cout << "Installing crash catcher" << std::endl; + } + else + std::cout << "Running in a debugger, not installing crash catcher" << std::endl; +#endif + +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + // set current dir to bundle path + boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path(); + boost::filesystem::current_path(bundlePath); +#endif + OMW::Engine engine(cfgMgr); if (parseOptions(argc, argv, engine, cfgMgr)) @@ -292,76 +354,22 @@ int main(int argc, char**argv) #endif SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); - return 1; + ret = 1; } - return 0; + // Restore cout and cerr + std::cout.rdbuf(cout_rdbuf); + std::cerr.rdbuf(cerr_rdbuf); + + return ret; } // Platform specific for Windows when there is no console built into the executable. // Windows will call the WinMain function instead of main in this case, the normal // main function is then called with the __argc and __argv parameters. -// In addition if it is a debug build it will redirect cout to the debug console in Visual Studio #if defined(_WIN32) && !defined(_CONSOLE) - -#if defined(_DEBUG) -class DebugOutput : public boost::iostreams::sink -{ -public: - std::streamsize write(const char *str, std::streamsize size) - { - // Make a copy for null termination - std::string tmp (str, size); - // Write string to Visual Studio Debug output - OutputDebugString (tmp.c_str ()); - return size; - } -}; -#else -class Logger : public boost::iostreams::sink -{ -public: - Logger(std::ofstream &stream) - : out(stream) - { - } - - std::streamsize write(const char *str, std::streamsize size) - { - out.write (str, size); - out.flush(); - return size; - } - -private: - std::ofstream &out; -}; -#endif - int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { - std::streambuf* old_rdbuf = std::cout.rdbuf (); - - int ret = 0; -#if defined(_DEBUG) - // Redirect cout to VS debug output when running in debug mode - { - boost::iostreams::stream_buffer sb; - sb.open(DebugOutput()); -#else - // Redirect cout to openmw.log - std::ofstream logfile ("openmw.log"); - { - boost::iostreams::stream_buffer sb; - sb.open (Logger (logfile)); -#endif - std::cout.rdbuf (&sb); - - ret = main (__argc, __argv); - - std::cout.rdbuf(old_rdbuf); - } - return ret; + return main(__argc, __argv); } - #endif From 14a9f0ebf8d54608057f8a40e632a5859b721eb3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 02:24:17 +0200 Subject: [PATCH 042/226] Handle Quadratic and Linear attenuation independently (Fixes #1456) --- apps/openmw/mwrender/animation.cpp | 36 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 9ed4cb6d3..dd900f79c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -356,28 +356,40 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScene objlist->mControllers.push_back(Ogre::Controller(src, dest, func)); bool interior = !(mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior()); - bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ? - !interior : fallback->getFallbackBool("LightAttenuation_UseQuadratic"); + + static bool outQuadInLin = fallback->getFallbackBool("LightAttenuation_OutQuadInLin"); + static bool useQuadratic = fallback->getFallbackBool("LightAttenuation_UseQuadratic"); + static float quadraticValue = fallback->getFallbackFloat("LightAttenuation_QuadraticValue"); + static float quadraticRadiusMult = fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); + static bool useLinear = fallback->getFallbackBool("LightAttenuation_UseLinear"); + static float linearRadiusMult = fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); + static float linearValue = fallback->getFallbackFloat("LightAttenuation_LinearValue"); + + bool quadratic = useQuadratic && (!outQuadInLin || !interior); + // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, // so we ignore lights if their attenuation falls below this factor. const float threshold = 0.03; - if (!quadratic) + float quadraticAttenuation = 0; + float linearAttenuation = 0; + float activationRange = 0; + if (quadratic) { - float r = radius * fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); - float attenuation = fallback->getFallbackFloat("LightAttenuation_LinearValue") / r; - float activationRange = 1.0f / (threshold * attenuation); - olight->setAttenuation(activationRange, 0, attenuation, 0); + float r = radius * quadraticRadiusMult; + quadraticAttenuation = quadraticValue / std::pow(r, 2); + activationRange = std::sqrt(1.0f / (threshold * quadraticAttenuation)); } - else + if (useLinear) { - float r = radius * fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); - float attenuation = fallback->getFallbackFloat("LightAttenuation_QuadraticValue") / std::pow(r, 2); - float activationRange = std::sqrt(1.0f / (threshold * attenuation)); - olight->setAttenuation(activationRange, 0, 0, attenuation); + float r = radius * linearRadiusMult; + linearAttenuation = linearValue / r; + activationRange = std::max(activationRange, 1.0f / (threshold * linearAttenuation)); } + olight->setAttenuation(activationRange, 0, linearAttenuation, quadraticAttenuation); + // If there's an AttachLight bone, attach the light to that, otherwise put it in the center, if(objlist->mSkelBase && objlist->mSkelBase->getSkeleton()->hasBone("AttachLight")) objlist->mSkelBase->attachObjectToBone("AttachLight", olight); From 0f31e310885cbb1777b96899daca15d0ef430890 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 03:08:22 +0200 Subject: [PATCH 043/226] Allow opening journal during dialogue (Fixes #1460) --- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 3d2f57bf6..bb18b5aa7 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -807,8 +807,9 @@ namespace MWInput if (MyGUI::InputManager::getInstance ().isModalAny()) return; - // Toggle between game mode and journal mode - if(!MWBase::Environment::get().getWindowManager()->isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) + if((!MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue) + && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); @@ -817,7 +818,6 @@ namespace MWInput { MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } - // .. but don't touch any other mode. } void InputManager::quickKey (int index) From 7d1ecea20c9e3c2cac2168f961c8a78b00bf5d2b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 11 Jun 2014 11:43:38 +0200 Subject: [PATCH 044/226] added extended versions of revert and delete --- apps/opencs/model/world/commanddispatcher.cpp | 102 ++++++++++++++++++ apps/opencs/model/world/commanddispatcher.hpp | 16 +++ apps/opencs/view/world/table.cpp | 26 +++++ apps/opencs/view/world/table.hpp | 2 + 4 files changed, 146 insertions(+) diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index ce6d1e398..4e146d87c 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -1,6 +1,10 @@ #include "commanddispatcher.hpp" +#include + +#include + #include "../doc/document.hpp" #include "idtable.hpp" @@ -88,6 +92,13 @@ void CSMWorld::CommandDispatcher::setEditLock (bool locked) void CSMWorld::CommandDispatcher::setSelection (const std::vector& selection) { mSelection = selection; + std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::toLower); + std::sort (mSelection.begin(), mSelection.end()); +} + +void CSMWorld::CommandDispatcher::setExtendedTypes (const std::vector& types) +{ + mExtendedTypes = types; } bool CSMWorld::CommandDispatcher::canDelete() const @@ -106,6 +117,20 @@ bool CSMWorld::CommandDispatcher::canRevert() const return getRevertableRecords().size()!=0; } +std::vector CSMWorld::CommandDispatcher::getExtendedTypes() const +{ + std::vector tables; + + if (mId==UniversalId::Type_Cells) + { + tables.push_back (mId); + tables.push_back (UniversalId::Type_References); + /// \todo add other cell-specific types + } + + return tables; +} + void CSMWorld::CommandDispatcher::executeDelete() { if (mLocked) @@ -163,3 +188,80 @@ void CSMWorld::CommandDispatcher::executeRevert() if (rows.size()>1) mDocument.getUndoStack().endMacro(); } + +void CSMWorld::CommandDispatcher::executeExtendedDelete() +{ + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records")); + + for (std::vector::const_iterator iter (mExtendedTypes.begin()); + iter!=mExtendedTypes.end(); ++iter) + { + if (*iter==mId) + executeDelete(); + else if (*iter==UniversalId::Type_References) + { + IdTable& model = dynamic_cast ( + *mDocument.getData().getTableModel (*iter)); + + const RefCollection& collection = mDocument.getData().getReferences(); + + int size = collection.getSize(); + + for (int i=size-1; i>=0; --i) + { + const Record& record = collection.getRecord (i); + + if (record.mState==RecordBase::State_Deleted) + continue; + + if (!std::binary_search (mSelection.begin(), mSelection.end(), + Misc::StringUtils::lowerCase (record.get().mCell))) + continue; + + mDocument.getUndoStack().push ( + new CSMWorld::DeleteCommand (model, record.get().mId)); + } + } + } + + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().endMacro(); +} + +void CSMWorld::CommandDispatcher::executeExtendedRevert() +{ + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records")); + + for (std::vector::const_iterator iter (mExtendedTypes.begin()); + iter!=mExtendedTypes.end(); ++iter) + { + if (*iter==mId) + executeRevert(); + else if (*iter==UniversalId::Type_References) + { + IdTable& model = dynamic_cast ( + *mDocument.getData().getTableModel (*iter)); + + const RefCollection& collection = mDocument.getData().getReferences(); + + int size = collection.getSize(); + + for (int i=size-1; i>=0; --i) + { + const Record& record = collection.getRecord (i); + + if (!std::binary_search (mSelection.begin(), mSelection.end(), + Misc::StringUtils::lowerCase (record.get().mCell))) + continue; + + mDocument.getUndoStack().push ( + new CSMWorld::RevertCommand (model, record.get().mId)); + } + } + } + + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().endMacro(); +} \ No newline at end of file diff --git a/apps/opencs/model/world/commanddispatcher.hpp b/apps/opencs/model/world/commanddispatcher.hpp index 3bf81f068..50085b1a1 100644 --- a/apps/opencs/model/world/commanddispatcher.hpp +++ b/apps/opencs/model/world/commanddispatcher.hpp @@ -22,6 +22,7 @@ namespace CSMWorld CSMDoc::Document& mDocument; UniversalId mId; std::vector mSelection; + std::vector mExtendedTypes; std::vector getDeletableRecords() const; @@ -37,16 +38,31 @@ namespace CSMWorld void setSelection (const std::vector& selection); + void setExtendedTypes (const std::vector& types); + ///< Set record lists selected by the user for extended operations. + bool canDelete() const; bool canRevert() const; + /// Return IDs of the record collection that can also be affected when + /// operating on the record collection this dispatcher is used for. + /// + /// \note The returned collection contains the ID of the record collection this + /// dispatcher is used for. However if that record collection does not support + /// the extended mode, the returned vector will be empty instead. + std::vector getExtendedTypes() const; + public slots: void executeDelete(); void executeRevert(); + void executeExtendedDelete(); + + void executeExtendedRevert(); + }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 0fc7dd4b2..877fd51c0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -44,6 +44,10 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) mDispatcher->setSelection (records); + std::vector extendedTypes = mDispatcher->getExtendedTypes(); + + mDispatcher->setExtendedTypes (extendedTypes); + // create context menu QMenu menu (this); @@ -63,11 +67,21 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.addAction (mCreateAction); if (mDispatcher->canRevert()) + { menu.addAction (mRevertAction); + if (!extendedTypes.empty()) + menu.addAction (mExtendedRevertAction); + } + if (mDispatcher->canDelete()) + { menu.addAction (mDeleteAction); + if (!extendedTypes.empty()) + menu.addAction (mExtendedDeleteAction); + } + if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic) { /// \todo allow reordering of multiple rows @@ -203,6 +217,18 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); addAction (mPreviewAction); + /// \todo add a user option, that redirects the extended action to an input panel (in + /// the bottom bar) that lets the user select which record collections should be + /// modified. + + mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this); + connect (mExtendedDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedDelete())); + addAction (mExtendedDeleteAction); + + mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this); + connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert())); + addAction (mExtendedRevertAction); + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 54971fb66..255c430ea 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -46,6 +46,8 @@ namespace CSVWorld QAction *mMoveDownAction; QAction *mViewAction; QAction *mPreviewAction; + QAction *mExtendedDeleteAction; + QAction *mExtendedRevertAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; int mRecordStatusDisplay; From 73be45780585e1c3427a35b47c70a0b5e22fe26c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 15:37:05 +0200 Subject: [PATCH 045/226] Merge ESM::Cell fields by subrecord Fixes an issue with the Morrowind Patched mod where cell ambient values would become black due to the new cell records not including an AMBI subrecord. Also fixes a bug where mLeasedRefs was incorrectly cleared when overwriting a cell (*oldcell = *cell;) --- apps/openmw/mwworld/store.cpp | 87 ++++++++++++++++++++--------------- apps/openmw/mwworld/store.hpp | 2 + components/esm/loadcell.cpp | 55 +++++++++------------- components/esm/loadcell.hpp | 12 ++--- 4 files changed, 82 insertions(+), 74 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 1156cbc15..19b204073 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -3,22 +3,8 @@ namespace MWWorld { - -void Store::load(ESM::ESMReader &esm, const std::string &id) +void Store::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) { - // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, - // and we merge all this data into one Cell object. However, we can't simply search for the cell id, - // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they - // are not available until both cells have been loaded! So first, proceed as usual. - - // All cells have a name record, even nameless exterior cells. - std::string idLower = Misc::StringUtils::lowerCase(id); - ESM::Cell *cell = new ESM::Cell; - cell->mName = id; - - //First part of cell loading - cell->preLoad(esm); - //Handling MovedCellRefs, there is no way to do it inside loadcell while (esm.isNextSub("MVRF")) { ESM::CellRef ref; @@ -43,35 +29,56 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) else *iter = ref; } +} - //Second part of cell loading - cell->postLoad(esm); +void Store::load(ESM::ESMReader &esm, const std::string &id) +{ + // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, + // and we merge all this data into one Cell object. However, we can't simply search for the cell id, + // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they + // are not available until both cells have been loaded at least partially! - if(cell->mData.mFlags & ESM::Cell::Interior) + // All cells have a name record, even nameless exterior cells. + std::string idLower = Misc::StringUtils::lowerCase(id); + ESM::Cell cell; + cell.mName = id; + + // Load the (x,y) coordinates of the cell, if it is an exterior cell, + // so we can find the cell we need to merge with + cell.loadData(esm); + + if(cell.mData.mFlags & ESM::Cell::Interior) { // Store interior cell by name, try to merge with existing parent data. ESM::Cell *oldcell = const_cast(search(idLower)); if (oldcell) { - // push the new references on the list of references to manage - oldcell->mContextList.push_back(cell->mContextList.at(0)); - // copy list into new cell - cell->mContextList = oldcell->mContextList; - // have new cell replace old cell - ESM::Cell::merge(oldcell, cell); + // merge new cell into old cell + // push the new references on the list of references to manage (saveContext = true) + oldcell->mData = cell.mData; + oldcell->loadCell(esm, true); } else - mInt[idLower] = *cell; + { + // spawn a new cell + cell.loadCell(esm, true); + + mInt[idLower] = cell; + } } else { // Store exterior cells by grid position, try to merge with existing parent data. - ESM::Cell *oldcell = const_cast(search(cell->getGridX(), cell->getGridY())); + ESM::Cell *oldcell = const_cast(search(cell.getGridX(), cell.getGridY())); if (oldcell) { - // push the new references on the list of references to manage - oldcell->mContextList.push_back(cell->mContextList.at(0)); - // copy list into new cell - cell->mContextList = oldcell->mContextList; + // merge new cell into old cell + // push the new references on the list of references to manage (saveContext = true) + oldcell->mData = cell.mData; + oldcell->loadCell(esm, true); + + // handle moved ref (MVRF) subrecords + handleMovedCellRefs (esm, &cell); + // merge lists of leased references, use newer data in case of conflict - for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); ++it) { + for (ESM::MovedCellRefTracker::const_iterator it = cell.mMovedRefs.begin(); it != cell.mMovedRefs.end(); ++it) { // remove reference from current leased ref tracker and add it to new cell ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum); if (itold != oldcell->mMovedRefs.end()) { @@ -82,13 +89,21 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) *itold = *it; } } - cell->mMovedRefs = oldcell->mMovedRefs; - // have new cell replace old cell - ESM::Cell::merge(oldcell, cell); + + // We don't need to merge mLeasedRefs of cell / oldcell. This list is filled when another cell moves a + // reference to this cell, so the list for the new cell should be empty. The list for oldcell, + // however, could have leased refs in it and so should be kept. } else - mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell; + { + // spawn a new cell + cell.loadCell(esm, true); + + // handle moved ref (MVRF) subrecords + handleMovedCellRefs (esm, &cell); + + mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell; + } } - delete cell; } } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 1dfb2f976..a04267f49 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -591,6 +591,8 @@ namespace MWWorld return search(cell.mName); } + void handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell); + public: ESMStore *mEsmStore; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 0830c5de6..83864569f 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -50,18 +50,14 @@ namespace ESM return ref.mRefNum == refNum; } - void Cell::load(ESMReader &esm, bool saveContext) { - // Ignore this for now, it might mean we should delete the entire - // cell? - // TODO: treat the special case "another plugin moved this ref, but we want to delete it"! - if (esm.isNextSub("DELE")) { - esm.skipHSub(); - } - - esm.getHNT(mData, "DATA", 12); + loadData(esm); + loadCell(esm, saveContext); +} +void Cell::loadCell(ESMReader &esm, bool saveContext) +{ mNAM0 = 0; if (mData.mFlags & Interior) @@ -73,12 +69,10 @@ void Cell::load(ESMReader &esm, bool saveContext) esm.getHT(waterl); mWater = (float) waterl; mWaterInt = true; - mHasWaterLevelRecord = true; } else if (esm.isNextSub("WHGT")) { esm.getHT(mWater); - mHasWaterLevelRecord = true; } // Quasi-exterior cells have a region (which determines the @@ -107,6 +101,18 @@ void Cell::load(ESMReader &esm, bool saveContext) } } +void Cell::loadData(ESMReader &esm) +{ + // Ignore this for now, it might mean we should delete the entire + // cell? + // TODO: treat the special case "another plugin moved this ref, but we want to delete it"! + if (esm.isNextSub("DELE")) { + esm.skipHSub(); + } + + esm.getHNT(mData, "DATA", 12); +} + void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool { this->load(esm, false); @@ -124,14 +130,12 @@ void Cell::save(ESMWriter &esm) const esm.writeHNT("DATA", mData, 12); if (mData.mFlags & Interior) { - if (mHasWaterLevelRecord) { - if (mWaterInt) { - int water = - (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); - esm.writeHNT("INTV", water); - } else { - esm.writeHNT("WHGT", mWater); - } + if (mWaterInt) { + int water = + (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); + esm.writeHNT("INTV", water); + } else { + esm.writeHNT("WHGT", mWater); } if (mData.mFlags & QuasiEx) @@ -228,19 +232,6 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) mAmbi.mFogDensity = 0; } - void Cell::merge(Cell *original, Cell *modified) - { - float waterLevel = original->mWater; - if (modified->mHasWaterLevelRecord) - { - waterLevel = modified->mWater; - } - // else: keep original water level, instead of resetting to 0 - - *original = *modified; - original->mWater = waterLevel; - } - CellId Cell::getCellId() const { CellId id; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 28204c9ee..fb4b6b28a 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -78,10 +78,7 @@ struct Cell float mFogDensity; }; - Cell() : mWater(0), mHasWaterLevelRecord(false) {} - - /// Merge \a modified into \a original - static void merge (Cell* original, Cell* modified); + Cell() : mWater(0) {} // Interior cells are indexed by this (it's the 'id'), for exterior // cells it is optional. @@ -93,8 +90,8 @@ struct Cell std::vector mContextList; // File position; multiple positions for multiple plugin support DATAstruct mData; AMBIstruct mAmbi; + float mWater; // Water level - bool mHasWaterLevelRecord; bool mWaterInt; int mMapColor; int mNAM0; @@ -109,7 +106,10 @@ struct Cell // This method is left in for compatibility with esmtool. Parsing moved references currently requires // passing ESMStore, bit it does not know about this parameter, so we do it this way. - void load(ESMReader &esm, bool saveContext = true); + void load(ESMReader &esm, bool saveContext = true); // Load everything (except references) + void loadData(ESMReader &esm); // Load DATAstruct only + void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except DATAstruct and references + void save(ESMWriter &esm) const; bool isExterior() const From d60df66811a53e130fc03128c64954c1bee533aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 16:17:48 +0200 Subject: [PATCH 046/226] Change openmw.log to boost ofstream to fix unicode path on windows (see https://github.com/OpenMW/openmw/pull/108) --- apps/openmw/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 338eb2a7c..503ccaf25 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -10,6 +10,7 @@ #include #include +#include #if defined(_WIN32) // For OutputDebugString @@ -309,7 +310,8 @@ int main(int argc, char**argv) std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to openmw.log - std::ofstream logfile (std::string(cfgMgr.getLogPath().string() + "/openmw.log").c_str()); + boost::filesystem::ofstream logfile (boost::filesystem::path( + cfgMgr.getLogPath() / "/openmw.log")); boost::iostreams::stream_buffer coutsb; std::ostream oldcout(cout_rdbuf); From d970cc06d72bde012ffbcf315424c4e94d7cea1a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 18:10:58 +0200 Subject: [PATCH 047/226] Don't play the same music track twice in a row (Fixes #746) --- apps/openmw/mwsound/soundmanagerimp.cpp | 28 +++++++++++++++++++------ apps/openmw/mwsound/soundmanagerimp.hpp | 4 ++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 4a3093b10..3f85059ce 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -179,6 +179,7 @@ namespace MWSound if(!mOutput->isInitialized()) return; std::cout <<"Playing "<begin(), resourcesInThisGroup->end()); + Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); + for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) + { + Ogre::StringVectorPtr resourcesInThisGroup = mResourceMgr.findResourceNames(*it, + "Music/"+mCurrentPlaylist+"/*"); + filelist.insert(filelist.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); + } + mMusicFiles[mCurrentPlaylist] = filelist; } + else + filelist = mMusicFiles[mCurrentPlaylist]; if(!filelist.size()) return; int i = rand()%filelist.size(); + + // Don't play the same music track twice in a row + if (filelist[i] == mLastPlayedMusic) + { + if (i-1 == int(filelist.size())) + i = 0; + else + ++i; + } + streamMusicFull(filelist[i]); } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index ab9dcf734..558b6966a 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -31,6 +31,10 @@ namespace MWSound std::auto_ptr mOutput; + // Caches available music tracks by + std::map mMusicFiles; + std::string mLastPlayedMusic; // The music file that was last played + float mMasterVolume; float mSFXVolume; float mMusicVolume; From 013916fca3c92841e8aad016e5c77b639ebed9d4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 18:59:51 +0200 Subject: [PATCH 048/226] Fix for broken physics in exteriors (Fixes #1446) The wrong function was being used to check the distance between old and new positions. It took the length of the vectors into account, which makes no sense for positions. The issue was only observed in exteriors, since most interiors are relatively close to the origin. --- apps/openmw/mwworld/physicssystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 0f6fdec19..73a704c88 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -304,7 +304,7 @@ namespace MWWorld continue; // velocity updated, calculate nextpos again } - if(!newPosition.positionCloses(nextpos, 0.00000001)) + if(newPosition.squaredDistance(nextpos) > 0.00000001*0.00000001) { // trace to where character would go if there were no obstructions tracer.doTrace(colobj, newPosition, nextpos, engine); From 41ab7329a8fac6b579addb11d69bc3483d104ec0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 19:47:42 +0200 Subject: [PATCH 049/226] Store keybindings as keycode, not keyname. Also use SDL_GetKeyName instead of a manually created map. Fixes #1202 Note: breaks compatibility with input.xml, so the filename was changed. --- apps/openmw/engine.cpp | 2 +- extern/oics/ICSInputControlSystem.cpp | 127 +----------------- extern/oics/ICSInputControlSystem.h | 4 - .../oics/ICSInputControlSystem_keyboard.cpp | 2 +- 4 files changed, 6 insertions(+), 129 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3647f8ccb..bda903d56 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -357,7 +357,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create input and UI first to set up a bootstrapping environment for // showing a loading screen and keeping the window responsive while doing so - std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input.xml").string(); + std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v1.xml").string(); bool keybinderUserExists = boost::filesystem::exists(keybinderUser); MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab); mEnvironment.setInputManager (input); diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp index a053bbab4..34d16ed2b 100644 --- a/extern/oics/ICSInputControlSystem.cpp +++ b/extern/oics/ICSInputControlSystem.cpp @@ -41,8 +41,6 @@ namespace ICS this->mActive = active; - this->fillSDLKeysMap(); - ICS_LOG("Channel count = " + ToString(channelCount) ); for(size_t i=0;i( getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); keyBinder.SetAttribute( "direction", "INCREASE" ); control.InsertEndChild(keyBinder); @@ -443,7 +438,7 @@ namespace ICS { TiXmlElement keyBinder( "KeyBinder" ); - keyBinder.SetAttribute( "key", keyCodeToString( + keyBinder.SetAttribute( "key", ToString( getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); keyBinder.SetAttribute( "direction", "DECREASE" ); control.InsertEndChild(keyBinder); @@ -806,128 +801,14 @@ namespace ICS mDetectingBindingControl = NULL; } - void InputControlSystem::fillSDLKeysMap() - { - mKeys["UNASSIGNED"]= SDLK_UNKNOWN; - mKeys["ESCAPE"]= SDLK_ESCAPE; - mKeys["1"]= SDLK_1; - mKeys["2"]= SDLK_2; - mKeys["3"]= SDLK_3; - mKeys["4"]= SDLK_4; - mKeys["5"]= SDLK_5; - mKeys["6"]= SDLK_6; - mKeys["7"]= SDLK_7; - mKeys["8"]= SDLK_8; - mKeys["9"]= SDLK_9; - mKeys["0"]= SDLK_0; - mKeys["MINUS"]= SDLK_MINUS; - mKeys["EQUALS"]= SDLK_EQUALS; - mKeys["BACK"]= SDLK_BACKSPACE; - mKeys["TAB"]= SDLK_TAB; - mKeys["Q"]= SDLK_q; - mKeys["W"]= SDLK_w; - mKeys["E"]= SDLK_e; - mKeys["R"]= SDLK_r; - mKeys["T"]= SDLK_t; - mKeys["Y"]= SDLK_y; - mKeys["U"]= SDLK_u; - mKeys["I"]= SDLK_i; - mKeys["O"]= SDLK_o; - mKeys["P"]= SDLK_p; - mKeys["LBRACKET"]= SDLK_LEFTBRACKET; - mKeys["RBRACKET"]= SDLK_RIGHTBRACKET; - mKeys["RETURN"]= SDLK_RETURN; - mKeys["LCONTROL"]= SDLK_LCTRL; - mKeys["A"]= SDLK_a; - mKeys["S"]= SDLK_s; - mKeys["D"]= SDLK_d; - mKeys["F"]= SDLK_f; - mKeys["G"]= SDLK_g; - mKeys["H"]= SDLK_h; - mKeys["J"]= SDLK_j; - mKeys["K"]= SDLK_k; - mKeys["L"]= SDLK_l; - mKeys["SEMICOLON"]= SDLK_SEMICOLON; - mKeys["APOSTROPHE"]= SDLK_QUOTE; - mKeys["GRAVE"]= SDLK_BACKQUOTE; - mKeys["LSHIFT"]= SDLK_LSHIFT; - mKeys["BACKSLASH"]= SDLK_BACKSLASH; - mKeys["Z"]= SDLK_z; - mKeys["X"]= SDLK_x; - mKeys["C"]= SDLK_c; - mKeys["V"]= SDLK_v; - mKeys["B"]= SDLK_b; - mKeys["N"]= SDLK_n; - mKeys["M"]= SDLK_m; - mKeys["COMMA"]= SDLK_COMMA; - mKeys["PERIOD"]= SDLK_PERIOD; - mKeys["SLASH"]= SDLK_SLASH; - mKeys["RSHIFT"]= SDLK_RSHIFT; - mKeys["MULTIPLY"]= SDLK_ASTERISK; - mKeys["LMENU"]= SDLK_LALT; - mKeys["SPACE"]= SDLK_SPACE; - mKeys["CAPITAL"]= SDLK_CAPSLOCK; - mKeys["F1"]= SDLK_F1; - mKeys["F2"]= SDLK_F2; - mKeys["F3"]= SDLK_F3; - mKeys["F4"]= SDLK_F4; - mKeys["F5"]= SDLK_F5; - mKeys["F6"]= SDLK_F6; - mKeys["F7"]= SDLK_F7; - mKeys["F8"]= SDLK_F8; - mKeys["F9"]= SDLK_F9; - mKeys["F10"]= SDLK_F10; - mKeys["F11"]= SDLK_F11; - mKeys["F12"]= SDLK_F12; - mKeys["NUMLOCK"]= SDLK_NUMLOCKCLEAR; - mKeys["SCROLL"]= SDLK_SCROLLLOCK; - mKeys["NUMPAD7"]= SDLK_KP_7; - mKeys["NUMPAD8"]= SDLK_KP_8; - mKeys["NUMPAD9"]= SDLK_KP_9; - mKeys["SUBTRACT"]= SDLK_KP_MINUS; - mKeys["NUMPAD4"]= SDLK_KP_4; - mKeys["NUMPAD5"]= SDLK_KP_5; - mKeys["NUMPAD6"]= SDLK_KP_6; - mKeys["ADD"]= SDLK_KP_PLUS; - mKeys["NUMPAD1"]= SDLK_KP_1; - mKeys["NUMPAD2"]= SDLK_KP_2; - mKeys["NUMPAD3"]= SDLK_KP_3; - mKeys["NUMPAD0"]= SDLK_KP_0; - mKeys["DECIMAL"]= SDLK_KP_DECIMAL; - mKeys["RCONTROL"]= SDLK_RCTRL; - mKeys["DIVIDE"]= SDLK_SLASH; - mKeys["SYSRQ"]= SDLK_SYSREQ; - mKeys["PRNTSCRN"] = SDLK_PRINTSCREEN; - mKeys["RMENU"]= SDLK_RALT; - mKeys["PAUSE"]= SDLK_PAUSE; - mKeys["HOME"]= SDLK_HOME; - mKeys["UP"]= SDLK_UP; - mKeys["PGUP"]= SDLK_PAGEUP; - mKeys["LEFT"]= SDLK_LEFT; - mKeys["RIGHT"]= SDLK_RIGHT; - mKeys["END"]= SDLK_END; - mKeys["DOWN"]= SDLK_DOWN; - mKeys["PGDOWN"]= SDLK_PAGEDOWN; - mKeys["INSERT"]= SDLK_INSERT; - mKeys["DELETE"]= SDLK_DELETE; - - mKeys["NUMPADENTER"]= SDLK_KP_ENTER; - - for(std::map::iterator it = mKeys.begin() - ; it != mKeys.end() ; it++) - { - mKeyCodes[ it->second ] = it->first; - } - } - std::string InputControlSystem::keyCodeToString(SDL_Keycode key) { - return mKeyCodes[key]; + return std::string(SDL_GetKeyName(key)); } SDL_Keycode InputControlSystem::stringToKeyCode(std::string key) { - return mKeys[key]; + return SDL_GetKeyFromName(key.c_str()); } void InputControlSystem::adjustMouseRegion(Uint16 width, Uint16 height) diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index 4aaecdea9..1e9c78e60 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -208,8 +208,6 @@ namespace ICS std::vector mChannels; ControlsKeyBinderMapType mControlsKeyBinderMap; - std::map mKeys; - std::map mKeyCodes; bool mActive; InputControlSystemLog* mLog; @@ -227,8 +225,6 @@ namespace ICS private: - void fillSDLKeysMap(); - Uint16 mClientWidth; Uint16 mClientHeight; }; diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp index 0a9a34d63..8e7415b5b 100644 --- a/extern/oics/ICSInputControlSystem_keyboard.cpp +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -43,7 +43,7 @@ namespace ICS dir = Control::DECREASE; } - addKeyBinding(mControls.back(), mKeys[xmlKeyBinder->Attribute("key")], dir); + addKeyBinding(mControls.back(), FromString(xmlKeyBinder->Attribute("key")), dir); xmlKeyBinder = xmlKeyBinder->NextSiblingElement("KeyBinder"); } From 68d6b6b2f3eeea910e6490a45d826db7fd43e8c3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 21:56:10 +0200 Subject: [PATCH 050/226] Cell merge fix (reference context position was not saved correctly) --- apps/openmw/mwworld/store.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 19b204073..fdeb290e5 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -70,13 +70,15 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) ESM::Cell *oldcell = const_cast(search(cell.getGridX(), cell.getGridY())); if (oldcell) { // merge new cell into old cell - // push the new references on the list of references to manage (saveContext = true) oldcell->mData = cell.mData; - oldcell->loadCell(esm, true); + oldcell->loadCell(esm, false); // handle moved ref (MVRF) subrecords handleMovedCellRefs (esm, &cell); + // push the new references on the list of references to manage + oldcell->postLoad(esm); + // merge lists of leased references, use newer data in case of conflict for (ESM::MovedCellRefTracker::const_iterator it = cell.mMovedRefs.begin(); it != cell.mMovedRefs.end(); ++it) { // remove reference from current leased ref tracker and add it to new cell @@ -96,11 +98,14 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) } else { // spawn a new cell - cell.loadCell(esm, true); + cell.loadCell(esm, false); // handle moved ref (MVRF) subrecords handleMovedCellRefs (esm, &cell); + // push the new references on the list of references to manage + cell.postLoad(esm); + mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell; } } From 37f0c253f6a922fdb202b04173b062d0aaa9a105 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 12 Jun 2014 07:04:57 +1000 Subject: [PATCH 051/226] For compiling with MSVC 2012 --- apps/openmw/mwstate/charactermanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 91d728ae0..70e9f0925 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -3,6 +3,7 @@ #include #include +#include // std::isalnum #include From 029e438c110808f8ee3105af9a664db8c6289397 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jun 2014 23:57:39 +0200 Subject: [PATCH 052/226] Don't check mInterpolationType each iteration --- components/nif/niffile.hpp | 51 ++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index daec80ea1..fbb64e4f7 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -177,42 +177,49 @@ struct KeyListT { KeyT key; NIFStream &nifReference = *nif; - for(size_t i = 0;i < count;i++) + + if(mInterpolationType == sLinearInterpolation) { - if(mInterpolationType == sLinearInterpolation) + for(size_t i = 0;i < count;i++) { readTimeAndValue(nifReference, key); mKeys.push_back(key); } - else if(mInterpolationType == sQuadraticInterpolation) + } + else if(mInterpolationType == sQuadraticInterpolation) + { + for(size_t i = 0;i < count;i++) { readQuadratic(nifReference, key); mKeys.push_back(key); } - else if(mInterpolationType == sTBCInterpolation) + } + else if(mInterpolationType == sTBCInterpolation) + { + for(size_t i = 0;i < count;i++) { readTBC(nifReference, key); mKeys.push_back(key); } - //XYZ keys aren't actually read here. - //data.hpp sees that the last type read was sXYZInterpolation and: - // Eats a floating point number, then - // Re-runs the read function 3 more times, with force enabled so that the previous values aren't cleared. - // When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation. - else if(mInterpolationType == sXYZInterpolation) - { - //Don't try to read XYZ keys into the wrong part - if ( count != 1 ) - nif->file->fail("XYZ_ROTATION_KEY count should always be '1' . Retrieved Value: "+Ogre::StringConverter::toString(count)); - } - else if (0 == mInterpolationType) - { - if (count != 0) - nif->file->fail("Interpolation type 0 doesn't work with keys"); - } - else - nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); } + //XYZ keys aren't actually read here. + //data.hpp sees that the last type read was sXYZInterpolation and: + // Eats a floating point number, then + // Re-runs the read function 3 more times, with force enabled so that the previous values aren't cleared. + // When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation. + else if(mInterpolationType == sXYZInterpolation) + { + //Don't try to read XYZ keys into the wrong part + if ( count != 1 ) + nif->file->fail("XYZ_ROTATION_KEY count should always be '1' . Retrieved Value: "+Ogre::StringConverter::toString(count)); + } + else if (0 == mInterpolationType) + { + if (count != 0) + nif->file->fail("Interpolation type 0 doesn't work with keys"); + } + else + nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); } private: From df7213185fd90ce93e69a33a0ce63e34e80af343 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 12 Jun 2014 23:42:33 +0400 Subject: [PATCH 053/226] warning fixes --- apps/openmw/mwmechanics/aicombat.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4a3c724d5..94bb2cd90 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -167,15 +167,18 @@ namespace MWMechanics const MWWorld::Class& actorClass = actor.getClass(); MWBase::World* world = MWBase::Environment::get().getWorld(); - if (!actorClass.isNpc() && target == world->getPlayerPtr() && - (actorClass.canSwim(actor) && !actor.getClass().canWalk(actor) // 1. pure water creature and Player moved out of water - && !world->isSwimming(target)) - || (!actorClass.canSwim(actor) && world->isSwimming(target))) // 2. creature can't swim to Player + if (!actorClass.isNpc() && + // 1. pure water creature and Player moved out of water + ((target == world->getPlayerPtr() && + actorClass.canSwim(actor) && !actor.getClass().canWalk(actor) && !world->isSwimming(target)) + // 2. creature can't swim to target + || (!actorClass.canSwim(actor) && world->isSwimming(target)))) { - actorClass.getCreatureStats(actor).setHostile(false); + if (target == world->getPlayerPtr()) + actorClass.getCreatureStats(actor).setHostile(false); actorClass.getCreatureStats(actor).setAttackingOrSpell(false); return true; - } + } //Update every frame if(mCombatMove) @@ -246,7 +249,7 @@ namespace MWMechanics const ESM::Weapon *weapon = NULL; MWMechanics::WeaponType weaptype; - float weapRange, weapSpeed = 1.0f; + float weapRange; actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); @@ -275,7 +278,6 @@ namespace MWMechanics // All other WeapTypes are actually weapons, so get is safe. weapon = weaponSlot->get()->mBase; weapRange = weapon->mData.mReach; - weapSpeed = weapon->mData.mSpeed; } weapRange *= 100.0f; } From b158919c4bfecf53e2a6f6adab04913e9d828c3e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 12 Jun 2014 15:11:52 +0200 Subject: [PATCH 054/226] Fix uninitialized variable use --- components/nif/niffile.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index fbb64e4f7..a9e2c6fe7 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -165,6 +165,9 @@ struct KeyListT { void read(NIFStream *nif, bool force=false) { assert(nif); + + mInterpolationType = 0; + size_t count = nif->getUInt(); if(count == 0 && !force) return; From 604d5ac00089a79e00541ab5a87cb4caec26619f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 12 Jun 2014 21:46:23 +0200 Subject: [PATCH 055/226] Fix console reference label not resetting when loading save --- apps/openmw/mwgui/console.cpp | 6 ++++++ apps/openmw/mwgui/console.hpp | 2 ++ apps/openmw/mwgui/referenceinterface.hpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 2ae4d6ed1..9f67524ae 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -435,4 +435,10 @@ namespace MWGui { setSelectedObject(MWWorld::Ptr()); } + + void Console::resetReference() + { + ReferenceInterface::resetReference(); + setSelectedObject(MWWorld::Ptr()); + } } diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index ec699b317..90da740c2 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -66,6 +66,8 @@ namespace MWGui void executeFile (const std::string& path); + virtual void resetReference (); + protected: virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/referenceinterface.hpp b/apps/openmw/mwgui/referenceinterface.hpp index df53a42b7..0ba180de8 100644 --- a/apps/openmw/mwgui/referenceinterface.hpp +++ b/apps/openmw/mwgui/referenceinterface.hpp @@ -17,7 +17,7 @@ namespace MWGui void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable - void resetReference() { mPtr = MWWorld::Ptr(); mCurrentPlayerCell = NULL; } + virtual void resetReference() { mPtr = MWWorld::Ptr(); mCurrentPlayerCell = NULL; } protected: virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable From 91ed5183c8efbe52982da3938c00f771c0986b41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 12 Jun 2014 21:49:18 +0200 Subject: [PATCH 056/226] Show base item value in tooltip, not real value (Fixes #1469) --- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 947a9cb94..d61ba038a 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -124,7 +124,7 @@ namespace MWClass std::string text; text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 825b14978..a7a459382 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -244,7 +244,7 @@ namespace MWClass + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + " (" + typeText + ")"; - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 0cc2e6020..0adee57e3 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -136,7 +136,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c0362188b..dc98e323e 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -190,7 +190,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 60c0efeb8..683092923 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -144,7 +144,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index ef4549268..8a2c20f69 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -184,7 +184,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 19381a3fd..5d91db9e2 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -138,7 +138,7 @@ namespace MWClass text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 7440617c2..d7cb0cd9b 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -127,7 +127,7 @@ namespace MWClass std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects); diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 5d076a3c5..5db0d7a79 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -137,7 +137,7 @@ namespace MWClass text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 9b528a4fc..f8b72be37 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -141,7 +141,7 @@ namespace MWClass text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 26618c021..98b02b914 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -343,7 +343,7 @@ namespace MWClass } text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); - text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); + text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); info.enchant = ref->mBase->mEnchant; From be6f1fe4fe1e9a536b490b1751ae98c4690328a3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 12 Jun 2014 23:12:48 +0200 Subject: [PATCH 057/226] Fix a sign error --- apps/openmw/mwsound/soundmanagerimp.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 3f85059ce..35b8579d7 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -226,10 +226,7 @@ namespace MWSound // Don't play the same music track twice in a row if (filelist[i] == mLastPlayedMusic) { - if (i-1 == int(filelist.size())) - i = 0; - else - ++i; + i = (i+1) % filelist.size(); } streamMusicFull(filelist[i]); From a54ac579a554d95823d3f8153b74c9600b800034 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 12 Jun 2014 23:27:04 +0200 Subject: [PATCH 058/226] Savegame: Store AiSequence --- apps/openmw/mwmechanics/aiactivate.cpp | 59 ++++-- apps/openmw/mwmechanics/aiactivate.hpp | 21 ++- apps/openmw/mwmechanics/aiavoiddoor.cpp | 8 +- apps/openmw/mwmechanics/aiavoiddoor.hpp | 2 + apps/openmw/mwmechanics/aicombat.cpp | 121 +++++-------- apps/openmw/mwmechanics/aicombat.hpp | 20 +- apps/openmw/mwmechanics/aiescort.cpp | 57 +++--- apps/openmw/mwmechanics/aiescort.hpp | 15 +- apps/openmw/mwmechanics/aifollow.cpp | 48 ++++- apps/openmw/mwmechanics/aifollow.hpp | 28 ++- apps/openmw/mwmechanics/aipackage.cpp | 2 - apps/openmw/mwmechanics/aipackage.hpp | 19 +- apps/openmw/mwmechanics/aipursue.cpp | 19 ++ apps/openmw/mwmechanics/aipursue.hpp | 12 ++ apps/openmw/mwmechanics/aisequence.cpp | 77 +++++++- apps/openmw/mwmechanics/aisequence.hpp | 11 ++ apps/openmw/mwmechanics/aitravel.cpp | 24 +++ apps/openmw/mwmechanics/aitravel.hpp | 16 +- apps/openmw/mwmechanics/aiwander.cpp | 113 ++++++++---- apps/openmw/mwmechanics/aiwander.hpp | 22 ++- apps/openmw/mwmechanics/creaturestats.cpp | 2 + apps/openmw/mwscript/aiextensions.cpp | 3 +- components/CMakeLists.txt | 1 + components/esm/aisequence.cpp | 211 ++++++++++++++++++++++ components/esm/aisequence.hpp | 154 ++++++++++++++++ components/esm/creaturestats.cpp | 2 + components/esm/creaturestats.hpp | 3 + 27 files changed, 868 insertions(+), 202 deletions(-) create mode 100644 components/esm/aisequence.cpp create mode 100644 components/esm/aisequence.hpp diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 3dfacb853..1cdda24c0 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -1,5 +1,7 @@ #include "aiactivate.hpp" +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -19,26 +21,26 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const return new AiActivate(*this); } 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 - - if(target == MWWorld::Ptr()) - return true; //Target doesn't exist - - //Set the target desition from the actor - ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; - - if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close - actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); - target.getClass().activate(target,actor).get()->execute(actor); //Arrest player - return true; - } - else { - pathTo(actor, dest, duration); //Go to the destination - } - +{ + 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 + + if(target == MWWorld::Ptr()) + return true; //Target doesn't exist + + //Set the target desition from the actor + ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; + + if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); + target.getClass().activate(target,actor).get()->execute(actor); //Arrest player + return true; + } + else { + pathTo(actor, dest, duration); //Go to the destination + } + return false; } @@ -46,3 +48,20 @@ int MWMechanics::AiActivate::getTypeId() const { return TypeIdActivate; } + +void MWMechanics::AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const +{ + std::auto_ptr activate(new ESM::AiSequence::AiActivate()); + activate->mTargetId = mObjectId; + + ESM::AiSequence::AiPackageContainer package; + package.mType = ESM::AiSequence::Ai_Activate; + package.mPackage = activate.release(); + sequence.mPackages.push_back(package); +} + +MWMechanics::AiActivate::AiActivate(const ESM::AiSequence::AiActivate *activate) + : mObjectId(activate->mTargetId) +{ + +} diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 0e660e967..8003c2a36 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -6,20 +6,33 @@ #include "pathfinding.hpp" +namespace ESM +{ +namespace AiSequence +{ + struct AiActivate; +} +} + namespace MWMechanics -{ - /// \brief Causes actor to walk to activatable object and activate it +{ + /// \brief Causes actor to walk to activatable object and activate it /** Will activate when close to object **/ class AiActivate : public AiPackage { - public: - /// Constructor + public: + /// Constructor /** \param objectId Reference to object to activate **/ AiActivate(const std::string &objectId); + + AiActivate(const ESM::AiSequence::AiActivate* activate); + virtual AiActivate *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual int getTypeId() const; + virtual void writeState(ESM::AiSequence::AiSequence& sequence) const; + private: std::string mObjectId; int mCellX; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index ea6f296cc..bab8bca28 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -78,8 +78,14 @@ MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const return new AiAvoidDoor(*this); } - int MWMechanics::AiAvoidDoor::getTypeId() const +int MWMechanics::AiAvoidDoor::getTypeId() const { return TypeIdAvoidDoor; } +unsigned int MWMechanics::AiAvoidDoor::getPriority() const +{ + return 2; +} + + diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index d2a2e33a1..2374fbc1b 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -24,6 +24,8 @@ namespace MWMechanics virtual int getTypeId() const; + virtual unsigned int getPriority() const; + private: float mDuration; MWWorld::Ptr mDoorPtr; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 9ec770c9d..f4aae14d5 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" @@ -81,23 +82,22 @@ namespace MWMechanics // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp AiCombat::AiCombat(const MWWorld::Ptr& actor) : - mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()), - mTimerAttack(0), - mTimerReact(0), - mTimerCombatMove(0), - mFollowTarget(false), - mReadyToAttack(false), - mAttack(false), - mCombatMove(false), - mMovement(), - mForceNoShortcut(false), - mShortcutFailPos(), - mBackOffDoor(false), - mCell(NULL), - mDoorIter(actor.getCell()->get().mList.end()), - mDoors(actor.getCell()->get()), - mDoorCheckDuration(0) + mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) { + init(); + } + + void AiCombat::init() + { + mTimerAttack = 0; + mTimerReact = 0; + mTimerCombatMove = 0; + mFollowTarget = false; + mReadyToAttack = false; + mAttack = false; + mCombatMove = false; + mForceNoShortcut = false; + mCell = NULL; } /* @@ -511,70 +511,22 @@ namespace MWMechanics // coded at 250ms or 1/4 second // // TODO: Add a parameter to vary DURATION_SAME_SPOT? - MWWorld::CellStore *cell = actor.getCell(); if((distToTarget > rangeAttack || mFollowTarget) && mObstacleCheck.check(actor, tReaction)) // check if evasive action needed { - // first check if we're walking into a door - mDoorCheckDuration += 1.0f; // add time taken for obstacle check - if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL && !cell->getCell()->isExterior()) - { - mDoorCheckDuration = 0; - // Check all the doors in this cell - mDoors = cell->get(); // update - mDoorIter = mDoors.mList.begin(); - for (; mDoorIter != mDoors.mList.end(); ++mDoorIter) - { - MWWorld::LiveCellRef& ref = *mDoorIter; - float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility - if(vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.getPosition().pos)) < minSqr && - ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening - { - //std::cout<<"closed door id \""<getCell()->isExterior() && !mDoors.mList.empty()) - { - MWWorld::LiveCellRef& ref = *mDoorIter; - float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility - // TODO: add reaction to checking open doors - if(mBackOffDoor && - vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.getPosition().pos)) < minSqr) - { - mMovement.mPosition[1] = -0.2; // back off, but slowly - } - else if(mBackOffDoor && - mDoorIter != mDoors.mList.end() && - ref.mData.getLocalRotation().rot[2] >= 1) - { - mDoorIter = mDoors.mList.end(); - mBackOffDoor = false; - //std::cout<<"open door id \""<mTargetActorId; + init(); + } + + void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const + { + std::auto_ptr combat(new ESM::AiSequence::AiCombat()); + combat->mTargetActorId = mTargetActorId; + + ESM::AiSequence::AiPackageContainer package; + package.mType = ESM::AiSequence::Ai_Combat; + package.mPackage = combat.release(); + sequence.mPackages.push_back(package); + } } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 4b728ff22..061a6a3d3 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -12,6 +12,14 @@ #include "../mwbase/world.hpp" +namespace ESM +{ + namespace AiSequence + { + struct AiCombat; + } +} + namespace MWMechanics { /// \brief Causes the actor to fight another actor @@ -22,6 +30,10 @@ namespace MWMechanics /** \param actor Actor to fight **/ AiCombat(const MWWorld::Ptr& actor); + AiCombat (const ESM::AiSequence::AiCombat* combat); + + void init(); + virtual AiCombat *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); @@ -33,6 +45,8 @@ namespace MWMechanics ///Returns target ID MWWorld::Ptr getTarget() const; + virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; + private: PathFinder mPathFinder; // controls duration of the actual strike @@ -46,7 +60,6 @@ namespace MWMechanics bool mReadyToAttack, mAttack; bool mFollowTarget; bool mCombatMove; - bool mBackOffDoor; bool mForceNoShortcut; ESM::Position mShortcutFailPos; @@ -57,11 +70,6 @@ namespace MWMechanics const MWWorld::CellStore* mCell; ObstacleCheck mObstacleCheck; - float mDoorCheckDuration; - // TODO: for some reason mDoors.searchViaHandle() returns - // null pointers, workaround by keeping an iterator - MWWorld::CellRefList::List::iterator mDoorIter; - MWWorld::CellRefList& mDoors; void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); }; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 07bb9726e..3f5724077 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,12 +1,13 @@ #include "aiescort.hpp" +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/timestamp.hpp" #include "steering.hpp" #include "movement.hpp" @@ -20,7 +21,7 @@ namespace MWMechanics { AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) - : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) + : mActorId(actorId), mX(x), mY(y), mZ(z), mRemainingDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { @@ -29,32 +30,29 @@ namespace MWMechanics // The CS Help File states that if a duration is given, the AI package will run for that long // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) - mDuration = 0; - - else - { - MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); - } + mRemainingDuration = 0; } AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) - : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) + : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mRemainingDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { mMaxDist = 470; // The CS Help File states that if a duration is given, the AI package will run for that long - // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. + // BUT if a location is given, it "trumps" the duration so it will simply escort to that location. if(mX != 0 || mY != 0 || mZ != 0) - mDuration = 0; + mRemainingDuration = 0; + } - else - { - MWWorld::TimeStamp startTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mStartingSecond = ((startTime.getHour() - int(startTime.getHour())) * 100); - } + AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort) + : mActorId(escort->mTargetId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ) + , mCellX(std::numeric_limits::max()) + , mCellY(std::numeric_limits::max()) + , mCellId(escort->mCellId) + , mRemainingDuration(escort->mRemainingDuration) + { } @@ -67,11 +65,10 @@ namespace MWMechanics { // If AiEscort has ran for as long or longer then the duration specified // and the duration is not infinite, the package is complete. - if(mDuration != 0) + if(mRemainingDuration != 0) { - MWWorld::TimeStamp current = MWBase::Environment::get().getWorld()->getTimeStamp(); - unsigned int currentSecond = ((current.getHour() - int(current.getHour())) * 100); - if(currentSecond - mStartingSecond >= mDuration) + mRemainingDuration -= duration; + if (duration <= 0) return true; } @@ -89,7 +86,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { - if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete + if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete return true; mMaxDist = 470; } @@ -108,5 +105,21 @@ namespace MWMechanics { return TypeIdEscort; } + + void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const + { + std::auto_ptr escort(new ESM::AiSequence::AiEscort()); + escort->mData.mX = mX; + escort->mData.mY = mY; + escort->mData.mZ = mZ; + escort->mTargetId = mActorId; + escort->mRemainingDuration = mRemainingDuration; + escort->mCellId = mCellId; + + ESM::AiSequence::AiPackageContainer package; + package.mType = ESM::AiSequence::Ai_Escort; + package.mPackage = escort.release(); + sequence.mPackages.push_back(package); + } } diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 3771417fa..820df969f 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -6,6 +6,14 @@ #include "pathfinding.hpp" +namespace ESM +{ +namespace AiSequence +{ + struct AiEscort; +} +} + namespace MWMechanics { /// \brief AI Package to have an NPC lead the player to a specific point @@ -21,12 +29,16 @@ namespace MWMechanics \implement AiEscortCell **/ AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); + AiEscort(const ESM::AiSequence::AiEscort* escort); + virtual AiEscort *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual int getTypeId() const; + void writeState(ESM::AiSequence::AiSequence &sequence) const; + private: std::string mActorId; std::string mCellId; @@ -34,8 +46,7 @@ namespace MWMechanics float mY; float mZ; float mMaxDist; - unsigned int mStartingSecond; - unsigned int mDuration; + float mRemainingDuration; // In seconds PathFinder mPathFinder; int mCellX; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index f1296a949..5ab7e1730 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -1,5 +1,9 @@ #include "aifollow.hpp" + #include + +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" @@ -12,16 +16,16 @@ #include "steering.hpp" MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), AiPackage() +: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId("") { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), AiPackage() +: mAlwaysFollow(false), mRemainingDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId) -: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage() +: mAlwaysFollow(true), mRemainingDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId("") { } @@ -35,8 +39,13 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) if(!mAlwaysFollow) //Update if you only follow for a bit { - if(mTotalTime > mDuration && mDuration != 0) //Check if we've run out of time - return true; + //Check if we've run out of time + if (mRemainingDuration != 0) + { + mRemainingDuration -= duration; + if (duration <= 0) + return true; + } if((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + @@ -55,7 +64,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) } } - //Set the target desition from the actor + //Set the target destination from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close @@ -83,7 +92,32 @@ MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const return new AiFollow(*this); } - int MWMechanics::AiFollow::getTypeId() const +int MWMechanics::AiFollow::getTypeId() const { return TypeIdFollow; } + +void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const +{ + std::auto_ptr follow(new ESM::AiSequence::AiFollow()); + follow->mData.mX = mX; + follow->mData.mY = mY; + follow->mData.mZ = mZ; + follow->mTargetId = mActorId; + follow->mRemainingDuration = mRemainingDuration; + follow->mCellId = mCellId; + follow->mAlwaysFollow = mAlwaysFollow; + + ESM::AiSequence::AiPackageContainer package; + package.mType = ESM::AiSequence::Ai_Follow; + package.mPackage = follow.release(); + sequence.mPackages.push_back(package); +} + +MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) + : mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration) + , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) + , mActorId(follow->mTargetId), mCellId(follow->mCellId) +{ + +} diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 10a381410..e9587b36e 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -6,13 +6,21 @@ #include "pathfinding.hpp" #include +namespace ESM +{ +namespace AiSequence +{ + struct AiFollow; +} +} + namespace MWMechanics { /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely - **/ - class AiFollow : public AiPackage - { + **/ + class AiFollow : public AiPackage + { public: /// Follow Actor for duration or until you arrive at a world position AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); @@ -21,6 +29,8 @@ namespace MWMechanics /// Follow Actor indefinitively AiFollow(const std::string &ActorId); + AiFollow(const ESM::AiSequence::AiFollow* follow); + virtual AiFollow *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); @@ -30,16 +40,18 @@ namespace MWMechanics /// Returns the actor being followed std::string getFollowedActor(); + virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; + private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ bool mAlwaysFollow; - float mDuration; + float mRemainingDuration; // Seconds float mX; float mY; float mZ; std::string mActorId; - std::string mCellId; - }; -} -#endif + std::string mCellId; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 2144aa11d..7790942b2 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -26,8 +26,6 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Po //Update various Timers mTimer += duration; //Update timer mStuckTimer += duration; //Update stuck timer - mTotalTime += duration; //Update total time following - ESM::Position pos = actor.getRefData().getPosition(); //position of the actor diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 055958384..983777c0a 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -12,6 +12,14 @@ namespace MWWorld class Ptr; } +namespace ESM +{ + namespace AiSequence + { + class AiSequence; + } +} + namespace MWMechanics { /// \brief Base class for AI packages @@ -51,6 +59,8 @@ namespace MWMechanics /// Higher number is higher priority (0 being the lowest) virtual unsigned int getPriority() const {return 0;} + virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} + protected: /// Causes the actor to attempt to walk to the specified location /** \return If the actor has arrived at his destination **/ @@ -60,12 +70,11 @@ namespace MWMechanics ObstacleCheck mObstacleCheck; float mDoorCheckDuration; - float mTimer; - float mStuckTimer; - float mTotalTime; + float mTimer; + float mStuckTimer; + + MWWorld::Ptr mLastDoorChecked; //Used to ensure we don't try to CONSTANTLY open a door - MWWorld::Ptr mLastDoorChecked; //Used to ensure we don't try to CONSTANTLY open a door - ESM::Position mStuckPos; ESM::Pathgrid::Point mPrevDest; }; diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index cd9d34e08..60f671c12 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -1,5 +1,7 @@ #include "aipursue.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" @@ -18,6 +20,12 @@ AiPursue::AiPursue(const MWWorld::Ptr& actor) : mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) { } + +AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue) + : mTargetActorId(pursue->mTargetActorId) +{ +} + AiPursue *MWMechanics::AiPursue::clone() const { return new AiPursue(*this); @@ -57,4 +65,15 @@ MWWorld::Ptr AiPursue::getTarget() const return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); } +void AiPursue::writeState(ESM::AiSequence::AiSequence &sequence) const +{ + std::auto_ptr pursue(new ESM::AiSequence::AiPursue()); + pursue->mTargetActorId = mTargetActorId; + + ESM::AiSequence::AiPackageContainer package; + package.mType = ESM::AiSequence::Ai_Pursue; + package.mPackage = pursue.release(); + sequence.mPackages.push_back(package); +} + } // namespace MWMechanics diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 27affc9ac..18a22b676 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -7,6 +7,14 @@ #include "pathfinding.hpp" +namespace ESM +{ +namespace AiSequence +{ + struct AiPursue; +} +} + namespace MWMechanics { /// \brief Makes the actor very closely follow the actor @@ -20,12 +28,16 @@ namespace MWMechanics /** \param actor Actor to pursue **/ AiPursue(const MWWorld::Ptr& actor); + AiPursue(const ESM::AiSequence::AiPursue* pursue); + virtual AiPursue *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual int getTypeId() const; MWWorld::Ptr getTarget() const; + virtual void writeState (ESM::AiSequence::AiSequence& sequence) const; + private: int mTargetActorId; // The actor to pursue diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 8a6a69b27..2d4dc2732 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -11,6 +11,8 @@ #include "aicombat.hpp" #include "aipursue.hpp" +#include + #include "../mwworld/class.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" @@ -258,7 +260,8 @@ void AiSequence::fill(const ESM::AIPackageList &list) if (it->mType == ESM::AI_Wander) { ESM::AIWander data = it->mWander; - std::vector idles; + std::vector idles; + idles.reserve(8); for (int i=0; i<8; ++i) idles.push_back(data.mIdle[i]); package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat); @@ -287,4 +290,76 @@ void AiSequence::fill(const ESM::AIPackageList &list) } } +void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const +{ + for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) + { + (*iter)->writeState(sequence); + } +} + +void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) +{ + clear(); + + for (std::vector::const_iterator it = sequence.mPackages.begin(); + it != sequence.mPackages.end(); ++it) + { + switch (it->mType) + { + case ESM::AiSequence::Ai_Wander: + { + MWMechanics::AiWander* wander = new AiWander( + dynamic_cast(it->mPackage)); + mPackages.push_back(wander); + break; + } + case ESM::AiSequence::Ai_Travel: + { + MWMechanics::AiTravel* travel = new AiTravel( + dynamic_cast(it->mPackage)); + mPackages.push_back(travel); + break; + } + case ESM::AiSequence::Ai_Escort: + { + MWMechanics::AiEscort* escort = new AiEscort( + dynamic_cast(it->mPackage)); + mPackages.push_back(escort); + break; + } + case ESM::AiSequence::Ai_Follow: + { + MWMechanics::AiFollow* follow = new AiFollow( + dynamic_cast(it->mPackage)); + mPackages.push_back(follow); + break; + } + case ESM::AiSequence::Ai_Activate: + { + MWMechanics::AiActivate* activate = new AiActivate( + dynamic_cast(it->mPackage)); + mPackages.push_back(activate); + break; + } + case ESM::AiSequence::Ai_Combat: + { + MWMechanics::AiCombat* combat = new AiCombat( + dynamic_cast(it->mPackage)); + mPackages.push_back(combat); + break; + } + case ESM::AiSequence::Ai_Pursue: + { + MWMechanics::AiPursue* pursue = new AiPursue( + dynamic_cast(it->mPackage)); + mPackages.push_back(pursue); + break; + } + default: + break; + } + } +} + } // namespace MWMechanics diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 41a280da8..b789d33cd 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -10,6 +10,14 @@ namespace MWWorld class Ptr; } +namespace ESM +{ + namespace AiSequence + { + class AiSequence; + } +} + namespace MWMechanics { class AiPackage; @@ -90,6 +98,9 @@ namespace MWMechanics /** Typically used for loading from the ESM \see ESM::AIPackageList **/ void fill (const ESM::AIPackageList& list); + + void writeState (ESM::AiSequence::AiSequence& sequence) const; + void readState (const ESM::AiSequence::AiSequence& sequence); }; } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 024656b38..db137037d 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,5 +1,7 @@ #include "aitravel.hpp" +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -19,6 +21,15 @@ namespace MWMechanics { } + AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel) + : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ) + , mPathFinder() + , mCellX(std::numeric_limits::max()) + , mCellY(std::numeric_limits::max()) + { + + } + AiTravel *MWMechanics::AiTravel::clone() const { return new AiTravel(*this); @@ -92,5 +103,18 @@ namespace MWMechanics { return TypeIdTravel; } + + void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const + { + std::auto_ptr travel(new ESM::AiSequence::AiTravel()); + travel->mData.mX = mX; + travel->mData.mY = mY; + travel->mData.mZ = mZ; + + ESM::AiSequence::AiPackageContainer package; + package.mType = ESM::AiSequence::Ai_Travel; + package.mPackage = travel.release(); + sequence.mPackages.push_back(package); + } } diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index ea7f1dc32..91ee30253 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -5,14 +5,26 @@ #include "pathfinding.hpp" +namespace ESM +{ +namespace AiSequence +{ + struct AiTravel; +} +} + namespace MWMechanics -{ +{ /// \brief Causes the AI to travel to the specified point class AiTravel : public AiPackage { - public: + public: /// Default constructor AiTravel(float x, float y, float z); + AiTravel(const ESM::AiSequence::AiTravel* travel); + + void writeState(ESM::AiSequence::AiSequence &sequence) const; + virtual AiTravel *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 1c870bda4..6a68397fd 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -21,31 +23,28 @@ namespace MWMechanics static const int COUNT_BEFORE_RESET = 200; // TODO: maybe no longer needed static const float DOOR_CHECK_INTERVAL = 1.5f; - AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) - , mCellX(std::numeric_limits::max()) - , mCellY(std::numeric_limits::max()) - , mXCell(0) - , mYCell(0) - , mCell(NULL) - , mStuckCount(0) // TODO: maybe no longer needed - , mDoorCheckDuration(0) - , mTrimCurrentNode(false) - , mReaction(0) - , mGreetDistanceMultiplier(0) - , mGreetDistanceReset(0) - , mChance(0) - , mRotate(false) - , mTargetAngle(0) - , mSaidGreeting(false) - , mHasReturnPosition(false) - , mReturnPosition(0,0,0) { - for(unsigned short counter = 0; counter < mIdle.size(); counter++) - { - if(mIdle[counter] >= 127 || mIdle[counter] < 0) - mIdle[counter] = 0; - } + init(); + } + + void AiWander::init() + { + mCellX = std::numeric_limits::max(); + mCellY = std::numeric_limits::max(); + mXCell = 0; + mYCell = 0; + mCell = NULL; + mStuckCount = 0;// TODO: maybe no longer needed + mDoorCheckDuration = 0; + mTrimCurrentNode = false; + mReaction = 0; + mRotate = false; + mTargetAngle = 0; + mSaidGreeting = false; + mHasReturnPosition = false; + mReturnPosition = Ogre::Vector3(0,0,0); if(mDistance < 0) mDistance = 0; @@ -56,15 +55,6 @@ namespace MWMechanics mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mPlayedIdle = 0; - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - mIdleChanceMultiplier = - store.get().find("fIdleChanceMultiplier")->getFloat(); - - mGreetDistanceMultiplier = - store.get().find("iGreetDistanceMultiplier")->getInt(); - mGreetDistanceReset = - store.get().find("fGreetDistanceReset")->getFloat(); - mChance = store.get().find("fVoiceIdleOdds")->getFloat(); mStoredAvailableNodes = false; mChooseAction = true; @@ -219,11 +209,12 @@ namespace MWMechanics } mReaction += duration; - if(mReaction > 0.25f) // FIXME: hard coded constant + if(mReaction < 0.25f) // FIXME: hard coded constant { - mReaction = 0; return false; } + else + mReaction = 0; // NOTE: everything below get updated every 0.25 seconds @@ -394,7 +385,10 @@ namespace MWMechanics MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // Don't bother if the player is out of hearing range - if (roll < mChance && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500) + static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() + .get().find("fVoiceIdleOdds")->getFloat(); + + if (roll < fVoiceIdleOdds && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } @@ -406,7 +400,10 @@ namespace MWMechanics // Play a random voice greeting if the player gets too close int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = hello; - helloDistance *= mGreetDistanceMultiplier; + static int iGreetDistanceMultiplier =MWBase::Environment::get().getWorld()->getStore() + .get().find("iGreetDistanceMultiplier")->getInt(); + + helloDistance *= iGreetDistanceMultiplier; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); Ogre::Vector3 playerPos(player.getRefData().getPosition().pos); @@ -455,7 +452,10 @@ namespace MWMechanics } else { - if (playerDistSqr >= mGreetDistanceReset*mGreetDistanceReset * mGreetDistanceMultiplier*mGreetDistanceMultiplier) + static float fGreetDistanceReset = MWBase::Environment::get().getWorld()->getStore() + .get().find("fGreetDistanceReset")->getFloat(); + + if (playerDistSqr >= fGreetDistanceReset*fGreetDistanceReset * iGreetDistanceMultiplier*iGreetDistanceMultiplier) mSaidGreeting = false; } @@ -632,8 +632,11 @@ namespace MWMechanics for(unsigned int counter = 0; counter < mIdle.size(); counter++) { - unsigned short idleChance = mIdleChanceMultiplier * mIdle[counter]; - unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / mIdleChanceMultiplier)); + static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore() + .get().find("fIdleChanceMultiplier")->getFloat(); + + unsigned short idleChance = fIdleChanceMultiplier * mIdle[counter]; + unsigned short randSelect = (int)(rand() / ((double)RAND_MAX + 1) * int(100 / fIdleChanceMultiplier)); if(randSelect < idleChance && randSelect > idleRoll) { mPlayedIdle = counter+2; @@ -641,5 +644,37 @@ namespace MWMechanics } } } + + void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const + { + std::auto_ptr wander(new ESM::AiSequence::AiWander()); + wander->mData.mDistance = mDistance; + wander->mData.mDuration = mDuration; + wander->mData.mTimeOfDay = mTimeOfDay; + wander->mStartTime = mStartTime.toEsm(); + assert (mIdle.size() >= 8); + for (int i=0; i<8; ++i) + wander->mData.mIdle[i] = mIdle[i]; + wander->mData.mShouldRepeat = mRepeat; + + ESM::AiSequence::AiPackageContainer package; + package.mType = ESM::AiSequence::Ai_Wander; + package.mPackage = wander.release(); + sequence.mPackages.push_back(package); + } + + AiWander::AiWander (const ESM::AiSequence::AiWander* wander) + { + init(); + + mDistance = wander->mData.mDistance; + mDuration = wander->mData.mDuration; + mStartTime = MWWorld::TimeStamp(wander->mStartTime); + mTimeOfDay = wander->mData.mTimeOfDay; + for (int i=0; i<8; ++i) + mIdle.push_back(wander->mData.mIdle[i]); + + mRepeat = wander->mData.mShouldRepeat; + } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 6481b2a01..7abd19e27 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -12,6 +12,14 @@ #include "../mwworld/timestamp.hpp" +namespace ESM +{ + namespace AiSequence + { + struct AiWander; + } +} + namespace MWMechanics { /// \brief Causes the Actor to wander within a specified range @@ -24,7 +32,11 @@ namespace MWMechanics \param timeOfDay Start time of the package, if it has a duration. Currently unimplemented \param idle Chances of each idle to play (9 in total) \param repeat Repeat wander or not **/ - AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat); + AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat); + + AiWander (const ESM::AiSequence::AiWander* wander); + + void init(); virtual AiPackage *clone() const; @@ -36,6 +48,8 @@ namespace MWMechanics /** In case another AI package moved the actor elsewhere **/ void setReturnPosition (const Ogre::Vector3& position); + virtual void writeState(ESM::AiSequence::AiSequence &sequence) const; + private: void stopWalking(const MWWorld::Ptr& actor); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); @@ -45,13 +59,10 @@ namespace MWMechanics int mDistance; // how far the actor can wander from the spawn point int mDuration; int mTimeOfDay; - std::vector mIdle; + std::vector mIdle; bool mRepeat; bool mSaidGreeting; - int mGreetDistanceMultiplier; - float mGreetDistanceReset; - float mChance; bool mHasReturnPosition; // NOTE: Could be removed if mReturnPosition was initialized to actor position, // if we had the actor in the AiWander constructor... @@ -74,7 +85,6 @@ namespace MWMechanics bool mMoveNow; bool mWalking; - float mIdleChanceMultiplier; unsigned short mPlayedIdle; MWWorld::TimeStamp mStartTime; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 7fd26c25c..7396b2735 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -503,6 +503,7 @@ namespace MWMechanics mSpells.writeState(state.mSpells); mActiveSpells.writeState(state.mActiveSpells); + mAiSequence.writeState(state.mAiSequence); } void CreatureStats::readState (const ESM::CreatureStats& state) @@ -543,6 +544,7 @@ namespace MWMechanics mSpells.readState(state.mSpells); mActiveSpells.readState(state.mActiveSpells); + mAiSequence.readState(state.mAiSequence); } void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index cc17905df..823d22a6a 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -186,7 +186,7 @@ namespace MWScript Interpreter::Type_Integer time = runtime[0].mFloat; runtime.pop(); - std::vector idleList; + std::vector idleList; bool repeat = false; for(int i=1; i < 10 && arg0; ++i) @@ -194,6 +194,7 @@ namespace MWScript if(!repeat) repeat = true; Interpreter::Type_Integer idleValue = runtime[0].mInteger; + idleValue = std::min(255, std::max(0, idleValue)); idleList.push_back(idleValue); runtime.pop(); --arg0; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e0166138e..060e3472d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -42,6 +42,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate + aisequence ) add_component_dir (misc diff --git a/components/esm/aisequence.cpp b/components/esm/aisequence.cpp new file mode 100644 index 000000000..80440bdd3 --- /dev/null +++ b/components/esm/aisequence.cpp @@ -0,0 +1,211 @@ +#include "aisequence.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +#include "defs.hpp" + +#include + +namespace ESM +{ +namespace AiSequence +{ + + void AiWander::load(ESMReader &esm) + { + esm.getHNT (mData, "DATA"); + esm.getHNT(mStartTime, "STAR"); + } + + void AiWander::save(ESMWriter &esm) const + { + esm.writeHNT ("DATA", mData); + esm.writeHNT ("STAR", mStartTime); + } + + void AiTravel::load(ESMReader &esm) + { + esm.getHNT (mData, "DATA"); + } + + void AiTravel::save(ESMWriter &esm) const + { + esm.writeHNT ("DATA", mData); + } + + void AiEscort::load(ESMReader &esm) + { + esm.getHNT (mData, "DATA"); + mTargetId = esm.getHNString("TARG"); + esm.getHNT (mRemainingDuration, "DURA"); + mCellId = esm.getHNOString ("CELL"); + } + + void AiEscort::save(ESMWriter &esm) const + { + esm.writeHNT ("DATA", mData); + esm.writeHNString ("TARG", mTargetId); + esm.writeHNT ("DURA", mRemainingDuration); + if (!mCellId.empty()) + esm.writeHNString ("CELL", mCellId); + } + + void AiFollow::load(ESMReader &esm) + { + esm.getHNT (mData, "DATA"); + mTargetId = esm.getHNString("TARG"); + esm.getHNT (mRemainingDuration, "DURA"); + mCellId = esm.getHNOString ("CELL"); + esm.getHNT (mAlwaysFollow, "ALWY"); + } + + void AiFollow::save(ESMWriter &esm) const + { + esm.writeHNT ("DATA", mData); + esm.writeHNString("TARG", mTargetId); + esm.writeHNT ("DURA", mRemainingDuration); + if (!mCellId.empty()) + esm.writeHNString ("CELL", mCellId); + esm.writeHNT ("ALWY", mAlwaysFollow); + } + + void AiActivate::load(ESMReader &esm) + { + mTargetId = esm.getHNString("TARG"); + } + + void AiActivate::save(ESMWriter &esm) const + { + esm.writeHNString("TARG", mTargetId); + } + + void AiCombat::load(ESMReader &esm) + { + esm.getHNT (mTargetActorId, "TARG"); + } + + void AiCombat::save(ESMWriter &esm) const + { + esm.writeHNT ("TARG", mTargetActorId); + } + + void AiPursue::load(ESMReader &esm) + { + esm.getHNT (mTargetActorId, "TARG"); + } + + void AiPursue::save(ESMWriter &esm) const + { + esm.writeHNT ("TARG", mTargetActorId); + } + + AiSequence::~AiSequence() + { + for (std::vector::iterator it = mPackages.begin(); it != mPackages.end(); ++it) + delete it->mPackage; + } + + void AiSequence::save(ESMWriter &esm) const + { + for (std::vector::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + esm.writeHNT ("AIPK", it->mType); + switch (it->mType) + { + case Ai_Wander: + static_cast(it->mPackage)->save(esm); + break; + case Ai_Travel: + static_cast(it->mPackage)->save(esm); + break; + case Ai_Escort: + static_cast(it->mPackage)->save(esm); + break; + case Ai_Follow: + static_cast(it->mPackage)->save(esm); + break; + case Ai_Activate: + static_cast(it->mPackage)->save(esm); + break; + case Ai_Combat: + static_cast(it->mPackage)->save(esm); + break; + case Ai_Pursue: + static_cast(it->mPackage)->save(esm); + break; + + default: + break; + } + } + } + + void AiSequence::load(ESMReader &esm) + { + while (esm.isNextSub("AIPK")) + { + int type; + esm.getHT(type); + + mPackages.push_back(AiPackageContainer()); + mPackages.back().mType = type; + + switch (type) + { + case Ai_Wander: + { + std::auto_ptr ptr (new AiWander()); + ptr->load(esm); + mPackages.back().mPackage = ptr.release(); + break; + } + case Ai_Travel: + { + std::auto_ptr ptr (new AiTravel()); + ptr->load(esm); + mPackages.back().mPackage = ptr.release(); + break; + } + case Ai_Escort: + { + std::auto_ptr ptr (new AiEscort()); + ptr->load(esm); + mPackages.back().mPackage = ptr.release(); + break; + } + case Ai_Follow: + { + std::auto_ptr ptr (new AiFollow()); + ptr->load(esm); + mPackages.back().mPackage = ptr.release(); + break; + } + case Ai_Activate: + { + std::auto_ptr ptr (new AiActivate()); + ptr->load(esm); + mPackages.back().mPackage = ptr.release(); + break; + } + case Ai_Combat: + { + std::auto_ptr ptr (new AiCombat()); + ptr->load(esm); + mPackages.back().mPackage = ptr.release(); + break; + } + case Ai_Pursue: + { + std::auto_ptr ptr (new AiPursue()); + ptr->load(esm); + mPackages.back().mPackage = ptr.release(); + break; + } + default: + return; + } + } + } +} +} diff --git a/components/esm/aisequence.hpp b/components/esm/aisequence.hpp new file mode 100644 index 000000000..bf0e17fa0 --- /dev/null +++ b/components/esm/aisequence.hpp @@ -0,0 +1,154 @@ +#ifndef OPENMW_COMPONENTS_ESM_AISEQUENCE_H +#define OPENMW_COMPONENTS_ESM_AISEQUENCE_H + +#include +#include + +#include "defs.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + namespace AiSequence + { + + // format 0, saved games only + // As opposed to AiPackageList, this stores the "live" version of AI packages. + + enum AiPackages + { + Ai_Wander = ESM::FourCC<'W','A','N','D'>::value, + Ai_Travel = ESM::FourCC<'T','R','A','V'>::value, + Ai_Escort = ESM::FourCC<'E','S','C','O'>::value, + Ai_Follow = ESM::FourCC<'F','O','L','L'>::value, + Ai_Activate = ESM::FourCC<'A','C','T','I'>::value, + Ai_Combat = ESM::FourCC<'C','O','M','B'>::value, + Ai_Pursue = ESM::FourCC<'P','U','R','S'>::value + }; + + + struct AiPackage + { + virtual ~AiPackage() {} + }; + + +#pragma pack(push,1) + struct AiWanderData + { + short mDistance; + short mDuration; + unsigned char mTimeOfDay; + unsigned char mIdle[8]; + unsigned char mShouldRepeat; + }; + struct AiTravelData + { + float mX, mY, mZ; + }; + struct AiEscortData + { + float mX, mY, mZ; + short mDuration; + }; + +#pragma pack(pop) + + struct AiWander : AiPackage + { + AiWanderData mData; + ESM::TimeStamp mStartTime; + + /// \todo add more AiWander state + + void load(ESMReader &esm); + void save(ESMWriter &esm) const; + }; + + struct AiTravel : AiPackage + { + AiTravelData mData; + + void load(ESMReader &esm); + void save(ESMWriter &esm) const; + }; + + struct AiEscort : AiPackage + { + AiEscortData mData; + + std::string mTargetId; + std::string mCellId; + float mRemainingDuration; + + void load(ESMReader &esm); + void save(ESMWriter &esm) const; + }; + + struct AiFollow : AiPackage + { + AiEscortData mData; + + std::string mTargetId; + std::string mCellId; + float mRemainingDuration; + + bool mAlwaysFollow; + + void load(ESMReader &esm); + void save(ESMWriter &esm) const; + }; + + struct AiActivate : AiPackage + { + std::string mTargetId; + + void load(ESMReader &esm); + void save(ESMWriter &esm) const; + }; + + struct AiCombat : AiPackage + { + int mTargetActorId; + + void load(ESMReader &esm); + void save(ESMWriter &esm) const; + }; + + struct AiPursue : AiPackage + { + int mTargetActorId; + + void load(ESMReader &esm); + void save(ESMWriter &esm) const; + }; + + struct AiPackageContainer + { + int mType; + + AiPackage* mPackage; + }; + + struct AiSequence + { + AiSequence() {} + ~AiSequence(); + + std::vector mPackages; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + + private: + AiSequence(const AiSequence&); + AiSequence& operator=(const AiSequence&); + }; + + } + +} + +#endif diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 3860e9351..8151091b2 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -79,6 +79,7 @@ void ESM::CreatureStats::load (ESMReader &esm) mSpells.load(esm); mActiveSpells.load(esm); + mAiSequence.load(esm); } void ESM::CreatureStats::save (ESMWriter &esm) const @@ -160,4 +161,5 @@ void ESM::CreatureStats::save (ESMWriter &esm) const mSpells.save(esm); mActiveSpells.save(esm); + mAiSequence.save(esm); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 5ca3d071f..c8dc97403 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -11,6 +11,7 @@ #include "spellstate.hpp" #include "activespells.hpp" +#include "aisequence.hpp" namespace ESM { @@ -23,6 +24,8 @@ namespace ESM StatState mAttributes[8]; StatState mDynamic[3]; + AiSequence::AiSequence mAiSequence; + ESM::TimeStamp mTradeTime; int mGoldPool; int mActorId; From 797134aa512db26665235475ed61cf11afdd8a2b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 00:01:29 +0200 Subject: [PATCH 059/226] Handle activation scripts in AiActivate (Fixes #1478) --- apps/openmw/engine.cpp | 19 +------------------ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/aiactivate.cpp | 5 +++-- apps/openmw/mwscript/interpretercontext.cpp | 7 +++---- apps/openmw/mwscript/interpretercontext.hpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 20 ++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 8 files changed, 33 insertions(+), 26 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bda903d56..46f384f59 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -489,24 +489,7 @@ void OMW::Engine::activate() if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated return; - MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); - - interpreterContext.activate (ptr); - - std::string script = ptr.getClass().getScript (ptr); - - MWBase::Environment::get().getWorld()->breakInvisibility(MWBase::Environment::get().getWorld()->getPlayerPtr()); - - if (!script.empty()) - { - MWBase::Environment::get().getWorld()->getLocalScripts().setIgnore (ptr); - MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); - } - - if (!interpreterContext.hasActivationBeenHandled()) - { - interpreterContext.executeActivation(ptr); - } + MWBase::Environment::get().getWorld()->activate(ptr, MWBase::Environment::get().getWorld()->getPlayerPtr()); } void OMW::Engine::screenshot() diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 81bec6fe8..9fb91398d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -517,6 +517,8 @@ namespace MWBase virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; + + virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; }; } diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 1cdda24c0..9e01c3fe7 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -6,7 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/action.hpp" #include "../mwworld/cellstore.hpp" #include "steering.hpp" @@ -34,7 +33,9 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); - target.getClass().activate(target,actor).get()->execute(actor); //Arrest player + + MWBase::Environment::get().getWorld()->activate(target, actor); + return true; } else { diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 6bf50371b..ebe88c3a4 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -419,11 +419,10 @@ namespace MWScript mActivationHandled = false; } - void InterpreterContext::executeActivation(MWWorld::Ptr ptr) + void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - boost::shared_ptr action = (ptr.getClass().activate(ptr, player)); - action->execute (player); + boost::shared_ptr action = (ptr.getClass().activate(ptr, actor)); + action->execute (actor); if (mActivated == ptr) mActivationHandled = true; } diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 1137efed3..fdf9d6424 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -129,7 +129,7 @@ namespace MWScript ///< Store reference acted upon. The actual execution of the action does not /// take place here. - void executeActivation(MWWorld::Ptr ptr); + void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor); ///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled. void clearActivation(); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 9d6d5e50d..cd76f3b9c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -121,7 +121,7 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); - context.executeActivation(ptr); + context.executeActivation(ptr, MWBase::Environment::get().getWorld()->getPlayerPtr()); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bf752734f..371f9edfe 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -36,6 +36,8 @@ #include "../mwrender/sky.hpp" #include "../mwrender/animation.hpp" +#include "../mwscript/interpretercontext.hpp" + #include "../mwclass/door.hpp" #include "player.hpp" @@ -2759,4 +2761,22 @@ namespace MWWorld cast.inflict(apply->first, caster, effects, ESM::RT_Target, false, true); } } + + void World::activate(const Ptr &object, const Ptr &actor) + { + MWScript::InterpreterContext interpreterContext (&object.getRefData().getLocals(), object); + interpreterContext.activate (object); + + std::string script = object.getClass().getScript (object); + + breakInvisibility(actor); + + if (!script.empty()) + { + getLocalScripts().setIgnore (object); + MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); + } + if (!interpreterContext.hasActivationBeenHandled()) + interpreterContext.executeActivation(object, actor); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0a396ef5c..5b493efcc 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -586,6 +586,8 @@ namespace MWWorld virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); + + virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); }; } From e458cf1df2a2e6adbbfeff565d05b31fc869facf Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 01:24:58 +0200 Subject: [PATCH 060/226] Savegame: Store death counter (Fixes #1477) --- apps/openmw/mwbase/mechanicsmanager.hpp | 17 ++++++++++ apps/openmw/mwmechanics/actors.cpp | 32 +++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 6 ++++ .../mwmechanics/mechanicsmanagerimp.cpp | 20 ++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 8 +++++ apps/openmw/mwstate/statemanagerimp.cpp | 10 +++++- components/esm/defs.hpp | 1 + 7 files changed, 93 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index f31241bdb..30a576a15 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace Ogre { @@ -13,6 +14,9 @@ namespace Ogre namespace ESM { struct Class; + + class ESMReader; + class ESMWriter; } namespace MWWorld @@ -21,6 +25,11 @@ namespace MWWorld class CellStore; } +namespace Loading +{ + class Listener; +} + namespace MWBase { /// \brief Interface for game mechanics manager (implemented in MWMechanics) @@ -174,6 +183,14 @@ namespace MWBase virtual std::list getActorsFighting(const MWWorld::Ptr& actor) = 0; virtual void playerLoaded() = 0; + + virtual int countSavedGameRecords() const = 0; + + virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; + + virtual void clear() = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5727996d9..f3c42682b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1181,4 +1181,36 @@ namespace MWMechanics } return list; } + + void Actors::write (ESM::ESMWriter& writer, Loading::Listener& listener) const + { + writer.startRecord(ESM::REC_DCOU); + for (std::map::const_iterator it = mDeathCount.begin(); it != mDeathCount.end(); ++it) + { + writer.writeHNString("ID__", it->first); + writer.writeHNT ("COUN", it->second); + } + writer.endRecord(ESM::REC_DCOU); + + listener.increaseProgress(1); + } + + void Actors::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type == ESM::REC_DCOU) + { + while (reader.isNextSub("ID__")) + { + std::string id = reader.getHString(); + int count; + reader.getHNT (count, "COUN"); + mDeathCount[id] = count; + } + } + } + + void Actors::clear() + { + mDeathCount.clear(); + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index cc95660dc..4784162f4 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -112,6 +112,12 @@ namespace MWMechanics /**ie AiCombat is active and the target is the actor **/ std::list getActorsFighting(const MWWorld::Ptr& actor); + void write (ESM::ESMWriter& writer, Loading::Listener& listener) const; + + void readRecord (ESM::ESMReader& reader, int32_t type); + + void clear(); // Clear death counter + private: PtrControllerMap mActors; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index f39a4b961..5737c380a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1050,4 +1050,24 @@ namespace MWMechanics std::list MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) { return mActors.getActorsFighting(actor); } + + int MechanicsManager::countSavedGameRecords() const + { + return 1; // Death counter + } + + void MechanicsManager::write(ESM::ESMWriter &writer, Loading::Listener &listener) const + { + mActors.write(writer, listener); + } + + void MechanicsManager::readRecord(ESM::ESMReader &reader, int32_t type) + { + mActors.readRecord(reader, type); + } + + void MechanicsManager::clear() + { + mActors.clear(); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index dcd12ee14..2511d5785 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -147,6 +147,14 @@ namespace MWMechanics virtual bool isAIActive(); virtual void playerLoaded(); + + virtual int countSavedGameRecords() const; + + virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type); + + virtual void clear(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 68cb91eb9..a459c87bb 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -45,6 +45,7 @@ void MWState::StateManager::cleanup (bool force) MWBase::Environment::get().getWorld()->clear(); MWBase::Environment::get().getWindowManager()->clear(); MWBase::Environment::get().getInputManager()->clear(); + MWBase::Environment::get().getMechanicsManager()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); @@ -205,7 +206,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot +MWBase::Environment::get().getWorld()->countSavedGameRecords() +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() - +MWBase::Environment::get().getWindowManager()->countSavedGameRecords(); + +MWBase::Environment::get().getWindowManager()->countSavedGameRecords() + +MWBase::Environment::get().getMechanicsManager()->countSavedGameRecords(); writer.setRecordCount (recordCount); writer.save (stream); @@ -226,6 +228,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWBase::Environment::get().getWorld()->write (writer, listener); MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer, listener); MWBase::Environment::get().getWindowManager()->write(writer, listener); + MWBase::Environment::get().getMechanicsManager()->write(writer, listener); // Ensure we have written the number of records that was estimated if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record @@ -357,6 +360,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); break; + case ESM::REC_DCOU: + + MWBase::Environment::get().getMechanicsManager()->readRecord(reader, n.val); + break; + default: // ignore invalid records diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index bdeb95291..f967af274 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -111,6 +111,7 @@ enum RecNameInts REC_ACTC = FourCC<'A','C','T','C'>::value, REC_MPRJ = FourCC<'M','P','R','J'>::value, REC_PROJ = FourCC<'P','R','O','J'>::value, + REC_DCOU = FourCC<'D','C','O','U'>::value, // format 1 REC_FILT = 0x544C4946 From 8114f4846963aa802bbd4624714b1afad2753275 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 19:06:09 +0200 Subject: [PATCH 061/226] Workaround per-frame resources leak in MyGUI. New vertex buffers were being created every frame. --- apps/openmw/mwgui/itemwidget.cpp | 31 +++++++++++++++++++++++++++++-- apps/openmw/mwgui/itemwidget.hpp | 3 +++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index a1ca5cb6c..7c79f8027 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -33,12 +33,26 @@ namespace MWGui void ItemWidget::setIcon(const std::string &icon) { + // HACK HACK HACK: Don't setImageTexture if it hasn't changed. + // There is a leak in MyGUI for each setImageTexture on the same widget. + // http://www.ogre3d.org/addonforums/viewtopic.php?f=17&t=30251 + if (mCurrentItemTexture == icon) + return; + + mCurrentItemTexture = icon; if (mItem) mItem->setImageTexture(icon); } void ItemWidget::setFrame(const std::string &frame, const MyGUI::IntCoord &coord) { + // HACK HACK HACK: Don't setImageTexture if it hasn't changed. + // There is a leak in MyGUI for each setImageTexture on the same widget. + // http://www.ogre3d.org/addonforums/viewtopic.php?f=17&t=30251 + if (mCurrentFrameTexture == frame) + return; + + mCurrentFrameTexture = frame; if (mFrame) { mFrame->setImageTexture(frame); @@ -69,8 +83,21 @@ namespace MWGui if (ptr.isEmpty()) { if (mFrame) - mFrame->setImageTexture(""); - mItem->setImageTexture(""); + { + // HACK HACK HACK: Don't setImageTexture if it hasn't changed. + // There is a leak in MyGUI for each setImageTexture on the same widget. + // http://www.ogre3d.org/addonforums/viewtopic.php?f=17&t=30251 + if (!mCurrentFrameTexture.empty()) + { + mFrame->setImageTexture(""); + mCurrentFrameTexture = ""; + } + } + if (!mCurrentItemTexture.empty()) + { + mCurrentItemTexture = ""; + mItem->setImageTexture(""); + } return; } diff --git a/apps/openmw/mwgui/itemwidget.hpp b/apps/openmw/mwgui/itemwidget.hpp index 3de98489d..5cdf71212 100644 --- a/apps/openmw/mwgui/itemwidget.hpp +++ b/apps/openmw/mwgui/itemwidget.hpp @@ -42,6 +42,9 @@ namespace MWGui MyGUI::ImageBox* mItem; MyGUI::ImageBox* mFrame; + + std::string mCurrentItemTexture; + std::string mCurrentFrameTexture; }; } From 3334078d4dc6ddab109c2178c7a99041e7428214 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 21:14:17 +0200 Subject: [PATCH 062/226] Add first person meshes to refraction render (Fixes #1481) --- apps/openmw/mwrender/refraction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/refraction.cpp b/apps/openmw/mwrender/refraction.cpp index f22968e9d..c2809a1aa 100644 --- a/apps/openmw/mwrender/refraction.cpp +++ b/apps/openmw/mwrender/refraction.cpp @@ -33,7 +33,7 @@ namespace MWRender Ogre::Viewport* vp = mRenderTarget->addViewport(mCamera); vp->setOverlaysEnabled(false); vp->setShadowsEnabled(false); - vp->setVisibilityMask(RV_Actors + RV_Misc + RV_Statics + RV_StaticsSmall + RV_Terrain + RV_Sky); + vp->setVisibilityMask(RV_Actors + RV_Misc + RV_Statics + RV_StaticsSmall + RV_Terrain + RV_Sky + RV_FirstPerson); vp->setMaterialScheme("water_refraction"); vp->setBackgroundColour (Ogre::ColourValue(0.18039, 0.23137, 0.25490)); mRenderTarget->setAutoUpdated(true); From c4e5872620ed7f6d1a6210b9dc0eaccddf6f42a2 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 13 Jun 2014 23:22:00 +0400 Subject: [PATCH 063/226] fix bug http://bugs.openmw.org/issues/1470 --- apps/openmw/mwmechanics/aicombat.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 94bb2cd90..78e304b31 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -377,8 +377,11 @@ namespace MWMechanics bool canMoveByZ = (actorClass.canSwim(actor) && world->isSwimming(actor)) || world->isFlying(actor); + // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack + bool inLOS = distantCombat ? world->getLOS(actor, target) : true; + // (within attack dist) || (not quite attack dist while following) - if(distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck) ) + if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck))) { //Melee and Close-up combat @@ -437,7 +440,7 @@ namespace MWMechanics else // remote pathfinding { bool preferShortcut = false; - bool inLOS = world->getLOS(actor, target); + if (!distantCombat) inLOS = world->getLOS(actor, target); // check if shortcut is available if(inLOS && (!isStuck || mReadyToAttack) From 4f742fd4686dc05055258c64fa496f705e6a83ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 21:53:47 +0200 Subject: [PATCH 064/226] Allow INFO records with no subrecords following DATA (Fixes #1486) --- components/esm/loadinfo.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index f86ad3b51..c9860dcef 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -18,6 +18,9 @@ void DialInfo::load(ESMReader &esm) esm.getHT(mData, 12); } + if (!esm.hasMoreSubs()) + return; + // What follows is somewhat spaghetti-ish, but it's worth if for // an extra speedup. INFO is by far the most common record type. From 54d8606b78ae0db14c4db865905ef493fd0300b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 22:03:52 +0200 Subject: [PATCH 065/226] Fix logging fatal exceptions to cerr, and log them to openmw.log --- apps/openmw/main.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 503ccaf25..b082ff908 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -294,9 +294,19 @@ private: int main(int argc, char**argv) { + // Some objects used to redirect cout and cerr + // Scope must be here, so this still works inside the catch block for logging exceptions std::streambuf* cout_rdbuf = std::cout.rdbuf (); std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; + + std::ostream oldcout(cout_rdbuf); + std::ostream oldcerr(cerr_rdbuf); + + boost::filesystem::ofstream logfile; + int ret = 0; try { @@ -310,20 +320,16 @@ int main(int argc, char**argv) std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to openmw.log - boost::filesystem::ofstream logfile (boost::filesystem::path( - cfgMgr.getLogPath() / "/openmw.log")); + logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/openmw.log")); - boost::iostreams::stream_buffer coutsb; - std::ostream oldcout(cout_rdbuf); coutsb.open (Tee(logfile, oldcout)); - std::cout.rdbuf (&coutsb); - - boost::iostreams::stream_buffer cerrsb; - std::ostream oldcerr(cerr_rdbuf); cerrsb.open (Tee(logfile, oldcerr)); + + std::cout.rdbuf (&coutsb); std::cerr.rdbuf (&cerrsb); #endif + #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE // Unix crash catcher if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached()) From fcab53b3f7ab7cf730ece238ba9fea795a86ef63 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 22:21:44 +0200 Subject: [PATCH 066/226] Fix locked mouse cursor due to SDL_SetRelativeMouseMode even when --no-grab was specified --- extern/sdl4ogre/sdlinputwrapper.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index 82db5ea99..b9314781e 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -239,9 +239,11 @@ namespace SFO mWrapPointer = false; - //eep, wrap the pointer manually if the input driver doesn't support - //relative positioning natively - bool success = SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0; + // eep, wrap the pointer manually if the input driver doesn't support + // relative positioning natively + // also use wrapping if no-grab was specified in options (SDL_SetRelativeMouseMode + // appears to eat the mouse cursor when pausing in a debugger) + bool success = mAllowGrab && SDL_SetRelativeMouseMode(relative ? SDL_TRUE : SDL_FALSE) == 0; if(relative && !success) mWrapPointer = true; From 162549b7af1e610706c45ba75878bf11a41dc0aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 22:23:24 +0200 Subject: [PATCH 067/226] Remove a wrong comment --- extern/sdl4ogre/sdlinputwrapper.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index b9314781e..f65dfb376 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -340,9 +340,6 @@ namespace SFO { //lifted from OIS's SDLKeyboard.cpp - //TODO: Consider switching to scancodes so we - //can properly support international keyboards - //look at SDL_GetKeyFromScancode and SDL_GetKeyName mKeyMap.insert( KeyMap::value_type(SDLK_UNKNOWN, OIS::KC_UNASSIGNED)); mKeyMap.insert( KeyMap::value_type(SDLK_ESCAPE, OIS::KC_ESCAPE) ); mKeyMap.insert( KeyMap::value_type(SDLK_1, OIS::KC_1) ); From ad3a78706ea1260c030001f3e361e6c166776ba8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 23:17:54 +0200 Subject: [PATCH 068/226] Bug #1417: Use fmod to wrap local rotations (more efficient and robust) --- apps/openmw/mwworld/worldimp.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 371f9edfe..20a3e495e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -54,6 +54,20 @@ using namespace Ogre; +namespace +{ + +// Wraps a value to (-PI, PI] +void wrap(float& rad) +{ + if (rad>0) + rad = std::fmod(rad+M_PI, 2.0*M_PI)-M_PI; + else + rad = std::fmod(rad-M_PI, 2.0*M_PI)+M_PI; +} + +} + namespace MWWorld { struct GameContentLoader : public ContentLoader @@ -1105,21 +1119,9 @@ namespace MWWorld ptr.getRefData().getLocalRotation().rot[1]=Ogre::Degree(y).valueRadians(); ptr.getRefData().getLocalRotation().rot[2]=Ogre::Degree(z).valueRadians(); - float fullRotateRad=Ogre::Degree(360).valueRadians(); - - while(ptr.getRefData().getLocalRotation().rot[0]>=fullRotateRad) - ptr.getRefData().getLocalRotation().rot[0]-=fullRotateRad; - while(ptr.getRefData().getLocalRotation().rot[1]>=fullRotateRad) - ptr.getRefData().getLocalRotation().rot[1]-=fullRotateRad; - while(ptr.getRefData().getLocalRotation().rot[2]>=fullRotateRad) - ptr.getRefData().getLocalRotation().rot[2]-=fullRotateRad; - - while(ptr.getRefData().getLocalRotation().rot[0]<=-fullRotateRad) - ptr.getRefData().getLocalRotation().rot[0]+=fullRotateRad; - while(ptr.getRefData().getLocalRotation().rot[1]<=-fullRotateRad) - ptr.getRefData().getLocalRotation().rot[1]+=fullRotateRad; - while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad) - ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad; + wrap(ptr.getRefData().getLocalRotation().rot[0]); + wrap(ptr.getRefData().getLocalRotation().rot[1]); + wrap(ptr.getRefData().getLocalRotation().rot[2]); Ogre::Quaternion worldRotQuat(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z); if (!ptr.getClass().isActor()) From 0637cde26793f984aa7b99bf23fde82950d7006e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jun 2014 23:40:49 +0200 Subject: [PATCH 069/226] Bug #1417: Make sure to reset all position/rotation fields when placing items --- apps/openmw/mwworld/worldimp.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 20a3e495e..066cae494 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1681,6 +1681,13 @@ namespace MWWorld MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos); + // Reset some position values that could be uninitialized if this item came from a container + LocalRotation& localRotation = dropped.getRefData().getLocalRotation(); + localRotation.rot[0] = 0; + localRotation.rot[1] = 0; + localRotation.rot[2] = 0; + dropped.getCellRef().setPosition(pos); + if (mWorldScene->isCellActive(*cell)) { if (dropped.getRefData().isEnabled()) { mWorldScene->addObjectToScene(dropped); From 832f1a4857d6095f3d3b8e3f0bf2b3f0907345ed Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 02:31:01 +0200 Subject: [PATCH 070/226] Fix merge mistake --- apps/openmw/mwmechanics/aicombat.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 31de1a888..a134e3dec 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -88,12 +88,23 @@ namespace MWMechanics AiCombat::AiCombat(const MWWorld::Ptr& actor) : mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()) + , mMinMaxAttackDuration() + , mMovement() { init(); mLastTargetPos = Ogre::Vector3(actor.getRefData().getPosition().pos); } + AiCombat::AiCombat(const ESM::AiSequence::AiCombat *combat) + : mMinMaxAttackDuration() + , mMovement() + { + mTargetActorId = combat->mTargetActorId; + + init(); + } + void AiCombat::init() { mTimerAttack = 0; @@ -638,13 +649,6 @@ namespace MWMechanics return new AiCombat(*this); } - AiCombat::AiCombat(const ESM::AiSequence::AiCombat *combat) - { - mTargetActorId = combat->mTargetActorId; - - init(); - } - void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const { std::auto_ptr combat(new ESM::AiSequence::AiCombat()); From dc2fefffc7d4980fe809bf28275b8179a49c15a1 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 14 Jun 2014 13:29:55 +0200 Subject: [PATCH 071/226] Fix missing M_PI define on Windows --- apps/openmw/mwworld/worldimp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 066cae494..4a29ae1d1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -52,6 +52,10 @@ #include "esmloader.hpp" #include "omwloader.hpp" +#ifdef _WIN32 +#define M_PI 3.14159 // Windows doesn't have this +#endif + using namespace Ogre; namespace From 6ea29812ac401d41787a7c1e376f38fa22994bbd Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 14 Jun 2014 13:54:01 +0200 Subject: [PATCH 072/226] Another way of getting M_PI --- apps/openmw/mwworld/worldimp.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4a29ae1d1..f8c537cce 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,4 +1,10 @@ #include "worldimp.hpp" + +#ifdef _WIN32 // For M_PI +#define _USE_MATH_DEFINES +#include +#endif + #ifdef _WIN32 #include #elif defined HAVE_UNORDERED_MAP @@ -52,10 +58,6 @@ #include "esmloader.hpp" #include "omwloader.hpp" -#ifdef _WIN32 -#define M_PI 3.14159 // Windows doesn't have this -#endif - using namespace Ogre; namespace From 5960d3f1d94105f1e49b4faf369ebc47cdcda909 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 14 Jun 2014 13:54:12 +0200 Subject: [PATCH 073/226] These files moved, leading to package failure --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d92930c9..1769f884d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -446,8 +446,8 @@ if(WIN32) INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") INSTALL(FILES "${OpenMW_SOURCE_DIR}/readme.txt" - "${OpenMW_SOURCE_DIR}/GPL3.txt" - "${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" + "${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt" + "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt" "${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" "${OpenMW_BINARY_DIR}/Release/openmw.exe" From c24e08dff6db50ce674e11ec26cad22186ccecc8 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 14 Jun 2014 14:43:24 +0200 Subject: [PATCH 074/226] Use Ogre::Math::PI --- apps/openmw/mwworld/worldimp.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f8c537cce..6a18298f9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,10 +1,5 @@ #include "worldimp.hpp" -#ifdef _WIN32 // For M_PI -#define _USE_MATH_DEFINES -#include -#endif - #ifdef _WIN32 #include #elif defined HAVE_UNORDERED_MAP @@ -67,9 +62,9 @@ namespace void wrap(float& rad) { if (rad>0) - rad = std::fmod(rad+M_PI, 2.0*M_PI)-M_PI; + rad = std::fmod(rad+Ogre::Math::PI, 2.0f*Ogre::Math::PI)-Ogre::Math::PI; else - rad = std::fmod(rad-M_PI, 2.0*M_PI)+M_PI; + rad = std::fmod(rad-Ogre::Math::PI, 2.0f*Ogre::Math::PI)+Ogre::Math::PI; } } From 6da061fd96cb22f525d62c9a81967988f191f342 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sat, 14 Jun 2014 23:36:57 +0400 Subject: [PATCH 075/226] fix http://bugs.openmw.org/issues/1340 Did I understand right that guards trigger combat after refusing pay gold/go to jail via script? --- .../mwmechanics/mechanicsmanagerimp.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index f39a4b961..7178a2556 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1024,8 +1024,27 @@ 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")) + { + for (Actors::PtrControllerMap::const_iterator iter = mActors.begin(); iter != mActors.end(); ++iter) + { + if (iter->first.getClass().isClass(iter->first, "Guard")) + { + MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); + if (aiSeq.getActivePackage()->getTypeId() == MWMechanics::AiPackage::TypeIdPursue) + { + aiSeq.stopPursuit(); + aiSeq.stack(MWMechanics::AiCombat(target), ptr); + } + } + } + } + } + // Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly if (ptr.getClass().isNpc()) MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); From a6ee4272aa890d6b721088a077c44e19370456ce Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sun, 15 Jun 2014 00:14:18 +0400 Subject: [PATCH 076/226] fix bug when you were able to kill somebody from large distance without being spotted --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7178a2556..2fa12bc45 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -853,8 +853,15 @@ namespace MWMechanics // Find all the actors within the alarm radius std::vector neighbors; - mActors.getObjectsInRange(Ogre::Vector3(player.getRefData().getPosition().pos), - esmStore.get().find("fAlarmRadius")->getInt(), neighbors); + + Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos); + int radius = esmStore.get().find("fAlarmRadius")->getInt(); + + mActors.getObjectsInRange(from, radius, neighbors); + + // victim should be considered even beyond alarm radius + if (from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) + neighbors.push_back(victim); int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); From abeb1d4ab31b287915d3baad91f46a2eafd418db Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 15 Jun 2014 09:37:24 +0200 Subject: [PATCH 077/226] Valgrind: Added constructor for KeyListT class, and added initialziation of ver member field in NIFFile class. Signed-off-by: Lukasz Gromanowski --- components/nif/niffile.cpp | 3 ++- components/nif/niffile.hpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 10847e8f4..84f4aacee 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -172,7 +172,8 @@ NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::cre /// Open a NIF stream. The name is used for error messages. NIFFile::NIFFile(const std::string &name, psudo_private_modifier) - : filename(name) + : ver(0) + , filename(name) { parse(); } diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index fbb64e4f7..bc9e99faf 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -161,6 +161,8 @@ struct KeyListT { unsigned int mInterpolationType; VecType mKeys; + KeyListT() : mInterpolationType(sLinearInterpolation) {} + //Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html) void read(NIFStream *nif, bool force=false) { From b20f8cc04f40b49d5c8c2f5ecf3a04d864e24ae4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 15:35:35 +0200 Subject: [PATCH 078/226] Don't clear AiSequence if there are no saved packages (eg for old savegames) --- apps/openmw/mwmechanics/aisequence.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 2d4dc2732..85e56af8b 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -300,7 +300,8 @@ void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) { - clear(); + if (!sequence.mPackages.empty()) + clear(); for (std::vector::const_iterator it = sequence.mPackages.begin(); it != sequence.mPackages.end(); ++it) From 395f98e47683998d98c32f61241a2b9b1647930a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 17:56:41 +0200 Subject: [PATCH 079/226] Fix triggering changed flag for all references when cell is visited The InsertFunctor for cells was calling localRotateObject() for all references which set the mChanged flag in RefData to true. Also clean up RefData interface slightly. --- apps/openmw/mwclass/creature.cpp | 3 +- apps/openmw/mwclass/npc.cpp | 3 +- apps/openmw/mwmechanics/aisequence.cpp | 4 +- apps/openmw/mwmechanics/pathgrid.cpp | 5 +- apps/openmw/mwrender/ripplesimulation.cpp | 2 +- .../mwscript/transformationextensions.cpp | 13 ++-- apps/openmw/mwworld/cellref.hpp | 3 +- apps/openmw/mwworld/cellstore.cpp | 3 + apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/player.cpp | 5 +- apps/openmw/mwworld/refdata.cpp | 14 +++- apps/openmw/mwworld/refdata.hpp | 6 +- apps/openmw/mwworld/scene.cpp | 44 +++++++++-- apps/openmw/mwworld/scene.hpp | 4 + apps/openmw/mwworld/worldimp.cpp | 76 +++++++++---------- apps/openmw/mwworld/worldimp.hpp | 3 +- 16 files changed, 122 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5744ebb9b..99b83a5ac 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -837,8 +837,7 @@ namespace MWClass ptr.getRefData().setCount(1); // Reset to original position - ESM::Position& pos = ptr.getRefData().getPosition(); - pos = ptr.getCellRef().getPosition(); + ptr.getRefData().setPosition(ptr.getCellRef().getPosition()); ptr.getRefData().setCustomData(NULL); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5ec192ab2..6d5ff9f7f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1325,8 +1325,7 @@ namespace MWClass ptr.getRefData().setCount(1); // Reset to original position - ESM::Position& pos = ptr.getRefData().getPosition(); - pos = ptr.getCellRef().getPosition(); + ptr.getRefData().setPosition(ptr.getCellRef().getPosition()); ptr.getRefData().setCustomData(NULL); } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 85e56af8b..05723c575 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -88,7 +88,7 @@ bool AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) else { // add new target only if current target (player) is farther - ESM::Position &targetPos = combat->getTarget().getRefData().getPosition(); + const ESM::Position &targetPos = combat->getTarget().getRefData().getPosition(); float distToCurrTarget = (Ogre::Vector3(targetPos.pos) - Ogre::Vector3(actorPos.pos)).length(); return (distToCurrTarget > distToTarget); @@ -153,7 +153,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor,float duration) } else { - ESM::Position &targetPos = target.getRefData().getPosition(); + const ESM::Position &targetPos = target.getRefData().getPosition(); float distTo = (Ogre::Vector3(targetPos.pos) - vActorPos).length(); if (distTo < nearestDist) diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index c3fa0a662..4983a4a4f 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -100,6 +100,9 @@ namespace MWMechanics if(!cell) return false; + if(mIsGraphConstructed) + return true; + mCell = cell; mIsExterior = cell->isExterior(); mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); @@ -107,8 +110,6 @@ namespace MWMechanics if(!mPathgrid) return false; - if(mIsGraphConstructed) - return true; mGraph.resize(mPathgrid->mPoints.size()); for(int i = 0; i < static_cast (mPathgrid->mEdges.size()); i++) diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 74216c1de..64b5e48c3 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -165,7 +165,7 @@ void RippleSimulation::addImpulses() // for non-player actors this is done in updateObjectCell it->mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); } - float* _currentPos = it->mPtr.getRefData().getPosition().pos; + const float* _currentPos = it->mPtr.getRefData().getPosition().pos; Ogre::Vector3 currentPos (_currentPos[0], _currentPos[1], _currentPos[2]); if ( (currentPos - it->mLastEmitPosition).length() > 2 diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index a944a31b8..138326ff0 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -578,7 +578,7 @@ namespace MWScript Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); runtime.pop(); - float *objRot = ptr.getRefData().getPosition().rot; + const float *objRot = ptr.getRefData().getPosition().rot; float ax = Ogre::Radian(objRot[0]).valueDegrees(); float ay = Ogre::Radian(objRot[1]).valueDegrees(); @@ -613,9 +613,12 @@ namespace MWScript if (!ptr.isInCell()) return; - ptr.getRefData().getLocalRotation().rot[0] = 0; - ptr.getRefData().getLocalRotation().rot[1] = 0; - ptr.getRefData().getLocalRotation().rot[2] = 0; + MWWorld::LocalRotation rot; + rot.rot[0] = 0; + rot.rot[1] = 0; + rot.rot[2] = 0; + ptr.getRefData().setLocalRotation(rot); + MWBase::Environment::get().getWorld()->rotateObject(ptr, 0,0,0,true); MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], ptr.getCellRef().getPosition().pos[1], ptr.getCellRef().getPosition().pos[2]); @@ -678,7 +681,7 @@ namespace MWScript Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); runtime.pop(); - float *objPos = ptr.getRefData().getPosition().pos; + const float *objPos = ptr.getRefData().getPosition().pos; if (axis == "x") { diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 4db362b1e..689671c02 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -42,7 +42,8 @@ namespace MWWorld float getScale() const; void setScale(float scale); - // Position and rotation of this object within the cell + // The *original* position and rotation as it was given in the Construction Set. + // Current position and rotation of the object is stored in RefData. ESM::Position getPosition() const; void setPosition (const ESM::Position& position); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 63cdbfb1a..3e29a9cae 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -410,6 +410,9 @@ namespace MWWorld loadRefs (store, esm); mState = State_Loaded; + + // TODO: the pathgrid graph only needs to be loaded for active cells, so move this somewhere else. + // In a simple test, loading the graph for all cells in MW + expansions took 200 ms mPathgridGraph.load(mCell); } } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index c13ecfab5..446519b87 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -366,7 +366,7 @@ namespace MWWorld Class::copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const { Ptr newPtr = copyToCell(ptr, cell); - newPtr.getRefData().getPosition() = pos; + newPtr.getRefData().setPosition(pos); return newPtr; } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 9913b888b..b88483bfe 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -44,8 +44,9 @@ namespace MWWorld cellRef.mRefID = "player"; mPlayer = LiveCellRef(cellRef, player); - float* playerPos = mPlayer.mData.getPosition().pos; - playerPos[0] = playerPos[1] = playerPos[2] = 0; + ESM::Position playerPos = mPlayer.mData.getPosition(); + playerPos.pos[0] = playerPos.pos[1] = playerPos.pos[2] = 0; + mPlayer.mData.setPosition(playerPos); } void Player::set(const ESM::NPC *player) diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 2e267b37c..3b7521e8d 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -188,15 +188,25 @@ namespace MWWorld mEnabled = false; } - ESM::Position& RefData::getPosition() + void RefData::setPosition(const ESM::Position& pos) { mChanged = true; + mPosition = pos; + } + + const ESM::Position& RefData::getPosition() + { return mPosition; } - LocalRotation& RefData::getLocalRotation() + void RefData::setLocalRotation(const LocalRotation& rot) { mChanged = true; + mLocalRotation = rot; + } + + const LocalRotation& RefData::getLocalRotation() + { return mLocalRotation; } diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index a8ffad684..fcd1437c9 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -100,9 +100,11 @@ namespace MWWorld void disable(); - ESM::Position& getPosition(); + void setPosition (const ESM::Position& pos); + const ESM::Position& getPosition(); - LocalRotation& getLocalRotation(); + void setLocalRotation (const LocalRotation& rotation); + const LocalRotation& getLocalRotation(); void setCustomData (CustomData *data); ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index f753ae1f4..b2faa1a01 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -22,6 +22,30 @@ namespace { + void updateObjectLocalRotation (const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, + MWRender::RenderingManager& rendering) + { + if (ptr.getRefData().getBaseNode() != NULL) + { + Ogre::Quaternion worldRotQuat(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z); + if (!ptr.getClass().isActor()) + worldRotQuat = Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* worldRotQuat; + + float x = ptr.getRefData().getLocalRotation().rot[0]; + float y = ptr.getRefData().getLocalRotation().rot[1]; + float z = ptr.getRefData().getLocalRotation().rot[2]; + + Ogre::Quaternion rot(Ogre::Radian(z), Ogre::Vector3::NEGATIVE_UNIT_Z); + if (!ptr.getClass().isActor()) + rot = Ogre::Quaternion(Ogre::Radian(x), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Radian(y), Ogre::Vector3::NEGATIVE_UNIT_Y)*rot; + + ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); + physics.rotateObject(ptr); + } + } + struct InsertFunctor { MWWorld::CellStore& mCell; @@ -60,11 +84,7 @@ namespace mRendering.addObject (ptr); ptr.getClass().insertObject (ptr, mPhysics); - float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); - MWBase::Environment::get().getWorld()->localRotateObject (ptr, ax, ay, az); - + updateObjectLocalRotation(ptr, mPhysics, mRendering); MWBase::Environment::get().getWorld()->scaleObject (ptr, ptr.getCellRef().getScale()); ptr.getClass().adjustPosition (ptr); } @@ -85,6 +105,20 @@ namespace namespace MWWorld { + void Scene::updateObjectLocalRotation (const Ptr& ptr) + { + ::updateObjectLocalRotation(ptr, *mPhysics, mRendering); + } + + void Scene::updateObjectRotation (const Ptr& ptr) + { + if(ptr.getRefData().getBaseNode() != 0) + { + mRendering.rotateObject(ptr); + mPhysics->rotateObject(ptr); + } + } + void Scene::update (float duration, bool paused) { if (mNeedMapUpdate) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 449644754..e0eeee187 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -103,6 +103,10 @@ namespace MWWorld void removeObjectFromScene (const Ptr& ptr); ///< Remove an object from the scene, but not from the world model. + void updateObjectLocalRotation (const Ptr& ptr); + + void updateObjectRotation (const Ptr& ptr); + bool isCellActive(const CellStore &cell); Ptr searchPtrViaHandle (const std::string& handle); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6a18298f9..e6049c185 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -957,12 +957,14 @@ namespace MWWorld void World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z) { - ESM::Position &pos = ptr.getRefData().getPosition(); + ESM::Position pos = ptr.getRefData().getPosition(); pos.pos[0] = x; pos.pos[1] = y; pos.pos[2] = z; + ptr.getRefData().setPosition(pos); + Ogre::Vector3 vec(x, y, z); CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; @@ -1068,7 +1070,8 @@ namespace MWWorld const float two_pi = Ogre::Math::TWO_PI; const float pi = Ogre::Math::PI; - float *objRot = ptr.getRefData().getPosition().rot; + ESM::Position pos = ptr.getRefData().getPosition(); + float *objRot = pos.rot; if(adjust) { objRot[0] += rot.x; @@ -1105,43 +1108,33 @@ namespace MWWorld while(objRot[2] < -pi) objRot[2] += two_pi; while(objRot[2] > pi) objRot[2] -= two_pi; - if(ptr.getRefData().getBaseNode() != 0) - { - mRendering->rotateObject(ptr); - mPhysics->rotateObject(ptr); - } + ptr.getRefData().setPosition(pos); + + mWorldScene->updateObjectRotation(ptr); } void World::localRotateObject (const Ptr& ptr, float x, float y, float z) { - if (ptr.getRefData().getBaseNode() != 0) { + 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(); - ptr.getRefData().getLocalRotation().rot[0]=Ogre::Degree(x).valueRadians(); - ptr.getRefData().getLocalRotation().rot[1]=Ogre::Degree(y).valueRadians(); - ptr.getRefData().getLocalRotation().rot[2]=Ogre::Degree(z).valueRadians(); + wrap(rot.rot[0]); + wrap(rot.rot[1]); + wrap(rot.rot[2]); - wrap(ptr.getRefData().getLocalRotation().rot[0]); - wrap(ptr.getRefData().getLocalRotation().rot[1]); - wrap(ptr.getRefData().getLocalRotation().rot[2]); + ptr.getRefData().setLocalRotation(rot); - Ogre::Quaternion worldRotQuat(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z); - if (!ptr.getClass().isActor()) - worldRotQuat = Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* - Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* worldRotQuat; - - Ogre::Quaternion rot(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z); - if (!ptr.getClass().isActor()) - rot = Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)* - Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)*rot; - - ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); - mPhysics->rotateObject(ptr); + mWorldScene->updateObjectLocalRotation(ptr); } } void World::adjustPosition(const Ptr &ptr) { - Ogre::Vector3 pos (ptr.getRefData().getPosition().pos); + ESM::Position pos (ptr.getRefData().getPosition()); if(!ptr.getRefData().getBaseNode()) { @@ -1149,21 +1142,23 @@ namespace MWWorld return; } - float terrainHeight = mRendering->getTerrainHeightAt(pos); + float terrainHeight = mRendering->getTerrainHeightAt(Ogre::Vector3(pos.pos)); - if (pos.z < terrainHeight) - pos.z = terrainHeight; + if (pos.pos[2] < terrainHeight) + pos.pos[2] = terrainHeight; - ptr.getRefData().getPosition().pos[2] = pos.z + 20; // place slightly above. will snap down to ground with code below + pos.pos[2] += 20; // place slightly above. will snap down to ground with code below + + ptr.getRefData().setPosition(pos); if (!isFlying(ptr)) { Ogre::Vector3 traced = mPhysics->traceDown(ptr); - if (traced.z < pos.z) - pos.z = traced.z; + if (traced.z < pos.pos[2]) + pos.pos[2] = traced.z; } - moveObject(ptr, ptr.getCell(), pos.x, pos.y, pos.z); + moveObject(ptr, ptr.getCell(), pos.pos[0], pos.pos[1], pos.pos[2]); } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) @@ -1429,7 +1424,7 @@ namespace MWWorld // cast a ray from player to sun to detect if the sun is visible // this is temporary until we find a better place to put this code // currently its here because we need to access the physics system - float* p = mPlayer->getPlayer().getRefData().getPosition().pos; + const float* p = mPlayer->getPlayer().getRefData().getPosition().pos; Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); } @@ -1683,10 +1678,11 @@ namespace MWWorld object.getClass().copyToCell(object, *cell, pos); // Reset some position values that could be uninitialized if this item came from a container - LocalRotation& localRotation = dropped.getRefData().getLocalRotation(); + LocalRotation localRotation; localRotation.rot[0] = 0; localRotation.rot[1] = 0; localRotation.rot[2] = 0; + dropped.getRefData().setLocalRotation(localRotation); dropped.getCellRef().setPosition(pos); if (mWorldScene->isCellActive(*cell)) { @@ -1785,7 +1781,7 @@ namespace MWWorld bool World::isSubmerged(const MWWorld::Ptr &object) const { - float *fpos = object.getRefData().getPosition().pos; + const float *fpos = object.getRefData().getPosition().pos; Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); @@ -1798,7 +1794,7 @@ namespace MWWorld World::isSwimming(const MWWorld::Ptr &object) const { /// \todo add check ifActor() - only actors can swim - float *fpos = object.getRefData().getPosition().pos; + const float *fpos = object.getRefData().getPosition().pos; Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); /// \fixme 3/4ths submerged? @@ -2031,9 +2027,9 @@ namespace MWWorld if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled()) return false; // cannot get LOS unless both NPC's are enabled Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); - float* pos1 = npc.getRefData().getPosition().pos; + const float* pos1 = npc.getRefData().getPosition().pos; Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); - float* pos2 = targetNpc.getRefData().getPosition().pos; + const float* pos2 = targetNpc.getRefData().getPosition().pos; btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z); btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5b493efcc..8307d53a4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -336,10 +336,11 @@ namespace MWWorld virtual void scaleObject (const Ptr& ptr, float scale); - /// Rotates object, uses degrees + /// World rotates object, uses degrees /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); + /// Local rotates object, uses degrees virtual void localRotateObject (const Ptr& ptr, float x, float y, float z); virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos); From 31a4e10c6f67bcf3041648d321994369434c5eb9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 19:12:49 +0200 Subject: [PATCH 080/226] Fix incorrect trigger of change flag for static doors --- apps/openmw/mwclass/door.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 12645c9f3..0a5b774f5 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -57,11 +57,13 @@ namespace MWClass physics.addObject(ptr); // Resume the door's opening/closing animation if it wasn't finished - ensureCustomData(ptr); - const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); - if (customData.mDoorState > 0) + if (ptr.getRefData().getCustomData()) { - MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false); + const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + if (customData.mDoorState > 0) + { + MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false); + } } } From 7afd2ca6141246a55225d83a8ee13979dc826d28 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 19:13:40 +0200 Subject: [PATCH 081/226] Consider moved references in CellStore::listRefs --- apps/openmw/mwworld/cellstore.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 3e29a9cae..ce8d77758 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -450,10 +450,25 @@ namespace MWWorld if (deleted) continue; + // Don't list reference if it was moved to a different cell. + ESM::MovedCellRefTracker::const_iterator iter = + std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); + if (iter != mCell->mMovedRefs.end()) { + continue; + } + mIds.push_back (Misc::StringUtils::lowerCase (ref.mRefID)); } } + // List moved references, from separately tracked list. + for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) + { + ESM::CellRef &ref = const_cast(*it); + + mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); + } + std::sort (mIds.begin(), mIds.end()); } From afbd5162ee947c893284c0cec1b1f19ed23a65bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 20:02:49 +0200 Subject: [PATCH 082/226] Move AiWander arrival check to time-critical section --- apps/openmw/mwmechanics/aiwander.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 6a68397fd..79151e896 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -153,6 +153,17 @@ namespace MWMechanics } } + // Are we there yet? + if(mWalking && + mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mChooseAction = true; + mHasReturnPosition = false; + } + if(mWalking) // have not yet reached the destination { // turn towards the next point in mPath @@ -524,17 +535,6 @@ namespace MWMechanics } } - // Are we there yet? - if(mWalking && - mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) - { - stopWalking(actor); - mMoveNow = false; - mWalking = false; - mChooseAction = true; - mHasReturnPosition = false; - } - return false; // AiWander package not yet completed } From 8d8015ce184dfacb214758c139b6fb3e0ed3b002 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 20:05:23 +0200 Subject: [PATCH 083/226] Remove a squareroot in pathfinding --- apps/openmw/mwmechanics/pathfinding.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index d77a35ea4..62c1756c2 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -90,12 +90,12 @@ namespace namespace MWMechanics { - float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z) + float sqrDistanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z) { x -= point.mX; y -= point.mY; z -= point.mZ; - return sqrt(x * x + y * y + 0.1 * z * z); + return (x * x + y * y + 0.1 * z * z); } float distance(ESM::Pathgrid::Point point, float x, float y, float z) @@ -296,7 +296,7 @@ namespace MWMechanics return true; ESM::Pathgrid::Point nextPoint = *mPath.begin(); - if(distanceZCorrected(nextPoint, x, y, z) < 64) + if(sqrDistanceZCorrected(nextPoint, x, y, z) < 64*64) { mPath.pop_front(); if(mPath.empty()) mIsPathConstructed = false; @@ -311,7 +311,7 @@ namespace MWMechanics return true; ESM::Pathgrid::Point nextPoint = *mPath.begin(); - if(distanceZCorrected(nextPoint, x, y, z) < 64) + if(sqrDistanceZCorrected(nextPoint, x, y, z) < 64*64) { mPath.pop_front(); if(mPath.empty()) From 88f8f25ffa6f745a6c49d9f874c03937100e8f33 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 20:12:52 +0200 Subject: [PATCH 084/226] Don't allow scripted dialogue with dead actors (Fixes #1493) --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index aa7df1fd4..678e040c1 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -125,6 +125,10 @@ namespace MWDialogue void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { + // Dialogue with dead actor (e.g. through script) should not be allowed. + if (actor.getClass().getCreatureStats(actor).isDead()) + return; + mLastTopic = ""; mPermanentDispositionChange = 0; mTemporaryDispositionChange = 0; From 1adb81abd0f4933c4e584b0c4c7e15e0acac17ea Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 20:25:44 +0200 Subject: [PATCH 085/226] Sort using ciLess, not ciEqual --- 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 678e040c1..be0a1b163 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -406,7 +406,7 @@ namespace MWDialogue win->setServices (windowServices); // sort again, because the previous sort was case-sensitive - keywordList.sort(Misc::StringUtils::ciEqual); + keywordList.sort(Misc::StringUtils::ciLess); win->setKeywords(keywordList); mChoice = choice; From e2884378857f7c6a1a8af8dcd2c81be451e5da3c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 21:27:16 +0200 Subject: [PATCH 086/226] Disable all other animations on death (Fixes #1483) --- apps/openmw/mwmechanics/character.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c54d00fb0..a5964bdf4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -435,9 +435,23 @@ void CharacterController::playDeath(float startpoint, CharacterState death) mPtr.getClass().getCreatureStats(mPtr).setDeathAnimation(mDeathState - CharState_Death1); // For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually. + // Note that these animations wouldn't actually be visible (due to the Death animation's priority being higher). + // However, they could still trigger text keys, such as Hit events, or sounds. mMovementState = CharState_None; mAnimation->disable(mCurrentMovement); mCurrentMovement = ""; + mUpperBodyState = UpperCharState_Nothing; + mAnimation->disable(mCurrentWeapon); + mCurrentWeapon = ""; + mHitState = CharState_None; + mAnimation->disable(mCurrentHit); + mCurrentHit = ""; + mIdleState = CharState_None; + mAnimation->disable(mCurrentIdle); + mCurrentIdle = ""; + mJumpState = JumpState_None; + mAnimation->disable(mCurrentJump); + mCurrentJump = ""; mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", startpoint, 0); From 9e6d21d95bac5a2fa528c2c8fb92df6d914e2e03 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jun 2014 21:52:54 +0200 Subject: [PATCH 087/226] Clear message boxes in windowmanager cleanup (Fixes #1496) --- apps/openmw/mwgui/messagebox.cpp | 17 +++++++++++++++++ apps/openmw/mwgui/messagebox.hpp | 3 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 1 + 3 files changed, 21 insertions(+) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index d73a0e122..c4b204de7 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -25,6 +25,23 @@ namespace MWGui } } + void MessageBoxManager::clear() + { + delete mInterMessageBoxe; + mInterMessageBoxe = NULL; + + std::vector::iterator it(mMessageBoxes.begin()); + for (; it != mMessageBoxes.end(); ++it) + { + if (*it == mStaticMessageBox) + mStaticMessageBox = NULL; + delete *it; + } + mMessageBoxes.clear(); + + mLastButtonPressed = -1; + } + void MessageBoxManager::onFrame (float frameDuration) { std::vector::iterator it; diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index caa37008c..406d98c48 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -30,6 +30,9 @@ namespace MWGui bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); + /// Remove all message boxes + void clear(); + bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index b49bfbfa6..3f239e0e3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1502,6 +1502,7 @@ namespace MWGui { mMap->clear(); mQuickKeysMenu->clear(); + mMessageBoxManager->clear(); mTrainingWindow->resetReference(); mDialogueWindow->resetReference(); From 326c74750855e8bfa43ff28d5a6a1d420525c74b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jun 2014 00:09:12 +0200 Subject: [PATCH 088/226] Don't run scripts when in pause menu (Fixes #1495) --- apps/openmw/engine.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 46f384f59..f8b4c9856 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -91,7 +91,11 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (mUseSound) MWBase::Environment::get().getSoundManager()->update(frametime); - bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); + // GUI active? Most game processing will be paused, but scripts still run. + bool guiActive = MWBase::Environment::get().getWindowManager()->isGuiMode(); + + // Main menu opened? Then scripts are also paused. + bool paused = MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu); // update game state MWBase::Environment::get().getStateManager()->update (frametime); @@ -99,15 +103,18 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { - // global scripts - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - - // local scripts - executeLocalScripts(); - - MWBase::Environment::get().getWorld()->markCellAsUnchanged(); - if (!paused) + { + // global scripts + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + + // local scripts + executeLocalScripts(); + + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + } + + if (!guiActive) MWBase::Environment::get().getWorld()->advanceTime( frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); } @@ -118,14 +125,14 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::StateManager::State_NoGame) { MWBase::Environment::get().getMechanicsManager()->update(frametime, - paused); + guiActive); } if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_Running) { MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); - if(!paused && player.getClass().getCreatureStats(player).isDead()) + if(!guiActive && player.getClass().getCreatureStats(player).isDead()) MWBase::Environment::get().getStateManager()->endGame(); } @@ -133,7 +140,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (MWBase::Environment::get().getStateManager()->getState()!= MWBase::StateManager::State_NoGame) { - MWBase::Environment::get().getWorld()->update(frametime, paused); + MWBase::Environment::get().getWorld()->update(frametime, guiActive); } // update GUI From 77388fe2cef8005fa074b3292bcc2b455b77cae0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jun 2014 15:15:59 +0200 Subject: [PATCH 089/226] Implement BetaComment instruction (dumps reference info) --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++- apps/openmw/mwscript/miscextensions.cpp | 43 +++++++++++++++++++++++++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 2 ++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 1576bf0af..ab69bc4b4 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -396,5 +396,7 @@ op 0x2000243: GetFactionReaction op 0x2000244: Activate, explicit op 0x2000245: ClearInfoActor op 0x2000246: ClearInfoActor, explicit +op 0x2000247: BetaComment +op 0x2000248: BetaComment, explicit -opcodes 0x2000247-0x3ffffff unused +opcodes 0x2000249-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index cd76f3b9c..17824fe49 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -859,6 +859,47 @@ namespace MWScript } }; + template + class OpBetaComment : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::stringstream msg; + + msg << "Content file: "; + + if (ptr.getCellRef().getRefNum().mContentFile == -1) + msg << "[None]" << std::endl; + else + { + std::vector contentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); + assert (contentFiles.size() > ptr.getCellRef().getRefNum().mContentFile); + msg << contentFiles[ptr.getCellRef().getRefNum().mContentFile] << std::endl; + } + + msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl; + + if (ptr.isInCell()) + { + MWWorld::CellStore* cell = ptr.getCell(); + msg << "Cell: " << MWBase::Environment::get().getWorld()->getCellName(cell) << std::endl; + + Ogre::Vector3 pos (ptr.getRefData().getPosition().pos); + msg << "Coordinates: " << pos << std::endl; + } + + std::string notes = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + if (!notes.empty()) + msg << "Notes: " << notes << std::endl; + + runtime.getContext().report(msg.str()); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -932,6 +973,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcInJail, new OpGetPcInJail); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling); + interpreter.installSegment5 (Compiler::Misc::opcodeBetaComment, new OpBetaComment); + interpreter.installSegment5 (Compiler::Misc::opcodeBetaCommentExplicit, new OpBetaComment); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index b9e36db80..c4b078ae8 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -282,6 +282,8 @@ namespace Compiler extensions.registerInstruction ("enablelevitation", "", opcodeEnableLevitation); extensions.registerFunction ("getpcinjail", 'l', "", opcodeGetPcInJail); extensions.registerFunction ("getpctraveling", 'l', "", opcodeGetPcTraveling); + extensions.registerInstruction ("betacomment", "S", opcodeBetaComment, opcodeBetaCommentExplicit); + extensions.registerInstruction ("bc", "S", opcodeBetaComment, opcodeBetaCommentExplicit); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 14f4db060..8716cdd18 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -207,6 +207,8 @@ namespace Compiler const int opcodeGetLockedExplicit = 0x20001c8; const int opcodeGetEffect = 0x20001cf; const int opcodeGetEffectExplicit = 0x20001d0; + const int opcodeBetaComment = 0x2000247; + const int opcodeBetaCommentExplicit = 0x2000248; const int opcodeAddSoulGem = 0x20001f3; const int opcodeAddSoulGemExplicit = 0x20001f4; const int opcodeRemoveSoulGem = 0x20027; From fd59a4a79e7142c8167f0c47386ee29a6cdf0563 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jun 2014 15:58:01 +0200 Subject: [PATCH 090/226] Fix some doxygen typos --- apps/opencs/model/world/columns.hpp | 2 +- apps/opencs/view/world/util.hpp | 4 ++-- apps/openmw/mwworld/refdata.hpp | 2 +- components/compiler/controlparser.hpp | 2 +- components/compiler/extensions.hpp | 2 +- components/compiler/fileparser.hpp | 2 +- components/compiler/output.hpp | 2 +- components/compiler/scanner.hpp | 2 +- components/compiler/scriptparser.hpp | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 855e89cad..1dba99b89 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -212,7 +212,7 @@ namespace CSMWorld bool hasEnums (ColumnId column); std::vector getEnums (ColumnId column); - ///< Returns an empty vector, if \æ column isn't an enum type column. + ///< Returns an empty vector, if \a column isn't an enum type column. } } diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 1c7e37818..c95a24982 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -73,9 +73,9 @@ namespace CSVWorld ~CommandDelegateFactoryCollection(); void add (CSMWorld::ColumnBase::Display display, CommandDelegateFactory *factory); - ///< The ownership of \æ factory is transferred to *this. + ///< The ownership of \a factory is transferred to *this. /// - /// This function must not be called more than once per value of \æ display. + /// This function must not be called more than once per value of \a display. CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, QUndoStack& undoStack, QObject *parent) const; diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index fcd1437c9..753230e7f 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -107,7 +107,7 @@ namespace MWWorld const LocalRotation& getLocalRotation(); void setCustomData (CustomData *data); - ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is + ///< Set custom data (potentially replacing old custom data). The ownership of \a data is /// transferred to this. CustomData *getCustomData(); diff --git a/components/compiler/controlparser.hpp b/components/compiler/controlparser.hpp index 1175a0ed5..59958fd90 100644 --- a/components/compiler/controlparser.hpp +++ b/components/compiler/controlparser.hpp @@ -52,7 +52,7 @@ namespace Compiler Literals& literals); void appendCode (std::vector& code) const; - ///< store generated code in \æ code. + ///< store generated code in \a code. virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index 3f91ca357..418cbb402 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -105,7 +105,7 @@ namespace Compiler ///< Append code for function to \a code. void listKeywords (std::vector& keywords) const; - ///< Append all known keywords to \æ kaywords. + ///< Append all known keywords to \a kaywords. }; } diff --git a/components/compiler/fileparser.hpp b/components/compiler/fileparser.hpp index 13c7fb537..00f96cff0 100644 --- a/components/compiler/fileparser.hpp +++ b/components/compiler/fileparser.hpp @@ -31,7 +31,7 @@ namespace Compiler ///< Return script name. void getCode (std::vector& code) const; - ///< store generated code in \æ code. + ///< store generated code in \a code. const Locals& getLocals() const; ///< get local variable declarations. diff --git a/components/compiler/output.hpp b/components/compiler/output.hpp index 37b88cee9..c544fb715 100644 --- a/components/compiler/output.hpp +++ b/components/compiler/output.hpp @@ -22,7 +22,7 @@ namespace Compiler Output (Locals& locals); void getCode (std::vector& code) const; - ///< store generated code in \æ code. + ///< store generated code in \a code. const Literals& getLiterals() const; diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp index 19f4ca96a..344ae0582 100644 --- a/components/compiler/scanner.hpp +++ b/components/compiler/scanner.hpp @@ -119,7 +119,7 @@ namespace Compiler ///< put back a keyword token void listKeywords (std::vector& keywords); - ///< Append all known keywords to \æ kaywords. + ///< Append all known keywords to \a kaywords. }; } diff --git a/components/compiler/scriptparser.hpp b/components/compiler/scriptparser.hpp index 000244c79..edabb9c5c 100644 --- a/components/compiler/scriptparser.hpp +++ b/components/compiler/scriptparser.hpp @@ -27,7 +27,7 @@ namespace Compiler bool end = false); void getCode (std::vector& code) const; - ///< store generated code in \æ code. + ///< store generated code in \a code. virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); From a0f9a6718ffbfe4cc5cefd87a30492468a403662 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jun 2014 16:00:29 +0200 Subject: [PATCH 091/226] Disable fProjectileThrownStoreChance when shooting at player (Fixes #1490) --- apps/openmw/mwmechanics/combat.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index be62e8315..6feb5879b 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -231,9 +231,13 @@ namespace MWMechanics if (damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); - float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); - if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f) - victim.getClass().getContainerStore(victim).add(projectile, 1, victim); + // Arrows shot at enemies have a chance to turn up in their inventory + if (victim != MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); + if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f) + victim.getClass().getContainerStore(victim).add(projectile, 1, victim); + } victim.getClass().onHit(victim, damage, true, projectile, attacker, true); } From ee2b81763ea336684c1fae4e2f8a3e22e22d696d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jun 2014 21:19:37 +0200 Subject: [PATCH 092/226] Savegame: Store AiSettings and summoned creatures CreatureStats state is now completely stored (Closes #1174) Also play VFX_Summon_Start and VFX_Summon_End visual effects. --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwmechanics/actors.cpp | 45 ++++++++++++++++++----- apps/openmw/mwmechanics/creaturestats.cpp | 24 ++++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 16 ++++---- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 5 +++ apps/openmw/mwworld/worldimp.hpp | 2 + components/esm/creaturestats.cpp | 40 ++++++++++++++++++++ components/esm/creaturestats.hpp | 6 +++ 9 files changed, 124 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9fb91398d..df75ce64d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -515,6 +515,8 @@ namespace MWBase /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; + virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0; + virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 43857e519..e9e9fbe28 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -20,6 +20,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwrender/animation.hpp" + #include "npcstats.hpp" #include "creaturestats.hpp" #include "movement.hpp" @@ -529,7 +531,8 @@ namespace MWMechanics for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it) { - bool found = creatureStats.mSummonedCreatures.find(it->first) != creatureStats.mSummonedCreatures.end(); + std::map& creatureMap = creatureStats.getSummonedCreatureMap(); + bool found = creatureMap.find(it->first) != creatureMap.end(); int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; if (found != (magnitude > 0)) { @@ -563,17 +566,25 @@ namespace MWMechanics summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); int creatureActorId = summonedCreatureStats.getActorId(); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); - // TODO: VFX_SummonStart, VFX_SummonEnd - creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId)); + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed); + if (anim) + { + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_Start"); + if (fx) + anim->addEffect("meshes\\" + fx->mModel, -1, false); + } + + creatureMap.insert(std::make_pair(it->first, creatureActorId)); } } else { // Summon lifetime has expired. Try to delete the creature. - int actorId = creatureStats.mSummonedCreatures[it->first]; - creatureStats.mSummonedCreatures.erase(it->first); + int actorId = creatureMap[it->first]; + creatureMap.erase(it->first); MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId); if (!ptr.isEmpty()) @@ -581,24 +592,38 @@ namespace MWMechanics // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation // plays though, which is a rather lame exploit in vanilla. MWBase::Environment::get().getWorld()->deleteObject(ptr); - creatureStats.mSummonedCreatures.erase(it->first); + + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_End"); + if (fx) + MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, + "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); } else { // We didn't find the creature. It's probably in an inactive cell. // Add to graveyard so we can delete it when the cell becomes active. - creatureStats.mSummonGraveyard.push_back(actorId); + std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard(); + graveyard.push_back(actorId); } } } } - for (std::vector::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); ) + std::vector& graveyard = creatureStats.getSummonedCreatureGraveyard(); + for (std::vector::iterator it = graveyard.begin(); it != graveyard.end(); ) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); if (!ptr.isEmpty()) { - it = creatureStats.mSummonGraveyard.erase(it); + it = graveyard.erase(it); + + const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get() + .search("VFX_Summon_End"); + if (fx) + MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel, + "", Ogre::Vector3(ptr.getRefData().getPosition().pos)); + MWBase::Environment::get().getWorld()->deleteObject(ptr); } else diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 7396b2735..e26e7a8ba 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -504,6 +504,13 @@ namespace MWMechanics mSpells.writeState(state.mSpells); mActiveSpells.writeState(state.mActiveSpells); mAiSequence.writeState(state.mAiSequence); + + state.mSummonedCreatureMap = mSummonedCreatures; + state.mSummonGraveyard = mSummonGraveyard; + + state.mHasAiSettings = true; + for (int i=0; i<4; ++i) + mAiSettings[i].writeState (state.mAiSettings[i]); } void CreatureStats::readState (const ESM::CreatureStats& state) @@ -545,6 +552,13 @@ namespace MWMechanics mSpells.readState(state.mSpells); mActiveSpells.readState(state.mActiveSpells); mAiSequence.readState(state.mAiSequence); + + mSummonedCreatures = state.mSummonedCreatureMap; + mSummonGraveyard = state.mSummonGraveyard; + + if (state.mHasAiSettings) + for (int i=0; i<4; ++i) + mAiSettings[i].readState(state.mAiSettings[i]); } void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime) @@ -605,4 +619,14 @@ namespace MWMechanics { mDeathAnimation = index; } + + std::map& CreatureStats::getSummonedCreatureMap() + { + return mSummonedCreatures; + } + + std::vector& CreatureStats::getSummonedCreatureGraveyard() + { + return mSummonGraveyard; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 8b2398dfb..d02af8fb6 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -67,6 +67,12 @@ namespace MWMechanics // The index of the death animation that was played unsigned char mDeathAnimation; + // + std::map mSummonedCreatures; + // Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet. + // This may be necessary when the creature is in an inactive cell. + std::vector mSummonGraveyard; + protected: // These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods. bool mIsWerewolf; @@ -208,6 +214,9 @@ namespace MWMechanics void setBlock(bool value); bool getBlock() const; + std::map& getSummonedCreatureMap(); + std::vector& getSummonedCreatureGraveyard(); + enum Flag { Flag_ForceRun = 1, @@ -233,13 +242,6 @@ namespace MWMechanics // TODO: Put it somewhere else? std::set mBoundItems; - // TODO: store in savegame - // TODO: encapsulate? - // - std::map mSummonedCreatures; - // Contains summoned creatures with an expired lifetime that have not been deleted yet. - std::vector mSummonGraveyard; - void writeState (ESM::CreatureStats& state) const; void readState (const ESM::CreatureStats& state); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index b2d69b79a..297af558f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -210,7 +210,7 @@ public: /** * @brief Add an effect mesh attached to a bone or the insert scene node * @param model - * @param effectId An ID for this effect. Note that adding the same ID again won't add another effect. + * @param effectId An ID for this effect by which you can identify it later. If this is not wanted, set to -1. * @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true, * you need to remove it manually using removeEffect when the effect should end. * @param bonename Bone to attach to, or empty string to use the scene node instead diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e6049c185..625942da7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2704,6 +2704,11 @@ namespace MWWorld mRendering->spawnEffect(model, texture, worldPosition); } + void World::spawnEffect(const std::string &model, const std::string &textureOverride, const Vector3 &worldPos) + { + mRendering->spawnEffect(model, textureOverride, worldPos); + } + void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, const std::string& id, const std::string& sourceName) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8307d53a4..ac1244652 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -585,6 +585,8 @@ namespace MWWorld /// Spawn a blood effect for \a ptr at \a worldPosition virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition); + virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos); + virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 8151091b2..84902d8c2 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -80,6 +80,31 @@ void ESM::CreatureStats::load (ESMReader &esm) mSpells.load(esm); mActiveSpells.load(esm); mAiSequence.load(esm); + + while (esm.isNextSub("SUMM")) + { + int magicEffect; + esm.getHT(magicEffect); + int actorId; + esm.getHNT (actorId, "ACID"); + mSummonedCreatureMap[magicEffect] = actorId; + } + + while (esm.isNextSub("GRAV")) + { + int actorId; + esm.getHT(actorId); + mSummonGraveyard.push_back(actorId); + } + + mHasAiSettings = false; + esm.getHNOT(mHasAiSettings, "AISE"); + + if (mHasAiSettings) + { + for (int i=0; i<4; ++i) + mAiSettings[i].load(esm); + } } void ESM::CreatureStats::save (ESMWriter &esm) const @@ -162,4 +187,19 @@ void ESM::CreatureStats::save (ESMWriter &esm) const mSpells.save(esm); mActiveSpells.save(esm); mAiSequence.save(esm); + + for (std::map::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it) + { + esm.writeHNT ("SUMM", it->first); + esm.writeHNT ("ACID", it->second); + } + + for (std::vector::const_iterator it = mSummonGraveyard.begin(); it != mSummonGraveyard.end(); ++it) + { + esm.writeHNT ("GRAV", *it); + } + + esm.writeHNT("AISE", mHasAiSettings); + for (int i=0; i<4; ++i) + mAiSettings[i].save(esm); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index c8dc97403..628bfee0a 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -26,6 +26,12 @@ namespace ESM AiSequence::AiSequence mAiSequence; + bool mHasAiSettings; + StatState mAiSettings[4]; + + std::map mSummonedCreatureMap; + std::vector mSummonGraveyard; + ESM::TimeStamp mTradeTime; int mGoldPool; int mActorId; From d5b97005aba2406b26aece189ea03ca44f61dae9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jun 2014 23:05:38 +0200 Subject: [PATCH 093/226] Make ESM::Faction skills optional (Fixes #1508) Also increased size of mSkills array to 7. Some factions with 7 skills can be found in the vanilla CS. The previously "mUnknown" int appears to be the 7th element of the skills array. --- apps/esmtool/record.cpp | 4 +--- apps/opencs/model/tools/factioncheck.cpp | 4 ++-- apps/opencs/model/world/data.cpp | 4 ++-- apps/openmw/mwgui/statswindow.cpp | 15 +++++++++++---- apps/openmw/mwmechanics/npcstats.cpp | 13 +++++++++++-- components/esm/loadfact.cpp | 7 +++---- components/esm/loadfact.hpp | 5 +++-- 7 files changed, 33 insertions(+), 19 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index bcf16091f..e4ada7d18 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -682,13 +682,11 @@ void Record::print() { std::cout << " Name: " << mData.mName << std::endl; std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl; - if (mData.mData.mUnknown != -1) - std::cout << " Unknown: " << mData.mData.mUnknown << std::endl; std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0]) << " (" << mData.mData.mAttribute[0] << ")" << std::endl; std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) << " (" << mData.mData.mAttribute[1] << ")" << std::endl; - for (int i = 0; i != 6; i++) + for (int i = 0; i < 7; i++) if (mData.mData.mSkills[i] != -1) std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) << " (" << mData.mData.mSkills[i] << ")" << std::endl; diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 42d577163..ba8cfe1f9 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -42,7 +42,7 @@ void CSMTools::FactionCheckStage::perform (int stage, Messages& messages) // test for non-unique skill std::map skills; // ID, number of occurrences - for (int i=0; i<6; ++i) + for (int i=0; i<7; ++i) if (faction.mData.mSkills[i]!=-1) ++skills[faction.mData.mSkills[i]]; @@ -54,4 +54,4 @@ void CSMTools::FactionCheckStage::perform (int stage, Messages& messages) } /// \todo check data members that can't be edited in the table view -} \ No newline at end of file +} diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 7f2eac20d..0319d71ac 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -102,7 +102,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding) mFactions.addColumn (new AttributesColumn (0)); mFactions.addColumn (new AttributesColumn (1)); mFactions.addColumn (new HiddenColumn); - for (int i=0; i<6; ++i) + for (int i=0; i<7; ++i) mFactions.addColumn (new SkillsColumn (i)); mRaces.addColumn (new StringIdColumn); @@ -742,4 +742,4 @@ void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end) { emit idListChanged(); -} \ No newline at end of file +} diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 246ade7bf..b4bd0d738 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -504,11 +504,18 @@ namespace MWGui text += "\n\n#DDC79E#{sFavoriteSkills}"; text += "\n#BF9959"; - for (int i=0; i<6; ++i) + bool firstSkill = true; + for (int i=0; i<7; ++i) { - text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; - if (i<5) - text += ", "; + if (faction->mData.mSkills[i] != -1) + { + if (!firstSkill) + text += ", "; + + firstSkill = false; + + text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkills[i]]+"}"; + } } text += "\n"; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index d50f2c5ae..642e1cfe1 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -361,8 +361,14 @@ bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int std::vector skills; - for (int i=0; i<6; ++i) - skills.push_back (static_cast (getSkill (faction.mData.mSkills[i]).getModified())); + for (int i=0; i<7; ++i) + { + if (faction.mData.mSkills[i] != -1) + skills.push_back (static_cast (getSkill (faction.mData.mSkills[i]).getModified())); + } + + if (skills.empty()) + return true; std::sort (skills.begin(), skills.end()); @@ -373,6 +379,9 @@ bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int if (*iter=rankData.mSkill2; } diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index 0924efb17..db7e5b7b4 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -12,7 +12,7 @@ namespace ESM int& Faction::FADTstruct::getSkill (int index, bool ignored) { - if (index<0 || index>=6) + if (index<0 || index>=7) throw std::logic_error ("skill index out of range"); return mSkills[index]; @@ -20,7 +20,7 @@ namespace ESM int Faction::FADTstruct::getSkill (int index, bool ignored) const { - if (index<0 || index>=6) + if (index<0 || index>=7) throw std::logic_error ("skill index out of range"); return mSkills[index]; @@ -75,7 +75,6 @@ void Faction::save(ESMWriter &esm) const { mName.clear(); mData.mAttribute[0] = mData.mAttribute[1] = 0; - mData.mUnknown = -1; mData.mIsHidden = 0; for (int i=0; i<10; ++i) @@ -87,7 +86,7 @@ void Faction::save(ESMWriter &esm) const mRanks[i].clear(); } - for (int i=0; i<6; ++i) + for (int i=0; i<7; ++i) mData.mSkills[i] = 0; mReactions.clear(); diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 75e30a5bf..d31670fe2 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -40,8 +40,9 @@ struct Faction RankData mRankData[10]; - int mSkills[6]; // IDs of skills this faction require - int mUnknown; // Always -1? + int mSkills[7]; // IDs of skills this faction require + // Each element will either contain an ESM::Skill index, or -1. + int mIsHidden; // 1 - hidden from player int& getSkill (int index, bool ignored = false); From 3b7119ba0d2326066e270e46efcb3efadb9256b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jun 2014 23:09:51 +0200 Subject: [PATCH 094/226] Make Bipedal creatures always able to walk and swim (Fixes #1509) This is necessary since the vanilla CS greys out the walk/swim checkboxes when Bipedal is checked. --- apps/openmw/mwclass/creature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 99b83a5ac..10c318367 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -713,7 +713,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mFlags & ESM::Creature::Swims; + return ref->mBase->mFlags & ESM::Creature::Swims || ref->mBase->mFlags & ESM::Creature::Bipedal; } bool Creature::canWalk(const MWWorld::Ptr &ptr) const @@ -721,7 +721,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mFlags & ESM::Creature::Walks; + return ref->mBase->mFlags & ESM::Creature::Walks || ref->mBase->mFlags & ESM::Creature::Bipedal; } int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) From 0aba1088a1ff249baa85d57d84f8a5c46f84c8cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 00:51:58 +0200 Subject: [PATCH 095/226] Attempt to open companion window if no greetings are found (Bug #1507) --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index be0a1b163..8d9dc670f 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -182,10 +182,20 @@ namespace MWDialogue win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript); mLastTopic = Misc::StringUtils::lowerCase(it->mId); - break; + return; } } } + + // No greetings found. The dialogue window should not be shown. + // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique). + bool isCompanion = !mActor.getClass().getScript(mActor).empty() + && mActor.getRefData().getLocals().getIntVar(mActor.getClass().getScript(mActor), "companion"); + if (isCompanion) + { + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion); + MWBase::Environment::get().getWindowManager()->showCompanionWindow(mActor); + } } bool DialogueManager::compile (const std::string& cmd,std::vector& code) From 4f73e8bb7184ebdc72cdc125e1513b5a562b5017 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 00:52:40 +0200 Subject: [PATCH 096/226] Add items to player inventory upon drag start (Fixes #1507) --- apps/openmw/mwgui/container.cpp | 26 ++++++++++++++++++++++++++ apps/openmw/mwgui/inventorywindow.cpp | 5 +++++ apps/openmw/mwgui/inventorywindow.hpp | 1 + 3 files changed, 32 insertions(+) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 4ba454a1c..011feb4d3 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -38,6 +38,32 @@ namespace MWGui mIsOnDragAndDrop = true; mDragAndDropWidget->setVisible(true); + // If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend + // immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive, + // but this is how it works in vanilla, and not doing so would break quests (BM_beasts for instance). + ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); + if (mSourceModel != playerModel) + { + MWWorld::Ptr item = mSourceModel->moveItem(mItem, mDraggedCount, playerModel); + + playerModel->update(); + + ItemModel::ModelIndex newIndex = -1; + for (unsigned int i=0; igetItemCount(); ++i) + { + if (playerModel->getItem(i).mBase == item) + { + newIndex = i; + break; + } + } + mItem = playerModel->getItem(newIndex); + mSourceModel = playerModel; + + SortFilterItemModel* playerFilterModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getSortFilterModel(); + mSourceSortModel = playerFilterModel; + } + std::string sound = mItem.mBase.getClass().getUpSoundId(mItem.mBase); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index b1e8052d8..abea68185 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -132,6 +132,11 @@ namespace MWGui adjustPanes(); } + SortFilterItemModel* InventoryWindow::getSortFilterModel() + { + return mSortModel; + } + TradeItemModel* InventoryWindow::getTradeModel() { return mTradeModel; diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index df563b3d4..ae7af5719 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -37,6 +37,7 @@ namespace MWGui mPreview->rebuild(); } + SortFilterItemModel* getSortFilterModel(); TradeItemModel* getTradeModel(); ItemModel* getModel(); From edccb62c1f8f3a4d9936e6419dc68a1f0130b0f9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 01:03:43 +0200 Subject: [PATCH 097/226] Clear MyGUI's clipboard, since we don't want to use it (Fixes #1277) This would cause pasted text to appear twice, but only when using an SVN version of MyGUI, since 3.2 had a bug where it wouldn't recognize the Ctrl C/V/X keys when using separated key/textinput events. --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index bb18b5aa7..15b5abde6 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -547,6 +547,10 @@ namespace MWInput } if (!mControlsDisabled) mInputBinder->keyPressed (arg); + + // Clear MyGUI's clipboard, so it doesn't interfere with our own clipboard implementation. + // We do not use MyGUI's clipboard manager because it doesn't support system clipboard integration with SDL. + MyGUI::ClipboardManager::getInstance().clearClipboardData("Text"); } void InputManager::textInput(const SDL_TextInputEvent &arg) From 9ea071c1e8c8e1ba0c8f2ee111728e7b4b0f31e2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 01:46:10 +0200 Subject: [PATCH 098/226] Fix weapon incorrectly showing when saving and loading with spell equipped --- apps/openmw/mwmechanics/character.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a5964bdf4..6847cd725 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -509,7 +509,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if (cls.hasInventoryStore(mPtr)) { getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); - if(mWeaponType != WeapType_None) + if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; @@ -522,8 +522,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mIdleState = CharState_Idle; else { - /* FIXME: Get the actual death state used. */ - mDeathState = CharState_Death1; + int deathindex = mPtr.getClass().getCreatureStats(mPtr).getDeathAnimation(); + playDeath(1.0f, CharacterState(CharState_Death1 + deathindex)); } } else @@ -535,12 +535,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim } - if(mDeathState != CharState_None) - { - int deathindex = mPtr.getClass().getCreatureStats(mPtr).getDeathAnimation(); - playDeath(1.0f, CharacterState(CharState_Death1 + deathindex)); - } - else + if(mDeathState == CharState_None) refreshCurrentAnims(mIdleState, mMovementState, true); } From 345ba99c1751ad7cc95282b94d4be5fff26d8d6a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 01:58:13 +0200 Subject: [PATCH 099/226] Fix WeaponAnimationTime not working after restoring draw state (when loading save or re-entering a cell - Fixes #1497) --- apps/openmw/mwmechanics/character.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6847cd725..6f0a255a9 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -514,6 +514,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; mAnimation->showWeapons(true); + mAnimation->setWeaponGroup(mCurrentWeapon); } mAnimation->showCarriedLeft(mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand); } From 78f30e297057e5445e63267be4e2101e67ed99e2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 02:42:07 +0200 Subject: [PATCH 100/226] Fix a journal layout bug --- apps/openmw/mwgui/bookpage.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 2cebc8e19..659ba714c 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -360,10 +360,16 @@ struct TypesetBookImpl::Typesetter : BookTypesetter int spaceLeft = mPageHeight - (curPageStop - curPageStart); int sectionHeight = i->mRect.height (); - if (sectionHeight <= mPageHeight) + // This is NOT equal to i->mRect.height(), which doesn't account for section breaks. + int spaceRequired = (i->mRect.bottom - curPageStop); + if (curPageStart == curPageStop) // If this is a new page, the section break is not needed + spaceRequired = i->mRect.height(); + + if (spaceRequired <= mPageHeight) { - if (sectionHeight > spaceLeft) + if (spaceRequired > spaceLeft) { + // The section won't completely fit on the current page. Finish the current page and start a new one. assert (curPageStart != curPageStop); mBook->mPages.push_back (Page (curPageStart, curPageStop)); From 468db7a9c4237a8c9b91f003ccf4576a7b3e49a5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 03:16:50 +0200 Subject: [PATCH 101/226] Remove terrain limit warning text that no longer applies --- files/settings-default.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2ed3d6cb9..65883b97f 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -65,8 +65,6 @@ shader mode = enabled = false # Split the shadow maps, allows for a larger shadow distance -# Warning: enabling this will cause some terrain textures to disappear due to -# hitting the texture unit limit of the terrain material split = false # Increasing shadow distance will lower the shadow quality. From 1b610cdbd0f96b4d7302549ca58f0ccd80ae67d4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 03:17:31 +0200 Subject: [PATCH 102/226] Allow following NPCs to fast-travel (Fixes #1501) --- apps/openmw/mwgui/travelwindow.cpp | 25 +++++++++++-------------- apps/openmw/mwgui/travelwindow.hpp | 4 +--- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 79d50cdc5..c16603554 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -13,6 +13,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/actionteleport.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" @@ -50,7 +51,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); } - void TravelWindow::addDestination(const std::string& travelId,ESM::Position pos,bool interior) + void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior) { int price = 0; @@ -85,13 +86,12 @@ namespace MWGui oss << price; toAdd->setUserString("price",oss.str()); - toAdd->setCaptionWithReplacing("#{sCell=" + travelId + "} - " + boost::lexical_cast(price)+"#{sgp}"); + toAdd->setCaptionWithReplacing("#{sCell=" + name + "} - " + boost::lexical_cast(price)+"#{sgp}"); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); - toAdd->setUserString("Destination", travelId); + toAdd->setUserString("Destination", name); toAdd->setUserData(pos); toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onTravelButtonClick); - mDestinationsWidgetMap.insert(std::make_pair (toAdd, travelId)); } void TravelWindow::clearDestinations() @@ -100,7 +100,6 @@ namespace MWGui mCurrentY = 0; while (mDestinationsView->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0)); - mDestinationsWidgetMap.clear(); } void TravelWindow::startTravel(const MWWorld::Ptr& actor) @@ -118,7 +117,8 @@ namespace MWGui mPtr.get()->mBase->mTransport[i].mPos.pos[1],x,y); if (cellname == "") { - cellname = MWBase::Environment::get().getWorld()->getExterior(x,y)->getCell()->mName; + MWWorld::CellStore* cell = MWBase::Environment::get().getWorld()->getExterior(x,y); + cellname = MWBase::Environment::get().getWorld()->getCellName(cell); interior = false; } addDestination(cellname,mPtr.get()->mBase->mTransport[i].mPos,interior); @@ -146,12 +146,8 @@ namespace MWGui MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); ESM::Position pos = *_sender->getUserData(); std::string cellname = _sender->getUserString("Destination"); - int x,y; bool interior = _sender->getUserString("interior") == "y"; - MWBase::Environment::get().getWorld()->positionToIndex(pos.pos[0],pos.pos[1],x,y); - if(interior) - MWBase::Environment::get().getWorld()->changeToInteriorCell(cellname, pos); - else + if (!interior) { ESM::Position playerPos = player.getRefData().getPosition(); float d = Ogre::Vector3(pos.pos[0], pos.pos[1], 0).distance( @@ -162,11 +158,12 @@ namespace MWGui MWBase::Environment::get().getMechanicsManager ()->rest (true); } MWBase::Environment::get().getWorld()->advanceTime(hours); - - MWBase::Environment::get().getWorld()->changeToExteriorCell(pos); } - player.getClass().adjustPosition(player); + // Teleports any followers, too. + MWWorld::ActionTeleport action(interior ? cellname : "", pos); + action.execute(player); + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0); diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 5387bd690..4328f7ac2 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -34,12 +34,10 @@ namespace MWGui MyGUI::ScrollView* mDestinationsView; - std::map mDestinationsWidgetMap; - void onCancelButtonClicked(MyGUI::Widget* _sender); void onTravelButtonClick(MyGUI::Widget* _sender); void onMouseWheel(MyGUI::Widget* _sender, int _rel); - void addDestination(const std::string& destinationID,ESM::Position pos,bool interior); + void addDestination(const std::string& name, ESM::Position pos, bool interior); void clearDestinations(); int mLastPos,mCurrentY; From 5f5c0c5bff9040898859babe81e2210e80290b94 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 03:29:02 +0200 Subject: [PATCH 103/226] Remove an unused setting --- files/settings-default.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 65883b97f..fac2a0f97 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -164,8 +164,6 @@ ui sensitivity = 1.0 camera y multiplier = 1.0 -ui y multiplier = 1.0 - always run = false [Game] From f935cfc6c973e42c57d78edb11a21e251ece7767 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 04:03:53 +0200 Subject: [PATCH 104/226] Fix crash when stealing items with the owner not around (Fixes #1512) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 +++- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2dea28346..79b121b31 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -827,6 +827,8 @@ namespace MWMechanics // NOTE: int arg can be from itemTaken() so DON'T modify it, since it is // passed to reportCrime later on in this function. + // NOTE: victim may be empty + // Only player can commit crime if (player.getRefData().getHandle() != "player") return false; @@ -860,7 +862,7 @@ namespace MWMechanics mActors.getObjectsInRange(from, radius, neighbors); // victim should be considered even beyond alarm radius - if (from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) + if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) neighbors.push_back(victim); int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 2511d5785..714537a38 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -111,6 +111,7 @@ namespace MWMechanics /** * @brief Commit a crime. If any actors witness the crime and report it, * reportCrime will be called automatically. + * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @return was the crime reported? */ From 47bd170d7eab68fea72b219e00b5636492f940d6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 04:13:36 +0200 Subject: [PATCH 105/226] Crashcatcher: create temp file in /tmp, not working directory (which may not have write access) --- apps/openmw/crashcatcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index 65a036919..7f805f233 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -128,7 +128,7 @@ static void gdb_info(pid_t pid) int fd; /* Create a temp file to put gdb commands into */ - strcpy(respfile, "gdb-respfile-XXXXXX"); + strcpy(respfile, "/tmp/gdb-respfile-XXXXXX"); if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) { fprintf(f, "attach %d\n" From 2ce8323a422776f957067fde24ebe40317f17d11 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 16:34:00 +0200 Subject: [PATCH 106/226] Fix getDistance not detecting references in inactive cells properly --- apps/openmw/mwscript/interpretercontext.cpp | 12 ++++++++++-- apps/openmw/mwscript/interpretercontext.hpp | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index ebe88c3a4..028f83a61 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -378,12 +378,20 @@ namespace MWScript float InterpreterContext::getDistance (const std::string& name, const std::string& id) const { - const MWWorld::Ptr ref2 = getReference (id, false, false); + // NOTE: id may be empty, indicating an implicit reference + + MWWorld::Ptr ref2; + + if (id.empty()) + ref2 = getReference("", true, true); + else + ref2 = MWBase::Environment::get().getWorld()->searchPtr(id, true); + // If either actor is in a non-active cell, return a large value (just like vanilla) if (ref2.isEmpty()) return std::numeric_limits().max(); - const MWWorld::Ptr ref = getReference (name, false, false); + const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->searchPtr(name, true); if (ref.isEmpty()) return std::numeric_limits().max(); diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index fdf9d6424..99026392e 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -118,6 +118,7 @@ namespace MWScript virtual void stopScript (const std::string& name); virtual float getDistance (const std::string& name, const std::string& id = "") const; + ///< @note if \a id is empty, assumes an implicit reference bool hasBeenActivated (const MWWorld::Ptr& ptr); ///< \attention Calling this function for the right reference will mark the action as From d11a5e19f79b9ed3cf0e49cde5b1b063a9155fb9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 17:14:35 +0200 Subject: [PATCH 107/226] Fix positionCell not properly teleporting actors from inactive to active cells (Fixes #1516) --- apps/openmw/mwworld/worldimp.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 625942da7..4386f5b0d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -989,9 +989,18 @@ namespace MWWorld } else { - if (!mWorldScene->isCellActive(*currCell)) - ptr.getClass().copyToCell(ptr, *newCell, pos); - else if (!mWorldScene->isCellActive(*newCell)) + if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell)) + { + MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); + mWorldScene->addObjectToScene(newPtr); + + std::string script = newPtr.getClass().getScript(newPtr); + if (!script.empty()) { + mLocalScripts.add(script, newPtr); + } + addContainerScripts(newPtr, newCell); + } + else if (!mWorldScene->isCellActive(*newCell) && mWorldScene->isCellActive(*currCell)) { mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); From 28feb260eb8fd675dca04f8da2dc0dc88e3e043a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 19:34:53 +0200 Subject: [PATCH 108/226] Implement disposition/distance based aggression (Fixes #1520) --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 3 +-- apps/openmw/mwdialogue/filter.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 22 +++++++--------- .../mwmechanics/mechanicsmanagerimp.cpp | 25 +++++++++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ 7 files changed, 42 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 30a576a15..07d4eb2bc 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -191,6 +191,8 @@ namespace MWBase virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual void clear() = 0; + + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 10c318367..59c1087f9 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -345,7 +345,7 @@ namespace MWClass getCreatureStats(ptr).setAttacked(true); // Self defense - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80 + 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) MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6d5ff9f7f..2b4c54e24 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -624,9 +624,8 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. // Attacking peaceful NPCs is a crime - // anything below 80 is considered peaceful (see Actors::updateActor) if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && - ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) + !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); getCreatureStats(ptr).setAttacked(true); diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index d301e88aa..08cdb1d00 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -530,7 +530,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_ShouldAttack: - return mActor.getClass().getCreatureStats(mActor).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() >= 80; + return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor, + MWBase::Environment::get().getWorld()->getPlayerPtr()); case SelectWrapper::Function_CreatureTargetted: diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e9e9fbe28..ddb3087dd 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -201,32 +201,28 @@ namespace MWMechanics || (!actor1.getClass().canSwim(actor1) && MWBase::Environment::get().getWorld()->isSwimming(actor2)))) // creature can't swim to target return; - float fight; + bool aggressive; if (againstPlayer) - fight = actor1.getClass().getCreatureStats(actor1).getAiSetting(CreatureStats::AI_Fight).getModified(); + aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); else { - fight = 0; + 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()) { // if creature is hostile then it is necessarily to start combat - if (creatureStats.isHostile()) fight = 100; - else fight = creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified(); + if (creatureStats.isHostile()) aggressive = true; + else aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); } } - ESM::Position actor1Pos = actor1.getRefData().getPosition(); - ESM::Position actor2Pos = actor2.getRefData().getPosition(); - float d = Ogre::Vector3(actor1Pos.pos).distance(Ogre::Vector3(actor2Pos.pos)); - - if( (fight == 100 && d <= 5000) - || (fight >= 95 && d <= 3000) - || (fight >= 90 && d <= 2000) - || (fight >= 80 && d <= 1000)) + 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); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 79b121b31..4d6930135 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1098,4 +1098,29 @@ namespace MWMechanics { mActors.clear(); } + + bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) + { + Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); + Ogre::Vector3 pos2 (target.getRefData().getPosition().pos); + + float d = pos1.distance(pos2); + + static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( + "iFightDistanceBase")->getInt(); + static float fFightDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find( + "fFightDistanceMultiplier")->getFloat(); + static float fFightDispMult = MWBase::Environment::get().getWorld()->getStore().get().find( + "fFightDispMult")->getFloat(); + + int disposition = 50; + if (ptr.getClass().isNpc()) + disposition = getDerivedDisposition(ptr); + + int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + + (iFightDistanceBase - fFightDistanceMultiplier * d) + + ((50 - disposition) * fFightDispMult); + + return (fight >= 100); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 714537a38..677d60ae5 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -156,6 +156,8 @@ namespace MWMechanics virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual void clear(); + + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); }; } From cfea7736d94fa0dacb16c3754218b7e3dd89cc81 Mon Sep 17 00:00:00 2001 From: Fil Krynicki Date: Mon, 16 Jun 2014 17:08:02 -0400 Subject: [PATCH 109/226] WIP fix Bug is fixed, but appears to have broken vision underwater. Notes: + basically fixed by darkening the colour of water such that it is darker than refracted terrain + also disabled sunlight scattering at night. This may actually be desirable, but given there is no visible moon it seems unlikely to make much sense, and blends a lot of green into the water colour appearing luminescent at night --- apps/openmw/mwrender/refraction.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 11 ++++++----- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwrender/sky.cpp | 4 ++-- apps/openmw/mwrender/sky.hpp | 2 +- apps/openmw/mwworld/weather.cpp | 4 +++- files/materials/underwater.h | 2 +- files/materials/water.shader | 4 ++-- 8 files changed, 17 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/refraction.cpp b/apps/openmw/mwrender/refraction.cpp index f22968e9d..7c64ea1d8 100644 --- a/apps/openmw/mwrender/refraction.cpp +++ b/apps/openmw/mwrender/refraction.cpp @@ -35,7 +35,7 @@ namespace MWRender vp->setShadowsEnabled(false); vp->setVisibilityMask(RV_Actors + RV_Misc + RV_Statics + RV_StaticsSmall + RV_Terrain + RV_Sky); vp->setMaterialScheme("water_refraction"); - vp->setBackgroundColour (Ogre::ColourValue(0.18039, 0.23137, 0.25490)); + vp->setBackgroundColour (Ogre::ColourValue(0.090195, 0.115685, 0.12745)); mRenderTarget->setAutoUpdated(true); mRenderTarget->addListener(this); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8a22c63c6..3ceda6d21 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -522,9 +522,10 @@ void RenderingManager::applyFog (bool underwater) } else { - mRendering.getScene()->setFog (FOG_LINEAR, Ogre::ColourValue(0.18039, 0.23137, 0.25490), 0, 0, 1000); - mRendering.getViewport()->setBackgroundColour (Ogre::ColourValue(0.18039, 0.23137, 0.25490)); - mWater->setViewportBackground (Ogre::ColourValue(0.18039, 0.23137, 0.25490)); + //Ogre::ColourValue clv(0.090195, 0.115685, 0.12745); + mRendering.getScene()->setFog (FOG_LINEAR, Ogre::ColourValue(clv)); + mRendering.getViewport()->setBackgroundColour (Ogre::ColourValue(clv)); + mWater->setViewportBackground (Ogre::ColourValue(clv)); } } @@ -631,12 +632,12 @@ void RenderingManager::sunDisable(bool real) } } -void RenderingManager::setSunDirection(const Ogre::Vector3& direction) +void RenderingManager::setSunDirection(const Ogre::Vector3& direction, bool is_moon) { // direction * -1 (because 'direction' is camera to sun vector and not sun to camera), if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.y, -direction.z)); - mSkyManager->setSunDirection(direction); + mSkyManager->setSunDirection(direction, is_moon); } void RenderingManager::setGlare(bool glare) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f539f9270..ea7905cf5 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -145,7 +145,7 @@ public: void setAmbientColour(const Ogre::ColourValue& colour); void setSunColour(const Ogre::ColourValue& colour); - void setSunDirection(const Ogre::Vector3& direction); + void setSunDirection(const Ogre::Vector3& direction, bool is_moon); void sunEnable(bool real); ///< @param real whether or not to really disable the sunlight (otherwise just set diffuse to 0) void sunDisable(bool real); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 90c08c299..b10b7fd5f 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -546,14 +546,14 @@ void SkyManager::sunDisable() mSunEnabled = false; } -void SkyManager::setSunDirection(const Vector3& direction) +void SkyManager::setSunDirection(const Vector3& direction, bool is_moon) { if (!mCreated) return; mSun->setPosition(direction); mSunGlare->setPosition(direction); float height = direction.z; - float fade = ( height > 0.5) ? 1.0 : height * 2; + float fade = is_moon ? 0.0 : (( height > 0.5) ? 1.0 : height * 2); sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(fade, height))); } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 965907a97..35f2a267e 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -148,7 +148,7 @@ namespace MWRender void sunDisable(); - void setSunDirection(const Ogre::Vector3& direction); + void setSunDirection(const Ogre::Vector3& direction, bool is_moon); void setMasserDirection(const Ogre::Vector3& direction); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 25f523bee..64d1385da 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -378,11 +378,13 @@ void WeatherManager::update(float duration) int facing = (mHour > 13.f) ? 1 : -1; + bool sun_is_moon = mHour >= mNightStart || mHour <= mSunriseTime; + Vector3 final( (height - 1) * facing, (height - 1) * facing, height); - mRendering->setSunDirection(final); + mRendering->setSunDirection(final, sun_is_moon); /* * TODO: import separated fadeInStart/Finish, fadeOutStart/Finish diff --git a/files/materials/underwater.h b/files/materials/underwater.h index 8474f299d..332a0fd7d 100644 --- a/files/materials/underwater.h +++ b/files/materials/underwater.h @@ -1,4 +1,4 @@ -#define UNDERWATER_COLOUR float3(0.18039, 0.23137, 0.25490) +#define UNDERWATER_COLOUR float3(0.090195, 0.115685, 0.12745) #define VISIBILITY 1000.0 // how far you can look through water diff --git a/files/materials/water.shader b/files/materials/water.shader index 87e90a291..701154ffa 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -340,7 +340,7 @@ #if REFRACTION float3 refraction = shSample(refractionMap, (screenCoords-(normal.xy*REFR_BUMP))*1.0).rgb; - + // brighten up the refraction underwater refraction = (cameraPos.z < 0) ? shSaturate(refraction * 1.5) : refraction; #endif @@ -351,7 +351,7 @@ #if REFRACTION shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz; #else - shOutputColour(0).xyz = shLerp(reflection, float3(0.18039, 0.23137, 0.25490), (1.0-fresnel)*0.5) + specular * sunSpecular.xyz; + shOutputColour(0).xyz = shLerp(reflection, float3(0.090195, 0.115685, 0.12745), (1.0-fresnel)*0.5) + specular * sunSpecular.xyz; #endif // fog float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); From c36fc48c473d03b4d2f2416425ad93052093f09a Mon Sep 17 00:00:00 2001 From: Fil Krynicki Date: Mon, 16 Jun 2014 17:15:49 -0400 Subject: [PATCH 110/226] Fixed underwater issue I had accidentally deleted some interpolation parameters. --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3ceda6d21..2c1b663dc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -522,8 +522,8 @@ void RenderingManager::applyFog (bool underwater) } else { - //Ogre::ColourValue clv(0.090195, 0.115685, 0.12745); - mRendering.getScene()->setFog (FOG_LINEAR, Ogre::ColourValue(clv)); + Ogre::ColourValue clv(0.090195, 0.115685, 0.12745); + mRendering.getScene()->setFog (FOG_LINEAR, Ogre::ColourValue(clv), 0, 0, 1000); mRendering.getViewport()->setBackgroundColour (Ogre::ColourValue(clv)); mWater->setViewportBackground (Ogre::ColourValue(clv)); } From 8ac019611dc3ae875e5a86a4e6e567bffb0d1b99 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 16 Jun 2014 20:23:57 +0200 Subject: [PATCH 111/226] Fix Potion use action removing the potion even when the action is not executed (Fixes #1521) --- apps/openmw/mwclass/potion.cpp | 7 +------ apps/openmw/mwworld/actionapply.cpp | 20 +++++++++++++------- apps/openmw/mwworld/actionapply.hpp | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index d7cb0cd9b..440121d35 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -166,13 +166,8 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - // remove used potion (assume it is present in inventory) - ptr.getContainerStore()->remove(ptr, 1, actor); - boost::shared_ptr action ( - new MWWorld::ActionApply (actor, ref->mBase->mId)); + new MWWorld::ActionApply (ptr, ref->mBase->mId)); action->setSound ("Drink"); diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp index 6b12cc3e6..bfd64c85d 100644 --- a/apps/openmw/mwworld/actionapply.cpp +++ b/apps/openmw/mwworld/actionapply.cpp @@ -6,30 +6,36 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/containerstore.hpp" + namespace MWWorld { - ActionApply::ActionApply (const Ptr& target, const std::string& id) - : Action (false, target), mId (id) + ActionApply::ActionApply (const Ptr& object, const std::string& id) + : Action (false, object), mId (id) {} void ActionApply::executeImp (const Ptr& actor) { MWBase::Environment::get().getWorld()->breakInvisibility(actor); - getTarget().getClass().apply (getTarget(), mId, actor); + actor.getClass().apply (actor, mId, actor); + + actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor); } - ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id, + ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& object, const std::string& id, int skillIndex, int usageType) - : Action (false, target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) + : Action (false, object), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) {} void ActionApplyWithSkill::executeImp (const Ptr& actor) { MWBase::Environment::get().getWorld()->breakInvisibility(actor); - if (getTarget().getClass().apply (getTarget(), mId, actor) && mUsageType!=-1) - getTarget().getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType); + if (actor.getClass().apply (actor, mId, actor) && mUsageType!=-1) + actor.getClass().skillUsageSucceeded (actor, mSkillIndex, mUsageType); + + actor.getClass().getContainerStore(actor).remove(getTarget(), 1, actor); } } diff --git a/apps/openmw/mwworld/actionapply.hpp b/apps/openmw/mwworld/actionapply.hpp index 669a19067..5294745a6 100644 --- a/apps/openmw/mwworld/actionapply.hpp +++ b/apps/openmw/mwworld/actionapply.hpp @@ -16,7 +16,7 @@ namespace MWWorld public: - ActionApply (const Ptr& target, const std::string& id); + ActionApply (const Ptr& object, const std::string& id); }; class ActionApplyWithSkill : public Action @@ -29,7 +29,7 @@ namespace MWWorld public: - ActionApplyWithSkill (const Ptr& target, const std::string& id, + ActionApplyWithSkill (const Ptr& object, const std::string& id, int skillIndex, int usageType); }; } From ffb6f5d555fd850ecdd2a9ff0930f58c309f0161 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 01:10:37 +0200 Subject: [PATCH 112/226] Use fFight GMSTs to control attacks in response to crimes --- apps/openmw/mwbase/mechanicsmanager.hpp | 5 +- apps/openmw/mwmechanics/actors.cpp | 11 -- apps/openmw/mwmechanics/aisequence.cpp | 5 + .../mwmechanics/mechanicsmanagerimp.cpp | 137 ++++++++++++------ .../mwmechanics/mechanicsmanagerimp.hpp | 4 +- 5 files changed, 105 insertions(+), 57 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 07d4eb2bc..91d0ed9bf 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -120,6 +120,7 @@ namespace MWBase /** * @brief Commit a crime. If any actors witness the crime and report it, * reportCrime will be called automatically. + * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @return was the crime reported? */ @@ -192,7 +193,9 @@ namespace MWBase virtual void clear() = 0; - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; + /// @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) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ddb3087dd..5e46a11a3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -789,7 +789,6 @@ namespace MWMechanics if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile()) { - /// \todo Move me! I shouldn't be here... const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()); // Force dialogue on sight if bounty is greater than the cutoff @@ -817,7 +816,6 @@ namespace MWMechanics creatureStats.getAiSequence().stopCombat(); // Reset factors to attack - // TODO: Not a complete list, disposition changes? creatureStats.setHostile(false); creatureStats.setAttacked(false); creatureStats.setAlarmed(false); @@ -825,15 +823,6 @@ namespace MWMechanics // Update witness crime id npcStats.setCrimeId(-1); } - else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue) - { - if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPursue(player), ptr); - else - { - MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); - } - } } } } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 05723c575..3aeeee65a 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -221,6 +221,11 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor) { return; // target is already pursued } + if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat + && static_cast(*iter)->getTarget() == static_cast(&package)->getTarget()) + { + return; // already in combat with this actor + } else if ((*iter)->getTypeId() == AiPackage::TypeIdWander) static_cast(*iter)->setReturnPosition(Ogre::Vector3(actor.getRefData().getPosition().pos)); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4d6930135..cb59d188f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -14,6 +14,7 @@ #include "../mwworld/player.hpp" #include "../mwmechanics/aicombat.hpp" +#include "../mwmechanics/aipursue.hpp" #include @@ -836,7 +837,7 @@ namespace MWMechanics const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); // What amount of alarm did this crime generate? - int alarm; + int alarm = 0; if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) alarm = esmStore.get().find("iAlarmTresspass")->getInt(); else if (type == OT_Pickpocket) @@ -847,17 +848,14 @@ namespace MWMechanics alarm = esmStore.get().find("iAlarmKilling")->getInt(); else if (type == OT_Theft) alarm = esmStore.get().find("iAlarmStealing")->getInt(); - else - return false; - // Innocent until proven guilty bool reported = false; // Find all the actors within the alarm radius std::vector neighbors; Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos); - int radius = esmStore.get().find("fAlarmRadius")->getInt(); + float radius = esmStore.get().find("fAlarmRadius")->getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -865,9 +863,9 @@ namespace MWMechanics if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) neighbors.push_back(victim); - int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); + bool victimAware = false; - // Find actors who witnessed the crime + // Find actors who directly witnessed the crime for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if (*it == player) continue; // not the player @@ -875,22 +873,13 @@ namespace MWMechanics // Was the crime seen? if (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) { - // TODO: Add more messages + if (*it == victim) + victimAware = true; + + // TODO: are there other messages? if (type == OT_Theft) MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); - if (*it == victim) - { - // Self-defense - // The victim is aware of the criminal/assailant. If being assaulted, fight back now - // (regardless of whether the assault is reported or not) - // This applies to both NPCs and creatures - - // ... except if this is a guard: then the player is given a chance to pay a fine / go to jail instead - if (type == OT_Assault && !it->getClass().isClass(*it, "guard")) - MWBase::Environment::get().getMechanicsManager()->startCombat(victim, player); - } - // Crime reporting only applies to NPCs if (!it->getClass().isNpc()) continue; @@ -899,32 +888,19 @@ namespace MWMechanics if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { reported = true; - - // Tell everyone, including yourself - for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) - { - if ( *it1 == player - || !it1->getClass().isNpc()) continue; // not the player and is an NPC - - // Will other witnesses paticipate in crime - if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm - || type == OT_Assault ) - { - it1->getClass().getNpcStats(*it1).setCrimeId(id); - } - - // Mark as Alarmed for dialogue - it1->getClass().getCreatureStats(*it1).setAlarmed(true); - } } } } + if (reported) reportCrime(player, victim, type, arg); + else if (victimAware && !victim.isEmpty() && type == OT_Assault) + startCombat(victim, player); + return reported; } - void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); @@ -944,7 +920,7 @@ namespace MWMechanics } MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); - ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() + arg); // If committing a crime against a faction member, expell from the faction @@ -953,9 +929,81 @@ namespace MWMechanics std::string factionID; if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; - if (ptr.getClass().getNpcStats(ptr).isSameFaction(victim.getClass().getNpcStats(victim))) + if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim))) { - ptr.getClass().getNpcStats(ptr).expell(factionID); + player.getClass().getNpcStats(player).expell(factionID); + } + } + + // Make surrounding actors within alarm distance respond to the crime + std::vector neighbors; + + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + + Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos); + float radius = esmStore.get().find("fAlarmRadius")->getFloat(); + + mActors.getObjectsInRange(from, radius, neighbors); + + // victim should be considered even beyond alarm radius + if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) + neighbors.push_back(victim); + + int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); + + // What amount of provocation did this crime generate? + // Controls whether witnesses will engage combat with the criminal. + int fight = 0; + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + fight = esmStore.get().find("iFightTrespass")->getInt(); + else if (type == OT_Pickpocket) + fight = esmStore.get().find("iFightPickpocket")->getInt(); + else if (type == OT_Assault) // Note: iFightAttack is for the victim, iFightAttacking for witnesses? + fight = esmStore.get().find("iFightAttack")->getInt(); + else if (type == OT_Murder) + fight = esmStore.get().find("iFightKilling")->getInt(); + else if (type == OT_Theft) + fight = esmStore.get().find("fFightStealing")->getFloat(); + + const int iFightAttacking = esmStore.get().find("iFightAttacking")->getInt(); + + // Tell everyone (including the original reporter) in alarm range + for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + { + if ( *it == player + || !it->getClass().isNpc()) continue; + + int aggression = fight; + + // Note: iFightAttack is used for the victim, iFightAttacking for witnesses? + if (*it != victim && type == OT_Assault) + aggression = iFightAttacking; + + if (it->getClass().isClass(*it, "guard")) + { + // Mark as Alarmed for dialogue + it->getClass().getCreatureStats(*it).setAlarmed(true); + + // Set the crime ID, which we will use to calm down participants + // once the bounty has been paid. + it->getClass().getNpcStats(*it).setCrimeId(id); + + it->getClass().getCreatureStats(*it).getAiSequence().stack(AiPursue(player), *it); + } + else + { + bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression); + if (aggressive) + { + startCombat(*it, player); + + // Set the crime ID, which we will use to calm down participants + // once the bounty has been paid. + it->getClass().getNpcStats(*it).setCrimeId(id); + + // Mark as Alarmed for dialogue + it->getClass().getCreatureStats(*it).setAlarmed(true); + } } } } @@ -1099,7 +1147,7 @@ namespace MWMechanics mActors.clear(); } - bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) + bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias) { Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); Ogre::Vector3 pos2 (target.getRefData().getPosition().pos); @@ -1117,9 +1165,10 @@ namespace MWMechanics if (ptr.getClass().isNpc()) disposition = getDerivedDisposition(ptr); - int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + int fight = std::max(0.f, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() + (iFightDistanceBase - fFightDistanceMultiplier * d) - + ((50 - disposition) * fFightDispMult); + + ((50 - disposition) * fFightDispMult)) + + bias; return (fight >= 100); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 677d60ae5..0fee72b77 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -157,7 +157,9 @@ namespace MWMechanics virtual void clear(); - virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); + /// @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); }; } From ec66484472b235ed1cfcd310b312683daa3a0544 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 01:19:14 +0200 Subject: [PATCH 113/226] Fix forceGreeting with explicit references (Fixes #1518) --- components/compiler/extensions0.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c4b078ae8..5733a4170 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -167,7 +167,6 @@ namespace Compiler extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex); extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic); extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice); - extensions.registerInstruction("forcegreeting","",opcodeForceGreeting); extensions.registerInstruction("forcegreeting","",opcodeForceGreeting, opcodeForceGreetingExplicit); extensions.registerInstruction("goodbye", "", opcodeGoodbye); From 3801dfb4bab2ae5fdde3c24e65edeed006fdc2d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 01:53:09 +0200 Subject: [PATCH 114/226] Add delay to sneak icon update and skill progress (Fixes #1321) --- apps/openmw/mwmechanics/actors.cpp | 58 ++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5e46a11a3..dcbb6b8bd 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1074,6 +1074,9 @@ namespace MWMechanics isBattleMusic = true; } + static float sneakTimer = 0.f; // times update of sneak icon + static float sneakSkillTimer = 0.f; // times sneak skill progress from "avoid notice" + // if player is in sneak state see if anyone detects him if (player.getClass().getCreatureStats(player).getMovementFlag(MWMechanics::CreatureStats::Flag_Sneak)) { @@ -1081,27 +1084,54 @@ namespace MWMechanics const int radius = esmStore.get().find("fSneakUseDist")->getInt(); bool detected = false; - for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + static float fSneakUseDelay = esmStore.get().find("fSneakUseDelay")->getFloat(); + + if (sneakTimer >= fSneakUseDelay) + sneakTimer = 0.f; + + if (sneakTimer == 0.f) { - if (iter->first == player) // not the player - continue; + // Set when an NPC is within line of sight and distance, but is still unaware. Used for skill progress. + bool avoidedNotice = false; - // is the player in range and can they be detected - if ( (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius) - && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first) - && MWBase::Environment::get().getWorld()->getLOS(player, iter->first)) + for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - detected = true; - MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); - break; - } - } + if (iter->first == player) // not the player + continue; - if (!detected) - MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); + // is the player in range and can they be detected + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(player.getRefData().getPosition().pos)) <= radius*radius + && MWBase::Environment::get().getWorld()->getLOS(player, iter->first)) + { + if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, iter->first)) + { + detected = true; + avoidedNotice = false; + MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + break; + } + else if (!detected) + avoidedNotice = true; + } + } + + if (sneakSkillTimer >= fSneakUseDelay) + sneakSkillTimer = 0.f; + + if (avoidedNotice && sneakSkillTimer == 0.f) + player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 0); + + if (!detected) + MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); + } + sneakTimer += duration; + sneakSkillTimer += duration; } else + { + sneakTimer = 0.f; MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + } } } void Actors::restoreDynamicStats(bool sleep) From 2477456f993acdc1f26215da51603d36b7acdfcd Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 03:54:41 +0200 Subject: [PATCH 115/226] Implement Murder crimes and OnMurder instruction (Fixes #1315) --- apps/openmw/mwclass/npc.cpp | 13 ++++++ apps/openmw/mwmechanics/activespells.hpp | 8 ++-- apps/openmw/mwmechanics/actors.cpp | 40 +++++++++++++++++++ apps/openmw/mwmechanics/creaturestats.cpp | 19 ++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 7 ++++ .../mwmechanics/mechanicsmanagerimp.cpp | 15 +++++-- apps/openmw/mwscript/docs/vmformat.txt | 4 +- apps/openmw/mwscript/statsextensions.cpp | 21 ++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 + components/esm/creaturestats.cpp | 6 +++ components/esm/creaturestats.hpp | 1 + 12 files changed, 128 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 2b4c54e24..1a33747f2 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -628,6 +628,8 @@ namespace MWClass !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + bool wasDead = getCreatureStats(ptr).isDead(); + getCreatureStats(ptr).setAttacked(true); if(!successful) @@ -751,6 +753,17 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } + + if (!wasDead && getCreatureStats(ptr).isDead()) + { + // NPC was killed + // Simple check for who attacked first: if the player attacked first, a crimeId should be set + // Doesn't handle possible edge case where no one reported the assault, but in such a case, + // for bystanders it is not possible to tell who attacked first, anyway. + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); + } } void Npc::block(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 6c4c93128..9c1a5e613 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -40,6 +40,10 @@ namespace MWMechanics void readState (const ESM::ActiveSpells& state); void writeState (ESM::ActiveSpells& state) const; + TIterator begin() const; + + TIterator end() const; + private: mutable TContainer mSpells; @@ -57,10 +61,6 @@ namespace MWMechanics const TContainer& getActiveSpells() const; - TIterator begin() const; - - TIterator end() const; - public: ActiveSpells(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index dcbb6b8bd..0c5a2abec 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -342,6 +342,8 @@ namespace MWMechanics CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr); const MagicEffects &effects = creatureStats.getMagicEffects(); + bool wasDead = creatureStats.isDead(); + // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { @@ -454,6 +456,44 @@ namespace MWMechanics } creatureStats.setHealth(health); + if (!wasDead && creatureStats.isDead()) + { + // The actor was killed by a magic effect. Figure out if the player was responsible for it. + const ActiveSpells& spells = creatureStats.getActiveSpells(); + for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + const ActiveSpells::ActiveSpellParams& spell = it->second; + for (std::vector::const_iterator effectIt = spell.mEffects.begin(); + effectIt != spell.mEffects.end(); ++effectIt) + { + int effectId = effectIt->mEffectId; + bool isDamageEffect = false; + for (unsigned int i=0; igetPlayerPtr(); + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId); + if (isDamageEffect && caster == player) + { + // Simple check for who attacked first: if the player attacked first, a crimeId should be set + // Doesn't handle possible edge case where no one reported the assault, but in such a case, + // for bystanders it is not possible to tell who attacked first, anyway. + if (ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 + && ptr != player) + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); + break; + } + } + } + } + // TODO: dirty flag for magic effects to avoid some unnecessary work below? // Update bound effects diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index e26e7a8ba..af35a109a 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -14,7 +14,7 @@ namespace MWMechanics int CreatureStats::sActorId = 0; CreatureStats::CreatureStats() - : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), + : mLevel (0), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), mAttackingOrSpell(false), @@ -245,6 +245,21 @@ namespace MWMechanics mDied = false; } + bool CreatureStats::hasBeenMurdered() const + { + return mMurdered; + } + + void CreatureStats::notifyMurder() + { + mMurdered = true; + } + + void CreatureStats::clearHasBeenMurdered() + { + mMurdered = false; + } + void CreatureStats::resurrect() { if (mDead) @@ -479,6 +494,7 @@ namespace MWMechanics state.mDead = mDead; state.mDied = mDied; + state.mMurdered = mMurdered; state.mFriendlyHits = mFriendlyHits; state.mTalkedTo = mTalkedTo; state.mAlarmed = mAlarmed; @@ -527,6 +543,7 @@ namespace MWMechanics mDead = state.mDead; mDied = state.mDied; + mMurdered = state.mMurdered; mFriendlyHits = state.mFriendlyHits; mTalkedTo = state.mTalkedTo; mAlarmed = state.mAlarmed; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index d02af8fb6..b365a0b89 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -35,6 +35,7 @@ namespace MWMechanics AiSequence mAiSequence; bool mDead; bool mDied; + bool mMurdered; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; @@ -170,6 +171,12 @@ namespace MWMechanics void clearHasDied(); + bool hasBeenMurdered() const; + + void clearHasBeenMurdered(); + + void notifyMurder(); + void resurrect(); bool hasCommonDisease() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index cb59d188f..612a4ea84 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -868,10 +868,16 @@ namespace MWMechanics // Find actors who directly witnessed the crime for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { - if (*it == player) continue; // not the player + if (*it == player) + continue; // skip player + if (it->getClass().getCreatureStats(*it).isDead()) + continue; // Was the crime seen? - if (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) + if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) + // Murder crime can be reported even if no one saw it (hearing is enough, I guess). + // TODO: Add mod support for stealth executions! + || (type == OT_Murder && *it != victim)) { if (*it == victim) victimAware = true; @@ -904,6 +910,9 @@ namespace MWMechanics { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); + if (type == OT_Murder && !victim.isEmpty()) + victim.getClass().getCreatureStats(victim).notifyMurder(); + // Bounty for each type of crime if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) arg = store.find("iCrimeTresspass")->getInt(); @@ -971,7 +980,7 @@ namespace MWMechanics for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if ( *it == player - || !it->getClass().isNpc()) continue; + || !it->getClass().isNpc() || it->getClass().getCreatureStats(*it).isDead()) continue; int aggression = fight; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index ab69bc4b4..c125985c0 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -398,5 +398,7 @@ op 0x2000245: ClearInfoActor op 0x2000246: ClearInfoActor, explicit op 0x2000247: BetaComment op 0x2000248: BetaComment, explicit +op 0x2000249: OnMurder +op 0x200024a: OnMurder, explicit -opcodes 0x2000249-0x3ffffff unused +opcodes 0x200024b-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 8b61237a1..a3bc223aa 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1072,6 +1072,25 @@ namespace MWScript } }; + template + class OpOnMurder : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = + ptr.getClass().getCreatureStats (ptr).hasBeenMurdered(); + + if (value) + ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered(); + + runtime.push (value); + } + }; + template class OpOnKnockout : public Interpreter::Opcode0 { @@ -1264,6 +1283,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeOnDeath, new OpOnDeath); interpreter.installSegment5 (Compiler::Stats::opcodeOnDeathExplicit, new OpOnDeath); + interpreter.installSegment5 (Compiler::Stats::opcodeOnMurder, new OpOnMurder); + interpreter.installSegment5 (Compiler::Stats::opcodeOnMurderExplicit, new OpOnMurder); interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockout, new OpOnKnockout); interpreter.installSegment5 (Compiler::Stats::opcodeOnKnockoutExplicit, new OpOnKnockout); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 5733a4170..2d140044c 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -449,6 +449,7 @@ namespace Compiler extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit); + extensions.registerFunction ("onmurder", 'l', "", opcodeOnMurder, opcodeOnMurderExplicit); extensions.registerFunction ("onknockout", 'l', "", opcodeOnKnockout, opcodeOnKnockoutExplicit); extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 8716cdd18..ed6f1df92 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -385,6 +385,8 @@ namespace Compiler const int opcodeLowerRankExplicit = 0x20001eb; const int opcodeOnDeath = 0x20001fc; const int opcodeOnDeathExplicit = 0x2000205; + const int opcodeOnMurder = 0x2000249; + const int opcodeOnMurderExplicit = 0x200024a; const int opcodeOnKnockout = 0x2000240; const int opcodeOnKnockoutExplicit = 0x2000241; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 84902d8c2..0a9a36109 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -21,6 +21,9 @@ void ESM::CreatureStats::load (ESMReader &esm) mDied = false; esm.getHNOT (mDied, "DIED"); + mMurdered = false; + esm.getHNOT (mMurdered, "MURD"); + mFriendlyHits = 0; esm.getHNOT (mFriendlyHits, "FRHT"); @@ -127,6 +130,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mDied) esm.writeHNT ("DIED", mDied); + if (mMurdered) + esm.writeHNT ("MURD", mMurdered); + if (mFriendlyHits) esm.writeHNT ("FRHT", mFriendlyHits); diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 628bfee0a..7ae57da16 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -38,6 +38,7 @@ namespace ESM bool mDead; bool mDied; + bool mMurdered; int mFriendlyHits; bool mTalkedTo; bool mAlarmed; From 073cc3f02ca8799e22364be50cdbc8150de2a891 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Jun 2014 11:01:17 +0200 Subject: [PATCH 116/226] fixed base flag in content file loader code --- apps/opencs/model/doc/loader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index c106c06e8..6fb10426c 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -39,6 +39,7 @@ void CSMDoc::Loader::load() Document *document = iter->first; int size = static_cast (document->getContentFiles().size()); + int editedIndex = size-1; // index of the file to be edited/created if (document->isNew()) --size; @@ -77,7 +78,7 @@ void CSMDoc::Loader::load() { boost::filesystem::path path = document->getContentFiles()[iter->second.mFile]; - int steps = document->getData().startLoading (path, iter->second.mFilegetData().startLoading (path, iter->second.mFile!=editedIndex, false); iter->second.mRecordsLeft = true; emit nextStage (document, path.filename().string(), steps/batchingSize); From cb6f1d3a52b50a858487bcfa571b5bb9aa1721af Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 14:46:18 +0200 Subject: [PATCH 117/226] Fix wait dialog arrow button step (Fixes #1524) --- files/mygui/openmw_wait_dialog.layout | 2 ++ 1 file changed, 2 insertions(+) diff --git a/files/mygui/openmw_wait_dialog.layout b/files/mygui/openmw_wait_dialog.layout index eeb7012eb..7c065038d 100644 --- a/files/mygui/openmw_wait_dialog.layout +++ b/files/mygui/openmw_wait_dialog.layout @@ -19,6 +19,8 @@ + + From 4f9ebd148c06770256d32e37aac02a633f7db9bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 15:25:54 +0200 Subject: [PATCH 118/226] Fix broken AI movement on Z axis --- apps/openmw/mwmechanics/aicombat.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 846f6d8dc..f5881d605 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -470,6 +470,8 @@ namespace MWMechanics if(preferShortcut) { + if (canMoveByZ) + mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget); mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget); mForceNoShortcut = false; mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0; From 1244da85df5ab746e4eafa8d34ddc867c1a7f173 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 16:27:33 +0200 Subject: [PATCH 119/226] Make Detect Life spell detect NPCs when in werewolf form (Fixes #1527) --- apps/openmw/mwgui/hud.cpp | 2 ++ apps/openmw/mwgui/mapwindow.cpp | 15 +++++++++++++++ apps/openmw/mwgui/mapwindow.hpp | 6 +++++- apps/openmw/mwworld/worldimp.cpp | 24 +++++++++++++++++------- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index d87ac7ec5..b39fd0db7 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -384,6 +384,8 @@ namespace MWGui void HUD::onFrame(float dt) { + LocalMapBase::onFrame(dt); + mCellNameTimer -= dt; mWeaponSpellTimer -= dt; if (mCellNameTimer < 0) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 29c065f3d..db3cfadde 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -36,6 +36,7 @@ namespace MWGui , mLastDirectionX(0.0f) , mLastDirectionY(0.0f) , mCompass(NULL) + , mMarkerUpdateTimer(0.0f) { } @@ -345,6 +346,18 @@ namespace MWGui markerWidget->setUserString("IsMarker", "true"); markerWidget->setUserData(markerPos); markerWidget->setColour(markerColour); + mMarkerWidgets.push_back(markerWidget); + } + } + + void LocalMapBase::onFrame(float dt) + { + mMarkerUpdateTimer += dt; + + if (mMarkerUpdateTimer >= 0.25) + { + mMarkerUpdateTimer = 0; + updateMarkers(); } } @@ -471,6 +484,8 @@ namespace MWGui void MapWindow::onFrame(float dt) { + LocalMapBase::onFrame(dt); + for (std::vector::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it) { mGlobalMapRender->exploreCell(it->first, it->second); diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index d23b0c228..c73e5d7a1 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -35,6 +35,8 @@ namespace MWGui void setPlayerDir(const float x, const float y); void setPlayerPos(const float x, const float y); + void onFrame(float dt); + bool toggleFogOfWar(); struct MarkerPosition @@ -73,12 +75,14 @@ namespace MWGui virtual void notifyMapChanged() {} // Update markers (Detect X effects, Mark/Recall effects) - // Note, door markers handled in setActiveCell + // Note, door markers are handled in setActiveCell void updateMarkers(); void addDetectionMarkers(int type); OEngine::GUI::Layout* mLayout; + float mMarkerUpdateTimer; + bool mMapDragAndDrop; float mLastPositionX; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4386f5b0d..f6573096d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2445,14 +2445,15 @@ namespace MWWorld if (!ptr.getRefData().isEnabled()) return true; - // Consider references inside containers as well - if (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name()) + // Consider references inside containers as well (except if we are looking for a Creature, they cannot be in containers) + if (mType != World::Detect_Creature && + (ptr.getClass().isActor() || ptr.getClass().getTypeName() == typeid(ESM::Container).name())) { MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr); { for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - if (needToAdd(*it)) + if (needToAdd(*it, mDetector)) { mOut.push_back(ptr); return true; @@ -2461,16 +2462,25 @@ namespace MWWorld } } - if (needToAdd(ptr)) + if (needToAdd(ptr, mDetector)) mOut.push_back(ptr); return true; } - bool needToAdd (MWWorld::Ptr ptr) + bool needToAdd (MWWorld::Ptr ptr, MWWorld::Ptr detector) { - if (mType == World::Detect_Creature && ptr.getClass().getTypeName() != typeid(ESM::Creature).name()) - return false; + if (mType == World::Detect_Creature) + { + // If in werewolf form, this detects only NPCs, otherwise only creatures + if (detector.getClass().isNpc() && detector.getClass().getNpcStats(detector).isWerewolf()) + { + if (ptr.getClass().getTypeName() != typeid(ESM::NPC).name()) + return false; + } + else if (ptr.getClass().getTypeName() != typeid(ESM::Creature).name()) + return false; + } if (mType == World::Detect_Key && !ptr.getClass().isKey(ptr)) return false; if (mType == World::Detect_Enchantment && ptr.getClass().getEnchantment(ptr).empty()) From 1dc9e151cb63355844a9347e66b02729db64f787 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 16:51:59 +0200 Subject: [PATCH 120/226] Count werewolf kills (Fixes #1525) --- apps/openmw/mwclass/npc.cpp | 7 ++++++- apps/openmw/mwmechanics/actors.cpp | 12 +++++++++--- apps/openmw/mwmechanics/npcstats.cpp | 7 +++++++ apps/openmw/mwmechanics/npcstats.hpp | 3 +++ components/compiler/extensions0.cpp | 2 +- 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 1a33747f2..a93cae9ba 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -757,11 +757,16 @@ namespace MWClass if (!wasDead && getCreatureStats(ptr).isDead()) { // NPC was killed + if (!attacker.isEmpty() && attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()) + { + attacker.getClass().getNpcStats(attacker).addWerewolfKill(); + } + // Simple check for who attacked first: if the player attacked first, a crimeId should be set // Doesn't handle possible edge case where no one reported the assault, but in such a case, // for bystanders it is not possible to tell who attacked first, anyway. MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - if (ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) + if (attacker == player && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0c5a2abec..d72ab7db8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -460,6 +460,9 @@ namespace MWMechanics { // The actor was killed by a magic effect. Figure out if the player was responsible for it. const ActiveSpells& spells = creatureStats.getActiveSpells(); + bool killedByPlayer = false; + bool murderedByPlayer = false; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ActiveSpells::ActiveSpellParams& spell = it->second; @@ -478,20 +481,23 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DamageHealth || effectId == ESM::MagicEffect::AbsorbHealth) isDamageEffect = true; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId); if (isDamageEffect && caster == player) { + killedByPlayer = true; // Simple check for who attacked first: if the player attacked first, a crimeId should be set // Doesn't handle possible edge case where no one reported the assault, but in such a case, // for bystanders it is not possible to tell who attacked first, anyway. if (ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player) - MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); - break; + murderedByPlayer = true; } } } + if (murderedByPlayer) + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder); + if (killedByPlayer && player.getClass().getNpcStats(player).isWerewolf()) + player.getClass().getNpcStats(player).addWerewolfKill(); } // TODO: dirty flag for magic effects to avoid some unnecessary work below? diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 642e1cfe1..579969f9d 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -396,6 +396,8 @@ void MWMechanics::NpcStats::setWerewolf (bool set) { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + mWerewolfKills = 0; + for(size_t i = 0;i < ESM::Attribute::Length;i++) { mWerewolfAttributes[i] = getAttribute(i); @@ -427,6 +429,11 @@ int MWMechanics::NpcStats::getWerewolfKills() const return mWerewolfKills; } +void MWMechanics::NpcStats::addWerewolfKill() +{ + ++mWerewolfKills; +} + int MWMechanics::NpcStats::getProfit() const { return mProfit; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 185a58b94..a066760d0 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -126,6 +126,9 @@ namespace MWMechanics int getWerewolfKills() const; + /// Increments mWerewolfKills by 1. + void addWerewolfKill(); + float getTimeToStartDrowning() const; /// Sets time left for the creature to drown if it stays underwater. /// @param time value from [0,20] diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 2d140044c..efd45f912 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -441,7 +441,7 @@ namespace Compiler extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace, opcodeGetRaceExplicit); - extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills); + extensions.registerFunction ("getwerewolfkills", 'l', "", opcodeGetWerewolfKills); extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit); extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit); extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit); From a3ea7cb9563ccaf93372893e7c4b05299202a917 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 17:04:41 +0200 Subject: [PATCH 121/226] Ignore distance when considering aggression due to crime (seems to work better, all balmora mages guild members now come to help when one is attacked) --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 8 +++++--- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 91d0ed9bf..f2b71bd4c 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -195,7 +195,7 @@ 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) = 0; + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0; }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 612a4ea84..f4ffa499c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1001,7 +1001,7 @@ namespace MWMechanics } else { - bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression); + bool aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(*it, player, aggression, true); if (aggressive) { startCombat(*it, player); @@ -1156,12 +1156,14 @@ namespace MWMechanics mActors.clear(); } - bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias) + bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target, int bias, bool ignoreDistance) { Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); Ogre::Vector3 pos2 (target.getRefData().getPosition().pos); - float d = pos1.distance(pos2); + float d = 0; + if (!ignoreDistance) + d = pos1.distance(pos2); static int iFightDistanceBase = MWBase::Environment::get().getWorld()->getStore().get().find( "iFightDistanceBase")->getInt(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 0fee72b77..596e6887e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -159,7 +159,7 @@ 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); + virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false); }; } From 666dbc6ddca39329240cef6141bccb943e1b773e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 17:18:30 +0200 Subject: [PATCH 122/226] Disable QuickKeysMenu in werewolf form --- apps/openmw/mwinput/inputmanagerimp.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 15b5abde6..cd3be4b42 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -26,7 +26,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "../mwdialogue/dialoguemanagerimp.hpp" @@ -826,6 +826,14 @@ namespace MWInput void InputManager::quickKey (int index) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (player.getClass().getNpcStats(player).isWerewolf()) + { + // Cannot use items or spells while in werewolf form + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + return; + } + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) MWBase::Environment::get().getWindowManager()->activateQuickKey (index); } @@ -834,7 +842,18 @@ namespace MWInput { if (!MWBase::Environment::get().getWindowManager()->isGuiMode () && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (player.getClass().getNpcStats(player).isWerewolf()) + { + // Cannot use items or spells while in werewolf form + MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); + return; + } + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); + + } else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) { while(MyGUI::InputManager::getInstance().isModalAny()) { //Handle any open Modal windows MWBase::Environment::get().getWindowManager()->getCurrentModal()->exit(); From d4678a8d5517a8d897c64151eba63f2a02c2bd45 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 18:33:53 +0200 Subject: [PATCH 123/226] Fix level up dialogue layout (Fixes #1393) --- apps/openmw/mwgui/levelupdialog.cpp | 5 +++-- apps/openmw/mwgui/tooltips.cpp | 8 ++------ apps/openmw/mwgui/widgets.cpp | 7 +++++-- files/mygui/openmw_levelup_dialog.layout | 24 ++++++++++++++++-------- files/mygui/openmw_text.skin.xml | 7 +++++++ 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 38995ac32..eaa822722 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -104,9 +104,10 @@ namespace MWGui int attribute = mSpentAttributes[i]; - int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 30; + int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 20; - MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(24+xdiff,-4); + MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(22+xdiff,0); + pos.top += (mAttributes[attribute]->getHeight() - image->getHeight())/2; image->setPosition(pos); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 16b010908..676a9ee63 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -233,13 +233,9 @@ namespace MWGui for (std::map::iterator it = userStrings.begin(); it != userStrings.end(); ++it) { - if (it->first == "ToolTipType" - || it->first == "ToolTipLayout" - || it->first == "IsMarker") - continue; - - size_t underscorePos = it->first.find("_"); + if (underscorePos == std::string::npos) + continue; std::string propertyKey = it->first.substr(0, underscorePos); std::string widgetName = it->first.substr(underscorePos+1, it->first.size()-(underscorePos+1)); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index b30cf2bae..b83ca0b09 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -630,8 +630,11 @@ namespace MWGui MyGUI::IntSize AutoSizedButton::getRequestedSize() { - MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); - size.height = std::max(24, size.height); + MyGUI::IntSize padding(24, 8); + if (isUserString("TextPadding")) + padding = MyGUI::IntSize::parse(getUserString("TextPadding")); + + MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(padding.width,padding.height); return size; } diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index 765bf88a8..0b12d7a05 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -31,17 +31,18 @@ - - - - - - - - + + + + + + + + + @@ -55,6 +56,7 @@ + @@ -68,6 +70,7 @@ + @@ -81,6 +84,7 @@ + @@ -95,6 +99,7 @@ + @@ -108,6 +113,7 @@ + @@ -121,6 +127,7 @@ + @@ -134,6 +141,7 @@ + diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index d4c72c75b..4c1117cf5 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -32,6 +32,13 @@ + + + + + + + From 80f66e21576d96cb1f4b486c816451e826cf1827 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 21:16:32 +0200 Subject: [PATCH 124/226] Fix crash when avformat_open_input fails (Fixes #1522) --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 75f7ccae4..10a782b96 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -159,17 +159,21 @@ void FFmpeg_Decoder::open(const std::string &fname) mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) { - if (mFormatCtx->pb != NULL) + // "Note that a user-supplied AVFormatContext will be freed on failure". + if (mFormatCtx) { - if (mFormatCtx->pb->buffer != NULL) - { - av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; - } - av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + if (mFormatCtx->pb != NULL) + { + if (mFormatCtx->pb->buffer != NULL) + { + av_free(mFormatCtx->pb->buffer); + mFormatCtx->pb->buffer = NULL; + } + av_free(mFormatCtx->pb); + mFormatCtx->pb = NULL; + } + avformat_free_context(mFormatCtx); } - avformat_free_context(mFormatCtx); mFormatCtx = NULL; fail("Failed to allocate input stream"); } From 56bc5a9d39421c71b15a16883f0253e74ba99dde Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 21:27:41 +0200 Subject: [PATCH 125/226] Fix being able to steal undetected just after invisibility breaks --- apps/openmw/mwworld/worldimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f6573096d..645591cb8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2344,6 +2344,9 @@ namespace MWWorld actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); if (actor.getClass().hasInventoryStore(actor)) actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); + + // Normally updated once per frame, but here it is kinda important to do it right away. + MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(actor); } bool World::isDark() const From fe1e6a27199794da5c093ce4ff7e0f792e1987d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 17 Jun 2014 22:24:56 +0200 Subject: [PATCH 126/226] Make Weakness effects apply to all subsequent effects within the same spell (Fixes #1150) --- apps/openmw/mwmechanics/spellcasting.cpp | 27 ++++++++++++++++++------ apps/openmw/mwmechanics/spellcasting.hpp | 9 ++++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index c996e90d6..26480ed03 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -148,13 +148,17 @@ namespace MWMechanics return school; } - float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell, const MagicEffects* effects) { const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( effectId); const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects(); + if (effects) + magicEffects = effects; float resisted = 0; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) @@ -165,9 +169,9 @@ namespace MWMechanics float resistance = 0; if (resistanceEffect != -1) - resistance += stats.getMagicEffects().get(resistanceEffect).mMagnitude; + resistance += magicEffects->get(resistanceEffect).mMagnitude; if (weaknessEffect != -1) - resistance -= stats.getMagicEffects().get(weaknessEffect).mMagnitude; + resistance -= magicEffects->get(weaknessEffect).mMagnitude; float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); @@ -204,9 +208,10 @@ namespace MWMechanics return resisted; } - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell) + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell, const MagicEffects* effects) { - float resistance = getEffectResistance(effectId, actor, caster, spell); + float resistance = getEffectResistance(effectId, actor, caster, spell, effects); if (resistance >= 0) return 1 - resistance / 100.f; else @@ -261,6 +266,13 @@ namespace MWMechanics bool firstAppliedEffect = true; bool anyHarmfulEffect = false; + // HACK: cache target's magic effects here, and add any applied effects to it. Use the cached effects for determining resistance. + // This is required for Weakness effects in a spell to apply to any subsequent effects in the spell. + // Otherwise, they'd only apply after the whole spell was added. + MagicEffects targetEffects; + if (target.getClass().isActor()) + targetEffects += target.getClass().getCreatureStats(target).getMagicEffects(); + bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player"); for (std::vector::const_iterator effectIt (effects.mList.begin()); @@ -346,8 +358,7 @@ namespace MWMechanics // Try resisting if (magnitudeMult > 0 && target.getClass().isActor()) { - - magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell); + magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects); if (magnitudeMult == 0) { // Fully resisted, show message @@ -375,6 +386,8 @@ namespace MWMechanics effect.mDuration = effectIt->mDuration; effect.mMagnitude = magnitude; + targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); + appliedLastingEffects.push_back(effect); // For absorb effects, also apply the effect to the caster - but with a negative diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index dce4b792e..821d7ae0e 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -18,6 +18,7 @@ namespace ESM namespace MWMechanics { class EffectKey; + class MagicEffects; ESM::Skill::SkillEnum spellSchoolToSkill(int school); @@ -35,9 +36,13 @@ namespace MWMechanics int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); /// @return >=100 for fully resisted. can also return negative value for damage amplification. - float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); + /// @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. + float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); - float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, const ESM::Spell* spell = NULL); + float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, + const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); class CastSpell { From e95483c40f7c04168a09e64d45040146310ecc81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 01:41:07 +0200 Subject: [PATCH 127/226] Fix crash for on target spells cast by non-actors (Fixes #1529) --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 22 ++++++++++++++---- apps/openmw/mwmechanics/spellcasting.hpp | 9 ++++++-- apps/openmw/mwworld/projectilemanager.cpp | 28 ++++++++++++++++------- apps/openmw/mwworld/projectilemanager.hpp | 11 +++++++-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- 7 files changed, 57 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index df75ce64d..29f821e85 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -470,7 +470,7 @@ namespace MWBase virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName) = 0; + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) = 0; virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 26480ed03..6467730dd 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -340,7 +340,7 @@ namespace MWMechanics } // Try reflecting - if (!reflected && magnitudeMult > 0 && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) + if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) { int reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).mMagnitude; int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] @@ -465,14 +465,14 @@ namespace MWMechanics if (!appliedLastingEffects.empty()) { int casterActorId = -1; - if (caster.getClass().isActor()) + if (!caster.isEmpty() && caster.getClass().isActor()) casterActorId = caster.getClass().getCreatureStats(caster).getActorId(); target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, casterActorId); } // Notify the target actor they've been hit - if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor()) + if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor()) target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); } @@ -657,7 +657,9 @@ namespace MWMechanics getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed); if (!projectileModel.empty()) MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, - false, enchantment->mEffects, mCaster, mSourceName); + false, enchantment->mEffects, mCaster, mSourceName, + // Not needed, enchantments can only be cast by actors + Ogre::Vector3(1,0,0)); return true; } @@ -742,8 +744,18 @@ namespace MWMechanics float speed = 0; getProjectileInfo(spell->mEffects, projectileModel, sound, speed); if (!projectileModel.empty()) + { + Ogre::Vector3 fallbackDirection (0,1,0); + // Fall back to a "caster to target" direction if we have no other means of determining it + // (e.g. when cast by a non-actor) + if (!mTarget.isEmpty()) + fallbackDirection = + Ogre::Vector3(mTarget.getRefData().getPosition().pos)- + Ogre::Vector3(mCaster.getRefData().getPosition().pos); + MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed, - false, spell->mEffects, mCaster, mSourceName); + false, spell->mEffects, mCaster, mSourceName, fallbackDirection); + } return true; } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 821d7ae0e..b526a4353 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -47,8 +47,8 @@ namespace MWMechanics class CastSpell { private: - MWWorld::Ptr mCaster; - MWWorld::Ptr mTarget; + MWWorld::Ptr mCaster; // May be empty + MWWorld::Ptr mTarget; // May be empty public: bool mStack; std::string mId; // ID of spell, potion, item etc @@ -59,8 +59,13 @@ namespace MWMechanics CastSpell(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); bool cast (const ESM::Spell* spell); + + /// @note mCaster must be an actor bool cast (const MWWorld::Ptr& item); + + /// @note mCaster must be an NPC bool cast (const ESM::Ingredient* ingredient); + bool cast (const ESM::Potion* potion); /// @note Auto detects if spell, ingredient or potion diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 4e4f0b271..cfb407b4a 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -57,22 +57,32 @@ namespace MWWorld void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound, const std::string &spellId, float speed, bool stack, - const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName) + const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName, + const Ogre::Vector3& fallbackDirection) { - // Spawn at 0.75 * ActorHeight - float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75; + float height = 0; + if (OEngine::Physic::PhysicActor* actor = mPhysEngine.getCharacter(caster.getRefData().getHandle())) + height = actor->getHalfExtents().z * 2 * 0.75; // Spawn at 0.75 * ActorHeight - Ogre::Vector3 pos(actor.getRefData().getPosition().pos); + Ogre::Vector3 pos(caster.getRefData().getPosition().pos); pos.z += height; - Ogre::Quaternion 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::Quaternion orient; + if (caster.getClass().isActor()) + orient = Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + else + orient = Ogre::Vector3::UNIT_Y.getRotationTo(fallbackDirection); MagicBoltState state; state.mSourceName = sourceName; state.mId = model; state.mSpellId = spellId; - state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); + state.mCaster = caster; + if (caster.getClass().isActor()) + state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); + else + state.mActorId = -1; state.mSpeed = speed; state.mStack = stack; state.mSoundId = sound; @@ -152,7 +162,9 @@ namespace MWWorld { MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); - MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); + MWWorld::Ptr caster = it->mCaster; + if (caster.isEmpty()) + caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); if (!obstacle.isEmpty() && obstacle == caster) continue; diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index da965a4cf..4627d3b8a 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -39,9 +39,10 @@ namespace MWWorld ProjectileManager (Ogre::SceneManager* sceneMgr, OEngine::Physic::PhysicEngine& engine); + /// If caster is an actor, the actor's facing orientation is used. Otherwise fallbackDirection is used. void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName); + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection); void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); @@ -64,9 +65,15 @@ namespace MWWorld NifOgre::ObjectScenePtr mObject; Ogre::SceneNode* mNode; - // Actor who shot this projectile int mActorId; + // actorId doesn't work for non-actors, so we also keep track of the Ptr. + // For non-actors, the caster ptr is mainly needed to prevent the projectile + // from colliding with its caster. + // TODO: this will break when the game is saved and reloaded, since there is currently + // no way to write identifiers for non-actors to a savegame. + MWWorld::Ptr mCaster; + // MW-id of this projectile std::string mId; }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 645591cb8..0b623f854 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2329,9 +2329,9 @@ namespace MWWorld void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName) + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) { - mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName); + mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, caster, sourceName, fallbackDirection); } const std::vector& World::getContentFiles() const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ac1244652..db599b0de 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -546,7 +546,7 @@ namespace MWWorld virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, float speed, bool stack, const ESM::EffectList& effects, - const MWWorld::Ptr& actor, const std::string& sourceName); + const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection); virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); From 224163e5a2f4f1a8f073d5331df1d124cb4afb71 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 05:04:49 +0200 Subject: [PATCH 128/226] Fix console text becoming unreadable when selected (Fixes #1530) --- files/mygui/openmw_console.layout | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index 0c9a97d04..c88c1955d 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -13,10 +13,13 @@ + - + + + From 9a6737073fe400c0215daa7a93aaac9b45ac37af Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 15:33:09 +0200 Subject: [PATCH 129/226] Fix broken swimdeath in first person --- apps/openmw/mwmechanics/character.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6f0a255a9..ddfba51ce 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -409,13 +409,6 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I void CharacterController::playDeath(float startpoint, CharacterState death) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()) - { - // The first-person animations do not include death, so we need to - // force-switch to third person before playing the death animation. - MWBase::Environment::get().getWorld()->useDeathCamera(); - } - switch (death) { case CharState_SwimDeath: @@ -459,6 +452,13 @@ void CharacterController::playDeath(float startpoint, CharacterState death) void CharacterController::playRandomDeath(float startpoint) { + if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + // The first-person animations do not include death, so we need to + // force-switch to third person before playing the death animation. + MWBase::Environment::get().getWorld()->useDeathCamera(); + } + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) { mDeathState = CharState_SwimDeath; From 8a4227ec380f7e895ba449537167b9a5328c786f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 16:01:20 +0200 Subject: [PATCH 130/226] Heal player while in jail --- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0b623f854..6e975d25a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2615,6 +2615,8 @@ namespace MWWorld int days = std::max(1, bounty / iDaysinPrisonMod); advanceTime(days * 24); + for (int i=0; irest (true); std::set skills; for (int day=0; day buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); From 5645c9185beafb5dc81b0948b62b4f56d908e009 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 17:18:48 +0200 Subject: [PATCH 131/226] Fix location of local data path --- components/files/configurationmanager.cpp | 4 ++-- files/openmw.cfg | 2 +- files/openmw.cfg.local | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index ffa911b44..58d75d1fd 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -18,7 +18,7 @@ static const char* const openmwCfgFile = "openmw.cfg"; const char* const mwToken = "?mw?"; const char* const localToken = "?local?"; -const char* const userToken = "?user?"; +const char* const userDataToken = "?userdata?"; const char* const globalToken = "?global?"; ConfigurationManager::ConfigurationManager() @@ -40,7 +40,7 @@ void ConfigurationManager::setupTokensMapping() { mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); - mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath)); + mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } diff --git a/files/openmw.cfg b/files/openmw.cfg index 4633141a6..b67b79a96 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,4 +1,4 @@ data="?global?data" data="?mw?Data Files" -data-local="?user?data" +data-local="?userdata?data" resources=${OPENMW_RESOURCE_FILES} diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index d6ca2d554..6a578542d 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -1,5 +1,5 @@ data="?global?data" data="?mw?Data Files" data=./data -data-local="?user?data" +data-local="?userdata?data" resources=./resources From 4e71db70819a49ffcb8144dc16d27e1ed252b4bf Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 21:39:17 +0200 Subject: [PATCH 132/226] Savegame: Don't load/save deleted container items. This is currently pointless, and also causes new garbage being added on each load/save cycle: Container stores are first filled from ESM records, then cleared and filled from the savegame. The items from ESM records remain as deleted refs. --- apps/openmw/mwworld/containerstore.cpp | 2 ++ components/esm/inventorystate.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8a076c3fc..e330ddaee 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -92,6 +92,8 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { + if (iter->mData.getCount() == 0) + continue; ESM::ObjectState state; storeState (*iter, state); int slot = equipable ? getSlot (*iter) : -1; diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index 1b0bc772e..2154faa83 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -37,6 +37,8 @@ void ESM::InventoryState::load (ESMReader &esm) LightState state; int slot; read (esm, state, slot); + if (state.mCount == 0) + continue; mLights.push_back (std::make_pair (state, slot)); } else @@ -44,6 +46,8 @@ void ESM::InventoryState::load (ESMReader &esm) ObjectState state; int slot; read (esm, state, slot); + if (state.mCount == 0) + continue; mItems.push_back (std::make_pair (state, std::make_pair (id, slot))); } } From 2193977eec52e51981ee7799de03123caabfeadc Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 22:59:18 +0200 Subject: [PATCH 133/226] Savegame: Don't fill CustomData from ESM records if the savegame overwrites it anyway This gets rid of some junk in ContainerStores (since clear() only sets count to 0 and doesn't really delete references), and significantly speeds up loading savegames (by about 80% in my test) --- apps/openmw/mwclass/container.cpp | 7 ++++++- apps/openmw/mwclass/creature.cpp | 15 ++++++++++++++- apps/openmw/mwclass/npc.cpp | 7 ++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 9498ea52d..53add4274 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -286,7 +286,12 @@ namespace MWClass { const ESM::ContainerState& state2 = dynamic_cast (state); - ensureCustomData (ptr); + if (!ptr.getRefData().getCustomData()) + { + // Create a CustomData, but don't fill it from ESM records (not needed) + std::auto_ptr data (new ContainerCustomData); + ptr.getRefData().setCustomData (data.release()); + } dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. readState (state2.mInventory); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 59c1087f9..97cf30fe1 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -798,7 +798,20 @@ namespace MWClass { const ESM::CreatureState& state2 = dynamic_cast (state); - ensureCustomData (ptr); + if (!ptr.getRefData().getCustomData()) + { + // Create a CustomData, but don't fill it from ESM records (not needed) + std::auto_ptr data (new CreatureCustomData); + + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::Creature::Weapon) + data->mContainerStore = new MWWorld::InventoryStore(); + else + data->mContainerStore = new MWWorld::ContainerStore(); + + ptr.getRefData().setCustomData (data.release()); + } CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a93cae9ba..58a5bc622 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1293,7 +1293,12 @@ namespace MWClass { const ESM::NpcState& state2 = dynamic_cast (state); - ensureCustomData (ptr); + if (!ptr.getRefData().getCustomData()) + { + // Create a CustomData, but don't fill it from ESM records (not needed) + std::auto_ptr data (new NpcCustomData); + ptr.getRefData().setCustomData (data.release()); + } NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); From c3e4160a0ae94dda942f483850b3f267e8f7665a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 18 Jun 2014 23:50:55 +0200 Subject: [PATCH 134/226] Don't crash in StatsWindow for invalid faction rank values PcRaiseRank: don't allow to raise rank beyond max rank --- apps/openmw/mwgui/statswindow.cpp | 10 ++++++---- apps/openmw/mwscript/statsextensions.cpp | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index b4bd0d738..6c4e00ae5 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -488,14 +488,16 @@ namespace MWGui text += "\n#BF9959#{sExpelled}"; else { - text += std::string("\n#BF9959") + faction->mRanks[it->second]; + int rank = it->second; + rank = std::max(0, std::min(9, rank)); + text += std::string("\n#BF9959") + faction->mRanks[rank]; - if (it->second < 9) + if (rank < 9) { // player doesn't have max rank yet - text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; + text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[rank+1]; - ESM::RankData rankData = faction->mData.mRankData[it->second+1]; + ESM::RankData rankData = faction->mData.mRankData[rank+1]; const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index a3bc223aa..5a0cd8ae6 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -587,7 +587,9 @@ namespace MWScript } else { - player.getClass().getNpcStats(player).getFactionRanks()[factionID] = player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1; + player.getClass().getNpcStats(player).getFactionRanks()[factionID] = + std::min(player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1, + 9); } } } From d878456d0f1c77a6fe3c6831e3bc10e00cf32e8d Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 01:10:33 +0200 Subject: [PATCH 135/226] Don't add an extra path separator --- apps/openmw/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index b082ff908..a061cf63c 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -335,7 +335,7 @@ int main(int argc, char**argv) if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached()) { int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; - cc_install_handlers(argc, argv, 5, s, std::string(cfgMgr.getLogPath().string() + "/crash.log").c_str(), NULL); + cc_install_handlers(argc, argv, 5, s, (cfgMgr.getLogPath() / "crash.log").string().c_str(), NULL); std::cout << "Installing crash catcher" << std::endl; } else From 4234c70232f80e46bcb72000c976989fd7cfaae0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 02:00:40 +0200 Subject: [PATCH 136/226] Savegame: Disable CustomData load optimization for npcs and creatures for now to preserve compatibility (still enabled for containers) --- apps/openmw/mwclass/creature.cpp | 7 +++++++ apps/openmw/mwclass/npc.cpp | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 97cf30fe1..5dea2ed1a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -798,6 +798,12 @@ namespace MWClass { const ESM::CreatureState& state2 = dynamic_cast (state); + ensureCustomData(ptr); + + // If we do the following instead we get a sizable speedup, but this causes compatibility issues + // with 0.30 savegames, where some state in CreatureStats was not saved yet, + // and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release. + /* if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) @@ -812,6 +818,7 @@ namespace MWClass ptr.getRefData().setCustomData (data.release()); } + */ CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 58a5bc622..6b77f30e7 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1293,12 +1293,18 @@ namespace MWClass { const ESM::NpcState& state2 = dynamic_cast (state); + ensureCustomData(ptr); + // If we do the following instead we get a sizable speedup, but this causes compatibility issues + // with 0.30 savegames, where some state in CreatureStats was not saved yet, + // and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release. + /* if (!ptr.getRefData().getCustomData()) { // Create a CustomData, but don't fill it from ESM records (not needed) std::auto_ptr data (new NpcCustomData); ptr.getRefData().setCustomData (data.release()); } + */ NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); From 6760f4c897b083b71d412000ee534541c3e1be5a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 02:09:46 +0200 Subject: [PATCH 137/226] Make cached GMSTs in MWClass::Npc/Creature safer --- apps/openmw/mwclass/creature.cpp | 83 +++++++-------- apps/openmw/mwclass/creature.hpp | 31 +++--- apps/openmw/mwclass/npc.cpp | 175 ++++++++++++++----------------- apps/openmw/mwclass/npc.hpp | 47 +++++---- 4 files changed, 160 insertions(+), 176 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5dea2ed1a..4a159a773 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -59,35 +59,38 @@ namespace namespace MWClass { + const Creature::GMST& Creature::getGmst() + { + static GMST gmst; + static bool inited = false; + if (!inited) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &store = world->getStore().get(); + gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature"); + gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature"); + gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect"); + gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier"); + gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus"); + gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier"); + gmst.fMinFlySpeed = store.find("fMinFlySpeed"); + gmst.fMaxFlySpeed = store.find("fMaxFlySpeed"); + gmst.fSwimRunBase = store.find("fSwimRunBase"); + gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult"); + gmst.fKnockDownMult = store.find("fKnockDownMult"); + gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult"); + gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); + inited = true; + } + return gmst; + } + void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data (new CreatureCustomData); - static bool inited = false; - if(!inited) - { - const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); - - fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature"); - fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature"); - fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect"); - fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier"); - fAthleticsRunBonus = gmst.find("fAthleticsRunBonus"); - fBaseRunMultiplier = gmst.find("fBaseRunMultiplier"); - fMinFlySpeed = gmst.find("fMinFlySpeed"); - fMaxFlySpeed = gmst.find("fMaxFlySpeed"); - fSwimRunBase = gmst.find("fSwimRunBase"); - fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); - fKnockDownMult = gmst.find("fKnockDownMult"); - iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); - iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); - - inited = true; - } - MWWorld::LiveCellRef *ref = ptr.get(); // creature stats @@ -376,9 +379,9 @@ namespace MWClass if (damage > 0.f) { // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat(); float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() - * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + * getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt(); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) { @@ -522,9 +525,10 @@ namespace MWClass float Creature::getSpeed(const MWWorld::Ptr &ptr) const { MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + const GMST& gmst = getGmst(); - float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() - * (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat()); + float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() + * (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat()); const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); @@ -534,7 +538,7 @@ namespace MWClass bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * - fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); + gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); float moveSpeed; if(normalizedEncumbrance >= 1.0f) @@ -544,8 +548,8 @@ namespace MWClass { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); - flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); - flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } @@ -555,8 +559,8 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; - swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * - fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * + gmst.fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; } else if(running) @@ -871,19 +875,4 @@ namespace MWClass MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); } - - const ESM::GameSetting* Creature::fMinWalkSpeedCreature; - const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; - const ESM::GameSetting *Creature::fEncumberedMoveEffect; - const ESM::GameSetting *Creature::fSneakSpeedMultiplier; - const ESM::GameSetting *Creature::fAthleticsRunBonus; - const ESM::GameSetting *Creature::fBaseRunMultiplier; - const ESM::GameSetting *Creature::fMinFlySpeed; - const ESM::GameSetting *Creature::fMaxFlySpeed; - const ESM::GameSetting *Creature::fSwimRunBase; - const ESM::GameSetting *Creature::fSwimRunAthleticsMult; - const ESM::GameSetting *Creature::fKnockDownMult; - const ESM::GameSetting *Creature::iKnockDownOddsMult; - const ESM::GameSetting *Creature::iKnockDownOddsBase; - } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 30573cd15..6920a4b1d 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -19,20 +19,25 @@ namespace MWClass static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name); - static const ESM::GameSetting *fMinWalkSpeedCreature; - static const ESM::GameSetting *fMaxWalkSpeedCreature; - static const ESM::GameSetting *fEncumberedMoveEffect; - static const ESM::GameSetting *fSneakSpeedMultiplier; - static const ESM::GameSetting *fAthleticsRunBonus; - static const ESM::GameSetting *fBaseRunMultiplier; - static const ESM::GameSetting *fMinFlySpeed; - static const ESM::GameSetting *fMaxFlySpeed; - static const ESM::GameSetting *fSwimRunBase; - static const ESM::GameSetting *fSwimRunAthleticsMult; - static const ESM::GameSetting *fKnockDownMult; - static const ESM::GameSetting *iKnockDownOddsMult; - static const ESM::GameSetting *iKnockDownOddsBase; + // cached GMSTs + struct GMST + { + const ESM::GameSetting *fMinWalkSpeedCreature; + const ESM::GameSetting *fMaxWalkSpeedCreature; + const ESM::GameSetting *fEncumberedMoveEffect; + const ESM::GameSetting *fSneakSpeedMultiplier; + const ESM::GameSetting *fAthleticsRunBonus; + const ESM::GameSetting *fBaseRunMultiplier; + const ESM::GameSetting *fMinFlySpeed; + const ESM::GameSetting *fMaxFlySpeed; + const ESM::GameSetting *fSwimRunBase; + const ESM::GameSetting *fSwimRunAthleticsMult; + const ESM::GameSetting *fKnockDownMult; + const ESM::GameSetting *iKnockDownOddsMult; + const ESM::GameSetting *iKnockDownOddsBase; + }; + static const GMST& getGmst(); public: diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 6b77f30e7..c003e0b22 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -228,38 +228,44 @@ namespace namespace MWClass { - void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const + const Npc::GMST& Npc::getGmst() { + static GMST gmst; static bool inited = false; if(!inited) { const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); - fMinWalkSpeed = gmst.find("fMinWalkSpeed"); - fMaxWalkSpeed = gmst.find("fMaxWalkSpeed"); - fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect"); - fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier"); - fAthleticsRunBonus = gmst.find("fAthleticsRunBonus"); - fBaseRunMultiplier = gmst.find("fBaseRunMultiplier"); - fMinFlySpeed = gmst.find("fMinFlySpeed"); - fMaxFlySpeed = gmst.find("fMaxFlySpeed"); - fSwimRunBase = gmst.find("fSwimRunBase"); - fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); - fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase"); - fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier"); - fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase"); - fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); - fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); - fWereWolfRunMult = gmst.find("fWereWolfRunMult"); - fKnockDownMult = gmst.find("fKnockDownMult"); - iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); - iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); - fDamageStrengthBase = gmst.find("fDamageStrengthBase"); - fDamageStrengthMult = gmst.find("fDamageStrengthMult"); + gmst.fMinWalkSpeed = store.find("fMinWalkSpeed"); + gmst.fMaxWalkSpeed = store.find("fMaxWalkSpeed"); + gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect"); + gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier"); + gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus"); + gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier"); + gmst.fMinFlySpeed = store.find("fMinFlySpeed"); + gmst.fMaxFlySpeed = store.find("fMaxFlySpeed"); + gmst.fSwimRunBase = store.find("fSwimRunBase"); + gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult"); + gmst.fJumpEncumbranceBase = store.find("fJumpEncumbranceBase"); + gmst.fJumpEncumbranceMultiplier = store.find("fJumpEncumbranceMultiplier"); + gmst.fJumpAcrobaticsBase = store.find("fJumpAcrobaticsBase"); + gmst.fJumpAcroMultiplier = store.find("fJumpAcroMultiplier"); + gmst.fJumpRunMultiplier = store.find("fJumpRunMultiplier"); + gmst.fWereWolfRunMult = store.find("fWereWolfRunMult"); + gmst.fKnockDownMult = store.find("fKnockDownMult"); + gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult"); + gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase"); + gmst.fDamageStrengthBase = store.find("fDamageStrengthBase"); + gmst.fDamageStrengthMult = store.find("fDamageStrengthMult"); inited = true; } + return gmst; + } + + void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const + { if (!ptr.getRefData().getCustomData()) { std::auto_ptr data(new NpcCustomData); @@ -404,11 +410,6 @@ namespace MWClass ptr.get(); assert(ref->mBase != NULL); - //std::string headID = ref->mBase->mHead; - - //int end = headID.find_last_of("head_") - 4; - //std::string bodyRaceID = headID.substr(0, end); - std::string model = "meshes\\base_anim.nif"; const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); if(race->mData.mFlags & ESM::Race::Beast) @@ -423,9 +424,9 @@ namespace MWClass if(getNpcStats(ptr).isWerewolf()) { const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); - return gmst.find("sWerewolfPopup")->getString(); + return store.find("sWerewolfPopup")->getString(); } MWWorld::LiveCellRef *ref = ptr.get(); @@ -450,7 +451,9 @@ namespace MWClass void Npc::hit(const MWWorld::Ptr& ptr, int type) const { MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const GMST& gmst = getGmst(); + + const MWWorld::Store &store = world->getStore().get(); // Get the weapon used (if hand-to-hand, weapon = inv.end()) MWWorld::InventoryStore &inv = getInventoryStore(ptr); @@ -461,9 +464,9 @@ namespace MWClass // Reduce fatigue // somewhat of a guess, but using the weapon weight makes sense - const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); - const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); - const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); + const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat(); + const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat(); + const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat(); MWMechanics::DynamicStat fatigue = getCreatureStats(ptr).getFatigue(); const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; @@ -472,10 +475,10 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); getCreatureStats(ptr).setFatigue(fatigue); - const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); + const float fCombatDistance = store.find("fCombatDistance")->getFloat(); float dist = fCombatDistance * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : - gmst.find("fHandToHandReach")->getFloat()); + store.find("fHandToHandReach")->getFloat()); // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist); @@ -522,8 +525,8 @@ namespace MWClass if(attack) { damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); - damage *= fDamageStrengthBase->getFloat() + - (stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1); + damage *= gmst.fDamageStrengthBase->getFloat() + + (stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1); if(weaphashealth) { int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); @@ -535,7 +538,7 @@ namespace MWClass { // Reduce weapon charge by at least one, but cap at 0 weaphealth -= std::min(std::max(1, - (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth); + (int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth); weapon.getCellRef().setCharge(weaphealth); } @@ -552,8 +555,8 @@ namespace MWClass // Note: MCP contains an option to include Strength in hand-to-hand damage // calculations. Some mods recommend using it, so we may want to include am // option for it. - float minstrike = gmst.find("fMinHandToHandMult")->getFloat(); - float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat(); + float minstrike = store.find("fMinHandToHandMult")->getFloat(); + float maxstrike = store.find("fMaxHandToHandMult")->getFloat(); damage = stats.getSkill(weapskill).getModified(); damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); @@ -567,7 +570,7 @@ namespace MWClass damage *= glob.find("WerewolfClawMult")->mValue.getFloat(); } if(healthdmg) - damage *= gmst.find("fHandtoHandHealthPer")->getFloat(); + damage *= store.find("fHandtoHandHealthPer")->getFloat(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(stats.isWerewolf()) @@ -585,12 +588,12 @@ namespace MWClass bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); if(!detected) { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + 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 *= gmst.find("fCombatKODamageMult")->getFloat(); + damage *= store.find("fCombatKODamageMult")->getFloat(); // Apply "On hit" enchanted weapons std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; @@ -661,6 +664,8 @@ namespace MWClass // something, alert the character controller, scripts, etc. const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const GMST& gmst = getGmst(); + int chance = store.get().find("iVoiceHitOdds")->getInt(); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (roll < chance) @@ -669,9 +674,9 @@ namespace MWClass } // Check for knockdown - float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat(); float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() - * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + * gmst.iKnockDownOddsMult->getInt() * 0.01 + gmst.iKnockDownOddsBase->getInt(); roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) { @@ -870,6 +875,8 @@ namespace MWClass float Npc::getSpeed(const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); + const GMST& gmst = getGmst(); + const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); @@ -878,17 +885,17 @@ namespace MWClass bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); - float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* - (fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat()); - walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; + float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* + (gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat()); + walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed = std::max(0.0f, walkSpeed); if(sneaking) - walkSpeed *= fSneakSpeedMultiplier->getFloat(); + walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat(); float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * - fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); + gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); if(npcdata->mNpcStats.isWerewolf()) - runSpeed *= fWereWolfRunMult->getFloat(); + runSpeed *= gmst.fWereWolfRunMult->getFloat(); float moveSpeed; if(normalizedEncumbrance >= 1.0f) @@ -898,8 +905,8 @@ namespace MWClass { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); - flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); - flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat()); + flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } @@ -909,8 +916,8 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; - swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* - fSwimRunAthleticsMult->getFloat(); + swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* + gmst.fSwimRunAthleticsMult->getFloat(); moveSpeed = swimSpeed; } else if(running && !sneaking) @@ -926,9 +933,10 @@ namespace MWClass float Npc::getJump(const MWWorld::Ptr &ptr) const { const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const GMST& gmst = getGmst(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); - const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + - fJumpEncumbranceMultiplier->getFloat() * + const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() + + gmst.fJumpEncumbranceMultiplier->getFloat() * (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr)); float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified(); @@ -939,14 +947,14 @@ namespace MWClass a = 50.0f; } - float x = fJumpAcrobaticsBase->getFloat() + - std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); - x += 3.0f * b * fJumpAcroMultiplier->getFloat(); + float x = gmst.fJumpAcrobaticsBase->getFloat() + + std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat()); + x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat(); x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64; x *= encumbranceTerm; if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) - x *= fJumpRunMultiplier->getFloat(); + x *= gmst.fJumpRunMultiplier->getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); x -= -627.2f;/*gravity constant*/ x /= 3.0f; @@ -957,19 +965,19 @@ namespace MWClass float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const { MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); - const float fallDistanceMin = gmst.find("fFallDamageDistanceMin")->getFloat(); + const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat(); if (fallHeight >= fallDistanceMin) { const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude; - const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat(); - const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat(); - const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat(); - const float fallDistanceMult = gmst.find("fFallDistanceMult")->getFloat(); + const float fallAcroBase = store.find("fFallAcroBase")->getFloat(); + const float fallAcroMult = store.find("fFallAcroMult")->getFloat(); + const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat(); + const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat(); float x = fallHeight - fallDistanceMin; x -= (1.5 * acrobaticsSkill) + jumpSpellBonus; @@ -1104,14 +1112,14 @@ namespace MWClass float Npc::getArmorRating (const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); + const MWWorld::Store &store = world->getStore().get(); MWMechanics::NpcStats &stats = getNpcStats(ptr); MWWorld::InventoryStore &invStore = getInventoryStore(ptr); - int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); - float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); - float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); + int iBaseArmorSkill = store.find("iBaseArmorSkill")->getInt(); + float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat(); + float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); int ratings[MWWorld::InventoryStore::Slots]; @@ -1367,27 +1375,4 @@ namespace MWClass MWWorld::ContainerStore& store = getContainerStore(ptr); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); } - - const ESM::GameSetting *Npc::fMinWalkSpeed; - const ESM::GameSetting *Npc::fMaxWalkSpeed; - const ESM::GameSetting *Npc::fEncumberedMoveEffect; - const ESM::GameSetting *Npc::fSneakSpeedMultiplier; - const ESM::GameSetting *Npc::fAthleticsRunBonus; - const ESM::GameSetting *Npc::fBaseRunMultiplier; - const ESM::GameSetting *Npc::fMinFlySpeed; - const ESM::GameSetting *Npc::fMaxFlySpeed; - const ESM::GameSetting *Npc::fSwimRunBase; - const ESM::GameSetting *Npc::fSwimRunAthleticsMult; - const ESM::GameSetting *Npc::fJumpEncumbranceBase; - const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier; - const ESM::GameSetting *Npc::fJumpAcrobaticsBase; - const ESM::GameSetting *Npc::fJumpAcroMultiplier; - const ESM::GameSetting *Npc::fJumpRunMultiplier; - const ESM::GameSetting *Npc::fWereWolfRunMult; - const ESM::GameSetting *Npc::fKnockDownMult; - const ESM::GameSetting *Npc::iKnockDownOddsMult; - const ESM::GameSetting *Npc::iKnockDownOddsBase; - const ESM::GameSetting *Npc::fDamageStrengthBase; - const ESM::GameSetting *Npc::fDamageStrengthMult; - } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 356e358b9..d6b1f8c26 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -17,27 +17,32 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; - static const ESM::GameSetting *fMinWalkSpeed; - static const ESM::GameSetting *fMaxWalkSpeed; - static const ESM::GameSetting *fEncumberedMoveEffect; - static const ESM::GameSetting *fSneakSpeedMultiplier; - static const ESM::GameSetting *fAthleticsRunBonus; - static const ESM::GameSetting *fBaseRunMultiplier; - static const ESM::GameSetting *fMinFlySpeed; - static const ESM::GameSetting *fMaxFlySpeed; - static const ESM::GameSetting *fSwimRunBase; - static const ESM::GameSetting *fSwimRunAthleticsMult; - static const ESM::GameSetting *fJumpEncumbranceBase; - static const ESM::GameSetting *fJumpEncumbranceMultiplier; - static const ESM::GameSetting *fJumpAcrobaticsBase; - static const ESM::GameSetting *fJumpAcroMultiplier; - static const ESM::GameSetting *fJumpRunMultiplier; - static const ESM::GameSetting *fWereWolfRunMult; - static const ESM::GameSetting *fKnockDownMult; - static const ESM::GameSetting *iKnockDownOddsMult; - static const ESM::GameSetting *iKnockDownOddsBase; - static const ESM::GameSetting *fDamageStrengthBase; - static const ESM::GameSetting *fDamageStrengthMult; + struct GMST + { + const ESM::GameSetting *fMinWalkSpeed; + const ESM::GameSetting *fMaxWalkSpeed; + const ESM::GameSetting *fEncumberedMoveEffect; + const ESM::GameSetting *fSneakSpeedMultiplier; + const ESM::GameSetting *fAthleticsRunBonus; + const ESM::GameSetting *fBaseRunMultiplier; + const ESM::GameSetting *fMinFlySpeed; + const ESM::GameSetting *fMaxFlySpeed; + const ESM::GameSetting *fSwimRunBase; + const ESM::GameSetting *fSwimRunAthleticsMult; + const ESM::GameSetting *fJumpEncumbranceBase; + const ESM::GameSetting *fJumpEncumbranceMultiplier; + const ESM::GameSetting *fJumpAcrobaticsBase; + const ESM::GameSetting *fJumpAcroMultiplier; + const ESM::GameSetting *fJumpRunMultiplier; + const ESM::GameSetting *fWereWolfRunMult; + const ESM::GameSetting *fKnockDownMult; + const ESM::GameSetting *iKnockDownOddsMult; + const ESM::GameSetting *iKnockDownOddsBase; + const ESM::GameSetting *fDamageStrengthBase; + const ESM::GameSetting *fDamageStrengthMult; + }; + + static const GMST& getGmst(); public: From cc3c6ae7b854601d74cbebf9c26e1178e230f62e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 02:33:44 +0200 Subject: [PATCH 138/226] Fix very slow movement on some creatures, e.g. rats (Bug #1136) Neither fAthleticsRunBonus, fBaseRunMultiplier or the creature's athletics skill (i.e. Combat stat) have any effect on the run speed (tested by setting those to absurd values). The new formula is just a guess and doesn't seem to be completely accurate. --- apps/openmw/mwclass/creature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4a159a773..1f286f7e6 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -537,8 +537,8 @@ namespace MWClass bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); - float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * - gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat()); + float runSpeed = walkSpeed*6; + runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this float moveSpeed; if(normalizedEncumbrance >= 1.0f) From 185ff279a3a19aa0b3a66fcc1cc5389d8adc62dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 03:29:36 +0200 Subject: [PATCH 139/226] Add missing sound effect for mages guild transport --- apps/openmw/mwgui/travelwindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index c16603554..9aa75173a 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -10,6 +10,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -140,6 +141,9 @@ namespace MWGui if (playerGoldisExterior()) + // Interior cell -> mages guild transport + MWBase::Environment::get().getSoundManager()->playSound("mysticism cast", 1, 1); player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); From 4648524df43d4c8bed8195791b2323a05384412c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 04:53:25 +0200 Subject: [PATCH 140/226] Improve getLOS (use eye level). Also, don't crash when used with non-actors. --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwscript/aiextensions.cpp | 9 ++++++++- apps/openmw/mwworld/worldimp.cpp | 20 ++++++++++++-------- apps/openmw/mwworld/worldimp.hpp | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 29f821e85..f42edafd4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -407,7 +407,7 @@ namespace MWBase virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out) = 0; ///< get all items in active cells owned by this Npc - virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0; + virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) = 0; ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0; diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 823d22a6a..9a3387a00 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -371,6 +371,12 @@ namespace MWScript MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); + if(!actor.getClass().isActor() || !observer.getClass().isActor()) + { + runtime.push(0); + return; + } + Interpreter::Type_Integer value = MWBase::Environment::get().getWorld()->getLOS(observer, actor) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer); @@ -392,9 +398,10 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + MWWorld::Ptr dest = MWBase::Environment::get().getWorld()->getPtr(actorID,true); bool value = false; - if(dest != MWWorld::Ptr() ) + if(dest != MWWorld::Ptr() && source.getClass().isActor() && dest.getClass().isActor()) { value = MWBase::Environment::get().getWorld()->getLOS(source,dest); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6e975d25a..bcae8c070 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2031,20 +2031,24 @@ namespace MWWorld } } - bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) + bool World::getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) { - if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled()) + if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled()) return false; // cannot get LOS unless both NPC's are enabled - Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); - const float* pos1 = npc.getRefData().getPosition().pos; - Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); - const float* pos2 = targetNpc.getRefData().getPosition().pos; + if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode()) + return false; // not in active cell - btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z); - btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z); + Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents(); + const float* pos1 = actor.getRefData().getPosition().pos; + Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle())->getHalfExtents(); + const float* pos2 = targetActor.getRefData().getPosition().pos; + + btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z*2*0.9); // eye level + btVector3 to(pos2[0],pos2[1],pos2[2]+halfExt2.z*2*0.9); std::pair result = mPhysEngine->rayTest(from, to,false); if(result.first == "") return true; + return false; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index db599b0de..8cb2ac18a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -482,7 +482,7 @@ namespace MWWorld virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector& out); ///< get all items in active cells owned by this Npc - virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc); + virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor); ///< get Line of Sight (morrowind stupid implementation) virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist); From c69a311ad83a9c4046a6131118738911d6e620f3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 04:57:46 +0200 Subject: [PATCH 141/226] Disable lighting for particles Fixes magic cast visuals being too dark depending on the environment. --- components/nifogre/material.cpp | 10 +++++++++- components/nifogre/material.hpp | 2 +- components/nifogre/ogrenifloader.cpp | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index 3a87e1d52..b1ae83107 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -106,7 +106,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents) + bool &needTangents, bool disableLighting) { Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialPtr material = matMgr.getByName(name); @@ -245,6 +245,14 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, } } + if (disableLighting) + { + ambient = Ogre::Vector3(0.f); + diffuse = Ogre::Vector3(0.f); + specular = Ogre::Vector3(0.f); + emissive = Ogre::Vector3(1.f); + } + { // Generate a hash out of all properties that can affect the material. size_t h = 0; diff --git a/components/nifogre/material.hpp b/components/nifogre/material.hpp index b02c7c236..890255dcc 100644 --- a/components/nifogre/material.hpp +++ b/components/nifogre/material.hpp @@ -49,7 +49,7 @@ public: const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents); + bool &needTangents, bool disableLighting=false); }; } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 36d750821..813c1660d 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -850,7 +850,10 @@ class NIFObjectLoader partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, texprop, matprop, alphaprop, vertprop, zprop, specprop, - wireprop, needTangents)); + wireprop, needTangents, + // MW doesn't light particles, but the MaterialProperty + // used still has lighting, so that must be ignored. + true)); partsys->setCullIndividually(false); partsys->setParticleQuota(particledata->numParticles); From 11b05c352fc038dabd41eb8e51d0ebbb411dcff3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 16:00:27 +0200 Subject: [PATCH 142/226] Fix exception when casting Bound Gloves spell --- apps/openmw/mwmechanics/actors.cpp | 8 ++++++-- apps/openmw/mwworld/projectilemanager.cpp | 4 ++-- apps/openmw/mwworld/projectilemanager.hpp | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d72ab7db8..6ea65275b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -530,8 +530,12 @@ namespace MWMechanics itemGmst)->getString(); if (it->first == ESM::MagicEffect::BoundGloves) { - adjustBoundItem("sMagicBoundLeftGauntletID", magnitude > 0, ptr); - adjustBoundItem("sMagicBoundRightGauntletID", magnitude > 0, ptr); + item = MWBase::Environment::get().getWorld()->getStore().get().find( + "sMagicBoundLeftGauntletID")->getString(); + adjustBoundItem(item, magnitude > 0, ptr); + item = MWBase::Environment::get().getWorld()->getStore().get().find( + "sMagicBoundRightGauntletID")->getString(); + adjustBoundItem(item, magnitude > 0, ptr); } else adjustBoundItem(item, magnitude > 0, ptr); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index cfb407b4a..3122cb325 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -78,7 +78,7 @@ namespace MWWorld state.mSourceName = sourceName; state.mId = model; state.mSpellId = spellId; - state.mCaster = caster; + state.mCasterHandle = caster.getRefData().getHandle(); if (caster.getClass().isActor()) state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); else @@ -162,7 +162,7 @@ namespace MWWorld { MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); - MWWorld::Ptr caster = it->mCaster; + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaHandle(it->mCasterHandle); if (caster.isEmpty()) caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index 4627d3b8a..6e84b9efb 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -67,12 +67,12 @@ namespace MWWorld int mActorId; - // actorId doesn't work for non-actors, so we also keep track of the Ptr. + // actorId doesn't work for non-actors, so we also keep track of the Ogre-handle. // For non-actors, the caster ptr is mainly needed to prevent the projectile // from colliding with its caster. // TODO: this will break when the game is saved and reloaded, since there is currently // no way to write identifiers for non-actors to a savegame. - MWWorld::Ptr mCaster; + std::string mCasterHandle; // MW-id of this projectile std::string mId; From 218f916d6da5c63203fa7001cfbac3cc37d34b2a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 17:17:26 +0200 Subject: [PATCH 143/226] Savegame: Don't write stat modifiers that are zero --- components/esm/statstate.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp index 4b4023bc2..f1a3b4d79 100644 --- a/components/esm/statstate.hpp +++ b/components/esm/statstate.hpp @@ -12,7 +12,8 @@ namespace ESM struct StatState { T mBase; - T mMod; + T mMod; // Note: can either be the modifier, or the modified value. + // A bit inconsistent, but we can't fix this without breaking compatibility. T mCurrent; T mDamage; float mProgress; @@ -30,7 +31,9 @@ namespace ESM void StatState::load (ESMReader &esm) { esm.getHNT (mBase, "STBA"); - esm.getHNT (mMod, "STMO"); + + mMod = 0; + esm.getHNOT (mMod, "STMO"); mCurrent = 0; esm.getHNOT (mCurrent, "STCU"); mDamage = 0; @@ -43,7 +46,9 @@ namespace ESM void StatState::save (ESMWriter &esm) const { esm.writeHNT ("STBA", mBase); - esm.writeHNT ("STMO", mMod); + + if (mMod != 0) + esm.writeHNT ("STMO", mMod); if (mCurrent) esm.writeHNT ("STCU", mCurrent); @@ -56,4 +61,4 @@ namespace ESM } } -#endif \ No newline at end of file +#endif From 8eab3abb15b30770a8f08c25165db53a1f8a5d47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 19:02:42 +0200 Subject: [PATCH 144/226] Fix initial view offset for large world maps (Fixes #1523) --- apps/openmw/mwgui/mapwindow.cpp | 7 +++---- files/mygui/openmw_map_window.layout | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index db3cfadde..af26456f2 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -431,6 +431,9 @@ namespace MWGui { mGlobalMapRender = new MWRender::GlobalMap(""); mGlobalMapRender->render(loadingListener); + mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setImageTexture("GlobalMap.png"); mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); } @@ -512,7 +515,6 @@ namespace MWGui else mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); - mLastDragPos = MyGUI::IntPoint(_left, _top); } @@ -536,9 +538,6 @@ namespace MWGui void MapWindow::open() { - mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - // force markers to foreground for (unsigned int i=0; igetChildCount (); ++i) { diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 6e0efce7e..b842888a1 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -17,8 +17,6 @@ - - From 091f9a8fdceb59be6cd37aa716aef04d0460ab0c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 19:14:59 +0200 Subject: [PATCH 145/226] Optimize global map render slightly --- apps/openmw/mwrender/globalmap.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 0537ed516..1ccfd9527 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -62,6 +62,12 @@ namespace MWRender loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgress(0); + const Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19); + const Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13); + const Ogre::ColourValue groundColour(0.254, 0.19, 0.13); + const Ogre::ColourValue mountainColour(0.05, 0.05, 0.05); + const Ogre::ColourValue hillColour(0.16, 0.12, 0.08); + //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) if (1) { @@ -91,12 +97,6 @@ namespace MWRender int texelX = (x-mMinX) * cellSize + cellX; int texelY = (mHeight-1) - ((y-mMinY) * cellSize + cellY); - Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19); - Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13); - Ogre::ColourValue groundColour(0.254, 0.19, 0.13); - Ogre::ColourValue mountainColour(0.05, 0.05, 0.05); - Ogre::ColourValue hillColour(0.16, 0.12, 0.08); - unsigned char r,g,b; if (land) From ad0a182b7e57b90a953d70a4541d2e63b3f249e2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 20:08:53 +0200 Subject: [PATCH 146/226] Improve error message for unknown cells --- apps/openmw/mwscript/transformationextensions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 138326ff0..3860257ad 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -323,7 +323,7 @@ namespace MWScript } else { - throw std::runtime_error ("unknown cell"); + throw std::runtime_error (std::string("unknown cell (") + cellID + ")"); } } }; @@ -420,7 +420,7 @@ namespace MWScript } else { - throw std::runtime_error ("unknown cell"); + throw std::runtime_error ( std::string("unknown cell (") + cellID + ")"); } } }; From 9a26cf22e6a904ef1875d3cc4d23f78ac263b821 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jun 2014 18:49:19 +0200 Subject: [PATCH 147/226] Implement toggleMenus --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 17 ++++++++++++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++++ apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/guiextensions.cpp | 10 ++++++++++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 1 + 8 files changed, 37 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 0f45542ba..8b407c9ba 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -228,6 +228,7 @@ namespace MWBase virtual void showCrosshair(bool show) = 0; virtual bool getSubtitlesEnabled() = 0; virtual void toggleHud() = 0; + virtual bool toggleGui() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3f239e0e3..865ad1dca 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -118,6 +118,7 @@ namespace MWGui , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHudEnabled(true) + , mGuiEnabled(true) , mCursorVisible(true) , mPlayerName() , mPlayerRaceId() @@ -420,7 +421,7 @@ namespace MWGui mRecharge->setVisible(false); mVideoBackground->setVisible(false); - mHud->setVisible(mHudEnabled); + mHud->setVisible(mHudEnabled && mGuiEnabled); bool gameMode = !isGuiMode(); @@ -430,6 +431,13 @@ namespace MWGui if (gameMode) setKeyFocusWidget (NULL); + if (!mGuiEnabled) + { + if (containsMode(GM_Console)) + mConsole->setVisible(true); + return; + } + // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory))); @@ -1345,6 +1353,13 @@ namespace MWGui mHud->setVisible (mHudEnabled); } + bool WindowManager::toggleGui() + { + mGuiEnabled = !mGuiEnabled; + updateVisible(); + return mGuiEnabled; + } + bool WindowManager::getRestEnabled() { //Enable rest dialogue if character creation finished diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index dd41635ad..8093d637e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -223,6 +223,9 @@ namespace MWGui virtual bool getSubtitlesEnabled(); virtual void toggleHud(); + /// Turn visibility of *all* GUI elements on or off (HUD and all windows, except the console) + virtual bool toggleGui(); + virtual void disallowMouse(); virtual void allowMouse(); virtual void notifyInputActionBound(); @@ -381,6 +384,7 @@ namespace MWGui bool mCrosshairEnabled; bool mSubtitlesEnabled; bool mHudEnabled; + bool mGuiEnabled; bool mCursorVisible; void setCursorVisible(bool visible); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index cd3be4b42..afdde6fb0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -277,7 +277,7 @@ namespace MWInput showQuickKeysMenu(); break; case A_ToggleHUD: - MWBase::Environment::get().getWindowManager()->toggleHud(); + MWBase::Environment::get().getWindowManager()->toggleGui(); break; case A_QuickSave: quickSave(); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index c125985c0..4b2a91a95 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -400,5 +400,6 @@ op 0x2000247: BetaComment op 0x2000248: BetaComment, explicit op 0x2000249: OnMurder op 0x200024a: OnMurder, explicit +op 0x200024b: ToggleMenus -opcodes 0x200024b-0x3ffffff unused +opcodes 0x200024c-0x3ffffff unused diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 333be5be6..afc745beb 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -203,6 +203,15 @@ namespace MWScript } }; + class OpToggleMenus : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + bool state = MWBase::Environment::get().getWindowManager()->toggleGui(); + runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off"); + } + }; void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -242,6 +251,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap); interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap); interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest); + interpreter.installSegment5 (Compiler::Gui::opcodeToggleMenus, new OpToggleMenus); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index efd45f912..9fc66900f 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -216,6 +216,8 @@ namespace Compiler extensions.registerInstruction ("showmap", "S", opcodeShowMap); extensions.registerInstruction ("fillmap", "", opcodeFillMap); extensions.registerInstruction ("menutest", "/l", opcodeMenuTest); + extensions.registerInstruction ("togglemenus", "", opcodeToggleMenus); + extensions.registerInstruction ("tm", "", opcodeToggleMenus); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index ed6f1df92..b097a017b 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -178,6 +178,7 @@ namespace Compiler const int opcodeShowMap = 0x20001a0; const int opcodeFillMap = 0x20001a1; const int opcodeMenuTest = 0x2002c; + const int opcodeToggleMenus = 0x200024b; } namespace Misc From 7bf7daa846606774e57fd5320e5af764a9ba35af Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jun 2014 21:33:03 +0200 Subject: [PATCH 148/226] Fix exception (getline will trigger failbit on the last line) --- components/translation/translation.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 976ae926d..5341240af 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -32,9 +32,6 @@ namespace Translation boost::filesystem::ifstream stream ( dataFileCollections.getCollection (extension).getPath (fileName)); - // Configure the stream to throw exception upon error - stream.exceptions ( boost::filesystem::ifstream::failbit | boost::filesystem::ifstream::badbit ); - if (!stream.is_open()) throw std::runtime_error ("failed to open translation file: " + fileName); @@ -44,9 +41,8 @@ namespace Translation void Storage::loadDataFromStream(ContainerType& container, std::istream& stream) { - // NOTE: does not handle failbit/badbit. stream must be set up beforehand to throw in these cases. std::string line; - while (!stream.eof()) + while (!stream.eof() && !stream.fail()) { std::getline( stream, line ); if (!line.empty() && *line.rbegin() == '\r') From a4ce9d6a7fb472549b0a136d431bf312ec34743a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jun 2014 23:56:21 +0200 Subject: [PATCH 149/226] Always show the script name when a script fails to compile --- apps/openmw/mwscript/scriptmanagerimp.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 74c85dbbf..7b858dacf 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -64,12 +64,12 @@ namespace MWScript Success = false; } - if (!Success && mVerbose) + if (!Success) { std::cerr - << "compiling failed: " << name << std::endl - << script->mScriptText - << std::endl << std::endl; + << "compiling failed: " << name << std::endl; + if (mVerbose) + std::cerr << script->mScriptText << std::endl << std::endl; } if (Success) From 98329a94b4b73b6ded6cd4ee2dd04cbf2a920762 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 21 Jun 2014 19:54:12 +0200 Subject: [PATCH 150/226] Add case sensitivity workaround for spine bones (Fixes #1547) --- apps/openmw/mwrender/weaponanimation.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 5f953bd83..cd9ae16b8 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -150,9 +150,19 @@ void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel) return; float pitch = xrot * mPitchFactor; - Ogre::Node *node = skel->getBone("Bip01 Spine2"); + Ogre::Node *node; + + // In spherearcher.nif, we have spine, not Spine. Not sure if all bone names should be case insensitive? + if (skel->hasBone("Bip01 spine2")) + node = skel->getBone("Bip01 spine2"); + else + node = skel->getBone("Bip01 Spine2"); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); - node = skel->getBone("Bip01 Spine1"); + + if (skel->hasBone("Bip01 spine1")) // in spherearcher.nif + node = skel->getBone("Bip01 spine1"); + else + node = skel->getBone("Bip01 Spine1"); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); } From e002acdeaeb3c2559f36b7c9f0ee4ac6abeb7748 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 21 Jun 2014 23:32:58 +0200 Subject: [PATCH 151/226] Physics: Create actor shapes outside of BulletNifLoader This will allow to create a specialised shape instead, such as a capsule, which tends to work better for character controllers. --- apps/openmw/mwworld/physicssystem.cpp | 10 +- apps/openmw/mwworld/worldimp.cpp | 2 +- components/nifbullet/bulletnifloader.cpp | 49 ++++++ components/nifbullet/bulletnifloader.hpp | 5 + libs/openengine/bullet/physic.cpp | 194 +++++++++++------------ libs/openengine/bullet/physic.hpp | 52 +++--- libs/openengine/bullet/trace.cpp | 18 +-- libs/openengine/bullet/trace.h | 3 +- 8 files changed, 183 insertions(+), 150 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 73a704c88..8aca591e8 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -175,7 +175,7 @@ namespace MWWorld const int maxHeight = 200.f; OEngine::Physic::ActorTracer tracer; - tracer.findGround(physicActor->getCollisionBody(), position, position-Ogre::Vector3(0,0,maxHeight), engine); + tracer.findGround(physicActor, position, position-Ogre::Vector3(0,0,maxHeight), engine); if(tracer.mFraction >= 1.0f) { physicActor->setOnGround(false); @@ -607,9 +607,10 @@ namespace MWWorld Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); const std::string &handle = node->getName(); const Ogre::Quaternion &rotation = node->getOrientation(); + + // TODO: map to MWWorld::Ptr for faster access if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { - //Needs to be changed act->setRotation(rotation); } if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) @@ -740,8 +741,9 @@ namespace MWWorld btCollisionObject object; object.setCollisionShape(&planeShape); + // TODO: this seems to have a slight performance impact if (waterCollision) - mEngine->dynamicsWorld->addCollisionObject(&object); + mEngine->mDynamicsWorld->addCollisionObject(&object); // 100 points of slowfall reduce gravity by 90% (this is just a guess) float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f); @@ -751,7 +753,7 @@ namespace MWWorld waterlevel, slowFall, mEngine); if (waterCollision) - mEngine->dynamicsWorld->removeCollisionObject(&object); + mEngine->mDynamicsWorld->removeCollisionObject(&object); float heightDiff = newpos.z - oldHeight; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bcae8c070..be334646b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1847,7 +1847,7 @@ namespace MWWorld Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); OEngine::Physic::ActorTracer tracer; // a small distance above collision object is considered "on ground" - tracer.findGround(physactor->getCollisionBody(), + tracer.findGround(physactor, pos, pos - Ogre::Vector3(0, 0, 1.5f), // trace a small amount down mPhysEngine); diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index cdddb94d0..3e753dd37 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -304,4 +304,53 @@ void ManualBulletShapeLoader::load(const std::string &name,const std::string &gr OEngine::Physic::BulletShapeManager::getSingleton().create(name,group,true,this); } +bool findBoundingBox (const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation) +{ + if(node->hasBounds) + { + if (!(node->flags & Nif::NiNode::Flag_Hidden)) + { + translation = node->boundPos; + orientation = node->boundRot; + halfExtents = node->boundXYZ; + return true; + } + } + + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + const Nif::NodeList &list = ninode->children; + for(size_t i = 0;i < list.length();i++) + { + if(!list[i].empty()) + if (findBoundingBox(list[i].getPtr(), halfExtents, translation, orientation)) + return true; + } + } + return false; +} + +bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation) +{ + Nif::NIFFile::ptr pnif (Nif::NIFFile::create (nifFile)); + Nif::NIFFile & nif = *pnif.get (); + + if (nif.numRoots() < 1) + { + return false; + } + + Nif::Record *r = nif.getRoot(0); + assert(r != NULL); + + Nif::Node *node = dynamic_cast(r); + if (node == NULL) + { + return false; + } + + return findBoundingBox(node, halfExtents, translation, orientation); +} + } // namespace NifBullet diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index d1e876305..fb7d3d70a 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -108,6 +108,11 @@ private: bool mHasShape; }; + +bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation); + +bool findBoundingBox(const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation); + } #endif diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 8d9ac3c22..bc34f2f51 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -16,115 +16,93 @@ namespace Physic { PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) - : mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0) - , mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0) - , mCollisionBody(true) + : mName(name), mEngine(engine), mMesh(mesh) + , mBody(0), mOnGround(false), mInternalCollisionMode(true) + , mExternalCollisionMode(true) , mForce(0.0f) + , mScale(scale) { - mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); - mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); - Ogre::Quaternion inverse = mBoxRotation.Inverse(); - mBoxRotationInverse = Ogre::Quaternion(inverse.w, inverse.x, inverse.y,inverse.z); - mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map + if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation)) + { + mHalfExtents = Ogre::Vector3(0.f); + mMeshTranslation = Ogre::Vector3(0.f); + mMeshOrientation = Ogre::Quaternion::IDENTITY; + } + + mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); + mShape->setLocalScaling(btVector3(scale,scale,scale)); + + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo + (0,0, mShape.get()); + mBody = new RigidBody(CI, name); + mBody->mPlaceable = false; + + setPosition(position); + setRotation(rotation); + + mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, + CollisionType_Actor|CollisionType_World|CollisionType_HeightMap); } PhysicActor::~PhysicActor() { if(mBody) { - mEngine->dynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->removeRigidBody(mBody); delete mBody; - } - if(mRaycastingBody) - { - mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody); - delete mRaycastingBody; - } + } } void PhysicActor::enableCollisionMode(bool collision) { - mCollisionMode = collision; + mInternalCollisionMode = collision; } void PhysicActor::enableCollisionBody(bool collision) { assert(mBody); - if(collision && !mCollisionBody) enableCollisionBody(); - if(!collision && mCollisionBody) disableCollisionBody(); - mCollisionBody = collision; + if(collision && !mExternalCollisionMode) enableCollisionBody(); + if(!collision && mExternalCollisionMode) disableCollisionBody(); + mExternalCollisionMode = collision; } - void PhysicActor::setPosition(const Ogre::Vector3 &pos) + const Ogre::Vector3& PhysicActor::getPosition() const + { + return mPosition; + } + + void PhysicActor::setPosition(const Ogre::Vector3 &position) { assert(mBody); - if(pos != getPosition()) - { - mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); - mEngine->adjustRigidBody(mRaycastingBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); - } + + mPosition = position; + + btTransform tr = mBody->getWorldTransform(); + Ogre::Quaternion meshrot = mMeshOrientation; + Ogre::Vector3 transrot = meshrot * (mMeshTranslation * mScale); + Ogre::Vector3 newPosition = transrot + position; + + tr.setOrigin(BtOgre::Convert::toBullet(newPosition)); + mBody->setWorldTransform(tr); } - void PhysicActor::setRotation(const Ogre::Quaternion &quat) + void PhysicActor::setRotation (const Ogre::Quaternion& rotation) { - assert(mBody); - if(!quat.equals(getRotation(), Ogre::Radian(0))){ - mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); - mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); - - } + btTransform tr = mBody->getWorldTransform(); + tr.setRotation(BtOgre::Convert::toBullet(mMeshOrientation * rotation)); + mBody->setWorldTransform(tr); } - - Ogre::Vector3 PhysicActor::getPosition() + void PhysicActor::setScale(float scale) { - assert(mBody); - btVector3 vec = mBody->getWorldTransform().getOrigin(); - Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(), - mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ()); - Ogre::Vector3 transrot = rotation * mBoxScaledTranslation; - Ogre::Vector3 visualPosition = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()) - transrot; - return visualPosition; - } - - Ogre::Quaternion PhysicActor::getRotation() - { - assert(mBody); - btQuaternion quat = mBody->getWorldTransform().getRotation(); - return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()) * mBoxRotationInverse; - } - - void PhysicActor::setScale(float scale){ - //We only need to change the scaled box translation, box rotations remain the same. - assert(mBody); - mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX(); - mBoxScaledTranslation *= scale; - Ogre::Vector3 pos = getPosition(); - Ogre::Quaternion rot = getRotation(); - if(mBody){ - mEngine->dynamicsWorld->removeRigidBody(mBody); - mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody); - delete mBody; - delete mRaycastingBody; - } - //Create the newly scaled rigid body - mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot); - mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true); - mEngine->addRigidBody(mCollisionBody ? mBody : 0, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map + mScale = scale; + mShape->setLocalScaling(btVector3(scale,scale,scale)); + setPosition(mPosition); } Ogre::Vector3 PhysicActor::getHalfExtents() const { - if(mBody) - { - btBoxShape *box = static_cast(mBody->getCollisionShape()); - if(box != NULL) - { - btVector3 size = box->getHalfExtentsWithMargin(); - return Ogre::Vector3(size.getX(), size.getY(), size.getZ()); - } - } - return Ogre::Vector3(0.0f); + return mHalfExtents; } void PhysicActor::setInertialForce(const Ogre::Vector3 &force) @@ -139,12 +117,16 @@ namespace Physic void PhysicActor::disableCollisionBody() { - mEngine->dynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, + CollisionType_Raycasting); } void PhysicActor::enableCollisionBody() { - mEngine->dynamicsWorld->addRigidBody(mBody,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); + mEngine->mDynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, + CollisionType_Actor|CollisionType_World|CollisionType_HeightMap); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -189,8 +171,8 @@ namespace Physic broadphase = new btDbvtBroadphase(); // The world. - dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); - dynamicsWorld->setGravity(btVector3(0,0,-10)); + mDynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); + mDynamicsWorld->setGravity(btVector3(0,0,-10)); if(BulletShapeManager::getSingletonPtr() == NULL) { @@ -208,10 +190,10 @@ namespace Physic if(!isDebugCreated) { Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); - dynamicsWorld->setDebugDrawer(mDebugDrawer); + mDebugDrawer = new BtOgre::DebugDrawer(node, mDynamicsWorld); + mDynamicsWorld->setDebugDrawer(mDebugDrawer); isDebugCreated = true; - dynamicsWorld->debugDrawWorld(); + mDynamicsWorld->debugDrawWorld(); } } @@ -241,7 +223,7 @@ namespace Physic HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); for (; hf_it != mHeightFieldMap.end(); ++hf_it) { - dynamicsWorld->removeRigidBody(hf_it->second.mBody); + mDynamicsWorld->removeRigidBody(hf_it->second.mBody); delete hf_it->second.mShape; delete hf_it->second.mBody; } @@ -251,7 +233,7 @@ namespace Physic { if (rb_it->second != NULL) { - dynamicsWorld->removeRigidBody(rb_it->second); + mDynamicsWorld->removeRigidBody(rb_it->second); delete rb_it->second; rb_it->second = NULL; @@ -262,7 +244,7 @@ namespace Physic { if (rb_it->second != NULL) { - dynamicsWorld->removeRigidBody(rb_it->second); + mDynamicsWorld->removeRigidBody(rb_it->second); delete rb_it->second; rb_it->second = NULL; @@ -281,7 +263,7 @@ namespace Physic delete mDebugDrawer; - delete dynamicsWorld; + delete mDynamicsWorld; delete solver; delete collisionConfiguration; delete dispatcher; @@ -331,7 +313,7 @@ namespace Physic mHeightFieldMap [name] = hf; - dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting, + mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap, CollisionType_World|CollisionType_Actor|CollisionType_Raycasting); } @@ -343,7 +325,7 @@ namespace Physic HeightField hf = mHeightFieldMap [name]; - dynamicsWorld->removeRigidBody(hf.mBody); + mDynamicsWorld->removeRigidBody(hf.mBody); delete hf.mShape; delete hf.mBody; @@ -367,7 +349,6 @@ namespace Physic { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; - //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif mShapeLoader->load(outputstring,"General"); @@ -419,7 +400,7 @@ namespace Physic } - void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor) + void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody) { if(!body && !raycastingBody) return; // nothing to do @@ -427,12 +408,11 @@ namespace Physic const std::string& name = (body ? body->mName : raycastingBody->mName); if (body){ - if(actor) dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); - else dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); + mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); } if (raycastingBody) - dynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World); + mDynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting); if(addToMap){ removeRigidBody(name); @@ -453,7 +433,7 @@ namespace Physic RigidBody* body = it->second; if(body != NULL) { - dynamicsWorld->removeRigidBody(body); + mDynamicsWorld->removeRigidBody(body); } } it = mRaycastingObjectMap.find(name); @@ -462,7 +442,7 @@ namespace Physic RigidBody* body = it->second; if(body != NULL) { - dynamicsWorld->removeRigidBody(body); + mDynamicsWorld->removeRigidBody(body); } } } @@ -605,7 +585,7 @@ namespace Physic if (!body) // fall back to raycasting body if there is no collision body body = getRigidBody(name, true); ContactTestResultCallback callback; - dynamicsWorld->contactTest(body, callback); + mDynamicsWorld->contactTest(body, callback); return callback.mResult; } @@ -615,8 +595,9 @@ namespace Physic btCollisionObject *object) { DeepestNotMeContactTestResultCallback callback(filter, origin); + callback.m_collisionFilterGroup = 0xff; callback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; - dynamicsWorld->contactTest(object, callback); + mDynamicsWorld->contactTest(object, callback); return std::make_pair(callback.mObject, callback.mContactPoint); } @@ -624,7 +605,7 @@ namespace Physic void PhysicEngine::stepSimulation(double deltaT) { // This seems to be needed for character controller objects - dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); + mDynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { mDebugDrawer->step(); @@ -684,14 +665,15 @@ namespace Physic float d = -1; btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); + resultCallback1.m_collisionFilterGroup = 0xff; if(raycastingObjectOnly) - resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor; else resultCallback1.m_collisionFilterMask = CollisionType_World; if(!ignoreHeightMap) resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap; - dynamicsWorld->rayTest(from, to, resultCallback1); + mDynamicsWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit()) { name = static_cast(*resultCallback1.m_collisionObject).mName; @@ -724,6 +706,7 @@ namespace Physic std::pair PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to) { OurClosestConvexResultCallback callback(from, to); + callback.m_collisionFilterGroup = 0xff; callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap; btSphereShape shape(radius); @@ -732,7 +715,7 @@ namespace Physic btTransform from_ (btrot, from); btTransform to_ (btrot, to); - dynamicsWorld->convexSweepTest(&shape, from_, to_, callback); + mDynamicsWorld->convexSweepTest(&shape, from_, to_, callback); if (callback.hasHit()) return std::make_pair(true, callback.m_closestHitFraction); @@ -743,8 +726,9 @@ namespace Physic std::vector< std::pair > PhysicEngine::rayTest2(btVector3& from, btVector3& to) { MyRayResultCallback resultCallback1; - resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; - dynamicsWorld->rayTest(from, to, resultCallback1); + resultCallback1.m_collisionFilterGroup = 0xff; + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor; + mDynamicsWorld->rayTest(from, to, resultCallback1); std::vector< std::pair > results = resultCallback1.results; std::vector< std::pair > results2; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 803986d5b..f24ef93d0 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -76,6 +76,9 @@ namespace Physic RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); virtual ~RigidBody(); std::string mName; + + // Hack: placeable objects (that can be picked up by the player) have different collision behaviour. + // This variable needs to be passed to BulletNifLoader. bool mPlaceable; }; @@ -92,13 +95,6 @@ namespace Physic void setPosition(const Ogre::Vector3 &pos); - /** - * This adjusts the rotation of a PhysicActor - * If we have any problems with this (getting stuck in pmove) we should change it - * from setting the visual orientation to setting the orientation of the rigid body directly. - */ - void setRotation(const Ogre::Quaternion &quat); - /** * Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry. */ @@ -111,26 +107,18 @@ namespace Physic bool getCollisionMode() const { - return mCollisionMode; + return mInternalCollisionMode; } - - /** - * This returns the visual position of the PhysicActor (used to position a scenenode). - * Note - this is different from the position of the contained mBody. - */ - Ogre::Vector3 getPosition(); - - /** - * Returns the visual orientation of the PhysicActor - */ - Ogre::Quaternion getRotation(); - /** * Sets the scale of the PhysicActor */ void setScale(float scale); + void setRotation (const Ogre::Quaternion& rotation); + + const Ogre::Vector3& getPosition() const; + /** * Returns the half extents for this PhysiActor */ @@ -153,7 +141,7 @@ namespace Physic bool getOnGround() const { - return mCollisionMode && mOnGround; + return mInternalCollisionMode && mOnGround; } btCollisionObject *getCollisionBody() const @@ -165,17 +153,21 @@ namespace Physic void disableCollisionBody(); void enableCollisionBody(); - OEngine::Physic::RigidBody* mBody; - OEngine::Physic::RigidBody* mRaycastingBody; + boost::shared_ptr mShape; - Ogre::Vector3 mBoxScaledTranslation; - Ogre::Quaternion mBoxRotation; - Ogre::Quaternion mBoxRotationInverse; + OEngine::Physic::RigidBody* mBody; + + Ogre::Quaternion mMeshOrientation; + Ogre::Vector3 mMeshTranslation; + Ogre::Vector3 mHalfExtents; + + float mScale; + Ogre::Vector3 mPosition; Ogre::Vector3 mForce; bool mOnGround; - bool mCollisionMode; - bool mCollisionBody; + bool mInternalCollisionMode; + bool mExternalCollisionMode; std::string mMesh; std::string mName; @@ -242,7 +234,7 @@ namespace Physic /** * Add a RigidBody to the simulation */ - void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL,bool actor = false); + void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL); /** * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. @@ -335,7 +327,7 @@ namespace Physic btDefaultCollisionConfiguration* collisionConfiguration; btSequentialImpulseConstraintSolver* solver; btCollisionDispatcher* dispatcher; - btDiscreteDynamicsWorld* dynamicsWorld; + btDiscreteDynamicsWorld* mDynamicsWorld; //the NIF file loader. BulletShapeLoader* mShapeLoader; diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 6eab43a60..78fb5a3a7 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -65,12 +65,13 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, to.setOrigin(btend); ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + newTraceCallback.m_collisionFilterGroup = CollisionType_Actor; newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; btCollisionShape *shape = actor->getCollisionShape(); assert(shape->isConvex()); - enginePass->dynamicsWorld->convexSweepTest(static_cast(shape), + enginePass->mDynamicsWorld->convexSweepTest(static_cast(shape), from, to, newTraceCallback); // Copy the hit data over to our trace results struct: @@ -89,27 +90,26 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, } } -void ActorTracer::findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass) +void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass) { const btVector3 btstart(start.x, start.y, start.z+1.0f); const btVector3 btend(end.x, end.y, end.z+1.0f); - const btTransform &trans = actor->getWorldTransform(); + const btTransform &trans = actor->getCollisionBody()->getWorldTransform(); btTransform from(trans.getBasis(), btstart); btTransform to(trans.getBasis(), btend); - ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionBody(), btstart-btend, btScalar(0.0)); + newTraceCallback.m_collisionFilterGroup = CollisionType_Actor; newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; - const btBoxShape *shape = dynamic_cast(actor->getCollisionShape()); - assert(shape); + btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z); - btVector3 halfExtents = shape->getHalfExtentsWithMargin(); halfExtents[2] = 1.0f; - btBoxShape box(halfExtents); + btBoxShape base(halfExtents); - enginePass->dynamicsWorld->convexSweepTest(&box, from, to, newTraceCallback); + enginePass->mDynamicsWorld->convexSweepTest(&base, from, to, newTraceCallback); if(newTraceCallback.hasHit()) { const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index 92795c87f..b9fbce64d 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -12,6 +12,7 @@ namespace OEngine namespace Physic { class PhysicEngine; + class PhysicActor; struct ActorTracer { @@ -22,7 +23,7 @@ namespace Physic void doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass); - void findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, + void findGround(const OEngine::Physic::PhysicActor* actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass); }; } From 0bed6d9d56fd89510f8f869044c76757153c47d4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 21 Jun 2014 23:56:59 +0200 Subject: [PATCH 152/226] Physics: Recognize BBoxCollision flag, but don't use it for raycasting (Fixes #1349) --- components/nifbullet/bulletnifloader.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 3e753dd37..bed3e8869 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -137,12 +137,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) handleNode(mesh2, node,0,true,true,false); - if(mBoundingBox != NULL) - { - mShape->mRaycastingShape = mBoundingBox; - delete mesh2; - } - else if (mHasShape) + if (mHasShape) { mShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); } @@ -227,10 +222,12 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * if ( (isCollisionNode || (!mShape->mHasCollisionNode && !raycasting)) && (!isMarker || (mShape->mHasCollisionNode && !raycasting))) { + // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! + // It must be ignored completely. + // (occurs in tr_ex_imp_wall_arch_04.nif) if(node->hasBounds) { - // Checking for BBoxCollision flag causes issues with some actors :/ - if (!(node->flags & Nif::NiNode::Flag_Hidden)) + if (flags & Nif::NiNode::Flag_BBoxCollision && !raycasting) { mShape->mBoxTranslation = node->boundPos; mShape->mBoxRotation = node->boundRot; From e23a7694f30afcb8ae683fe67bfa6631fab96244 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 00:34:32 +0200 Subject: [PATCH 153/226] Don't throw exception when using ModDisposition on creatures (Fixes #1548) --- apps/openmw/mwscript/statsextensions.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 5a0cd8ae6..1d3a8bc4b 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -690,8 +690,11 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - ptr.getClass().getNpcStats (ptr).setBaseDisposition - (ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value); + if (ptr.getClass().isNpc()) + ptr.getClass().getNpcStats (ptr).setBaseDisposition + (ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value); + + // else: must not throw exception (used by an Almalexia dialogue script) } }; @@ -707,7 +710,8 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - ptr.getClass().getNpcStats (ptr).setBaseDisposition (value); + if (ptr.getClass().isNpc()) + ptr.getClass().getNpcStats (ptr).setBaseDisposition (value); } }; @@ -720,7 +724,10 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); + if (!ptr.getClass().isNpc()) + runtime.push(0); + else + runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); } }; From ead801f2d031fb1947a17fa1e71213277b00234e Mon Sep 17 00:00:00 2001 From: slothlife Date: Sat, 21 Jun 2014 21:15:41 -0500 Subject: [PATCH 154/226] Fix for compiling Win32 debug. --- apps/openmw/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index a061cf63c..adde408b9 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -299,8 +299,10 @@ int main(int argc, char**argv) std::streambuf* cout_rdbuf = std::cout.rdbuf (); std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); +#if !(defined(_WIN32) && defined(_DEBUG)) boost::iostreams::stream_buffer coutsb; boost::iostreams::stream_buffer cerrsb; +#endif std::ostream oldcout(cout_rdbuf); std::ostream oldcerr(cerr_rdbuf); From 33ed11d8e6be6065b4683fbd4103555fc32c9431 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 00:47:14 +0200 Subject: [PATCH 155/226] Physics: Use capsule shapes for actors if possible (Fixes #1437) --- libs/openengine/bullet/physic.cpp | 11 +++++++++-- libs/openengine/bullet/trace.cpp | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index bc34f2f51..d934639e0 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -29,7 +29,14 @@ namespace Physic mMeshOrientation = Ogre::Quaternion::IDENTITY; } - mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); + // Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it) + if (std::abs(mHalfExtents.x-mHalfExtents.y)= mHalfExtents.x) + { + mShape.reset(new btCapsuleShapeZ(mHalfExtents.x, mHalfExtents.z*2.f - mHalfExtents.x*2.f)); + } + else + mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); + mShape->setLocalScaling(btVector3(scale,scale,scale)); btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo @@ -102,7 +109,7 @@ namespace Physic Ogre::Vector3 PhysicActor::getHalfExtents() const { - return mHalfExtents; + return mHalfExtents * mScale; } void PhysicActor::setInertialForce(const Ogre::Vector3 &force) diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 78fb5a3a7..46db4c6b8 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -107,7 +107,7 @@ void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Og btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z); halfExtents[2] = 1.0f; - btBoxShape base(halfExtents); + btCylinderShapeZ base(halfExtents); enginePass->mDynamicsWorld->convexSweepTest(&base, from, to, newTraceCallback); if(newTraceCallback.hasHit()) From baf490a2b593f89f4469028082dabe784ed46889 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 03:05:10 +0200 Subject: [PATCH 156/226] Change to btCylinderShape --- libs/openengine/bullet/physic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d934639e0..31cb67a58 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -32,7 +32,8 @@ namespace Physic // Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it) if (std::abs(mHalfExtents.x-mHalfExtents.y)= mHalfExtents.x) { - mShape.reset(new btCapsuleShapeZ(mHalfExtents.x, mHalfExtents.z*2.f - mHalfExtents.x*2.f)); + // Could also be btCapsuleShapeZ, but the movement solver seems to have issues with it (jumping on slopes doesn't work) + mShape.reset(new btCylinderShapeZ(BtOgre::Convert::toBullet(mHalfExtents))); } else mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); From cbec0ffaee7bbb93d5c0346160f1b7c9449d15dd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 03:05:17 +0200 Subject: [PATCH 157/226] Exception fix --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index f4ffa499c..0ad0b4d5a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1101,7 +1101,7 @@ namespace MWMechanics if (iter->first.getClass().isClass(iter->first, "Guard")) { MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); - if (aiSeq.getActivePackage()->getTypeId() == MWMechanics::AiPackage::TypeIdPursue) + if (aiSeq.getTypeId() == MWMechanics::AiPackage::TypeIdPursue) { aiSeq.stopPursuit(); aiSeq.stack(MWMechanics::AiCombat(target), ptr); From d296c6e9b7bb7dbceafcb4b0c0ea1ae2d00950fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 16:10:52 +0200 Subject: [PATCH 158/226] Handle fog density values of 0 (Bug #1549) --- apps/openmw/mwrender/renderingmanager.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8a22c63c6..89933414d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -506,10 +506,19 @@ void RenderingManager::configureFog(const float density, const Ogre::ColourValue mFogColour = colour; float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance"); - mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); - mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); + if (density == 0) + { + mFogStart = 0; + mFogEnd = std::numeric_limits().max(); + mRendering.getCamera()->setFarClipDistance (max); + } + else + { + mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); + mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); + mRendering.getCamera()->setFarClipDistance (max / density); + } - mRendering.getCamera()->setFarClipDistance ( Settings::Manager::getFloat("max viewing distance", "Viewing distance") / density ); } void RenderingManager::applyFog (bool underwater) From 8df0effcf5244121ca53fbd5560e0e5b3e915988 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 16:25:29 +0200 Subject: [PATCH 159/226] Remove unused string --- libs/openengine/bullet/BulletShapeLoader.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index 0e5c65226..c6f7f727c 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -15,8 +15,6 @@ namespace Physic */ class BulletShape : public Ogre::Resource { - Ogre::String mString; - protected: void loadImpl(); void unloadImpl(); From 64a4c2785e2883f530d10448bb523d94a8de66ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 16:43:41 +0200 Subject: [PATCH 160/226] Fix water walking --- apps/openmw/mwworld/physicssystem.cpp | 3 ++- libs/openengine/bullet/physic.hpp | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 8aca591e8..ad4300b1f 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -743,7 +743,8 @@ namespace MWWorld // TODO: this seems to have a slight performance impact if (waterCollision) - mEngine->mDynamicsWorld->addCollisionObject(&object); + mEngine->mDynamicsWorld->addCollisionObject(&object, + 0xff, OEngine::Physic::CollisionType_Actor); // 100 points of slowfall reduce gravity by 90% (this is just a guess) float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f24ef93d0..deef21443 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -82,10 +82,7 @@ namespace Physic bool mPlaceable; }; - /** - * A physic actor uses a rigid body based on box shapes. - * Pmove is used to move the physic actor around the dynamic world. - */ + class PhysicActor { public: From 9ea22324f76d1c5f71319050c1bbdcdc36a0e4f7 Mon Sep 17 00:00:00 2001 From: slothlife Date: Mon, 23 Jun 2014 01:13:30 -0500 Subject: [PATCH 161/226] Fix some MSVC warnings. Several fixes are warnings about truncations on 64-bit, while others are complaints about mixed signed / unsigned integer operations. --- CMakeLists.txt | 5 ++++- apps/opencs/view/render/scenewidget.cpp | 2 +- apps/openmw/mwgui/list.cpp | 2 +- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/videoplayer.cpp | 2 +- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- components/terrain/buffercache.cpp | 6 +++--- extern/sdl4ogre/sdlwindowhelper.cpp | 2 +- 8 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1769f884d..70ca1c7d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -615,6 +615,7 @@ if (WIN32) 4244 # Storing value of one type in variable of another (size_t in int, for example) 4305 # Truncating value (double to float, for example) 4309 # Variable overflow, trying to store 128 in a signed char for example + 4351 # New behavior: elements of array 'array' will be default initialized (desired behavior) 4355 # Using 'this' in member initialization list 4505 # Unreferenced local function has been removed 4701 # Potentially uninitialized local variable used @@ -633,7 +634,9 @@ if (WIN32) set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101") set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${SHINY_OGRE_WARNINGS}) set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS ${WARNINGS}) - set_target_properties(oics PROPERTIES COMPILE_FLAGS ${WARNINGS}) + # oics uses tinyxml, which has an initialized but unused variable + set(OICS_WARNINGS "${WARNINGS} /wd4189") + set_target_properties(oics PROPERTIES COMPILE_FLAGS ${OICS_WARNINGS}) set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS}) if (BUILD_LAUNCHER) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 52e7afefd..eccaebd00 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -87,7 +87,7 @@ namespace CSVRender std::stringstream windowHandle; #ifdef WIN32 - windowHandle << Ogre::StringConverter::toString((unsigned long)(this->winId())); + windowHandle << Ogre::StringConverter::toString((uintptr_t)(this->winId())); #else windowHandle << this->winId(); #endif diff --git a/apps/openmw/mwgui/list.cpp b/apps/openmw/mwgui/list.cpp index ca2989646..b0c514b9d 100644 --- a/apps/openmw/mwgui/list.cpp +++ b/apps/openmw/mwgui/list.cpp @@ -101,7 +101,7 @@ namespace MWGui size_t viewRange = mScrollView->getCanvasSize().height; if(viewPosition > viewRange) viewPosition = viewRange; - mScrollView->setViewOffset(MyGUI::IntPoint(0, -viewPosition)); + mScrollView->setViewOffset(MyGUI::IntPoint(0, viewPosition * -1)); } void MWList::setPropertyOverride(const std::string &_key, const std::string &_value) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 2b0323675..62907fcc3 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -319,7 +319,7 @@ void LocalMap::createFogOfWar(const std::string& texturePrefix) std::vector buffer; // initialize to (0, 0, 0, 1) - buffer.resize(sFogOfWarResolution*sFogOfWarResolution, (255 << 24)); + buffer.resize(sFogOfWarResolution*sFogOfWarResolution, 0xFF000000); // upload to the texture memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 79f2fa948..03e74697c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -747,7 +747,7 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) * buffer. We use this to store the global_pts in * a frame at the time it is allocated. */ -static uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; +static uint64_t global_video_pkt_pts = static_cast(AV_NOPTS_VALUE); static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) { int ret = avcodec_default_get_buffer(c, pic); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 35b8579d7..7683c2b90 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -385,7 +385,7 @@ namespace MWSound sound = mOutput->playSound3D(file, initialPos, volume, basevol, pitch, min, max, mode|type, offset); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); } - catch(std::exception &e) + catch(std::exception &) { //std::cout <<"Sound Error: "< Date: Mon, 23 Jun 2014 20:43:24 +0200 Subject: [PATCH 162/226] Add support for animated collision shapes (Fixes #1549) --- apps/openmw/mwworld/physicssystem.cpp | 53 ++++++- apps/openmw/mwworld/physicssystem.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 2 +- components/nifbullet/bulletnifloader.cpp | 150 +++++++++++++------ components/nifbullet/bulletnifloader.hpp | 32 +++- libs/openengine/bullet/BulletShapeLoader.cpp | 5 +- libs/openengine/bullet/BulletShapeLoader.h | 8 + libs/openengine/bullet/physic.cpp | 136 +++++++++++++---- libs/openengine/bullet/physic.hpp | 21 ++- 9 files changed, 312 insertions(+), 97 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index ad4300b1f..7f26fa75a 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -26,10 +27,49 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "../apps/openmw/mwrender/animation.hpp" +#include "../apps/openmw/mwbase/world.hpp" +#include "../apps/openmw/mwbase/environment.hpp" + #include "ptr.hpp" #include "class.hpp" using namespace Ogre; + +namespace +{ + +void animateCollisionShapes (std::map& map) +{ + for (std::map::iterator it = map.begin(); + it != map.end(); ++it) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(it->first->mName); + MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr); + + OEngine::Physic::AnimatedShapeInstance& instance = it->second; + + std::map& shapes = instance.mAnimatedShapes; + for (std::map::iterator shapeIt = shapes.begin(); + shapeIt != shapes.end(); ++shapeIt) + { + Ogre::Node* bone = animation->getNode(shapeIt->first); + + btCompoundShape* compound = dynamic_cast(instance.mCompound); + + btTransform trans; + trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition())); + trans.setRotation(BtOgre::Convert::toBullet(bone->_getDerivedOrientation())); + + compound->getChildShape(shapeIt->second)->setLocalScaling(BtOgre::Convert::toBullet(bone->_getDerivedScale())); + compound->updateChildTransform(shapeIt->second, trans); + } + } +} + +} + + namespace MWWorld { @@ -564,11 +604,10 @@ namespace MWWorld std::string mesh = ptr.getClass().getModel(ptr); Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); handleToMesh[node->getName()] = mesh; - OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody( + mEngine->createAndAdjustRigidBody( mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable); - OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody( + mEngine->createAndAdjustRigidBody( mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable); - mEngine->addRigidBody(body, true, raycastingBody); } void PhysicsSystem::addActor (const Ptr& ptr) @@ -770,4 +809,12 @@ namespace MWWorld return mMovementResults; } + + void PhysicsSystem::stepSimulation(float dt) + { + animateCollisionShapes(mEngine->mAnimatedShapes); + animateCollisionShapes(mEngine->mAnimatedRaycastingShapes); + + mEngine->stepSimulation(dt); + } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index c590b40c8..df9718669 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -53,6 +53,8 @@ namespace MWWorld bool toggleCollisionMode(); + void stepSimulation(float dt); + std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index be334646b..b4957abfd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1231,7 +1231,7 @@ namespace MWWorld if(player != results.end()) moveObjectImp(player->first, player->second.x, player->second.y, player->second.z); - mPhysEngine->stepSimulation(duration); + mPhysics->stepSimulation(duration); } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index bed3e8869..64febc3c2 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -49,20 +49,6 @@ typedef unsigned char ubyte; namespace NifBullet { -struct TriangleMeshShape : public btBvhTriangleMeshShape -{ - TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression) - : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression) - { - } - - virtual ~TriangleMeshShape() - { - delete getTriangleInfoMap(); - delete m_meshInterface; - } -}; - ManualBulletShapeLoader::~ManualBulletShapeLoader() { } @@ -81,9 +67,8 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mBoundingBox = NULL; mShape->mBoxTranslation = Ogre::Vector3(0,0,0); mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; - mHasShape = false; - - btTriangleMesh* mesh1 = new btTriangleMesh(); + mCompoundShape = NULL; + mStaticMesh = NULL; // Load the NIF. TODO: Wrap this in a try-catch block once we're out // of the early stages of development. Right now we WANT to catch @@ -111,19 +96,35 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mShape->mHasCollisionNode = hasRootCollisionNode(node); //do a first pass - handleNode(mesh1, node,0,false,false,false); + handleNode(node,0,false,false,false); if(mBoundingBox != NULL) { mShape->mCollisionShape = mBoundingBox; - delete mesh1; - } - else if (mHasShape && mShape->mCollide) - { - mShape->mCollisionShape = new TriangleMeshShape(mesh1,true); + delete mStaticMesh; + if (mCompoundShape) + { + int n = mCompoundShape->getNumChildShapes(); + for(int i=0; i getChildShape(i)); + delete mCompoundShape; + } } else - delete mesh1; + { + if (mCompoundShape) + { + mShape->mCollisionShape = mCompoundShape; + if (mStaticMesh) + { + btTransform trans; + trans.setIdentity(); + mCompoundShape->addChildShape(trans, new TriangleMeshShape(mStaticMesh,true)); + } + } + else if (mStaticMesh) + mShape->mCollisionShape = new TriangleMeshShape(mStaticMesh,true); + } //second pass which create a shape for raycasting. mResourceName = mShape->getName(); @@ -131,18 +132,23 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mBoundingBox = NULL; mShape->mBoxTranslation = Ogre::Vector3(0,0,0); mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; - mHasShape = false; + mStaticMesh = NULL; + mCompoundShape = NULL; - btTriangleMesh* mesh2 = new btTriangleMesh(); + handleNode(node,0,true,true,false); - handleNode(mesh2, node,0,true,true,false); - - if (mHasShape) + if (mCompoundShape) { - mShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); + mShape->mRaycastingShape = mCompoundShape; + if (mStaticMesh) + { + btTransform trans; + trans.setIdentity(); + mCompoundShape->addChildShape(trans, new TriangleMeshShape(mStaticMesh,true)); + } } - else - delete mesh2; + else if (mStaticMesh) + mShape->mRaycastingShape = new TriangleMeshShape(mStaticMesh,true); } bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) @@ -167,14 +173,17 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) return false; } -void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *node, int flags, +void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, bool isCollisionNode, - bool raycasting, bool isMarker) + bool raycasting, bool isMarker, bool isAnimated) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; + if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController) + isAnimated = true; + if (!raycasting) isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); else @@ -237,7 +246,7 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * else if(node->recType == Nif::RC_NiTriShape) { mShape->mCollide = !(flags&0x800); - handleNiTriShape(mesh, static_cast(node), flags, node->getWorldTransform(), raycasting); + handleNiTriShape(static_cast(node), flags, node->getWorldTransform(), raycasting, isAnimated); } } @@ -249,13 +258,13 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(mesh, list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker); + handleNode(list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker, isAnimated); } } } -void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, - bool raycasting) +void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, + bool raycasting, bool isAnimated) { assert(shape != NULL); @@ -278,17 +287,64 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif:: // bother setting it up. return; - mHasShape = true; + if (!shape->skin.empty()) + isAnimated = false; - const Nif::NiTriShapeData *data = shape->data.getPtr(); - const std::vector &vertices = data->vertices; - const short *triangles = &data->triangles[0]; - for(size_t i = 0;i < data->triangles.size();i+=3) + if (isAnimated) { - Ogre::Vector3 b1 = transform*vertices[triangles[i+0]]; - Ogre::Vector3 b2 = transform*vertices[triangles[i+1]]; - Ogre::Vector3 b3 = transform*vertices[triangles[i+2]]; - mesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); + if (!mCompoundShape) + mCompoundShape = new btCompoundShape(); + + btTriangleMesh* childMesh = new btTriangleMesh(); + + const Nif::NiTriShapeData *data = shape->data.getPtr(); + + childMesh->preallocateVertices(data->vertices.size()); + childMesh->preallocateIndices(data->triangles.size()); + + const std::vector &vertices = data->vertices; + const std::vector &triangles = data->triangles; + + for(size_t i = 0;i < data->triangles.size();i+=3) + { + Ogre::Vector3 b1 = vertices[triangles[i+0]]; + Ogre::Vector3 b2 = vertices[triangles[i+1]]; + Ogre::Vector3 b3 = vertices[triangles[i+2]]; + childMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); + } + + TriangleMeshShape* childShape = new TriangleMeshShape(childMesh,true); + + childShape->setLocalScaling(btVector3(transform[0][0], transform[1][1], transform[2][2])); + + Ogre::Quaternion q = transform.extractQuaternion(); + Ogre::Vector3 v = transform.getTrans(); + btTransform trans(btQuaternion(q.x, q.y, q.z, q.w), btVector3(v.x, v.y, v.z)); + + if (raycasting) + mShape->mAnimatedRaycastingShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes())); + else + mShape->mAnimatedShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes())); + + mCompoundShape->addChildShape(trans, childShape); + } + else + { + if (!mStaticMesh) + mStaticMesh = new btTriangleMesh(); + + // Static shape, just transform all vertices into position + const Nif::NiTriShapeData *data = shape->data.getPtr(); + const std::vector &vertices = data->vertices; + const std::vector &triangles = data->triangles; + + for(size_t i = 0;i < data->triangles.size();i+=3) + { + Ogre::Vector3 b1 = transform*vertices[triangles[i+0]]; + Ogre::Vector3 b2 = transform*vertices[triangles[i+1]]; + Ogre::Vector3 b3 = transform*vertices[triangles[i+2]]; + mStaticMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); + } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index fb7d3d70a..a9ee968b9 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,22 @@ namespace Nif namespace NifBullet { +// Subclass btBhvTriangleMeshShape to auto-delete the meshInterface +struct TriangleMeshShape : public btBvhTriangleMeshShape +{ + TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression) + : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression) + { + } + + virtual ~TriangleMeshShape() + { + delete getTriangleInfoMap(); + delete m_meshInterface; + } +}; + + /** *Load bulletShape from NIF files. */ @@ -52,8 +69,9 @@ class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader public: ManualBulletShapeLoader() : mShape(NULL) + , mStaticMesh(NULL) + , mCompoundShape(NULL) , mBoundingBox(NULL) - , mHasShape(false) { } @@ -88,7 +106,8 @@ private: /** *Parse a node. */ - void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool isCollisionNode, bool raycasting, bool isMarker); + void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, + bool raycasting, bool isMarker, bool isAnimated=false); /** *Helper function @@ -98,14 +117,17 @@ private: /** *convert a NiTriShape to a bullet trishape. */ - void handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting); + void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting, bool isAnimated); std::string mResourceName; OEngine::Physic::BulletShape* mShape;//current shape - btBoxShape *mBoundingBox; - bool mHasShape; + btCompoundShape* mCompoundShape; + + btTriangleMesh* mStaticMesh; + + btBoxShape *mBoundingBox; }; diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index 5528924a9..bcb2ba6b2 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -42,7 +42,7 @@ void BulletShape::deleteShape(btCollisionShape* shape) { if(shape->isCompound()) { - btCompoundShape* ms = static_cast(mCollisionShape); + btCompoundShape* ms = static_cast(shape); int a = ms->getNumChildShapes(); for(int i=0; i mAnimatedShapes; + + std::map mAnimatedRaycastingShapes; + btCollisionShape* mCollisionShape; btCollisionShape* mRaycastingShape; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 31cb67a58..469e33f25 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -11,6 +11,59 @@ #include #include +namespace +{ + +// Create a copy of the given collision shape (responsibility of user to delete the returned shape). +btCollisionShape *duplicateCollisionShape(btCollisionShape *shape) +{ + if(shape->isCompound()) + { + btCompoundShape *comp = static_cast(shape); + btCompoundShape *newShape = new btCompoundShape; + + int numShapes = comp->getNumChildShapes(); + for(int i = 0;i < numShapes;i++) + { + btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i)); + btTransform trans = comp->getChildTransform(i); + newShape->addChildShape(trans, child); + } + + return newShape; + } + + if(btBvhTriangleMeshShape *trishape = dynamic_cast(shape)) + { + btTriangleMesh* oldMesh = dynamic_cast(trishape->getMeshInterface()); + btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); + NifBullet::TriangleMeshShape *newShape = new NifBullet::TriangleMeshShape(newMesh, true); + + return newShape; + } + + throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName()); +} + +void deleteShape(btCollisionShape* shape) +{ + if(shape!=NULL) + { + if(shape->isCompound()) + { + btCompoundShape* ms = static_cast(shape); + int a = ms->getNumChildShapes(); + for(int i=0; i getChildShape(i)); + } + } + delete shape; + } +} + +} + namespace OEngine { namespace Physic { @@ -228,6 +281,11 @@ namespace Physic PhysicEngine::~PhysicEngine() { + for (std::map::iterator it = mAnimatedShapes.begin(); it != mAnimatedShapes.end(); ++it) + deleteShape(it->second.mCompound); + for (std::map::iterator it = mAnimatedRaycastingShapes.begin(); it != mAnimatedRaycastingShapes.end(); ++it) + deleteShape(it->second.mCompound); + HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); for (; hf_it != mHeightFieldMap.end(); ++hf_it) { @@ -386,17 +444,38 @@ namespace Physic if (!shape->mRaycastingShape && raycasting) return NULL; - if (!raycasting) - shape->mCollisionShape->setLocalScaling( btVector3(scale,scale,scale)); - else - shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale)); + btCollisionShape* collisionShape = raycasting ? shape->mRaycastingShape : shape->mCollisionShape; + + // If this is an animated compound shape, we must duplicate it so we can animate + // multiple instances independently. + if (!raycasting && !shape->mAnimatedShapes.empty()) + collisionShape = duplicateCollisionShape(collisionShape); + if (raycasting && !shape->mAnimatedRaycastingShapes.empty()) + collisionShape = duplicateCollisionShape(collisionShape); + + collisionShape->setLocalScaling( btVector3(scale,scale,scale)); //create the real body btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo - (0,0, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); + (0,0, collisionShape); RigidBody* body = new RigidBody(CI,name); body->mPlaceable = placeable; + if (!raycasting && !shape->mAnimatedShapes.empty()) + { + AnimatedShapeInstance instance; + instance.mAnimatedShapes = shape->mAnimatedShapes; + instance.mCompound = collisionShape; + mAnimatedShapes[body] = instance; + } + if (raycasting && !shape->mAnimatedRaycastingShapes.empty()) + { + AnimatedShapeInstance instance; + instance.mAnimatedShapes = shape->mAnimatedRaycastingShapes; + instance.mCompound = collisionShape; + mAnimatedRaycastingShapes[body] = instance; + } + if(scaledBoxTranslation != 0) *scaledBoxTranslation = shape->mBoxTranslation * scale; if(boxRotation != 0) @@ -404,33 +483,20 @@ namespace Physic adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation); - return body; - - } - - void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody) - { - if(!body && !raycastingBody) - return; // nothing to do - - const std::string& name = (body ? body->mName : raycastingBody->mName); - - if (body){ + if (!raycasting) + { + assert (mCollisionObjectMap.find(name) == mCollisionObjectMap.end()); + mCollisionObjectMap[name] = body; mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); } - - if (raycastingBody) - mDynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting); - - if(addToMap){ - removeRigidBody(name); - deleteRigidBody(name); - - if (body) - mCollisionObjectMap[name] = body; - if (raycastingBody) - mRaycastingObjectMap[name] = raycastingBody; + else + { + assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end()); + mRaycastingObjectMap[name] = body; + mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting); } + + return body; } void PhysicEngine::removeRigidBody(const std::string &name) @@ -464,6 +530,10 @@ namespace Physic if(body != NULL) { + if (mAnimatedShapes.find(body) != mAnimatedShapes.end()) + deleteShape(mAnimatedShapes[body].mCompound); + mAnimatedShapes.erase(body); + delete body; } mCollisionObjectMap.erase(it); @@ -475,6 +545,10 @@ namespace Physic if(body != NULL) { + if (mAnimatedRaycastingShapes.find(body) != mAnimatedRaycastingShapes.end()) + deleteShape(mAnimatedRaycastingShapes[body].mCompound); + mAnimatedRaycastingShapes.erase(body); + delete body; } mRaycastingObjectMap.erase(it); @@ -609,7 +683,6 @@ namespace Physic return std::make_pair(callback.mObject, callback.mContactPoint); } - void PhysicEngine::stepSimulation(double deltaT) { // This seems to be needed for character controller objects @@ -629,8 +702,6 @@ namespace Physic PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale); - - //dynamicsWorld->addAction( newActor->mCharacter ); mActorMap[name] = newActor; } @@ -642,7 +713,6 @@ namespace Physic PhysicActor* act = it->second; if(act != NULL) { - delete act; } mActorMap.erase(it); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index deef21443..e37caee38 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -117,7 +117,7 @@ namespace Physic const Ogre::Vector3& getPosition() const; /** - * Returns the half extents for this PhysiActor + * Returns the (scaled) half extents */ Ogre::Vector3 getHalfExtents() const; @@ -178,6 +178,14 @@ namespace Physic RigidBody* mBody; }; + struct AnimatedShapeInstance + { + btCollisionShape* mCompound; + + // Maps bone name to child index in the compound shape + std::map mAnimatedShapes; + }; + /** * The PhysicEngine class contain everything which is needed for Physic. * It's needed that Ogre Resources are set up before the PhysicEngine is created. @@ -228,11 +236,6 @@ namespace Physic */ void removeHeightField(int x, int y); - /** - * Add a RigidBody to the simulation - */ - void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL); - /** * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. */ @@ -335,8 +338,14 @@ namespace Physic typedef std::map RigidBodyContainer; RigidBodyContainer mCollisionObjectMap; + // Compound shapes that must be animated each frame based on bone positions + // the index refers to an element in mCollisionObjectMap + std::map mAnimatedShapes; + RigidBodyContainer mRaycastingObjectMap; + std::map mAnimatedRaycastingShapes; + typedef std::map PhysicActorContainer; PhysicActorContainer mActorMap; From 320ab1b2c1572b20fe193aff921a63524275e837 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jun 2014 21:59:13 +0200 Subject: [PATCH 163/226] Don't say voice when using startcombat for a dead actor (Fixes #1542) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0ad0b4d5a..652f5363e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1112,7 +1112,7 @@ namespace MWMechanics } // Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly - if (ptr.getClass().isNpc()) + if (ptr.getClass().isNpc() && !ptr.getClass().getCreatureStats(ptr).isDead()) MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); } From 750d8b55892c0efa495cb0d99d95b9442c1896b0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jun 2014 22:02:22 +0200 Subject: [PATCH 164/226] Allow garbage integer argument for addSpell (Fixes #1539) --- components/compiler/exprparser.cpp | 19 +++++++++++-------- components/compiler/extensions.hpp | 3 ++- components/compiler/extensions0.cpp | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index ed628278b..e54b2e2a8 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -775,7 +775,7 @@ namespace Compiler { parser.reset(); - if (optional) + if (optional || *iter == 'X') parser.setOptional (true); scanner.scan (parser); @@ -783,17 +783,20 @@ namespace Compiler if (optional && parser.isEmpty()) break; - std::vector tmp; + if (*iter != 'X') + { + 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 418cbb402..d229751de 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -20,7 +20,8 @@ namespace Compiler l - Integer
s - Short
S - String, case preserved
- x - Optional, ignored argument + x - Optional, ignored string argument + X - Optional, ignored integer argument **/ typedef std::string ScriptArgs; diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 9fc66900f..f38c3320c 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -405,7 +405,7 @@ namespace Compiler extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); - extensions.registerInstruction ("addspell", "cx", opcodeAddSpell, opcodeAddSpellExplicit); + extensions.registerInstruction ("addspell", "cxX", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, From 1d46ac19ff0e488c03be0a3cbd68e9d19c94a736 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 02:24:32 +0200 Subject: [PATCH 165/226] Fix being able to activate through terrain --- apps/openmw/mwworld/worldimp.cpp | 12 +++++++++--- libs/openengine/bullet/physic.cpp | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b4957abfd..c4e4e3d3a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1471,8 +1471,13 @@ namespace MWWorld std::vector < std::pair < float, std::string > >::iterator it = results.begin(); while (it != results.end()) { - if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain - || getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) + if ((*it).second.find("HeightField") != std::string::npos) // Don't attempt to getPtrViaHandle on terrain + { + ++it; + continue; + } + + if (getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) { it = results.erase(it); } @@ -1480,7 +1485,8 @@ namespace MWWorld ++it; } - if (results.empty()) + if (results.empty() + || results.front().second.find("HeightField") != std::string::npos) // Blocked by terrain { mFacedHandle = ""; mFacedDistance = FLT_MAX; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 469e33f25..d00441fdf 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -805,7 +805,7 @@ namespace Physic { MyRayResultCallback resultCallback1; resultCallback1.m_collisionFilterGroup = 0xff; - resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor; + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor|CollisionType_HeightMap; mDynamicsWorld->rayTest(from, to, resultCallback1); std::vector< std::pair > results = resultCallback1.results; From 30be59c029307937758c75a2a1ba98d1844950d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 03:07:10 +0200 Subject: [PATCH 166/226] Implement auto-adjusting of particle emit rate This makes ashcloud.nif and blightcloud.nif work properly. --- components/nif/controller.hpp | 9 ++++++++- components/nifogre/ogrenifloader.cpp | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index e44f4a6f3..920e634e6 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -89,7 +89,14 @@ public: float lifetime; float lifetimeRandom; - int emitFlags; // Bit 0: Emit Rate toggle bit (0 = auto adjust, 1 = use Emit Rate value) + enum EmitFlags + { + NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise, + // we calculate an emit rate so that the maximum number of particles + // in the system (numParticles) is never exceeded. + }; + int emitFlags; + Ogre::Vector3 offsetRandom; NodePtr emitter; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 813c1660d..eed320756 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -756,7 +756,12 @@ class NIFObjectLoader Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, partctrl->velocity + partctrl->velocityRandom*0.5f); - emitter->setEmissionRate(partctrl->emitRate); + + if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust) + emitter->setEmissionRate(partctrl->emitRate); + else + emitter->setEmissionRate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); + emitter->setTimeToLive(partctrl->lifetime, partctrl->lifetime + partctrl->lifetimeRandom); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); From 96ba4a414f67d63cd9124c331c40c808e3ae9a56 Mon Sep 17 00:00:00 2001 From: slothlife Date: Tue, 24 Jun 2014 00:13:34 -0500 Subject: [PATCH 167/226] Fix MSVC size_t conversion warning. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70ca1c7d7..9de8efeb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -613,6 +613,7 @@ if (WIN32) 4127 # Conditional expression is constant 4242 # Storing value in a variable of a smaller type, possible loss of data 4244 # Storing value of one type in variable of another (size_t in int, for example) + 4267 # Conversion from 'size_t' to 'int', possible loss of data 4305 # Truncating value (double to float, for example) 4309 # Variable overflow, trying to store 128 in a signed char for example 4351 # New behavior: elements of array 'array' will be default initialized (desired behavior) From 36132e054afa8e85c5bd2ec4517908c810e580b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 15:00:15 +0200 Subject: [PATCH 168/226] Implement ashstorm, blightstorm, snow and blizzard effects (Feature #41) --- apps/openmw/mwrender/sky.cpp | 41 ++++++++++++++++++++++++++++++++- apps/openmw/mwrender/sky.hpp | 5 ++++ apps/openmw/mwworld/weather.cpp | 23 ++++++++++-------- apps/openmw/mwworld/weather.hpp | 7 +++++- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 90c08c299..545d34bdf 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -8,10 +8,11 @@ #include #include #include -#include +#include #include #include #include +#include #include @@ -234,6 +235,7 @@ SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) , mCreated(false) , mCloudAnimationTimer(0.f) , mMoonRed(false) + , mParticleNode(NULL) { mSceneMgr = root->getCreator(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); @@ -372,6 +374,12 @@ void SkyManager::update(float duration) if (!mEnabled) return; const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); + if (!mParticle.isNull()) + { + for (unsigned int i=0; imControllers.size(); ++i) + mParticle->mControllers[i].update(); + } + // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed; sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", @@ -441,6 +449,37 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mCreated) return; + if (mCurrentParticleEffect != weather.mParticleEffect) + { + mCurrentParticleEffect = weather.mParticleEffect; + + if (mCurrentParticleEffect.empty()) + { + mParticle.setNull(); + } + else + { + if (!mParticleNode) + { + mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(); + mParticleNode->setInheritOrientation(false); + } + + mParticle = NifOgre::Loader::createObjects(mParticleNode, mCurrentParticleEffect); + for(size_t i = 0; i < mParticle->mParticles.size(); ++i) + { + ParticleSystem* particle = mParticle->mParticles[i]; + particle->setRenderQueueGroup(RQG_Alpha); + particle->setVisibilityFlags(RV_Sky); + } + for (size_t i = 0; i < mParticle->mControllers.size(); ++i) + { + if (mParticle->mControllers[i].getSource().isNull()) + mParticle->mControllers[i].setSource(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); + } + } + } + if (mClouds != weather.mCloudTexture) { sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", "textures\\"+weather.mCloudTexture); diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 965907a97..07edc461e 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -200,6 +200,9 @@ namespace MWRender std::vector mObjects; + Ogre::SceneNode* mParticleNode; + NifOgre::ObjectScenePtr mParticle; + // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; @@ -211,6 +214,8 @@ namespace MWRender Ogre::ColourValue mSkyColour; Ogre::ColourValue mFogColour; + std::string mCurrentParticleEffect; + Ogre::Light* mLightning; float mRemainingTransitionTime; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 25f523bee..ab5ec0004 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -58,6 +58,11 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name weather.mWindSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Wind_Speed"); weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); + weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture"); + size_t offset = weather.mCloudTexture.find(".tga"); + if (offset != std::string::npos) + weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds"); + mWeatherSettings[name] = weather; } @@ -123,48 +128,42 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa //Weather Weather clear; - clear.mCloudTexture = "tx_sky_clear.dds"; setFallbackWeather(clear,"clear"); Weather cloudy; - cloudy.mCloudTexture = "tx_sky_cloudy.dds"; setFallbackWeather(cloudy,"cloudy"); Weather foggy; - foggy.mCloudTexture = "tx_sky_foggy.dds"; setFallbackWeather(foggy,"foggy"); Weather thunderstorm; - thunderstorm.mCloudTexture = "tx_sky_thunder.dds"; thunderstorm.mRainLoopSoundID = "rain heavy"; setFallbackWeather(thunderstorm,"thunderstorm"); Weather rain; - rain.mCloudTexture = "tx_sky_rainy.dds"; rain.mRainLoopSoundID = "rain"; setFallbackWeather(rain,"rain"); Weather overcast; - overcast.mCloudTexture = "tx_sky_overcast.dds"; setFallbackWeather(overcast,"overcast"); Weather ashstorm; - ashstorm.mCloudTexture = "tx_sky_ashstorm.dds"; ashstorm.mAmbientLoopSoundID = "ashstorm"; + ashstorm.mParticleEffect = "meshes\\ashcloud.nif"; setFallbackWeather(ashstorm,"ashstorm"); Weather blight; - blight.mCloudTexture = "tx_sky_blight.dds"; blight.mAmbientLoopSoundID = "blight"; + blight.mParticleEffect = "meshes\\blightcloud.nif"; setFallbackWeather(blight,"blight"); Weather snow; - snow.mCloudTexture = "tx_bm_sky_snow.dds"; + snow.mParticleEffect = "meshes\\snow.nif"; setFallbackWeather(snow, "snow"); Weather blizzard; - blizzard.mCloudTexture = "tx_bm_sky_blizzard.dds"; blizzard.mAmbientLoopSoundID = "BM Blizzard"; + blizzard.mParticleEffect = "meshes\\blizzard.nif"; setFallbackWeather(blizzard,"blizzard"); } @@ -214,6 +213,8 @@ void WeatherManager::setResult(const String& weatherType) mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mSunColor = current.mSunDiscSunsetColor; + mResult.mParticleEffect = current.mParticleEffect; + mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; @@ -316,6 +317,8 @@ void WeatherManager::transition(float factor) mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor); mResult.mNight = current.mNight; + + mResult.mParticleEffect = current.mParticleEffect; } void WeatherManager::update(float duration) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 3e9df504b..4d0458249 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -58,6 +58,8 @@ namespace MWWorld float mNightFade; // fading factor for night skybox std::string mAmbientLoopSoundID; + + std::string mParticleEffect; }; @@ -119,7 +121,10 @@ namespace MWWorld // Rain sound effect std::string mRainLoopSoundID; - /// \todo disease chance + std::string mParticleEffect; + + // Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature + // is broken in the vanilla game and was disabled. }; /// From b52977e44c1685f8763b35cc64ff9fe525d9899a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 15:09:13 +0200 Subject: [PATCH 169/226] Add dummy Face implementation for now (Bug #1541) --- apps/openmw/mwscript/aiextensions.cpp | 13 +++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 4 +++- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 9a3387a00..d844138d6 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -477,6 +477,16 @@ namespace MWScript } }; + template + class OpFace : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime& runtime) + { + /// \todo implement + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment3 (Compiler::Ai::opcodeAIActivate, new OpAiActivate); @@ -538,6 +548,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting(3)); + + interpreter.installSegment5 (Compiler::Ai::opcodeFace, new OpFace); + interpreter.installSegment5 (Compiler::Ai::opcodeFaceExplicit, new OpFace); } } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 4b2a91a95..319feee0e 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -401,5 +401,7 @@ op 0x2000248: BetaComment, explicit op 0x2000249: OnMurder op 0x200024a: OnMurder, explicit op 0x200024b: ToggleMenus +op 0x200024c: Face +op 0x200024d: Face, explicit -opcodes 0x200024c-0x3ffffff unused +opcodes 0x200024e-0x3ffffff unused diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index f38c3320c..4cd77cf1d 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -70,6 +70,7 @@ namespace Compiler extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction ("getlos", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction("gettarget", 'l', "c", opcodeGetTarget, opcodeGetTargetExplicit); + extensions.registerInstruction("face", "llX", opcodeFace, opcodeFaceExplicit); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index b097a017b..85ac578f3 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -59,6 +59,8 @@ namespace Compiler const int opcodeStartCombatExplicit = 0x200023b; const int opcodeStopCombat = 0x200023c; const int opcodeStopCombatExplicit = 0x200023d; + const int opcodeFace = 0x200024c; + const int opcodeFaceExplicit = 0x200024d; } namespace Animation From 36135293e887ab412acdc7686e71d1f6e61b6ecf Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 15:29:36 +0200 Subject: [PATCH 170/226] Fix moving object from an inactive to another inactive cell --- apps/openmw/mwworld/worldimp.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4e4e3d3a..30024d310 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -967,7 +967,7 @@ namespace MWWorld Ogre::Vector3 vec(x, y, z); - CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; + CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); @@ -989,7 +989,9 @@ namespace MWWorld } else { - if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell)) + bool currCellActive = mWorldScene->isCellActive(*currCell); + bool newCellActive = mWorldScene->isCellActive(*newCell); + if (!currCellActive && newCellActive) { MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); mWorldScene->addObjectToScene(newPtr); @@ -1000,7 +1002,7 @@ namespace MWWorld } addContainerScripts(newPtr, newCell); } - else if (!mWorldScene->isCellActive(*newCell) && mWorldScene->isCellActive(*currCell)) + else if (!newCellActive && currCellActive) { mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); @@ -1011,7 +1013,9 @@ namespace MWWorld .copyToCell(ptr, *newCell); newPtr.getRefData().setBaseNode(0); } - else + else if (!currCellActive && !newCellActive) + ptr.getClass().copyToCell(ptr, *newCell); + else // both cells active { MWWorld::Ptr copy = ptr.getClass().copyToCell(ptr, *newCell, pos); From 1c157b86f6405a90d9a392bef372972ed0d449ba Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 18:14:27 +0200 Subject: [PATCH 171/226] Fix dead bodies blocking hits (again) --- libs/openengine/bullet/physic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d00441fdf..31530ebad 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -677,7 +677,7 @@ namespace Physic btCollisionObject *object) { DeepestNotMeContactTestResultCallback callback(filter, origin); - callback.m_collisionFilterGroup = 0xff; + callback.m_collisionFilterGroup = CollisionType_Actor; callback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; mDynamicsWorld->contactTest(object, callback); return std::make_pair(callback.mObject, callback.mContactPoint); From 693a097b21fef5f89b0601cc2f36a988c9d49248 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 18:37:38 +0200 Subject: [PATCH 172/226] Implement idlestorm animation (Feature #41) --- apps/openmw/mwbase/world.hpp | 6 ++++ apps/openmw/mwmechanics/character.cpp | 42 +++++++++++++++++++++++++++ apps/openmw/mwmechanics/character.hpp | 1 + apps/openmw/mwrender/sky.cpp | 2 +- apps/openmw/mwworld/refdata.hpp | 2 +- apps/openmw/mwworld/weather.cpp | 15 ++++++++++ apps/openmw/mwworld/weather.hpp | 15 +++++++++- apps/openmw/mwworld/worldimp.cpp | 17 +++++++++++ apps/openmw/mwworld/worldimp.hpp | 6 ++++ 9 files changed, 103 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f42edafd4..311d072c5 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -521,6 +521,12 @@ namespace MWBase const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; + + /// @see MWWorld::WeatherManager::isInStorm + virtual bool isInStorm() const = 0; + + /// @see MWWorld::WeatherManager::getStormDirection + virtual Ogre::Vector3 getStormDirection() const = 0; }; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ddfba51ce..81183da3c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -20,6 +20,7 @@ #include "character.hpp" #include +#include #include "movement.hpp" #include "npcstats.hpp" @@ -234,6 +235,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat 1.0f, "start", "stop", 0.0f, ~0ul); } + updateIdleStormState(); + if(force && mJumpState != JumpState_None) { std::string jump; @@ -550,6 +553,45 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) mPtr = ptr; } +void CharacterController::updateIdleStormState() +{ + bool inStormDirection = false; + if (MWBase::Environment::get().getWorld()->isInStorm()) + { + Ogre::Vector3 stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); + Ogre::Vector3 characterDirection = mPtr.getRefData().getBaseNode()->getOrientation().yAxis(); + inStormDirection = stormDirection.angleBetween(characterDirection) < Ogre::Degree(40); + } + if (inStormDirection && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm")) + { + float complete = 0; + mAnimation->getInfo("idlestorm", &complete); + + if (complete == 0) + mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, false, + 1.0f, "start", "loop start", 0.0f, 0); + else if (complete == 1) + mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, false, + 1.0f, "loop start", "loop stop", 0.0f, ~0ul); + } + else + { + if (mUpperBodyState == UpperCharState_Nothing) + { + if (mAnimation->isPlaying("idlestorm")) + { + if (mAnimation->getCurrentTime("idlestorm") < mAnimation->getTextKeyTime("idlestorm: loop stop")) + { + mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, true, + 1.0f, "loop stop", "stop", 0.0f, 0); + } + } + } + else + mAnimation->disable("idlestorm"); + } +} + bool CharacterController::updateCreatureState() { const MWWorld::Class &cls = mPtr.getClass(); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 5cefe13bc..59c20db8f 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -178,6 +178,7 @@ class CharacterController bool updateWeaponState(); bool updateCreatureState(); + void updateIdleStormState(); void updateVisibility(); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 545d34bdf..000a1d409 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -461,7 +461,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mParticleNode) { - mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(); + mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(Ogre::Vector3(0,0,100)); mParticleNode->setInheritOrientation(false); } diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 753230e7f..db66c091b 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -54,7 +54,7 @@ namespace MWWorld RefData(); /// @param cellRef Used to copy constant data such as position into this class where it can - /// be altered without effecting the original data. This makes it possible + /// be altered without affecting the original data. This makes it possible /// to reset the position as the orignal data is still held in the CellRef RefData (const ESM::CellRef& cellRef); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index ab5ec0004..df78711ad 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -63,6 +63,8 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name if (offset != std::string::npos) weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds"); + weather.mIsStorm = (name == "ashstorm" || name == "blight"); + mWeatherSettings[name] = weather; } @@ -213,6 +215,8 @@ void WeatherManager::setResult(const String& weatherType) mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mSunColor = current.mSunDiscSunsetColor; + mResult.mIsStorm = current.mIsStorm; + mResult.mParticleEffect = current.mParticleEffect; mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); @@ -318,6 +322,7 @@ void WeatherManager::transition(float factor) mResult.mNight = current.mNight; + mResult.mIsStorm = current.mIsStorm; mResult.mParticleEffect = current.mParticleEffect; } @@ -771,3 +776,13 @@ void WeatherManager::switchToNextWeather(bool instantly) } } } + +bool WeatherManager::isInStorm() const +{ + return mResult.mIsStorm; +} + +Ogre::Vector3 WeatherManager::getStormDirection() const +{ + return Ogre::Vector3(0,-1,0); +} diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 4d0458249..8aa3d2635 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -57,6 +57,8 @@ namespace MWWorld bool mNight; // use night skybox float mNightFade; // fading factor for night skybox + bool mIsStorm; + std::string mAmbientLoopSoundID; std::string mParticleEffect; @@ -102,7 +104,7 @@ namespace MWWorld // Duration of weather transition (in days) float mTransitionDelta; - // No idea what this one is used for? + // Used by scripts to animate signs, etc based on the wind (GetWindSpeed) float mWindSpeed; // Cloud animation speed multiplier @@ -121,6 +123,12 @@ namespace MWWorld // Rain sound effect std::string mRainLoopSoundID; + // Is this an ash storm / blight storm? This controls two things: + // - The particle node will be oriented so that the particles appear to come from the Red Mountain. (not implemented yet) + // - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation) + // Possible effect on movement speed? + bool mIsStorm; + std::string mParticleEffect; // Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature @@ -156,6 +164,11 @@ namespace MWWorld float getWindSpeed() const; + /// Are we in an ash or blight storm? + bool isInStorm() const; + + Ogre::Vector3 getStormDirection() const; + void advanceTime(double hours) { mTimePassed += hours*3600; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 30024d310..35042b3ff 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1998,6 +1998,22 @@ namespace MWWorld return 0.f; } + bool World::isInStorm() const + { + if (isCellExterior() || isCellQuasiExterior()) + return mWeatherManager->isInStorm(); + else + return false; + } + + Ogre::Vector3 World::getStormDirection() const + { + if (isCellExterior() || isCellQuasiExterior()) + return mWeatherManager->getStormDirection(); + else + return Ogre::Vector3(0,1,0); + } + void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) { const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); @@ -2313,6 +2329,7 @@ namespace MWWorld { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + // TODO: this only works for the player MWWorld::Ptr target = getFacedObject(); std::string selectedSpell = stats.getSpells().getSelectedSpell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8cb2ac18a..69b72b533 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -591,6 +591,12 @@ namespace MWWorld const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); + + /// @see MWWorld::WeatherManager::isInStorm + virtual bool isInStorm() const; + + /// @see MWWorld::WeatherManager::getStormDirection + virtual Ogre::Vector3 getStormDirection() const; }; } From e2743145485c125297aaa1d26078375f7b9b5ad6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 19:51:30 +0200 Subject: [PATCH 173/226] Ignore alpha modifier for particle materials This makes the tx_ash_flake.dds particles from ashcloud.nif appear. --- components/nifogre/material.cpp | 6 ++++-- components/nifogre/material.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index b1ae83107..cc06d65f3 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -106,7 +106,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents, bool disableLighting) + bool &needTangents, bool particleMaterial) { Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialPtr material = matMgr.getByName(name); @@ -245,8 +245,10 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, } } - if (disableLighting) + if (particleMaterial) { + alpha = 1.f; // Apparently ignored, might be overridden by particle vertex colors? + ambient = Ogre::Vector3(0.f); diffuse = Ogre::Vector3(0.f); specular = Ogre::Vector3(0.f); diff --git a/components/nifogre/material.hpp b/components/nifogre/material.hpp index 890255dcc..abe1982eb 100644 --- a/components/nifogre/material.hpp +++ b/components/nifogre/material.hpp @@ -49,7 +49,7 @@ public: const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents, bool disableLighting=false); + bool &needTangents, bool particleMaterial=false); }; } From 8a8ecce1e550efe9c94696b881024fd6ee10a57e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 00:11:25 +0200 Subject: [PATCH 174/226] Fix some uninitialized data written to savegames --- apps/openmw/mwstate/statemanagerimp.cpp | 7 +++++++ components/esm/cellref.cpp | 9 +++++---- components/esm/esmwriter.cpp | 5 +++++ components/esm/esmwriter.hpp | 5 +++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a459c87bb..a3604cc66 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -201,6 +201,13 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 writer.setFormat (ESM::Header::CurrentFormat); + + // all unused + writer.setVersion(0); + writer.setType(0); + writer.setAuthor(""); + writer.setDescription(""); + int recordCount = 1 // saved game header +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index d785fb2b6..84c638d9c 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -108,13 +108,13 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons esm.writeHNT("NAM9", mGoldValue); } - if (mTeleport && !inInventory) + if (!inInventory && mTeleport) { esm.writeHNT("DODT", mDoorDest); esm.writeHNOCString("DNAM", mDestCell); } - if (mLockLevel != 0 && !inInventory) { + if (!inInventory && mLockLevel != 0) { esm.writeHNT("FLTV", mLockLevel); } @@ -127,13 +127,13 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked); - if (mFltv != 0 && !inInventory) + if (!inInventory && mFltv != 0) esm.writeHNT("FLTV", mFltv); if (!inInventory) esm.writeHNT("DATA", mPos, 24); - if (mNam0 != 0 && !inInventory) + if (!inInventory && mNam0 != 0) esm.writeHNT("NAM0", mNam0); } @@ -158,6 +158,7 @@ void ESM::CellRef::blank() mReferenceBlocked = 0; mFltv = 0; mNam0 = 0; + mTeleport = false; for (int i=0; i<3; ++i) { diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 9d8d943d9..06572ce8f 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -18,6 +18,11 @@ namespace ESM mHeader.mData.version = ver; } + void ESMWriter::setType(int type) + { + mHeader.mData.type = type; + } + void ESMWriter::setAuthor(const std::string& auth) { mHeader.mData.author.assign (auth); diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index ca4f42217..e57c6e45d 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -25,10 +25,15 @@ class ESMWriter ESMWriter(); unsigned int getVersion() const; + + // Set various header data (ESM::Header::Data). All of the below functions must be called before writing, + // otherwise this data will be left uninitialized. void setVersion(unsigned int ver = 0x3fa66666); + void setType(int type); void setEncoder(ToUTF8::Utf8Encoder *encoding); void setAuthor(const std::string& author); void setDescription(const std::string& desc); + // Set the record count for writing it in the file header void setRecordCount (int count); // Counts how many records we have actually written. From 7f1d0fc2a2ef53c82e8696634217a742ef2f6454 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 00:11:51 +0200 Subject: [PATCH 175/226] Fix disposition for npcs in same faction, by Hrnchamd --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 652f5363e..6e515142d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -501,7 +501,8 @@ namespace MWMechanics { if (!playerStats.getExpelled(npcFaction)) { - reaction = playerStats.getFactionReputation(npcFaction); + // faction reaction towards itself. yes, that exists + reaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, npcFaction); rank = playerStats.getFactionRanks().find(npcFaction)->second; } From 3d9bdad8ba55fb525f918b540dcbc11d847a2558 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 00:51:02 +0200 Subject: [PATCH 176/226] Correct run speed for creatures (by Hrnchamd - Fixes #1136) --- apps/openmw/mwclass/creature.cpp | 4 +-- apps/openmw/mwmechanics/character.cpp | 50 ++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1f286f7e6..feb6a2714 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -537,8 +537,8 @@ namespace MWClass bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); - float runSpeed = walkSpeed*6; - runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this + // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp) + float runSpeed = walkSpeed; float moveSpeed; if(normalizedEncumbrance >= 1.0f) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 81183da3c..5f8013b14 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -55,6 +55,43 @@ std::string getBestAttack (const ESM::Weapon* weapon) return "thrust"; } +// Converts a movement Run state to its equivalent Walk state. +MWMechanics::CharacterState runStateToWalkState (MWMechanics::CharacterState state) +{ + using namespace MWMechanics; + CharacterState ret = state; + switch (state) + { + case CharState_RunForward: + ret = CharState_WalkForward; + break; + case CharState_RunBack: + ret = CharState_WalkBack; + break; + case CharState_RunLeft: + ret = CharState_WalkLeft; + break; + case CharState_RunRight: + ret = CharState_WalkRight; + break; + case CharState_SwimRunForward: + ret = CharState_SwimWalkForward; + break; + case CharState_SwimRunBack: + ret = CharState_SwimWalkBack; + break; + case CharState_SwimRunLeft: + ret = CharState_SwimWalkLeft; + break; + case CharState_SwimRunRight: + ret = CharState_SwimWalkRight; + break; + default: + break; + } + return ret; +} + } namespace MWMechanics @@ -323,7 +360,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); - if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity + // even if we are running. This must be replicated, otherwise the observed speed would differ drastically. + std::string anim = mCurrentMovement; + if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() + && !(mPtr.get()->mBase->mFlags & ESM::Creature::Flies)) + { + CharacterState walkState = runStateToWalkState(mMovementState); + const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState)); + anim = stateinfo->groupname; + } + + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f) { mMovementAnimVelocity = vel; speedmult = mMovementSpeed / vel; From b259c5def26cc371b13fdb048591f3a257981f59 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 01:12:57 +0200 Subject: [PATCH 177/226] Make GetFactionReaction garbage argument optional --- apps/openmw/mwscript/dialogueextensions.cpp | 3 --- components/compiler/extensions0.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 45eeccf18..ecaf4253e 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -227,9 +227,6 @@ namespace MWScript std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - // ignore extra garbage argument - runtime.pop(); - runtime.push(MWBase::Environment::get().getDialogueManager() ->getFactionReaction(faction1, faction2)); } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 4cd77cf1d..e095958d1 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -180,7 +180,7 @@ namespace Compiler extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, opcodeSameFactionExplicit); extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction); - extensions.registerFunction("getfactionreaction", 'l', "ccl", opcodeGetFactionReaction); + extensions.registerFunction("getfactionreaction", 'l', "ccX", opcodeGetFactionReaction); extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit); } } From ee098de0a69c6fbf538fe1841598715e82c3f53a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 01:14:00 +0200 Subject: [PATCH 178/226] Don't ignore lighting values of particles not attached to a character --- apps/openmw/mwrender/animation.cpp | 68 +++++++++++++++++++++++++----- components/nifogre/material.cpp | 5 --- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 78703172e..83fe766a9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1169,20 +1169,68 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationTime())); } - if (!texture.empty()) + + // Do some manual adjustments on the created entities/particle systems + + // It looks like vanilla MW totally ignores lighting settings for effects attached to characters. + // If we don't do this, some effects will look way too dark depending on the environment + // (e.g. magic_cast_dst.nif). They were clearly meant to use emissive lighting. + // We used to have this hack in the NIF material loader, but for effects not attached to characters + // (e.g. ash storms) the lighting settings do seem to be in use. Is there maybe a flag we have missed? + Ogre::ColourValue ambient = Ogre::ColourValue(0.f, 0.f, 0.f); + Ogre::ColourValue diffuse = Ogre::ColourValue(0.f, 0.f, 0.f); + Ogre::ColourValue specular = Ogre::ColourValue(0.f, 0.f, 0.f); + Ogre::ColourValue emissive = Ogre::ColourValue(1.f, 1.f, 1.f); + for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) { - for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) + Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; + + Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); + + for (int t=0; tgetNumTechniques(); ++t) { - Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; - - Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); - - for (int t=0; tgetNumTechniques(); ++t) + Ogre::Technique* tech = mat->getTechnique(t); + for (int p=0; pgetNumPasses(); ++p) { - Ogre::Technique* tech = mat->getTechnique(t); - for (int p=0; pgetNumPasses(); ++p) + Ogre::Pass* pass = tech->getPass(p); + + pass->setAmbient(ambient); + pass->setDiffuse(diffuse); + pass->setSpecular(specular); + pass->setEmissive(emissive); + + if (!texture.empty()) + { + for (int tex=0; texgetNumTextureUnitStates(); ++tex) + { + Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); + tus->setTextureName("textures\\" + texture); + } + } + } + } + } + for(size_t i = 0;i < params.mObjects->mEntities.size(); ++i) + { + Ogre::Entity* ent = params.mObjects->mEntities[i]; + if (ent == params.mObjects->mSkelBase) + continue; + Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(ent); + + for (int t=0; tgetNumTechniques(); ++t) + { + Ogre::Technique* tech = mat->getTechnique(t); + for (int p=0; pgetNumPasses(); ++p) + { + Ogre::Pass* pass = tech->getPass(p); + + pass->setAmbient(ambient); + pass->setDiffuse(diffuse); + pass->setSpecular(specular); + pass->setEmissive(emissive); + + if (!texture.empty()) { - Ogre::Pass* pass = tech->getPass(p); for (int tex=0; texgetNumTextureUnitStates(); ++tex) { Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index cc06d65f3..44831c13b 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -248,11 +248,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, if (particleMaterial) { alpha = 1.f; // Apparently ignored, might be overridden by particle vertex colors? - - ambient = Ogre::Vector3(0.f); - diffuse = Ogre::Vector3(0.f); - specular = Ogre::Vector3(0.f); - emissive = Ogre::Vector3(1.f); } { From ec64f1a53a5ab38f39b5879acfe8f393c92e2afa Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 02:46:39 +0200 Subject: [PATCH 179/226] Reset accumulation root when its animation finishes Fixes a position flicker after standing up from knockdown. --- apps/openmw/mwrender/animation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 83fe766a9..8bf2160e3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1027,7 +1027,11 @@ Ogre::Vector3 Animation::runAnimation(float duration) if(!state.mPlaying && state.mAutoDisable) { + if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) + mAccumRoot->setPosition(0.f,0.f,0.f); + mStates.erase(stateiter++); + resetActiveGroups(); } else From 2bcbc6ab7d630dcad9e5f73140a16db05edbcda9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 14:52:12 +0200 Subject: [PATCH 180/226] Potential crash fix, either way should have a better error message now --- apps/openmw/mwworld/physicssystem.cpp | 5 +++++ components/nifbullet/bulletnifloader.cpp | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7f26fa75a..c4b904ca9 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -45,7 +45,12 @@ void animateCollisionShapes (std::mapsearchPtrViaHandle(it->first->mName); + if (ptr.isEmpty()) // Shouldn't happen + throw std::runtime_error("can't find Ptr"); + MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if (!animation) // Shouldn't happen either, since keyframe-controlled objects are not batched in StaticGeometry + throw std::runtime_error("can't find Animation for " + ptr.getCellRef().getRefId()); OEngine::Physic::AnimatedShapeInstance& instance = it->second; diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 64febc3c2..7f78c04aa 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -181,7 +181,8 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, // the flags we currently use, at least. flags |= node->flags; - if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController) + if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController + && (node->controller->flags & Nif::NiNode::ControllerFlag_Active)) isAnimated = true; if (!raycasting) From 09926a86cbfd0032d3c4b401f8aaf0aecccd17ff Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 18:10:26 +0200 Subject: [PATCH 181/226] Fix comparing outdated listener position with up-to-date cell (Fixes #1499) --- apps/openmw/mwsound/soundmanagerimp.cpp | 13 ++++++++----- apps/openmw/mwsound/soundmanagerimp.hpp | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 35b8579d7..200c50e69 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -42,6 +42,7 @@ namespace MWSound , mListenerPos(0,0,0) , mListenerDir(1,0,0) , mListenerUp(0,0,1) + , mListenerUnderwater(false) { if(!useSound) return; @@ -584,12 +585,8 @@ namespace MWSound if(!isMusicPlaying()) startRandomTitle(); - MWWorld::Ptr player = - MWBase::Environment::get().getWorld()->getPlayerPtr(); - const ESM::Cell *cell = player.getCell()->getCell(); - Environment env = Env_Normal; - if((cell->mData.mFlags&cell->HasWater) && mListenerPos.z < cell->mWater) + if (mListenerUnderwater) { env = Env_Underwater; //play underwater sound @@ -682,6 +679,12 @@ namespace MWSound mListenerPos = pos; mListenerDir = dir; mListenerUp = up; + + MWWorld::Ptr player = + MWBase::Environment::get().getWorld()->getPlayerPtr(); + const ESM::Cell *cell = player.getCell()->getCell(); + + mListenerUnderwater = ((cell->mData.mFlags&cell->HasWater) && mListenerPos.z < cell->mWater); } void SoundManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 558b6966a..380cfe255 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -50,6 +50,7 @@ namespace MWSound MWBase::SoundPtr mUnderwaterSound; + bool mListenerUnderwater; Ogre::Vector3 mListenerPos; Ogre::Vector3 mListenerDir; Ogre::Vector3 mListenerUp; From 4a6dbe6f8971b49bf2a426a7d0d57dcfd15d9e7f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 18:15:56 +0200 Subject: [PATCH 182/226] Use the actor's collision group/mask for traces Fixes dead bodies being lifted up when standing below them. --- libs/openengine/bullet/physic.cpp | 2 +- libs/openengine/bullet/trace.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 31530ebad..6bc544af4 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -180,7 +180,7 @@ namespace Physic { mEngine->mDynamicsWorld->removeRigidBody(mBody); mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, - CollisionType_Raycasting); + CollisionType_World|CollisionType_HeightMap); } void PhysicActor::enableCollisionBody() diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 46db4c6b8..ad140f1f7 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -65,9 +65,9 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, to.setOrigin(btend); ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); - newTraceCallback.m_collisionFilterGroup = CollisionType_Actor; - newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | - CollisionType_Actor; + // Inherit the actor's collision group and mask + newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup; + newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask; btCollisionShape *shape = actor->getCollisionShape(); assert(shape->isConvex()); @@ -100,9 +100,9 @@ void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Og btTransform to(trans.getBasis(), btend); ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionBody(), btstart-btend, btScalar(0.0)); - newTraceCallback.m_collisionFilterGroup = CollisionType_Actor; - newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | - CollisionType_Actor; + // Inherit the actor's collision group and mask + newTraceCallback.m_collisionFilterGroup = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterGroup; + newTraceCallback.m_collisionFilterMask = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterMask; btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z); From f6a568c995c46cc5f8490e2358afc542561e9399 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 18:20:21 +0200 Subject: [PATCH 183/226] Implement rain (Feature #41) --- apps/openmw/mwrender/sky.cpp | 83 ++++++++++++++++++++++++++++++++- apps/openmw/mwrender/sky.hpp | 13 ++++++ apps/openmw/mwworld/weather.cpp | 32 +++++++++++-- apps/openmw/mwworld/weather.hpp | 15 ++++++ 4 files changed, 139 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 000a1d409..2bbc95c89 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -236,6 +236,10 @@ SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) , mCloudAnimationTimer(0.f) , mMoonRed(false) , mParticleNode(NULL) + , mRainEnabled(false) + , mRainTimer(0) + , mRainSpeed(0) + , mRainFrequency(1) { mSceneMgr = root->getCreator(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); @@ -351,6 +355,7 @@ void SkyManager::create() SkyManager::~SkyManager() { + clearRain(); delete mSun; delete mSunGlare; delete mMasser; @@ -369,6 +374,66 @@ int SkyManager::getSecundaPhase() const return mSecunda->getPhaseInt(); } +void SkyManager::clearRain() +{ + for (std::map::iterator it = mRainModels.begin(); it != mRainModels.end();) + { + it->second.setNull(); + Ogre::SceneNode* parent = it->first->getParentSceneNode(); + mSceneMgr->destroySceneNode(it->first); + mSceneMgr->destroySceneNode(parent); + mRainModels.erase(it++); + } +} + +void SkyManager::updateRain(float dt) +{ + // Move existing rain + // Note: if rain gets disabled, we let the existing rain drops finish falling down. + float minHeight = 200; + for (std::map::iterator it = mRainModels.begin(); it != mRainModels.end();) + { + Ogre::Vector3 pos = it->first->getPosition(); + pos.z -= mRainSpeed * dt; + it->first->setPosition(pos); + if (pos.z < -minHeight) + { + it->second.setNull(); + Ogre::SceneNode* parent = it->first->getParentSceneNode(); + mSceneMgr->destroySceneNode(it->first); + mSceneMgr->destroySceneNode(parent); + mRainModels.erase(it++); + } + else + ++it; + } + + // Spawn new rain + float rainFrequency = mRainFrequency; + float startHeight = 700; + if (mRainEnabled) + { + mRainTimer += dt; + if (mRainTimer >= 1.f/rainFrequency) + { + mRainTimer = 0; + + const float rangeRandom = 100; + float xOffs = (std::rand()/(RAND_MAX+1.0)) * rangeRandom - (rangeRandom/2); + float yOffs = (std::rand()/(RAND_MAX+1.0)) * rangeRandom - (rangeRandom/2); + Ogre::SceneNode* sceneNode = mCamera->getParentSceneNode()->createChildSceneNode(); + sceneNode->setInheritOrientation(false); + + // Create a separate node to control the offset, since a node with setInheritOrientation(false) will still + // consider the orientation of the parent node for its position, just not for its orientation + Ogre::SceneNode* offsetNode = sceneNode->createChildSceneNode(Ogre::Vector3(xOffs,yOffs,startHeight)); + + NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(offsetNode, mRainEffect); + mRainModels[offsetNode] = objects; + } + } +} + void SkyManager::update(float duration) { if (!mEnabled) return; @@ -380,6 +445,8 @@ void SkyManager::update(float duration) mParticle->mControllers[i].update(); } + updateRain(duration); + // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed; sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", @@ -430,13 +497,22 @@ void SkyManager::enable() if (!mCreated) create(); + if (mParticleNode) + mParticleNode->setVisible(true); + mRootNode->setVisible(true); mEnabled = true; } void SkyManager::disable() { + if (mParticleNode) + mParticleNode->setVisible(false); + + clearRain(); + mRootNode->setVisible(false); + mEnabled = false; } @@ -449,6 +525,11 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mCreated) return; + mRainEffect = weather.mRainEffect; + mRainEnabled = !mRainEffect.empty(); + mRainFrequency = weather.mRainFrequency; + mRainSpeed = weather.mRainSpeed; + if (mCurrentParticleEffect != weather.mParticleEffect) { mCurrentParticleEffect = weather.mParticleEffect; @@ -461,7 +542,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mParticleNode) { - mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(Ogre::Vector3(0,0,100)); + mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(); mParticleNode->setInheritOrientation(false); } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 07edc461e..f81a42782 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -148,6 +148,8 @@ namespace MWRender void sunDisable(); + void setRainSpeed(float speed); + void setSunDirection(const Ogre::Vector3& direction); void setMasserDirection(const Ogre::Vector3& direction); @@ -176,6 +178,9 @@ namespace MWRender void create(); ///< no need to call this, automatically done on first enable() + void updateRain(float dt); + void clearRain(); + bool mCreated; bool mMoonRed; @@ -203,6 +208,9 @@ namespace MWRender Ogre::SceneNode* mParticleNode; NifOgre::ObjectScenePtr mParticle; + std::map mRainModels; + float mRainTimer; + // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; @@ -223,6 +231,11 @@ namespace MWRender float mGlare; // target float mGlareFade; // actual + bool mRainEnabled; + std::string mRainEffect; + float mRainSpeed; + float mRainFrequency; + bool mEnabled; bool mSunEnabled; bool mMasserEnabled; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index df78711ad..6050e0c29 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -59,6 +59,21 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture"); + + bool usesPrecip = mFallback->getFallbackBool("Weather_"+upper+"_Using_Precip"); + if (usesPrecip) + weather.mRainEffect = "meshes\\raindrop.nif"; + weather.mRainSpeed = mRainSpeed; + weather.mRainFrequency = mFallback->getFallbackFloat("Weather_"+upper+"_Rain_Entrance_Speed"); + /* +Unhandled: +Rain Diameter=600 ? +Rain Height Min=200 ? +Rain Height Max=700 ? +Rain Threshold=0.6 ? +Max Raindrops=650 ? +*/ + size_t offset = weather.mCloudTexture.find(".tga"); if (offset != std::string::npos) weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds"); @@ -100,7 +115,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa mHour(14), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), - mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering) + mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering), mIsStorm(false) { //Globals mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); @@ -117,6 +132,8 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa mThunderThreshold = mFallback->getFallbackFloat("Weather_Thunderstorm_Thunder_Threshold"); mThunderSoundDelay = 0.25; + mRainSpeed = mFallback->getFallbackFloat("Weather_Precip_Gravity"); + //Some useful values /* TODO: Use pre-sunrise_time, pre-sunset_time, * post-sunrise_time, and post-sunset_time to better @@ -140,10 +157,12 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa Weather thunderstorm; thunderstorm.mRainLoopSoundID = "rain heavy"; + thunderstorm.mRainEffect = "meshes\\raindrop.nif"; setFallbackWeather(thunderstorm,"thunderstorm"); Weather rain; rain.mRainLoopSoundID = "rain"; + rain.mRainEffect = "meshes\\raindrop.nif"; setFallbackWeather(rain,"rain"); Weather overcast; @@ -217,7 +236,11 @@ void WeatherManager::setResult(const String& weatherType) mResult.mIsStorm = current.mIsStorm; + mResult.mRainSpeed = current.mRainSpeed; + mResult.mRainFrequency = current.mRainFrequency; + mResult.mParticleEffect = current.mParticleEffect; + mResult.mRainEffect = current.mRainEffect; mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); @@ -324,6 +347,9 @@ void WeatherManager::transition(float factor) mResult.mIsStorm = current.mIsStorm; mResult.mParticleEffect = current.mParticleEffect; + mResult.mRainEffect = current.mRainEffect; + mResult.mRainSpeed = current.mRainSpeed; + mResult.mRainFrequency = current.mRainFrequency; } void WeatherManager::update(float duration) @@ -361,6 +387,7 @@ void WeatherManager::update(float duration) setResult(mCurrentWeather); mWindSpeed = mResult.mWindSpeed; + mIsStorm = mResult.mIsStorm; mRendering->configureFog(mResult.mFogDepth, mResult.mFogColor); @@ -499,7 +526,6 @@ void WeatherManager::update(float duration) mRendering->getSkyManager()->setWeather(mResult); - // Play sounds if (mNextWeather == "") { @@ -779,7 +805,7 @@ void WeatherManager::switchToNextWeather(bool instantly) bool WeatherManager::isInStorm() const { - return mResult.mIsStorm; + return mIsStorm; } Ogre::Vector3 WeatherManager::getStormDirection() const diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 8aa3d2635..9693014a9 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -62,6 +62,10 @@ namespace MWWorld std::string mAmbientLoopSoundID; std::string mParticleEffect; + + std::string mRainEffect; + float mRainSpeed; + float mRainFrequency; }; @@ -129,8 +133,17 @@ namespace MWWorld // Possible effect on movement speed? bool mIsStorm; + // How fast does rain travel down? + // In Morrowind.ini this is set globally, but we may want to change it per weather later. + float mRainSpeed; + + // How often does a new rain mesh spawn? + float mRainFrequency; + std::string mParticleEffect; + std::string mRainEffect; + // Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature // is broken in the vanilla game and was disabled. }; @@ -188,6 +201,7 @@ namespace MWWorld private: float mHour; float mWindSpeed; + bool mIsStorm; MWWorld::Fallback* mFallback; void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; @@ -226,6 +240,7 @@ namespace MWWorld typedef std::map > RegionModMap; RegionModMap mRegionMods; + float mRainSpeed; float mSunriseTime; float mSunsetTime; float mSunriseDuration; From 5bd2f30a99dfe5e60fd4f4684826b9df35592a88 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 18:32:06 +0200 Subject: [PATCH 184/226] CellRef uninitialized fixes --- apps/openmw/mwworld/manualref.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 3842e7ff1..b77257e47 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -35,6 +35,8 @@ namespace MWWorld cellRef.mTeleport = false; cellRef.mLockLevel = 0; cellRef.mReferenceBlocked = 0; + cellRef.mFltv = 0; + cellRef.mNam0 = 0; LiveCellRef ref(cellRef, base); From b25026299cff8dc745e5efbdfbc4b2f2f3e94cd0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 20:33:03 +0200 Subject: [PATCH 185/226] Why was Hrnchamd not in credits? --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 791db0433..5c757f957 100644 --- a/credits.txt +++ b/credits.txt @@ -106,6 +106,7 @@ sir_herrbatka - Forum Administrator Formula Research: +Hrnchamd Epsilon fragonard Greendogo From 2d17d8f61a4a8df792e4cb19d0b4e496d4ce4afc Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 01:19:03 +0200 Subject: [PATCH 186/226] Savegame: start in (0,0) cell if player's cell no longer exists This happens frequently when saves are loaded that were created in a different MW language. --- apps/openmw/mwworld/player.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index b88483bfe..12908ca9d 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -246,7 +246,15 @@ namespace MWWorld MWBase::World& world = *MWBase::Environment::get().getWorld(); - mCellStore = world.getCell (player.mCellId); + try + { + mCellStore = world.getCell (player.mCellId); + } + catch (...) + { + // Cell no longer exists. Place the player in a default cell. + mCellStore = world.getExterior(0,0); + } if (!player.mBirthsign.empty() && !world.getStore().get().search (player.mBirthsign)) From 15b486e1499fad91875722e03589cf2cb6617b81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 01:21:15 +0200 Subject: [PATCH 187/226] Don't trigger changed flag if a script with no locals is configured --- apps/openmw/mwscript/locals.cpp | 5 +++++ apps/openmw/mwscript/locals.hpp | 3 +++ apps/openmw/mwworld/refdata.cpp | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 094fe085a..177536fe9 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -21,6 +21,11 @@ namespace MWScript mFloats.resize (script.mData.mNumFloats, 0); } + bool Locals::isEmpty() const + { + return (mShorts.empty() && mLongs.empty() && mFloats.empty()); + } + int Locals::getIntVar(const std::string &script, const std::string &var) { Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script); diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp index d17d1be2d..1e8c6e12a 100644 --- a/apps/openmw/mwscript/locals.hpp +++ b/apps/openmw/mwscript/locals.hpp @@ -20,6 +20,9 @@ namespace MWScript std::vector mLongs; std::vector mFloats; + /// Are there any locals? + bool isEmpty() const; + void configure (const ESM::Script& script); bool setVarByInt(const std::string& script, const std::string& var, int val); int getIntVar (const std::string& script, const std::string& var); ///< if var does not exist, returns 0 diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 3b7521e8d..f4bc64b70 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -152,7 +152,8 @@ namespace MWWorld { mLocals.configure (script); mHasLocals = true; - mChanged = true; + if (!mLocals.isEmpty()) + mChanged = true; } } From 697329f12812c21efa74fa7a15f46e95af454022 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 03:33:09 +0200 Subject: [PATCH 188/226] Fix crash for in_dagoth_bridge00.nif (Fixes #1561) This one is causing trouble by using an unnamed node, will need some more work. --- apps/openmw/mwworld/physicssystem.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index c4b904ca9..87ec39707 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -60,6 +60,12 @@ void animateCollisionShapes (std::mapgetNode(shapeIt->first); + // FIXME: this will happen for nodes with empty names. Ogre's SkeletonInstance::cloneBoneAndChildren + // will assign an auto-generated name if the bone name was empty. We could use the bone handle instead of + // the bone name, but that is a bit tricky to retrieve. + if (bone == NULL) + continue; + btCompoundShape* compound = dynamic_cast(instance.mCompound); btTransform trans; From bd252d0aec944e6ced8e4f723647f486f52ae3d8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 26 Jun 2014 11:41:21 +0200 Subject: [PATCH 189/226] display record count in loading window --- apps/opencs/model/doc/documentmanager.cpp | 4 +-- apps/opencs/model/doc/documentmanager.hpp | 5 ++-- apps/opencs/model/doc/loader.cpp | 14 +++++++--- apps/opencs/model/doc/loader.hpp | 6 +++-- apps/opencs/view/doc/loader.cpp | 31 +++++++++++++++-------- apps/opencs/view/doc/loader.hpp | 10 +++++--- apps/opencs/view/doc/viewmanager.cpp | 4 +-- 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 096864b77..4020a8f72 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -31,8 +31,8 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& con &mLoader, SLOT (loadDocument (CSMDoc::Document *))); connect (&mLoader, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)), this, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int))); - connect (&mLoader, SIGNAL (nextRecord (CSMDoc::Document *)), - this, SIGNAL (nextRecord (CSMDoc::Document *))); + connect (&mLoader, SIGNAL (nextRecord (CSMDoc::Document *, int)), + this, SIGNAL (nextRecord (CSMDoc::Document *, int))); connect (this, SIGNAL (cancelLoading (CSMDoc::Document *)), &mLoader, SLOT (abortLoading (CSMDoc::Document *))); connect (&mLoader, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)), diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 9b675826a..d6824112b 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -79,9 +79,10 @@ namespace CSMDoc void loadingStopped (CSMDoc::Document *document, bool completed, const std::string& error); - void nextStage (CSMDoc::Document *document, const std::string& name, int steps); + void nextStage (CSMDoc::Document *document, const std::string& name, + int totalRecords); - void nextRecord (CSMDoc::Document *document); + void nextRecord (CSMDoc::Document *document, int records); void cancelLoading (CSMDoc::Document *document); diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index 6fb10426c..ca9c45696 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -8,7 +8,7 @@ #include "document.hpp" #include "state.hpp" -CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLeft (false) {} +CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (false) {} CSMDoc::Loader::Loader() @@ -59,17 +59,21 @@ void CSMDoc::Loader::load() iter->second.mRecordsLeft = false; break; } + else + ++(iter->second.mRecordsLoaded); CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0); + { // silence a g++ warning for (CSMDoc::Stage::Messages::const_iterator iter (messages.begin()); iter!=messages.end(); ++iter) { document->getReport (log)->add (iter->first, iter->second); emit loadMessage (document, iter->second); } + } - emit nextRecord (document); + emit nextRecord (document, iter->second.mRecordsLoaded); return; } @@ -80,15 +84,17 @@ void CSMDoc::Loader::load() int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false); iter->second.mRecordsLeft = true; + iter->second.mRecordsLoaded = 0; - emit nextStage (document, path.filename().string(), steps/batchingSize); + emit nextStage (document, path.filename().string(), steps); } else if (iter->second.mFile==size) { int steps = document->getData().startLoading (document->getProjectPath(), false, true); iter->second.mRecordsLeft = true; + iter->second.mRecordsLoaded = 0; - emit nextStage (document, "Project File", steps/batchingSize); + emit nextStage (document, "Project File", steps); } else { diff --git a/apps/opencs/model/doc/loader.hpp b/apps/opencs/model/doc/loader.hpp index c276e14ff..d1ee38f9f 100644 --- a/apps/opencs/model/doc/loader.hpp +++ b/apps/opencs/model/doc/loader.hpp @@ -18,6 +18,7 @@ namespace CSMDoc struct Stage { int mFile; + int mRecordsLoaded; bool mRecordsLeft; Stage(); @@ -56,9 +57,10 @@ namespace CSMDoc ///< Document load has been interrupted either because of a call to abortLoading /// or a problem during loading). In the former case error will be an empty string. - void nextStage (CSMDoc::Document *document, const std::string& name, int steps); + void nextStage (CSMDoc::Document *document, const std::string& name, + int totalRecords); - void nextRecord (CSMDoc::Document *document); + void nextRecord (CSMDoc::Document *document, int records); ///< \note This signal is only given once per group of records. The group size is /// approximately the total number of records divided by the steps value of the /// previous nextStage signal. diff --git a/apps/opencs/view/doc/loader.cpp b/apps/opencs/view/doc/loader.cpp index 7e4754ddf..ca7c93f9d 100644 --- a/apps/opencs/view/doc/loader.cpp +++ b/apps/opencs/view/doc/loader.cpp @@ -45,7 +45,7 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) mFileProgress->setValue (0); // record progress - mLayout->addWidget (new QLabel ("Records", this)); + mLayout->addWidget (mRecords = new QLabel ("Records", this)); mRecordProgress = new QProgressBar (this); @@ -75,22 +75,30 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel())); } -void CSVDoc::LoadingDocument::nextStage (const std::string& name, int steps) +void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords) { mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str())); mFileProgress->setValue (mFileProgress->value()+1); mRecordProgress->setValue (0); - mRecordProgress->setMaximum (steps>0 ? steps : 1); + mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1); + + mTotalRecords = totalRecords; } -void CSVDoc::LoadingDocument::nextRecord() +void CSVDoc::LoadingDocument::nextRecord (int records) { - int value = mRecordProgress->value()+1; + if (records<=mTotalRecords) + { + mRecordProgress->setValue (records); - if (value<=mRecordProgress->maximum()) - mRecordProgress->setValue (value); + std::ostringstream stream; + + stream << "Records: " << records << " of " << mTotalRecords; + + mRecords->setText (QString::fromUtf8 (stream.str().c_str())); + } } void CSVDoc::LoadingDocument::abort (const std::string& error) @@ -168,20 +176,21 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed, } } -void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name, int steps) +void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name, + int totalRecords) { std::map::iterator iter = mDocuments.find (document); if (iter!=mDocuments.end()) - iter->second->nextStage (name, steps); + iter->second->nextStage (name, totalRecords); } -void CSVDoc::Loader::nextRecord (CSMDoc::Document *document) +void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records) { std::map::iterator iter = mDocuments.find (document); if (iter!=mDocuments.end()) - iter->second->nextRecord(); + iter->second->nextRecord (records); } void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& message) diff --git a/apps/opencs/view/doc/loader.hpp b/apps/opencs/view/doc/loader.hpp index 69a8b48ba..e004007c9 100644 --- a/apps/opencs/view/doc/loader.hpp +++ b/apps/opencs/view/doc/loader.hpp @@ -26,6 +26,7 @@ namespace CSVDoc CSMDoc::Document *mDocument; QLabel *mFile; + QLabel *mRecords; QProgressBar *mFileProgress; QProgressBar *mRecordProgress; bool mAborted; @@ -33,6 +34,7 @@ namespace CSVDoc QLabel *mError; QListWidget *mMessages; QVBoxLayout *mLayout; + int mTotalRecords; private: @@ -42,9 +44,9 @@ namespace CSVDoc LoadingDocument (CSMDoc::Document *document); - void nextStage (const std::string& name, int steps); + void nextStage (const std::string& name, int totalRecords); - void nextRecord(); + void nextRecord (int records); void abort (const std::string& error); @@ -88,9 +90,9 @@ namespace CSVDoc void loadingStopped (CSMDoc::Document *document, bool completed, const std::string& error); - void nextStage (CSMDoc::Document *document, const std::string& name, int steps); + void nextStage (CSMDoc::Document *document, const std::string& name, int totalRecords); - void nextRecord (CSMDoc::Document *document); + void nextRecord (CSMDoc::Document *document, int records); void loadMessage (CSMDoc::Document *document, const std::string& message); }; diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 02f6a5467..f4d6fc6cb 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -97,8 +97,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) &mLoader, SLOT (nextStage (CSMDoc::Document *, const std::string&, int))); connect ( - &mDocumentManager, SIGNAL (nextRecord (CSMDoc::Document *)), - &mLoader, SLOT (nextRecord (CSMDoc::Document *))); + &mDocumentManager, SIGNAL (nextRecord (CSMDoc::Document *, int)), + &mLoader, SLOT (nextRecord (CSMDoc::Document *, int))); connect ( &mDocumentManager, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)), From 06e89d8bd30f04af52d3c0e1fdad485faa6cffb1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 26 Jun 2014 11:43:21 +0200 Subject: [PATCH 190/226] reduced loading batch size (making the the loading progress bars a bit more responsive --- apps/opencs/model/doc/loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index ca9c45696..712deb9df 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -46,7 +46,7 @@ void CSMDoc::Loader::load() bool done = false; - const int batchingSize = 100; + const int batchingSize = 50; try { From e5254ff8efe8571f31bb0ffc06526c7e611a4736 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 14:26:53 +0200 Subject: [PATCH 191/226] Set render queue for rain --- apps/openmw/mwrender/sky.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 66a99feb7..67e301fdc 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -429,6 +429,16 @@ void SkyManager::updateRain(float dt) Ogre::SceneNode* offsetNode = sceneNode->createChildSceneNode(Ogre::Vector3(xOffs,yOffs,startHeight)); NifOgre::ObjectScenePtr objects = NifOgre::Loader::createObjects(offsetNode, mRainEffect); + for (unsigned int i=0; imEntities.size(); ++i) + { + objects->mEntities[i]->setRenderQueueGroup(RQG_Alpha); + objects->mEntities[i]->setVisibilityFlags(RV_Sky); + } + for (unsigned int i=0; imParticles.size(); ++i) + { + objects->mParticles[i]->setRenderQueueGroup(RQG_Alpha); + objects->mParticles[i]->setVisibilityFlags(RV_Sky); + } mRainModels[offsetNode] = objects; } } From 869fa08a328450f5edc390fcdbb2cdec6873df36 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 14:30:08 +0200 Subject: [PATCH 192/226] Fix disarming a trap when key is used (Fixes #1556) --- apps/openmw/mwclass/door.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 0a5b774f5..e435511b9 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -127,7 +127,7 @@ namespace MWClass MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); unlock(ptr); //Call the function here. because that makes sense. // using a key disarms the trap - ptr.getCellRef().getTrap() = ""; + ptr.getCellRef().setTrap(""); } if (!needKey || hasKey) From 4aab4e1c26e3b63ff71450005c4e9ea7711ea214 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 14:59:33 +0200 Subject: [PATCH 193/226] Ignore item condition when determining value, except in trade (Fixes #1557) --- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 17 +++++++++++++++-- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index a7a459382..b29bf36b2 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -168,7 +168,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mValue * (static_cast(getItemHealth(ptr)) / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue; } void Armor::registerSelf() diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 5d91db9e2..bc6855129 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -86,7 +86,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mValue * (static_cast(getItemHealth(ptr)) / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue; } void Lockpick::registerSelf() diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 5db0d7a79..ed8625eec 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -85,7 +85,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mValue * (static_cast(getItemHealth(ptr)) / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue; } void Probe::registerSelf() diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index f8b72be37..d7a080534 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -76,7 +76,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mValue * (static_cast(getItemHealth(ptr)) / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue; } void Repair::registerSelf() diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 98b02b914..66affa599 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -154,7 +154,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return ref->mBase->mData.mValue * (static_cast(getItemHealth(ptr)) / getItemMaxHealth(ptr)); + return ref->mBase->mData.mValue; } void Weapon::registerSelf() diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 7ef472282..c0a51311f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -23,6 +23,19 @@ #include "countdialog.hpp" #include "dialogue.hpp" +namespace +{ + + int getEffectiveValue (MWWorld::Ptr item, int count) + { + int price = item.getClass().getValue(item) * count; + if (item.getClass().hasItemHealth(item)) + price *= (static_cast(item.getClass().getItemHealth(item)) / item.getClass().getItemMaxHealth(item)); + return price; + } + +} + namespace MWGui { const float TradeWindow::sBalanceChangeInitialPause = 0.5; @@ -465,7 +478,7 @@ namespace MWGui void TradeWindow::sellToNpc(const MWWorld::Ptr& item, int count, bool boughtItem) { - int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, item.getClass().getValue(item) * count, boughtItem); + int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, getEffectiveValue(item, count), boughtItem); mCurrentBalance += diff; mCurrentMerchantOffer += diff; @@ -475,7 +488,7 @@ namespace MWGui void TradeWindow::buyFromNpc(const MWWorld::Ptr& item, int count, bool soldItem) { - int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, item.getClass().getValue(item) * count, !soldItem); + int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, getEffectiveValue(item, count), !soldItem); mCurrentBalance -= diff; mCurrentMerchantOffer -= diff; From 44fd526c98ad2b0e9af7e885b19f5f0078554aaf Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 15:36:19 +0200 Subject: [PATCH 194/226] Don't show disposition bar for creatures --- apps/openmw/mwgui/dialogue.cpp | 21 ++++++++++++++++++++- apps/openmw/mwgui/dialogue.hpp | 2 +- files/mygui/openmw_dialogue_window_skin.xml | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index e7dd74eee..a6ab1f122 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -586,13 +586,32 @@ namespace MWGui //Clear the list of topics mTopicsList->clear(); - if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + bool dispositionVisible = false; + if (mPtr.getClass().isNpc()) { + dispositionVisible = true; mDispositionBar->setProgressRange(100); mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); mDispositionText->eraseText(0, mDispositionText->getTextLength()); mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); } + + bool dispositionWasVisible = mDispositionBar->getVisible(); + + if (dispositionVisible && !dispositionWasVisible) + { + mDispositionBar->setVisible(true); + float offset = mDispositionBar->getHeight()+5; + mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0,offset,0,-offset)); + mTopicsList->adjustSize(); + } + else if (!dispositionVisible && dispositionWasVisible) + { + mDispositionBar->setVisible(false); + float offset = mDispositionBar->getHeight()+5; + mTopicsList->setCoord(mTopicsList->getCoord() - MyGUI::IntCoord(0,offset,0,-offset)); + mTopicsList->adjustSize(); + } } void DialogueWindow::goodbye() diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 4e0ca5dde..516c04942 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -170,7 +170,7 @@ namespace MWGui BookPage* mHistory; Widgets::MWList* mTopicsList; MyGUI::ScrollBar* mScrollBar; - MyGUI::ProgressPtr mDispositionBar; + MyGUI::Progress* mDispositionBar; MyGUI::EditBox* mDispositionText; PersuasionDialog mPersuasionDialog; diff --git a/files/mygui/openmw_dialogue_window_skin.xml b/files/mygui/openmw_dialogue_window_skin.xml index 4f68a90fa..5e16fd1f6 100644 --- a/files/mygui/openmw_dialogue_window_skin.xml +++ b/files/mygui/openmw_dialogue_window_skin.xml @@ -13,6 +13,7 @@ + From 865f4648b0c31a48bbe2b2a17bd08a6375542031 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 17:15:07 +0200 Subject: [PATCH 195/226] Fix flying creatures not falling on death --- apps/openmw/mwworld/physicssystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 87ec39707..cf587c5c4 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -246,7 +246,7 @@ namespace MWWorld // Early-out for totally static creatures // (Not sure if gravity should still apply?) - if (!ptr.getClass().canWalk(ptr) && !isFlying && !ptr.getClass().canSwim(ptr)) + if (!ptr.getClass().canWalk(ptr) && !ptr.getClass().canFly(ptr) && !ptr.getClass().canSwim(ptr)) return position; /* Anything to collide with? */ From e19bbfd1b54241ce9f67df05712d248e881c65a9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 20:49:22 +0200 Subject: [PATCH 196/226] Fix OpenCS crash when drag and drop is used with unexpected data, e.g. plain text (Fixes #1543) --- apps/opencs/view/filter/filterbox.cpp | 8 +++++-- apps/opencs/view/render/worldspacewidget.cpp | 4 +++- apps/opencs/view/world/regionmap.cpp | 5 +++- apps/opencs/view/world/scriptedit.cpp | 25 ++++++++++++++++---- apps/opencs/view/world/table.cpp | 3 +++ apps/opencs/view/world/tablesubview.cpp | 3 +++ apps/opencs/view/world/util.cpp | 3 +++ 7 files changed, 43 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index e588770b1..7a42ef0a5 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -35,7 +35,11 @@ void CSVFilter::FilterBox::setRecordFilter (const std::string& filter) void CSVFilter::FilterBox::dropEvent (QDropEvent* event) { - std::vector data = dynamic_cast (event->mimeData())->getData(); + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + return; + + std::vector data = mime->getData(); emit recordDropped(data, event->proposedAction()); } @@ -54,4 +58,4 @@ void CSVFilter::FilterBox::createFilterRequest (std::vector< std::pair< std::str Qt::DropAction action) { mRecordFilterBox->createFilterRequest(filterSource, action); -} \ No newline at end of file +} diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 59b82bb67..afc6b4603 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -120,9 +120,11 @@ void CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event) void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + return; if (mime->fromDocument (mDocument)) { emit dataDropped(mime->getData()); } //not handling drops from different documents at the moment -} \ No newline at end of file +} diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp index 849a1988a..9497e4054 100644 --- a/apps/opencs/view/world/regionmap.cpp +++ b/apps/opencs/view/world/regionmap.cpp @@ -382,6 +382,9 @@ void CSVWorld::RegionMap::dropEvent (QDropEvent* event) } const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + return; + if (mime->fromDocument(mDocument) && mime->holdsType(CSMWorld::UniversalId::Type_Region)) { CSMWorld::UniversalId record (mime->returnMatching (CSMWorld::UniversalId::Type_Region)); @@ -402,4 +405,4 @@ void CSVWorld::RegionMap::dropEvent (QDropEvent* event) mRegionId = record.getId(); } -} \ No newline at end of file +} diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index b1528d525..23bc76000 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -45,19 +45,36 @@ CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& docum void CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event) { - setTextCursor (cursorForPosition (event->pos())); - event->acceptProposedAction(); + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) + QTextEdit::dragEnterEvent(event); + else + { + setTextCursor (cursorForPosition (event->pos())); + event->acceptProposedAction(); + } } void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event) { - setTextCursor (cursorForPosition (event->pos())); - event->accept(); + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) + QTextEdit::dragMoveEvent(event); + else + { + setTextCursor (cursorForPosition (event->pos())); + event->accept(); + } } void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + { + QTextEdit::dropEvent(event); + return; + } setTextCursor (cursorForPosition (event->pos())); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 877fd51c0..d0849d631 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -464,6 +464,9 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) } const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped + return; + if (mime->fromDocument (mDocument)) { CSMWorld::ColumnBase::Display display = static_cast diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index a0bef366b..327fb1c0e 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -131,6 +131,9 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) { QDropEvent* drop = dynamic_cast(event); const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); + if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped + return false; + bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); if (handled) { diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index ea8a7c541..6f3999363 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -255,6 +255,9 @@ void CSVWorld::DropLineEdit::dragMoveEvent(QDragMoveEvent *event) void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event) { const CSMWorld::TableMimeData* data(dynamic_cast(event->mimeData())); + if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped + return; + emit tableMimeDataDropped(data->getData(), data->getDocumentPtr()); //WIP } From 9eb8addc706c014ccc0fb72f44d55940a8a87e76 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 17:26:33 +0200 Subject: [PATCH 197/226] Make flying creatures fall when paralyzed (Fixes #1494) --- apps/openmw/mwmechanics/character.cpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5f8013b14..5d5377669 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1395,6 +1395,9 @@ void CharacterController::update(float duration) if (mMovementAnimVelocity == 0) world->queueMovement(mPtr, vec); } + else + // We must always queue movement, even if there is none, to apply gravity. + world->queueMovement(mPtr, Ogre::Vector3(0.0f)); movement = vec; cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = cls.getMovementSettings(mPtr).mPosition[2] = 0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 35042b3ff..fdac643be 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1764,6 +1764,9 @@ namespace MWWorld bool World::isFlying(const MWWorld::Ptr &ptr) const { + const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); + bool isParalyzed = (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0); + if(!ptr.getClass().isActor()) return false; @@ -1771,9 +1774,8 @@ namespace MWWorld return false; if (ptr.getClass().canFly(ptr)) - return true; + return !isParalyzed; - const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).mMagnitude > 0 && isLevitationEnabled()) return true; From 07d0f4458def1fdf5c44d6787ea3eb55494ba188 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 19:01:49 +0200 Subject: [PATCH 198/226] Rotate clouds/particles to come from red mountain (Fixes #245) --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 22 +++++++++++++++++++--- apps/openmw/mwrender/sky.hpp | 8 ++++++++ apps/openmw/mwworld/weather.cpp | 16 ++++++++++++++-- apps/openmw/mwworld/weather.hpp | 3 +++ files/materials/clouds.shader | 22 ++++++++++++---------- files/materials/stars.shader | 21 +++++++++++---------- 7 files changed, 68 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5d5377669..c15cc540f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -608,7 +608,7 @@ void CharacterController::updateIdleStormState() { Ogre::Vector3 stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); Ogre::Vector3 characterDirection = mPtr.getRefData().getBaseNode()->getOrientation().yAxis(); - inStormDirection = stormDirection.angleBetween(characterDirection) < Ogre::Degree(40); + inStormDirection = stormDirection.angleBetween(characterDirection) > Ogre::Degree(120); } if (inStormDirection && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm")) { diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 67e301fdc..8354cca5d 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -219,6 +219,7 @@ SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) , mSceneMgr(NULL) , mAtmosphereDay(NULL) , mAtmosphereNight(NULL) + , mCloudNode(NULL) , mClouds() , mNextClouds() , mCloudBlendFactor(0.0f) @@ -240,10 +241,11 @@ SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) , mRainTimer(0) , mRainSpeed(0) , mRainFrequency(1) + , mStormDirection(0,-1,0) + , mIsStorm(false) { mSceneMgr = root->getCreator(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mRootNode->setInheritOrientation(false); } void SkyManager::create() @@ -335,8 +337,8 @@ void SkyManager::create() mObjects.push_back(objects); // Clouds - SceneNode* clouds_node = mRootNode->createChildSceneNode(); - objects = NifOgre::Loader::createObjects(clouds_node, "meshes\\sky_clouds_01.nif"); + mCloudNode = mRootNode->createChildSceneNode(); + objects = NifOgre::Loader::createObjects(mCloudNode, "meshes\\sky_clouds_01.nif"); for(size_t i = 0;i < objects->mEntities.size();i++) { Entity* clouds_ent = objects->mEntities[i]; @@ -453,8 +455,16 @@ void SkyManager::update(float duration) { for (unsigned int i=0; imControllers.size(); ++i) mParticle->mControllers[i].update(); + + if (mIsStorm) + mParticleNode->setOrientation(Ogre::Vector3::UNIT_Y.getRotationTo(mStormDirection)); } + if (mIsStorm) + mCloudNode->setOrientation(Ogre::Vector3::UNIT_Y.getRotationTo(mStormDirection)); + else + mCloudNode->setOrientation(Ogre::Quaternion::IDENTITY); + updateRain(duration); // UV Scroll the clouds @@ -539,6 +549,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) mRainEnabled = !mRainEffect.empty(); mRainFrequency = weather.mRainFrequency; mRainSpeed = weather.mRainSpeed; + mIsStorm = weather.mIsStorm; if (mCurrentParticleEffect != weather.mParticleEffect) { @@ -676,6 +687,11 @@ void SkyManager::sunDisable() mSunEnabled = false; } +void SkyManager::setStormDirection(const Vector3 &direction) +{ + mStormDirection = direction; +} + void SkyManager::setSunDirection(const Vector3& direction, bool is_moon) { if (!mCreated) return; diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index f752011c0..7c31150f3 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -150,6 +150,8 @@ namespace MWRender void setRainSpeed(float speed); + void setStormDirection(const Ogre::Vector3& direction); + void setSunDirection(const Ogre::Vector3& direction, bool is_moon); void setMasserDirection(const Ogre::Vector3& direction); @@ -185,6 +187,8 @@ namespace MWRender bool mMoonRed; + bool mIsStorm; + float mHour; int mDay; int mMonth; @@ -203,6 +207,8 @@ namespace MWRender Ogre::SceneNode* mAtmosphereDay; Ogre::SceneNode* mAtmosphereNight; + Ogre::SceneNode* mCloudNode; + std::vector mObjects; Ogre::SceneNode* mParticleNode; @@ -211,6 +217,8 @@ namespace MWRender std::map mRainModels; float mRainTimer; + Ogre::Vector3 mStormDirection; + // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index e2862eb87..fb45cb034 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -115,7 +115,8 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa mHour(14), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), - mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering), mIsStorm(false) + mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering), mIsStorm(false), + mStormDirection(0,1,0) { //Globals mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); @@ -389,6 +390,17 @@ void WeatherManager::update(float duration) mWindSpeed = mResult.mWindSpeed; mIsStorm = mResult.mIsStorm; + if (mIsStorm) + { + MWWorld::Ptr player = world->getPlayerPtr(); + Ogre::Vector3 playerPos (player.getRefData().getPosition().pos); + Ogre::Vector3 redMountainPos (19950, 72032, 27831); + + mStormDirection = (playerPos - redMountainPos); + mStormDirection.z = 0; + mRendering->getSkyManager()->setStormDirection(mStormDirection); + } + mRendering->configureFog(mResult.mFogDepth, mResult.mFogColor); // disable sun during night @@ -812,5 +824,5 @@ bool WeatherManager::isInStorm() const Ogre::Vector3 WeatherManager::getStormDirection() const { - return Ogre::Vector3(0,-1,0); + return mStormDirection; } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 9693014a9..3cc58aa4c 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace ESM { @@ -202,6 +203,8 @@ namespace MWWorld float mHour; float mWindSpeed; bool mIsStorm; + Ogre::Vector3 mStormDirection; + MWWorld::Fallback* mFallback; void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index b76f2ded7..d3e5f083c 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -3,25 +3,27 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, view) @shAutoConstant(view, view_matrix) -shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) + shUniform(float4x4, worldview) @shAutoConstant(worldview, worldview_matrix) + shUniform(float4x4, proj) @shAutoConstant(proj, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) shOutput(float, alphaFade) SH_START_PROGRAM { - float4x4 viewFixed = view; + float4x4 worldviewFixed = worldview; + #if !SH_GLSL - viewFixed[0][3] = 0; - viewFixed[1][3] = 0; - viewFixed[2][3] = 0; + worldviewFixed[0][3] = 0; + worldviewFixed[1][3] = 0; + worldviewFixed[2][3] = 0; #else - viewFixed[3][0] = 0; - viewFixed[3][1] = 0; - viewFixed[3][2] = 0; + worldviewFixed[3][0] = 0; + worldviewFixed[3][1] = 0; + worldviewFixed[3][2] = 0; #endif - shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); + + shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition)); UV = uv0; alphaFade = (shInputPosition.z <= 200.f) ? ((shInputPosition.z <= 100.f) ? 0.0 : 0.25) : 1.0; } diff --git a/files/materials/stars.shader b/files/materials/stars.shader index 33f22bd14..b84d37135 100644 --- a/files/materials/stars.shader +++ b/files/materials/stars.shader @@ -3,8 +3,8 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4, view) @shAutoConstant(view, view_matrix) -shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) + shUniform(float4x4, worldview) @shAutoConstant(worldview, worldview_matrix) + shUniform(float4x4, proj) @shAutoConstant(proj, projection_matrix) shVertexInput(float2, uv0) shOutput(float2, UV) @@ -12,17 +12,18 @@ shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix) SH_START_PROGRAM { - float4x4 viewFixed = view; + float4x4 worldviewFixed = worldview; #if !SH_GLSL - viewFixed[0][3] = 0; - viewFixed[1][3] = 0; - viewFixed[2][3] = 0; + worldviewFixed[0][3] = 0; + worldviewFixed[1][3] = 0; + worldviewFixed[2][3] = 0; #else - viewFixed[3][0] = 0; - viewFixed[3][1] = 0; - viewFixed[3][2] = 0; + worldviewFixed[3][0] = 0; + worldviewFixed[3][1] = 0; + worldviewFixed[3][2] = 0; #endif - shOutputPosition = shMatrixMult(projection, shMatrixMult(viewFixed, shInputPosition)); + + shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition)); UV = uv0; fade = (shInputPosition.z > 50) ? 1 : 0; From 8b340ddd5e734852b6855bcda0fd09d9f560a0d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jun 2014 20:26:46 +0200 Subject: [PATCH 199/226] Implement slower movement against storms (Closes #41) --- apps/openmw/mwworld/physicssystem.cpp | 15 +++++++++++---- apps/openmw/mwworld/weather.hpp | 6 +++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index cf587c5c4..9984633cd 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -315,15 +315,22 @@ namespace MWWorld wasOnGround = physicActor->getOnGround(); // store current state 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) + { isOnGround = true; + // if we're on the ground, don't try to fall any more + velocity.z = std::max(0.0f, velocity.z); + } } } - // NOTE: isOnGround was initialised false, so should stay false if falling or sliding horizontally - if(isOnGround) + // Now that we have the effective movement vector, apply wind forces to it + if (MWBase::Environment::get().getWorld()->isInStorm()) { - // if we're on the ground, don't try to fall any more - velocity.z = std::max(0.0f, velocity.z); // NOTE: two different velocity assignments above + Ogre::Vector3 stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); + Ogre::Degree angle = stormDirection.angleBetween(velocity); + static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get() + .find("fStromWalkMult")->getFloat(); + velocity *= 1.f-(fStromWalkMult * (angle.valueDegrees()/180.f)); } Ogre::Vector3 newPosition = position; diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 3cc58aa4c..292d06747 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -128,10 +128,10 @@ namespace MWWorld // Rain sound effect std::string mRainLoopSoundID; - // Is this an ash storm / blight storm? This controls two things: - // - The particle node will be oriented so that the particles appear to come from the Red Mountain. (not implemented yet) + // Is this an ash storm / blight storm? If so, the following will happen: + // - The particles and clouds will be oriented so they appear to come from the Red Mountain. // - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation) - // Possible effect on movement speed? + // - Slower movement when walking against the storm (fStromWalkMult) bool mIsStorm; // How fast does rain travel down? From 3100206b28811b10e61873eba71352795fa261d3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jun 2014 08:37:41 +0200 Subject: [PATCH 200/226] replaced an assert with an exception --- apps/openmw/mwscript/miscextensions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 17824fe49..76b99d04a 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -876,8 +876,8 @@ namespace MWScript else { std::vector contentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); - assert (contentFiles.size() > ptr.getCellRef().getRefNum().mContentFile); - msg << contentFiles[ptr.getCellRef().getRefNum().mContentFile] << std::endl; + + msg << contentFiles.at (ptr.getCellRef().getRefNum().mContentFile) << std::endl; } msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl; From 0c1ad54e68ef7951505c477e5dee4865bac0ef25 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jun 2014 09:12:26 +0200 Subject: [PATCH 201/226] refactored object rendering into a separate class --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/object.cpp | 217 ++++++++++++++++++++++ apps/opencs/view/render/object.hpp | 80 ++++++++ apps/opencs/view/render/previewwidget.cpp | 151 +++------------ apps/opencs/view/render/previewwidget.hpp | 20 +- apps/opencs/view/world/previewsubview.cpp | 4 +- 6 files changed, 329 insertions(+), 145 deletions(-) create mode 100644 apps/opencs/view/render/object.cpp create mode 100644 apps/opencs/view/render/object.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ddaca7e71..a8cc4ceae 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -70,7 +70,7 @@ opencs_units (view/render opencs_units_noqt (view/render navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight - lightingbright + lightingbright object ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp new file mode 100644 index 000000000..6fa7e7707 --- /dev/null +++ b/apps/opencs/view/render/object.cpp @@ -0,0 +1,217 @@ + +#include "object.hpp" + +#include +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/ref.hpp" +#include "../../model/world/refidcollection.hpp" + +void CSVRender::Object::clearSceneNode (Ogre::SceneNode *node) +{ + for (Ogre::SceneNode::ObjectIterator iter = node->getAttachedObjectIterator(); + iter.hasMoreElements(); ) + { + Ogre::MovableObject* object = dynamic_cast (iter.getNext()); + node->getCreator()->destroyMovableObject (object); + } + + for (Ogre::SceneNode::ChildNodeIterator iter = node->getChildIterator(); + iter.hasMoreElements(); ) + { + Ogre::SceneNode* childNode = dynamic_cast (iter.getNext()); + clearSceneNode (childNode); + node->getCreator()->destroySceneNode (childNode); + } +} + +void CSVRender::Object::clear() +{ + mObject.setNull(); + + clearSceneNode (mBase); +} + +void CSVRender::Object::update() +{ + clear(); + + std::string model; + int error = 0; // 1 referemceanöe does not exist, 2 referenceable does not specify a mesh + + const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); + + int index = referenceables.searchId (mReferenceableId); + + if (index==-1) + error = 1; + else + { + // xxx check for Deleted state (error 1) + + model = referenceables.getData (index, + referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). + toString().toUtf8().constData(); + + if (model.empty()) + error = 2; + } + + if (error) + { + Ogre::Entity* entity = mBase->getCreator()->createEntity (Ogre::SceneManager::PT_CUBE); + entity->setMaterialName("BaseWhite"); /// \todo adjust material according to error + + mBase->attachObject (entity); + } + else + { + mObject = NifOgre::Loader::createObjects (mBase, "Meshes\\" + model); + } +} + +void CSVRender::Object::adjust() +{ + if (mReferenceId.empty()) + return; + + const CSMWorld::CellRef& reference = getReference(); + + // position + if (!mForceBaseToZero) + { + Ogre::Vector3 (reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2]); + + mBase->setPosition (Ogre::Vector3 ( + reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2])); + } + + // orientation + Ogre::Quaternion xr (Ogre::Radian (-reference.mPos.pos[0]), Ogre::Vector3::UNIT_X); + + Ogre::Quaternion yr (Ogre::Radian (-reference.mPos.pos[1]), Ogre::Vector3::UNIT_Y); + + Ogre::Quaternion zr (Ogre::Radian (-reference.mPos.pos[2]), Ogre::Vector3::UNIT_Z); + + mBase->setOrientation (xr*yr*zr); + + // scale + mBase->setScale (reference.mScale, reference.mScale, reference.mScale); +} + +const CSMWorld::CellRef& CSVRender::Object::getReference() const +{ + if (mReferenceId.empty()) + throw std::logic_error ("object does not represent a reference"); + + return mData.getReferences().getRecord (mReferenceId).get(); +} + +CSVRender::Object::Object (const CSMWorld::Data& data, Ogre::SceneNode *cellNode, + const std::string& id, bool referenceable, bool forceBaseToZero) +: mData (data), mBase (0), mForceBaseToZero (forceBaseToZero) +{ + mBase = cellNode->createChildSceneNode(); + + if (referenceable) + { + mReferenceableId = id; + } + else + { + mReferenceId = id; + mReferenceableId = getReference().mRefID; + } + + update(); + adjust(); +} + +CSVRender::Object::~Object() +{ + clear(); + + if (mBase) + mBase->getCreator()->destroySceneNode (mBase); +} + +bool CSVRender::Object::ReferenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); + + int index = referenceables.searchId (mReferenceableId); + + if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row()) + { + update(); + adjust(); + return true; + } + + return false; +} + +bool CSVRender::Object::ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, + int end) +{ + const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); + + int index = referenceables.searchId (mReferenceableId); + + if (index!=-1 && index>=start && index<=end) + { + // Deletion of referenceable-type objects is handled outside of Object. + if (!mReferenceId.empty()) + { + update(); + adjust(); + return true; + } + } + + return false; +} + +bool CSVRender::Object::ReferenceDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + if (mReferenceId.empty()) + return false; + + const CSMWorld::RefCollection& references = mData.getReferences(); + + int index = references.searchId (mReferenceId); + + if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row()) + { + int columnIndex = + references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); + + if (columnIndex>=topLeft.column() && columnIndex<=bottomRight.row()) + { + mReferenceableId = + references.getData (index, columnIndex).toString().toUtf8().constData(); + + update(); + } + + adjust(); + + return true; + } + + return false; +} + +std::string CSVRender::Object::getReferenceId() const +{ + return mReferenceId; +} + +std::string CSVRender::Object::getReferenceableId() const +{ + return mReferenceableId; +} \ No newline at end of file diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp new file mode 100644 index 000000000..d4089307f --- /dev/null +++ b/apps/opencs/view/render/object.hpp @@ -0,0 +1,80 @@ +#ifndef OPENCS_VIEW_OBJECT_H +#define OPENCS_VIEW_OBJECT_H + +#include + +class QModelIndex; + +namespace Ogre +{ + class SceneNode; +} + +namespace CSMWorld +{ + class Data; + class CellRef; +} + +namespace CSVRender +{ + class Object + { + const CSMWorld::Data& mData; + std::string mReferenceId; + std::string mReferenceableId; + Ogre::SceneNode *mBase; + NifOgre::ObjectScenePtr mObject; + bool mForceBaseToZero; + + /// Not implemented + Object (const Object&); + + /// Not implemented + Object& operator= (const Object&); + + /// Destroy all scene nodes and movable objects attached to node. + static void clearSceneNode (Ogre::SceneNode *node); + + /// Remove object from node (includes deleting) + void clear(); + + /// Update model + void update(); + + /// Adjust position, orientation and scale + void adjust(); + + /// Throws an exception if *this was constructed with referenceable + const CSMWorld::CellRef& getReference() const; + + public: + + Object (const CSMWorld::Data& data, Ogre::SceneNode *cellNode, + const std::string& id, bool referenceable, bool forceBaseToZero = false); + /// \param forceBaseToZero If this is a reference ignore the coordinates and place + /// it at 0, 0, 0 instead. + + ~Object(); + + /// \return Did this call result in a modification of the visual representation of + /// this object? + bool ReferenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight); + + /// \return Did this call result in a modification of the visual representation of + /// this object? + bool ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + /// \return Did this call result in a modification of the visual representation of + /// this object? + bool ReferenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + /// Returns an empty string if this is a refereceable-type object. + std::string getReferenceId() const; + + std::string getReferenceableId() const; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 99e57ea11..ccc6e4acf 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -7,15 +7,13 @@ #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" -void CSVRender::PreviewWidget::setup() +CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, + const std::string& id, bool referenceable, QWidget *parent) +: SceneWidget (parent), mData (data), + mObject (data, getSceneManager()->getRootSceneNode(), id, referenceable, true) { setNavigation (&mOrbit); - mNode = getSceneManager()->getRootSceneNode()->createChildSceneNode(); - mNode->setPosition (Ogre::Vector3 (0, 0, 0)); - - setModel(); - QAbstractItemModel *referenceables = mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables); @@ -23,142 +21,57 @@ void CSVRender::PreviewWidget::setup() this, SLOT (ReferenceableDataChanged (const QModelIndex&, const QModelIndex&))); connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (ReferenceableAboutToBeRemoved (const QModelIndex&, int, int))); -} -void CSVRender::PreviewWidget::setModel() -{ - if (mNode) + if (!referenceable) { - mObject.setNull(); + QAbstractItemModel *references = + mData.getTableModel (CSMWorld::UniversalId::Type_References); - if (mReferenceableId.empty()) - return; - - int column = - mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); - - int row = mData.getReferenceables().searchId (mReferenceableId); - - if (row==-1) - return; - - QVariant value = mData.getReferenceables().getData (row, column); - - if (!value.isValid()) - return; - - std::string model = value.toString().toUtf8().constData(); - - if (model.empty()) - return; - - mObject = NifOgre::Loader::createObjects (mNode, "Meshes\\" + model); + connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (ReferenceDataChanged (const QModelIndex&, const QModelIndex&))); + connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (ReferenceAboutToBeRemoved (const QModelIndex&, int, int))); } } -void CSVRender::PreviewWidget::adjust() -{ - if (mNode) - { - int row = mData.getReferences().getIndex (mReferenceId); - - float scale = mData.getReferences().getData (row, mData.getReferences(). - findColumnIndex (CSMWorld::Columns::ColumnId_Scale)).toFloat(); - float rotX = mData.getReferences().getData (row, mData.getReferences(). - findColumnIndex (CSMWorld::Columns::ColumnId_PositionXRot)).toFloat(); - float rotY = mData.getReferences().getData (row, mData.getReferences(). - findColumnIndex (CSMWorld::Columns::ColumnId_PositionYRot)).toFloat(); - float rotZ = mData.getReferences().getData (row, mData.getReferences(). - findColumnIndex (CSMWorld::Columns::ColumnId_PositionZRot)).toFloat(); - - mNode->setScale (scale, scale, scale); - - Ogre::Quaternion xr (Ogre::Radian(-rotX), Ogre::Vector3::UNIT_X); - - Ogre::Quaternion yr (Ogre::Radian(-rotY), Ogre::Vector3::UNIT_Y); - - Ogre::Quaternion zr (Ogre::Radian(-rotZ), Ogre::Vector3::UNIT_Z); - - mNode->setOrientation (xr*yr*zr); - } -} - -CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, - const std::string& referenceableId, QWidget *parent) -: SceneWidget (parent), mData (data), mNode (0), mReferenceableId (referenceableId) -{ - setup(); -} - -CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, - const std::string& referenceableId, const std::string& referenceId, QWidget *parent) -: SceneWidget (parent), mData (data), mReferenceableId (referenceableId), - mReferenceId (referenceId) -{ - setup(); - - adjust(); - - QAbstractItemModel *references = - mData.getTableModel (CSMWorld::UniversalId::Type_References); - - connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (ReferenceDataChanged (const QModelIndex&, const QModelIndex&))); - connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), - this, SLOT (ReferenceAboutToBeRemoved (const QModelIndex&, int, int))); -} - void CSVRender::PreviewWidget::ReferenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - if (mReferenceableId.empty()) - return; - - CSMWorld::IdTable& referenceables = dynamic_cast ( - *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); - - QModelIndex index = referenceables.getModelIndex (mReferenceableId, 0); - - if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) - { - /// \todo possible optimisation; check columns and only update if relevant columns have - /// changed - setModel(); + if (mObject.ReferenceableDataChanged (topLeft, bottomRight)) flagAsModified(); - } } void CSVRender::PreviewWidget::ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) { - if (mReferenceableId.empty()) + if (mObject.ReferenceableAboutToBeRemoved (parent, start, end)) + flagAsModified(); + + if (mObject.getReferenceableId().empty()) return; CSMWorld::IdTable& referenceables = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); - QModelIndex index = referenceables.getModelIndex (mReferenceableId, 0); + QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(), 0); if (index.row()>=start && index.row()<=end) { - if (mReferenceId.empty()) + if (mObject.getReferenceId().empty()) { // this is a preview for a referenceble emit closeRequest(); } - else - { - // this is a preview for a reference - mObject.setNull(); - flagAsModified(); - } } } void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - if (mReferenceId.empty()) + if (mObject.ReferenceDataChanged (topLeft, bottomRight)) + flagAsModified(); + + if (mObject.getReferenceId().empty()) return; CSMWorld::IdTable& references = dynamic_cast ( @@ -166,35 +79,23 @@ void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); - QModelIndex index = references.getModelIndex (mReferenceId, columnIndex); + QModelIndex index = references.getModelIndex (mObject.getReferenceId(), columnIndex); if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) - { - /// \todo possible optimisation; check columns and only update if relevant columns have - /// changed - adjust(); - if (index.column()>=topLeft.column() && index.column()<=bottomRight.row()) - { - mReferenceableId = references.data (index).toString().toUtf8().constData(); - emit referenceableIdChanged (mReferenceableId); - setModel(); - } - - flagAsModified(); - } + emit referenceableIdChanged (mObject.getReferenceableId()); } void CSVRender::PreviewWidget::ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { - if (mReferenceId.empty()) + if (mObject.getReferenceId().empty()) return; CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); - QModelIndex index = references.getModelIndex (mReferenceId, 0); + QModelIndex index = references.getModelIndex (mObject.getReferenceId(), 0); if (index.row()>=start && index.row()<=end) emit closeRequest(); diff --git a/apps/opencs/view/render/previewwidget.hpp b/apps/opencs/view/render/previewwidget.hpp index 7a63d8fb1..09cb19dc7 100644 --- a/apps/opencs/view/render/previewwidget.hpp +++ b/apps/opencs/view/render/previewwidget.hpp @@ -1,11 +1,10 @@ #ifndef OPENCS_VIEW_PREVIEWWIDGET_H #define OPENCS_VIEW_PREVIEWWIDGET_H -#include - #include "scenewidget.hpp" #include "navigationorbit.hpp" +#include "object.hpp" class QModelIndex; @@ -22,26 +21,13 @@ namespace CSVRender CSMWorld::Data& mData; CSVRender::NavigationOrbit mOrbit; - NifOgre::ObjectScenePtr mObject; - Ogre::SceneNode *mNode; - std::string mReferenceId; - std::string mReferenceableId; - - void setup(); - - void setModel(); - - void adjust(); - ///< Adjust referenceable preview according to the reference + Object mObject; public: - PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId, + PreviewWidget (CSMWorld::Data& data, const std::string& id, bool referenceable, QWidget *parent = 0); - PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId, - const std::string& referenceId, QWidget *parent = 0); - signals: void closeRequest(); diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index e9a30e65d..49e6d9361 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -23,10 +23,10 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo referenceableIdChanged (referenceableId); mScene = - new CSVRender::PreviewWidget (document.getData(), referenceableId, id.getId(), this); + new CSVRender::PreviewWidget (document.getData(), id.getId(), false, this); } else - mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), this); + mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this); SceneToolbar *toolbar = new SceneToolbar (48+6, this); From 36c1170d09e9d68d64ff2e04a098d004c9b0e150 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jun 2014 10:35:00 +0200 Subject: [PATCH 202/226] fixed some function names --- apps/opencs/view/render/object.cpp | 6 +++--- apps/opencs/view/render/object.hpp | 6 +++--- apps/opencs/view/render/previewwidget.cpp | 22 +++++++++++----------- apps/opencs/view/render/previewwidget.hpp | 8 ++++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 6fa7e7707..59cfd965d 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -137,7 +137,7 @@ CSVRender::Object::~Object() mBase->getCreator()->destroySceneNode (mBase); } -bool CSVRender::Object::ReferenceableDataChanged (const QModelIndex& topLeft, +bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); @@ -154,7 +154,7 @@ bool CSVRender::Object::ReferenceableDataChanged (const QModelIndex& topLeft, return false; } -bool CSVRender::Object::ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, +bool CSVRender::Object::referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) { const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); @@ -175,7 +175,7 @@ bool CSVRender::Object::ReferenceableAboutToBeRemoved (const QModelIndex& parent return false; } -bool CSVRender::Object::ReferenceDataChanged (const QModelIndex& topLeft, +bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { if (mReferenceId.empty()) diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index d4089307f..df39d7393 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -59,16 +59,16 @@ namespace CSVRender /// \return Did this call result in a modification of the visual representation of /// this object? - bool ReferenceableDataChanged (const QModelIndex& topLeft, + bool referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); /// \return Did this call result in a modification of the visual representation of /// this object? - bool ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); + bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); /// \return Did this call result in a modification of the visual representation of /// this object? - bool ReferenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); /// Returns an empty string if this is a refereceable-type object. std::string getReferenceId() const; diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index ccc6e4acf..76e9971dd 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -18,9 +18,9 @@ CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables); connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (ReferenceableDataChanged (const QModelIndex&, const QModelIndex&))); + this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&))); connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), - this, SLOT (ReferenceableAboutToBeRemoved (const QModelIndex&, int, int))); + this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int))); if (!referenceable) { @@ -28,23 +28,23 @@ CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, mData.getTableModel (CSMWorld::UniversalId::Type_References); connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (ReferenceDataChanged (const QModelIndex&, const QModelIndex&))); + this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&))); connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), - this, SLOT (ReferenceAboutToBeRemoved (const QModelIndex&, int, int))); + this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int))); } } -void CSVRender::PreviewWidget::ReferenceableDataChanged (const QModelIndex& topLeft, +void CSVRender::PreviewWidget::referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - if (mObject.ReferenceableDataChanged (topLeft, bottomRight)) + if (mObject.referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); } -void CSVRender::PreviewWidget::ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, +void CSVRender::PreviewWidget::referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) { - if (mObject.ReferenceableAboutToBeRemoved (parent, start, end)) + if (mObject.referenceableAboutToBeRemoved (parent, start, end)) flagAsModified(); if (mObject.getReferenceableId().empty()) @@ -65,10 +65,10 @@ void CSVRender::PreviewWidget::ReferenceableAboutToBeRemoved (const QModelIndex& } } -void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, +void CSVRender::PreviewWidget::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - if (mObject.ReferenceDataChanged (topLeft, bottomRight)) + if (mObject.referenceDataChanged (topLeft, bottomRight)) flagAsModified(); if (mObject.getReferenceId().empty()) @@ -86,7 +86,7 @@ void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, emit referenceableIdChanged (mObject.getReferenceableId()); } -void CSVRender::PreviewWidget::ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, +void CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { if (mObject.getReferenceId().empty()) diff --git a/apps/opencs/view/render/previewwidget.hpp b/apps/opencs/view/render/previewwidget.hpp index 09cb19dc7..dd6a99c0f 100644 --- a/apps/opencs/view/render/previewwidget.hpp +++ b/apps/opencs/view/render/previewwidget.hpp @@ -36,14 +36,14 @@ namespace CSVRender private slots: - void ReferenceableDataChanged (const QModelIndex& topLeft, + void referenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - void ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); + void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); - void ReferenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - void ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); + void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); }; } From a2f514f024856abd524465927611a1ea6599e006 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jun 2014 11:38:34 +0200 Subject: [PATCH 203/226] disabled preview function for deleted records --- apps/opencs/view/world/table.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 877fd51c0..0605e87ff 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -115,12 +115,12 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (selectedRows.size()==1) { + int row = selectedRows.begin()->row(); + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + if (mModel->getFeatures() & CSMWorld::IdTable::Feature_View) { - int row = selectedRows.begin()->row(); - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - CSMWorld::UniversalId id = mModel->view (row).first; int index = mDocument.getData().getCells().searchId (id.getId()); @@ -132,7 +132,16 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) } if (mModel->getFeatures() & CSMWorld::IdTable::Feature_Preview) - menu.addAction (mPreviewAction); + { + QModelIndex index = mModel->index (row, + mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); + + CSMWorld::RecordBase::State state = static_cast ( + mModel->data (index).toInt()); + + if (state!=CSMWorld::RecordBase::State_Deleted) + menu.addAction (mPreviewAction); + } } menu.exec (event->globalPos()); @@ -377,7 +386,12 @@ void CSVWorld::Table::previewRecord() { std::string id = getUniversalId (selectedRows.begin()->row()).getId(); - emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id) , ""); + QModelIndex index = mModel->getModelIndex (id, + mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); + + if (mModel->data (index)!=CSMWorld::RecordBase::State_Deleted) + emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id), + ""); } } From 6020edf60f7408f214d415ca66a310b0055cf521 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jun 2014 11:55:02 +0200 Subject: [PATCH 204/226] make preview watch for changes in modification state of relevant records --- apps/opencs/view/render/previewwidget.cpp | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 76e9971dd..75b4e9396 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -39,6 +39,18 @@ void CSVRender::PreviewWidget::referenceableDataChanged (const QModelIndex& topL { if (mObject.referenceableDataChanged (topLeft, bottomRight)) flagAsModified(); + + if (mObject.getReferenceId().empty()) + { + CSMWorld::IdTable& referenceables = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); + + QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(), + referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); + + if (referenceables.data (index).toInt()==CSMWorld::RecordBase::State_Deleted) + emit closeRequest(); + } } void CSVRender::PreviewWidget::referenceableAboutToBeRemoved (const QModelIndex& parent, int start, @@ -77,6 +89,18 @@ void CSVRender::PreviewWidget::referenceDataChanged (const QModelIndex& topLeft, CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + // check for deleted state + { + QModelIndex index = references.getModelIndex (mObject.getReferenceId(), + references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification)); + + if (references.data (index).toInt()==CSMWorld::RecordBase::State_Deleted) + { + emit closeRequest(); + return; + } + } + int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); QModelIndex index = references.getModelIndex (mObject.getReferenceId(), columnIndex); From 3541d0380957802e714d9de5815e34e754c0d3ef Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jun 2014 12:29:23 +0200 Subject: [PATCH 205/226] fixed deleting of referenceables (modification state column was incorrectly flagged as non-editable) --- apps/opencs/model/world/refidcollection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index cb2027880..46a22eded 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -44,7 +44,7 @@ CSMWorld::RefIdCollection::RefIdCollection() ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); baseColumns.mId = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState, - ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); + ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true, false)); baseColumns.mModified = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); From 6a900e0aad2575f76a5858043741e5273c6e3eae Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Jun 2014 03:11:37 +0200 Subject: [PATCH 206/226] Update weapon and shield controllers for creatures --- apps/openmw/mwrender/creatureanimation.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 0eb883953..f2447cb70 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -184,6 +184,18 @@ Ogre::Vector3 CreatureWeaponAnimation::runAnimation(float duration) { Ogre::Vector3 ret = Animation::runAnimation(duration); pitchSkeleton(mPtr.getRefData().getPosition().rot[0], mSkelBase->getSkeleton()); + + if (!mWeapon.isNull()) + { + for (unsigned int i=0; imControllers.size(); ++i) + mWeapon->mControllers[i].update(); + } + if (!mShield.isNull()) + { + for (unsigned int i=0; imControllers.size(); ++i) + mShield->mControllers[i].update(); + } + return ret; } From 37c85f0af41fedd2642be8c9d1b713f76c67d086 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Jun 2014 03:12:07 +0200 Subject: [PATCH 207/226] Don't update object root controllers with no time source (Fixes #1564) --- apps/openmw/mwrender/animation.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8bf2160e3..57a2ac1ec 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -144,12 +144,6 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) } else mAttachedObjects.clear(); - - for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) - { - if(mObjectRoot->mControllers[i].getSource().isNull()) - mObjectRoot->mControllers[i].setSource(mAnimationTimePtr[0]); - } } struct AddGlow @@ -309,6 +303,12 @@ void Animation::addAnimSource(const std::string &model) ctrls[i].setSource(mAnimationTimePtr[grp]); grpctrls[grp].push_back(ctrls[i]); } + + for (unsigned int i = 0; i < mObjectRoot->mControllers.size(); ++i) + { + if (mObjectRoot->mControllers[i].getSource().isNull()) + mObjectRoot->mControllers[i].setSource(mAnimationTimePtr[0]); + } } void Animation::clearAnimSources() @@ -1039,7 +1039,10 @@ Ogre::Vector3 Animation::runAnimation(float duration) } for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) - mObjectRoot->mControllers[i].update(); + { + if(!mObjectRoot->mControllers[i].getSource().isNull()) + mObjectRoot->mControllers[i].update(); + } // Apply group controllers for(size_t grp = 0;grp < sNumGroups;grp++) From 7f26843dc3f651845d47fdfeb02f7a320f7baae1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Jun 2014 18:18:58 +0200 Subject: [PATCH 208/226] Reset key focus when the key focus widget is hidden (Fixes #1568) --- apps/openmw/mwgui/windowbase.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index cc18e6694..4af0afc1f 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -21,6 +21,17 @@ void WindowBase::setVisible(bool visible) open(); else if (wasVisible && !visible) close(); + + // This is needed as invisible widgets can retain key focus. + if (!visible) + { + MyGUI::Widget* keyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + while (keyFocus != mMainWidget && keyFocus != NULL) + keyFocus = keyFocus->getParent(); + + if (keyFocus == mMainWidget) + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + } } bool WindowBase::isVisible() From 2451eead8afacd74f70070869976782cfcb7b4a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Jun 2014 20:07:19 +0200 Subject: [PATCH 209/226] Some additional wrapping for faction rank instructions --- apps/openmw/mwscript/statsextensions.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 1d3a8bc4b..5c47dea0f 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -623,7 +623,8 @@ namespace MWScript MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end()) { - player.getClass().getNpcStats(player).getFactionRanks()[factionID] = player.getClass().getNpcStats(player).getFactionRanks()[factionID] -1; + player.getClass().getNpcStats(player).getFactionRanks()[factionID] = + std::max(0, player.getClass().getNpcStats(player).getFactionRanks()[factionID]-1); } } } @@ -1031,7 +1032,7 @@ namespace MWScript return; std::map& ranks = ptr.getClass().getNpcStats(ptr).getFactionRanks (); - ranks[factionID] = ranks[factionID]+1; + ranks[factionID] = std::min(9, ranks[factionID]+1); } }; @@ -1058,7 +1059,7 @@ namespace MWScript return; std::map& ranks = ptr.getClass().getNpcStats(ptr).getFactionRanks (); - ranks[factionID] = ranks[factionID]-1; + ranks[factionID] = std::max(0, ranks[factionID]-1); } }; From 78d02d97da1c0aa67c8f3b85963d8ccba7de7e62 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 00:06:08 +0200 Subject: [PATCH 210/226] Find text keys in reverse (Bug #1578) --- apps/openmw/mwrender/animation.cpp | 55 +++++++++++++++++++----------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 57a2ac1ec..62303fd42 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -467,16 +467,17 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node const std::string stop = groupname+": stop"; float starttime = std::numeric_limits::max(); float stoptime = 0.0f; - NifOgre::TextKeyMap::const_iterator keyiter(keys.begin()); - while(keyiter != keys.end()) + // Have to find keys in reverse (see reset method) + NifOgre::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin()); + while(keyiter != keys.rend()) { if(keyiter->second == start || keyiter->second == loopstart) - starttime = keyiter->first; - else if(keyiter->second == loopstop || keyiter->second == stop) { - stoptime = keyiter->first; + starttime = keyiter->first; break; } + else if(keyiter->second == loopstop || keyiter->second == stop) + stoptime = keyiter->first; ++keyiter; } @@ -592,31 +593,39 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) { - const NifOgre::TextKeyMap::const_iterator groupstart = findGroupStart(keys, groupname); + // Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two + // separate walkforward keys, and the last one is supposed to be used. + NifOgre::TextKeyMap::const_reverse_iterator groupend(keys.rbegin()); + for(;groupend != keys.rend();++groupend) + { + if(groupend->second.compare(0, groupname.size(), groupname) == 0 && + groupend->second.compare(groupname.size(), 2, ": ") == 0) + break; + } std::string starttag = groupname+": "+start; - NifOgre::TextKeyMap::const_iterator startkey(groupstart); - while(startkey != keys.end() && startkey->second != starttag) + NifOgre::TextKeyMap::const_reverse_iterator startkey(groupend); + while(startkey != keys.rend() && startkey->second != starttag) ++startkey; - if(startkey == keys.end() && start == "loop start") + if(startkey == keys.rend() && start == "loop start") { starttag = groupname+": start"; - startkey = groupstart; - while(startkey != keys.end() && startkey->second != starttag) + startkey = groupend; + while(startkey != keys.rend() && startkey->second != starttag) ++startkey; } - if(startkey == keys.end()) + if(startkey == keys.rend()) return false; const std::string stoptag = groupname+": "+stop; - NifOgre::TextKeyMap::const_iterator stopkey(groupstart); - while(stopkey != keys.end() + NifOgre::TextKeyMap::const_reverse_iterator stopkey(groupend); + while(stopkey != keys.rend() // We have to ignore extra garbage at the end. // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". // Why, just why? :( && (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag)) ++stopkey; - if(stopkey == keys.end()) + if(stopkey == keys.rend()) return false; if(startkey->first > stopkey->first) @@ -628,18 +637,24 @@ bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const s state.mStopTime = stopkey->first; state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint); + + // mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation + // (see handleTextKey). But if startpoint is already past these keys, we need to assign them now. if(state.mTime > state.mStartTime) { const std::string loopstarttag = groupname+": loop start"; const std::string loopstoptag = groupname+": loop stop"; - NifOgre::TextKeyMap::const_iterator key(groupstart); - while(key->first <= state.mTime && key != stopkey) + + NifOgre::TextKeyMap::const_reverse_iterator key(groupend); + for (; key != startkey && key != keys.rend(); ++key) { - if(key->second == loopstarttag) + if (key->first > state.mTime) + continue; + + if (key->second == loopstarttag) state.mLoopStartTime = key->first; - else if(key->second == loopstoptag) + else if (key->second == loopstoptag) state.mLoopStopTime = key->first; - ++key; } } From 911bd0e3407d2f7ef2f67c208037091299020dcf Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 00:07:13 +0200 Subject: [PATCH 211/226] Use walk animation as fallback if there's no run animation (Fixes #1578) --- apps/openmw/mwmechanics/character.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c15cc540f..bd076bead 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -336,7 +336,17 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat { std::string::size_type swimpos = movement.find("swim"); if(swimpos == std::string::npos) - movement.clear(); + { + std::string::size_type runpos = movement.find("run"); + if (runpos != std::string::npos) + { + movement.replace(runpos, runpos+3, "walk"); + if (!mAnimation->hasAnimation(movement)) + movement.clear(); + } + else + movement.clear(); + } else { movegroup = MWRender::Animation::Group_LowerBody; From 1ab02d80720ddbb21a6d5bf320ce8a79e3882042 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 00:10:36 +0200 Subject: [PATCH 212/226] Fix exception for box shapes (Fixes #1580) --- components/nifbullet/bulletnifloader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 7f78c04aa..31d4e10d6 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -108,6 +108,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) for(int i=0; i getChildShape(i)); delete mCompoundShape; + mShape->mAnimatedShapes.clear(); } } else From 516014c071ab3fe1429b91bbbcfa9937cf6d9fe3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 01:06:53 +0200 Subject: [PATCH 213/226] Trigger hit on start key if there's no hit key (Fixes #1574) --- apps/openmw/mwrender/animation.cpp | 39 ++++++++++++++++++++++++++---- apps/openmw/mwrender/animation.hpp | 3 ++- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 62303fd42..872740d74 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -669,7 +669,8 @@ void split(const std::string &s, char delim, std::vector &elems) { } } -void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) +void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key, + const NifOgre::TextKeyMap& textkeys) { //float time = key->first; const std::string &evt = key->second; @@ -743,6 +744,34 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else mPtr.getClass().hit(mPtr); } + else if (!groupname.empty() && groupname.compare(0, groupname.size()-1, "attack") == 0 + && evt.compare(off, len, "start") == 0) + { + NifOgre::TextKeyMap::const_iterator hitKey = key; + + // Not all animations have a hit key defined. If there is none, the hit happens with the start key. + bool hasHitKey = false; + while (hitKey != textkeys.end()) + { + if (hitKey->second == groupname + ": hit") + { + hasHitKey = true; + break; + } + if (hitKey->second == groupname + ": stop") + break; + ++hitKey; + } + if (!hasHitKey) + { + if (groupname == "attack1") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); + else if (groupname == "attack2") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); + else if (groupname == "attack3") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); + } + } else if (evt.compare(off, len, "shoot attach") == 0) attachArrow(); else if (evt.compare(off, len, "shoot release") == 0) @@ -821,7 +850,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); while(textkey != textkeys.end() && textkey->first <= state.mTime) { - handleTextKey(state, groupname, textkey); + handleTextKey(state, groupname, textkey, textkeys); ++textkey; } @@ -836,7 +865,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo textkey = textkeys.lower_bound(state.mTime); while(textkey != textkeys.end() && textkey->first <= state.mTime) { - handleTextKey(state, groupname, textkey); + handleTextKey(state, groupname, textkey, textkeys); ++textkey; } } @@ -1014,7 +1043,7 @@ Ogre::Vector3 Animation::runAnimation(float duration) while(textkey != textkeys.end() && textkey->first <= state.mTime) { - handleTextKey(state, stateiter->first, textkey); + handleTextKey(state, stateiter->first, textkey, textkeys); ++textkey; } @@ -1028,7 +1057,7 @@ Ogre::Vector3 Animation::runAnimation(float duration) textkey = textkeys.lower_bound(state.mTime); while(textkey != textkeys.end() && textkey->first <= state.mTime) { - handleTextKey(state, stateiter->first, textkey); + handleTextKey(state, stateiter->first, textkey, textkeys); ++textkey; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 297af558f..1544d42c9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -173,7 +173,8 @@ protected: const std::string &groupname, const std::string &start, const std::string &stop, float startpoint); - void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); + void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key, + const NifOgre::TextKeyMap& map); /* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle * systems in the model are ignored (useful for NPCs, where only the skeleton is needed for From 523c2715e3f4bdd6e32ae3260577050ee862f7a8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 01:31:34 +0200 Subject: [PATCH 214/226] AiCombat: Handle Start to Min and Min to Max durations of 0 (found in Riekling animation) --- apps/openmw/mwmechanics/aicombat.cpp | 13 +++++++++---- apps/openmw/mwmechanics/aicombat.hpp | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index f5881d605..2606daa88 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -118,6 +118,7 @@ namespace MWMechanics mStrength = 0; mCell = NULL; mLastTargetPos = Ogre::Vector3(0,0,0); + mMinMaxAttackDurationInitialised = false; } /* @@ -219,8 +220,6 @@ namespace MWMechanics if(smoothTurn(actor, Ogre::Degree(mMovement.mRotation[0]), 0)) mMovement.mRotation[0] = 0; } - mTimerAttack -= duration; - //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f float attacksPeriod = 1.0f; @@ -228,12 +227,16 @@ namespace MWMechanics if(mReadyToAttack) { - if (mMinMaxAttackDuration[0][0] == 0) + if (!mMinMaxAttackDurationInitialised) { + // TODO: this must be updated when a different weapon is equipped getMinMaxAttackDuration(actor, mMinMaxAttackDuration); + mMinMaxAttackDurationInitialised = true; } - if (mTimerAttack <= 0) mAttack = false; + if (mTimerAttack < 0) mAttack = false; + + mTimerAttack -= duration; } else { @@ -326,6 +329,8 @@ namespace MWMechanics else attackType = ESM::Weapon::AT_Chop; // cause it's =0 mStrength = static_cast(rand()) / RAND_MAX; + + // Note: may be 0 for some animations mTimerAttack = mMinMaxAttackDuration[attackType][0] + (mMinMaxAttackDuration[attackType][1] - mMinMaxAttackDuration[attackType][0]) * mStrength; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 3315998ba..311dee617 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -65,6 +65,7 @@ namespace MWMechanics float mStrength; // this is actually make sense only in ranged combat float mMinMaxAttackDuration[3][2]; // slash, thrust, chop has different durations + bool mMinMaxAttackDurationInitialised; bool mForceNoShortcut; ESM::Position mShortcutFailPos; From c35b87de953d533a6a0820e7c507b1915677d61c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 28 Jun 2014 11:25:28 +0200 Subject: [PATCH 215/226] various Object class fixes --- apps/opencs/view/render/object.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 59cfd965d..bb7c2f386 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -49,7 +49,7 @@ void CSVRender::Object::update() error = 1; else { - // xxx check for Deleted state (error 1) + /// \todo check for Deleted state (error 1) model = referenceables.getData (index, referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). @@ -81,19 +81,15 @@ void CSVRender::Object::adjust() // position if (!mForceBaseToZero) - { - Ogre::Vector3 (reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2]); - mBase->setPosition (Ogre::Vector3 ( reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2])); - } // orientation - Ogre::Quaternion xr (Ogre::Radian (-reference.mPos.pos[0]), Ogre::Vector3::UNIT_X); + Ogre::Quaternion xr (Ogre::Radian (-reference.mPos.rot[0]), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr (Ogre::Radian (-reference.mPos.pos[1]), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion yr (Ogre::Radian (-reference.mPos.rot[1]), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr (Ogre::Radian (-reference.mPos.pos[2]), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion zr (Ogre::Radian (-reference.mPos.rot[2]), Ogre::Vector3::UNIT_Z); mBase->setOrientation (xr*yr*zr); From 92f5898b325e8d6b9c05a98131fb35808636d945 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 14:22:27 +0200 Subject: [PATCH 216/226] AiWander: Make sure we have 8 idle values (Fixes #1583) The AiWander instruction may specify more (or less) than 8. --- apps/openmw/mwmechanics/aiwander.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 79151e896..b97554e2a 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -26,6 +26,7 @@ namespace MWMechanics AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) { + mIdle.resize(8, 0); init(); } @@ -652,7 +653,7 @@ namespace MWMechanics wander->mData.mDuration = mDuration; wander->mData.mTimeOfDay = mTimeOfDay; wander->mStartTime = mStartTime.toEsm(); - assert (mIdle.size() >= 8); + assert (mIdle.size() == 8); for (int i=0; i<8; ++i) wander->mData.mIdle[i] = mIdle[i]; wander->mData.mShouldRepeat = mRepeat; From 8e361bb879fe11a1a61556a7307bcfebea5f519c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 14:49:07 +0200 Subject: [PATCH 217/226] Make targeted spells collide with water (Fixes #1500) --- apps/openmw/mwworld/projectilemanager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 3122cb325..fb376bb93 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -67,6 +67,9 @@ namespace MWWorld Ogre::Vector3 pos(caster.getRefData().getPosition().pos); pos.z += height; + if (MWBase::Environment::get().getWorld()->isUnderwater(caster.getCell(), pos)) // Underwater casting not possible + return; + Ogre::Quaternion orient; if (caster.getClass().isActor()) orient = Ogre::Quaternion(Ogre::Radian(caster.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * @@ -188,6 +191,11 @@ namespace MWWorld hit = true; } + + // Explodes when hitting water + if (MWBase::Environment::get().getWorld()->isUnderwater(MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(), newPos)) + hit = true; + if (hit) { MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); From 3b2358888b3279f3bf393656919314ee3e3793ab Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 14:59:33 +0200 Subject: [PATCH 218/226] Attempt to fix player position after using coc/coe --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwscript/cellextensions.cpp | 2 ++ apps/openmw/mwworld/physicssystem.cpp | 7 +++---- apps/openmw/mwworld/physicssystem.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 13 ++++++++++++- apps/openmw/mwworld/worldimp.hpp | 3 +++ 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 311d072c5..49ac5bc15 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -274,6 +274,9 @@ namespace MWBase virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; ///< Adjust position after load to be on ground. Must be called after model load. + virtual void fixPosition (const MWWorld::Ptr& actor) = 0; + ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. + virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 2e2e9b698..94d734029 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -47,6 +47,7 @@ namespace MWScript if (world->findExteriorPosition(cell, pos)) { world->changeToExteriorCell(pos); + world->fixPosition(world->getPlayerPtr()); } else { @@ -79,6 +80,7 @@ namespace MWScript pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; world->changeToExteriorCell (pos); + world->fixPosition(world->getPlayerPtr()); } }; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 9984633cd..6802d6118 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -215,7 +215,7 @@ namespace MWWorld public: - static Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, OEngine::Physic::PhysicEngine *engine) + static Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, OEngine::Physic::PhysicEngine *engine, float maxHeight) { const ESM::Position &refpos = ptr.getRefData().getPosition(); Ogre::Vector3 position(refpos.pos); @@ -224,7 +224,6 @@ namespace MWWorld if (!physicActor) return position; - const int maxHeight = 200.f; OEngine::Physic::ActorTracer tracer; tracer.findGround(physicActor, position, position-Ogre::Vector3(0,0,maxHeight), engine); if(tracer.mFraction >= 1.0f) @@ -600,9 +599,9 @@ namespace MWWorld return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName()); } - Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr) + Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, float maxHeight) { - return MovementSolver::traceDown(ptr, mEngine); + return MovementSolver::traceDown(ptr, mEngine, maxHeight); } void PhysicsSystem::addHeightField (float* heights, diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index df9718669..8e0be95d5 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -56,7 +56,7 @@ namespace MWWorld void stepSimulation(float dt); std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with - Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); + Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr, float maxHeight); std::pair getFacedHandle(float queryDistance); std::pair getHitContact(const std::string &name, diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index fdac643be..33405b4d8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1166,7 +1166,7 @@ namespace MWWorld if (!isFlying(ptr)) { - Ogre::Vector3 traced = mPhysics->traceDown(ptr); + Ogre::Vector3 traced = mPhysics->traceDown(ptr, 200); if (traced.z < pos.pos[2]) pos.pos[2] = traced.z; } @@ -1174,6 +1174,17 @@ namespace MWWorld moveObject(ptr, ptr.getCell(), pos.pos[0], pos.pos[1], pos.pos[2]); } + void World::fixPosition(const Ptr &actor) + { + const float dist = 8000; + ESM::Position pos (actor.getRefData().getPosition()); + pos.pos[2] += dist; + actor.getRefData().setPosition(pos); + + Ogre::Vector3 traced = mPhysics->traceDown(actor, dist*1.1); + moveObject(actor, actor.getCell(), traced.x, traced.y, traced.z); + } + void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { rotateObjectImp(ptr, Ogre::Vector3(Ogre::Degree(x).valueRadians(), diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 69b72b533..08d7eb42d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -263,6 +263,9 @@ namespace MWWorld virtual void adjustPosition (const Ptr& ptr); ///< Adjust position after load to be on ground. Must be called after model load. + virtual void fixPosition (const Ptr& actor); + ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. + virtual void enable (const Ptr& ptr); virtual void disable (const Ptr& ptr); From 65e36793fc8b3d65976b8b1779a57726b5e26491 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 15:21:04 +0200 Subject: [PATCH 219/226] Fix messagebox on crash showing wrong path --- apps/openmw/crashcatcher.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index 7f805f233..75d2d7953 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -381,10 +381,7 @@ static void crash_handler(const char *logfile) if(logfile) { - char cwd[MAXPATHLEN]; - getcwd(cwd, MAXPATHLEN); - - std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(cwd) + "/" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; + std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); } exit(0); From f929004635a78804234fc5615d38ed9659c4bcbc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 17:44:52 +0200 Subject: [PATCH 220/226] Search exterior cells in reverse (workaround for duplicate chargen_plank reference in Morrowind.esm and Bloodmoon.esm) --- apps/openmw/mwworld/cells.cpp | 6 ++++-- apps/openmw/mwworld/store.hpp | 21 +++++++-------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 16c1b497c..ef3d299a9 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -193,8 +193,10 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) } // Then check cells that are already listed - for (std::map, CellStore>::iterator iter = mExteriors.begin(); - iter!=mExteriors.end(); ++iter) + // Search in reverse, this is a workaround for an ambiguous chargen_plank reference in the vanilla game. + // there is one at -22,16 and one at -2,-9, the latter should be used. + for (std::map, CellStore>::reverse_iterator iter = mExteriors.rbegin(); + iter!=mExteriors.rend(); ++iter) { Ptr ptr = getPtrAndCache (name, iter->second); if (!ptr.isEmpty()) diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index a04267f49..3e7e7a5f9 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -552,23 +552,16 @@ namespace MWWorld template <> class Store : public StoreBase { - struct ExtCmp - { - bool operator()(const ESM::Cell &x, const ESM::Cell &y) { - if (x.mData.mX == y.mData.mX) { - return x.mData.mY < y.mData.mY; - } - return x.mData.mX < y.mData.mX; - } - }; - struct DynamicExtCmp { bool operator()(const std::pair &left, const std::pair &right) const { - if (left.first == right.first) { - return left.second < right.second; - } - return left.first < right.first; + if (left.first == right.first && left.second == right.second) + return false; + + if (left.first == right.first) + return left.second > right.second; + + return left.first > right.first; } }; From 10ef0a34d94909522fd69141f3a55792bd00e503 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 17:54:14 +0200 Subject: [PATCH 221/226] Update effects even when main animation is paused (Fixes #1585) --- apps/openmw/mwmechanics/character.cpp | 2 ++ apps/openmw/mwrender/animation.hpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index bd076bead..2c5d68ceb 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1449,6 +1449,8 @@ void CharacterController::update(float duration) if(mMovementAnimVelocity > 0) world->queueMovement(mPtr, moved); } + else if (mAnimation) + mAnimation->updateEffects(duration); mSkipAnim = false; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 1544d42c9..e15bd6ecb 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -226,9 +226,6 @@ public: virtual void preRender (Ogre::Camera* camera); virtual void setAlpha(float alpha) {} -private: - void updateEffects(float duration); - public: void updatePtr(const MWWorld::Ptr &ptr); @@ -301,6 +298,9 @@ public: virtual Ogre::Vector3 runAnimation(float duration); + /// This is typically called as part of runAnimation, but may be called manually if needed. + void updateEffects(float duration); + virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool show) {} virtual void attachArrow() {} From 4949aa1fbb316cb8805bb5df4a6588581a41f3d0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Jun 2014 18:20:57 +0200 Subject: [PATCH 222/226] Add hack required for unnamed animated collision shapes (in_dagoth_bridge00.nif) --- apps/openmw/mwworld/physicssystem.cpp | 13 ++++++++----- components/nifogre/skeleton.cpp | 20 +++++++++++++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 6802d6118..201393761 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -58,13 +58,16 @@ void animateCollisionShapes (std::map::iterator shapeIt = shapes.begin(); shapeIt != shapes.end(); ++shapeIt) { - Ogre::Node* bone = animation->getNode(shapeIt->first); - // FIXME: this will happen for nodes with empty names. Ogre's SkeletonInstance::cloneBoneAndChildren - // will assign an auto-generated name if the bone name was empty. We could use the bone handle instead of - // the bone name, but that is a bit tricky to retrieve. + Ogre::Node* bone; + if (shapeIt->first.empty()) + // HACK: see NifSkeletonLoader::buildBones + bone = animation->getNode(" "); + else + bone = animation->getNode(shapeIt->first); + if (bone == NULL) - continue; + throw std::runtime_error("can't find bone"); btCompoundShape* compound = dynamic_cast(instance.mCompound); diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 26647e595..c96f03950 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -14,10 +14,24 @@ namespace NifOgre void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent) { Ogre::Bone *bone; - if(!skel->hasBone(node->name)) - bone = skel->createBone(node->name); + if (node->name.empty()) + { + // HACK: use " " instead of empty name, otherwise Ogre will replace it with an auto-generated + // name in SkeletonInstance::cloneBoneAndChildren. + static const char* emptyname = " "; + if (!skel->hasBone(emptyname)) + bone = skel->createBone(emptyname); + else + bone = skel->createBone(); + } else - bone = skel->createBone(); + { + if(!skel->hasBone(node->name)) + bone = skel->createBone(node->name); + else + bone = skel->createBone(); + } + if(parent) parent->addChild(bone); mNifToOgreHandleMap[node->recIndex] = bone->getHandle(); From e25fa6c157e97baa6e49de90163da629ea29c0ed Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 29 Jun 2014 02:42:36 +0200 Subject: [PATCH 223/226] Refactor non-distant land terrain path to a grid based implementation (Fixes #1562) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 18 +- apps/openmw/mwrender/terraingrid.cpp | 165 +++++++++ apps/openmw/mwrender/terraingrid.hpp | 75 +++++ apps/openmw/mwrender/terrainstorage.hpp | 4 +- components/CMakeLists.txt | 2 +- components/terrain/backgroundloader.cpp | 0 components/terrain/chunk.cpp | 14 +- components/terrain/chunk.hpp | 10 +- components/terrain/defaultworld.cpp | 315 +++++++++++++++++ components/terrain/defaultworld.hpp | 156 +++++++++ components/terrain/material.cpp | 4 +- components/terrain/material.hpp | 7 +- components/terrain/quadtreenode.cpp | 16 +- components/terrain/quadtreenode.hpp | 8 +- components/terrain/world.cpp | 390 +++------------------- components/terrain/world.hpp | 134 ++------ 17 files changed, 825 insertions(+), 495 deletions(-) create mode 100644 apps/openmw/mwrender/terraingrid.cpp create mode 100644 apps/openmw/mwrender/terraingrid.hpp create mode 100644 components/terrain/backgroundloader.cpp create mode 100644 components/terrain/defaultworld.cpp create mode 100644 components/terrain/defaultworld.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 7c047adef..5c5b0d16c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,7 +15,7 @@ add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows characterpreview globalmap videoplayer ripplesimulation refraction - terrainstorage renderconst effectmanager weaponanimation + terrainstorage renderconst effectmanager weaponanimation terraingrid ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ee1cbfe5d..23edb3a7f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" @@ -45,6 +45,7 @@ #include "globalmap.hpp" #include "terrainstorage.hpp" #include "effectmanager.hpp" +#include "terraingrid.hpp" using namespace MWRender; using namespace Ogre; @@ -223,6 +224,9 @@ MWRender::Camera* RenderingManager::getCamera() const void RenderingManager::removeCell (MWWorld::CellStore *store) { + if (store->isExterior()) + mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); + mLocalMap->saveFogOfWar(store); mObjects->removeCell(store); mActors->removeCell(store); @@ -241,6 +245,9 @@ bool RenderingManager::toggleWater() void RenderingManager::cellAdded (MWWorld::CellStore *store) { + if (store->isExterior()) + mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); + mObjects->buildStaticGeometry (*store); sh::Factory::getInstance().unloadUnreferencedMaterials(); mDebugging->cellAdded(store); @@ -1039,9 +1046,12 @@ void RenderingManager::enableTerrain(bool enable) { if (!mTerrain) { - mTerrain = new Terrain::World(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, - Settings::Manager::getBool("distant land", "Terrain"), - Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); + if (Settings::Manager::getBool("distant land", "Terrain")) + mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); + else + mTerrain = new MWRender::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY); mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), Settings::Manager::getBool("split", "Shadows")); mTerrain->update(mRendering.getCamera()->getRealPosition()); diff --git a/apps/openmw/mwrender/terraingrid.cpp b/apps/openmw/mwrender/terraingrid.cpp new file mode 100644 index 000000000..4688fbfd9 --- /dev/null +++ b/apps/openmw/mwrender/terraingrid.cpp @@ -0,0 +1,165 @@ +#include "terraingrid.hpp" + +#include +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include + +namespace MWRender +{ + +TerrainGrid::TerrainGrid(Ogre::SceneManager *sceneMgr, Terrain::Storage *storage, int visibilityFlags, bool shaders, Terrain::Alignment align) + : Terrain::World(sceneMgr, storage, visibilityFlags, shaders, align) + , mVisible(true) +{ + mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); +} + +TerrainGrid::~TerrainGrid() +{ + while (!mGrid.empty()) + { + unloadCell(mGrid.begin()->first.first, mGrid.begin()->first.second); + } + + mSceneMgr->destroySceneNode(mRootNode); +} + +void TerrainGrid::update(const Ogre::Vector3 &cameraPos) +{ +} + +void TerrainGrid::loadCell(int x, int y) +{ + if (mGrid.find(std::make_pair(x, y)) != mGrid.end()) + return; // already loaded + + Ogre::Vector2 center(x+0.5, y+0.5); + float minH, maxH; + if (!mStorage->getMinMaxHeights(1, center, minH, maxH)) + return; // no terrain defined + + Ogre::Vector3 min (-0.5*mStorage->getCellWorldSize(), + -0.5*mStorage->getCellWorldSize(), + minH); + Ogre::Vector3 max (0.5*mStorage->getCellWorldSize(), + 0.5*mStorage->getCellWorldSize(), + maxH); + + Ogre::AxisAlignedBox bounds(min, max); + + GridElement element; + + Ogre::Vector2 worldCenter = center*mStorage->getCellWorldSize(); + element.mSceneNode = mRootNode->createChildSceneNode(Ogre::Vector3(worldCenter.x, worldCenter.y, 0)); + + std::vector positions; + std::vector normals; + std::vector colours; + mStorage->fillVertexBuffers(0, 1, center, mAlign, positions, normals, colours); + + element.mChunk = new Terrain::Chunk(mCache.getUVBuffer(), bounds, positions, normals, colours); + element.mChunk->setIndexBuffer(mCache.getIndexBuffer(0)); + + std::vector blendmaps; + std::vector layerList; + mStorage->getBlendmaps(1, center, mShaders, blendmaps, layerList); + + element.mMaterialGenerator.setLayerList(layerList); + + // upload blendmaps to GPU + std::vector blendTextures; + for (std::vector::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) + { + static int count=0; + Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/" + + Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, it->getWidth(), it->getHeight(), 0, it->format); + + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(it->data, it->getWidth()*it->getHeight()*Ogre::PixelUtil::getNumElemBytes(it->format), true)); + map->loadRawData(stream, it->getWidth(), it->getHeight(), it->format); + blendTextures.push_back(map); + } + + element.mMaterialGenerator.setBlendmapList(blendTextures); + + element.mSceneNode->attachObject(element.mChunk); + updateMaterial(element); + + mGrid[std::make_pair(x,y)] = element; +} + +void TerrainGrid::updateMaterial(GridElement &element) +{ + element.mMaterialGenerator.enableShadows(getShadowsEnabled()); + element.mMaterialGenerator.enableSplitShadows(getSplitShadowsEnabled()); + element.mChunk->setMaterial(element.mMaterialGenerator.generate()); +} + +void TerrainGrid::unloadCell(int x, int y) +{ + Grid::iterator it = mGrid.find(std::make_pair(x,y)); + if (it == mGrid.end()) + return; + + GridElement& element = it->second; + delete element.mChunk; + element.mChunk = NULL; + + const std::vector& blendmaps = element.mMaterialGenerator.getBlendmapList(); + for (std::vector::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) + Ogre::TextureManager::getSingleton().remove((*it)->getName()); + + mSceneMgr->destroySceneNode(element.mSceneNode); + element.mSceneNode = NULL; + + mGrid.erase(it); +} + +void TerrainGrid::applyMaterials(bool shadows, bool splitShadows) +{ + mShadows = shadows; + mSplitShadows = splitShadows; + for (Grid::iterator it = mGrid.begin(); it != mGrid.end(); ++it) + { + updateMaterial(it->second); + } +} + +bool TerrainGrid::getVisible() +{ + return mVisible; +} + +void TerrainGrid::setVisible(bool visible) +{ + mVisible = visible; + mRootNode->setVisible(visible); +} + +Ogre::AxisAlignedBox TerrainGrid::getWorldBoundingBox (const Ogre::Vector2& center) +{ + int cellX, cellY; + MWBase::Environment::get().getWorld()->positionToIndex(center.x, center.y, cellX, cellY); + + Grid::iterator it = mGrid.find(std::make_pair(cellX, cellY)); + if (it == mGrid.end()) + return Ogre::AxisAlignedBox::BOX_NULL; + + Terrain::Chunk* chunk = it->second.mChunk; + Ogre::SceneNode* node = it->second.mSceneNode; + Ogre::AxisAlignedBox box = chunk->getBoundingBox(); + box = Ogre::AxisAlignedBox(box.getMinimum() + node->getPosition(), box.getMaximum() + node->getPosition()); + return box; +} + +void TerrainGrid::syncLoad() +{ + +} + +} diff --git a/apps/openmw/mwrender/terraingrid.hpp b/apps/openmw/mwrender/terraingrid.hpp new file mode 100644 index 000000000..1b5250dcf --- /dev/null +++ b/apps/openmw/mwrender/terraingrid.hpp @@ -0,0 +1,75 @@ +#ifndef OPENMW_MWRENDER_TERRAINGRID_H +#define OPENMW_MWRENDER_TERRAINGRID_H + +#include +#include + +namespace Terrain +{ + class Chunk; +} + +namespace MWRender +{ + + struct GridElement + { + Ogre::SceneNode* mSceneNode; + + Terrain::MaterialGenerator mMaterialGenerator; + + Terrain::Chunk* mChunk; + }; + + /// @brief Simple terrain implementation that loads cells in a grid, with no LOD + class TerrainGrid : public Terrain::World + { + public: + /// @note takes ownership of \a storage + /// @param sceneMgr scene manager to use + /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) + /// @param visbilityFlags visibility flags for the created meshes + /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually + /// faster so this is just here for compatibility. + /// @param align The align of the terrain, see Alignment enum + TerrainGrid(Ogre::SceneManager* sceneMgr, + Terrain::Storage* storage, int visibilityFlags, bool shaders, Terrain::Alignment align); + ~TerrainGrid(); + + /// Update chunk LODs according to this camera position + virtual void update (const Ogre::Vector3& cameraPos); + + virtual void loadCell(int x, int y); + virtual void unloadCell(int x, int y); + + /// Get the world bounding box of a chunk of terrain centered at \a center + virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center); + + /// Show or hide the whole terrain + /// @note this setting may be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden + virtual void setVisible(bool visible); + virtual bool getVisible(); + + /// Recreate materials used by terrain chunks. This should be called whenever settings of + /// the material factory are changed. (Relying on the factory to update those materials is not + /// enough, since turning a feature on/off can change the number of texture units available for layer/blend + /// textures, and to properly respond to this we may need to change the structure of the material, such as + /// adding or removing passes. This can only be achieved by a full rebuild.) + virtual void applyMaterials(bool shadows, bool splitShadows); + + /// Wait until all background loading is complete. + virtual void syncLoad(); + + private: + void updateMaterial (GridElement& element); + + typedef std::map, GridElement> Grid; + Grid mGrid; + + Ogre::SceneNode* mRootNode; + bool mVisible; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index b5e6012f3..c1fb74445 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -46,7 +46,7 @@ namespace MWRender /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. - /// @note May be called from *one* background thread. + /// @note May be called from background threads. /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - @@ -62,7 +62,7 @@ namespace MWRender /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once. /// @note The terrain chunks shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. - /// @note May be called from *one* background thread. + /// @note May be called from background threads. /// @param nodes A collection of nodes for which to retrieve the aforementioned data /// @param out Output vector /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 060e3472d..b8ebb84b1 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 storage material buffercache defs + quadtreenode chunk world defaultworld storage material buffercache defs backgroundloader ) add_component_dir (loadinglistener diff --git a/components/terrain/backgroundloader.cpp b/components/terrain/backgroundloader.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index bb8710b87..9c60ee017 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -8,19 +8,17 @@ #include - -#include "world.hpp" // FIXME: for LoadResponseData, move to backgroundloader.hpp - namespace Terrain { - Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data) + Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, + const std::vector& positions, const std::vector& normals, const std::vector& colours) : mBounds(bounds) , mOwnMaterial(false) { mVertexData = OGRE_NEW Ogre::VertexData; mVertexData->vertexStart = 0; - mVertexData->vertexCount = data.mPositions.size()/3; + mVertexData->vertexCount = positions.size()/3; // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration; @@ -48,9 +46,9 @@ namespace Terrain Ogre::HardwareVertexBufferSharedPtr colourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &data.mPositions[0], true); - normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &data.mNormals[0], true); - colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &data.mColours[0], true); + vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); + normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); + colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colours[0], true); mVertexData->vertexBufferBinding->setBinding(0, vertexBuffer); mVertexData->vertexBufferBinding->setBinding(1, normalBuffer); diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp index 9550b3046..9b2ed76ac 100644 --- a/components/terrain/chunk.hpp +++ b/components/terrain/chunk.hpp @@ -7,16 +7,16 @@ namespace Terrain { - class BufferCache; - struct LoadResponseData; - /** - * @brief Renders a chunk of terrain, either using alpha splatting or a composite map. + * @brief A movable object representing a chunk of terrain. */ class Chunk : public Ogre::Renderable, public Ogre::MovableObject { public: - Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data); + Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, + const std::vector& positions, + const std::vector& normals, + const std::vector& colours); virtual ~Chunk(); diff --git a/components/terrain/defaultworld.cpp b/components/terrain/defaultworld.cpp new file mode 100644 index 000000000..943658235 --- /dev/null +++ b/components/terrain/defaultworld.cpp @@ -0,0 +1,315 @@ +#include "defaultworld.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "storage.hpp" +#include "quadtreenode.hpp" + +namespace +{ + + bool isPowerOfTwo(int x) + { + return ( (x > 0) && ((x & (x - 1)) == 0) ); + } + + int nextPowerOfTwo (int v) + { + if (isPowerOfTwo(v)) return v; + int depth=0; + while(v) + { + v >>= 1; + depth++; + } + return 1 << depth; + } + + Terrain::QuadTreeNode* findNode (const Ogre::Vector2& center, Terrain::QuadTreeNode* node) + { + if (center == node->getCenter()) + return node; + + if (center.x > node->getCenter().x && center.y > node->getCenter().y) + return findNode(center, node->getChild(Terrain::NE)); + else if (center.x > node->getCenter().x && center.y < node->getCenter().y) + return findNode(center, node->getChild(Terrain::SE)); + else if (center.x < node->getCenter().x && center.y > node->getCenter().y) + return findNode(center, node->getChild(Terrain::NW)); + else //if (center.x < node->getCenter().x && center.y < node->getCenter().y) + return findNode(center, node->getChild(Terrain::SW)); + } + +} + +namespace Terrain +{ + + const Ogre::uint REQ_ID_CHUNK = 1; + const Ogre::uint REQ_ID_LAYERS = 2; + + DefaultWorld::DefaultWorld(Ogre::SceneManager* sceneMgr, + Storage* storage, int visibilityFlags, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) + : World(sceneMgr, storage, visibilityFlags, shaders, align) + , mMinBatchSize(minBatchSize) + , mMaxBatchSize(maxBatchSize) + , mVisible(true) + , mMaxX(0) + , mMinX(0) + , mMaxY(0) + , mMinY(0) + , mChunksLoading(0) + , mWorkQueueChannel(0) + , mLayerLoadPending(true) + { +#if TERRAIN_USE_SHADER == 0 + if (mShaders) + std::cerr << "Compiled Terrain without shader support, disabling..." << std::endl; + mShaders = false; +#endif + + mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + + /// \todo make composite map size configurable + Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a"); + mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual( + "terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, 128, 128, 0, Ogre::PF_A8B8G8R8, Ogre::TU_RENDERTARGET); + mCompositeMapRenderTarget = mCompositeMapRenderTexture->getBuffer()->getRenderTarget(); + mCompositeMapRenderTarget->setAutoUpdated(false); + mCompositeMapRenderTarget->addViewport(compositeMapCam); + + storage->getBounds(mMinX, mMaxX, mMinY, mMaxY); + + int origSizeX = mMaxX-mMinX; + int origSizeY = mMaxY-mMinY; + + // Dividing a quad tree only works well for powers of two, so round up to the nearest one + int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); + + // Adjust the center according to the new size + float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f; + float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; + + mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + + // While building the quadtree, remember leaf nodes since we need to load their layers + LayersRequestData data; + data.mPack = getShadersEnabled(); + + mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); + buildQuadTree(mRootNode, data.mNodes); + //loadingListener->indicateProgress(); + mRootNode->initAabb(); + //loadingListener->indicateProgress(); + mRootNode->initNeighbours(); + //loadingListener->indicateProgress(); + + Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); + mWorkQueueChannel = wq->getChannel("LargeTerrain"); + wq->addRequestHandler(mWorkQueueChannel, this); + wq->addResponseHandler(mWorkQueueChannel, this); + + // Start loading layers in the background (for leaf nodes) + wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data)); + } + + DefaultWorld::~DefaultWorld() + { + Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); + wq->removeRequestHandler(mWorkQueueChannel, this); + wq->removeResponseHandler(mWorkQueueChannel, this); + + delete mRootNode; + } + + void DefaultWorld::buildQuadTree(QuadTreeNode *node, std::vector& leafs) + { + float halfSize = node->getSize()/2.f; + + if (node->getSize() <= mMinBatchSize) + { + // We arrived at a leaf + float minZ,maxZ; + Ogre::Vector2 center = node->getCenter(); + float cellWorldSize = getStorage()->getCellWorldSize(); + if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) + { + Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), + Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ)); + convertBounds(bounds); + node->setBoundingBox(bounds); + leafs.push_back(node); + } + else + node->markAsDummy(); // no data available for this node, skip it + return; + } + + if (node->getCenter().x - halfSize > mMaxX + || node->getCenter().x + halfSize < mMinX + || node->getCenter().y - halfSize > mMaxY + || node->getCenter().y + halfSize < mMinY ) + // Out of bounds of the actual terrain - this will happen because + // we rounded the size up to the next power of two + { + node->markAsDummy(); + return; + } + + // Not a leaf, create its children + node->createChild(SW, halfSize, node->getCenter() - halfSize/2.f); + node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f)); + node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f)); + node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f); + buildQuadTree(node->getChild(SW), leafs); + buildQuadTree(node->getChild(SE), leafs); + buildQuadTree(node->getChild(NW), leafs); + buildQuadTree(node->getChild(NE), leafs); + + // if all children are dummy, we are also dummy + for (int i=0; i<4; ++i) + { + if (!node->getChild((ChildDirection)i)->isDummy()) + return; + } + node->markAsDummy(); + } + + void DefaultWorld::update(const Ogre::Vector3& cameraPos) + { + if (!mVisible) + return; + mRootNode->update(cameraPos); + mRootNode->updateIndexBuffers(); + } + + Ogre::AxisAlignedBox DefaultWorld::getWorldBoundingBox (const Ogre::Vector2& center) + { + if (center.x > mMaxX + || center.x < mMinX + || center.y > mMaxY + || center.y < mMinY) + return Ogre::AxisAlignedBox::BOX_NULL; + QuadTreeNode* node = findNode(center, mRootNode); + return node->getWorldBoundingBox(); + } + + void DefaultWorld::renderCompositeMap(Ogre::TexturePtr target) + { + mCompositeMapRenderTarget->update(); + target->getBuffer()->blit(mCompositeMapRenderTexture->getBuffer()); + } + + void DefaultWorld::clearCompositeMapSceneManager() + { + mCompositeMapSceneMgr->destroyAllManualObjects(); + mCompositeMapSceneMgr->clearScene(); + } + + void DefaultWorld::applyMaterials(bool shadows, bool splitShadows) + { + mShadows = shadows; + mSplitShadows = splitShadows; + mRootNode->applyMaterials(); + } + + void DefaultWorld::setVisible(bool visible) + { + if (visible && !mVisible) + mSceneMgr->getRootSceneNode()->addChild(mRootSceneNode); + else if (!visible && mVisible) + mSceneMgr->getRootSceneNode()->removeChild(mRootSceneNode); + + mVisible = visible; + } + + bool DefaultWorld::getVisible() + { + return mVisible; + } + + void DefaultWorld::syncLoad() + { + while (mChunksLoading || mLayerLoadPending) + { + OGRE_THREAD_SLEEP(0); + Ogre::Root::getSingleton().getWorkQueue()->processResponses(); + } + } + + Ogre::WorkQueue::Response* DefaultWorld::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ) + { + if (req->getType() == REQ_ID_CHUNK) + { + const LoadRequestData data = Ogre::any_cast(req->getData()); + + QuadTreeNode* node = data.mNode; + + LoadResponseData* responseData = new LoadResponseData(); + + getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(), + responseData->mPositions, responseData->mNormals, responseData->mColours); + + return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + } + else // REQ_ID_LAYERS + { + const LayersRequestData data = Ogre::any_cast(req->getData()); + + LayersResponseData* responseData = new LayersResponseData(); + + getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack); + + return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + } + } + + void DefaultWorld::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ) + { + assert(res->succeeded() && "Response failure not handled"); + + if (res->getRequest()->getType() == REQ_ID_CHUNK) + { + LoadResponseData* data = Ogre::any_cast(res->getData()); + + const LoadRequestData requestData = Ogre::any_cast(res->getRequest()->getData()); + + requestData.mNode->load(*data); + + delete data; + + --mChunksLoading; + } + else // REQ_ID_LAYERS + { + LayersResponseData* data = Ogre::any_cast(res->getData()); + + for (std::vector::iterator it = data->mLayerCollections.begin(); it != data->mLayerCollections.end(); ++it) + { + it->mTarget->loadLayers(*it); + } + + delete data; + + mRootNode->loadMaterials(); + + mLayerLoadPending = false; + } + } + + void DefaultWorld::queueLoad(QuadTreeNode *node) + { + LoadRequestData data; + data.mNode = node; + + Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data)); + ++mChunksLoading; + } +} diff --git a/components/terrain/defaultworld.hpp b/components/terrain/defaultworld.hpp new file mode 100644 index 000000000..8769a0d88 --- /dev/null +++ b/components/terrain/defaultworld.hpp @@ -0,0 +1,156 @@ +#ifndef COMPONENTS_TERRAIN_H +#define COMPONENTS_TERRAIN_H + +#include +#include +#include + +#include "world.hpp" + +namespace Ogre +{ + class Camera; +} + +namespace Terrain +{ + + class QuadTreeNode; + class Storage; + + /** + * @brief A quadtree-based terrain implementation suitable for large data sets. \n + * Near cells are rendered with alpha splatting, distant cells are merged + * together in batches and have their layers pre-rendered onto a composite map. \n + * Cracks at LOD transitions are avoided using stitching. + * @note Multiple cameras are not supported yet + */ + class DefaultWorld : public World, public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler + { + public: + /// @note takes ownership of \a storage + /// @param sceneMgr scene manager to use + /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) + /// @param visbilityFlags visibility flags for the created meshes + /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually + /// faster so this is just here for compatibility. + /// @param align The align of the terrain, see Alignment enum + /// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree. + /// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree. + DefaultWorld(Ogre::SceneManager* sceneMgr, + Storage* storage, int visibilityFlags, bool shaders, Alignment align, float minBatchSize, float maxBatchSize); + ~DefaultWorld(); + + /// Update chunk LODs according to this camera position + /// @note Calling this method might lead to composite textures being rendered, so it is best + /// not to call it when render commands are still queued, since that would cause a flush. + virtual void update (const Ogre::Vector3& cameraPos); + + /// Get the world bounding box of a chunk of terrain centered at \a center + virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center); + + Ogre::SceneNode* getRootSceneNode() { return mRootSceneNode; } + + /// Show or hide the whole terrain + /// @note this setting will be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden + virtual void setVisible(bool visible); + virtual bool getVisible(); + + /// Recreate materials used by terrain chunks. This should be called whenever settings of + /// the material factory are changed. (Relying on the factory to update those materials is not + /// enough, since turning a feature on/off can change the number of texture units available for layer/blend + /// textures, and to properly respond to this we may need to change the structure of the material, such as + /// adding or removing passes. This can only be achieved by a full rebuild.) + virtual void applyMaterials(bool shadows, bool splitShadows); + + int getMaxBatchSize() { return mMaxBatchSize; } + + /// Wait until all background loading is complete. + void syncLoad(); + + private: + // Called from a background worker thread + Ogre::WorkQueue::Response* handleRequest(const Ogre::WorkQueue::Request* req, const Ogre::WorkQueue* srcQ); + // Called from the main thread + void handleResponse(const Ogre::WorkQueue::Response* res, const Ogre::WorkQueue* srcQ); + Ogre::uint16 mWorkQueueChannel; + + bool mVisible; + + QuadTreeNode* mRootNode; + Ogre::SceneNode* mRootSceneNode; + + /// The number of chunks currently loading in a background thread. If 0, we have finished loading! + int mChunksLoading; + + Ogre::SceneManager* mCompositeMapSceneMgr; + + /// Bounds in cell units + float mMinX, mMaxX, mMinY, mMaxY; + + /// Minimum size of a terrain batch along one side (in cell units) + float mMinBatchSize; + /// Maximum size of a terrain batch along one side (in cell units) + float mMaxBatchSize; + + void buildQuadTree(QuadTreeNode* node, std::vector& leafs); + + // Are layers for leaf nodes loaded? This is done once at startup (but in a background thread) + bool mLayerLoadPending; + + public: + // ----INTERNAL---- + Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } + + bool areLayersLoaded() { return !mLayerLoadPending; } + + // Delete all quads + void clearCompositeMapSceneManager(); + void renderCompositeMap (Ogre::TexturePtr target); + + // Adds a WorkQueue request to load a chunk for this node in the background. + void queueLoad (QuadTreeNode* node); + + private: + Ogre::RenderTarget* mCompositeMapRenderTarget; + Ogre::TexturePtr mCompositeMapRenderTexture; + }; + + struct LoadRequestData + { + QuadTreeNode* mNode; + + friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r) + { return o; } + }; + + struct LoadResponseData + { + std::vector mPositions; + std::vector mNormals; + std::vector mColours; + + friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r) + { return o; } + }; + + struct LayersRequestData + { + std::vector mNodes; + bool mPack; + + friend std::ostream& operator<<(std::ostream& o, const LayersRequestData& r) + { return o; } + }; + + struct LayersResponseData + { + std::vector mLayerCollections; + + friend std::ostream& operator<<(std::ostream& o, const LayersResponseData& r) + { return o; } + }; + +} + +#endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 771dcdf91..a40e576ed 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -36,8 +36,8 @@ std::string getBlendmapComponentForLayer (int layerIndex) namespace Terrain { - MaterialGenerator::MaterialGenerator(bool shaders) - : mShaders(shaders) + MaterialGenerator::MaterialGenerator() + : mShaders(true) , mShadows(false) , mSplitShadows(false) , mNormalMapping(true) diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index 7f607a7af..b9000cb1b 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -11,11 +11,7 @@ namespace Terrain class MaterialGenerator { public: - /// @param layerList layer textures - /// @param blendmapList blend textures - /// @param shaders Whether to use shaders. With a shader, blendmap packing can be used (4 channels instead of one), - /// so if this parameter is true, then the supplied blend maps are expected to be packed. - MaterialGenerator (bool shaders); + MaterialGenerator (); void setLayerList (const std::vector& layerList) { mLayerList = layerList; } bool hasLayers() { return mLayerList.size(); } @@ -23,6 +19,7 @@ namespace Terrain const std::vector& getBlendmapList() { return mBlendmapList; } void setCompositeMap (const std::string& name) { mCompositeMap = name; } + void enableShaders(bool shaders) { mShaders = shaders; } void enableShadows(bool shadows) { mShadows = shadows; } void enableNormalMapping(bool normalMapping) { mNormalMapping = normalMapping; } void enableParallaxMapping(bool parallaxMapping) { mParallaxMapping = parallaxMapping; } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 37c638da0..44974eeb1 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -6,7 +6,7 @@ #include #include -#include "world.hpp" +#include "defaultworld.hpp" #include "chunk.hpp" #include "storage.hpp" #include "buffercache.hpp" @@ -142,7 +142,7 @@ namespace } } -QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) +QuadTreeNode::QuadTreeNode(DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) : mMaterialGenerator(NULL) , mIsDummy(false) , mSize(size) @@ -178,7 +178,8 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const mSceneNode->setPosition(sceneNodePos); - mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); + mMaterialGenerator = new MaterialGenerator(); + mMaterialGenerator->enableShaders(mTerrain->getShadersEnabled()); } void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er) @@ -386,11 +387,9 @@ void QuadTreeNode::load(const LoadResponseData &data) { assert (!mChunk); - mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data); - mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); + mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data.mPositions, data.mNormals, data.mColours); + mChunk->setVisibilityFlags(mTerrain->getVisibilityFlags()); mChunk->setCastShadows(true); - if (!mTerrain->getDistantLandEnabled()) - mChunk->setRenderingDistance(8192); mSceneNode->attachObject(mChunk); mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); @@ -550,7 +549,8 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) if (mIsDummy) { // TODO - store this default material somewhere instead of creating one for each empty cell - MaterialGenerator matGen(mTerrain->getShadersEnabled()); + MaterialGenerator matGen; + matGen.enableShaders(mTerrain->getShadersEnabled()); std::vector layer; layer.push_back(mTerrain->getStorage()->getDefaultLayer()); matGen.setLayerList(layer); diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index c57589487..626572701 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -14,7 +14,7 @@ namespace Ogre namespace Terrain { - class World; + class DefaultWorld; class Chunk; class MaterialGenerator; struct LoadResponseData; @@ -48,7 +48,7 @@ namespace Terrain /// @param size size (in *cell* units!) /// @param center center (in *cell* units!) /// @param parent parent node - QuadTreeNode (World* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent); + QuadTreeNode (DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent); ~QuadTreeNode(); /// Rebuild all materials @@ -95,7 +95,7 @@ namespace Terrain const Ogre::AxisAlignedBox& getWorldBoundingBox(); - World* getTerrain() { return mTerrain; } + DefaultWorld* getTerrain() { return mTerrain; } /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. /// @param force Always choose to render this node, even if not the perfect LOD. @@ -158,7 +158,7 @@ namespace Terrain Chunk* mChunk; - World* mTerrain; + DefaultWorld* mTerrain; Ogre::TexturePtr mCompositeMap; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 3d968470f..49fb9b5c9 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -1,356 +1,60 @@ #include "world.hpp" #include -#include -#include -#include -#include -#include -#include #include "storage.hpp" -#include "quadtreenode.hpp" - -namespace -{ - - bool isPowerOfTwo(int x) - { - return ( (x > 0) && ((x & (x - 1)) == 0) ); - } - - int nextPowerOfTwo (int v) - { - if (isPowerOfTwo(v)) return v; - int depth=0; - while(v) - { - v >>= 1; - depth++; - } - return 1 << depth; - } - - Terrain::QuadTreeNode* findNode (const Ogre::Vector2& center, Terrain::QuadTreeNode* node) - { - if (center == node->getCenter()) - return node; - - if (center.x > node->getCenter().x && center.y > node->getCenter().y) - return findNode(center, node->getChild(Terrain::NE)); - else if (center.x > node->getCenter().x && center.y < node->getCenter().y) - return findNode(center, node->getChild(Terrain::SE)); - else if (center.x < node->getCenter().x && center.y > node->getCenter().y) - return findNode(center, node->getChild(Terrain::NW)); - else //if (center.x < node->getCenter().x && center.y < node->getCenter().y) - return findNode(center, node->getChild(Terrain::SW)); - } - -} namespace Terrain { - const Ogre::uint REQ_ID_CHUNK = 1; - const Ogre::uint REQ_ID_LAYERS = 2; +World::World(Ogre::SceneManager* sceneMgr, + Storage* storage, int visibilityFlags, bool shaders, Alignment align) + : mStorage(storage) + , mSceneMgr(sceneMgr) + , mVisibilityFlags(visibilityFlags) + , mShaders(shaders) + , mAlign(align) + , mCache(storage->getCellVertices()) +{ +} - World::World(Ogre::SceneManager* sceneMgr, - Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) - : mStorage(storage) - , mMinBatchSize(minBatchSize) - , mMaxBatchSize(maxBatchSize) - , mSceneMgr(sceneMgr) - , mVisibilityFlags(visibilityFlags) - , mDistantLand(distantLand) - , mShaders(shaders) - , mVisible(true) - , mAlign(align) - , mMaxX(0) - , mMinX(0) - , mMaxY(0) - , mMinY(0) - , mChunksLoading(0) - , mWorkQueueChannel(0) - , mCache(storage->getCellVertices()) - , mLayerLoadPending(true) +World::~World() +{ + delete mStorage; +} + +float World::getHeightAt(const Ogre::Vector3 &worldPos) +{ + return mStorage->getHeightAt(worldPos); +} + +void World::convertPosition(float &x, float &y, float &z) +{ + Terrain::convertPosition(mAlign, x, y, z); +} + +void World::convertPosition(Ogre::Vector3 &pos) +{ + convertPosition(pos.x, pos.y, pos.z); +} + +void World::convertBounds(Ogre::AxisAlignedBox& bounds) +{ + switch (mAlign) { -#if TERRAIN_USE_SHADER == 0 - if (mShaders) - std::cerr << "Compiled Terrain without shader support, disabling..." << std::endl; - mShaders = false; -#endif - - mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); - - /// \todo make composite map size configurable - Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a"); - mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual( - "terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, 128, 128, 0, Ogre::PF_A8B8G8R8, Ogre::TU_RENDERTARGET); - mCompositeMapRenderTarget = mCompositeMapRenderTexture->getBuffer()->getRenderTarget(); - mCompositeMapRenderTarget->setAutoUpdated(false); - mCompositeMapRenderTarget->addViewport(compositeMapCam); - - storage->getBounds(mMinX, mMaxX, mMinY, mMaxY); - - int origSizeX = mMaxX-mMinX; - int origSizeY = mMaxY-mMinY; - - // Dividing a quad tree only works well for powers of two, so round up to the nearest one - int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); - - // Adjust the center according to the new size - float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f; - float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; - - mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - - // While building the quadtree, remember leaf nodes since we need to load their layers - LayersRequestData data; - data.mPack = getShadersEnabled(); - - mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); - buildQuadTree(mRootNode, data.mNodes); - //loadingListener->indicateProgress(); - mRootNode->initAabb(); - //loadingListener->indicateProgress(); - mRootNode->initNeighbours(); - //loadingListener->indicateProgress(); - - Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); - mWorkQueueChannel = wq->getChannel("LargeTerrain"); - wq->addRequestHandler(mWorkQueueChannel, this); - wq->addResponseHandler(mWorkQueueChannel, this); - - // Start loading layers in the background (for leaf nodes) - wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data)); - } - - World::~World() - { - Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); - wq->removeRequestHandler(mWorkQueueChannel, this); - wq->removeResponseHandler(mWorkQueueChannel, this); - - delete mRootNode; - delete mStorage; - } - - void World::buildQuadTree(QuadTreeNode *node, std::vector& leafs) - { - float halfSize = node->getSize()/2.f; - - if (node->getSize() <= mMinBatchSize) - { - // We arrived at a leaf - float minZ,maxZ; - Ogre::Vector2 center = node->getCenter(); - float cellWorldSize = getStorage()->getCellWorldSize(); - if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) - { - Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), - Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ)); - convertBounds(bounds); - node->setBoundingBox(bounds); - leafs.push_back(node); - } - else - node->markAsDummy(); // no data available for this node, skip it - return; - } - - if (node->getCenter().x - halfSize > mMaxX - || node->getCenter().x + halfSize < mMinX - || node->getCenter().y - halfSize > mMaxY - || node->getCenter().y + halfSize < mMinY ) - // Out of bounds of the actual terrain - this will happen because - // we rounded the size up to the next power of two - { - node->markAsDummy(); - return; - } - - // Not a leaf, create its children - node->createChild(SW, halfSize, node->getCenter() - halfSize/2.f); - node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f)); - node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f)); - node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f); - buildQuadTree(node->getChild(SW), leafs); - buildQuadTree(node->getChild(SE), leafs); - buildQuadTree(node->getChild(NW), leafs); - buildQuadTree(node->getChild(NE), leafs); - - // if all children are dummy, we are also dummy - for (int i=0; i<4; ++i) - { - if (!node->getChild((ChildDirection)i)->isDummy()) - return; - } - node->markAsDummy(); - } - - void World::update(const Ogre::Vector3& cameraPos) - { - if (!mVisible) - return; - mRootNode->update(cameraPos); - mRootNode->updateIndexBuffers(); - } - - Ogre::AxisAlignedBox World::getWorldBoundingBox (const Ogre::Vector2& center) - { - if (center.x > mMaxX - || center.x < mMinX - || center.y > mMaxY - || center.y < mMinY) - return Ogre::AxisAlignedBox::BOX_NULL; - QuadTreeNode* node = findNode(center, mRootNode); - return node->getWorldBoundingBox(); - } - - void World::renderCompositeMap(Ogre::TexturePtr target) - { - mCompositeMapRenderTarget->update(); - target->getBuffer()->blit(mCompositeMapRenderTexture->getBuffer()); - } - - void World::clearCompositeMapSceneManager() - { - mCompositeMapSceneMgr->destroyAllManualObjects(); - mCompositeMapSceneMgr->clearScene(); - } - - float World::getHeightAt(const Ogre::Vector3 &worldPos) - { - return mStorage->getHeightAt(worldPos); - } - - void World::applyMaterials(bool shadows, bool splitShadows) - { - mShadows = shadows; - mSplitShadows = splitShadows; - mRootNode->applyMaterials(); - } - - void World::setVisible(bool visible) - { - if (visible && !mVisible) - mSceneMgr->getRootSceneNode()->addChild(mRootSceneNode); - else if (!visible && mVisible) - mSceneMgr->getRootSceneNode()->removeChild(mRootSceneNode); - - mVisible = visible; - } - - bool World::getVisible() - { - return mVisible; - } - - void World::convertPosition(float &x, float &y, float &z) - { - Terrain::convertPosition(mAlign, x, y, z); - } - - void World::convertPosition(Ogre::Vector3 &pos) - { - convertPosition(pos.x, pos.y, pos.z); - } - - void World::convertBounds(Ogre::AxisAlignedBox& bounds) - { - switch (mAlign) - { - case Align_XY: - return; - case Align_XZ: - convertPosition(bounds.getMinimum()); - convertPosition(bounds.getMaximum()); - // Because we changed sign of Z - std::swap(bounds.getMinimum().z, bounds.getMaximum().z); - return; - case Align_YZ: - convertPosition(bounds.getMinimum()); - convertPosition(bounds.getMaximum()); - return; - } - } - - void World::syncLoad() - { - while (mChunksLoading || mLayerLoadPending) - { - OGRE_THREAD_SLEEP(0); - Ogre::Root::getSingleton().getWorkQueue()->processResponses(); - } - } - - Ogre::WorkQueue::Response* World::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ) - { - if (req->getType() == REQ_ID_CHUNK) - { - const LoadRequestData data = Ogre::any_cast(req->getData()); - - QuadTreeNode* node = data.mNode; - - LoadResponseData* responseData = new LoadResponseData(); - - getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(), - responseData->mPositions, responseData->mNormals, responseData->mColours); - - return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); - } - else // REQ_ID_LAYERS - { - const LayersRequestData data = Ogre::any_cast(req->getData()); - - LayersResponseData* responseData = new LayersResponseData(); - - getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack); - - return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); - } - } - - void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ) - { - assert(res->succeeded() && "Response failure not handled"); - - if (res->getRequest()->getType() == REQ_ID_CHUNK) - { - LoadResponseData* data = Ogre::any_cast(res->getData()); - - const LoadRequestData requestData = Ogre::any_cast(res->getRequest()->getData()); - - requestData.mNode->load(*data); - - delete data; - - --mChunksLoading; - } - else // REQ_ID_LAYERS - { - LayersResponseData* data = Ogre::any_cast(res->getData()); - - for (std::vector::iterator it = data->mLayerCollections.begin(); it != data->mLayerCollections.end(); ++it) - { - it->mTarget->loadLayers(*it); - } - - delete data; - - mRootNode->loadMaterials(); - - mLayerLoadPending = false; - } - } - - void World::queueLoad(QuadTreeNode *node) - { - LoadRequestData data; - data.mNode = node; - - Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data)); - ++mChunksLoading; + case Align_XY: + return; + case Align_XZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + // Because we changed sign of Z + std::swap(bounds.getMinimum().z, bounds.getMaximum().z); + return; + case Align_YZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + return; } } + +} diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 26a6d034d..beca7903a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -1,50 +1,38 @@ -#ifndef COMPONENTS_TERRAIN_H -#define COMPONENTS_TERRAIN_H +#ifndef COMPONENTS_TERRAIN_WORLD_H +#define COMPONENTS_TERRAIN_WORLD_H -#include -#include -#include +#include #include "defs.hpp" #include "buffercache.hpp" namespace Ogre { - class Camera; + class SceneManager; } namespace Terrain { - - class QuadTreeNode; class Storage; /** - * @brief A quadtree-based terrain implementation suitable for large data sets. \n - * Near cells are rendered with alpha splatting, distant cells are merged - * together in batches and have their layers pre-rendered onto a composite map. \n - * Cracks at LOD transitions are avoided using stitching. - * @note Multiple cameras are not supported yet + * @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed + * is up to the implementation. */ - class World : public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler + class World { public: /// @note takes ownership of \a storage /// @param sceneMgr scene manager to use /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param visbilityFlags visibility flags for the created meshes - /// @param distantLand Whether to draw all of the terrain, or only a 3x3 grid around the camera. - /// This is a temporary option until it can be streamlined. /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually /// faster so this is just here for compatibility. /// @param align The align of the terrain, see Alignment enum - /// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree. - /// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree. World(Ogre::SceneManager* sceneMgr, - Storage* storage, int visiblityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize); - ~World(); + Storage* storage, int visiblityFlags, bool shaders, Alignment align); + virtual ~World(); - bool getDistantLandEnabled() { return mDistantLand; } bool getShadersEnabled() { return mShaders; } bool getShadowsEnabled() { return mShadows; } bool getSplitShadowsEnabled() { return mSplitShadows; } @@ -54,138 +42,60 @@ namespace Terrain /// Update chunk LODs according to this camera position /// @note Calling this method might lead to composite textures being rendered, so it is best /// not to call it when render commands are still queued, since that would cause a flush. - void update (const Ogre::Vector3& cameraPos); + virtual void update (const Ogre::Vector3& cameraPos) = 0; + + // This is only a hint and may be ignored by the implementation. + virtual void loadCell(int x, int y) {} + virtual void unloadCell(int x, int y) {} /// Get the world bounding box of a chunk of terrain centered at \a center - Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center); + virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center) = 0; Ogre::SceneManager* getSceneManager() { return mSceneMgr; } - Ogre::SceneNode* getRootSceneNode() { return mRootSceneNode; } - Storage* getStorage() { return mStorage; } /// Show or hide the whole terrain - /// @note this setting will be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden - void setVisible(bool visible); - bool getVisible(); + /// @note this setting may be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden + virtual void setVisible(bool visible) = 0; + virtual bool getVisible() = 0; /// Recreate materials used by terrain chunks. This should be called whenever settings of /// the material factory are changed. (Relying on the factory to update those materials is not /// enough, since turning a feature on/off can change the number of texture units available for layer/blend /// textures, and to properly respond to this we may need to change the structure of the material, such as /// adding or removing passes. This can only be achieved by a full rebuild.) - void applyMaterials(bool shadows, bool splitShadows); + virtual void applyMaterials(bool shadows, bool splitShadows) = 0; - int getVisiblityFlags() { return mVisibilityFlags; } - - int getMaxBatchSize() { return mMaxBatchSize; } - - void enableSplattingShader(bool enabled); + int getVisibilityFlags() { return mVisibilityFlags; } Alignment getAlign() { return mAlign; } /// Wait until all background loading is complete. - void syncLoad(); + virtual void syncLoad() {} - private: - // Called from a background worker thread - Ogre::WorkQueue::Response* handleRequest(const Ogre::WorkQueue::Request* req, const Ogre::WorkQueue* srcQ); - // Called from the main thread - void handleResponse(const Ogre::WorkQueue::Response* res, const Ogre::WorkQueue* srcQ); - Ogre::uint16 mWorkQueueChannel; - - bool mDistantLand; + protected: bool mShaders; bool mShadows; bool mSplitShadows; - bool mVisible; Alignment mAlign; - QuadTreeNode* mRootNode; - Ogre::SceneNode* mRootSceneNode; Storage* mStorage; int mVisibilityFlags; - /// The number of chunks currently loading in a background thread. If 0, we have finished loading! - int mChunksLoading; - Ogre::SceneManager* mSceneMgr; - Ogre::SceneManager* mCompositeMapSceneMgr; - - /// Bounds in cell units - float mMinX, mMaxX, mMinY, mMaxY; - - /// Minimum size of a terrain batch along one side (in cell units) - float mMinBatchSize; - /// Maximum size of a terrain batch along one side (in cell units) - float mMaxBatchSize; - - void buildQuadTree(QuadTreeNode* node, std::vector& leafs); BufferCache mCache; - // Are layers for leaf nodes loaded? This is done once at startup (but in a background thread) - bool mLayerLoadPending; - public: // ----INTERNAL---- - Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } BufferCache& getBufferCache() { return mCache; } - bool areLayersLoaded() { return !mLayerLoadPending; } - - // Delete all quads - void clearCompositeMapSceneManager(); - void renderCompositeMap (Ogre::TexturePtr target); - // Convert the given position from Z-up align, i.e. Align_XY to the wanted align set in mAlign void convertPosition (float& x, float& y, float& z); void convertPosition (Ogre::Vector3& pos); void convertBounds (Ogre::AxisAlignedBox& bounds); - - // Adds a WorkQueue request to load a chunk for this node in the background. - void queueLoad (QuadTreeNode* node); - - private: - Ogre::RenderTarget* mCompositeMapRenderTarget; - Ogre::TexturePtr mCompositeMapRenderTexture; - }; - - struct LoadRequestData - { - QuadTreeNode* mNode; - - friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r) - { return o; } - }; - - struct LoadResponseData - { - std::vector mPositions; - std::vector mNormals; - std::vector mColours; - - friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r) - { return o; } - }; - - struct LayersRequestData - { - std::vector mNodes; - bool mPack; - - friend std::ostream& operator<<(std::ostream& o, const LayersRequestData& r) - { return o; } - }; - - struct LayersResponseData - { - std::vector mLayerCollections; - - friend std::ostream& operator<<(std::ostream& o, const LayersResponseData& r) - { return o; } }; } From 74ed9cbb2d99a6d28a56dc5de2163075fdad1f31 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 29 Jun 2014 14:18:46 +0200 Subject: [PATCH 224/226] added cell rendering class --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/cell.cpp | 201 +++++++++++++++++++++++++++++++ apps/opencs/view/render/cell.hpp | 73 +++++++++++ 3 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/render/cell.cpp create mode 100644 apps/opencs/view/render/cell.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a8cc4ceae..c52f9d9bc 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -70,7 +70,7 @@ opencs_units (view/render opencs_units_noqt (view/render navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight - lightingbright object + lightingbright object cell ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp new file mode 100644 index 000000000..1e25f42e2 --- /dev/null +++ b/apps/opencs/view/render/cell.cpp @@ -0,0 +1,201 @@ + +#include "cell.hpp" + +#include +#include + +#include + +#include "../../model/world/idtable.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/data.hpp" + +bool CSVRender::Cell::removeObject (const std::string& id) +{ + std::map::iterator iter = + mObjects.find (Misc::StringUtils::lowerCase (id)); + + if (iter==mObjects.end()) + return false; + + delete iter->second; + mObjects.erase (iter); + return true; +} + +bool CSVRender::Cell::addObjects (int start, int end) +{ + CSMWorld::IdTable& references = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + + int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id); + int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); + int stateColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + + bool modified = false; + + for (int i=start; i<=end; ++i) + { + std::string cell = Misc::StringUtils::lowerCase (references.data ( + references.index (i, cellColumn)).toString().toUtf8().constData()); + + int state = references.data (references.index (i, stateColumn)).toInt(); + + if (cell==mId && state!=CSMWorld::RecordBase::State_Deleted) + { + std::string id = Misc::StringUtils::lowerCase (references.data ( + references.index (i, idColumn)).toString().toUtf8().constData()); + + mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false))); + modified = true; + } + } + + return modified; +} + +CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, + const std::string& id, const Ogre::Vector3& origin) +: mData (data), mId (Misc::StringUtils::lowerCase (id)) +{ + mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode(); + mCellNode->setPosition (origin); + + CSMWorld::IdTable& references = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + + int rows = references.rowCount(); + + addObjects (0, rows-1); +} + +CSVRender::Cell::~Cell() +{ + for (std::map::iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + delete iter->second; + + mCellNode->getCreator()->destroySceneNode (mCellNode); +} + +bool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + bool modified = false; + + for (std::map::iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + if (iter->second->referenceableDataChanged (topLeft, bottomRight)) + modified = true; + + return modified; +} + +bool CSVRender::Cell::referenceableAboutToBeRemoved (const QModelIndex& parent, int start, + int end) +{ + if (parent.isValid()) + return false; + + bool modified = false; + + for (std::map::iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + if (iter->second->referenceableAboutToBeRemoved (parent, start, end)) + modified = true; + + return modified; +} + +bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + CSMWorld::IdTable& references = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + + int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id); + int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); + int stateColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + + // list IDs in cell + std::map ids; // id, deleted state + + for (int i=topLeft.row(); i<=bottomRight.row(); ++i) + { + std::string cell = Misc::StringUtils::lowerCase (references.data ( + references.index (i, cellColumn)).toString().toUtf8().constData()); + + if (cell==mId) + { + std::string id = Misc::StringUtils::lowerCase (references.data ( + references.index (i, idColumn)).toString().toUtf8().constData()); + + int state = references.data (references.index (i, stateColumn)).toInt(); + + ids.insert (std::make_pair (id, state==CSMWorld::RecordBase::State_Deleted)); + } + } + + // perform update and remove where needed + bool modified = false; + + for (std::map::iterator iter (mObjects.begin()); + iter!=mObjects.end(); ++iter) + { + if (iter->second->referenceDataChanged (topLeft, bottomRight)) + modified = true; + + std::map::iterator iter2 = ids.find (iter->first); + + if (iter2!=ids.end()) + { + if (iter2->second) + { + removeObject (iter->first); + modified = true; + } + + ids.erase (iter2); + } + } + + // add new objects + for (std::map::iterator iter (ids.begin()); iter!=ids.end(); ++iter) + { + mObjects.insert (std::make_pair ( + iter->first, new Object (mData, mCellNode, iter->first, false))); + + modified = true; + } + + return modified; +} + +bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int start, + int end) +{ + if (parent.isValid()) + return false; + + CSMWorld::IdTable& references = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + + int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + bool modified = false; + + for (int row = start; row<=end; ++row) + if (removeObject (references.data ( + references.index (row, idColumn)).toString().toUtf8().constData())) + modified = true; + + return modified; +} + +bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int end) +{ + if (parent.isValid()) + return false; + + return addObjects (start, end); +} \ No newline at end of file diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp new file mode 100644 index 000000000..70adebe45 --- /dev/null +++ b/apps/opencs/view/render/cell.hpp @@ -0,0 +1,73 @@ +#ifndef OPENCS_VIEW_CELL_H +#define OPENCS_VIEW_CELL_H + +#include +#include + +#include + +#include "object.hpp" + +class QModelIndex; + +namespace Ogre +{ + class SceneManager; + class SceneNode; +} + +namespace CSMWorld +{ + class Data; +} + +namespace CSVRender +{ + class Cell + { + CSMWorld::Data& mData; + std::string mId; + Ogre::SceneNode *mCellNode; + std::map mObjects; + + /// Ignored if cell does not have an object with the given ID. + /// + /// \return Was the object deleted? + bool removeObject (const std::string& id); + + /// Add objects from reference table that are within this cell. + /// + /// \return Have any objects been added? + bool addObjects (int start, int end); + + public: + + Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, + const std::string& id, const Ogre::Vector3& origin = Ogre::Vector3 (0, 0, 0)); + + ~Cell(); + + /// \return Did this call result in a modification of the visual representation of + /// this cell? + bool referenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight); + + /// \return Did this call result in a modification of the visual representation of + /// this cell? + bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + /// \return Did this call result in a modification of the visual representation of + /// this cell? + bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + /// \return Did this call result in a modification of the visual representation of + /// this cell? + bool referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + /// \return Did this call result in a modification of the visual representation of + /// this cell? + bool referenceAdded (const QModelIndex& parent, int start, int end); + }; +} + +#endif From a6626b94c8a85309aa514fff4d35da3a04fe1762 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 29 Jun 2014 14:19:10 +0200 Subject: [PATCH 225/226] added cell rendering in unpaged worldspaces --- .../view/render/unpagedworldspacewidget.cpp | 63 +++++++++++++++++++ .../view/render/unpagedworldspacewidget.hpp | 19 ++++++ apps/opencs/view/render/worldspacewidget.cpp | 27 ++++++-- apps/opencs/view/render/worldspacewidget.hpp | 15 ++++- 4 files changed, 117 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 166c85f44..0b656ddc6 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -21,6 +21,8 @@ void CSVRender::UnpagedWorldspaceWidget::update() setDefaultAmbient (colour); /// \todo deal with mSunlight and mFog/mForDensity + + flagAsModified(); } CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent) @@ -29,12 +31,17 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& mCellsModel = &dynamic_cast ( *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + mReferenceablesModel = &dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables)); + connect (mCellsModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&))); connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int))); update(); + + mCell.reset (new Cell (document.getData(), getSceneManager(), mCellId)); } void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, @@ -72,6 +79,62 @@ void CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector< CSMWorld mCellId = data.begin()->getId(); update(); emit cellChanged(*data.begin()); + + /// \todo replace mCell +} + +void CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + if (mCell.get()) + if (mCell.get()->referenceableDataChanged (topLeft, bottomRight)) + flagAsModified(); +} + +void CSVRender::UnpagedWorldspaceWidget::referenceableAboutToBeRemoved ( + const QModelIndex& parent, int start, int end) +{ + if (mCell.get()) + if (mCell.get()->referenceableAboutToBeRemoved (parent, start, end)) + flagAsModified(); +} + +void CSVRender::UnpagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent, + int start, int end) +{ + if (mCell.get()) + { + QModelIndex topLeft = mReferenceablesModel->index (start, 0); + QModelIndex bottomRight = + mReferenceablesModel->index (end, mReferenceablesModel->columnCount()); + + if (mCell.get()->referenceableDataChanged (topLeft, bottomRight)) + flagAsModified(); + } +} + +void CSVRender::UnpagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + if (mCell.get()) + if (mCell.get()->referenceDataChanged (topLeft, bottomRight)) + flagAsModified(); +} + +void CSVRender::UnpagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent, + int start, int end) +{ + if (mCell.get()) + if (mCell.get()->referenceAboutToBeRemoved (parent, start, end)) + flagAsModified(); +} + +void CSVRender::UnpagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start, + int end) +{ + if (mCell.get()) + if (mCell.get()->referenceAdded (parent, start, end)) + flagAsModified(); } CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::dropType type) const diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index bb5340845..ee8377fae 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -2,8 +2,10 @@ #define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H #include +#include #include "worldspacewidget.hpp" +#include "cell.hpp" class QModelIndex; @@ -25,6 +27,8 @@ namespace CSVRender std::string mCellId; CSMWorld::IdTable *mCellsModel; + CSMWorld::IdTable *mReferenceablesModel; + std::auto_ptr mCell; void update(); @@ -37,6 +41,21 @@ namespace CSVRender virtual void handleDrop(const std::vector& data); + private: + + virtual void referenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight); + + virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + virtual void referenceableAdded (const QModelIndex& index, int start, int end); + + virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + virtual void referenceAdded (const QModelIndex& index, int start, int end); + private slots: void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index afc6b4603..fee2f0a16 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -10,15 +10,30 @@ #include "../world/scenetoolmode.hpp" #include -CSVRender::WorldspaceWidget::WorldspaceWidget (const CSMDoc::Document& document, QWidget* parent) +CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) : SceneWidget (parent), mDocument(document) { - Ogre::Entity* ent = getSceneManager()->createEntity("cube", Ogre::SceneManager::PT_CUBE); - ent->setMaterialName("BaseWhite"); - - getSceneManager()->getRootSceneNode()->attachObject(ent); - setAcceptDrops(true); + + QAbstractItemModel *referenceables = + document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables); + + connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&))); + connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int))); + connect (referenceables, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (referenceableAdded (const QModelIndex&, int, int))); + + QAbstractItemModel *references = + document.getData().getTableModel (CSMWorld::UniversalId::Type_References); + + connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&))); + connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int))); + connect (references, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (referenceAdded (const QModelIndex&, int, int))); } void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index a14e03915..280f9436d 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -47,7 +47,7 @@ namespace CSVRender ignored //either mixed cells, or not cells }; - WorldspaceWidget (const CSMDoc::Document& document, QWidget *parent = 0); + WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = 0); CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent); ///< \attention The created tool is not added to the toolbar (via addTool). Doing that @@ -79,6 +79,19 @@ namespace CSVRender void selectNavigationMode (const std::string& mode); + virtual void referenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) {} + + virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) {} + + virtual void referenceableAdded (const QModelIndex& index, int start, int end) {} + + virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) {} + + virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) {} + + virtual void referenceAdded (const QModelIndex& index, int start, int end) {} + signals: void closeRequest(); From 2fe2def64c0822e6ac7992484cb7de859a61d03b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 29 Jun 2014 16:00:06 +0200 Subject: [PATCH 226/226] added cell rendering in paged worldspaces --- .../view/render/pagedworldspacewidget.cpp | 134 +++++++++++++++++- .../view/render/pagedworldspacewidget.hpp | 25 ++++ apps/opencs/view/render/scenewidget.cpp | 7 +- apps/opencs/view/render/scenewidget.hpp | 2 + apps/opencs/view/render/worldspacewidget.hpp | 12 +- 5 files changed, 171 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index a3f34d218..20cd6abb5 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -3,14 +3,137 @@ #include +#include + #include -#include +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/idtable.hpp" + +bool CSVRender::PagedWorldspaceWidget::adjustCells() +{ + bool modified = false; + bool setCamera = false; + + { + // remove + std::map::iterator iter (mCells.begin()); + + while (iter!=mCells.end()) + { + if (!mSelection.has (iter->first)) + { + delete iter->second; + mCells.erase (iter++); + modified = true; + } + else + ++iter; + } + } + + if (mCells.begin()==mCells.end()) + setCamera = true; + + // add + for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end(); + ++iter) + { + if (mCells.find (*iter)==mCells.end()) + { + if (setCamera) + { + setCamera = false; + getCamera()->setPosition (8192*iter->getX()+4096, 8192*iter->getY()+4096, 0); + } + + mCells.insert (std::make_pair (*iter, + new Cell (mDocument.getData(), getSceneManager(), + iter->getId ("std::default")))); + + modified = true; + } + } + + return modified; +} + +void CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + if (iter->second->referenceableDataChanged (topLeft, bottomRight)) + flagAsModified(); +} + +void CSVRender::PagedWorldspaceWidget::referenceableAboutToBeRemoved ( + const QModelIndex& parent, int start, int end) +{ + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + if (iter->second->referenceableAboutToBeRemoved (parent, start, end)) + flagAsModified(); +} + +void CSVRender::PagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent, + int start, int end) +{ + CSMWorld::IdTable& referenceables = dynamic_cast ( + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables)); + + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + { + QModelIndex topLeft = referenceables.index (start, 0); + QModelIndex bottomRight = + referenceables.index (end, referenceables.columnCount()); + + if (iter->second->referenceableDataChanged (topLeft, bottomRight)) + flagAsModified(); + } +} + +void CSVRender::PagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + if (iter->second->referenceDataChanged (topLeft, bottomRight)) + flagAsModified(); +} + +void CSVRender::PagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent, + int start, int end) +{ + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + if (iter->second->referenceAboutToBeRemoved (parent, start, end)) + flagAsModified(); +} + +void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start, + int end) +{ + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + if (iter->second->referenceAdded (parent, start, end)) + flagAsModified(); +} + + CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) -: WorldspaceWidget (document, parent) +: WorldspaceWidget (document, parent), mDocument (document) {} +CSVRender::PagedWorldspaceWidget::~PagedWorldspaceWidget() +{ + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + delete iter->second; +} + void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) { if (!hint.empty()) @@ -47,6 +170,10 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSelection& selection) { mSelection = selection; + + if (adjustCells()) + flagAsModified(); + emit cellSelectionChanged (mSelection); } @@ -72,6 +199,9 @@ void CSVRender::PagedWorldspaceWidget::handleDrop (const std::vector< CSMWorld:: } if (selectionChanged) { + if (adjustCells()) + flagAsModified(); + emit cellSelectionChanged(mSelection); } } diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 0a73c791c..7f9a66f82 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -1,9 +1,12 @@ #ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H #define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H +#include + #include "../../model/world/cellselection.hpp" #include "worldspacewidget.hpp" +#include "cell.hpp" namespace CSVRender { @@ -11,12 +14,32 @@ namespace CSVRender { Q_OBJECT + CSMDoc::Document& mDocument; CSMWorld::CellSelection mSelection; + std::map mCells; private: std::pair getCoordinatesFromId(const std::string& record) const; + /// Bring mCells into sync with mSelection again. + /// + /// \return Any cells added or removed? + bool adjustCells(); + + virtual void referenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight); + + virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + virtual void referenceableAdded (const QModelIndex& index, int start, int end); + + virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + virtual void referenceAdded (const QModelIndex& index, int start, int end); + public: PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document); @@ -24,6 +47,8 @@ namespace CSVRender /// no cells are displayed. The cells to be displayed will be specified later through /// hint system. + virtual ~PagedWorldspaceWidget(); + void useViewHint (const std::string& hint); void setCellSelection (const CSMWorld::CellSelection& selection); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index eccaebd00..76f6db385 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -43,7 +43,7 @@ namespace CSVRender mCamera->setPosition (300, 0, 0); mCamera->lookAt (0, 0, 0); mCamera->setNearClipDistance (0.1); - mCamera->setFarClipDistance (30000); + mCamera->setFarClipDistance (300000); ///< \todo make this configurable mCamera->roll (Ogre::Degree (90)); setLighting (&mLightingDay); @@ -137,6 +137,11 @@ namespace CSVRender return mSceneMgr; } + Ogre::Camera *SceneWidget::getCamera() + { + return mCamera; + } + void SceneWidget::flagAsModified() { mUpdate = true; diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 7f8f104f1..f6b41942f 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -49,6 +49,8 @@ namespace CSVRender Ogre::SceneManager *getSceneManager(); + Ogre::Camera *getCamera(); + void flagAsModified(); void setDefaultAmbient (const Ogre::ColourValue& colour); diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 280f9436d..2af90b0fe 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -80,17 +80,17 @@ namespace CSVRender void selectNavigationMode (const std::string& mode); virtual void referenceableDataChanged (const QModelIndex& topLeft, - const QModelIndex& bottomRight) {} + const QModelIndex& bottomRight) = 0; - virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) {} + virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0; - virtual void referenceableAdded (const QModelIndex& index, int start, int end) {} + virtual void referenceableAdded (const QModelIndex& index, int start, int end) = 0; - virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) {} + virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0; - virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) {} + virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0; - virtual void referenceAdded (const QModelIndex& index, int start, int end) {} + virtual void referenceAdded (const QModelIndex& index, int start, int end) = 0; signals: