From 91a4d9a2ebf1655cd41eaba48afc6c11de07723d Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 10 Dec 2013 23:48:49 +0100 Subject: [PATCH 001/251] Fixes #845: NPCs hold torches during the day Added method in WeatherManger and World which returns true if it is night. This method is used later in character controller to show torches (or other sources of light) at night and hide them at day. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/character.cpp | 27 +++++++++++++++------ apps/openmw/mwrender/animation.hpp | 1 + apps/openmw/mwrender/npcanimation.cpp | 34 +++++++++++++++++++-------- apps/openmw/mwrender/npcanimation.hpp | 2 ++ apps/openmw/mwworld/weather.cpp | 5 ++++ apps/openmw/mwworld/weather.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.hpp | 1 + 9 files changed, 62 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8141af7124..ae1497a088 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -421,6 +421,8 @@ namespace MWBase const MWWorld::Ptr& actor, const std::string& sourceName) = 0; virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; + + virtual bool isNight() const = 0; }; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da3ed25232..4f7754951e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -707,17 +707,30 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - - MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + if (MWBase::Environment::get().getWorld()->isNight()) { + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + mAnimation->showLights(true); if(!mAnimation->isPlaying("torch")) - mAnimation->play("torch", Priority_Torch, - MWRender::Animation::Group_LeftArm, false, - 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + mAnimation->play("torch", Priority_Torch, + MWRender::Animation::Group_LeftArm, false, + 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } + else if (mAnimation->isPlaying("torch")) + { + mAnimation->disable("torch"); + mAnimation->showLights(false); + mAnimation->showShield(true); + } } - else if(mAnimation->isPlaying("torch")) + else + { mAnimation->disable("torch"); + mAnimation->showLights(false); + mAnimation->showShield(true); + } return forcestateupdate; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index aa04e39e21..16af6d5a67 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -275,6 +275,7 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool show) {} + virtual void showLights(bool show) {} void enableLights(bool enable); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index eb0c5dfbc8..6e363ab886 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -118,6 +118,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mViewMode(viewMode), mShowWeapons(false), mShowShield(true), + mShowLights(false), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f) { @@ -320,6 +321,7 @@ void NpcAnimation::updateParts() showWeapons(mShowWeapons); showShield(mShowShield); + showLights(mShowLights); // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination static std::map< std::pair,std::vector > sRaceMapping; @@ -660,21 +662,12 @@ void NpcAnimation::showShield(bool show) MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) - { - // ... Except for lights, which are still shown during spellcasting since they - // have their own (one-handed) casting animations - show = true; - } - if(show && shield != inv.end()) + if(show && shield != inv.end() && shield->getTypeName() != typeid(ESM::Light).name()) { Ogre::Vector3 glowColor = getEnchantmentColor(*shield); std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); - - if (shield->getTypeName() == typeid(ESM::Light).name()) - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); } else { @@ -682,6 +675,27 @@ void NpcAnimation::showShield(bool show) } } +void NpcAnimation::showLights(bool show) +{ + mShowLights = show; + MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + + if(show && shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + { + Ogre::Vector3 glowColor = getEnchantmentColor(*shield); + std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); + addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, + mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); + } + else + { + removeIndividualPart(ESM::PRT_Shield); + } +} + + void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) { // During first auto equip, we don't play any sounds. diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 04dde87c7f..0500b46c61 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -54,6 +54,7 @@ private: ViewMode mViewMode; bool mShowWeapons; bool mShowShield; + bool mShowLights; int mVisibilityFlags; @@ -101,6 +102,7 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool showShield); + virtual void showLights(bool showLights); void setViewMode(ViewMode viewMode); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 8b05d2256a..c355d86a8a 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -707,3 +707,8 @@ float WeatherManager::getWindSpeed() const { return mWindSpeed; } + +bool WeatherManager::isNight() const +{ + return (mHour < mSunriseTime || mHour > mNightStart - 1); +} diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 80cbe0418e..4c412c4498 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -152,6 +152,8 @@ namespace MWWorld void modRegion(const std::string ®ionid, const std::vector &chances); + bool isNight() const; + private: float mHour; int mDay, mMonth; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f64d221223..479feab3e8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2244,4 +2244,9 @@ namespace MWWorld actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } + + bool World::isNight() const + { + return mWeatherManager->isNight(); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index c8133441db..2f8994c8e5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -506,6 +506,7 @@ namespace MWWorld const MWWorld::Ptr& actor, const std::string& sourceName); virtual void breakInvisibility (const MWWorld::Ptr& actor); + virtual bool isNight() const; }; } From 8dd930cf97fc4a0fee398a31209fabf24ad7fcb1 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 13 Dec 2013 22:43:58 +0200 Subject: [PATCH 002/251] bug fix http://bugs.openmw.org/issues/428 --- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwbase/world.hpp | 3 ++ apps/openmw/mwgui/mainmenu.cpp | 13 +++++++- apps/openmw/mwgui/mainmenu.hpp | 4 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 ++ apps/openmw/mwinput/inputmanagerimp.cpp | 37 ++++++++++++----------- apps/openmw/mwmechanics/actors.cpp | 14 ++++----- apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++-- apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 5 +++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 9 +++++- apps/openmw/mwworld/worldimp.hpp | 2 ++ 14 files changed, 92 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c47ad066b5..4ffb44615d 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -202,6 +202,8 @@ namespace MWBase virtual void setSpellVisibility(bool visible) = 0; virtual void setSneakVisibility(bool visible) = 0; + void virtual setMainMenuNoReturn(bool noreturn) = 0; + virtual void activateQuickKey (int index) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8141af7124..b1f236bd11 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,6 +40,7 @@ namespace ESM namespace MWRender { + class Camera; class ExternalRendering; class Animation; } @@ -115,6 +116,8 @@ namespace MWBase virtual MWWorld::Player& getPlayer() = 0; + virtual MWRender::Camera* getCamera() const = 0; + virtual const MWWorld::ESMStore& getStore() const = 0; virtual std::vector& getEsmReader() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index fa7ed2aceb..e1c72193b6 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -17,6 +17,7 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) + , mNoReturn(false) { onResChange(w,h); } @@ -33,7 +34,8 @@ namespace MWGui int curH = 0; std::vector buttons; - buttons.push_back("return"); + if(!mNoReturn) + buttons.push_back("return"); buttons.push_back("newgame"); //buttons.push_back("loadgame"); //buttons.push_back("savegame"); @@ -68,6 +70,15 @@ namespace MWGui mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH); } + void MainMenu::setNoReturn(bool bNoReturn) + { + if (mNoReturn != bNoReturn) + { + mNoReturn = bNoReturn; + onResChange( Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video") ); + } + } + void MainMenu::onButtonClicked(MyGUI::Widget *sender) { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 4e76a64df7..a686cb020b 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -12,9 +12,13 @@ namespace MWGui void onResChange(int w, int h); + void setNoReturn(bool bNoReturn); + private: MyGUI::Widget* mButtonBox; + bool mNoReturn; + std::map mButtons; void onButtonClicked (MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 78986a0522..69ec11cccc 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -880,6 +880,11 @@ namespace MWGui mHud->setSneakVisible(visible); } + void WindowManager::setMainMenuNoReturn(bool noreturn) + { + mMenu->setNoReturn(noreturn); + } + void WindowManager::setDragDrop(bool dragDrop) { mToolTips->setEnabled(!dragDrop); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 4f19602958..3d6972718d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -198,6 +198,9 @@ namespace MWGui virtual void setSpellVisibility(bool visible); virtual void setSneakVisibility(bool visible); + //disables 'return' button in the main menu (used when player is dead) + void virtual setMainMenuNoReturn(bool noreturn); + virtual void activateQuickKey (int index); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ab25696351..9b7fe98bd4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -367,24 +367,27 @@ namespace MWInput } } - if (mControlSwitch["playerviewswitch"]) { - - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + if ( !player.getClass().getCreatureStats(player).isDead() ) { + if (mControlSwitch["playerviewswitch"]) { + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; } } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 22a641b34b..5dd294f906 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -570,15 +570,13 @@ namespace MWMechanics continue; } - if(iter->second->isDead()) - continue; + if (iter->second->kill()) + { + ++mDeathCount[cls.getId(iter->first)]; - iter->second->kill(); - - ++mDeathCount[cls.getId(iter->first)]; - - if(cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da3ed25232..c8eba20d6b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -27,6 +27,7 @@ #include "security.hpp" #include "../mwrender/animation.hpp" +#include "../mwrender/camera.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -1055,10 +1056,19 @@ void CharacterController::forceStateUpdate() } } -void CharacterController::kill() +bool CharacterController::kill() { - if(mDeathState != CharState_None) - return; + if( isDead() ) + { + //player death animation is over: toggle game menu without 'return' option + if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && !isAnimPlaying(mCurrentDeath) ) + { + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + return false; + } if(mPtr.getTypeName() == typeid(ESM::NPC).name()) { @@ -1096,6 +1106,10 @@ void CharacterController::kill() if(mAnimation) { + if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) + MWBase::Environment::get().getWorld()->togglePOV(); + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); @@ -1103,6 +1117,8 @@ void CharacterController::kill() mIdleState = CharState_None; mCurrentIdle.clear(); + + return true; } void CharacterController::resurrect() diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 9e07fca7d4..2a5c7bb03c 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -190,7 +190,7 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); - void kill(); + bool kill(); void resurrect(); bool isDead() const { return mDeathState != CharState_None; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b216c789f7..7d51627474 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -211,6 +211,11 @@ MWRender::SkyManager* RenderingManager::getSkyManager() return mSkyManager; } +MWRender::Camera* RenderingManager::getCamera() const +{ + return mCamera; +} + MWRender::Objects& RenderingManager::getObjects(){ return *mObjects; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index e5dcf0aebc..d6c1ef6c1b 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -98,6 +98,8 @@ public: SkyManager* getSkyManager(); Compositors* getCompositors(); + MWRender::Camera* getCamera() const; + void toggleLight(); bool toggleRenderMode(int mode); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f64d221223..a900d27aba 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -312,6 +312,8 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); + + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(false); } @@ -403,6 +405,11 @@ namespace MWWorld return *mPlayer; } + MWRender::Camera* World::getCamera() const + { + return mRendering->getCamera(); + } + const MWWorld::ESMStore& World::getStore() const { return mStore; @@ -1304,7 +1311,7 @@ namespace MWWorld // inform the GUI about focused object MWWorld::Ptr object = getFacedObject (); - MWBase::Environment::get().getWindowManager()->setFocusObject(object); + MWBase::Environment::get().getWindowManager()->setFocusObject(object); // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index c8133441db..2e572d0bbc 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -187,6 +187,8 @@ namespace MWWorld virtual Player& getPlayer(); + virtual MWRender::Camera* getCamera() const; + virtual const MWWorld::ESMStore& getStore() const; virtual std::vector& getEsmReader(); From c0d07fbdc9023b76853e2a0b20d9649e9eca1f0a Mon Sep 17 00:00:00 2001 From: mrcheko Date: Sat, 14 Dec 2013 15:07:00 +0200 Subject: [PATCH 003/251] improvement to bug fix http://bugs.openmw.org/issues/428 --- apps/openmw/mwinput/inputmanagerimp.cpp | 45 +++++++++++++------------ apps/openmw/mwmechanics/character.cpp | 19 ++++++----- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9b7fe98bd4..efa326a98a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -180,7 +180,9 @@ namespace MWInput switch (action) { case A_GameMenu: - toggleMainMenu (); + if(!(MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() + && MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_MainMenu ) ) + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -300,7 +302,9 @@ namespace MWInput return; // Disable movement in Gui mode - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode() + || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) + return; // Configure player movement according to keyboard input. Actual movement will @@ -367,29 +371,28 @@ namespace MWInput } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); - if ( !player.getClass().getCreatureStats(player).isDead() ) { - if (mControlSwitch["playerviewswitch"]) { - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + + if (mControlSwitch["playerviewswitch"]) { + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); - } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } } + } if (actionIsActive(A_MoveForward) || actionIsActive(A_MoveBackward) || diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c8eba20d6b..1c4979e378 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1061,11 +1061,11 @@ bool CharacterController::kill() if( isDead() ) { //player death animation is over: toggle game menu without 'return' option - if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && !isAnimPlaying(mCurrentDeath) ) + if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && !isAnimPlaying(mCurrentDeath) && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) { - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } return false; } @@ -1106,10 +1106,13 @@ bool CharacterController::kill() if(mAnimation) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) - MWBase::Environment::get().getWorld()->togglePOV(); - + if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); + if (MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) + MWBase::Environment::get().getWorld()->togglePOV(); + } + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); From 92072d968b65ec6fef32d0211aad45ff08dbef30 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 19 Dec 2013 21:11:07 +0100 Subject: [PATCH 004/251] Fixes #845: NPCs hold torches during the day Simplified a bit code which shows and hides light. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/character.cpp | 27 +++++++++++++-------------- apps/openmw/mwrender/npcanimation.cpp | 14 +++++++------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4f7754951e..398eadf866 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -36,6 +36,8 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/actionequip.hpp" +#include "../mwworld/actiontake.hpp" namespace { @@ -709,27 +711,24 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if (MWBase::Environment::get().getWorld()->isNight()) { - MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) { mAnimation->showLights(true); - if(!mAnimation->isPlaying("torch")) - mAnimation->play("torch", Priority_Torch, - MWRender::Animation::Group_LeftArm, false, - 1.0f, "start", "stop", 0.0f, (~(size_t)0)); - } - else if (mAnimation->isPlaying("torch")) - { - mAnimation->disable("torch"); - mAnimation->showLights(false); - mAnimation->showShield(true); + if (!mAnimation->isPlaying("torch")) + { + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } } } else { + if (mAnimation->isPlaying("torch")) + { mAnimation->disable("torch"); - mAnimation->showLights(false); - mAnimation->showShield(true); + } + mAnimation->showLights(false); } return forcestateupdate; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6e363ab886..2da45e8a19 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -677,17 +677,17 @@ void NpcAnimation::showShield(bool show) void NpcAnimation::showLights(bool show) { - mShowLights = show; MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + MWWorld::ContainerStoreIterator light = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(show && shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + if(show && light != inv.end() && light->getTypeName() == typeid(ESM::Light).name()) { - Ogre::Vector3 glowColor = getEnchantmentColor(*shield); - std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); + mShowLights = show; + Ogre::Vector3 glowColor = getEnchantmentColor(*light); + std::string mesh = MWWorld::Class::get(*light).getModel(*light); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); + mesh, !light->getClass().getEnchantment(*light).empty(), &glowColor); + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light->get()->mBase); } else { From 6eb674e4e5e899b906df25490877c65641e8b67a Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 20 Dec 2013 00:18:34 +0100 Subject: [PATCH 005/251] Fixes #845: NPCs hold torches during the day Added equipping/unequipping torches. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/character.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 398eadf866..9a4f65c821 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -712,6 +712,20 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if (MWBase::Environment::get().getWorld()->isNight()) { MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) + { + inv.unequipItem(*item, mPtr); + } + else if (item == inv.end()) + { + MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); + if (!itemPtr.isEmpty()) + { + item = inv.add(itemPtr, mPtr); + inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); + } + } + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) { mAnimation->showLights(true); @@ -727,8 +741,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if (mAnimation->isPlaying("torch")) { mAnimation->disable("torch"); + mAnimation->showLights(false); + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + { + inv.unequipItem(*item, mPtr); + inv.add(*item, mPtr); + } } - mAnimation->showLights(false); } return forcestateupdate; From abc126e2af89ced763abf33fde1e2c00850161ac Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 20 Dec 2013 01:01:30 +0100 Subject: [PATCH 006/251] Fixes #845: NPCs hold torches during the day Added check for Player character so it won't be affected by showing, or hidding torches. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/character.cpp | 70 ++++++++++++++------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9a4f65c821..f2b3532974 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -709,48 +709,50 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - if (MWBase::Environment::get().getWorld()->isNight()) + if (mPtr.getRefData().getHandle() != "player") { - MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) + if (MWBase::Environment::get().getWorld()->isNight()) { - inv.unequipItem(*item, mPtr); - } - else if (item == inv.end()) - { - MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); - if (!itemPtr.isEmpty()) - { - item = inv.add(itemPtr, mPtr); - inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); - } - } - - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) - { - mAnimation->showLights(true); - if (!mAnimation->isPlaying("torch")) - { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); - } - } - } - else - { - if (mAnimation->isPlaying("torch")) - { - mAnimation->disable("torch"); - mAnimation->showLights(false); MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) { inv.unequipItem(*item, mPtr); - inv.add(*item, mPtr); + } + else if (item == inv.end()) + { + MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); + if (!itemPtr.isEmpty()) + { + item = inv.add(itemPtr, mPtr); + inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); + } + } + + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + { + mAnimation->showLights(true); + if (!mAnimation->isPlaying("torch")) + { + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } + } + } + else + { + if (mAnimation->isPlaying("torch")) + { + mAnimation->disable("torch"); + mAnimation->showLights(false); + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) + { + inv.unequipItem(*item, mPtr); + inv.add(*item, mPtr); + } } } } - return forcestateupdate; } From e480e31d1ebcc02771eea694098477511069893f Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 20 Dec 2013 22:37:18 +0200 Subject: [PATCH 007/251] Revert "improvement to bug fix http://bugs.openmw.org/issues/428" This reverts commit c0d07fbdc9023b76853e2a0b20d9649e9eca1f0a. --- apps/openmw/mwinput/inputmanagerimp.cpp | 45 ++++++++++++------------- apps/openmw/mwmechanics/character.cpp | 19 +++++------ 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index efa326a98a..9b7fe98bd4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -180,9 +180,7 @@ namespace MWInput switch (action) { case A_GameMenu: - if(!(MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() - && MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_MainMenu ) ) - toggleMainMenu (); + toggleMainMenu (); break; case A_Screenshot: screenshot(); @@ -302,9 +300,7 @@ namespace MWInput return; // Disable movement in Gui mode - if (MWBase::Environment::get().getWindowManager()->isGuiMode() - || MWWorld::Class::get(mPlayer->getPlayer()).getCreatureStats(mPlayer->getPlayer()).isDead() ) - return; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Configure player movement according to keyboard input. Actual movement will @@ -371,28 +367,29 @@ namespace MWInput } } - - if (mControlSwitch["playerviewswitch"]) { - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + if ( !player.getClass().getCreatureStats(player).isDead() ) { + if (mControlSwitch["playerviewswitch"]) { + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; } } - } if (actionIsActive(A_MoveForward) || actionIsActive(A_MoveBackward) || diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 1c4979e378..c8eba20d6b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1061,11 +1061,11 @@ bool CharacterController::kill() if( isDead() ) { //player death animation is over: toggle game menu without 'return' option - if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && !isAnimPlaying(mCurrentDeath) && MWBase::Environment::get().getWindowManager()->getMode () != MWGui::GM_MainMenu ) + if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && !isAnimPlaying(mCurrentDeath) ) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setHealth(0); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); } return false; } @@ -1106,13 +1106,10 @@ bool CharacterController::kill() if(mAnimation) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - { - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); - if (MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) - MWBase::Environment::get().getWorld()->togglePOV(); - } - + if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) + MWBase::Environment::get().getWorld()->togglePOV(); + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); From b98cfe2d1b887a8c305945fd40e2d0ee7b34bebf Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 20 Dec 2013 22:39:02 +0200 Subject: [PATCH 008/251] Revert "bug fix http://bugs.openmw.org/issues/428" This reverts commit 8dd930cf97fc4a0fee398a31209fabf24ad7fcb1. --- apps/openmw/mwbase/windowmanager.hpp | 2 -- apps/openmw/mwbase/world.hpp | 3 -- apps/openmw/mwgui/mainmenu.cpp | 13 +------- apps/openmw/mwgui/mainmenu.hpp | 4 --- apps/openmw/mwgui/windowmanagerimp.cpp | 5 --- apps/openmw/mwgui/windowmanagerimp.hpp | 3 -- apps/openmw/mwinput/inputmanagerimp.cpp | 37 +++++++++++------------ apps/openmw/mwmechanics/actors.cpp | 14 +++++---- apps/openmw/mwmechanics/character.cpp | 22 ++------------ apps/openmw/mwmechanics/character.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 5 --- apps/openmw/mwrender/renderingmanager.hpp | 2 -- apps/openmw/mwworld/worldimp.cpp | 9 +----- apps/openmw/mwworld/worldimp.hpp | 2 -- 14 files changed, 31 insertions(+), 92 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4ffb44615d..c47ad066b5 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -202,8 +202,6 @@ namespace MWBase virtual void setSpellVisibility(bool visible) = 0; virtual void setSneakVisibility(bool visible) = 0; - void virtual setMainMenuNoReturn(bool noreturn) = 0; - virtual void activateQuickKey (int index) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b1f236bd11..8141af7124 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,7 +40,6 @@ namespace ESM namespace MWRender { - class Camera; class ExternalRendering; class Animation; } @@ -116,8 +115,6 @@ namespace MWBase virtual MWWorld::Player& getPlayer() = 0; - virtual MWRender::Camera* getCamera() const = 0; - virtual const MWWorld::ESMStore& getStore() const = 0; virtual std::vector& getEsmReader() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e1c72193b6..fa7ed2aceb 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -17,7 +17,6 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) - , mNoReturn(false) { onResChange(w,h); } @@ -34,8 +33,7 @@ namespace MWGui int curH = 0; std::vector buttons; - if(!mNoReturn) - buttons.push_back("return"); + buttons.push_back("return"); buttons.push_back("newgame"); //buttons.push_back("loadgame"); //buttons.push_back("savegame"); @@ -70,15 +68,6 @@ namespace MWGui mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH); } - void MainMenu::setNoReturn(bool bNoReturn) - { - if (mNoReturn != bNoReturn) - { - mNoReturn = bNoReturn; - onResChange( Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video") ); - } - } - void MainMenu::onButtonClicked(MyGUI::Widget *sender) { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index a686cb020b..4e76a64df7 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -12,13 +12,9 @@ namespace MWGui void onResChange(int w, int h); - void setNoReturn(bool bNoReturn); - private: MyGUI::Widget* mButtonBox; - bool mNoReturn; - std::map mButtons; void onButtonClicked (MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 69ec11cccc..78986a0522 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -880,11 +880,6 @@ namespace MWGui mHud->setSneakVisible(visible); } - void WindowManager::setMainMenuNoReturn(bool noreturn) - { - mMenu->setNoReturn(noreturn); - } - void WindowManager::setDragDrop(bool dragDrop) { mToolTips->setEnabled(!dragDrop); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 3d6972718d..4f19602958 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -198,9 +198,6 @@ namespace MWGui virtual void setSpellVisibility(bool visible); virtual void setSneakVisibility(bool visible); - //disables 'return' button in the main menu (used when player is dead) - void virtual setMainMenuNoReturn(bool noreturn); - virtual void activateQuickKey (int index); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9b7fe98bd4..ab25696351 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -367,27 +367,24 @@ namespace MWInput } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); - if ( !player.getClass().getCreatureStats(player).isDead() ) { - if (mControlSwitch["playerviewswitch"]) { - // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { - - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += dt) > 0.5) - { - mPreviewPOVDelay = 1.f; - MWBase::Environment::get().getWorld()->togglePreviewMode(true); - } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { - MWBase::Environment::get().getWorld()->togglePOV(); - } - mPreviewPOVDelay = 0.f; + if (mControlSwitch["playerviewswitch"]) { + + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += dt) > 0.5) + { + mPreviewPOVDelay = 1.f; + MWBase::Environment::get().getWorld()->togglePreviewMode(true); } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + MWBase::Environment::get().getWorld()->togglePOV(); + } + mPreviewPOVDelay = 0.f; } } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5dd294f906..22a641b34b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -570,13 +570,15 @@ namespace MWMechanics continue; } - if (iter->second->kill()) - { - ++mDeathCount[cls.getId(iter->first)]; + if(iter->second->isDead()) + continue; - if(cls.isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); - } + iter->second->kill(); + + ++mDeathCount[cls.getId(iter->first)]; + + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c8eba20d6b..da3ed25232 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -27,7 +27,6 @@ #include "security.hpp" #include "../mwrender/animation.hpp" -#include "../mwrender/camera.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -1056,19 +1055,10 @@ void CharacterController::forceStateUpdate() } } -bool CharacterController::kill() +void CharacterController::kill() { - if( isDead() ) - { - //player death animation is over: toggle game menu without 'return' option - if( mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && !isAnimPlaying(mCurrentDeath) ) - { - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(true); - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - return false; - } + if(mDeathState != CharState_None) + return; if(mPtr.getTypeName() == typeid(ESM::NPC).name()) { @@ -1106,10 +1096,6 @@ bool CharacterController::kill() if(mAnimation) { - if (mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() - && MWBase::Environment::get().getWorld()->getCamera()->isFirstPerson() ) - MWBase::Environment::get().getWorld()->togglePOV(); - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); @@ -1117,8 +1103,6 @@ bool CharacterController::kill() mIdleState = CharState_None; mCurrentIdle.clear(); - - return true; } void CharacterController::resurrect() diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 2a5c7bb03c..9e07fca7d4 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -190,7 +190,7 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); - bool kill(); + void kill(); void resurrect(); bool isDead() const { return mDeathState != CharState_None; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7d51627474..b216c789f7 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -211,11 +211,6 @@ MWRender::SkyManager* RenderingManager::getSkyManager() return mSkyManager; } -MWRender::Camera* RenderingManager::getCamera() const -{ - return mCamera; -} - MWRender::Objects& RenderingManager::getObjects(){ return *mObjects; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d6c1ef6c1b..e5dcf0aebc 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -98,8 +98,6 @@ public: SkyManager* getSkyManager(); Compositors* getCompositors(); - MWRender::Camera* getCamera() const; - void toggleLight(); bool toggleRenderMode(int mode); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a900d27aba..f64d221223 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -312,8 +312,6 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); MWBase::Environment::get().getScriptManager()->resetGlobalScripts(); - - MWBase::Environment::get().getWindowManager()->setMainMenuNoReturn(false); } @@ -405,11 +403,6 @@ namespace MWWorld return *mPlayer; } - MWRender::Camera* World::getCamera() const - { - return mRendering->getCamera(); - } - const MWWorld::ESMStore& World::getStore() const { return mStore; @@ -1311,7 +1304,7 @@ namespace MWWorld // inform the GUI about focused object MWWorld::Ptr object = getFacedObject (); - MWBase::Environment::get().getWindowManager()->setFocusObject(object); + MWBase::Environment::get().getWindowManager()->setFocusObject(object); // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2e572d0bbc..c8133441db 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -187,8 +187,6 @@ namespace MWWorld virtual Player& getPlayer(); - virtual MWRender::Camera* getCamera() const; - virtual const MWWorld::ESMStore& getStore() const; virtual std::vector& getEsmReader(); From 900bc06d2c236b80fe6c422ba335aefb897b37a7 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 20 Dec 2013 22:38:23 +0100 Subject: [PATCH 009/251] Fixes #845: NPCs hold torches during the day Moved 'equipping torches at night and unequipping at day' code from Character to Actors class. Removed unneeded showLights method (introduced in previous commits) from animation/npcanimation classes. Since this commit autoEquip() method doesn't automatically equip lights. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/actors.cpp | 37 ++++++++++++++++++- apps/openmw/mwmechanics/actors.hpp | 11 +++--- apps/openmw/mwmechanics/character.cpp | 51 +++++--------------------- apps/openmw/mwrender/animation.hpp | 1 - apps/openmw/mwrender/npcanimation.cpp | 34 +++++------------ apps/openmw/mwrender/npcanimation.hpp | 2 - apps/openmw/mwworld/inventorystore.cpp | 7 ++++ 7 files changed, 66 insertions(+), 77 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 22a641b34b..1561c9d5b4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -439,15 +439,48 @@ namespace MWMechanics void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) { - //If holding a light... + bool isPlayer = ptr.getRefData().getHandle()=="player"; + MWWorld::InventoryStore &inventoryStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); MWWorld::ContainerStoreIterator heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + /** + * Automatically equip NPCs torches at night and unequip them at day + */ + if (!isPlayer && !MWWorld::Class::get (ptr).getCreatureStats (ptr).isHostile()) + { + if (mTorchPtr.isEmpty()) + { + mTorchPtr = inventoryStore.search("torch_infinite_time"); + } + if (MWBase::Environment::get().getWorld()->isNight()) + { + if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) + { + inventoryStore.unequipItem(*heldIter, ptr); + } + else if (heldIter == inventoryStore.end() && !mTorchPtr.isEmpty()) + { + heldIter = inventoryStore.add(mTorchPtr, ptr); + inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, heldIter, ptr); + } + } + else + { + if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name()) + { + inventoryStore.unequipItem(*heldIter, ptr); + inventoryStore.add(*heldIter, ptr); + inventoryStore.autoEquip(ptr); + } + } + } + + //If holding a light... if(heldIter.getType() == MWWorld::ContainerStore::Type_Light) { // Use time from the player's light - bool isPlayer = ptr.getRefData().getHandle()=="player"; if(isPlayer) { float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 6afdefdbdc..411ac54ca8 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -25,14 +25,13 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; - PtrControllerMap mActors; - - std::map mDeathCount; - - void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); + typedef std::map PtrControllerMap; + PtrControllerMap mActors; + std::map mDeathCount; + MWWorld::Ptr mTorchPtr; + void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); void adjustMagicEffects (const MWWorld::Ptr& creature); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f2b3532974..e3373e7f3c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -709,50 +709,17 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - if (mPtr.getRefData().getHandle() != "player") + MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) { - if (MWBase::Environment::get().getWorld()->isNight()) - { - MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() != typeid(ESM::Light).name()) - { - inv.unequipItem(*item, mPtr); - } - else if (item == inv.end()) - { - MWWorld::Ptr itemPtr = inv.search("torch_infinite_time"); - if (!itemPtr.isEmpty()) - { - item = inv.add(itemPtr, mPtr); - inv.equip(MWWorld::InventoryStore::Slot_CarriedLeft, item, mPtr); - } - } - - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) - { - mAnimation->showLights(true); - if (!mAnimation->isPlaying("torch")) - { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); - } - } - } - else - { - if (mAnimation->isPlaying("torch")) - { - mAnimation->disable("torch"); - mAnimation->showLights(false); - MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (item != inv.end() && item->getTypeName() == typeid(ESM::Light).name()) - { - inv.unequipItem(*item, mPtr); - inv.add(*item, mPtr); - } - } - } + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); } + else if (mAnimation->isPlaying("torch")) + { + mAnimation->disable("torch"); + } + return forcestateupdate; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 16af6d5a67..aa04e39e21 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -275,7 +275,6 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool show) {} - virtual void showLights(bool show) {} void enableLights(bool enable); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 2da45e8a19..eb0c5dfbc8 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -118,7 +118,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mViewMode(viewMode), mShowWeapons(false), mShowShield(true), - mShowLights(false), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f) { @@ -321,7 +320,6 @@ void NpcAnimation::updateParts() showWeapons(mShowWeapons); showShield(mShowShield); - showLights(mShowLights); // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination static std::map< std::pair,std::vector > sRaceMapping; @@ -662,12 +660,21 @@ void NpcAnimation::showShield(bool show) MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(show && shield != inv.end() && shield->getTypeName() != typeid(ESM::Light).name()) + if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + { + // ... Except for lights, which are still shown during spellcasting since they + // have their own (one-handed) casting animations + show = true; + } + if(show && shield != inv.end()) { Ogre::Vector3 glowColor = getEnchantmentColor(*shield); std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); + + if (shield->getTypeName() == typeid(ESM::Light).name()) + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); } else { @@ -675,27 +682,6 @@ void NpcAnimation::showShield(bool show) } } -void NpcAnimation::showLights(bool show) -{ - MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator light = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - - if(show && light != inv.end() && light->getTypeName() == typeid(ESM::Light).name()) - { - mShowLights = show; - Ogre::Vector3 glowColor = getEnchantmentColor(*light); - std::string mesh = MWWorld::Class::get(*light).getModel(*light); - addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !light->getClass().getEnchantment(*light).empty(), &glowColor); - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light->get()->mBase); - } - else - { - removeIndividualPart(ESM::PRT_Shield); - } -} - - void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) { // During first auto equip, we don't play any sounds. diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 0500b46c61..04dde87c7f 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -54,7 +54,6 @@ private: ViewMode mViewMode; bool mShowWeapons; bool mShowShield; - bool mShowLights; int mVisibilityFlags; @@ -102,7 +101,6 @@ public: virtual void showWeapons(bool showWeapon); virtual void showShield(bool showShield); - virtual void showLights(bool showLights); void setViewMode(ViewMode viewMode); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 57e35adce9..856697b8e4 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -175,6 +175,13 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) { Ptr test = *iter; + + // Don't autoEquip lights + if (test.getTypeName() == typeid(ESM::Light).name()) + { + continue; + } + int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); std::pair, bool> itemsSlots = From 18a9878bdd82cd144f7bbb3efaa6a806a4169841 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 21 Dec 2013 09:33:05 +0100 Subject: [PATCH 010/251] Fixes #1042: TES3 header data wrong encoding Changed loading of HEDR structure from all-in-once to field-by-field so author and descryption could be converted to UTF-8. Signed-off-by: Lukasz Gromanowski --- components/esm/loadtes3.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index 87a8d1d57e..262d4f6faa 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -19,7 +19,15 @@ void ESM::Header::blank() void ESM::Header::load (ESMReader &esm) { - esm.getHNT (mData, "HEDR", 300); + if (esm.isNextSub("HEDR")) + { + esm.getSubHeader(); + esm.getT(mData.version); + esm.getT(mData.type); + mData.author.assign(esm.getString(sizeof(mData.author.name))); + mData.desc.assign(esm.getString(sizeof(mData.desc.name))); + esm.getT(mData.records); + } if (esm.isNextSub ("FORM")) { @@ -52,4 +60,4 @@ void ESM::Header::save (ESMWriter &esm) esm.writeHNCString ("MAST", iter->name); esm.writeHNT ("DATA", iter->size); } -} \ No newline at end of file +} From 6d27ebabb61d97414c91ec9732ab6a0644c7072a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Dec 2013 15:06:15 +0100 Subject: [PATCH 011/251] Integrate AddGlow with material controllers --- apps/openmw/mwbase/environment.cpp | 6 ++--- apps/openmw/mwrender/animation.cpp | 34 +++++++++++----------------- components/nifogre/material.cpp | 1 - components/nifogre/ogrenifloader.hpp | 2 ++ extern/shiny/Main/Factory.hpp | 3 +-- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 6b309025cc..4db0b45b96 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -141,15 +141,15 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; + delete mWindowManager; + mWindowManager = 0; + delete mWorld; mWorld = 0; delete mSoundManager; mSoundManager = 0; - delete mWindowManager; - mWindowManager = 0; - delete mInputManager; mInputManager = 0; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 42d11aa6d0..3b04457b66 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -146,29 +146,21 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) struct AddGlow { Ogre::Vector3* mColor; - AddGlow(Ogre::Vector3* col) : mColor(col) {} + NifOgre::MaterialControllerManager* mMaterialControllerMgr; + AddGlow(Ogre::Vector3* col, NifOgre::MaterialControllerManager* materialControllerMgr) + : mColor(col) + , mMaterialControllerMgr(materialControllerMgr) + {} - // TODO: integrate this with material controllers? void operator()(Ogre::Entity* entity) const { - unsigned int numsubs = entity->getNumSubEntities(); - for(unsigned int i = 0;i < numsubs;++i) - { - unsigned int numsubs = entity->getNumSubEntities(); - for(unsigned int i = 0;i < numsubs;++i) - { - Ogre::SubEntity* subEnt = entity->getSubEntity(i); - std::string newName = subEnt->getMaterialName() + "@fx"; - if (sh::Factory::getInstance().searchInstance(newName) == NULL) - { - sh::MaterialInstance* instance = - sh::Factory::getInstance().createMaterialInstance(newName, subEnt->getMaterialName()); - instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true))); - instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z))); - } - subEnt->setMaterialName(newName); - } - } + if (!entity->getNumSubEntities()) + return; + Ogre::MaterialPtr writableMaterial = mMaterialControllerMgr->getWritableMaterial(entity); + sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(writableMaterial->getName()); + + instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z))); } }; @@ -216,7 +208,7 @@ void Animation::setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint3 if (enchantedGlow) std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(), - AddGlow(glowColor)); + AddGlow(glowColor, &objlist->mMaterialControllerMgr)); } diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index bef0ec1d10..d529fb109f 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -390,7 +390,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); // depth_func??? - sh::Factory::getInstance()._ensureMaterial(name, "Default"); return name; } diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 976a31ccd8..5858aef39b 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -45,6 +45,8 @@ class MaterialControllerManager { public: ~MaterialControllerManager(); + + /// @attention if \a movable is an Entity, it needs to have *one* SubEntity Ogre::MaterialPtr getWritableMaterial (Ogre::MovableObject* movable); private: diff --git a/extern/shiny/Main/Factory.hpp b/extern/shiny/Main/Factory.hpp index 7d52266b55..15c8599583 100644 --- a/extern/shiny/Main/Factory.hpp +++ b/extern/shiny/Main/Factory.hpp @@ -259,9 +259,8 @@ namespace sh Platform* mPlatform; MaterialInstance* findInstance (const std::string& name); - public: - MaterialInstance* searchInstance (const std::string& name); private: + MaterialInstance* searchInstance (const std::string& name); /// @return was anything removed? bool removeCache (const std::string& pattern); From c5c3248376cac94a163a90416b8f32556455753e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Dec 2013 16:16:08 +0100 Subject: [PATCH 012/251] Compile fixes for Ogre 1.10 --- components/nifogre/particles.cpp | 56 +++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index e54a778853..d306f49447 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -189,23 +189,36 @@ public: xOff = Ogre::Math::SymmetricRandom() * mXRange; yOff = Ogre::Math::SymmetricRandom() * mYRange; zOff = Ogre::Math::SymmetricRandom() * mZRange; - - particle->position = mBone->_getDerivedPosition() + xOff + yOff + zOff; + +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + Ogre::Vector3& position = particle->mPosition; + Ogre::Vector3& direction = particle->mDirection; + Ogre::ColourValue& colour = particle->mColour; + Ogre::Real& totalTimeToLive = particle->mTotalTimeToLive; + Ogre::Real& timeToLive = particle->mTimeToLive; +#else + Ogre::Vector3& position = particle->position; + Ogre::Vector3& direction = particle->direction; + Ogre::ColourValue& colour = particle->colour; + Ogre::Real& totalTimeToLive = particle->totalTimeToLive; + Ogre::Real& timeToLive = particle->timeToLive; +#endif + position = mBone->_getDerivedPosition() + xOff + yOff + zOff; // Generate complex data by reference - genEmissionColour(particle->colour); + genEmissionColour(colour); // NOTE: We do not use mDirection/mAngle for the initial direction. Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); - particle->direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * Ogre::Vector3::UNIT_Z; - genEmissionVelocity(particle->direction); + genEmissionVelocity(direction); // Generate simpler data - particle->timeToLive = particle->totalTimeToLive = genEmissionTTL(); + timeToLive = totalTimeToLive = genEmissionTTL(); } /** Overloaded to update the trans. matrix */ @@ -466,9 +479,13 @@ public: /** See Ogre::ParticleAffector. */ void _initParticle(Ogre::Particle *particle) { +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + const Ogre::Real life_time = particle->mTotalTimeToLive; + Ogre::Real particle_time = particle->mTimeToLive; +#else const Ogre::Real life_time = particle->totalTimeToLive; Ogre::Real particle_time = particle->timeToLive; - +#endif Ogre::Real width = mParent->getDefaultWidth(); Ogre::Real height = mParent->getDefaultHeight(); if(life_time-particle_time < mGrowTime) @@ -493,9 +510,13 @@ public: while (!pi.end()) { Ogre::Particle *p = pi.getNext(); - const Ogre::Real life_time = p->totalTimeToLive; - Ogre::Real particle_time = p->timeToLive; - +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + const Ogre::Real life_time = p->mTotalTimeToLive; + Ogre::Real particle_time = p->mTimeToLive; +#else + const Ogre::Real life_time = p->totalTimeToLive; + Ogre::Real particle_time = p->timeToLive; +#endif Ogre::Real width = mParent->getDefaultWidth(); Ogre::Real height = mParent->getDefaultHeight(); if(life_time-particle_time < mGrowTime) @@ -772,7 +793,11 @@ protected: while (!pi.end()) { Ogre::Particle *p = pi.getNext(); +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + p->mDirection += vec; +#else p->direction += vec; +#endif } } @@ -783,9 +808,18 @@ protected: while (!pi.end()) { Ogre::Particle *p = pi.getNext(); +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + Ogre::Vector3 position = p->mPosition; +#else + Ogre::Vector3 position = p->position; +#endif const Ogre::Vector3 vec = ( - (mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - p->position).normalisedCopy() * force; + (mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - position).normalisedCopy() * force; +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + p->mDirection += vec; +#else p->direction += vec; +#endif } } From 74e902330fb91e0cc2757bf27e1f65c274aac7d0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Dec 2013 13:40:09 +0100 Subject: [PATCH 013/251] Fix a terrain shader issue --- files/materials/terrain.shader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 7903292d30..3b80c3aecd 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -345,6 +345,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; #endif @shForeach(@shPropertyString(num_layers)) + thisLayerUV = layerUV; #if @shPropertyBool(use_normal_map_@shIterator) normalTex = shSample(normalMap@shIterator, thisLayerUV); #if @shIterator == 0 && IS_FIRST_PASS @@ -354,7 +355,6 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; #endif #endif - thisLayerUV = layerUV; #if @shPropertyBool(use_parallax_@shIterator) thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS ); #endif From 81ec8c2f5565aa91be7a043808dde68deceb9422 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Dec 2013 23:55:40 +0100 Subject: [PATCH 014/251] Handle --version and --help before reading configuration - putting these options into openmw.cfg makes no sense --- apps/openmw/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 51281e213e..e3158d2687 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -168,8 +168,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat bpo::store(valid_opts, variables); bpo::notify(variables); - cfgMgr.readConfiguration(variables, desc); - bool run = true; if (variables.count ("help")) @@ -187,6 +185,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat if (!run) return false; + cfgMgr.readConfiguration(variables, desc); + engine.setGrabMouse(!variables.count("no-grab")); // Font encoding settings From b6bad969a063dc48d2d6ee8ca8f5a8dda94b5159 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Dec 2013 23:57:42 +0100 Subject: [PATCH 015/251] Fix an issue with items that have no UI icon --- apps/openmw/mwgui/container.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 19ed4dbc00..b7c6e3367d 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -61,8 +61,9 @@ namespace MWGui mDraggedWidget = baseWidget; MyGUI::ImageBox* image = baseWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); - int pos = path.rfind("."); - path.erase(pos); + size_t pos = path.rfind("."); + if (pos != std::string::npos) + path.erase(pos); path.append(".dds"); image->setImageTexture(path); image->setNeedMouseFocus(false); From 31c1f484eda7d40ff7c11f415e45fd53ea5d736a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 00:20:01 +0100 Subject: [PATCH 016/251] Slight performance improvement for WindowManager::updateVisible --- apps/openmw/mwgui/inventorywindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 56c474c89a..ffd81e98b2 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -120,10 +120,13 @@ namespace MWGui Settings::Manager::getFloat(setting + " y", "Windows") * viewSize.height); MyGUI::IntSize size (Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width, Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height); + + if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight()) + mPreviewDirty = true; + mMainWidget->setPosition(pos); mMainWidget->setSize(size); adjustPanes(); - mPreviewDirty = true; } TradeItemModel* InventoryWindow::getTradeModel() From a9e1e89bbc32fecb16efb746e31254def19347b5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 00:29:16 +0100 Subject: [PATCH 017/251] Bug #1007: Fix the console getting key focus when a reference becomes unavailable, even if the console is not visible --- apps/openmw/mwgui/console.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 96bc204c15..b8d20709d9 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -406,13 +406,14 @@ namespace MWGui setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")"); mPtr = object; } + // User clicked on an object. Restore focus to the console command line. + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } else { setTitle("#{sConsoleTitle}"); mPtr = MWWorld::Ptr(); } - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } void Console::onReferenceUnavailable() From 0050e6e67b2e32ce1ba9e75ffbdec5dcec6d19fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 01:29:42 +0100 Subject: [PATCH 018/251] Support materials with no base (diffuse) texture (should be white). Support alternate UV set for diffuse texture. --- components/nifogre/material.cpp | 7 ++++++- files/materials/objects.mat | 8 ++++++-- files/materials/objects.shader | 13 ++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index d529fb109f..a18b29544a 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -324,6 +324,11 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture])); instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture])); instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture])); + if (!texName[Nif::NiTexturingProperty::BaseTexture].empty()) + { + instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("diffuseMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::BaseTexture].uvSet))); + } if (!texName[Nif::NiTexturingProperty::GlowTexture].empty()) { instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true))); @@ -347,7 +352,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, i == Nif::NiTexturingProperty::GlowTexture) continue; if(!texName[i].empty()) - warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i)); + warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i) + " in " + name); } if (vertexColour) diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 32787e159b..2cced60910 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -10,8 +10,10 @@ material openmw_objects_base emissiveMap use_emissive_map false use_detail_map false + use_diffuse_map false emissiveMapUVSet 0 detailMapUVSet 0 + diffuseMapUVSet 0 use_parallax false scene_blend default @@ -34,8 +36,10 @@ material openmw_objects_base normalMap $normalMap emissiveMapUVSet $emissiveMapUVSet detailMapUVSet $detailMapUVSet + diffuseMapUVSet $diffuseMapUVSet emissiveMap $emissiveMap detailMap $detailMap + diffuseMap $diffuseMap env_map $env_map env_map_color $env_map_color use_parallax $use_parallax @@ -55,8 +59,8 @@ material openmw_objects_base texture_unit diffuseMap { direct_texture $diffuseMap - create_in_ffp true - tex_coord_set $emissiveMapUVSet + create_in_ffp $use_diffuse_map + tex_coord_set $diffuseMapUVSet } texture_unit normalMap diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 3d873f463f..e50c97a6a2 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -17,13 +17,14 @@ #define NORMAL_MAP @shPropertyHasValue(normalMap) #define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) #define DETAIL_MAP @shPropertyHasValue(detailMap) +#define DIFFUSE_MAP @shPropertyHasValue(diffuseMap) #define PARALLAX @shPropertyBool(use_parallax) #define PARALLAX_SCALE 0.04 #define PARALLAX_BIAS -0.02 // right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more -#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet)) +#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet)) // if normal mapping is enabled, we force pixel lighting #define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap)) @@ -246,7 +247,9 @@ #endif SH_BEGIN_PROGRAM +#if DIFFUSE_MAP shSampler2D(diffuseMap) +#endif #if NORMAL_MAP shSampler2D(normalMap) @@ -376,7 +379,15 @@ newUV += (TSeyeDir.xyxy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS )).xyxy; #endif +#if DIFFUSE_MAP + #if @shPropertyString(diffuseMapUVSet) + float4 diffuse = shSample(diffuseMap, newUV.zw); + #else float4 diffuse = shSample(diffuseMap, newUV.xy); + #endif +#else + float4 diffuse = float4(1,1,1,1); +#endif shOutputColour(0) = diffuse; #if DETAIL_MAP From e68e2f82a2db9288bc3babb6202c7a53fdc83991 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Dec 2013 02:40:55 +0100 Subject: [PATCH 019/251] Implement DarkTexture slot. Fix an issue with incorrect transparency override when base texture is empty. --- components/nifogre/material.cpp | 23 +++++++++++++++++------ files/materials/objects.mat | 24 +++++++++++++++++++----- files/materials/objects.shader | 25 +++++++++++++++++++------ 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index a18b29544a..c7c9abfc10 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -324,6 +324,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture])); instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture])); instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture])); + instance->setProperty("darkMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DarkTexture])); if (!texName[Nif::NiTexturingProperty::BaseTexture].empty()) { instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true))); @@ -339,6 +340,11 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true))); instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet))); } + if (!texName[Nif::NiTexturingProperty::DarkTexture].empty()) + { + instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("darkMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DarkTexture].uvSet))); + } bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty() && texName[Nif::NiTexturingProperty::BumpTexture].find("_nh.") != std::string::npos; @@ -348,6 +354,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, { if(i == Nif::NiTexturingProperty::BaseTexture || i == Nif::NiTexturingProperty::DetailTexture || + i == Nif::NiTexturingProperty::DarkTexture || i == Nif::NiTexturingProperty::BumpTexture || i == Nif::NiTexturingProperty::GlowTexture) continue; @@ -358,15 +365,19 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, if (vertexColour) instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); - // Add transparency if NiAlphaProperty was present - NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]); - if (result.first) + // Override alpha flags based on our override list (transparency-overrides.cfg) + if (!texName[0].empty()) { - alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ - alphaTest = result.second; - depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on + NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]); + if (result.first) + { + alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ + alphaTest = result.second; + depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on + } } + // Add transparency if NiAlphaProperty was present if((alphaFlags&1)) { std::string blend_mode; diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 2cced60910..751b512431 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -8,12 +8,15 @@ material openmw_objects_base diffuseMap black.png normalMap emissiveMap + darkMap use_emissive_map false use_detail_map false use_diffuse_map false + use_dark_map false emissiveMapUVSet 0 detailMapUVSet 0 diffuseMapUVSet 0 + darkMapUVSet 0 use_parallax false scene_blend default @@ -37,9 +40,11 @@ material openmw_objects_base emissiveMapUVSet $emissiveMapUVSet detailMapUVSet $detailMapUVSet diffuseMapUVSet $diffuseMapUVSet + darkMapUVSet $darkMapUVSet emissiveMap $emissiveMap detailMap $detailMap diffuseMap $diffuseMap + darkMap $darkMap env_map $env_map env_map_color $env_map_color use_parallax $use_parallax @@ -70,12 +75,13 @@ material openmw_objects_base num_mipmaps 4 } - texture_unit emissiveMap + texture_unit darkMap { - create_in_ffp $use_emissive_map - colour_op add - direct_texture $emissiveMap - tex_coord_set $emissiveMapUVSet + create_in_ffp $use_dark_map + colour_op_ex modulate src_current src_texture + alpha_op_ex modulate src_current src_texture + direct_texture $darkMap + tex_coord_set $darkMapUVSet } texture_unit detailMap @@ -86,6 +92,14 @@ material openmw_objects_base tex_coord_set $detailMapUVSet } + texture_unit emissiveMap + { + create_in_ffp $use_emissive_map + colour_op add + direct_texture $emissiveMap + tex_coord_set $emissiveMapUVSet + } + texture_unit envMap { create_in_ffp $env_map diff --git a/files/materials/objects.shader b/files/materials/objects.shader index e50c97a6a2..93368f1f68 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -18,13 +18,14 @@ #define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) #define DETAIL_MAP @shPropertyHasValue(detailMap) #define DIFFUSE_MAP @shPropertyHasValue(diffuseMap) +#define DARK_MAP @shPropertyHasValue(darkMap) #define PARALLAX @shPropertyBool(use_parallax) #define PARALLAX_SCALE 0.04 #define PARALLAX_BIAS -0.02 // right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more -#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet)) +#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet) || @shPropertyString(darkMapUVSet)) // if normal mapping is enabled, we force pixel lighting #define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap)) @@ -255,14 +256,18 @@ shSampler2D(normalMap) #endif -#if EMISSIVE_MAP - shSampler2D(emissiveMap) +#if DARK_MAP + shSampler2D(darkMap) #endif #if DETAIL_MAP shSampler2D(detailMap) #endif +#if EMISSIVE_MAP + shSampler2D(emissiveMap) +#endif + #if ENV_MAP shSampler2D(envMap) shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color) @@ -388,16 +393,24 @@ #else float4 diffuse = float4(1,1,1,1); #endif - shOutputColour(0) = diffuse; #if DETAIL_MAP #if @shPropertyString(detailMapUVSet) - shOutputColour(0) *= shSample(detailMap, newUV.zw)*2; + diffuse *= shSample(detailMap, newUV.zw)*2; #else - shOutputColour(0) *= shSample(detailMap, newUV.xy)*2; + diffuse *= shSample(detailMap, newUV.xy)*2; #endif #endif +#if DARK_MAP +#if @shPropertyString(darkMapUVSet) + diffuse *= shSample(darkMap, newUV.zw); +#else + diffuse *= shSample(darkMap, newUV.xy); +#endif +#endif + + shOutputColour(0) = diffuse; #if !VERTEX_LIGHTING float3 viewPos = shMatrixMult(worldView, float4(objSpacePositionPassthrough.xyz,1)).xyz; From aef0fd146085fd61782cbe7e89e173000c3aeb68 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 00:28:19 +0100 Subject: [PATCH 020/251] Rename some path methods --- components/files/configurationmanager.cpp | 14 +++++++------- components/files/fixedpath.hpp | 12 ++++++------ components/files/linuxpath.cpp | 4 ++-- components/files/linuxpath.hpp | 20 ++++---------------- components/files/macospath.cpp | 4 ++-- components/files/macospath.hpp | 4 ++-- components/files/windowspath.cpp | 4 ++-- components/files/windowspath.hpp | 4 ++-- 8 files changed, 27 insertions(+), 39 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 75c877dc5d..056adb8ce5 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -26,9 +26,9 @@ ConfigurationManager::ConfigurationManager() { setupTokensMapping(); - boost::filesystem::create_directories(mFixedPath.getUserPath()); + boost::filesystem::create_directories(mFixedPath.getUserConfigPath()); - mLogPath = mFixedPath.getUserPath(); + mLogPath = mFixedPath.getUserConfigPath(); } ConfigurationManager::~ConfigurationManager() @@ -39,19 +39,19 @@ 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<>::getUserPath)); + mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath)); mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description) { - loadConfig(mFixedPath.getUserPath(), variables, description); + loadConfig(mFixedPath.getUserConfigPath(), variables, description); boost::program_options::notify(variables); loadConfig(mFixedPath.getLocalPath(), variables, description); boost::program_options::notify(variables); - loadConfig(mFixedPath.getGlobalPath(), variables, description); + loadConfig(mFixedPath.getGlobalConfigPath(), variables, description); boost::program_options::notify(variables); } @@ -141,12 +141,12 @@ void ConfigurationManager::loadConfig(const boost::filesystem::path& path, const boost::filesystem::path& ConfigurationManager::getGlobalPath() const { - return mFixedPath.getGlobalPath(); + return mFixedPath.getGlobalConfigPath(); } const boost::filesystem::path& ConfigurationManager::getUserPath() const { - return mFixedPath.getUserPath(); + return mFixedPath.getUserConfigPath(); } const boost::filesystem::path& ConfigurationManager::getLocalPath() const diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index a309dc9fb6..b3708a4772 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -48,8 +48,8 @@ struct FixedPath */ FixedPath(const std::string& application_name) : mPath(application_name + "/") - , mUserPath(mPath.getUserPath()) - , mGlobalPath(mPath.getGlobalPath()) + , mUserPath(mPath.getUserConfigPath()) + , mGlobalConfigPath(mPath.getGlobalConfigPath()) , mLocalPath(mPath.getLocalPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) , mInstallPath(mPath.getInstallPath()) @@ -62,7 +62,7 @@ struct FixedPath * * \return boost::filesystem::path */ - const boost::filesystem::path& getUserPath() const + const boost::filesystem::path& getUserConfigPath() const { return mUserPath; } @@ -72,9 +72,9 @@ struct FixedPath * * \return boost::filesystem::path */ - const boost::filesystem::path& getGlobalPath() const + const boost::filesystem::path& getGlobalConfigPath() const { - return mGlobalPath; + return mGlobalConfigPath; } /** @@ -106,7 +106,7 @@ struct FixedPath PathType mPath; boost::filesystem::path mUserPath; /**< User path */ - boost::filesystem::path mGlobalPath; /**< Global path */ + boost::filesystem::path mGlobalConfigPath; /**< Global path */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ boost::filesystem::path mGlobalDataPath; /**< Global application data path */ diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index c974a91d35..0dcc10595c 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -19,7 +19,7 @@ LinuxPath::LinuxPath(const std::string& application_name) { } -boost::filesystem::path LinuxPath::getUserPath() const +boost::filesystem::path LinuxPath::getUserConfigPath() const { boost::filesystem::path userPath("."); @@ -63,7 +63,7 @@ boost::filesystem::path LinuxPath::getCachePath() const return userPath / ".cache" / mName; } -boost::filesystem::path LinuxPath::getGlobalPath() const +boost::filesystem::path LinuxPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("/etc/"); return globalPath / mName; diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 6acf2a2d5f..191f779b65 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -20,44 +20,32 @@ struct LinuxPath /** * \brief Return path to the user directory. - * - * \return boost::filesystem::path */ - boost::filesystem::path getUserPath() const; + boost::filesystem::path getUserConfigPath() const; /** - * \brief Return path to the global (system) directory where game files could be placed. - * - * \return boost::filesystem::path + * \brief Return path to the global (system) directory where config files can be placed. */ - boost::filesystem::path getGlobalPath() const; + boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return path to the runtime configuration directory which is the * place where an application was started. - * - * \return boost::filesystem::path */ boost::filesystem::path getLocalPath() const; /** - * \brief - * - * \return boost::filesystem::path + * \brief Return path to the global (system) directory where game files can be placed. */ boost::filesystem::path getGlobalDataPath() const; /** * \brief - * - * \return boost::filesystem::path */ boost::filesystem::path getCachePath() const; /** * \brief Gets the path of the installed Morrowind version if there is one. - * - * \return boost::filesystem::path */ boost::filesystem::path getInstallPath() const; diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 9edcd6ef2a..f9f9889951 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -22,7 +22,7 @@ MacOsPath::MacOsPath(const std::string& application_name) { } -boost::filesystem::path MacOsPath::getUserPath() const +boost::filesystem::path MacOsPath::getUserConfigPath() const { boost::filesystem::path userPath("."); @@ -43,7 +43,7 @@ boost::filesystem::path MacOsPath::getUserPath() const return userPath / mName; } -boost::filesystem::path MacOsPath::getGlobalPath() const +boost::filesystem::path MacOsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("/Library/Preferences/"); return globalPath / mName; diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 576ec16812..e9307c464a 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -23,14 +23,14 @@ struct MacOsPath * * \return boost::filesystem::path */ - boost::filesystem::path getUserPath() const; + boost::filesystem::path getUserConfigPath() const; /** * \brief Return path to the global (system) directory. * * \return boost::filesystem::path */ - boost::filesystem::path getGlobalPath() const; + boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return path to the runtime directory which is the diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index e8f1a2b08f..001611f996 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -25,7 +25,7 @@ WindowsPath::WindowsPath(const std::string& application_name) { } -boost::filesystem::path WindowsPath::getUserPath() const +boost::filesystem::path WindowsPath::getUserConfigPath() const { boost::filesystem::path userPath("."); @@ -41,7 +41,7 @@ boost::filesystem::path WindowsPath::getUserPath() const return userPath / mName; } -boost::filesystem::path WindowsPath::getGlobalPath() const +boost::filesystem::path WindowsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("."); diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 6044b67c21..dc3b71d918 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -29,14 +29,14 @@ struct WindowsPath * * \return boost::filesystem::path */ - boost::filesystem::path getUserPath() const; + boost::filesystem::path getUserConfigPath() const; /** * \brief Returns "X:\Program Files\" * * \return boost::filesystem::path */ - boost::filesystem::path getGlobalPath() const; + boost::filesystem::path getGlobalConfigPath() const; /** * \brief Return local path which is a location where From 33389b9b63aaea8b998e0e6bdd240769dd90562c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 01:24:43 +0100 Subject: [PATCH 021/251] XDG compliant paths --- apps/launcher/maindialog.cpp | 10 +-- apps/opencs/editor.cpp | 4 - apps/opencs/model/doc/document.cpp | 4 +- apps/opencs/model/doc/documentmanager.cpp | 4 +- apps/opencs/model/settings/usersettings.cpp | 2 +- apps/openmw/engine.cpp | 6 +- components/files/configurationmanager.cpp | 8 +- components/files/configurationmanager.hpp | 2 +- components/files/fixedpath.hpp | 19 ++--- components/files/linuxpath.cpp | 86 ++++++++++----------- components/files/linuxpath.hpp | 2 + components/files/macospath.cpp | 79 +++++++++---------- components/files/macospath.hpp | 2 + components/files/windowspath.cpp | 6 ++ components/files/windowspath.hpp | 2 + 15 files changed, 119 insertions(+), 117 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index a6ac3d78d7..9b3c4e1b02 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -219,7 +219,7 @@ bool Launcher::MainDialog::showFirstRunDialog() } // Create the file if it doesn't already exist, else the importer will fail - QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg"); + QString path = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + QString("openmw.cfg"); QFile file(path); if (!file.exists()) { @@ -334,7 +334,7 @@ bool Launcher::MainDialog::setupLauncherSettings() { mLauncherSettings.setMultiValueEnabled(true); - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QStringList paths; paths.append(QString("launcher.cfg")); @@ -440,7 +440,7 @@ bool Launcher::expansions(Launcher::UnshieldThread& cd) bool Launcher::MainDialog::setupGameSettings() { - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); // Load the user config file first, separately @@ -591,7 +591,7 @@ bool Launcher::MainDialog::setupGraphicsSettings() { mGraphicsSettings.setMultiValueEnabled(false); - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); QFile localDefault(QString("settings-default.cfg")); @@ -678,7 +678,7 @@ bool Launcher::MainDialog::writeSettings() mGraphicsPage->saveSettings(); mDataFilesPage->saveSettings(); - QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); + QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()); QDir dir(userPath); if (!dir.exists()) { diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 1c1e37c2d4..44926610b3 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -86,10 +86,6 @@ void CS::Editor::setupDataFiles() return; } - // Set the charset for reading the esm/esp files - // QString encoding = QString::fromStdString(variables["encoding"].as()); - //mFileDialog.setEncoding(encoding); - dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); mDocumentManager.setResourceDir (variables["resources"].as()); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 27f4f498a4..3ef14ee7e5 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2221,7 +2221,7 @@ void CSMDoc::Document::createBase() CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_) : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), - mProjectPath ((configuration.getUserPath() / "projects") / + mProjectPath ((configuration.getUserDataPath() / "projects") / (savePath.filename().string() + ".project")), mSaving (*this, mProjectPath) { @@ -2254,7 +2254,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co } else { - boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); + boost::filesystem::path locCustomFiltersPath (configuration.getUserDataPath()); locCustomFiltersPath /= "defaultfilters"; if (boost::filesystem::exists(locCustomFiltersPath)) diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 024c46beae..3ff75c9c15 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -15,7 +15,7 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) : mConfiguration (configuration) { - boost::filesystem::path projectPath = configuration.getUserPath() / "projects"; + boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects"; if (!boost::filesystem::is_directory (projectPath)) boost::filesystem::create_directories (projectPath); @@ -53,4 +53,4 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document) void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) { mResDir = boost::filesystem::system_complete(parResDir); -} \ No newline at end of file +} diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 1ce28ed75c..94cee8a43b 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -251,7 +251,7 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName) bool localOk = loadFromFile(localFilePath); //user - mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName; + mUserFilePath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + fileName; loadFromFile(mUserFilePath); if (!(localOk || globalOk)) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f2afb3ba51..01cca1b78a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -301,7 +301,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); // load user settings if they exist, otherwise just load the default settings as user settings - const std::string settingspath = mCfgMgr.getUserPath().string() + "/settings.cfg"; + const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg"; if (boost::filesystem::exists(settingspath)) settings.loadUser(settingspath); else if (boost::filesystem::exists(localdefault)) @@ -373,7 +373,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.getUserPath() / "input.xml").string(); + std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input.xml").string(); bool keybinderUserExists = boost::filesystem::exists(keybinderUser); MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab); mEnvironment.setInputManager (input); @@ -536,7 +536,7 @@ void OMW::Engine::screenshot() // Count screenshots. int shotCount = 0; - const std::string screenshotPath = mCfgMgr.getUserPath().string(); + const std::string& screenshotPath = mCfgMgr.getUserDataPath().string(); // Find the first unused filename with a do-while std::ostringstream stream; diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 056adb8ce5..761b7ca5ad 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -27,6 +27,7 @@ ConfigurationManager::ConfigurationManager() setupTokensMapping(); boost::filesystem::create_directories(mFixedPath.getUserConfigPath()); + boost::filesystem::create_directories(mFixedPath.getUserDataPath()); mLogPath = mFixedPath.getUserConfigPath(); } @@ -144,11 +145,16 @@ const boost::filesystem::path& ConfigurationManager::getGlobalPath() const return mFixedPath.getGlobalConfigPath(); } -const boost::filesystem::path& ConfigurationManager::getUserPath() const +const boost::filesystem::path& ConfigurationManager::getUserConfigPath() const { return mFixedPath.getUserConfigPath(); } +const boost::filesystem::path& ConfigurationManager::getUserDataPath() const +{ + return mFixedPath.getUserDataPath(); +} + const boost::filesystem::path& ConfigurationManager::getLocalPath() const { return mFixedPath.getLocalPath(); diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 4df8716647..35144fe04f 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -36,7 +36,7 @@ struct ConfigurationManager /**< Fixed paths */ const boost::filesystem::path& getGlobalPath() const; - const boost::filesystem::path& getUserPath() const; + const boost::filesystem::path& getUserConfigPath() const; const boost::filesystem::path& getLocalPath() const; const boost::filesystem::path& getGlobalDataPath() const; diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index b3708a4772..cfd3458ce1 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -48,7 +48,8 @@ struct FixedPath */ FixedPath(const std::string& application_name) : mPath(application_name + "/") - , mUserPath(mPath.getUserConfigPath()) + , mUserConfigPath(mPath.getUserConfigPath()) + , mUserDataPath(mPath.getUserDataPath()) , mGlobalConfigPath(mPath.getGlobalConfigPath()) , mLocalPath(mPath.getLocalPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) @@ -59,18 +60,19 @@ struct FixedPath /** * \brief Return path pointing to the user local configuration directory. - * - * \return boost::filesystem::path */ const boost::filesystem::path& getUserConfigPath() const { - return mUserPath; + return mUserConfigPath; + } + + const boost::filesystem::path& getUserDataPath() const + { + return mUserDataPath; } /** * \brief Return path pointing to the global (system) configuration directory. - * - * \return boost::filesystem::path */ const boost::filesystem::path& getGlobalConfigPath() const { @@ -79,8 +81,6 @@ struct FixedPath /** * \brief Return path pointing to the directory where application was started. - * - * \return boost::filesystem::path */ const boost::filesystem::path& getLocalPath() const { @@ -105,7 +105,8 @@ struct FixedPath private: PathType mPath; - boost::filesystem::path mUserPath; /**< User path */ + boost::filesystem::path mUserConfigPath; /**< User path */ + boost::filesystem::path mUserDataPath; boost::filesystem::path mGlobalConfigPath; /**< Global path */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 0dcc10595c..d285f4229c 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -8,6 +8,39 @@ #include #include + +namespace +{ + boost::filesystem::path getUserHome() + { + const char* dir = getenv("HOME"); + if (dir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + dir = pwd->pw_dir; + } + } + if (dir == NULL) + return boost::filesystem::path(); + else + return boost::filesystem::path(dir); + } + + boost::filesystem::path getEnv(const std::string& envVariable, const boost::filesystem::path& fallback) + { + const char* result = getenv(envVariable.c_str()); + if (!result) + return fallback; + boost::filesystem::path dir(result); + if (dir.empty()) + return fallback; + else + return dir; + } +} + /** * \namespace Files */ @@ -21,46 +54,17 @@ LinuxPath::LinuxPath(const std::string& application_name) boost::filesystem::path LinuxPath::getUserConfigPath() const { - boost::filesystem::path userPath("."); + return getEnv("XDG_CONFIG_HOME", getUserHome() / ".config") / mName; +} - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir); - } - - return userPath / ".config" / mName; +boost::filesystem::path LinuxPath::getUserDataPath() const +{ + return getEnv("XDG_DATA_HOME", getUserHome() / ".local/share") / mName; } boost::filesystem::path LinuxPath::getCachePath() const { - boost::filesystem::path userPath("."); - - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir); - } - - return userPath / ".cache" / mName; + return getEnv("XDG_CACHE_HOME", getUserHome() / ".cache") / mName; } boost::filesystem::path LinuxPath::getGlobalConfigPath() const @@ -84,17 +88,9 @@ boost::filesystem::path LinuxPath::getInstallPath() const { boost::filesystem::path installPath; - char *homePath = getenv("HOME"); - if (homePath == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - homePath = pwd->pw_dir; - } - } + boost::filesystem::path homePath = getUserHome(); - if (homePath != NULL) + if (!homePath.empty()) { boost::filesystem::path wineDefaultRegistry(homePath); wineDefaultRegistry /= ".wine/system.reg"; diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 191f779b65..b710165b44 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -23,6 +23,8 @@ struct LinuxPath */ boost::filesystem::path getUserConfigPath() const; + boost::filesystem::path getUserDataPath() const; + /** * \brief Return path to the global (system) directory where config files can be placed. */ diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index f9f9889951..3e53f53061 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -11,9 +11,26 @@ * FIXME: Someone with MacOS system should check this and correct if necessary */ -/** - * \namespace Files - */ +namespace +{ + boost::filesystem::path getUserHome() + { + const char* dir = getenv("HOME"); + if (dir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + dir = pwd->pw_dir; + } + } + if (dir == NULL) + return boost::filesystem::path(); + else + return boost::filesystem::path(dir); + } +} + namespace Files { @@ -24,21 +41,17 @@ MacOsPath::MacOsPath(const std::string& application_name) boost::filesystem::path MacOsPath::getUserConfigPath() const { - boost::filesystem::path userPath("."); + boost::filesystem::path userPath (getUserHome()); + userPath /= "Library/Preferences/"; - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir) / "Library/Preferences/"; - } + return userPath / mName; +} + +boost::filesystem::path MacOsPath::getUserDataPath() const +{ + // TODO: probably wrong? + boost::filesystem::path userPath (getUserHome()); + userPath /= "Library/Preferences/"; return userPath / mName; } @@ -51,23 +64,9 @@ boost::filesystem::path MacOsPath::getGlobalConfigPath() const boost::filesystem::path MacOsPath::getCachePath() const { - boost::filesystem::path userPath("."); - - const char* theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - userPath = boost::filesystem::path(theDir) / "Library/Caches" / mName; - } - - return userPath; + boost::filesystem::path userPath (getUserHome()); + userPath /= "Library/Caches"; + return userPath / mName; } boost::filesystem::path MacOsPath::getLocalPath() const @@ -85,17 +84,9 @@ boost::filesystem::path MacOsPath::getInstallPath() const { boost::filesystem::path installPath; - char *homePath = getenv("HOME"); - if (homePath == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - homePath = pwd->pw_dir; - } - } + boost::filesystem::path homePath = getUserHome(); - if (homePath != NULL) + if (!homePath.empty()) { boost::filesystem::path wineDefaultRegistry(homePath); wineDefaultRegistry /= ".wine/system.reg"; diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index e9307c464a..7a7dc55778 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -25,6 +25,8 @@ struct MacOsPath */ boost::filesystem::path getUserConfigPath() const; + boost::filesystem::path getUserDataPath() const; + /** * \brief Return path to the global (system) directory. * diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 001611f996..5bb8a6a021 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -41,6 +41,12 @@ boost::filesystem::path WindowsPath::getUserConfigPath() const return userPath / mName; } +boost::filesystem::path WindowsPath::getUserDataPath() const +{ + // Have some chaos, windows people! + return getUserConfigPath(); +} + boost::filesystem::path WindowsPath::getGlobalConfigPath() const { boost::filesystem::path globalPath("."); diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index dc3b71d918..31d0e0e7c1 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -31,6 +31,8 @@ struct WindowsPath */ boost::filesystem::path getUserConfigPath() const; + boost::filesystem::path getUserDataPath() const; + /** * \brief Returns "X:\Program Files\" * From 85ed21dbd297498d9da29fed1be624082fce76c5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 02:09:43 +0100 Subject: [PATCH 022/251] Remove unused command line option --- apps/openmw/engine.cpp | 4 ---- apps/openmw/engine.hpp | 2 -- apps/openmw/main.cpp | 4 ---- 3 files changed, 10 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 01cca1b78a..3c2423345b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -65,10 +65,6 @@ void OMW::Engine::executeLocalScripts() localScripts.setIgnore (MWWorld::Ptr()); } -void OMW::Engine::setAnimationVerbose(bool animverbose) -{ -} - bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt) { bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index e0f8f94e69..9292e81bb3 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -171,8 +171,6 @@ namespace OMW /// Font encoding void setEncoding(const ToUTF8::FromType& encoding); - void setAnimationVerbose(bool animverbose); - void setFallbackValues(std::map map); /// Enable console-only script functionality diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index e3158d2687..abb3723d43 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -121,9 +121,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("content", bpo::value()->default_value(StringsVector(), "") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") - ("anim-verbose", bpo::value()->implicit_value(true) - ->default_value(false), "output animation indices files") - ("nosound", bpo::value()->implicit_value(true) ->default_value(false), "disable all sounds") @@ -240,7 +237,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setSoundUsage(!variables["nosound"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setCompileAll(variables["script-all"].as()); - engine.setAnimationVerbose(variables["anim-verbose"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); From fb845e81a40aa9aab0facfe2b5b3c01fa4a3f039 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 02:24:12 +0100 Subject: [PATCH 023/251] Rename nosound to no-sound for consistency --- apps/mwiniimporter/importer.cpp | 2 +- apps/openmw/main.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index b8b7e4c9da..cf15891144 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -16,7 +16,7 @@ MwIniImporter::MwIniImporter() const char *map[][2] = { { "fps", "General:Show FPS" }, - { "nosound", "General:Disable Audio" }, + { "no-sound", "General:Disable Audio" }, { 0, 0 } }; const char *fallback[] = { diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index abb3723d43..2bf48c1bb9 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -121,7 +121,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("content", bpo::value()->default_value(StringsVector(), "") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") - ("nosound", bpo::value()->implicit_value(true) + ("no-sound", bpo::value()->implicit_value(true) ->default_value(false), "disable all sounds") ("script-verbose", bpo::value()->implicit_value(true) @@ -234,7 +234,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setNewGame(variables["new-game"].as()); // other settings - engine.setSoundUsage(!variables["nosound"].as()); + engine.setSoundUsage(!variables["no-sound"].as()); engine.setScriptsVerbosity(variables["script-verbose"].as()); engine.setCompileAll(variables["script-all"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); From def93f991093d2b72c66444e81fdf9bae0361ada Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 02:12:50 +0100 Subject: [PATCH 024/251] Readme updates --- readme.txt | 81 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/readme.txt b/readme.txt index afcfadea3d..5b9aafcb34 100644 --- a/readme.txt +++ b/readme.txt @@ -36,49 +36,58 @@ https://wiki.openmw.org/index.php?title=Development_Environment_Setup THE DATA PATH -The data path tells OpenMW where to find your Morrowind files. From 0.12.0 on OpenMW should be able to +The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install). -If that does not work for you, please check if you have any leftover openmw.cfg files from versions earlier than 0.12.0. These can interfere with the configuration process, so try to remove then. - -If you are running OpenMW without installing it, you still need to manually adjust the data path. Create a text file named openmw.cfg in the location of the binary and enter the following line: - -data=path to your data directory - -(where you replace "path to your data directory" with the actual location of your data directory) - - COMMAND LINE OPTIONS Syntax: openmw Allowed options: - --help print help message - --version print version information and quit - --data arg (=data) set data directories (later directories have higher priority) - --data-local arg set local data directory (highest priority) - --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --master arg master file(s) - --plugin arg plugin file(s) - --anim-verbose [=arg(=1)] (=0) output animation indices files - --debug [=arg(=1)] (=0) debug mode - --nosound [=arg(=1)] (=0) disable all sounds - --script-verbose [=arg(=1)] (=0) verbose script output - --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of console commands that is executed on startup - --new-game [=arg(=1)] (=0) activate char gen/new game mechanics - --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) - --encoding arg (=win1252) Character encoding used in OpenMW game messages: - - win1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages - - win1252 - Western European (Latin) alphabet, used by default - - --fallback arg fallback values + --help print help message + --version print version information and quit + --data arg (=data) set data directories (later directories + have higher priority) + --data-local arg set local data directory (highest + priority) + --fallback-archive arg (=fallback-archive) + set fallback BSA archives (later + archives have higher priority) + --resources arg (=resources) set resources directory + --start arg (=Beshara) set initial cell + --content arg content file(s): esm/esp, or + omwgame/omwaddon + --anim-verbose [=arg(=1)] (=0) output animation indices files + --no-sound [=arg(=1)] (=0) disable all sounds + --script-verbose [=arg(=1)] (=0) verbose script output + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue + scripts) at startup + --script-console [=arg(=1)] (=0) enable console-only script + functionality + --script-run arg select a file containing a list of + console commands that is executed on + startup + --new-game [=arg(=1)] (=0) activate char gen/new game mechanics + --fs-strict [=arg(=1)] (=0) strict file system handling (no case + folding) + --encoding arg (=win1252) Character encoding used in OpenMW game + messages: + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and + Albanian languages + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic + and other languages + + win1252 - Western European (Latin) + alphabet, used by default + --fallback arg fallback values + --no-grab Don't grab mouse cursor + --activate-dist arg (=-1) activation distance override CHANGELOG From a3ff9e5be870aa8f91c5357003979b223812dd88 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 15:57:54 +0100 Subject: [PATCH 025/251] Change destruction order - fixes a shutdown crash discovered with mesa --- apps/opencs/main.cpp | 4 ++-- libs/openengine/ogre/renderer.cpp | 11 ++++++++++- libs/openengine/ogre/renderer.hpp | 9 ++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 341bdc7800..57eaf2d253 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -42,10 +42,10 @@ int main(int argc, char *argv[]) // TODO: Ogre startup shouldn't be here, but it currently has to: // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( - OgreInit::OgreInit ogreInit; - ogreInit.init("./opencsOgre.log"); // TODO log path? Application mApplication (argc, argv); + OgreInit::OgreInit ogreInit; + ogreInit.init("./opencsOgre.log"); // TODO log path? #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index a0fe6ca849..07bc8f3c90 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include @@ -23,6 +25,12 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + mWindow = NULL; + + delete mOgreInit; + mOgreInit = NULL; + // If we don't do this, the desktop resolution is not restored on exit SDL_SetWindowFullscreen(mSDLWindow, 0); @@ -50,7 +58,8 @@ void OgreRenderer::configure(const std::string &logPath, const std::string& rttMode ) { - mRoot = mOgreInit.init(logPath + "/ogre.log"); + mOgreInit = new OgreInit::OgreInit(); + mRoot = mOgreInit->init(logPath + "/ogre.log"); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index e4af0bf77c..f45f09b01c 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -9,8 +9,6 @@ #include -#include - struct SDL_Window; struct SDL_Surface; @@ -26,6 +24,11 @@ namespace Ogre class ParticleAffectorFactory; } +namespace OgreInit +{ + class OgreInit; +} + namespace OEngine { namespace Render @@ -57,7 +60,7 @@ namespace OEngine Ogre::Camera *mCamera; Ogre::Viewport *mView; - OgreInit::OgreInit mOgreInit; + OgreInit::OgreInit* mOgreInit; Fader* mFader; From 5931fdcbde09d0b736fbe7c3d139c294aab80a92 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 18:16:28 +0100 Subject: [PATCH 026/251] Implement NiBillboardNode. Flags not handled yet. --- apps/openmw/mwrender/actors.cpp | 7 +++++-- apps/openmw/mwrender/actors.hpp | 2 +- apps/openmw/mwrender/animation.cpp | 12 ++++++++++++ apps/openmw/mwrender/animation.hpp | 3 +++ apps/openmw/mwrender/npcanimation.cpp | 11 +++++++++++ apps/openmw/mwrender/npcanimation.hpp | 3 +++ apps/openmw/mwrender/objects.cpp | 7 ++++++- apps/openmw/mwrender/objects.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +++--- components/nif/niffile.cpp | 2 +- components/nif/record.hpp | 1 + components/nifogre/ogrenifloader.cpp | 22 ++++++++++++++++++++++ components/nifogre/ogrenifloader.hpp | 6 ++++++ components/nifogre/skeleton.cpp | 1 + 14 files changed, 76 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 1bdec6e193..639045bbe9 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -150,9 +150,12 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store) } } -void Actors::update (float duration) +void Actors::update (Ogre::Camera* camera) { - // Nothing to do + for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end(); ++iter) + { + iter->second->preRender(camera); + } } Animation* Actors::getAnimation(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 61a0808f57..d91321843b 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -47,7 +47,7 @@ namespace MWRender void removeCell(MWWorld::CellStore* store); - void update (float duration); + void update (Ogre::Camera* camera); /// Updates containing cell for object rendering data void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3b04457b66..d03e91ab3b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1117,6 +1117,16 @@ void Animation::updateEffects(float duration) } } +void Animation::preRender(Ogre::Camera *camera) +{ + for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) + { + NifOgre::ObjectScenePtr objects = it->mObjects; + objects->rotateBillboardNodes(camera); + } + mObjectRoot->rotateBillboardNodes(camera); +} + // TODO: Should not be here Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item) { @@ -1180,6 +1190,8 @@ bool ObjectAnimation::canBatch() const { if(!mObjectRoot->mParticles.empty() || !mObjectRoot->mLights.empty() || !mObjectRoot->mControllers.empty()) return false; + if (!mObjectRoot->mBillboardNodes.empty()) + return false; return std::find_if(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(), FindEntityTransparency()) == mObjectRoot->mEntities.end(); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index aa04e39e21..c33328ab23 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -216,6 +216,9 @@ public: void removeEffect (int effectId); void getLoopingEffects (std::vector& out); + /// Prepare this animation for being rendered with \a camera (rotates billboard nodes) + virtual void preRender (Ogre::Camera* camera); + virtual void setAlpha(float alpha) {} private: void updateEffects(float duration); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index eb0c5dfbc8..8d2f1c7da0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -730,6 +730,17 @@ void NpcAnimation::setAlpha(float alpha) } } +void NpcAnimation::preRender(Ogre::Camera *camera) +{ + Animation::preRender(camera); + for (int i=0; irotateBillboardNodes(camera); + } +} + void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene) { ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent() diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 04dde87c7f..b6ed3f8575 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -116,6 +116,9 @@ public: /// Make the NPC only partially visible virtual void setAlpha(float alpha); + + /// Prepare this animation for being rendered with \a camera (rotates billboard nodes) + virtual void preRender (Ogre::Camera* camera); }; } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 827b9b52a7..2673207137 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -245,11 +245,16 @@ void Objects::disableLights() it->second->enableLights(false); } -void Objects::update(const float dt) +void Objects::update(float dt, Ogre::Camera* camera) { PtrAnimationMap::const_iterator it = mObjects.begin(); for(;it != mObjects.end();it++) it->second->runAnimation(dt); + + it = mObjects.begin(); + for(;it != mObjects.end();it++) + it->second->preRender(camera); + } void Objects::rebuildStaticGeometry() diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 022752fae9..8a50745032 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -48,7 +48,7 @@ public: void enableLights(); void disableLights(); - void update (const float dt); + void update (float dt, Ogre::Camera* camera); ///< per-frame update Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0b10791b8b..8ee292de1e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -372,9 +372,9 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; - mActors->update (duration); - mObjects->update (duration); - + mActors->update (mRendering.getCamera()); + mPlayerAnimation->preRender(mRendering.getCamera()); + mObjects->update (duration, mRendering.getCamera()); mSkyManager->update(duration); diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 402eadefb4..0f7e658fb8 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -210,7 +210,7 @@ static const RecordFactoryEntry recordFactories [] = { { "AvoidNode", &construct , RC_AvoidNode }, { "NiBSParticleNode", &construct , RC_NiBSParticleNode }, { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, - { "NiBillboardNode", &construct , RC_NiNode }, + { "NiBillboardNode", &construct , RC_NiBillboardNode }, { "NiTriShape", &construct , RC_NiTriShape }, { "NiRotatingParticles", &construct , RC_NiRotatingParticles }, { "NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles }, diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 87e342dca5..079b335f05 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -36,6 +36,7 @@ enum RecordType { RC_MISSING = 0, RC_NiNode, + RC_NiBillboardNode, RC_AvoidNode, RC_NiTriShape, RC_NiRotatingParticles, diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index acf8ac13af..ddf42393fb 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -110,6 +110,17 @@ ObjectScene::~ObjectScene() mSkelBase = NULL; } +void ObjectScene::rotateBillboardNodes(Ogre::Camera *camera) +{ + for (std::vector::iterator it = mBillboardNodes.begin(); it != mBillboardNodes.end(); ++it) + { + assert(mSkelBase); + Ogre::Node* node = *it; + node->_setDerivedOrientation(mSkelBase->getParentNode()->_getDerivedOrientation().Inverse() * + camera->getRealOrientation()); + } +} + // Animates a texture class FlipController { @@ -1007,6 +1018,17 @@ class NIFObjectLoader else flags |= node->flags; + if (node->recType == Nif::RC_NiBillboardNode) + { + // TODO: figure out what the flags mean. + // NifSkope has names for them, but doesn't implement them. + // Change mBillboardNodes to map + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + Ogre::Bone* bone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + bone->setManuallyControlled(true); + scene->mBillboardNodes.push_back(bone); + } + Nif::ExtraPtr e = node->extra; while(!e.empty()) { diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 5858aef39b..badb6ccd3b 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -61,6 +61,9 @@ struct ObjectScene { std::vector mParticles; std::vector mLights; + // Nodes that should always face the camera when rendering + std::vector mBillboardNodes; + Ogre::SceneManager* mSceneMgr; // The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length. @@ -76,6 +79,9 @@ struct ObjectScene { { } ~ObjectScene(); + + // Rotate nodes in mBillboardNodes so they face the given camera + void rotateBillboardNodes(Ogre::Camera* camera); }; typedef Ogre::SharedPtr ObjectScenePtr; diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 04bffdeab4..9ec6f15b0c 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -30,6 +30,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */ + node->recType == Nif::RC_NiBillboardNode || /* Handled in the object loader */ node->recType == Nif::RC_NiBSParticleNode || node->recType == Nif::RC_NiCamera || node->recType == Nif::RC_NiAutoNormalParticles || From eab2c89346ef2acc9b8ed78c3288e1fa2b94d834 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 18:46:39 +0100 Subject: [PATCH 027/251] Issue #983: Fix controllers to affect objects attached to the base node --- components/nifogre/ogrenifloader.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index ddf42393fb..78f173bba8 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -178,6 +178,7 @@ public: if ((texture->getName() == "diffuseMap" && mTexSlot == Nif::NiTexturingProperty::BaseTexture) || (texture->getName() == "normalMap" && mTexSlot == Nif::NiTexturingProperty::BumpTexture) || (texture->getName() == "detailMap" && mTexSlot == Nif::NiTexturingProperty::DetailTexture) + || (texture->getName() == "darkMap" && mTexSlot == Nif::NiTexturingProperty::DarkTexture) || (texture->getName() == "emissiveMap" && mTexSlot == Nif::NiTexturingProperty::GlowTexture)) texture->setTextureName(mTextures[curTexture]); } @@ -306,9 +307,6 @@ public: return mData.back().isSet; } - // FIXME: We are not getting all objects here. Skinned meshes get - // attached to the object's root node, and won't be connected via a - // TagPoint. static void setVisible(Ogre::Node *node, int vis) { Ogre::Node::ChildNodeIterator iter = node->getChildIterator(); @@ -317,6 +315,12 @@ public: node = iter.getNext(); setVisible(node, vis); + // Skinned meshes and particle systems are attached to the scene node, not the bone. + // We use the Node's user data to connect it with the mesh / particle system. + Ogre::Any customData = node->getUserObjectBindings().getUserAny(); + if (!customData.isEmpty()) + Ogre::any_cast(customData)->setVisible(vis); + Ogre::TagPoint *tag = dynamic_cast(node); if(tag != NULL) { @@ -659,6 +663,7 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(entity))); scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity); } } @@ -892,6 +897,7 @@ class NIFObjectLoader int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); } Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? From 9877db413ccfaf7d79e8741c3dfe035ae5e7f7c1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 19:49:11 +0100 Subject: [PATCH 028/251] Connect particle systems to the particle node, not the emitter node --- components/nifogre/ogrenifloader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 78f173bba8..7ac101d2b2 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -885,6 +885,10 @@ class NIFObjectLoader sceneNode->attachObject(partsys); + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); + Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); + Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) { @@ -897,7 +901,6 @@ class NIFObjectLoader int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); - trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); } Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? From 71d9755ef167a25ea3ce8098325b44e0811b6bf8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 21:26:59 +0100 Subject: [PATCH 029/251] Bug #991: Don't autoequip items with harmful permanent enchantments --- apps/openmw/mwworld/inventorystore.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 57e35adce9..9fd329b2c3 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -180,6 +180,29 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) std::pair, bool> itemsSlots = MWWorld::Class::get (*iter).getEquipmentSlots (*iter); + // Skip items that have *only* harmful permanent effects + if (!test.getClass().getEnchantment(test).empty()) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Enchantment* enchantment = store.get().find(test.getClass().getEnchantment(test)); + bool harmfulEffect = false; + bool usefulEffect = false; + if (enchantment->mData.mType == ESM::Enchantment::ConstantEffect) + { + for (std::vector::const_iterator it = enchantment->mEffects.mList.begin(); + it != enchantment->mEffects.mList.end(); ++it) + { + const ESM::MagicEffect* effect = store.get().find(it->mEffectID); + if (effect->mData.mFlags & ESM::MagicEffect::Harmful) + harmfulEffect = true; + else + usefulEffect = true; + } + } + if (harmfulEffect && !usefulEffect) + continue; + } + for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { From 5054d8e6c163135062d49eebcd080bc0dcf5f178 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 22:06:13 +0100 Subject: [PATCH 030/251] Bug #1055: Check power use and mana before starting cast animation --- apps/openmw/mwbase/world.hpp | 7 ++++ apps/openmw/mwmechanics/character.cpp | 6 ++- apps/openmw/mwmechanics/spellcasting.cpp | 31 ++------------- apps/openmw/mwworld/worldimp.cpp | 49 ++++++++++++++++++++++-- apps/openmw/mwworld/worldimp.hpp | 11 ++++++ 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 961d3d9587..336ec89dc4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -414,6 +414,13 @@ namespace MWBase virtual bool toggleGodMode() = 0; + /** + * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met. + * @param actor + * @return true if the spell can be casted (i.e. the animation should start) + */ + virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0; + virtual void castSpell (const MWWorld::Ptr& actor) = 0; virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da3ed25232..89afdbe47b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -505,10 +505,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mAttackType.clear(); if(mWeaponType == WeapType_Spell) { + // Unset casting flag, otherwise pressing the mouse button down would + // continue casting every frame if there is no animation + mPtr.getClass().getCreatureStats(mPtr).setAttackingOrSpell(false); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const std::string spellid = stats.getSpells().getSelectedSpell(); - if(!spellid.empty()) + if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) { static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 74816d12e2..025aa6b3a9 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -300,10 +300,11 @@ namespace MWMechanics if (item.getCellRef().mEnchantmentCharge == -1) item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; - if (mCaster.getRefData().getHandle() == "player" && item.getCellRef().mEnchantmentCharge < castCost) + if (item.getCellRef().mEnchantmentCharge < castCost) { // TODO: Should there be a sound here? - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); + if (mCaster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); return false; } @@ -370,33 +371,7 @@ namespace MWMechanics fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss)); stats.setFatigue(fatigue); - // Check mana bool fail = false; - DynamicStat magicka = stats.getMagicka(); - if (magicka.getCurrent() < spell->mData.mCost) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientSP}"); - fail = true; - } - - // Reduce mana - if (!fail) - { - magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); - stats.setMagicka(magicka); - } - - // If this is a power, check if it was already used in last 24h - if (!fail && spell->mData.mType & ESM::Spell::ST_Power) - { - if (stats.canUsePower(spell->mId)) - stats.usePower(spell->mId); - else - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sPowerAlreadyUsed}"); - fail = true; - } - } // Check success int successChance = getSpellSuccessChance(spell, mCaster); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 448211bc2e..021b6184fa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2049,15 +2049,56 @@ namespace MWWorld } } + bool World::startSpellCast(const Ptr &actor) + { + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + + std::string message; + bool fail = false; + bool isPlayer = (actor == getPlayer().getPlayer()); + + std::string selectedSpell = stats.getSpells().getSelectedSpell(); + + if (!selectedSpell.empty()) + { + const ESM::Spell* spell = getStore().get().search(selectedSpell); + + // Check mana + MWMechanics::DynamicStat magicka = stats.getMagicka(); + if (magicka.getCurrent() < spell->mData.mCost) + { + message = "#{sMagicInsufficientSP}"; + fail = true; + } + + // If this is a power, check if it was already used in the last 24h + if (!fail && spell->mData.mType & ESM::Spell::ST_Power) + { + if (stats.canUsePower(spell->mId)) + stats.usePower(spell->mId); + else + { + message = "#{sPowerAlreadyUsed}"; + fail = true; + } + } + + // Reduce mana + magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); + stats.setMagicka(magicka); + } + + if (isPlayer && fail) + MWBase::Environment::get().getWindowManager()->messageBox(message); + + return !fail; + } + void World::castSpell(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); InventoryStore& inv = actor.getClass().getInventoryStore(actor); - // Unset casting flag, otherwise pressing the mouse button down would continue casting every frame if using an enchantment - // (which casts instantly without an animation) - stats.setAttackingOrSpell(false); - 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 5a51cb773c..998d393175 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -499,6 +499,17 @@ namespace MWWorld virtual bool toggleGodMode(); + /** + * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met. + * @param actor + * @return true if the spell can be casted (i.e. the animation should start) + */ + virtual bool startSpellCast (const MWWorld::Ptr& actor); + + /** + * @brief Cast the actual spell, should be called mid-animation + * @param actor + */ virtual void castSpell (const MWWorld::Ptr& actor); virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, From a3017e16d46d650cc01154b5f513f68de9430661 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Dec 2013 22:32:39 +0100 Subject: [PATCH 031/251] Don't allow changing the spell that is being cast mid-animation --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/quickkeysmenu.cpp | 2 - apps/openmw/mwgui/spellwindow.cpp | 75 +++----------------------- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++ apps/openmw/mwgui/windowmanagerimp.hpp | 3 ++ apps/openmw/mwmechanics/character.cpp | 9 +++- 6 files changed, 23 insertions(+), 70 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c47ad066b5..1300efc753 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -204,6 +204,7 @@ namespace MWBase virtual void activateQuickKey (int index) = 0; + virtual std::string getSelectedSpell() = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 3956766499..deabb299e6 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -275,7 +275,6 @@ namespace MWGui if (type == Type_Magic) { std::string spellId = button->getChildAt(0)->getUserString("Spell"); - spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } @@ -342,7 +341,6 @@ namespace MWGui } store.setSelectedEnchantItem(it); - spells.setSelectedSpell(""); MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); } } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 42a0b98657..e2817c38b8 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -86,61 +86,8 @@ namespace MWGui MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); - // the following code switches between selected enchanted item and selected spell (only one of these - // can be active at a time) - std::string selectedSpell = spells.getSelectedSpell(); - MWWorld::Ptr selectedItem; - if (store.getSelectedEnchantItem() != store.end()) - { - selectedSpell = ""; - selectedItem = *store.getSelectedEnchantItem(); - - bool allowSelectedItem = true; - - // make sure that the item is still in the player inventory, otherwise it can't be selected - bool found = false; - for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it) - { - if (*it == selectedItem) - found = true; - } - if (!found) - allowSelectedItem = false; - - // if the selected item can be equipped, make sure that it actually is equipped - std::pair, bool> slots_; - slots_ = MWWorld::Class::get(selectedItem).getEquipmentSlots(selectedItem); - if (!slots_.first.empty()) - { - bool equipped = false; - for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) - { - if (store.getSlot(i) != store.end() && *store.getSlot(i) == selectedItem) - { - equipped = true; - break; - } - } - - if (!equipped) - allowSelectedItem = false; - } - - if (!allowSelectedItem) - { - store.setSelectedEnchantItem(store.end()); - spells.setSelectedSpell(""); - MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); - selectedItem = MWWorld::Ptr(); - } - } - - - for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) - { spellList.push_back (it->first); - } const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -210,7 +157,7 @@ namespace MWGui t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); - if (*it == selectedSpell) + if (*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()) t->setStateSelected(true); mHeight += spellHeight; @@ -229,7 +176,7 @@ namespace MWGui t->setUserString("Spell", *it); t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); - t->setStateSelected(*it == selectedSpell); + t->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()); // cost / success chance MyGUI::Button* costChance = mSpellView->createWidget("SpellText", @@ -239,7 +186,7 @@ namespace MWGui costChance->setCaption(cost + "/" + chance); costChance->setTextAlign(MyGUI::Align::Right); costChance->setNeedMouseFocus(false); - costChance->setStateSelected(*it == selectedSpell); + costChance->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell()); mHeight += spellHeight; @@ -276,7 +223,9 @@ namespace MWGui t->setUserString("Equipped", equipped ? "true" : "false"); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected); t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); - t->setStateSelected(item == selectedItem); + if (store.getSelectedEnchantItem() != store.end()) + t->setStateSelected(item == *store.getSelectedEnchantItem()); + // cost / charge MyGUI::Button* costCharge = mSpellView->createWidget(equipped ? "SpellText" : "SpellTextUnequipped", @@ -302,7 +251,8 @@ namespace MWGui costCharge->setCaption(cost + "/" + charge); costCharge->setTextAlign(MyGUI::Align::Right); costCharge->setNeedMouseFocus(false); - costCharge->setStateSelected(item == selectedItem); + if (store.getSelectedEnchantItem() != store.end()) + costCharge->setStateSelected(item == *store.getSelectedEnchantItem()); mHeight += spellHeight; } @@ -349,9 +299,7 @@ namespace MWGui void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::Spells& spells = stats.getSpells(); MWWorld::Ptr item = *_sender->getUserData(); // retrieve ContainerStoreIterator to the item @@ -379,7 +327,6 @@ namespace MWGui } store.setSelectedEnchantItem(it); - spells.setSelectedSpell(""); MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); updateSpells(); @@ -389,9 +336,7 @@ namespace MWGui { std::string spellId = _sender->getUserString("Spell"); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::Spells& spells = stats.getSpells(); if (MyGUI::InputManager::getInstance().isShiftPressed()) { @@ -419,7 +364,6 @@ namespace MWGui } else { - spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } @@ -450,10 +394,7 @@ namespace MWGui MWMechanics::Spells& spells = stats.getSpells(); if (spells.getSelectedSpell() == mSpellToDelete) - { - spells.setSelectedSpell(""); MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); - } spells.remove(mSpellToDelete); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index afa020082b..8818721f43 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1010,6 +1010,7 @@ namespace MWGui void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) { + mSelectedSpell = spellId; mHud->setSelectedSpell(spellId, successChancePercent); const ESM::Spell* spell = @@ -1020,6 +1021,7 @@ namespace MWGui void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { + mSelectedSpell = ""; const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() .find(MWWorld::Class::get(item).getEnchantment(item)); @@ -1039,6 +1041,7 @@ namespace MWGui void WindowManager::unsetSelectedSpell() { + mSelectedSpell = ""; mHud->unsetSelectedSpell(); mSpellWindow->setTitle("#{sNone}"); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 743160aa82..b332ffc364 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -200,6 +200,7 @@ namespace MWGui virtual void activateQuickKey (int index); + virtual std::string getSelectedSpell() { return mSelectedSpell; } virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); virtual void setSelectedWeapon(const MWWorld::Ptr& item); @@ -288,6 +289,8 @@ namespace MWGui void trackWindow(OEngine::GUI::Layout* layout, const std::string& name); void onWindowChangeCoord(MyGUI::Window* _sender); + std::string mSelectedSpell; + OEngine::GUI::MyGUIManager *mGuiManager; OEngine::Render::OgreRenderer *mRendering; HUD *mHud; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 89afdbe47b..3038faf310 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -511,7 +511,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const std::string spellid = stats.getSpells().getSelectedSpell(); + // For the player, set the spell we want to cast + // This has to be done at the start of the casting animation, + // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) + if (mPtr.getRefData().getHandle() == "player") + stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell()); + + std::string spellid = stats.getSpells().getSelectedSpell(); + if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) { static const std::string schools[] = { From d09a86e20861a7a675ed521573360c25685ee021 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 00:36:06 +0100 Subject: [PATCH 032/251] Issue #1018: Don't allow view mode switching while performing an action --- apps/openmw/mwinput/inputmanagerimp.cpp | 7 ++--- apps/openmw/mwrender/animation.cpp | 10 ++++++ apps/openmw/mwrender/animation.hpp | 2 ++ apps/openmw/mwrender/camera.cpp | 42 +++++++++++++++++++++++-- apps/openmw/mwrender/camera.hpp | 4 +++ 5 files changed, 59 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ab25696351..4713d92e1a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -378,10 +378,9 @@ namespace MWInput MWBase::Environment::get().getWorld()->togglePreviewMode(true); } } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - MWBase::Environment::get().getWorld()->togglePreviewMode(false); - } else if (mPreviewPOVDelay > 0.f) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) { MWBase::Environment::get().getWorld()->togglePOV(); } mPreviewPOVDelay = 0.f; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d03e91ab3b..d425efdbcf 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -996,6 +996,16 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) mSkelBase->detachObjectFromBone(obj); } +bool Animation::isPlaying(Group group) const +{ + for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) + { + if(stateiter->second.mGroups == group) + return true; + } + return false; +} + void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture) { // Early out if we already have this effect diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index c33328ab23..67d8baa3f9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -258,6 +258,8 @@ public: /** Returns true if the named animation group is playing. */ bool isPlaying(const std::string &groupname) const; + bool isPlaying(Group group) const; + /** Gets info about the given animation group. * \param groupname Animation group to check. * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9af3987a8e..8f54be3f88 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -29,7 +29,9 @@ namespace MWRender mNearest(30.f), mFurthest(800.f), mIsNearest(false), - mIsFurthest(false) + mIsFurthest(false), + mVanityToggleQueued(false), + mViewModeToggleQueued(false) { mVanity.enabled = false; mVanity.allowed = true; @@ -103,6 +105,23 @@ namespace MWRender void Camera::update(float duration, bool paused) { + if (!mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + { + // Now process the view changes we queued earlier + if (mVanityToggleQueued) + { + toggleVanityMode(!mVanity.enabled); + mVanityToggleQueued = false; + } + if (mViewModeToggleQueued) + { + + togglePreviewMode(false); + toggleViewMode(); + mViewModeToggleQueued = false; + } + } + updateListener(); if (paused) return; @@ -121,6 +140,14 @@ namespace MWRender void Camera::toggleViewMode() { + // Changing the view will stop all playing animations, so if we are playing + // anything important, queue the view change for later + if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + { + mViewModeToggleQueued = true; + return; + } + mFirstPersonView = !mFirstPersonView; processViewChange(); @@ -140,6 +167,14 @@ namespace MWRender bool Camera::toggleVanityMode(bool enable) { + // Changing the view will stop all playing animations, so if we are playing + // anything important, queue the view change for later + if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + { + mVanityToggleQueued = true; + return false; + } + if(!mVanity.allowed && enable) return false; @@ -168,6 +203,9 @@ namespace MWRender void Camera::togglePreviewMode(bool enable) { + if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + return; + if(mPreviewMode == enable) return; @@ -184,7 +222,6 @@ namespace MWRender } mCamera->setPosition(0.f, 0.f, offset); - rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } void Camera::setSneakOffset() @@ -319,6 +356,7 @@ namespace MWRender mAnimation->setViewMode(NpcAnimation::VM_Normal); mCameraNode->attachObject(mCamera); } + rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index baf2f3685a..87e4866296 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -47,6 +47,9 @@ namespace MWRender bool mDistanceAdjusted; + bool mVanityToggleQueued; + bool mViewModeToggleQueued; + /// Updates sound manager listener data void updateListener(); @@ -77,6 +80,7 @@ namespace MWRender bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); + /// @note this may be ignored if an important animation is currently playing void togglePreviewMode(bool enable); /// \brief Lowers the camera for sneak. From 5a287a7e010ff3834c97614bdd7b73ca9af1420c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 00:41:19 +0100 Subject: [PATCH 033/251] Remove no longer accurate flagAsModified calls. Container items are now modified via ContainerStore, not RefData. --- apps/openmw/mwworld/ptr.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/openmw/mwworld/ptr.cpp b/apps/openmw/mwworld/ptr.cpp index 127ab1364c..384bd71b11 100644 --- a/apps/openmw/mwworld/ptr.cpp +++ b/apps/openmw/mwworld/ptr.cpp @@ -25,9 +25,6 @@ ESM::CellRef& MWWorld::Ptr::getCellRef() const { assert(mRef); - if (mContainerStore) - mContainerStore->flagAsModified(); - return mRef->mRef; } @@ -35,9 +32,6 @@ MWWorld::RefData& MWWorld::Ptr::getRefData() const { assert(mRef); - if (mContainerStore) - mContainerStore->flagAsModified(); - return mRef->mData; } From 30b1da996b7f5e58f5d5ad78836d31497781899e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 00:51:29 +0100 Subject: [PATCH 034/251] Issue #1029 - Quick keys menu: Select compatible replacement when tool used up --- apps/openmw/mwgui/quickkeysmenu.cpp | 51 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index deabb299e6..b8f52cd1fb 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -269,8 +269,35 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); + + if (type == Type_Item || type == Type_MagicItem) + { + MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); + // make sure the item is available + if (item.getRefData ().getCount() < 1) + { + // Try searching for a compatible replacement + std::string id = item.getCellRef().mRefID; + + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, id)) + { + item = *it; + button->getChildAt(0)->setUserData(item); + break; + } + } + + if (item.getRefData().getCount() < 1) + { + // No replacement was found + MWBase::Environment::get().getWindowManager ()->messageBox ( + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); + return; + } + } + } if (type == Type_Magic) { @@ -282,15 +309,6 @@ namespace MWGui { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - // make sure the item is available - if (item.getRefData ().getCount() < 1) - { - // TODO: Try to find a replacement with the same ID? - MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); - return; - } - boost::shared_ptr action = MWWorld::Class::get(item).use(item); action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); @@ -309,14 +327,6 @@ namespace MWGui { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - // make sure the item is available - if (item.getRefData ().getCount() == 0) - { - MWBase::Environment::get().getWindowManager ()->messageBox ( - "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item)); - return; - } - // retrieve ContainerStoreIterator to the item MWWorld::ContainerStoreIterator it = store.begin(); for (; it != store.end(); ++it) @@ -335,9 +345,6 @@ namespace MWGui MWWorld::ActionEquip action(item); action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); - - // since we changed equipping status, update the inventory window - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); } store.setSelectedEnchantItem(it); From 608bd0f525e68f4a89c80b51062bb131673b6d0b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 01:34:56 +0100 Subject: [PATCH 035/251] Don't copy the base node pointer when adding a world object to a container. Fixes bug #1028 --- apps/openmw/mwworld/containerstore.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 3797e69223..2984bb319a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -130,6 +130,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr { MWWorld::ContainerStoreIterator it = addImp(itemPtr); MWWorld::Ptr item = *it; + item.getRefData().setBaseNode(NULL); std::string script = MWWorld::Class::get(item).getScript(item); if(script != "") From 561c6611565f7b41e6a3a2d34d4ec52963e63459 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 01:57:08 +0100 Subject: [PATCH 036/251] Reset starting angle / position when adding world item to a container --- apps/openmw/mwworld/containerstore.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2984bb319a..686e790a38 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -130,7 +130,15 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr { MWWorld::ContainerStoreIterator it = addImp(itemPtr); MWWorld::Ptr item = *it; + + // we may have copied an item from the world, so reset a few things first item.getRefData().setBaseNode(NULL); + item.getCellRef().mPos.rot[0] = 0; + item.getCellRef().mPos.rot[1] = 0; + item.getCellRef().mPos.rot[2] = 0; + item.getCellRef().mPos.pos[0] = 0; + item.getCellRef().mPos.pos[1] = 0; + item.getCellRef().mPos.pos[2] = 0; std::string script = MWWorld::Class::get(item).getScript(item); if(script != "") From 1c60a781a5e06561a83634b7efaf3dbca723c9f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 14:42:22 +0100 Subject: [PATCH 037/251] Add header to CMakeLists --- apps/openmw/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5a062575c7..e87da97c30 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows characterpreview externalrendering globalmap videoplayer ripplesimulation refraction - terrainstorage + terrainstorage renderconst ) add_openmw_dir (mwinput From d262d9e6b0fb761751b689923deda9d8746568f9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 14:51:59 +0100 Subject: [PATCH 038/251] Bug #1054: Set render queue group for effects --- 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 d425efdbcf..8b636f9466 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1027,6 +1027,10 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con params.mObjects = NifOgre::Loader::createObjects(mInsert, model); else params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + + setRenderProperties(params.mObjects, RV_Misc, + RQG_Main, RQG_Alpha, 0.f, false, NULL); + params.mLoop = loop; params.mEffectId = effectId; params.mBoneName = bonename; From 6400f23ab0205096144dd86c4af379be5d162b72 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 14:54:36 +0100 Subject: [PATCH 039/251] Use the material controller manager for effects with overridden texture --- apps/openmw/mwrender/animation.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8b636f9466..de86bcfa7a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1046,17 +1046,12 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) { Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; - sh::Factory::getInstance()._ensureMaterial(partSys->getMaterialName(), "Default"); - Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName()); - static int count = 0; - Ogre::String materialName = "openmw/" + Ogre::StringConverter::toString(count++); - // TODO: destroy when effect is removed - Ogre::MaterialPtr newMat = mat->clone(materialName); - partSys->setMaterialName(materialName); - for (int t=0; tgetNumTechniques(); ++t) + Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); + + for (int t=0; tgetNumTechniques(); ++t) { - Ogre::Technique* tech = newMat->getTechnique(t); + Ogre::Technique* tech = mat->getTechnique(t); for (int p=0; pgetNumPasses(); ++p) { Ogre::Pass* pass = tech->getPass(p); From a9526622b18df4e24d0f7a1ccfadd50ebf954803 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 17:45:04 +0100 Subject: [PATCH 040/251] Particle improvements: particle systems now move with the particle bone, not the scene node. This difference is not noticable if the particle bone is static, but it makes the code *much* nicer and mirrors more closely what NifSkope does. --- components/nifogre/ogrenifloader.cpp | 20 +--- components/nifogre/particles.cpp | 132 +++++---------------------- 2 files changed, 25 insertions(+), 127 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 7ac101d2b2..9fa9136862 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -789,8 +789,6 @@ class NIFObjectLoader emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees())); emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees())); emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees())); - emitter->setParameter("skelbase", skelBaseName); - emitter->setParameter("bone", bone->getName()); Nif::ExtraPtr e = partctrl->extra; while(!e.empty()) @@ -883,11 +881,9 @@ class NIFObjectLoader partsys->setParticleQuota(particledata->numParticles); partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace)); - sceneNode->attachObject(partsys); - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); - trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast(partsys))); + scene->mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) @@ -900,6 +896,9 @@ class NIFObjectLoader { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); + // Set the emitter bone as user data on the particle system + // so the emitters/affectors can access it easily. + partsys->getUserObjectBindings().setUserAny(Ogre::Any(trgtbone)); createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); } @@ -1274,17 +1273,6 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo } } - for(size_t i = 0;i < scene->mParticles.size();i++) - { - Ogre::ParticleSystem *partsys = scene->mParticles[i]; - if(partsys->isAttached()) - partsys->detachFromParent(); - - Ogre::TagPoint *tag = scene->mSkelBase->attachObjectToBone( - scene->mSkelBase->getSkeleton()->getRootBone()->getName(), partsys); - tag->setScale(scale); - } - return scene; } diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index d306f49447..a1433a6690 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -16,46 +16,11 @@ class NifEmitter : public Ogre::ParticleEmitter { public: - std::string mSkelBaseName; - Ogre::Bone* mBone; + Ogre::Bone* mEmitterBone; + Ogre::Bone* mParticleBone; Ogre::ParticleSystem* getPartSys() { return mParent; } - class CmdSkelBase : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - NifEmitter* emitter = static_cast(target); - emitter->mSkelBaseName = val; - } - }; - - class CmdBone : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - NifEmitter* emitter = static_cast(target); - assert(!emitter->mSkelBaseName.empty() && "Base entity needs to be set first"); - Ogre::ParticleSystem* partsys = emitter->getPartSys(); - Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(emitter->mSkelBaseName); - Ogre::Bone* bone = ent->getSkeleton()->getBone(val); - assert(bone); - emitter->mBone = bone; - } - }; - /** Command object for the emitter width (see Ogre::ParamCommand).*/ class CmdWidth : public Ogre::ParamCommand { @@ -165,8 +130,10 @@ public: NifEmitter(Ogre::ParticleSystem *psys) : Ogre::ParticleEmitter(psys) - , mBone(NULL) { + mEmitterBone = Ogre::any_cast(psys->getUserObjectBindings().getUserAny()); + Ogre::TagPoint* tag = static_cast(mParent->getParentNode()); + mParticleBone = static_cast(tag->getParent()); initDefaults("Nif"); } @@ -180,7 +147,6 @@ public: /** See Ogre::ParticleEmitter. */ void _initParticle(Ogre::Particle *particle) { - assert (mBone && "No node set"); Ogre::Vector3 xOff, yOff, zOff; // Call superclass @@ -203,7 +169,10 @@ public: Ogre::Real& totalTimeToLive = particle->totalTimeToLive; Ogre::Real& timeToLive = particle->timeToLive; #endif - position = mBone->_getDerivedPosition() + xOff + yOff + zOff; + + position = xOff + yOff + zOff + + mParticleBone->_getDerivedOrientation().Inverse() * (mEmitterBone->_getDerivedPosition() + - mParticleBone->_getDerivedPosition()); // Generate complex data by reference genEmissionColour(colour); @@ -211,7 +180,9 @@ public: // NOTE: We do not use mDirection/mAngle for the initial direction. Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); - direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + direction = (mParticleBone->_getDerivedOrientation().Inverse() + * mEmitterBone->_getDerivedOrientation() * + Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * Ogre::Vector3::UNIT_Z; @@ -374,16 +345,6 @@ protected: Ogre::PT_REAL), &msHorizontalAngleCmd); - dict->addParameter(Ogre::ParameterDef("bone", - "The bone where the particles should be spawned", - Ogre::PT_STRING), - &msBoneCmd); - - dict->addParameter(Ogre::ParameterDef("skelbase", - "The name of the entity containing the bone (see 'bone' parameter)", - Ogre::PT_STRING), - &msSkelBaseCmd); - return true; } return false; @@ -397,8 +358,6 @@ protected: static CmdVerticalAngle msVerticalAngleCmd; static CmdHorizontalDir msHorizontalDirCmd; static CmdHorizontalAngle msHorizontalAngleCmd; - static CmdBone msBoneCmd; - static CmdSkelBase msSkelBaseCmd; }; NifEmitter::CmdWidth NifEmitter::msWidthCmd; NifEmitter::CmdHeight NifEmitter::msHeightCmd; @@ -407,8 +366,6 @@ NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd; NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd; NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd; NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd; -NifEmitter::CmdBone NifEmitter::msBoneCmd; -NifEmitter::CmdSkelBase NifEmitter::msSkelBaseCmd; Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys) { @@ -575,47 +532,11 @@ class GravityAffector : public Ogre::ParticleAffector }; public: - std::string mSkelBaseName; - Ogre::Bone* mBone; + Ogre::Bone* mEmitterBone; + Ogre::Bone* mParticleBone; Ogre::ParticleSystem* getPartSys() { return mParent; } - class CmdSkelBase : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - GravityAffector* affector = static_cast(target); - affector->mSkelBaseName = val; - } - }; - - class CmdBone : public Ogre::ParamCommand - { - public: - Ogre::String doGet(const void *target) const - { - assert(false && "Unimplemented"); - return ""; - } - void doSet(void *target, const Ogre::String &val) - { - GravityAffector* affector = static_cast(target); - assert(!affector->mSkelBaseName.empty() && "Base entity needs to be set first"); - Ogre::ParticleSystem* partsys = affector->getPartSys(); - Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(affector->mSkelBaseName); - Ogre::Bone* bone = ent->getSkeleton()->getBone(val); - assert(bone); - affector->mBone = bone; - } - }; - - /** Command object for force (see Ogre::ParamCommand).*/ class CmdForce : public Ogre::ParamCommand { @@ -709,8 +630,11 @@ public: , mForceType(Type_Wind) , mPosition(0.0f) , mDirection(0.0f) - , mBone(NULL) { + mEmitterBone = Ogre::any_cast(psys->getUserObjectBindings().getUserAny()); + Ogre::TagPoint* tag = static_cast(mParent->getParentNode()); + mParticleBone = static_cast(tag->getParent()); + mType = "Gravity"; // Init parameters @@ -731,16 +655,6 @@ public: dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd); dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd); dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd); - - dict->addParameter(Ogre::ParameterDef("bone", - "The bone where the particles should be spawned", - Ogre::PT_STRING), - &msBoneCmd); - - dict->addParameter(Ogre::ParameterDef("skelbase", - "The name of the entity containing the bone (see 'bone' parameter)", - Ogre::PT_STRING), - &msSkelBaseCmd); } } @@ -782,13 +696,11 @@ public: static CmdForceType msForceTypeCmd; static CmdDirection msDirectionCmd; static CmdPosition msPositionCmd; - static CmdBone msBoneCmd; - static CmdSkelBase msSkelBaseCmd; protected: void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) { - const Ogre::Vector3 vec = mBone->_getDerivedOrientation() * mDirection * mForce * timeElapsed; + const Ogre::Vector3 vec = mDirection * mForce * timeElapsed; Ogre::ParticleIterator pi = psys->_getIterator(); while (!pi.end()) { @@ -813,8 +725,8 @@ protected: #else Ogre::Vector3 position = p->position; #endif - const Ogre::Vector3 vec = ( - (mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - position).normalisedCopy() * force; + + Ogre::Vector3 vec = (mPosition - position).normalisedCopy() * force; #if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) p->mDirection += vec; #else @@ -835,8 +747,6 @@ GravityAffector::CmdForce GravityAffector::msForceCmd; GravityAffector::CmdForceType GravityAffector::msForceTypeCmd; GravityAffector::CmdDirection GravityAffector::msDirectionCmd; GravityAffector::CmdPosition GravityAffector::msPositionCmd; -GravityAffector::CmdBone GravityAffector::msBoneCmd; -GravityAffector::CmdSkelBase GravityAffector::msSkelBaseCmd; Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys) { From 27092a44945b51d1beffc8612cb23a51f1aca267 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 18:28:54 +0100 Subject: [PATCH 041/251] flagAsModified should be private --- apps/openmw/mwworld/containerstore.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index df7168dfa1..b34c710063 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -94,6 +94,8 @@ namespace MWWorld ContainerStoreIterator addNewStack (const Ptr& ptr); ///< Add the item to this container (do not try to stack it onto existing items) + virtual void flagAsModified(); + public: virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); @@ -105,10 +107,6 @@ namespace MWWorld void clear(); ///< Empty container. - virtual void flagAsModified(); - ///< \attention This function is internal to the world model and should not be called from - /// outside. - float getWeight() const; ///< Return total weight of the items contained in *this. From 686d9efac3030a6220fce0e450c591a60f9fde24 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 18:29:15 +0100 Subject: [PATCH 042/251] Bug #1060: Fix incorrect spell type checks --- apps/openmw/mwmechanics/spells.cpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 6e7ac6f315..0088bcb603 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -125,7 +125,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mType & ESM::Spell::ST_Disease) + if (spell->mData.mType == ESM::Spell::ST_Disease) mSpells.erase(iter++); else iter++; @@ -139,7 +139,7 @@ namespace MWMechanics const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); - if (spell->mData.mType & ESM::Spell::ST_Blight) + if (spell->mData.mType == ESM::Spell::ST_Blight) mSpells.erase(iter++); else iter++; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 021b6184fa..bc60ea8be9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2072,7 +2072,7 @@ namespace MWWorld } // If this is a power, check if it was already used in the last 24h - if (!fail && spell->mData.mType & ESM::Spell::ST_Power) + if (!fail && spell->mData.mType == ESM::Spell::ST_Power) { if (stats.canUsePower(spell->mId)) stats.usePower(spell->mId); From 02277db685a31617e17cc067234419b2c78ebeaa Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 19:52:10 +0100 Subject: [PATCH 043/251] Bug #1052: Don't use set/getOnlyText which discards escape characters --- apps/openmw/mwgui/class.cpp | 2 +- apps/openmw/mwgui/class.hpp | 4 ++-- apps/openmw/mwgui/textinput.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index c33e54d6b4..6c46f21763 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -466,7 +466,7 @@ namespace MWGui std::string CreateClassDialog::getName() const { - return mEditName->getOnlyText(); + return mEditName->getCaption(); } std::string CreateClassDialog::getDescription() const diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 15fc89658f..e74370a4cd 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -228,8 +228,8 @@ namespace MWGui DescriptionDialog(); ~DescriptionDialog(); - std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } - void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } + std::string getTextInput() const { return mTextEdit->getCaption(); } + void setTextInput(const std::string &text) { mTextEdit->setCaption(text); } protected: void onOkClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/textinput.hpp b/apps/openmw/mwgui/textinput.hpp index 1f53263ecd..1ed80fc1e9 100644 --- a/apps/openmw/mwgui/textinput.hpp +++ b/apps/openmw/mwgui/textinput.hpp @@ -15,8 +15,8 @@ namespace MWGui public: TextInputDialog(); - std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } - void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } + std::string getTextInput() const { return mTextEdit->getCaption(); } + void setTextInput(const std::string &text) { mTextEdit->setCaption(text); } void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); From 7265b427fe27cca0b3e567e4f892ba1605cae96a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 21:21:18 +0100 Subject: [PATCH 044/251] Bug #1013: Rewrote fall height detection --- apps/openmw/mwmechanics/character.cpp | 18 ++++++------------ apps/openmw/mwmechanics/character.hpp | 3 --- apps/openmw/mwmechanics/creaturestats.cpp | 15 ++++++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 8 ++++++++ apps/openmw/mwworld/physicssystem.cpp | 10 ++++++++++ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 3038faf310..fdf09d2f85 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -350,7 +350,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mSkipAnim(false) , mSecondsOfRunning(0) , mSecondsOfSwimming(0) - , mFallHeight(0) { if(!mAnimation) return; @@ -800,10 +799,7 @@ void CharacterController::update(float duration) } if(sneak || inwater || flying) - { vec.z = 0.0f; - mFallHeight = mPtr.getRefData().getPosition().pos[2]; - } if(!onground && !flying && !inwater) { @@ -812,11 +808,7 @@ void CharacterController::update(float duration) if (world->isSlowFalling(mPtr)) { // SlowFalling spell effect is active, do not keep previous fall height - mFallHeight = mPtr.getRefData().getPosition().pos[2]; - } - else - { - mFallHeight = std::max(mFallHeight, mPtr.getRefData().getPosition().pos[2]); + cls.getCreatureStats(mPtr).land(); } const MWWorld::Store &gmst = world->getStore().get(); @@ -872,7 +864,8 @@ void CharacterController::update(float duration) mJumpState = JumpState_Landing; vec.z = 0.0f; - float healthLost = cls.getFallDamage(mPtr, mFallHeight - mPtr.getRefData().getPosition().pos[2]); + float height = cls.getCreatureStats(mPtr).land(); + float healthLost = cls.getFallDamage(mPtr, height); if (healthLost > 0.0f) { const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm(); @@ -893,8 +886,6 @@ void CharacterController::update(float duration) //TODO: actor falls over } } - - mFallHeight = mPtr.getRefData().getPosition().pos[2]; } else { @@ -933,6 +924,9 @@ void CharacterController::update(float duration) } } + if (onground || inwater || flying) + cls.getCreatureStats(mPtr).land(); + if(movestate != CharState_None) clearAnimQueue(); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 9e07fca7d4..817fa2fd51 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -156,9 +156,6 @@ class CharacterController float mSecondsOfSwimming; float mSecondsOfRunning; - // used for acrobatics progress and fall damages - float mFallHeight; - std::string mAttackType; // slash, chop or thrust void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 345b5a1563..bfbd32ff01 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -14,7 +14,8 @@ namespace MWMechanics mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), - mIsWerewolf(false) + mIsWerewolf(false), + mFallHeight(0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -356,4 +357,16 @@ namespace MWMechanics { mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp(); } + + void CreatureStats::addToFallHeight(float height) + { + mFallHeight += height; + } + + float CreatureStats::land() + { + float height = mFallHeight; + mFallHeight = 0; + return height; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index f28f50fc67..5fc3a7ec6c 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -36,6 +36,8 @@ namespace MWMechanics bool mHostile; bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + float mFallHeight; + int mAttackType; std::string mLastHitObject; // The last object to hit this actor @@ -49,6 +51,12 @@ namespace MWMechanics public: CreatureStats(); + void addToFallHeight(float height); + + /// Reset the fall height + /// @return total fall height + float land(); + bool canUsePower (const std::string& power) const; void usePower (const std::string& power); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 433fe08925..451e093aee 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -18,6 +18,8 @@ #include "../mwbase/world.hpp" // FIXME #include "../mwbase/environment.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include #include "../mwworld/esmstore.hpp" @@ -573,9 +575,17 @@ namespace MWWorld if(cell->hasWater()) waterlevel = cell->mWater; + float oldHeight = iter->first.getRefData().getPosition().pos[2]; + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), waterlevel, mEngine); + + float heightDiff = newpos.z - oldHeight; + + if (heightDiff < 0) + iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff); + mMovementResults.push_back(std::make_pair(iter->first, newpos)); } From 85ec80100ce607e0b510b2a452716fa4906298c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 22:00:16 +0100 Subject: [PATCH 045/251] Bug #1005: Hide torches/shields during spellcasting and hand-to-hand combat --- apps/openmw/mwmechanics/character.cpp | 7 ++++--- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 30 ++++++++++----------------- apps/openmw/mwrender/npcanimation.hpp | 4 ++-- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fdf09d2f85..87a7875f5d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -425,10 +425,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { forcestateupdate = true; - // Shields shouldn't be visible during spellcasting + // Shields/torches shouldn't be visible during spellcasting or hand-to-hand // There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop", // but they are also present in weapon drawing animation. - mAnimation->showShield(weaptype != WeapType_Spell); + mAnimation->showCarriedLeft(weaptype != WeapType_Spell && weaptype != WeapType_HandToHand); std::string weapgroup; if(weaptype == WeapType_None) @@ -719,7 +719,8 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() + && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { if(!mAnimation->isPlaying("torch")) mAnimation->play("torch", Priority_Torch, diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 67d8baa3f9..72d1c100ef 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -279,7 +279,7 @@ public: virtual Ogre::Vector3 runAnimation(float duration); virtual void showWeapons(bool showWeapon); - virtual void showShield(bool show) {} + virtual void showCarriedLeft(bool show) {} void enableLights(bool enable); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 8d2f1c7da0..ddbdde83a6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -117,7 +117,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mListenerDisabled(disableListener), mViewMode(viewMode), mShowWeapons(false), - mShowShield(true), + mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f) { @@ -319,7 +319,7 @@ void NpcAnimation::updateParts() } showWeapons(mShowWeapons); - showShield(mShowShield); + showCarriedLeft(mShowCarriedLeft); // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination static std::map< std::pair,std::vector > sRaceMapping; @@ -654,32 +654,24 @@ void NpcAnimation::showWeapons(bool showWeapon) mAlpha = 1.f; } -void NpcAnimation::showShield(bool show) +void NpcAnimation::showCarriedLeft(bool show) { - mShowShield = show; + mShowCarriedLeft = show; MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) + if(show && iter != inv.end()) { - // ... Except for lights, which are still shown during spellcasting since they - // have their own (one-handed) casting animations - show = true; - } - if(show && shield != inv.end()) - { - Ogre::Vector3 glowColor = getEnchantmentColor(*shield); - std::string mesh = MWWorld::Class::get(*shield).getModel(*shield); + Ogre::Vector3 glowColor = getEnchantmentColor(*iter); + std::string mesh = MWWorld::Class::get(*iter).getModel(*iter); addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); + mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor); - if (shield->getTypeName() == typeid(ESM::Light).name()) - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get()->mBase); + if (iter->getTypeName() == typeid(ESM::Light).name()) + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get()->mBase); } else - { removeIndividualPart(ESM::PRT_Shield); - } } void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index b6ed3f8575..28bb81dddf 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -53,7 +53,7 @@ private: std::string mHairModel; ViewMode mViewMode; bool mShowWeapons; - bool mShowShield; + bool mShowCarriedLeft; int mVisibilityFlags; @@ -100,7 +100,7 @@ public: virtual Ogre::Vector3 runAnimation(float timepassed); virtual void showWeapons(bool showWeapon); - virtual void showShield(bool showShield); + virtual void showCarriedLeft(bool showa); void setViewMode(ViewMode viewMode); From 596c9b80a99c0d8e95bde93129145be466e3785b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 27 Dec 2013 22:38:30 +0100 Subject: [PATCH 046/251] Check if threads are joinable before joining (issue with boost 1.52) --- apps/openmw/mwrender/videoplayer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index ee2b80f731..88bc6d8ac2 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -989,9 +989,12 @@ void VideoState::deinit() this->audioq.cond.notify_one(); this->videoq.cond.notify_one(); - this->parse_thread.join(); - this->video_thread.join(); - this->refresh_thread.join(); + if (this->parse_thread.joinable()) + this->parse_thread.join(); + if (this->video_thread.joinable()) + this->video_thread.join(); + if (this->refresh_thread.joinable()) + this->refresh_thread.join(); if(this->audio_st) avcodec_close((*this->audio_st)->codec); From 6d47d710a013fee4a38764358f6d489c43430f1c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Dec 2013 00:51:09 +0100 Subject: [PATCH 047/251] Reimplement NiGeomMorpherController using Ogre's pose animation system --- components/nifogre/mesh.cpp | 52 ++++++++++++-------- components/nifogre/ogrenifloader.cpp | 72 ++++++---------------------- 2 files changed, 47 insertions(+), 77 deletions(-) diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index ef4fbbe8df..80e377a49f 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -116,21 +116,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; bool vertShadowBuffer = false; - bool geomMorpherController = false; - if(!shape->controller.empty()) - { - Nif::ControllerPtr ctrl = shape->controller; - do { - if(ctrl->recType == Nif::RC_NiGeomMorpherController) - { - vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; - vertShadowBuffer = true; - geomMorpherController = true; - break; - } - } while(!(ctrl=ctrl->next).empty()); - } - if(skin != NULL) { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; @@ -350,10 +335,39 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); } - // Create a dummy vertex animation track if there's a geom morpher controller - // This is required to make Ogre create the buffers we will use for software vertex animation - if (srcVerts.size() && geomMorpherController) - mesh->createAnimation("dummy", 0)->createVertexTrack(1, sub->vertexData, Ogre::VAT_MORPH); + + if(!shape->controller.empty()) + { + Nif::ControllerPtr ctrl = shape->controller; + do { + // Load GeomMorpherController into an Ogre::Pose and Animation + if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = + static_cast(ctrl.getPtr()); + + const std::vector& morphs = geom->data.getPtr()->mMorphs; + // Note we are not interested in morph 0, which just contains the original vertices + for (unsigned int i = 1; i < morphs.size(); ++i) + { + Ogre::Pose* pose = mesh->createPose(i); + const Nif::NiMorphData::MorphData& data = morphs[i]; + for (unsigned int v = 0; v < data.mVertices.size(); ++v) + pose->addVertex(v, data.mVertices[v]); + + Ogre::String animationID = Ogre::StringConverter::toString(ctrl->recIndex) + + "_" + Ogre::StringConverter::toString(i); + Ogre::VertexAnimationTrack* track = + mesh->createAnimation(animationID, 0) + ->createVertexTrack(1, Ogre::VAT_POSE); + Ogre::VertexPoseKeyFrame* keyframe = track->createVertexPoseKeyFrame(0); + keyframe->addPoseReference(i-1, 1); + } + + break; + } + } while(!(ctrl=ctrl->next).empty()); + } } diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 9fa9136862..1e3598b336 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -534,18 +534,18 @@ public: class Value : public Ogre::ControllerValue, public ValueInterpolator { private: - Ogre::SubEntity *mSubEntity; + Ogre::Entity *mEntity; std::vector mMorphs; - std::vector mValues; + size_t mControllerIndex; std::vector mVertices; public: - Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) - : mSubEntity(subent) + Value(Ogre::Entity *ent, const Nif::NiMorphData *data, size_t controllerIndex) + : mEntity(ent) , mMorphs(data->mMorphs) + , mControllerIndex(controllerIndex) { - mValues.resize(mMorphs.size()-1, 0.f); } virtual Ogre::Real getValue() const @@ -558,21 +558,7 @@ public: { if (mMorphs.size() <= 1) return; - -#if OGRE_DOUBLE_PRECISION -#error "This code needs to be rewritten for double precision mode" -#endif - - Ogre::VertexData* data = mSubEntity->_getSoftwareVertexAnimVertexData(); - - const Ogre::VertexElement* posElem = - data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); - - Ogre::HardwareVertexBufferSharedPtr vbuf = - data->vertexBufferBinding->getBuffer(posElem->getSource()); - - bool needToUpdate = false; - int i=0; + int i = 1; for (std::vector::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) { float val = 0; @@ -580,37 +566,13 @@ public: val = interpKey(it->mData.mKeys, time); val = std::max(0.f, std::min(1.f, val)); - if (val != mValues[i]) - needToUpdate = true; - mValues[i] = val; + Ogre::String animationID = Ogre::StringConverter::toString(mControllerIndex) + + "_" + Ogre::StringConverter::toString(i); + + Ogre::AnimationState* state = mEntity->getAnimationState(animationID); + state->setEnabled(val > 0); + state->setWeight(val); } - if (!needToUpdate) - return; - - // The first morph key always contains the original positions - mVertices = mMorphs[0].mVertices; - - i = 0; - for (std::vector::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) - { - float val = mValues[i]; - - if (it->mVertices.size() != mMorphs[0].mVertices.size()) - continue; - - if (val != 0) - { - for (unsigned int v=0; vmVertices[v] * val; - } - } - - if (mVertices.size() * sizeof(float)*3 != vbuf->getSizeInBytes()) - return; - - vbuf->writeData(0, vbuf->getSizeInBytes(), &mVertices[0]); - - mSubEntity->_markBuffersUsedForAnimation(); } }; @@ -629,13 +591,6 @@ class NIFObjectLoader std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl; } - static void fail(const std::string &msg) - { - std::cerr << "NIFObjectLoader: Fail: "<< msg << std::endl; - abort(); - } - - static void createEntity(const std::string &name, const std::string &group, Ogre::SceneManager *sceneMgr, ObjectScenePtr scene, const Nif::Node *node, int flags, int animflags) @@ -693,7 +648,8 @@ class NIFObjectLoader Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(entity->getSubEntity(0), geom->data.getPtr())); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value( + entity, geom->data.getPtr(), geom->recIndex)); GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); From 9b73d231392b1e8a867e9f5627d9b6054f82b253 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 28 Dec 2013 13:47:01 +0100 Subject: [PATCH 048/251] Fix warning about uninitialized variable inside stream.peak(): MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit openmw/mwgui/bookpage.cpp:394:13: warning: ‘*((void*)& stream +24)’ may be used uninitialized in this function [-Wmaybe-uninitialized] Signed-off-by: Lukasz Gromanowski --- components/misc/utf8stream.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp index 19a0688b26..5e9dde2514 100644 --- a/components/misc/utf8stream.hpp +++ b/components/misc/utf8stream.hpp @@ -14,12 +14,12 @@ public: static UnicodeChar sBadChar () { return UnicodeChar (0xFFFFFFFF); } Utf8Stream (Point begin, Point end) : - cur (begin), nxt (begin), end (end) + cur (begin), nxt (begin), end (end), val(Utf8Stream::sBadChar()) { } Utf8Stream (std::pair range) : - cur (range.first), nxt (range.first), end (range.second) + cur (range.first), nxt (range.first), end (range.second), val(Utf8Stream::sBadChar()) { } From 100edda8c086f72599884bafd591d1bb260de76f Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 28 Dec 2013 16:15:34 +0100 Subject: [PATCH 049/251] Fixes #417: Apply weather instantly when teleporting Change speed of weather transition from blight to other (twice fast as normal) and from other to blight (four times faster than normal). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/weather.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 8b05d2256a..5641d34930 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -195,7 +195,22 @@ void WeatherManager::setWeather(const String& weather, bool instant) } mNextWeather = weather; - mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600; + + /** + * Issue #417: + * + * Change speed of weather transition from blight to other (twice fast as normal) + * and from other to blight (four times faster than normal) + */ + mRemainingTransitionTime = (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600.f); + if (mCurrentWeather == "blight" && mNextWeather != "") + { + mRemainingTransitionTime *= 0.25f; + } + else if (mNextWeather == "blight") + { + mRemainingTransitionTime *= 0.5f; + } } mFirstUpdate = false; } From 27b45dd3cde2be061459652b187c4f08e57a3524 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Dec 2013 16:34:32 +0100 Subject: [PATCH 050/251] Remove unused input --- files/materials/terrain.shader | 1 - 1 file changed, 1 deletion(-) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 3b80c3aecd..86eef36ffa 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -78,7 +78,6 @@ #endif shVertexInput(float2, uv0) - shVertexInput(float2, uv1) // lodDelta, lodThreshold #if LIGHTING shNormalInput(float4) From 2a8ab932efcd6c05887146561a44f6338799e21c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 28 Dec 2013 17:19:35 +0100 Subject: [PATCH 051/251] Bug #951: Only recalculate derived stats when attributes change --- apps/openmw/mwclass/creature.cpp | 16 +++---- apps/openmw/mwclass/npc.cpp | 23 +++++----- apps/openmw/mwgui/levelupdialog.cpp | 3 +- apps/openmw/mwmechanics/actors.cpp | 3 +- apps/openmw/mwmechanics/creaturestats.cpp | 42 +++++++++++++++---- apps/openmw/mwmechanics/creaturestats.hpp | 9 +++- .../mwmechanics/mechanicsmanagerimp.cpp | 21 +++++----- apps/openmw/mwscript/statsextensions.cpp | 23 +++++----- 8 files changed, 85 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2e7f00dd33..b225441aaf 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -68,14 +68,14 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); // creature stats - data->mCreatureStats.getAttribute(0).set (ref->mBase->mData.mStrength); - data->mCreatureStats.getAttribute(1).set (ref->mBase->mData.mIntelligence); - data->mCreatureStats.getAttribute(2).set (ref->mBase->mData.mWillpower); - data->mCreatureStats.getAttribute(3).set (ref->mBase->mData.mAgility); - data->mCreatureStats.getAttribute(4).set (ref->mBase->mData.mSpeed); - data->mCreatureStats.getAttribute(5).set (ref->mBase->mData.mEndurance); - data->mCreatureStats.getAttribute(6).set (ref->mBase->mData.mPersonality); - data->mCreatureStats.getAttribute(7).set (ref->mBase->mData.mLuck); + data->mCreatureStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mData.mStrength); + data->mCreatureStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mData.mIntelligence); + data->mCreatureStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mData.mWillpower); + data->mCreatureStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mData.mAgility); + data->mCreatureStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mData.mSpeed); + data->mCreatureStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mData.mEndurance); + data->mCreatureStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mData.mPersonality); + data->mCreatureStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mData.mLuck); data->mCreatureStats.setHealth (ref->mBase->mData.mHealth); data->mCreatureStats.setMagicka (ref->mBase->mData.mMana); data->mCreatureStats.setFatigue (ref->mBase->mData.mFatigue); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e8b9bfaf21..4187aa8c1f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -66,7 +66,7 @@ namespace for (int i=0; imData.mAttributeValues[i]; - creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); } // class bonus @@ -78,7 +78,7 @@ namespace int attribute = class_->mData.mAttribute[i]; if (attribute>=0 && attribute<8) { - creatureStats.getAttribute(attribute).setBase ( + creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); } } @@ -109,7 +109,7 @@ namespace } modifierSum += add; } - creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase() + creatureStats.setAttribute(attribute, std::min(creatureStats.getAttribute(attribute).getBase() + static_cast((level-1) * modifierSum+0.5), 100) ); } @@ -274,14 +274,15 @@ namespace MWClass for (unsigned int i=0; i< ESM::Skill::Length; ++i) data->mNpcStats.getSkill (i).setBase (ref->mBase->mNpdt52.mSkills[i]); - data->mNpcStats.getAttribute(0).set (ref->mBase->mNpdt52.mStrength); - data->mNpcStats.getAttribute(1).set (ref->mBase->mNpdt52.mIntelligence); - data->mNpcStats.getAttribute(2).set (ref->mBase->mNpdt52.mWillpower); - data->mNpcStats.getAttribute(3).set (ref->mBase->mNpdt52.mAgility); - data->mNpcStats.getAttribute(4).set (ref->mBase->mNpdt52.mSpeed); - data->mNpcStats.getAttribute(5).set (ref->mBase->mNpdt52.mEndurance); - data->mNpcStats.getAttribute(6).set (ref->mBase->mNpdt52.mPersonality); - data->mNpcStats.getAttribute(7).set (ref->mBase->mNpdt52.mLuck); + data->mNpcStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mNpdt52.mStrength); + data->mNpcStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mNpdt52.mIntelligence); + data->mNpcStats.setAttribute(ESM::Attribute::Willpower, ref->mBase->mNpdt52.mWillpower); + data->mNpcStats.setAttribute(ESM::Attribute::Agility, ref->mBase->mNpdt52.mAgility); + data->mNpcStats.setAttribute(ESM::Attribute::Speed, ref->mBase->mNpdt52.mSpeed); + data->mNpcStats.setAttribute(ESM::Attribute::Endurance, ref->mBase->mNpdt52.mEndurance); + data->mNpcStats.setAttribute(ESM::Attribute::Personality, ref->mBase->mNpdt52.mPersonality); + data->mNpcStats.setAttribute(ESM::Attribute::Luck, ref->mBase->mNpdt52.mLuck); + data->mNpcStats.setHealth (ref->mBase->mNpdt52.mHealth); data->mNpcStats.setMagicka (ref->mBase->mNpdt52.mMana); data->mNpcStats.setFatigue (ref->mBase->mNpdt52.mFatigue); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 7c0191d495..a772b3a15a 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -166,11 +166,12 @@ namespace MWGui // increase attributes for (int i=0; i<3; ++i) { - MWMechanics::Stat& attribute = creatureStats.getAttribute(mSpentAttributes[i]); + MWMechanics::Stat attribute = creatureStats.getAttribute(mSpentAttributes[i]); attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); if (attribute.getBase() >= 100) attribute.setBase(100); + creatureStats.setAttribute(mSpentAttributes[i], attribute); } creatureStats.levelUp(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 22a641b34b..1990292a78 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -54,7 +54,8 @@ namespace MWMechanics { // magic effects adjustMagicEffects (ptr); - calculateDynamicStats (ptr); + if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats()) + calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr, duration); if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index bfbd32ff01..95bd405b1d 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0) + mFallHeight(0), mRecalcDynamicStats(false) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -128,14 +128,6 @@ namespace MWMechanics return mAiSettings[index]; } - Stat &CreatureStats::getAttribute(int index) - { - if (index < 0 || index > 7) { - throw std::runtime_error("attribute index is out of range"); - } - return (!mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]); - } - const DynamicStat &CreatureStats::getDynamic(int index) const { if (index < 0 || index > 2) { @@ -164,11 +156,29 @@ namespace MWMechanics return mMagicEffects; } + void CreatureStats::setAttribute(int index, int base) + { + MWMechanics::Stat current = getAttribute(index); + current.setBase(base); + setAttribute(index, current); + } + void CreatureStats::setAttribute(int index, const Stat &value) { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } + + const Stat& currentValue = !mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]; + + if (value.getModified() != currentValue.getModified()) + { + if (index != ESM::Attribute::Luck + && index != ESM::Attribute::Personality + && index != ESM::Attribute::Speed) + mRecalcDynamicStats = true; + } + if(!mIsWerewolf) mAttributes[index] = value; else @@ -218,6 +228,10 @@ namespace MWMechanics void CreatureStats::setMagicEffects(const MagicEffects &effects) { + if (effects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude + != mMagicEffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude) + mRecalcDynamicStats = true; + mMagicEffects = effects; } @@ -369,4 +383,14 @@ namespace MWMechanics mFallHeight = 0; return height; } + + bool CreatureStats::needToRecalcDynamicStats() + { + if (mRecalcDynamicStats) + { + mRecalcDynamicStats = false; + return true; + } + return false; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 5fc3a7ec6c..4d9be01324 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -42,6 +42,9 @@ namespace MWMechanics std::string mLastHitObject; // The last object to hit this actor + // Do we need to recalculate stats derived from attributes or other factors? + bool mRecalcDynamicStats; + std::map mUsedPowers; protected: @@ -51,6 +54,8 @@ namespace MWMechanics public: CreatureStats(); + bool needToRecalcDynamicStats(); + void addToFallHeight(float height); /// Reset the fall height @@ -83,8 +88,6 @@ namespace MWMechanics int getAiSetting (int index) const; ///< 0: hello, 1 fight, 2 flee, 3 alarm - Stat & getAttribute(int index); - Spells & getSpells(); ActiveSpells & getActiveSpells(); @@ -92,6 +95,8 @@ namespace MWMechanics MagicEffects & getMagicEffects(); void setAttribute(int index, const Stat &value); + // Shortcut to set only the base + void setAttribute(int index, int base); void setHealth(const DynamicStat &value); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1316baaeb4..c084c86e5e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -31,15 +31,14 @@ namespace MWMechanics for (int i=0; i<27; ++i) npcStats.getSkill (i).setBase (player->mNpdt52.mSkills[i]); - creatureStats.getAttribute(0).setBase (player->mNpdt52.mStrength); - creatureStats.getAttribute(1).setBase (player->mNpdt52.mIntelligence); - creatureStats.getAttribute(2).setBase (player->mNpdt52.mWillpower); - creatureStats.getAttribute(3).setBase (player->mNpdt52.mAgility); - creatureStats.getAttribute(4).setBase (player->mNpdt52.mSpeed); - creatureStats.getAttribute(5).setBase (player->mNpdt52.mEndurance); - creatureStats.getAttribute(6).setBase (player->mNpdt52.mPersonality); - creatureStats.getAttribute(7).setBase (player->mNpdt52.mLuck); - + creatureStats.setAttribute(ESM::Attribute::Strength, player->mNpdt52.mStrength); + creatureStats.setAttribute(ESM::Attribute::Intelligence, player->mNpdt52.mIntelligence); + creatureStats.setAttribute(ESM::Attribute::Willpower, player->mNpdt52.mWillpower); + creatureStats.setAttribute(ESM::Attribute::Agility, player->mNpdt52.mAgility); + creatureStats.setAttribute(ESM::Attribute::Speed, player->mNpdt52.mSpeed); + creatureStats.setAttribute(ESM::Attribute::Endurance, player->mNpdt52.mEndurance); + creatureStats.setAttribute(ESM::Attribute::Personality, player->mNpdt52.mPersonality); + creatureStats.setAttribute(ESM::Attribute::Luck, player->mNpdt52.mLuck); const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -55,7 +54,7 @@ namespace MWMechanics { const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; - creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale); + creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); } for (int i=0; i<27; ++i) @@ -106,7 +105,7 @@ namespace MWMechanics int attribute = class_->mData.mAttribute[i]; if (attribute>=0 && attribute<8) { - creatureStats.getAttribute(attribute).setBase ( + creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 6cd483ae12..dc6b1d4a71 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -124,10 +124,9 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get(ptr) - .getCreatureStats(ptr) - .getAttribute(mIndex) - .setModified (value, 0); + MWMechanics::Stat attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); + attribute.setModified (value, 0); + ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -147,16 +146,16 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - value += - MWWorld::Class::get(ptr) - .getCreatureStats(ptr) - .getAttribute(mIndex) - .getModified(); - - MWWorld::Class::get(ptr) + MWMechanics::Stat attribute = MWWorld::Class::get(ptr) .getCreatureStats(ptr) - .getAttribute(mIndex) + .getAttribute(mIndex); + + value += + attribute.getModified(); + + attribute .setModified (value, 0, 100); + ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; From b50151cb38f3a4fa23ba887139ac878aaee932c3 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 28 Dec 2013 18:16:01 +0100 Subject: [PATCH 052/251] Quick build fix for windows --- components/files/windowspath.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 5bb8a6a021..ea1ca56d3c 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -69,12 +69,12 @@ boost::filesystem::path WindowsPath::getLocalPath() const boost::filesystem::path WindowsPath::getGlobalDataPath() const { - return getGlobalPath(); + return getGlobalConfigPath(); } boost::filesystem::path WindowsPath::getCachePath() const { - return getUserPath() / "cache"; + return getUserConfigPath() / "cache"; } boost::filesystem::path WindowsPath::getInstallPath() const From 17ff8165d24ff88d2808b76748c96267bcc104e8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 29 Dec 2013 00:36:36 +0100 Subject: [PATCH 053/251] Closes #1065: Don't apply fall damage when landing in water --- apps/openmw/mwmechanics/character.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 87a7875f5d..2b93225e41 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -802,6 +802,9 @@ void CharacterController::update(float duration) if(sneak || inwater || flying) vec.z = 0.0f; + if (inwater || flying) + cls.getCreatureStats(mPtr).land(); + if(!onground && !flying && !inwater) { // In the air (either getting up —ascending part of jump— or falling). @@ -925,7 +928,7 @@ void CharacterController::update(float duration) } } - if (onground || inwater || flying) + if (onground) cls.getCreatureStats(mPtr).land(); if(movestate != CharState_None) From 04f9f7af56a2863d05c9ac33d3649b5b7298e4d8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 29 Dec 2013 00:58:48 +0100 Subject: [PATCH 054/251] Closes #990: Add option to unlock mouse cursor when in any menu --- apps/openmw/mwgui/settingswindow.cpp | 6 +++++- apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++++- apps/openmw/mwinput/inputmanagerimp.hpp | 2 ++ files/mygui/openmw_settings_window.layout | 6 ++++++ files/settings-default.cfg | 2 ++ 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 1969c56518..c99e2d0de3 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -92,6 +92,7 @@ namespace MWGui { getWidget(mOkButton, "OkButton"); getWidget(mBestAttackButton, "BestAttackButton"); + getWidget(mGrabCursorButton, "GrabCursorButton"); getWidget(mSubtitlesButton, "SubtitlesButton"); getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mResolutionList, "ResolutionList"); @@ -133,6 +134,7 @@ namespace MWGui mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mBestAttackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mGrabCursorButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -201,6 +203,7 @@ namespace MWGui mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}"); mBestAttackButton->setCaptionWithReplacing(Settings::Manager::getBool("best attack", "Game") ? "#{sOn}" : "#{sOff}"); + mGrabCursorButton->setCaptionWithReplacing(Settings::Manager::getBool("grab cursor", "Input") ? "#{sOn}" : "#{sOff}"); float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin); mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); @@ -393,7 +396,8 @@ namespace MWGui Settings::Manager::setBool("subtitles", "GUI", newState); else if (_sender == mBestAttackButton) Settings::Manager::setBool("best attack", "Game", newState); - + else if (_sender == mGrabCursorButton) + Settings::Manager::setBool("grab cursor", "Input", newState); apply(); } } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index c81a86ab0e..6b9ce414b8 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -33,6 +33,7 @@ namespace MWGui MyGUI::Button* mSubtitlesButton; MyGUI::Button* mCrosshairButton; MyGUI::Button* mBestAttackButton; + MyGUI::Button* mGrabCursorButton; // graphics MyGUI::ListBox* mResolutionList; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4713d92e1a..ffb2af81e0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -103,6 +103,7 @@ namespace MWInput , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) , mOverencumberedMessageDelay(0.f) @@ -287,7 +288,7 @@ namespace MWInput mInputManager->setMouseRelative(is_relative); //we let the mouse escape in the main menu - mInputManager->setGrabPointer(grab); + mInputManager->setGrabPointer(grab && (mGrabCursor || is_relative)); //we switched to non-relative mode, move our cursor to where the in-game //cursor is @@ -430,6 +431,9 @@ namespace MWInput if (it->first == "Input" && it->second == "ui sensitivity") mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); + if (it->first == "Input" && it->second == "grab cursor") + mGrabCursor = Settings::Manager::getBool("grab cursor", "Input"); + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 8efa6cfc5a..d4693ff28c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -138,6 +138,8 @@ namespace MWInput bool mDragDrop; + bool mGrabCursor; + bool mInvertY; float mCameraSensitivity; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index e6002b51de..61103963db 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -65,6 +65,12 @@ + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 94bdd8c9df..2ca59159bb 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -156,6 +156,8 @@ voice volume = 1.0 [Input] +grab cursor = true + invert y axis = false camera sensitivity = 1.0 From c0dba2834b6fa566e990dcd0e575e1b43fd93827 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 29 Dec 2013 01:20:57 +0100 Subject: [PATCH 055/251] Closes #855: Don't try to look up bone if there's no skeleton --- components/nifogre/ogrenifloader.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 1e3598b336..345e7d6fd4 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1000,8 +1000,11 @@ class NIFObjectLoader { const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); - extractTextKeys(tk, scene->mTextKeys[trgtid]); + if (scene->mSkelBase) + { + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); + extractTextKeys(tk, scene->mTextKeys[trgtid]); + } } else if(e->recType == Nif::RC_NiStringExtraData) { From faf8011c48d0e5bf553ef3de08c2a474e95941e3 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Dec 2013 12:47:44 +0100 Subject: [PATCH 056/251] Fixes #417: Apply weather instantly when teleporting Removed changing speed of weather transition introduced in previous commit. Instead try to detect player "teleporting" (ie. coc), and then switch instantly to the next weather type. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/weather.cpp | 59 +++++++++++++++++++++++--------- apps/openmw/mwworld/weather.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 20 ++++++++++- apps/openmw/mwworld/worldimp.hpp | 2 +- 4 files changed, 64 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 5641d34930..4a8def1847 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -195,22 +195,7 @@ void WeatherManager::setWeather(const String& weather, bool instant) } mNextWeather = weather; - - /** - * Issue #417: - * - * Change speed of weather transition from blight to other (twice fast as normal) - * and from other to blight (four times faster than normal) - */ - mRemainingTransitionTime = (mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600.f); - if (mCurrentWeather == "blight" && mNextWeather != "") - { - mRemainingTransitionTime *= 0.25f; - } - else if (mNextWeather == "blight") - { - mRemainingTransitionTime *= 0.5f; - } + mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta * 24.f * 3600.f; } mFirstUpdate = false; } @@ -722,3 +707,45 @@ float WeatherManager::getWindSpeed() const { return mWindSpeed; } + +void WeatherManager::switchToNextWeather(bool instantly) +{ + const bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); + if (!exterior) + { + mRendering->sunDisable(false); + mRendering->skyDisable(); + mRendering->getSkyManager()->setLightningStrength(0.f); + stopSounds(true); + return; + } + + // Exterior + std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); + + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) + { + mCurrentRegion = regionstr; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; + + std::string weatherType = "clear"; + + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + { + weatherType = mRegionOverrides[regionstr]; + } + else + { + // get weather probabilities for the current region + const ESM::Region *region = + MWBase::Environment::get().getWorld()->getStore().get().search (regionstr); + + if (region != 0) + { + weatherType = nextWeather(region); + } + } + + setWeather(weatherType, instantly); + } +} diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 80cbe0418e..2111cac39a 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -128,6 +128,7 @@ namespace MWWorld * @param ID of the weather setting to shift to */ void changeWeather(const std::string& region, const unsigned int id); + void switchToNextWeather(bool instantly = true); /** * Per-frame update diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 448211bc2e..9ce4f47d28 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1287,7 +1287,7 @@ namespace MWWorld mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); } - mWeatherManager->update (duration); + updateWeather(duration); mWorldScene->update (duration, paused); @@ -2244,4 +2244,22 @@ namespace MWWorld actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } + + void World::updateWeather(float duration) + { + static const float TELEPORTATION_STEP_THRESHOLD = 256.f; + static ESM::Position lastPlayerPos; + ESM::Position currentPos = mPlayer->getPlayer().getRefData().getPosition(); + + if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= TELEPORTATION_STEP_THRESHOLD + || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= TELEPORTATION_STEP_THRESHOLD) + { + lastPlayerPos = currentPos; + mWeatherManager->switchToNextWeather(true); + } + else + { + mWeatherManager->update (duration); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5a51cb773c..346c64d82d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -106,7 +106,7 @@ namespace MWWorld }; std::map mProjectiles; - + void updateWeather(float duration); int getDaysPerMonth (int month) const; void rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust); From c65f018760539e9a990782ad10fbf6c9f81908f3 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Dec 2013 15:09:49 +0100 Subject: [PATCH 057/251] Fixes #417: Apply weather instantly when teleporting Corrected constant name. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/worldimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9ce4f47d28..18dc77d3aa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2247,12 +2247,12 @@ namespace MWWorld void World::updateWeather(float duration) { - static const float TELEPORTATION_STEP_THRESHOLD = 256.f; + static const float teleportationStepTreshold = 256.f; static ESM::Position lastPlayerPos; ESM::Position currentPos = mPlayer->getPlayer().getRefData().getPosition(); - if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= TELEPORTATION_STEP_THRESHOLD - || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= TELEPORTATION_STEP_THRESHOLD) + if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= teleportationStepTreshold + || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= teleportationStepTreshold) { lastPlayerPos = currentPos; mWeatherManager->switchToNextWeather(true); From 60fb75b03ab54494b52d0b07a17836d97e0d7c8f Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Dec 2013 21:58:55 +0100 Subject: [PATCH 058/251] Fixed valgrind warning about uninitialized variable: ==16814== Conditional jump or move depends on uninitialised value(s) ==16814== at 0xA945B8: Terrain::QuadTreeNode::update(Ogre::Vector3 const&, Loading::Listener*) (quadtreenode.cpp:269) ==16814== by 0xA94A77: Terrain::QuadTreeNode::update(Ogre::Vector3 const&, Loading::Listener*) (quadtreenode.cpp:354) ==16814== by 0xA77541: Terrain::World::update(Ogre::Vector3 const&) (world.cpp:159) ==16814== by 0x6EBA17: MWRender::RenderingManager::requestMap(MWWorld::CellStore*) (renderingmanager.cpp:649) ==16814== by 0x8A25C4: MWWorld::Scene::loadCell(MWWorld::CellStore*, Loading::Listener*) (scene.cpp:157) ==16814== by 0x8A2CEA: MWWorld::Scene::changeCell(int, int, ESM::Position const&, bool) (scene.cpp:296) ==16814== by 0x8A2DE0: MWWorld::Scene::changeToExteriorCell(ESM::Position const&) (scene.cpp:440) ==16814== by 0x85AC17: MWWorld::World::changeToExteriorCell(ESM::Position const&) (worldimp.cpp:761) ==16814== by 0x927E38: OMW::Engine::prepareEngine(Settings::Manager&) (engine.cpp:436) ==16814== by 0x92843D: OMW::Engine::go() (engine.cpp:483) ==16814== by 0x6C6B3F: main (main.cpp:279) ==16814== Uninitialised value was created by a heap allocation ==16814== at 0x4C27CC2: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==16814== by 0xA93E60: Terrain::QuadTreeNode::createChild(Terrain::ChildDirection, float, Ogre::Vector2 const&) (quadtreenode.cpp:178) ==16814== by 0xA7733E: Terrain::World::buildQuadTree(Terrain::QuadTreeNode*) (world.cpp:139) ==16814== by 0xA76B18: Terrain::World::World(Loading::Listener*, Ogre::SceneManager*, Terrain::Storage*, int, bool, bool) (world.cpp:94) ==16814== by 0x6EC6EB: MWRender::RenderingManager::enableTerrain(bool) (renderingmanager.cpp:1013) ==16814== by 0x8A2A00: MWWorld::Scene::changeCell(int, int, ESM::Position const&, bool) (scene.cpp:206) ==16814== by 0x8A2DE0: MWWorld::Scene::changeToExteriorCell(ESM::Position const&) (scene.cpp:440) ==16814== by 0x85AC17: MWWorld::World::changeToExteriorCell(ESM::Position const&) (worldimp.cpp:761) ==16814== by 0x927E38: OMW::Engine::prepareEngine(Settings::Manager&) (engine.cpp:436) ==16814== by 0x92843D: OMW::Engine::go() (engine.cpp:483) ==16814== by 0x6C6B3F: main (main.cpp:279) Signed-off-by: Lukasz Gromanowski --- components/terrain/quadtreenode.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index e16ae55ddb..02225cb02d 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -140,17 +140,19 @@ namespace } QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) - : mSize(size) - , mCenter(center) - , mParent(parent) - , mDirection(dir) + : mMaterialGenerator(NULL) + , mIsActive(false) , mIsDummy(false) - , mSceneNode(NULL) - , mTerrain(terrain) - , mChunk(NULL) - , mMaterialGenerator(NULL) + , mSize(size) + , mLodLevel(Log2(mSize)) , mBounds(Ogre::AxisAlignedBox::BOX_NULL) , mWorldBounds(Ogre::AxisAlignedBox::BOX_NULL) + , mDirection(dir) + , mCenter(center) + , mSceneNode(NULL) + , mParent(parent) + , mTerrain(terrain) + , mChunk(NULL) { mBounds.setNull(); for (int i=0; i<4; ++i) @@ -168,8 +170,6 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const pos = mCenter - pos; mSceneNode->setPosition(Ogre::Vector3(pos.x*8192, pos.y*8192, 0)); - mLodLevel = Log2(mSize); - mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); } From 2f082ef7960b6d5f9cc7aeb322d93e1a59581c70 Mon Sep 17 00:00:00 2001 From: Scott Howard Date: Sun, 29 Dec 2013 16:25:49 -0500 Subject: [PATCH 059/251] remove unneeded libraries --- CMakeLists.txt | 2 +- apps/launcher/CMakeLists.txt | 5 ----- apps/opencs/CMakeLists.txt | 2 +- apps/openmw/CMakeLists.txt | 10 +++------- cmake/FindBullet.cmake | 5 +---- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6014dff6e..ee4a0246b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,7 +212,7 @@ if (HAVE_UNORDERED_MAP) endif () -set(BOOST_COMPONENTS system filesystem program_options thread date_time wave) +set(BOOST_COMPONENTS system filesystem program_options) IF(BOOST_STATIC) set(Boost_USE_STATIC_LIBS ON) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 18c555a249..e4638c31b4 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -137,8 +137,3 @@ if (BUILD_WITH_CODE_COVERAGE) target_link_libraries(omwlauncher gcov) endif() -# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream -if (UNIX AND NOT APPLE) -target_link_libraries(omwlauncher dl Xt) -endif() - diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 1197e20140..e2dffdbde4 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -143,7 +143,7 @@ if(WIN32) set(QT_USE_QTMAIN TRUE) endif(WIN32) -find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED) +find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED) include(${QT_USE_FILE}) qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e87da97c30..57773507fe 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -82,6 +82,8 @@ add_openmw_dir (mwbase ) # Main executable +set(BOOST_COMPONENTS system filesystem program_options thread wave) +find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) IF(OGRE_STATIC) ADD_DEFINITIONS(-DENABLE_PLUGIN_OctreeSceneManager -DENABLE_PLUGIN_ParticleFX -DENABLE_PLUGIN_GL) @@ -111,6 +113,7 @@ add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw ${OGRE_LIBRARIES} ${OGRE_STATIC_PLUGINS} + ${SHINY_LIBRARIES} ${Boost_LIBRARIES} ${OPENAL_LIBRARY} ${SOUND_INPUT_LIBRARY} @@ -118,7 +121,6 @@ target_link_libraries(openmw ${MYGUI_LIBRARIES} ${SDL2_LIBRARY} ${MYGUI_PLATFORM_LIBRARIES} - ${SHINY_LIBRARIES} "oics" "sdl4ogre" components @@ -137,12 +139,6 @@ if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) endif() -# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream -if (UNIX AND NOT APPLE) -target_link_libraries(openmw dl Xt) -endif() - - if(APPLE) find_library(COCOA_FRAMEWORK Cocoa) find_library(IOKIT_FRAMEWORK IOKit) diff --git a/cmake/FindBullet.cmake b/cmake/FindBullet.cmake index 97feddffeb..b75f3105a6 100644 --- a/cmake/FindBullet.cmake +++ b/cmake/FindBullet.cmake @@ -60,8 +60,6 @@ _FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision) _FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d) _FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath) _FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_debug LinearMath_d) -_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody) -_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletSoftBody_d) # handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if @@ -69,12 +67,11 @@ _FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletS include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Bullet DEFAULT_MSG BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY - BULLET_SOFTBODY_LIBRARY BULLET_INCLUDE_DIR) + BULLET_INCLUDE_DIR) set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR}) if(BULLET_FOUND) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY) _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY) - _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_SOFTBODY_LIBRARY) endif() From f3a7321a43719baf49b257ddb80d0e06c7dbac52 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 30 Dec 2013 16:44:07 +0100 Subject: [PATCH 060/251] Closes #856: More aggressive supression of skeleton base: only create for keyframe controllers, not any controllers --- components/nifogre/skeleton.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index 9ec6f15b0c..e01ae22efd 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -86,14 +86,23 @@ bool NIFSkeletonLoader::needSkeleton(const Nif::Node *node) { /* We need to be a little aggressive here, since some NIFs have a crap-ton * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: - * There are no bones used for skinning, there are no controllers, there + * There are no bones used for skinning, there are no keyframe controllers, there * are no nodes named "AttachLight", and the tree consists of NiNode, * NiTriShape, and RootCollisionNode types only. */ if(node->boneTrafo) return true; - if(!node->controller.empty() || node->name == "AttachLight") + if(!node->controller.empty()) + { + Nif::ControllerPtr ctrl = node->controller; + do { + if(ctrl->recType == Nif::RC_NiKeyframeController) + return true; + } while(!(ctrl=ctrl->next).empty()); + } + + if (node->name == "AttachLight") return true; if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) From 79a440e94aa32965b057ab24b4d980bccd0937e4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 30 Dec 2013 17:53:02 +0100 Subject: [PATCH 061/251] Many additions to 900bc06d2c236b: - Fix indentation - Consider any kind of light, not just torch_infinite_time - Hostile NPCs should also wear lights, if they have nothing else that could use the slot (or a twohanded weapon) - Remove redundant code and don't add additional lights to the inventory - World::isDark returns false for interiors which are unaffected by weather --- apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwmechanics/actors.cpp | 65 +++++++++++++++++--------- apps/openmw/mwmechanics/actors.hpp | 1 - apps/openmw/mwmechanics/character.cpp | 8 ++-- apps/openmw/mwworld/inventorystore.cpp | 2 +- apps/openmw/mwworld/weather.cpp | 6 ++- apps/openmw/mwworld/weather.hpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 4 +- apps/openmw/mwworld/worldimp.hpp | 3 +- 9 files changed, 59 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 48803a2d80..609f873ee6 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -428,7 +428,8 @@ namespace MWBase virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; - virtual bool isNight() const = 0; + // Are we in an exterior or pseudo-exterior cell and it's night? + virtual bool isDark() const = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1aa9ce9f5d..62242ee8c9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -448,36 +448,57 @@ namespace MWMechanics /** * Automatically equip NPCs torches at night and unequip them at day */ - if (!isPlayer && !MWWorld::Class::get (ptr).getCreatureStats (ptr).isHostile()) + if (!isPlayer) { - if (mTorchPtr.isEmpty()) - { - mTorchPtr = inventoryStore.search("torch_infinite_time"); - } + MWWorld::ContainerStoreIterator torch = inventoryStore.end(); + for (MWWorld::ContainerStoreIterator it = inventoryStore.begin(); it != inventoryStore.end(); ++it) + { + if (it->getTypeName() == typeid(ESM::Light).name()) + { + torch = it; + break; + } + } - if (MWBase::Environment::get().getWorld()->isNight()) - { - if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) + if (MWBase::Environment::get().getWorld()->isDark()) { - inventoryStore.unequipItem(*heldIter, ptr); + if (torch != inventoryStore.end()) + { + if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isHostile()) + { + // For non-hostile NPCs, unequip whatever is in the left slot in favor of a light. + if (heldIter != inventoryStore.end() && heldIter->getTypeName() != typeid(ESM::Light).name()) + inventoryStore.unequipItem(*heldIter, ptr); + + // Also unequip twohanded weapons which conflict with anything in CarriedLeft + if (torch->getClass().canBeEquipped(*torch, ptr).first == 3) + inventoryStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, ptr); + } + + heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + + // If we have a torch and can equip it (left slot free, no + // twohanded weapon in right slot), then equip it now. + if (heldIter == inventoryStore.end() + && torch->getClass().canBeEquipped(*torch, ptr).first == 1) + { + inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr); + } + } } - else if (heldIter == inventoryStore.end() && !mTorchPtr.isEmpty()) + else { - heldIter = inventoryStore.add(mTorchPtr, ptr); - inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, heldIter, ptr); + if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name()) + { + // At day, unequip lights and auto equip shields or other suitable items + // (Note: autoEquip will ignore lights) + inventoryStore.autoEquip(ptr); + } } - } - else - { - if (heldIter != inventoryStore.end() && heldIter->getTypeName() == typeid(ESM::Light).name()) - { - inventoryStore.unequipItem(*heldIter, ptr); - inventoryStore.add(*heldIter, ptr); - inventoryStore.autoEquip(ptr); - } - } } + heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + //If holding a light... if(heldIter.getType() == MWWorld::ContainerStore::Type_Light) { diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 411ac54ca8..b03b68e89f 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -29,7 +29,6 @@ namespace MWMechanics PtrControllerMap mActors; std::map mDeathCount; - MWWorld::Ptr mTorchPtr; void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 17eff3d022..351e33f13e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -36,8 +36,6 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/actionequip.hpp" -#include "../mwworld/actiontake.hpp" namespace { @@ -724,12 +722,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); } else if (mAnimation->isPlaying("torch")) { - mAnimation->disable("torch"); + mAnimation->disable("torch"); } return forcestateupdate; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 849e9ff26c..2c7c05317d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -179,7 +179,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) // Don't autoEquip lights if (test.getTypeName() == typeid(ESM::Light).name()) { - continue; + continue; } int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index c355d86a8a..5967a02fdb 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -708,7 +708,9 @@ float WeatherManager::getWindSpeed() const return mWindSpeed; } -bool WeatherManager::isNight() const +bool WeatherManager::isDark() const { - return (mHour < mSunriseTime || mHour > mNightStart - 1); + bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() + || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); + return exterior && (mHour < mSunriseTime || mHour > mNightStart - 1); } diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 4c412c4498..5e8d059ee3 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -152,7 +152,8 @@ namespace MWWorld void modRegion(const std::string ®ionid, const std::vector &chances); - bool isNight() const; + /// @see World::isDark + bool isDark() const; private: float mHour; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf32217b62..82029d11de 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2286,8 +2286,8 @@ namespace MWWorld actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } - bool World::isNight() const + bool World::isDark() const { - return mWeatherManager->isNight(); + return mWeatherManager->isDark(); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2c2f096ee3..a7948dcf2f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -516,7 +516,8 @@ namespace MWWorld const MWWorld::Ptr& actor, const std::string& sourceName); virtual void breakInvisibility (const MWWorld::Ptr& actor); - virtual bool isNight() const; + // Are we in an exterior or pseudo-exterior cell and it's night? + virtual bool isDark() const; }; } From b8ac37347ca2c01a086ba31fd8de7906d5de6beb Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 30 Dec 2013 19:04:35 +0100 Subject: [PATCH 062/251] Code cleanup for Class::canBeEquipped --- apps/openmw/mwclass/armor.cpp | 49 +++++++++++++------------------- apps/openmw/mwclass/clothing.cpp | 37 +++++++++--------------- apps/openmw/mwclass/weapon.cpp | 31 +++++++++----------- 3 files changed, 45 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 5b2b7caa39..c7c80ec2e3 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -294,41 +294,30 @@ namespace MWClass // slots that this item can be equipped in std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + if (slots_.first.empty()) + return std::make_pair(0, ""); + std::string npcRace = npc.get()->mBase->mRace; + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) + { + std::vector parts = ptr.get()->mBase->mParts.mParts; + + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage14}"); + } + } + for (std::vector::const_iterator slot=slots_.first.begin(); slot!=slots_.first.end(); ++slot) { - - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) - { - std::vector parts = ptr.get()->mBase->mParts.mParts; - - if(*slot == MWWorld::InventoryStore::Slot_Helmet) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_Head) - { - return std::make_pair(0, "#{sNotifyMessage13}"); - } - } - } - - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - { - return std::make_pair(0, "#{sNotifyMessage14}"); - } - } - } - } - + // If equipping a shield, check if there's a twohanded weapon conflicting with it if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) { MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c162bbe9de..ffa96260df 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -235,37 +235,26 @@ namespace MWClass // slots that this item can be equipped in std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); + if (slots_.first.empty()) + return std::make_pair(0, ""); + std::string npcRace = npc.get()->mBase->mRace; - for (std::vector::const_iterator slot=slots_.first.begin(); - slot!=slots_.first.end(); ++slot) + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) { + std::vector parts = ptr.get()->mBase->mParts.mParts; - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) { - std::vector parts = ptr.get()->mBase->mParts.mParts; - - if(*slot == MWWorld::InventoryStore::Slot_Helmet) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_Head) - return std::make_pair(0, "#{sNotifyMessage13}"); - } - } - - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - return std::make_pair(0, "#{sNotifyMessage15}"); - } - } + if((*itr).mPart == ESM::PRT_Head) + return std::make_pair(0, "#{sNotifyMessage13}"); + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + return std::make_pair(0, "#{sNotifyMessage15}"); } } + return std::make_pair (1, ""); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index a09e83380c..7b0a5eb471 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -390,26 +390,21 @@ namespace MWClass { std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); - // equip the item in the first free slot - for (std::vector::const_iterator slot=slots_.first.begin(); - slot!=slots_.first.end(); ++slot) + if (slots_.first.empty()) + return std::make_pair (0, ""); + + if(ptr.get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + ptr.get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + ptr.get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { - if(*slot == MWWorld::InventoryStore::Slot_CarriedRight) - { - if(ptr.get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || - ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || - ptr.get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || - ptr.get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || - ptr.get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || - ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || - ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) - { - return std::make_pair (2, ""); - } - } - return std::make_pair(1, ""); + return std::make_pair (2, ""); } - return std::make_pair (0, ""); + + return std::make_pair(1, ""); } boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const From 5c5f87445b437d480e5877518403706d64acae78 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 21:47:06 +0100 Subject: [PATCH 063/251] Fixes for "Conditional jump or move depends on uninitialised value(s)" and memleaks reported by valgrind. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/actors.cpp | 10 ++++++++++ apps/openmw/mwmechanics/actors.hpp | 1 + apps/openmw/mwmechanics/objects.cpp | 10 ++++++++++ apps/openmw/mwmechanics/objects.hpp | 1 + apps/openmw/mwrender/camera.cpp | 10 ++++++---- apps/openmw/mwrender/water.cpp | 1 + apps/openmw/mwsound/ffmpeg_decoder.cpp | 25 +++++++++++++++++++++++-- libs/openengine/ogre/renderer.hpp | 3 ++- 8 files changed, 54 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1aa9ce9f5d..8f03b83496 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -516,6 +516,16 @@ namespace MWMechanics Actors::Actors() {} + Actors::~Actors() + { + PtrControllerMap::iterator it(mActors.begin()); + for (; it != mActors.end(); ++it) + { + delete it->second; + it->second = NULL; + } + } + void Actors::addActor (const MWWorld::Ptr& ptr) { // erase previous death events since we are currently only tracking them while in an active cell diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 411ac54ca8..80040680bf 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -49,6 +49,7 @@ namespace MWMechanics public: Actors(); + ~Actors(); /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 694987855e..41d6b4ffad 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -14,6 +14,16 @@ Objects::Objects() { } +Objects::~Objects() +{ + PtrControllerMap::iterator it(mObjects.begin()); + for (; it != mObjects.end();++it) + { + delete it->second; + it->second = NULL; + } +} + void Objects::addObject(const MWWorld::Ptr& ptr) { removeObject(ptr); diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 5cdcdaa0af..32432c130a 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -21,6 +21,7 @@ namespace MWMechanics public: Objects(); + ~Objects(); void addObject (const MWWorld::Ptr& ptr); ///< Register an animated object diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 8f54be3f88..bf71505bc5 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -19,25 +19,27 @@ namespace MWRender Camera::Camera (Ogre::Camera *camera) : mCamera(camera), mCameraNode(NULL), + mAnimation(NULL), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), - mHeight(128.f), - mCameraDistance(300.f), - mDistanceAdjusted(false), - mAnimation(NULL), mNearest(30.f), mFurthest(800.f), mIsNearest(false), mIsFurthest(false), + mHeight(128.f), + mCameraDistance(300.f), + mDistanceAdjusted(false), mVanityToggleQueued(false), mViewModeToggleQueued(false) { mVanity.enabled = false; mVanity.allowed = true; + mPreviewCam.pitch = 0.f; mPreviewCam.yaw = 0.f; mPreviewCam.offset = 400.f; + mMainCam.pitch = 0.f; mMainCam.yaw = 0.f; mMainCam.offset = 400.f; } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 082551f371..0a4db30e9c 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -292,6 +292,7 @@ Water::~Water() delete mReflection; delete mRefraction; + delete mSimulation; } void Water::changeCell(const ESM::Cell* cell) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index d5c382a419..b5a2ff1d16 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -158,6 +158,11 @@ 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) + { + avio_close(mFormatCtx->pb); + mFormatCtx->pb = NULL; + } avformat_free_context(mFormatCtx); mFormatCtx = NULL; fail("Failed to allocate input stream"); @@ -195,6 +200,9 @@ void FFmpeg_Decoder::open(const std::string &fname) } catch(std::exception &e) { + avio_close(mFormatCtx->pb); + mFormatCtx->pb = NULL; + avformat_close_input(&mFormatCtx); throw; } @@ -211,9 +219,22 @@ void FFmpeg_Decoder::close() if(mFormatCtx) { - AVIOContext* context = mFormatCtx->pb; + if (mFormatCtx->pb != NULL) + { + avio_flush(mFormatCtx->pb); + + // + // avio-close() gives segfault, but with av_free valgrind shows memleak + // near mFormatCtx->pb + // + // to be checked! + // + // avio_close(mFormatCtx->pb); + // + av_free(mFormatCtx->pb); + mFormatCtx->pb = NULL; + } avformat_close_input(&mFormatCtx); - av_free(context); } mDataStream.setNull(); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index f45f09b01c..767e7cf99e 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -74,8 +74,9 @@ namespace OEngine , mScene(NULL) , mCamera(NULL) , mView(NULL) - , mWindowListener(NULL) + , mOgreInit(NULL) , mFader(NULL) + , mWindowListener(NULL) { } From 6107d5bad26188ba772a998639b17aeadd6dfd28 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 22:16:06 +0100 Subject: [PATCH 064/251] Updated ffmpeg decoder fix Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 30 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index b5a2ff1d16..13d11f6918 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -160,7 +160,12 @@ void FFmpeg_Decoder::open(const std::string &fname) { if (mFormatCtx->pb != NULL) { - avio_close(mFormatCtx->pb); + 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); @@ -200,7 +205,12 @@ void FFmpeg_Decoder::open(const std::string &fname) } catch(std::exception &e) { - avio_close(mFormatCtx->pb); + if (mFormatCtx->pb->buffer != NULL) + { + av_free(mFormatCtx->pb->buffer); + mFormatCtx->pb->buffer = NULL; + } + av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; avformat_close_input(&mFormatCtx); @@ -221,16 +231,16 @@ void FFmpeg_Decoder::close() { if (mFormatCtx->pb != NULL) { - avio_flush(mFormatCtx->pb); - + // valgrind shows memleak near mFormatCtx->pb // - // avio-close() gives segfault, but with av_free valgrind shows memleak - // near mFormatCtx->pb - // - // to be checked! - // - // avio_close(mFormatCtx->pb); + // As scrawl pointed, memleak could be related to this ffmpeg ticket: + // https://trac.ffmpeg.org/ticket/1357 // + if (mFormatCtx->pb->buffer != NULL) + { + av_free(mFormatCtx->pb->buffer); + mFormatCtx->pb->buffer = NULL; + } av_free(mFormatCtx->pb); mFormatCtx->pb = NULL; } From cb04f43384603728e89b220df6d403571d5a46c6 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 23:08:53 +0100 Subject: [PATCH 065/251] Fixes for "Conditional jump or move depends on uninitialised value(s)" and memleaks reported by valgrind. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwgui/hud.cpp | 2 ++ apps/openmw/mwgui/messagebox.cpp | 9 ++++++++ apps/openmw/mwgui/messagebox.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwrender/videoplayer.cpp | 29 ++++++++++++++++++++++++-- 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index a78b1a6d1b..8ef5e59d08 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -55,6 +55,8 @@ namespace MWGui , mWorldMouseOver(false) , mEnemyHealthTimer(0) , mIsDrowning(false) + , mWeaponSpellTimer(0.f) + , mDrowningFlashTheta(0.f) { setCoord(0,0, width, height); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 6718455523..378e76e467 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -16,6 +16,15 @@ namespace MWGui mLastButtonPressed = -1; } + MessageBoxManager::~MessageBoxManager () + { + std::vector::iterator it(mMessageBoxes.begin()); + for (; it != mMessageBoxes.end(); ++it) + { + delete *it; + } + } + void MessageBoxManager::onFrame (float frameDuration) { std::vector::iterator it; diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index aac4704fa9..0288f366ce 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -23,6 +23,7 @@ namespace MWGui { public: MessageBoxManager (); + ~MessageBoxManager (); void onFrame (float frameDuration); void createMessageBox (const std::string& message, bool stat = false); void removeStaticMessageBox (); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8818721f43..627c542f2a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -322,6 +322,7 @@ namespace MWGui delete mSoulgemDialog; delete mCursorManager; delete mRecharge; + delete mCompanionWindow; cleanupGarbage(); diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 88bc6d8ac2..74b52c06bd 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -950,8 +950,22 @@ void VideoState::init(const std::string& resourceName) // Open video file /// \todo leak here, ffmpeg or valgrind bug ? + /// + /// https://trac.ffmpeg.org/ticket/1357 + /// if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { + if (this->format_ctx != NULL) + { + if (this->format_ctx->pb != NULL) + { + av_free(this->format_ctx->pb->buffer); + this->format_ctx->pb->buffer = NULL; + + av_free(this->format_ctx->pb); + this->format_ctx->pb = NULL; + } + } // "Note that a user-supplied AVFormatContext will be freed on failure." this->format_ctx = NULL; av_free(ioCtx); @@ -1009,9 +1023,20 @@ void VideoState::deinit() if(this->format_ctx) { - AVIOContext *ioContext = this->format_ctx->pb; + // valgrind shows memleak near format_ctx->pb + // + // As scrawl pointed, memleak could be related to this ffmpeg ticket: + // https://trac.ffmpeg.org/ticket/1357 + // + if (this->format_ctx->pb != NULL) + { + av_free(this->format_ctx->pb->buffer); + this->format_ctx->pb->buffer = NULL; + + av_free(this->format_ctx->pb); + this->format_ctx->pb = NULL;; + } avformat_close_input(&this->format_ctx); - av_free(ioContext); } } From 50b6e828cc8473031c6bc9e8b078587b27d00d05 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 30 Dec 2013 23:51:44 +0100 Subject: [PATCH 066/251] Added asserts in Interpreter::installSegmentX methods. Signed-off-by: Lukasz Gromanowski --- components/compiler/opcodes.hpp | 8 ++++---- components/interpreter/interpreter.cpp | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index a885a373de..7ca2a1a64e 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -25,9 +25,9 @@ namespace Compiler const int opcodeAiFollowExplicit = 0x20023; const int opcodeAiFollowCell = 0x20024; const int opcodeAiFollowCellExplicit = 0x20025; - const int opcodeSetHello = 0x200015e; + const int opcodeSetHello = 0x200015e; // Same id as opcodeSetFight const int opcodeSetHelloExplicit = 0x200015d; - const int opcodeSetFight = 0x200015e; + const int opcodeSetFight = 0x200015e; // Same id as opcodeSetHello const int opcodeSetFightExplicit = 0x200015f; const int opcodeSetFlee = 0x2000160; const int opcodeSetFleeExplicit = 0x2000161; @@ -69,7 +69,7 @@ namespace Compiler { const int opcodeCellChanged = 0x2000000; const int opcodeCOC = 0x2000026; - const int opcodeCOE = 0x200008e; + const int opcodeCOE = 0x200008e; // Same ID as opcodeGetSkill const int opcodeGetInterior = 0x2000131; const int opcodeGetPCCell = 0x2000136; const int opcodeGetWaterLevel = 0x2000141; @@ -290,7 +290,7 @@ namespace Compiler const int opcodeGetDynamicGetRatio = 0x200006f; const int opcodeGetDynamicGetRatioExplicit = 0x2000072; - const int opcodeGetSkill = 0x200008e; + const int opcodeGetSkill = 0x200008e; // Same ID as opcodeCOE const int opcodeGetSkillExplicit = 0x20000a9; const int opcodeSetSkill = 0x20000c4; const int opcodeSetSkillExplicit = 0x20000df; diff --git a/components/interpreter/interpreter.cpp b/components/interpreter/interpreter.cpp index 10937e6bca..ea1e9fc912 100644 --- a/components/interpreter/interpreter.cpp +++ b/components/interpreter/interpreter.cpp @@ -166,31 +166,37 @@ namespace Interpreter void Interpreter::installSegment0 (int code, Opcode1 *opcode) { + assert(mSegment0.find(code) == mSegment0.end()); mSegment0.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment1 (int code, Opcode2 *opcode) { + assert(mSegment1.find(code) == mSegment1.end()); mSegment1.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment2 (int code, Opcode1 *opcode) { + assert(mSegment2.find(code) == mSegment2.end()); mSegment2.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment3 (int code, Opcode1 *opcode) { + assert(mSegment3.find(code) == mSegment3.end()); mSegment3.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment4 (int code, Opcode2 *opcode) { + assert(mSegment4.find(code) == mSegment4.end()); mSegment4.insert (std::make_pair (code, opcode)); } void Interpreter::installSegment5 (int code, Opcode0 *opcode) { + assert(mSegment5.find(code) == mSegment5.end()); mSegment5.insert (std::make_pair (code, opcode)); } From 94cdc1efd2a119bc6c344b043ef42db17f07f844 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 00:54:40 +0100 Subject: [PATCH 067/251] Enable mipmap generator for 1.9+ --- apps/openmw/mwrender/renderingmanager.cpp | 9 ++++++--- files/materials/water.mat | 2 +- files/settings-default.cfg | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8ee292de1e..b739a9f952 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -109,10 +109,13 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b mFactory->loadAllFiles(); - // Set default mipmap level (NB some APIs ignore this) - // Mipmap generation is currently disabled because it causes issues on Intel/AMD - //TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); + // Compressed textures with 0 mip maps are bugged in 1.8, so disable mipmap generator in that case + // ( https://ogre3d.atlassian.net/browse/OGRE-259 ) +#if OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) + TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); +#else TextureManager::getSingleton().setDefaultNumMipmaps(0); +#endif // Set default texture filtering options TextureFilterOptions tfo; diff --git a/files/materials/water.mat b/files/materials/water.mat index 1e5f8c8e04..ade55f326f 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -37,7 +37,7 @@ material Water texture_unit normalMap { - texture water_nm.png 5 + texture water_nm.png } texture_unit rippleNormalMap diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2ca59159bb..22391fe936 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -53,8 +53,7 @@ texture filtering = anisotropic anisotropy = 4 # Number of texture mipmaps to generate -# This setting is currently ignored due to mipmap generation problems on Intel/AMD -#num mipmaps = 5 +num mipmaps = 8 shader mode = From 254eba350eb2ecc873b6866b43cd1a482fe8afb8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 00:56:04 +0100 Subject: [PATCH 068/251] Not handling interpolation type should be an error, since it will fail reading the next record if it hasn't read the previous one completely. --- components/nif/niffile.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 6e629772e6..91ae93b40b 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -200,7 +200,7 @@ struct KeyListT { } } else - nif->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); + nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); } }; typedef KeyListT FloatKeyList; From d6345bce91e3d65cf69c5f7368e58be348a22763 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 31 Dec 2013 13:24:20 +0200 Subject: [PATCH 069/251] added npc hit reactions --- apps/openmw/mwclass/npc.cpp | 1 + apps/openmw/mwmechanics/character.cpp | 134 ++++++++++++++------------ apps/openmw/mwmechanics/character.hpp | 11 ++- 3 files changed, 86 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4187aa8c1f..87d98a5782 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -604,6 +604,7 @@ namespace MWClass // something, alert the character controller, scripts, etc. MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + getCreatureStats(ptr).setAttacked(true); if(object.isEmpty()) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 17eff3d022..f4d6feab67 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -65,15 +65,24 @@ struct StateInfo { const char groupname[32]; }; -static const StateInfo sDeathList[] = { - { CharState_Death1, "death1" }, - { CharState_Death2, "death2" }, - { CharState_Death3, "death3" }, - { CharState_Death4, "death4" }, - { CharState_Death5, "death5" }, - { CharState_SwimDeath, "swimdeath" }, +static const std::string sDeathList[] = { + "death1" , + "death2" , + "death3" , + "death4" , + "death5" , + "swimdeath", }; -static const StateInfo *sDeathListEnd = &sDeathList[sizeof(sDeathList)/sizeof(sDeathList[0])]; +static const int sDeathListSize = sizeof(sDeathList)/sizeof(sDeathList[0]); + +static const std::string sHitList[] = { + "hit1" , + "hit2" , + "hit3" , + "hit4" , + "hit5" , +}; +static const int sHitListSize = sizeof(sHitList)/sizeof(sHitList[0]); static const StateInfo sMovementList[] = { { CharState_WalkForward, "walkforward" }, @@ -150,6 +159,38 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { + if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) + { + mHitState = CharState_Hit; + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); + + if(!mAnimation->isPlaying(mCurrentHit)) + { + mAnimation->disable(mCurrentHit); + if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) + && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) + mCurrentHit = "knockdown"; + else + { + int iHit = rand() % sHitListSize; + mCurrentHit = sHitList[iHit]; + } + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, 0); + if(mUpperBodyState == UpperCharState_WeapEquiped) + mUpdateAfterHit = false; + else + mUpdateAfterHit = true; + } + return; + } + else if(!mAnimation->isPlaying(mCurrentHit)) + { + mAnimation->disable(mCurrentHit); + mCurrentHit.erase(); + mHitState = CharState_None; + mUpdateAfterHit = true; + } + const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); if(force || idle != mIdleState) @@ -338,6 +379,23 @@ MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &s return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); } +void CharacterController::playRandomDeath(float startpoint) +{ + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) + { + mDeathState = CharState_SwimDeath; + mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' + } + else + { + int num = rand() % (sDeathListSize-1); + mDeathState = static_cast(CharState_Death1 + num); + mCurrentDeath = sDeathList[num]; + } + + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, + false, 1.0f, "start", "stop", 0.0f, 0); +} CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) @@ -346,6 +404,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mMovementState(CharState_None) , mMovementSpeed(0.0f) , mDeathState(CharState_None) + , mHitState(CharState_None) + , mUpdateAfterHit(true) , mUpperBodyState(UpperCharState_Nothing) , mJumpState(JumpState_None) , mWeaponType(WeapType_None) @@ -392,13 +452,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim refreshCurrentAnims(mIdleState, mMovementState, true); if(mDeathState != CharState_None) { - const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - - mCurrentDeath = state->groupname; - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 1.0f, 0); + playRandomDeath(1.0f); } } @@ -766,6 +820,8 @@ void CharacterController::update(float duration) bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); + if(mHitState != CharState_None && mJumpState == JumpState_None) + vec = Ogre::Vector3(0.0f); Ogre::Vector3 rot = cls.getRotationVector(mPtr); mMovementSpeed = cls.getSpeed(mPtr); @@ -778,6 +834,7 @@ void CharacterController::update(float duration) isrunning = isrunning && std::abs(vec[0])+std::abs(vec[1]) > 0.0f; + // advance athletics if(std::abs(vec[0])+std::abs(vec[1]) > 0.0f && mPtr.getRefData().getHandle() == "player") { @@ -951,7 +1008,7 @@ void CharacterController::update(float duration) } } - if(cls.isNpc()) + if(cls.isNpc() && mUpdateAfterHit) forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; refreshCurrentAnims(idlestate, movestate, forcestateupdate); @@ -1055,14 +1112,7 @@ void CharacterController::forceStateUpdate() refreshCurrentAnims(mIdleState, mMovementState, true); if(mDeathState != CharState_None) { - const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - - mCurrentDeath = state->groupname; - if(!mAnimation->getInfo(mCurrentDeath)) - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 0.0f, 0); + playRandomDeath(0.0f); } } @@ -1071,44 +1121,10 @@ void CharacterController::kill() if(mDeathState != CharState_None) return; - if(mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - const StateInfo *state = NULL; - if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) - { - mDeathState = CharState_SwimDeath; - state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - } - - static const CharacterState deathstates[5] = { - CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5 - }; - std::vector states(&deathstates[0], &deathstates[5]); - - while(states.size() > 1 && (!state || !mAnimation->hasAnimation(state->groupname))) - { - int pos = (int)(rand()/((double)RAND_MAX+1.0)*states.size()); - mDeathState = states[pos]; - states.erase(states.begin()+pos); - - state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState)); - if(state == sDeathListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - } - mCurrentDeath = state->groupname; - } - else - { - mDeathState = CharState_Death1; - mCurrentDeath = "death1"; - } + playRandomDeath(0.0f); if(mAnimation) { - mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, 1.0f, "start", "stop", 0.0f, 0); mAnimation->disable(mCurrentIdle); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 817fa2fd51..e0b065ceca 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -28,6 +28,7 @@ enum Priority { Priority_Default, Priority_Jump, Priority_Movement, + Priority_Hit, Priority_Weapon, Priority_Torch, @@ -87,7 +88,9 @@ enum CharacterState { CharState_Death3, CharState_Death4, CharState_Death5, - CharState_SwimDeath + CharState_SwimDeath, + + CharState_Hit }; enum WeaponType { @@ -142,6 +145,10 @@ class CharacterController CharacterState mDeathState; std::string mCurrentDeath; + CharacterState mHitState; + std::string mCurrentHit; + bool mUpdateAfterHit;//don't allow attack if it hadn't been started before actor got hit + UpperBodyCharacterState mUpperBodyState; JumpingState mJumpState; @@ -172,6 +179,8 @@ class CharacterController void updateVisibility(); + void playRandomDeath(float startpoint); + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); From 07a9b7623a7b1e543c372a9908bef8b773b3e24c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 01:12:50 +0100 Subject: [PATCH 070/251] Enable skeleton-based bounding boxes added in Ogre 1.10 --- components/nifogre/ogrenifloader.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 345e7d6fd4..453eff2360 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -607,6 +607,14 @@ class NIFObjectLoader NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex); Ogre::Entity *entity = sceneMgr->createEntity(fullname); + +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + // Enable skeleton-based bounding boxes. With the static bounding box, + // the animation may cause parts to go outside the box and cause culling problems. + if (entity->hasSkeleton()) + entity->setUpdateBoundingBoxFromSkeleton(true); +#endif + entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); scene->mEntities.push_back(entity); From 8381cad5a4d5155f58d3e8a4386b78d44a29c425 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Tue, 31 Dec 2013 14:13:42 +0100 Subject: [PATCH 071/251] Don't try to set a cursor before one exists --- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8818721f43..6c6f519674 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -174,11 +174,11 @@ namespace MWGui MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); - mCursorManager->setEnabled(true); - onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); SDL_ShowCursor(false); + mCursorManager->setEnabled(true); + // hide mygui's pointer MyGUI::PointerManager::getInstance().setVisible(false); } From 764ec9bc5f2f3fe3cb331226ec19a1fac7da5388 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 15:50:27 +0100 Subject: [PATCH 072/251] Closes #716: Use the particle controller's size instead of NiAutoNormalParticlesData particle radius. Same as NifSkope now. --- components/nifogre/ogrenifloader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 453eff2360..5f9674c91e 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -839,8 +839,6 @@ class NIFObjectLoader vertprop, zprop, specprop, wireprop, needTangents)); - partsys->setDefaultDimensions(particledata->particleRadius*2.0f, - particledata->particleRadius*2.0f); partsys->setCullIndividually(false); partsys->setParticleQuota(particledata->numParticles); partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace)); @@ -856,6 +854,8 @@ class NIFObjectLoader { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); + partsys->setDefaultDimensions(partctrl->size, partctrl->size); + if(!partctrl->emitter.empty()) { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); From a8fb1ae51c89c21aadaf2e4a3c263a2c6efa8f49 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Tue, 31 Dec 2013 17:55:04 +0200 Subject: [PATCH 073/251] improved, added knockdown after falling --- apps/openmw/mwmechanics/character.cpp | 27 ++++++++++++++------------- apps/openmw/mwmechanics/character.hpp | 1 - 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f4d6feab67..a7ae298245 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -166,7 +166,6 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(!mAnimation->isPlaying(mCurrentHit)) { - mAnimation->disable(mCurrentHit); if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) mCurrentHit = "knockdown"; @@ -175,20 +174,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat int iHit = rand() % sHitListSize; mCurrentHit = sHitList[iHit]; } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, 0); - if(mUpperBodyState == UpperCharState_WeapEquiped) - mUpdateAfterHit = false; - else - mUpdateAfterHit = true; + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - return; } else if(!mAnimation->isPlaying(mCurrentHit)) { - mAnimation->disable(mCurrentHit); mCurrentHit.erase(); mHitState = CharState_None; - mUpdateAfterHit = true; } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); @@ -405,7 +397,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mMovementSpeed(0.0f) , mDeathState(CharState_None) , mHitState(CharState_None) - , mUpdateAfterHit(true) , mUpperBodyState(UpperCharState_Nothing) , mJumpState(JumpState_None) , mWeaponType(WeapType_None) @@ -554,7 +545,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun bool animPlaying; if(stats.getAttackingOrSpell()) { - if(mUpperBodyState == UpperCharState_WeapEquiped) + if(mUpperBodyState == UpperCharState_WeapEquiped && mHitState == CharState_None) { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackType.clear(); @@ -718,9 +709,18 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun if(mUpperBodyState == UpperCharState_EquipingWeap || mUpperBodyState == UpperCharState_FollowStartToFollowStop || mUpperBodyState == UpperCharState_CastingSpell) + { mUpperBodyState = UpperCharState_WeapEquiped; + //don't allow to continue playing hit animation after actor had attacked during it + if(mHitState != CharState_None) + { + mAnimation->disable(mCurrentHit); + mCurrentHit.clear(); + mHitState = CharState_None; + } + } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) - mUpperBodyState = UpperCharState_Nothing; + mUpperBodyState = UpperCharState_Nothing; } else if(complete >= 1.0f) { @@ -938,6 +938,7 @@ void CharacterController::update(float duration) int realHealthLost = healthLost * (1.0f - 0.25 * fatigueTerm); health.setCurrent(health.getCurrent() - realHealthLost); cls.getCreatureStats(mPtr).setHealth(health); + cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), true); // report acrobatics progression if (mPtr.getRefData().getHandle() == "player") @@ -1008,7 +1009,7 @@ void CharacterController::update(float duration) } } - if(cls.isNpc() && mUpdateAfterHit) + if(cls.isNpc()) forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; refreshCurrentAnims(idlestate, movestate, forcestateupdate); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index e0b065ceca..4974b7c08e 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -147,7 +147,6 @@ class CharacterController CharacterState mHitState; std::string mCurrentHit; - bool mUpdateAfterHit;//don't allow attack if it hadn't been started before actor got hit UpperBodyCharacterState mUpperBodyState; From 86b2211932412dfb64c9ac0f3f90b063ca357333 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 17:33:15 +0100 Subject: [PATCH 074/251] Don't warn about NiFlipController (already implemented) --- components/nifogre/material.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index c7c9abfc10..8ae86b64a8 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -152,7 +152,8 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, Nif::ControllerPtr ctrls = texprop->controller; while(!ctrls.empty()) { - warn("Unhandled texture controller "+ctrls->recName+" in "+name); + if (ctrls->recType != Nif::RC_NiFlipController) // Handled in ogrenifloader + warn("Unhandled texture controller "+ctrls->recName+" in "+name); ctrls = ctrls->next; } } From 1ce4663065e34dbf612261a9e47226f872328b98 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 31 Dec 2013 18:13:38 +0100 Subject: [PATCH 075/251] Updated compiler opcodes for COE, and setHello. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++-- components/compiler/opcodes.hpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 9e024f8723..cf533451c6 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -127,7 +127,6 @@ op 0x200007e-0x2000084: Enable Controls op 0x2000085-0x200008b: Disable Controls op 0x200008c: Unlock op 0x200008d: Unlock, explicit reference -op 0x200008e: COE op 0x200008e-0x20000a8: GetSkill op 0x20000a9-0x20000c3: GetSkill, explicit reference op 0x20000c4-0x20000de: SetSkill @@ -358,5 +357,6 @@ op 0x2000222: GetLineOfSight op 0x2000223: GetLineOfSightExplicit op 0x2000224: ToggleAI op 0x2000225: ToggleAIExplicit +op 0x2000226: COE -opcodes 0x2000226-0x3ffffff unused +opcodes 0x2000227-0x3ffffff unused diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 7ca2a1a64e..b7d44a851e 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -25,9 +25,9 @@ namespace Compiler const int opcodeAiFollowExplicit = 0x20023; const int opcodeAiFollowCell = 0x20024; const int opcodeAiFollowCellExplicit = 0x20025; - const int opcodeSetHello = 0x200015e; // Same id as opcodeSetFight + const int opcodeSetHello = 0x200015c; const int opcodeSetHelloExplicit = 0x200015d; - const int opcodeSetFight = 0x200015e; // Same id as opcodeSetHello + const int opcodeSetFight = 0x200015e; const int opcodeSetFightExplicit = 0x200015f; const int opcodeSetFlee = 0x2000160; const int opcodeSetFleeExplicit = 0x2000161; @@ -69,7 +69,7 @@ namespace Compiler { const int opcodeCellChanged = 0x2000000; const int opcodeCOC = 0x2000026; - const int opcodeCOE = 0x200008e; // Same ID as opcodeGetSkill + const int opcodeCOE = 0x2000226; const int opcodeGetInterior = 0x2000131; const int opcodeGetPCCell = 0x2000136; const int opcodeGetWaterLevel = 0x2000141; @@ -290,7 +290,7 @@ namespace Compiler const int opcodeGetDynamicGetRatio = 0x200006f; const int opcodeGetDynamicGetRatioExplicit = 0x2000072; - const int opcodeGetSkill = 0x200008e; // Same ID as opcodeCOE + const int opcodeGetSkill = 0x200008e; const int opcodeGetSkillExplicit = 0x20000a9; const int opcodeSetSkill = 0x20000c4; const int opcodeSetSkillExplicit = 0x20000df; From be1938ee905efc028056b292915bb3bf6d1297f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 18:32:46 +0100 Subject: [PATCH 076/251] Closes #805: Don't add entities that are supposed to be invisible to static geometry --- apps/openmw/mwrender/animation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index de86bcfa7a..faf9d979ae 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1211,7 +1211,8 @@ void ObjectAnimation::fillBatch(Ogre::StaticGeometry *sg) for(;iter != mObjectRoot->mEntities.rend();++iter) { Ogre::Node *node = (*iter)->getParentNode(); - sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); + if ((*iter)->isVisible()) + sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); } } From 3604b9d171738a51f2a89ac3f502c50c36727fd6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 18:35:46 +0100 Subject: [PATCH 077/251] Closes #566: In interior cells, update global map position marker using the first exterior teleport door --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwgui/mapwindow.cpp | 37 +++++++++++++++++--------- apps/openmw/mwgui/mapwindow.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++++ apps/openmw/mwworld/worldimp.cpp | 23 +++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 2 ++ 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 609f873ee6..44b45badff 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -430,6 +430,8 @@ namespace MWBase // Are we in an exterior or pseudo-exterior cell and it's night? virtual bool isDark() const = 0; + + virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) = 0; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 93e1d11a3d..00380aefd7 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -396,20 +396,18 @@ namespace MWGui void MapWindow::globalMapUpdatePlayer () { - Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); - Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); - Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); - - float worldX, worldY; - mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); - worldX *= mGlobalMapRender->getWidth(); - worldY *= mGlobalMapRender->getHeight(); - - - // for interiors, we have no choice other than using the last position & direction. - /// \todo save this last position in the savegame? + // For interiors, position is set by WindowManager via setGlobalMapPlayerPosition if (MWBase::Environment::get().getWorld ()->isCellExterior ()) { + Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); + Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); + Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); + + float worldX, worldY; + mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); + worldX *= mGlobalMapRender->getWidth(); + worldY *= mGlobalMapRender->getHeight(); + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); @@ -444,4 +442,19 @@ namespace MWGui "#{sWorld}"); } + void MapWindow::setGlobalMapPlayerPosition(float worldX, float worldY) + { + float x, y; + mGlobalMapRender->worldPosToImageSpace (worldX, worldY, x, y); + x *= mGlobalMapRender->getWidth(); + y *= mGlobalMapRender->getHeight(); + + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(x - 16, y - 16)); + + // set the view offset so that player is in the center + MyGUI::IntSize viewsize = mGlobalMap->getSize(); + MyGUI::IntPoint viewoffs(0.5*viewsize.width - x, 0.5*viewsize.height - y); + mGlobalMap->setViewOffset(viewoffs); + } + } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 5518ab4a8f..2c90c422e6 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -81,6 +81,8 @@ namespace MWGui void addVisitedLocation(const std::string& name, int x, int y); // adds the marker to the global map void cellExplored(int x, int y); + void setGlobalMapPlayerPosition (float worldX, float worldY); + virtual void open(); private: diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8818721f43..7753c25741 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -774,6 +774,11 @@ namespace MWGui mHud->setCellName( cell->mCell->mName ); mMap->setCellPrefix( cell->mCell->mName ); mHud->setCellPrefix( cell->mCell->mName ); + + Ogre::Vector3 worldPos; + if (MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) + mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y); + } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 82029d11de..8f979da3bb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2288,6 +2288,27 @@ namespace MWWorld bool World::isDark() const { - return mWeatherManager->isDark(); + return mWeatherManager->isDark(); + } + + bool World::findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) + { + MWWorld::CellRefList& doors = cell->mDoors; + CellRefList::List& refList = doors.mList; + + // Check if any door in the cell leads to an exterior directly + for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) + { + MWWorld::LiveCellRef& ref = *it; + if (ref.mRef.mTeleport && ref.mRef.mDestCell.empty()) + { + ESM::Position pos = ref.mRef.mDoorDest; + result = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + return true; + } + } + + // No luck :( + return false; } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a7948dcf2f..fad683d18c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -518,6 +518,8 @@ namespace MWWorld virtual void breakInvisibility (const MWWorld::Ptr& actor); // Are we in an exterior or pseudo-exterior cell and it's night? virtual bool isDark() const; + + virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result); }; } From c7c3ba0ac69ba09d2696b3d89341f55c20c757a7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 18:37:55 +0100 Subject: [PATCH 078/251] Tweak .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f22f1bd49c..c061ca637e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ CMakeFiles */CMakeFiles CMakeCache.txt cmake_install.cmake -CMakeLists.txt.user Makefile makefile build @@ -22,6 +21,8 @@ Doxygen .project .settings .directory +## qt-creator +CMakeLists.txt.user* ## resources data From b02b966c446d688931f164f58293b8e76f1219f8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 31 Dec 2013 19:11:16 +0100 Subject: [PATCH 079/251] Closes #994: Don't cap skills to 100 when set via console (except for modX variants) --- apps/openmw/mwscript/statsextensions.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index dc6b1d4a71..45e5b0f187 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -355,8 +355,6 @@ namespace MWScript if (newLevel<0) newLevel = 0; - else if (newLevel>100) - newLevel = 100; progress = (progress / stats.getSkillGain (mIndex, class_, -1, level)) * stats.getSkillGain (mIndex, class_, -1, newLevel); From e9844e1b3724c8cecdf89631023cc3d7e716a352 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 31 Dec 2013 20:40:23 +0100 Subject: [PATCH 080/251] Fixes #417: Apply weather instantly when teleporting Changed teleporting detection from "position tracking" to manually setting "teleportation" flag ( player->setTeleported(true) ). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwmechanics/spellcasting.cpp | 7 ++ apps/openmw/mwscript/cellextensions.cpp | 14 ++- .../mwscript/transformationextensions.cpp | 12 +++ apps/openmw/mwworld/actionteleport.cpp | 8 +- apps/openmw/mwworld/player.cpp | 13 ++- apps/openmw/mwworld/player.hpp | 5 +- apps/openmw/mwworld/weather.cpp | 101 ++++++------------ apps/openmw/mwworld/worldimp.cpp | 23 ++-- 8 files changed, 92 insertions(+), 91 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 74816d12e2..411fa6bdfb 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,6 +7,7 @@ #include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/animation.hpp" @@ -240,11 +241,15 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::DivineIntervention) { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + // We need to be able to get the world location of an interior cell before implementing this // or alternatively, the last known exterior location of the player, which is how vanilla does it. } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + // Same as above } @@ -254,6 +259,8 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Recall) { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + // TODO } } diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 316f912dad..f26602f7a7 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -43,10 +43,13 @@ namespace MWScript ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->findExteriorPosition(cell, pos)) { + world->getPlayer().setTeleported(true); + if (world->findExteriorPosition(cell, pos)) + { world->changeToExteriorCell(pos); } - else { + else + { // Change to interior even if findInteriorPosition() // yields false. In this case position will be zero-point. world->findInteriorPosition(cell, pos); @@ -68,13 +71,14 @@ namespace MWScript runtime.pop(); ESM::Position pos; - - MWBase::Environment::get().getWorld()->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->getPlayer().setTeleported(true); + world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); pos.pos[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - MWBase::Environment::get().getWorld()->changeToExteriorCell (pos); + world->changeToExteriorCell (pos); } }; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index ae9ac041e1..47a632ae98 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -207,6 +207,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + if (ptr.getRefData().getHandle() == "player") + { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + } std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -271,6 +275,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + if (ptr.getRefData().getHandle() == "player") + { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + } Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); @@ -328,6 +336,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + if (ptr.getRefData().getHandle() == "player") + { + MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + } Interpreter::Type_Float x = runtime[0].mFloat; runtime.pop(); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index ae5ffc3b90..773fde81e3 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -3,6 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "player.hpp" namespace MWWorld { @@ -14,9 +15,12 @@ namespace MWWorld void ActionTeleport::executeImp (const Ptr& actor) { + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->getPlayer().setTeleported(true); + if (mCellName.empty()) - MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); + world->changeToExteriorCell (mPosition); else - MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, mPosition); + world->changeToInteriorCell (mCellName, mPosition); } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index e26c2e2a52..291490ae04 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -19,7 +19,8 @@ namespace MWWorld Player::Player (const ESM::NPC *player, const MWBase::World& world) : mCellStore(0), mAutoMove(false), - mForwardBackward (0) + mForwardBackward (0), + mTeleported(false) { mPlayer.mBase = player; mPlayer.mRef.mRefID = "player"; @@ -145,4 +146,14 @@ namespace MWWorld MWWorld::Ptr ptr = getPlayer(); return MWWorld::Class::get(ptr).getNpcStats(ptr).getDrawState(); } + + bool Player::wasTeleported() const + { + return mTeleported; + } + + void Player::setTeleported(bool teleported) + { + mTeleported = teleported; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d78b1901c4..55d8ff2c88 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -30,7 +30,7 @@ namespace MWWorld bool mAutoMove; int mForwardBackward; - + bool mTeleported; public: Player(const ESM::NPC *player, const MWBase::World& world); @@ -64,6 +64,9 @@ namespace MWWorld void yaw(float yaw); void pitch(float pitch); void roll(float roll); + + bool wasTeleported() const; + void setTeleported(bool teleported); }; } #endif diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 4a8def1847..124fc14ab2 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -324,42 +324,7 @@ void WeatherManager::update(float duration) mWeatherUpdateTime -= timePassed; - const bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); - if (!exterior) - { - mRendering->sunDisable(false); - mRendering->skyDisable(); - mRendering->getSkyManager()->setLightningStrength(0.f); - stopSounds(true); - return; - } - - // Exterior - std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); - - if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) - { - mCurrentRegion = regionstr; - mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - - std::string weatherType = "clear"; - - if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) - weatherType = mRegionOverrides[regionstr]; - else - { - // get weather probabilities for the current region - const ESM::Region *region = - MWBase::Environment::get().getWorld()->getStore().get().search (regionstr); - - if (region != 0) - { - weatherType = nextWeather(region); - } - } - - setWeather(weatherType, false); - } + switchToNextWeather(false); if (mNextWeather != "") { @@ -710,42 +675,42 @@ float WeatherManager::getWindSpeed() const void WeatherManager::switchToNextWeather(bool instantly) { - const bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); - if (!exterior) - { - mRendering->sunDisable(false); - mRendering->skyDisable(); - mRendering->getSkyManager()->setLightningStrength(0.f); - stopSounds(true); - return; - } + MWBase::World* world = MWBase::Environment::get().getWorld(); + const bool exterior = (world->isCellExterior() || world->isCellQuasiExterior()); + if (!exterior) + { + mRendering->sunDisable(false); + mRendering->skyDisable(); + mRendering->getSkyManager()->setLightningStrength(0.f); + stopSounds(true); + return; + } - // Exterior - std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); + // Exterior + std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); - if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) - { - mCurrentRegion = regionstr; - mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) + { + mCurrentRegion = regionstr; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - std::string weatherType = "clear"; + std::string weatherType = "clear"; - if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) - { - weatherType = mRegionOverrides[regionstr]; - } - else - { - // get weather probabilities for the current region - const ESM::Region *region = - MWBase::Environment::get().getWorld()->getStore().get().search (regionstr); + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + { + weatherType = mRegionOverrides[regionstr]; + } + else + { + // get weather probabilities for the current region + const ESM::Region *region = world->getStore().get().search (regionstr); - if (region != 0) - { - weatherType = nextWeather(region); - } - } + if (region != 0) + { + weatherType = nextWeather(region); + } + } - setWeather(weatherType, instantly); - } + setWeather(weatherType, instantly); + } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 18dc77d3aa..86dc38a8e9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2247,19 +2247,14 @@ namespace MWWorld void World::updateWeather(float duration) { - static const float teleportationStepTreshold = 256.f; - static ESM::Position lastPlayerPos; - ESM::Position currentPos = mPlayer->getPlayer().getRefData().getPosition(); - - if (fabs(fabs(lastPlayerPos.pos[0]) - fabs(currentPos.pos[0])) >= teleportationStepTreshold - || fabs(fabs(lastPlayerPos.pos[1]) - fabs(currentPos.pos[1])) >= teleportationStepTreshold) - { - lastPlayerPos = currentPos; - mWeatherManager->switchToNextWeather(true); - } - else - { - mWeatherManager->update (duration); - } + if (mPlayer->wasTeleported()) + { + mPlayer->setTeleported(false); + mWeatherManager->switchToNextWeather(true); + } + else + { + mWeatherManager->update (duration); + } } } From 101813fd0dc353f4732f5cd2508fdd144349e150 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Tue, 31 Dec 2013 21:18:10 +0100 Subject: [PATCH 081/251] Fixes #417: Apply weather instantly when teleporting Correction to previous commit - WeatherManager->update() will always be called. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/worldimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 86dc38a8e9..b4b1bd1f65 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2252,9 +2252,7 @@ namespace MWWorld mPlayer->setTeleported(false); mWeatherManager->switchToNextWeather(true); } - else - { - mWeatherManager->update (duration); - } + + mWeatherManager->update(duration); } } From c86760e3cd73336041755d70c5d8426f6afe6751 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 00:12:31 +0100 Subject: [PATCH 082/251] Remember the last known exterior position of the player in case we fail to map the interior to a world position. --- apps/openmw/mwgui/windowmanagerimp.cpp | 9 ++++++--- apps/openmw/mwworld/player.cpp | 1 + apps/openmw/mwworld/player.hpp | 11 +++++++++++ apps/openmw/mwworld/worldimp.cpp | 6 ++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7753c25741..4a47d0f9f0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -16,6 +16,7 @@ #include "../mwbase/inputmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "console.hpp" #include "journalwindow.hpp" @@ -776,9 +777,11 @@ namespace MWGui mHud->setCellPrefix( cell->mCell->mName ); Ogre::Vector3 worldPos; - if (MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) - mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y); - + if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) + worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); + else + MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos); + mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y); } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index e26c2e2a52..04dc8d0a12 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -18,6 +18,7 @@ namespace MWWorld { Player::Player (const ESM::NPC *player, const MWBase::World& world) : mCellStore(0), + mLastKnownExteriorPosition(0,0,0), mAutoMove(false), mForwardBackward (0) { diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d78b1901c4..23317f6759 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -6,6 +6,8 @@ #include "../mwmechanics/drawstate.hpp" +#include + namespace ESM { struct NPC; @@ -28,6 +30,8 @@ namespace MWWorld MWWorld::CellStore *mCellStore; std::string mSign; + Ogre::Vector3 mLastKnownExteriorPosition; + bool mAutoMove; int mForwardBackward; @@ -35,6 +39,13 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); + /// Interiors can not always be mapped to a world position. However + /// world position is still required for divine / almsivi magic effects + /// and the player arrow on the global map. + /// TODO: This should be stored in the savegame, too. + void setLastKnownExteriorPosition (const Ogre::Vector3& position) { mLastKnownExteriorPosition = position; } + Ogre::Vector3 getLastKnownExteriorPosition() const { return mLastKnownExteriorPosition; } + void set (const ESM::NPC *player); void setCell (MWWorld::CellStore *cellStore); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8f979da3bb..670c0387f1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1297,6 +1297,12 @@ namespace MWWorld performUpdateSceneQueries (); updateWindowManager (); + + if (mPlayer->getPlayer().getCell()->isExterior()) + { + ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); + mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2])); + } } void World::updateWindowManager () From ea3b88951a79e5bb8593ca98ec53a8a6c1fb0930 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 02:22:11 +0100 Subject: [PATCH 083/251] Implement divine/almsivi intervention magic effects --- apps/openmw/mwbase/world.hpp | 6 +++++ apps/openmw/mwmechanics/spellcasting.cpp | 19 +++++++++++--- apps/openmw/mwworld/cells.cpp | 16 +++++++++--- apps/openmw/mwworld/cells.hpp | 7 +++++ apps/openmw/mwworld/worldimp.cpp | 33 ++++++++++++++++++++++-- apps/openmw/mwworld/worldimp.hpp | 6 +++++ 6 files changed, 78 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 44b45badff..d9640f4d22 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -432,6 +432,12 @@ namespace MWBase virtual bool isDark() const = 0; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) = 0; + + /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) + /// closest to \a worldPos. + /// @note id must be lower case + virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos) = 0; }; } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 025aa6b3a9..ee907284d9 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,6 +7,7 @@ #include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/animation.hpp" @@ -238,14 +239,24 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::RemoveCurse) target.getClass().getCreatureStats(target).getSpells().purgeCurses(); - else if (effectId == ESM::MagicEffect::DivineIntervention) + if (target.getRefData().getHandle() != "player") + return; + if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) + return; + + Ogre::Vector3 worldPos; + if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(target.getCell(), worldPos)) + worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); + + if (effectId == ESM::MagicEffect::DivineIntervention) { - // We need to be able to get the world location of an interior cell before implementing this - // or alternatively, the last known exterior location of the player, which is how vanilla does it. + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", + worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - // Same as above + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", + worldPos); } else if (effectId == ESM::MagicEffect::Mark) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 37c4b6a3f4..865c0d01fc 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -129,9 +129,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce if (cell.mState==Ptr::CellStore::State_Preloaded) { - std::string lowerCase = Misc::StringUtils::lowerCase(name); - - if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), lowerCase)) + if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), name)) { cell.load (mStore, mReader); } @@ -261,3 +259,15 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name) // giving up return Ptr(); } + +void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) +{ + for (std::map, Ptr::CellStore>::iterator iter = mExteriors.begin(); + iter!=mExteriors.end(); ++iter) + { + Ptr ptr = getPtrAndCache (name, iter->second); + if (!ptr.isEmpty()) + out.push_back(ptr); + } + +} diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0c51cf4520..31de2f60e8 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -47,8 +47,15 @@ namespace MWWorld Ptr getPtr (const std::string& name, CellStore& cellStore, bool searchInContainers = false); ///< \param searchInContainers Only affect loaded cells. + /// @note name must be lower case + /// @note name must be lower case Ptr getPtr (const std::string& name); + + /// Get all Ptrs referencing \a name in exterior cells + /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. + /// @note name must be lower case + void getExteriorPtrs (const std::string& name, std::vector& out); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 670c0387f1..fe2d2e64e6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -39,6 +39,7 @@ #include "cellfunctors.hpp" #include "containerstore.hpp" #include "inventorystore.hpp" +#include "actionteleport.hpp" #include "contentloader.hpp" #include "esmloader.hpp" @@ -498,12 +499,14 @@ namespace MWWorld if (!ptr.isEmpty()) return ptr; + std::string lowerCaseName = Misc::StringUtils::lowerCase(name); + // active cells for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); iter!=mWorldScene->getActiveCells().end(); ++iter) { Ptr::CellStore* cellstore = *iter; - Ptr ptr = mCells.getPtr (name, *cellstore, true); + Ptr ptr = mCells.getPtr (lowerCaseName, *cellstore, true); if (!ptr.isEmpty()) return ptr; @@ -511,7 +514,7 @@ namespace MWWorld if (!activeOnly) { - Ptr ptr = mCells.getPtr (name); + Ptr ptr = mCells.getPtr (lowerCaseName); if (!ptr.isEmpty()) return ptr; @@ -2317,4 +2320,30 @@ namespace MWWorld // No luck :( return false; } + + void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos) + { + MWWorld::Ptr closestMarker; + float closestDistance = FLT_MAX; + + std::vector markers; + mCells.getExteriorPtrs(id, markers); + + for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) + { + ESM::Position pos = it->getRefData().getPosition(); + Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + float distance = worldPos.squaredDistance(markerPos); + if (distance < closestDistance) + { + closestDistance = distance; + closestMarker = *it; + } + + } + + MWWorld::ActionTeleport action("", closestMarker.getRefData().getPosition()); + action.execute(ptr); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fad683d18c..2ec5e42f0d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -520,6 +520,12 @@ namespace MWWorld virtual bool isDark() const; virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result); + + /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) + /// closest to \a worldPos. + /// @note id must be lower case + virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, + const std::string& id, Ogre::Vector3 worldPos); }; } From b3cd10dbeaeebb27cc5af27dda5bfac47fb46dee Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 15:18:25 +0100 Subject: [PATCH 084/251] Remove redundant setTeleported calls --- apps/openmw/mwmechanics/spellcasting.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 633771b393..c9750cd4ca 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -250,14 +250,10 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", worldPos); } From 69ba8a40bf0f6fe8642a8c2ac2510e88c2f5e60d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 16:13:20 +0100 Subject: [PATCH 085/251] Fix weird formatting added during the merge --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index c9750cd4ca..7db8f5367e 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -250,7 +250,7 @@ namespace MWMechanics if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { From 722469d57b12fe48c83cdbd40c128720278163e5 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 1 Jan 2014 16:45:39 +0100 Subject: [PATCH 086/251] Correction to the e9844e1 commit. Restored interrior check in WeatherManager::update(). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/weather.cpp | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 124fc14ab2..06c2e2a36c 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -324,6 +324,17 @@ void WeatherManager::update(float duration) mWeatherUpdateTime -= timePassed; + MWBase::World* world = MWBase::Environment::get().getWorld(); + const bool exterior = (world->isCellExterior() || world->isCellQuasiExterior()); + if (!exterior) + { + mRendering->sunDisable(false); + mRendering->skyDisable(); + mRendering->getSkyManager()->setLightningStrength(0.f); + stopSounds(true); + return; + } + switchToNextWeather(false); if (mNextWeather != "") @@ -676,41 +687,33 @@ float WeatherManager::getWindSpeed() const void WeatherManager::switchToNextWeather(bool instantly) { MWBase::World* world = MWBase::Environment::get().getWorld(); - const bool exterior = (world->isCellExterior() || world->isCellQuasiExterior()); - if (!exterior) + if (world->isCellExterior() || world->isCellQuasiExterior()) { - mRendering->sunDisable(false); - mRendering->skyDisable(); - mRendering->getSkyManager()->setLightningStrength(0.f); - stopSounds(true); - return; - } + std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); - // Exterior - std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); - - if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) - { - mCurrentRegion = regionstr; - mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - - std::string weatherType = "clear"; - - if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { - weatherType = mRegionOverrides[regionstr]; - } - else - { - // get weather probabilities for the current region - const ESM::Region *region = world->getStore().get().search (regionstr); + mCurrentRegion = regionstr; + mWeatherUpdateTime = mHoursBetweenWeatherChanges * 3600; - if (region != 0) + std::string weatherType = "clear"; + + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) { - weatherType = nextWeather(region); + weatherType = mRegionOverrides[regionstr]; } - } + else + { + // get weather probabilities for the current region + const ESM::Region *region = world->getStore().get().search (regionstr); - setWeather(weatherType, instantly); + if (region != 0) + { + weatherType = nextWeather(region); + } + } + + setWeather(weatherType, instantly); + } } } From 2d4e06cd5021f18bc143067ff7c2680fb599875e Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 1 Jan 2014 17:05:49 +0100 Subject: [PATCH 087/251] Updated comments about freeing format_ctx->pb->buffer. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwrender/videoplayer.cpp | 15 +++++++++------ apps/openmw/mwsound/ffmpeg_decoder.cpp | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 74b52c06bd..adf20dc633 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -949,7 +949,9 @@ void VideoState::init(const std::string& resourceName) this->format_ctx->pb = ioCtx; // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? + /// + /// format_ctx->pb->buffer must be freed by hand, + /// if not, valgrind will show memleak, see: /// /// https://trac.ffmpeg.org/ticket/1357 /// @@ -1023,11 +1025,12 @@ void VideoState::deinit() if(this->format_ctx) { - // valgrind shows memleak near format_ctx->pb - // - // As scrawl pointed, memleak could be related to this ffmpeg ticket: - // https://trac.ffmpeg.org/ticket/1357 - // + /// + /// format_ctx->pb->buffer must be freed by hand, + /// if not, valgrind will show memleak, see: + /// + /// https://trac.ffmpeg.org/ticket/1357 + /// if (this->format_ctx->pb != NULL) { av_free(this->format_ctx->pb->buffer); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 13d11f6918..c836974425 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -231,9 +231,9 @@ void FFmpeg_Decoder::close() { if (mFormatCtx->pb != NULL) { - // valgrind shows memleak near mFormatCtx->pb + // mFormatCtx->pb->buffer must be freed by hand, + // if not, valgrind will show memleak, see: // - // As scrawl pointed, memleak could be related to this ffmpeg ticket: // https://trac.ffmpeg.org/ticket/1357 // if (mFormatCtx->pb->buffer != NULL) From 8e5cae1081ca926c5f7157f07457c0a4046eed54 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 17:06:21 +0100 Subject: [PATCH 088/251] Implement mark/recall magic effects --- apps/openmw/mwmechanics/spellcasting.cpp | 15 ++++++++++++--- apps/openmw/mwworld/player.cpp | 16 +++++++++++++++- apps/openmw/mwworld/player.hpp | 8 ++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7db8f5367e..0b897c165c 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -8,6 +8,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/actionteleport.hpp" #include "../mwrender/animation.hpp" @@ -259,13 +260,21 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::Mark) { - // TODO + MWBase::Environment::get().getWorld()->getPlayer().markPosition( + target.getCell(), target.getRefData().getPosition()); } else if (effectId == ESM::MagicEffect::Recall) { - MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); + MWWorld::CellStore* markedCell = NULL; + ESM::Position markedPosition; - // TODO + MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); + if (markedCell) + { + MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->mCell->mName, + markedPosition); + action.execute(target); + } } } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 8be6a4d983..c594454028 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -21,7 +21,8 @@ namespace MWWorld mLastKnownExteriorPosition(0,0,0), mAutoMove(false), mForwardBackward (0), - mTeleported(false) + mTeleported(false), + mMarkedCell(NULL) { mPlayer.mBase = player; mPlayer.mRef.mRefID = "player"; @@ -157,4 +158,17 @@ namespace MWWorld { mTeleported = teleported; } + + void Player::markPosition(CellStore *markedCell, ESM::Position markedPosition) + { + mMarkedCell = markedCell; + mMarkedPosition = markedPosition; + } + + void Player::getMarkedPosition(CellStore*& markedCell, ESM::Position &markedPosition) const + { + markedCell = mMarkedCell; + if (mMarkedCell) + markedPosition = mMarkedPosition; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 48f5d06c11..1df848111b 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -32,6 +32,10 @@ namespace MWWorld Ogre::Vector3 mLastKnownExteriorPosition; + ESM::Position mMarkedPosition; + // If no position was marked, this is NULL + CellStore* mMarkedCell; + bool mAutoMove; int mForwardBackward; bool mTeleported; @@ -39,6 +43,10 @@ namespace MWWorld Player(const ESM::NPC *player, const MWBase::World& world); + // For mark/recall magic effects + void markPosition (CellStore* markedCell, ESM::Position markedPosition); + void getMarkedPosition (CellStore*& markedCell, ESM::Position& markedPosition) const; + /// Interiors can not always be mapped to a world position. However /// world position is still required for divine / almsivi magic effects /// and the player arrow on the global map. From eab7ffd6b408995594896d7d33142240faa4dc49 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 18:05:28 +0100 Subject: [PATCH 089/251] Remove redundant finding of default exterior position height --- apps/openmw/mwworld/worldimp.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index db58a1280c..5d7e9deebb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1940,17 +1940,8 @@ namespace MWWorld int y = ext->getGridY(); indexToPosition(x, y, pos.pos[0], pos.pos[1], true); - ESM::Land* land = getStore().get().search(x, y); - if (land) { - if (!land->isDataLoaded(ESM::Land::DATA_VHGT)) { - land->loadData(ESM::Land::DATA_VHGT); - } - pos.pos[2] = land->mLandData->mHeights[ESM::Land::LAND_NUM_VERTS / 2 + 1]; - } - else { - std::cerr << "Land data for cell at (" << x << ", " << y << ") not found\n"; - pos.pos[2] = 0; - } + // Note: Z pos will be adjusted by adjustPosition later + pos.pos[2] = 0; return true; } From 43cbff6333d3a838b15bed3ceed1a07d63efe287 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 1 Jan 2014 18:32:55 +0100 Subject: [PATCH 090/251] Bumped libogre to v1.9 in travis configuration file. Signed-off-by: Lukasz Gromanowski --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 112cf94f16..04d019c0d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_install: - sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev - sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev - sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev - - sudo apt-get install -qq libbullet-dev libogre-1.8-dev libmygui-dev libsdl2-dev libunshield-dev + - sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev - sudo mkdir /usr/src/gtest/build - cd /usr/src/gtest/build - sudo cmake .. -DBUILD_SHARED_LIBS=1 From 09a0a69b04cf80645dc1bb708f19b11c2bdb715f Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 1 Jan 2014 21:40:31 +0200 Subject: [PATCH 091/251] more improvements --- apps/openmw/mwmechanics/character.cpp | 159 +++++++++++++++++--------- apps/openmw/mwmechanics/character.hpp | 3 + apps/openmw/mwrender/animation.cpp | 15 ++- apps/openmw/mwrender/animation.hpp | 1 + 4 files changed, 124 insertions(+), 54 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a7ae298245..4a8cf5ea85 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -81,6 +81,7 @@ static const std::string sHitList[] = { "hit3" , "hit4" , "hit5" , + "knockdown" , }; static const int sHitListSize = sizeof(sHitList)/sizeof(sHitList[0]); @@ -161,23 +162,23 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat { if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) { - mHitState = CharState_Hit; MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); - if(!mAnimation->isPlaying(mCurrentHit)) + if(mHitState == CharState_None) { + mHitState = CharState_Hit; if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) - mCurrentHit = "knockdown"; + mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation else { - int iHit = rand() % sHitListSize; + int iHit = rand() % (sHitListSize-1); mCurrentHit = sHitList[iHit]; } mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } - else if(!mAnimation->isPlaying(mCurrentHit)) + else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) { mCurrentHit.erase(); mHitState = CharState_None; @@ -458,9 +459,27 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) } +void CharacterController::playWeaponAnim(const std::string& start, const std::string& stop, + float speed, bool autoDisable, bool disablePrevious, float startpoint, bool currentWeapon) +{ + std::string weapgroup; + if (currentWeapon) + weapgroup = mCurrentWeapon; + else + getWeaponGroup(mWeaponType, weapgroup); + + if (disablePrevious) + mAnimation->disable(weapgroup); + + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_All, autoDisable, + speed, start, stop, + startpoint, 0); +} + bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) { - const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); NpcStats &stats = cls.getNpcStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); @@ -490,6 +509,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { getWeaponGroup(weaptype, weapgroup); mAnimation->showWeapons(false); + mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "equip start", "equip stop", 0.0f, 0); @@ -642,7 +662,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_ThowWeapon) + { mAttackType = "shoot"; + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" start", mAttackType+" attach", + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToAttach; + } else { int attackType = stats.getAttackType(); @@ -655,13 +682,13 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mAttackType = "slash"; else mAttackType = "thrust"; - } mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; + } } } animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); @@ -711,79 +738,105 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mUpperBodyState == UpperCharState_CastingSpell) { mUpperBodyState = UpperCharState_WeapEquiped; - //don't allow to continue playing hit animation after actor had attacked during it - if(mHitState != CharState_None) + + if(mHitState != CharState_None) //don't allow to continue playing hit animation after actor had attacked during it { - mAnimation->disable(mCurrentHit); + mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody); mCurrentHit.clear(); mHitState = CharState_None; } } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) - mUpperBodyState = UpperCharState_Nothing; + mUpperBodyState = UpperCharState_Nothing; } else if(complete >= 1.0f) { - if(mUpperBodyState == UpperCharState_StartToMinAttack) + std::string start, stop; + switch(mUpperBodyState) { - mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min attack", mAttackType+" max attack", - 0.0f, 0); - mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + case UpperCharState_StartToMinAttack: + start = mAttackType+" min attack"; + stop = mAttackType+" max attack"; + mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + break; + case UpperCharState_StartToAttach: //only bows, crossbows, throwing weapons here + start = mAttackType+" attach"; + stop = mAttackType+" min attack"; + mUpperBodyState = UpperCharState_StartToMinAttack; + break; + case UpperCharState_MaxAttackToMinHit: + if(mAttackType == "shoot") + { + start = mAttackType+" min hit"; + stop = mAttackType+" release"; + } + else + { + start = mAttackType+" min hit"; + stop = mAttackType+" hit"; + } + mUpperBodyState = UpperCharState_MinHitToHit; + break; + case UpperCharState_MinHitToHit: + if(mAttackType == "shoot") + { + start = mAttackType+" follow start"; + stop = mAttackType+" follow stop"; + } + else + { + float str = stats.getAttackStrength(); + start = mAttackType+((str < 0.5f) ? " small follow start" + : (str < 1.0f) ? " medium follow start" + : " large follow start"); + stop = mAttackType+((str < 0.5f) ? " small follow stop" + : (str < 1.0f) ? " medium follow stop" + : " large follow stop"); + } + mUpperBodyState = UpperCharState_FollowStartToFollowStop; + break; + default: + break; } - else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit) + + if(!start.empty()) { mAnimation->disable(mCurrentWeapon); - if(mAttackType == "shoot") - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min hit", mAttackType+" follow start", - 0.0f, 0); - else - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min hit", mAttackType+" hit", - 0.0f, 0); - mUpperBodyState = UpperCharState_MinHitToHit; - } - else if(mUpperBodyState == UpperCharState_MinHitToHit) - { - mAnimation->disable(mCurrentWeapon); - if(mAttackType == "shoot") - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - weapSpeed, mAttackType+" follow start", mAttackType+" follow stop", - 0.0f, 0); - else - { - float str = stats.getAttackStrength(); - std::string start = mAttackType+((str < 0.5f) ? " small follow start" - : (str < 1.0f) ? " medium follow start" - : " large follow start"); - std::string stop = mAttackType+((str < 0.5f) ? " small follow stop" - : (str < 1.0f) ? " medium follow stop" - : " large follow stop"); + if (mUpperBodyState == UpperCharState_FollowStartToFollowStop) mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, weapSpeed, start, stop, 0.0f, 0); - } - mUpperBodyState = UpperCharState_FollowStartToFollowStop; + else + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, start, stop, 0.0f, 0); } } + //if playing combat animation and lowerbody is not busy switch to whole body animation + if(weaptype != WeaponType::WeapType_None && complete>0.0f) + { + if( mMovementState != CharState_None || + mJumpState != JumpState_None || + mHitState != CharState_None || + MWBase::Environment::get().getWorld()->isSwimming(mPtr) || + cls.getStance(mPtr, MWWorld::Class::Sneak)) + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); + else + mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); + } + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand) { - mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, - false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, + false, 1.0f, "start", "stop", 0.0f, (~(size_t)0)); } else if (mAnimation->isPlaying("torch")) { - mAnimation->disable("torch"); + mAnimation->disable("torch"); } return forcestateupdate; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4974b7c08e..3cf34b747b 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -114,6 +114,7 @@ enum UpperBodyCharacterState { UpperCharState_UnEquipingWeap, UpperCharState_WeapEquiped, UpperCharState_StartToMinAttack, + UpperCharState_StartToAttach, UpperCharState_MinAttackToMaxAttack, UpperCharState_MaxAttackToMinHit, UpperCharState_MinHitToHit, @@ -175,6 +176,8 @@ class CharacterController void clearAnimQueue(); bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); + void playWeaponAnim(const std::string& start, const std::string& stop, + float speed = 1.0f, bool autoDisable = true, bool disablePrevious = false, float startpoint = 0.0f, bool currentWeapon = true); void updateVisibility(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index de86bcfa7a..c7537aa901 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -672,7 +672,20 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co MWBase::Environment::get().getWorld()->castSpell(mPtr); } - +void Animation::changeGroups(const std::string &groupname, int groups) +{ + AnimStateMap::iterator stateiter = mStates.begin(); + stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) + { + if(stateiter->second.mGroups != groups) + { + stateiter->second.mGroups = groups; + resetActiveGroups(); + } + return; + } +} void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops) { if(!mSkelBase || mAnimSources.empty()) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 72d1c100ef..f5f79dd72b 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -272,6 +272,7 @@ public: * \param groupname Animation group to disable. */ void disable(const std::string &groupname); + void changeGroups(const std::string &groupname, int group); /** Retrieves the velocity (in units per second) that the animation will move. */ float getVelocity(const std::string &groupname) const; From bfc9fcf2cdd5e405c08e3e1d475dfcf54b0bb978 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 21:20:37 +0100 Subject: [PATCH 092/251] Add starting spells when building player (F_PCStart flag) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c084c86e5e..7740240b6b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -12,6 +12,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "spellcasting.hpp" + namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -123,6 +125,19 @@ namespace MWMechanics npcStats.getSkill (index).setBase ( npcStats.getSkill (index).getBase() + bonus); } + + if (i==1) + { + // Major skill - add starting spells for this skill if existing + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::Store::iterator it = store.get().begin(); + for (; it != store.get().end(); ++it) + { + if (it->mData.mFlags & ESM::Spell::F_PCStart + && spellSchoolToSkill(getSpellSchool(&*it, ptr)) == index) + creatureStats.getSpells().add(it->mId); + } + } } } From 16e10cc702e8f133172a1bd3570ef2135505c376 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 1 Jan 2014 22:46:29 +0200 Subject: [PATCH 093/251] error fix --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4a8cf5ea85..aa1a812aa0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -814,7 +814,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } //if playing combat animation and lowerbody is not busy switch to whole body animation - if(weaptype != WeaponType::WeapType_None && complete>0.0f) + if(weaptype != WeapType_None && complete>0.0f) { if( mMovementState != CharState_None || mJumpState != JumpState_None || From 9245faf2aa882846bd62f72eb0d0e3fc3bacec66 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:19:02 +0100 Subject: [PATCH 094/251] Don't destroyRenderTarget with a NULL window --- libs/openengine/ogre/renderer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 07bc8f3c90..9e5ec5414d 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -25,7 +25,8 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; - Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + if (mWindow) + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); mWindow = NULL; delete mOgreInit; From 57296722624b58837858424caa1b74975a2d9644 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:37:52 +0100 Subject: [PATCH 095/251] Show marked position on map. Implement Detect X magic effects. --- apps/openmw/mwbase/world.hpp | 14 +- apps/openmw/mwclass/misc.cpp | 7 + apps/openmw/mwclass/misc.hpp | 2 + apps/openmw/mwgui/mapwindow.cpp | 262 +++++++++++++++++++-------- apps/openmw/mwgui/mapwindow.hpp | 7 + apps/openmw/mwrender/localmap.cpp | 102 +++++------ apps/openmw/mwworld/cellfunctors.hpp | 8 +- apps/openmw/mwworld/cellstore.hpp | 2 +- apps/openmw/mwworld/class.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 93 +++++++++- apps/openmw/mwworld/worldimp.hpp | 8 +- 11 files changed, 362 insertions(+), 145 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d9640f4d22..3a88971149 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -130,7 +130,7 @@ namespace MWBase virtual Ogre::Vector2 getNorthVector (MWWorld::CellStore* cell) = 0; ///< get north vector (OGRE coordinates) for given interior cell - virtual std::vector getDoorMarkers (MWWorld::CellStore* cell) = 0; + virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out) = 0; ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) = 0; @@ -438,6 +438,18 @@ namespace MWBase /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id, Ogre::Vector3 worldPos) = 0; + + enum DetectionType + { + Detect_Enchantment, + Detect_Key, + Detect_Creature + }; + /// List all references (filtered by \a type) detected by \a ptr. The range + /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. + /// @note This also works for references in containers. + virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, + DetectionType type) = 0; }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1a40c45554..5e216fed4a 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -252,4 +252,11 @@ namespace MWClass return ref->mBase->mData.mWeight; } + bool Miscellaneous::isKey(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + return ref->mBase->mData.mIsKey; + } + } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 16a8e8c055..16e9ca10b0 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -57,6 +57,8 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + + virtual bool isKey (const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 00380aefd7..2f34df2360 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -103,27 +103,80 @@ namespace MWGui void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) { + // Workaround to not make the marker visible if it's under fog of war applyFogOfWar (); } void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) { + // Workaround to not make the marker visible if it's under fog of war applyFogOfWar (); } + MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerPosition& markerPos) + { + MyGUI::IntPoint widgetPos; + // normalized cell coordinates + float nX,nY; + + markerPos.interior = mInterior; + + if (!mInterior) + { + int cellX, cellY; + MWBase::Environment::get().getWorld()->positionToIndex(worldX, worldY, cellX, cellY); + const int cellSize = 8192; + nX = (worldX - cellSize * cellX) / cellSize; + // Image space is -Y up, cells are Y up + nY = 1 - (worldY - cellSize * cellY) / cellSize; + + float cellDx = cellX - mCurX; + float cellDy = cellY - mCurY; + + markerPos.cellX = cellX; + markerPos.cellY = cellY; + + widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellDx) * 512, + nY * 512 - (cellDy-1) * 512); + } + else + { + int cellX, cellY; + Ogre::Vector2 worldPos (worldX, worldY); + MWBase::Environment::get().getWorld ()->getInteriorMapPosition (worldPos, nX, nY, cellX, cellY); + + markerPos.cellX = cellX; + markerPos.cellY = cellY; + + widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellX-mCurX) * 512, + nY * 512 + (1+cellY-mCurY) * 512); + } + + markerPos.nX = nX; + markerPos.nY = nY; + return widgetPos; + } + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { - if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell + if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) + return; // don't do anything if we're still in the same cell + + mCurX = x; + mCurY = y; + mInterior = interior; + mChanged = false; // clear all previous markers for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) { - if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + if (mLocalMap->getChildAt(i)->getName ().substr (0, 4) == "Door") { MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); } } + // Update the map textures for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) @@ -138,78 +191,57 @@ namespace MWGui box->setImageTexture(image); else box->setImageTexture("black.png"); - - - // door markers - - // interior map only consists of one cell, so handle the markers only once - if (interior && (mx != 2 || my != 2)) - continue; - - MWWorld::CellStore* cell; - if (interior) - cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); - else - cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); - - std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); - - for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) - { - MWBase::World::DoorMarker marker = *it; - - // convert world coordinates to normalized cell coordinates - MyGUI::IntCoord widgetCoord; - float nX,nY; - int cellDx, cellDy; - if (!interior) - { - const int cellSize = 8192; - - nX = (marker.x - cellSize * (x+mx-1)) / cellSize; - nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); - } - else - { - Ogre::Vector2 position (marker.x, marker.y); - MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); - - widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); - } - - static int counter = 0; - ++counter; - MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); - markerWidget->setImageResource("DoorMarker"); - markerWidget->setUserString("ToolTipType", "Layout"); - markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); - markerWidget->setUserString("Caption_TextOneLine", marker.name); - markerWidget->setUserString("IsMarker", "true"); - markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); - markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); - - MarkerPosition markerPos; - markerPos.interior = interior; - markerPos.cellX = interior ? cellDx : x + mx - 1; - markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); - markerPos.nX = nX; - markerPos.nY = nY; - - markerWidget->setUserData(markerPos); - } - - } } - mInterior = interior; - mCurX = x; - mCurY = y; - mChanged = false; - // fog of war + MWBase::World* world = MWBase::Environment::get().getWorld(); + + // Retrieve the door markers we want to show + std::vector doors; + if (interior) + { + MWWorld::CellStore* cell = world->getInterior (mPrefix); + world->getDoorMarkers(cell, doors); + } + else + { + for (int dX=-1; dX<2; ++dX) + { + for (int dY=-1; dY<2; ++dY) + { + MWWorld::CellStore* cell = world->getExterior (mCurX+dX, mCurY+dY); + world->getDoorMarkers(cell, doors); + } + } + } + + // Create a widget for each marker + int counter = 0; + for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + { + MWBase::World::DoorMarker marker = *it; + + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(marker.x, marker.y, markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + ++counter; + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); + markerWidget->setUserString("Caption_TextOneLine", marker.name); + markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); + markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); + // Used by tooltips to not show the tooltip if marker is hidden by fog of war + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + } + + updateMarkers(); + applyFogOfWar(); // set the compass texture again, because MyGUI determines sorting of ImageBox widgets @@ -222,6 +254,8 @@ namespace MWGui void LocalMapBase::setPlayerPos(const float x, const float y) { + updateMarkers(); + if (x == mLastPositionX && y == mLastPositionY) return; @@ -255,6 +289,88 @@ namespace MWGui mLastDirectionY = y; } + void LocalMapBase::addDetectionMarkers(int type) + { + std::vector markers; + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->listDetectedReferences( + world->getPlayer().getPlayer(), + markers, MWBase::World::DetectionType(type)); + if (markers.empty()) + return; + + std::string markerTexture; + MyGUI::Colour markerColour; + if (type == MWBase::World::Detect_Creature) + { + markerTexture = "textures\\menu_map_dcreature.dds"; + markerColour = MyGUI::Colour(1,0,0,1); + } + if (type == MWBase::World::Detect_Key) + { + markerTexture = "textures\\menu_map_dkey.dds"; + markerColour = MyGUI::Colour(0,1,0,1); + } + if (type == MWBase::World::Detect_Enchantment) + { + markerTexture = "textures\\menu_map_dmagic.dds"; + markerColour = MyGUI::Colour(0,0,1,1); + } + + int counter = 0; + for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) + { + const ESM::Position& worldPos = it->getRefData().getPosition(); + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(worldPos.pos[0], worldPos.pos[1], markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + ++counter; + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); + markerWidget->setImageTexture(markerTexture); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + markerWidget->setColour(markerColour); + } + } + + void LocalMapBase::updateMarkers() + { + // clear all previous markers + for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) + { + if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + { + MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); + } + } + + addDetectionMarkers(MWBase::World::Detect_Creature); + addDetectionMarkers(MWBase::World::Detect_Key); + addDetectionMarkers(MWBase::World::Detect_Enchantment); + + // Add marker for the spot marked with Mark magic effect + MWWorld::CellStore* markedCell = NULL; + ESM::Position markedPosition; + MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); + if (markedCell && markedCell->isExterior() == !mInterior + && (!mInterior || Misc::StringUtils::ciEqual(markedCell->mCell->mName, mPrefix))) + { + MarkerPosition markerPos; + MyGUI::IntPoint widgetPos = getMarkerPosition(markedPosition.pos[0], markedPosition.pos[1], markerPos); + MyGUI::IntCoord widgetCoord(widgetPos.left - 4, + widgetPos.top - 4, + 8, 8); + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "MarkerMarked"); + markerWidget->setImageTexture("textures\\menu_map_smark.dds"); + markerWidget->setUserString("IsMarker", "true"); + markerWidget->setUserData(markerPos); + } + } + // ------------------------------------------------------------------------------------------ MapWindow::MapWindow(const std::string& cacheDir) @@ -319,7 +435,7 @@ namespace MWGui static int _counter=0; MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", - widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); + widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(_counter)); markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); @@ -385,7 +501,7 @@ namespace MWGui for (unsigned int i=0; igetChildCount (); ++i) { - if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") + if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door") mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 2c90c422e6..7df2105dcf 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -55,9 +55,16 @@ namespace MWGui void onMarkerFocused(MyGUI::Widget* w1, MyGUI::Widget* w2); void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + MyGUI::IntPoint getMarkerPosition (float worldX, float worldY, MarkerPosition& markerPos); + virtual void notifyPlayerUpdate() {} virtual void notifyMapChanged() {} + // Update markers (Detect X effects, Mark/Recall effects) + // Note, door markers handled in setActiveCell + void updateMarkers(); + void addDetectionMarkers(int type); + OEngine::GUI::Layout* mLayout; bool mMapDragAndDrop; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 5f41289788..3ea3380e85 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -225,64 +225,54 @@ void LocalMap::render(const float x, const float y, tex = TextureManager::getSingleton().getByName(texture); if (tex.isNull()) { - // try loading from disk - //if (boost::filesystem::exists(texture+".jpg")) - //{ - /// \todo - //} - //else + // render + tex = TextureManager::getSingleton().createManual( + texture, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sMapResolution/sSize, yw*sMapResolution/sSize, + 0, + PF_R8G8B8, + TU_RENDERTARGET); + + RenderTarget* rtt = tex->getBuffer()->getRenderTarget(); + + rtt->setAutoUpdated(false); + Viewport* vp = rtt->addViewport(mCellCamera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setBackgroundColour(ColourValue(0, 0, 0)); + vp->setVisibilityMask(RV_Map); + vp->setMaterialScheme("local_map"); + + rtt->update(); + + // create "fog of war" texture + TexturePtr tex2 = TextureManager::getSingleton().createManual( + texture + "_fog", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize, + 0, + PF_A8R8G8B8, + TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + + // create a buffer to use for dynamic operations + std::vector buffer; + buffer.resize(sFogOfWarResolution*sFogOfWarResolution); + + // initialize to (0, 0, 0, 1) + for (int p=0; pgetBuffer()->getRenderTarget(); - - rtt->setAutoUpdated(false); - Viewport* vp = rtt->addViewport(mCellCamera); - vp->setOverlaysEnabled(false); - vp->setShadowsEnabled(false); - vp->setBackgroundColour(ColourValue(0, 0, 0)); - vp->setVisibilityMask(RV_Map); - vp->setMaterialScheme("local_map"); - - rtt->update(); - - // create "fog of war" texture - TexturePtr tex2 = TextureManager::getSingleton().createManual( - texture + "_fog", - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize, - 0, - PF_A8R8G8B8, - TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - - // create a buffer to use for dynamic operations - std::vector buffer; - buffer.resize(sFogOfWarResolution*sFogOfWarResolution); - - // initialize to (0, 0, 0, 1) - for (int p=0; pgetBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); - tex2->getBuffer()->unlock(); - - mBuffers[texture] = buffer; - - // save to cache for next time - //rtt->writeContentsToFile("./" + texture + ".jpg"); + buffer[p] = (255 << 24); } + + memcpy(tex2->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4); + tex2->getBuffer()->unlock(); + + mBuffers[texture] = buffer; } + mRenderingManager->enableLights(true); mLight->setVisible(false); @@ -339,8 +329,6 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).yAxis(); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); - if (!mInterior) { x = std::ceil(pos.x / sSize)-1; diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 4b1f70096a..5115fa02db 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -4,7 +4,7 @@ #include #include -#include "refdata.hpp" +#include "ptr.hpp" namespace ESM { @@ -18,13 +18,13 @@ namespace MWWorld { std::vector mHandles; - bool operator() (ESM::CellRef& ref, RefData& data) + bool operator() (MWWorld::Ptr ptr) { - Ogre::SceneNode* handle = data.getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); if (handle) mHandles.push_back (handle); - data.setBaseNode(0); + ptr.getRefData().setBaseNode(0); return true; } }; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8a01caf183..8731c42dc6 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -143,7 +143,7 @@ namespace MWWorld { if (!iter->mData.getCount()) continue; - if (!functor (iter->mRef, iter->mData)) + if (!functor (MWWorld::Ptr(&*iter, this))) return false; } return true; diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index cb6690a464..d737c18a22 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -290,6 +290,8 @@ namespace MWWorld virtual bool isPersistent (const MWWorld::Ptr& ptr) const; + virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5d7e9deebb..ff2ae7161b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1442,10 +1442,8 @@ namespace MWWorld return d; } - std::vector World::getDoorMarkers (CellStore* cell) + void World::getDoorMarkers (CellStore* cell, std::vector& out) { - std::vector result; - MWWorld::CellRefList& doors = cell->mDoors; CellRefList::List& refList = doors.mList; for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) @@ -1461,11 +1459,9 @@ namespace MWWorld newMarker.x = pos.pos[0]; newMarker.y = pos.pos[1]; - result.push_back(newMarker); + out.push_back(newMarker); } } - - return result; } void World::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) @@ -1829,9 +1825,9 @@ namespace MWWorld { std::vector mHandles; - bool operator() (ESM::CellRef& ref, RefData& data) + bool operator() (Ptr ptr) { - Ogre::SceneNode* handle = data.getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); if (handle) mHandles.push_back(handle->getName()); return true; @@ -2348,4 +2344,85 @@ namespace MWWorld mWeatherManager->update(duration); } + + struct AddDetectedReference + { + AddDetectedReference(std::vector& out, Ptr detector, World::DetectionType type, float squaredDist) + : mOut(out), mDetector(detector), mType(type), mSquaredDist(squaredDist) + { + } + + std::vector& mOut; + Ptr mDetector; + float mSquaredDist; + World::DetectionType mType; + bool operator() (MWWorld::Ptr ptr) + { + if (Ogre::Vector3(ptr.getRefData().getPosition().pos).squaredDistance( + Ogre::Vector3(mDetector.getRefData().getPosition().pos)) >= mSquaredDist) + return true; + + if (!ptr.getRefData().isEnabled()) + return true; + + // Consider references inside containers as well + if (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)) + { + mOut.push_back(ptr); + return true; + } + } + } + } + + if (needToAdd(ptr)) + mOut.push_back(ptr); + + return true; + } + + bool needToAdd (MWWorld::Ptr ptr) + { + if (mType == World::Detect_Creature && 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()) + return false; + return true; + } + }; + + void World::listDetectedReferences(const Ptr &ptr, std::vector &out, DetectionType type) + { + const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); + float dist=0; + if (type == World::Detect_Creature) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectAnimal)).mMagnitude; + else if (type == World::Detect_Key) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectKey)).mMagnitude; + else if (type == World::Detect_Enchantment) + dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectEnchantment)).mMagnitude; + + if (!dist) + return; + + // TODO: "1 foot" = 20 game units? + dist *= 20; + + AddDetectedReference functor (out, ptr, type, dist*dist); + + const Scene::CellStoreCollection& active = mWorldScene->getActiveCells(); + for (Scene::CellStoreCollection::const_iterator it = active.begin(); it != active.end(); ++it) + { + MWWorld::CellStore* cellStore = *it; + cellStore->forEach(functor); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index def8b2fa42..7e3b0befe2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -202,7 +202,7 @@ namespace MWWorld virtual Ogre::Vector2 getNorthVector (CellStore* cell); ///< get north vector (OGRE coordinates) for given interior cell - virtual std::vector getDoorMarkers (MWWorld::CellStore* cell); + virtual void getDoorMarkers (MWWorld::CellStore* cell, std::vector& out); ///< get a list of teleport door markers for a given cell, to be displayed on the local map virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); @@ -526,6 +526,12 @@ namespace MWWorld /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id, Ogre::Vector3 worldPos); + + /// List all references (filtered by \a type) detected by \a ptr. The range + /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. + /// @note This also works for references in containers. + virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, + DetectionType type); }; } From 531bef6193c1e875cb5e5861e758c3c57a251576 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 22:46:10 +0100 Subject: [PATCH 096/251] Shorter Vector3 initialisation --- apps/openmw/mwsound/soundmanagerimp.cpp | 6 +++--- apps/openmw/mwworld/worldimp.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 372be83938..2e52739ac5 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -252,7 +252,7 @@ namespace MWSound float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, 20.0f, 12750.0f, Play_Normal|Play_TypeVoice, 0); @@ -354,7 +354,7 @@ namespace MWSound float min, max; std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|type, offset); if((mode&Play_NoTrack)) @@ -584,7 +584,7 @@ namespace MWSound if(!ptr.isEmpty()) { const ESM::Position &pos = ptr.getRefData().getPosition(); - const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + const Ogre::Vector3 objpos(pos.pos); snditer->first->setPosition(objpos); } //update fade out diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ff2ae7161b..e6c095baa4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1063,7 +1063,7 @@ namespace MWWorld void World::adjustPosition(const Ptr &ptr) { - Ogre::Vector3 pos (ptr.getRefData().getPosition().pos[0], ptr.getRefData().getPosition().pos[1], ptr.getRefData().getPosition().pos[2]); + Ogre::Vector3 pos (ptr.getRefData().getPosition().pos); if(!ptr.getRefData().getBaseNode()) { @@ -1304,7 +1304,7 @@ namespace MWWorld if (mPlayer->getPlayer().getCell()->isExterior()) { ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); - mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2])); + mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos)); } } @@ -1584,7 +1584,7 @@ namespace MWWorld pos.rot[1] = 0; Ogre::Vector3 orig = - Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3(pos.pos); Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; @@ -2299,7 +2299,7 @@ namespace MWWorld if (ref.mRef.mTeleport && ref.mRef.mDestCell.empty()) { ESM::Position pos = ref.mRef.mDoorDest; - result = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + result = Ogre::Vector3(pos.pos); return true; } } @@ -2320,7 +2320,7 @@ namespace MWWorld for (std::vector::iterator it = markers.begin(); it != markers.end(); ++it) { ESM::Position pos = it->getRefData().getPosition(); - Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos); float distance = worldPos.squaredDistance(markerPos); if (distance < closestDistance) { From f6387d5979a9d1571f61f4b217aff09c6b7ab09a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:30:58 +0100 Subject: [PATCH 097/251] Implement Telekinesis magic effect. Remove some duplicate code. --- apps/openmw/mwworld/worldimp.cpp | 39 +++++++++----------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e6c095baa4..d2e8a21b79 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -796,32 +796,9 @@ namespace MWWorld MWWorld::Ptr World::getFacedObject() { - std::pair result; - - if (!mRendering->occlusionQuerySupported()) - result = mPhysics->getFacedHandle (getMaxActivationDistance ()); - else - result = std::make_pair (mFacedDistance, mFacedHandle); - - if (result.second.empty()) - return MWWorld::Ptr (); - - MWWorld::Ptr object = searchPtrViaHandle (result.second); - if (object.isEmpty()) - return object; - float ActivationDistance; - - if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) - ActivationDistance = getObjectActivationDistance ()*50; - else if (object.getTypeName ().find("NPC") != std::string::npos) - ActivationDistance = getNpcActivationDistance (); - else - ActivationDistance = getObjectActivationDistance (); - - if (result.first > ActivationDistance) - return MWWorld::Ptr (); - - return object; + if (mFacedHandle.empty()) + return MWWorld::Ptr(); + return searchPtrViaHandle(mFacedHandle); } std::pair World::getHitContact(const MWWorld::Ptr &ptr, float distance) @@ -1346,6 +1323,12 @@ namespace MWWorld void World::updateFacedHandle () { + float telekinesisRangeBonus = + mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() + .get(ESM::MagicEffect::Telekinesis).mMagnitude * 22; + + float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + // send new query // figure out which object we want to test against std::vector < std::pair < float, std::string > > results; @@ -1353,13 +1336,13 @@ namespace MWWorld { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()); + results = mPhysics->getFacedHandles(x, y, activationDistance); if (MWBase::Environment::get().getWindowManager()->isConsoleMode()) results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()*50); } else { - results = mPhysics->getFacedHandles(getMaxActivationDistance ()); + results = mPhysics->getFacedHandles(activationDistance); } // ignore the player and other things we're not interested in From 24aa743573464fabaf20db7b15c3f7c6bf18bbfe Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:34:18 +0100 Subject: [PATCH 098/251] Add function for converting feet to game units --- apps/openmw/mwworld/worldimp.cpp | 13 ++++++++++--- apps/openmw/mwworld/worldimp.hpp | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d2e8a21b79..c11de7753c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1325,7 +1325,8 @@ namespace MWWorld { float telekinesisRangeBonus = mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).getMagicEffects() - .get(ESM::MagicEffect::Telekinesis).mMagnitude * 22; + .get(ESM::MagicEffect::Telekinesis).mMagnitude; + telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; @@ -2396,8 +2397,7 @@ namespace MWWorld if (!dist) return; - // TODO: "1 foot" = 20 game units? - dist *= 20; + dist = feetToGameUnits(dist); AddDetectedReference functor (out, ptr, type, dist*dist); @@ -2408,4 +2408,11 @@ namespace MWWorld cellStore->forEach(functor); } } + + float World::feetToGameUnits(float feet) + { + // Looks like there is no GMST for this. This factor was determined in experiments + // with the Telekinesis effect. + return feet * 22; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7e3b0befe2..1aecb6fb64 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -155,6 +155,8 @@ namespace MWWorld /// Called when \a object is moved to an inactive cell void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr); + float feetToGameUnits(float feet); + public: World (OEngine::Render::OgreRenderer& renderer, From c6421276bdc5dc2a25b9f45de5283f4f3f688ced Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Jan 2014 23:59:17 +0100 Subject: [PATCH 099/251] Closes #841: Correct activation distance in third person mode --- apps/openmw/mwrender/camera.cpp | 5 +++++ apps/openmw/mwrender/camera.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 1 + 5 files changed, 14 insertions(+) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 8f54be3f88..b2ec9884dc 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -278,6 +278,11 @@ namespace MWRender } } + float Camera::getCameraDistance() const + { + return mCamera->getPosition().z; + } + void Camera::setCameraDistance(float dist, bool adjust, bool override) { if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 87e4866296..d31d9e56c0 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -105,6 +105,8 @@ namespace MWRender /// Restore default camera distance for current mode. void setCameraDistance(); + float getCameraDistance() const; + void setAnimation(NpcAnimation *anim); /// Stores focal and camera world positions in passed arguments diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b739a9f952..f6e69b7260 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1026,4 +1026,9 @@ void RenderingManager::enableTerrain(bool enable) mTerrain->setVisible(false); } +float RenderingManager::getCameraDistance() const +{ + return mCamera->getCameraDistance(); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index abc8fd71a7..b13e546e83 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -90,6 +90,7 @@ public: bool vanityRotateCamera(const float *rot); void setCameraDistance(float dist, bool adjust = false, bool override = true); + float getCameraDistance() const; void setupPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c11de7753c..7152707807 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1329,6 +1329,7 @@ namespace MWWorld telekinesisRangeBonus = feetToGameUnits(telekinesisRangeBonus); float activationDistance = getMaxActivationDistance() + telekinesisRangeBonus; + activationDistance += mRendering->getCameraDistance(); // send new query // figure out which object we want to test against From 899214a906a7330b92c9fb6ddf3b7bee50143820 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 00:13:23 +0100 Subject: [PATCH 100/251] Use fVanityDelay --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ffb2af81e0..1ad4095155 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -831,9 +831,11 @@ namespace MWInput void InputManager::updateIdleTime(float dt) { + static const float vanityDelay = MWBase::Environment::get().getWorld()->getStore().get() + .find("fVanityDelay")->getFloat(); if (mTimeIdle >= 0.f) mTimeIdle += dt; - if (mTimeIdle > 30.f) { + if (mTimeIdle > vanityDelay) { MWBase::Environment::get().getWorld()->toggleVanityMode(true); mTimeIdle = -1.f; } From 590c8cb4a0c6bc1dc55f88c20a29c8f9453f757b Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 01:03:44 +0100 Subject: [PATCH 101/251] Implement Disintegrate effects. When an armor/weapon breaks, unequip it and do not allow equipping it again. --- apps/openmw/mwclass/armor.cpp | 3 ++ apps/openmw/mwclass/npc.cpp | 10 +++++ apps/openmw/mwclass/weapon.cpp | 3 ++ apps/openmw/mwmechanics/actors.cpp | 65 ++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index c7c80ec2e3..0fae1b05c3 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -291,6 +291,9 @@ namespace MWClass { MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + if (ptr.getCellRef().mCharge == 0) + return std::make_pair(0, "#{sInventoryMessage1}"); + // slots that this item can be equipped in std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4187aa8c1f..2ba0566dd9 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -493,6 +493,11 @@ namespace MWClass if (!MWBase::Environment::get().getWorld()->getGodModeState()) weapon.getCellRef().mCharge -= std::min(std::max(1, (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); + + // Weapon broken? unequip it + if (weapon.getCellRef().mCharge == 0) + weapon = *inv.unequipItem(weapon, ptr); + } healthdmg = true; } @@ -644,6 +649,11 @@ namespace MWClass armorref.mCharge = armor.get()->mBase->mData.mHealth; armorref.mCharge -= std::min(std::max(1, (int)damagediff), armorref.mCharge); + + // Armor broken? unequip it + if (armorref.mCharge == 0) + inv.unequipItem(armor, ptr); + switch(get(armor).getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 7b0a5eb471..5e93e0d81b 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -388,6 +388,9 @@ namespace MWClass std::pair Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const { + if (ptr.getCellRef().mCharge == 0) + return std::make_pair(0, "#{sInventoryMessage1}"); + std::pair, bool> slots_ = MWWorld::Class::get(ptr).getEquipmentSlots(ptr); if (slots_.first.empty()) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 62242ee8c9..13b5da11a3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -46,6 +46,44 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a } } +bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) +{ + // TODO: remove this check once creatures support inventory store + if (ptr.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); + MWWorld::ContainerStoreIterator item = + inv.getSlot(slot); + if (item != inv.end()) + { + if (!item->getClass().hasItemHealth(*item)) + return false; + if (item->getCellRef().mCharge == -1) + item->getCellRef().mCharge = item->getClass().getItemMaxHealth(*item); + + if (item->getCellRef().mCharge == 0) + return false; + + item->getCellRef().mCharge -= + std::min(disintegrate, + static_cast(item->getCellRef().mCharge)); + + if (item->getCellRef().mCharge == 0) + { + // Will unequip the broken item and try to find a replacement + if (ptr.getRefData().getHandle() != "player") + inv.autoEquip(ptr); + else + inv.unequipItem(*item, ptr); + } + + return true; + } + } + return true; +} + + } namespace MWMechanics @@ -241,6 +279,33 @@ namespace MWMechanics creatureStats.setDynamic(i, stat); } + // Apply disintegration (reduces item health) + float disintegrateWeapon = effects.get(EffectKey(ESM::MagicEffect::DisintegrateWeapon)).mMagnitude; + if (disintegrateWeapon > 0) + disintegrateSlot(ptr, MWWorld::InventoryStore::Slot_CarriedRight, disintegrateWeapon*duration); + float disintegrateArmor = effects.get(EffectKey(ESM::MagicEffect::DisintegrateArmor)).mMagnitude; + if (disintegrateArmor > 0) + { + // According to UESP + int priorities[] = { + MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, + MWWorld::InventoryStore::Slot_RightGauntlet, + MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots + }; + + for (unsigned int i=0; i Date: Thu, 2 Jan 2014 01:31:06 +0100 Subject: [PATCH 102/251] Some checks to prevent bound item abuse --- apps/openmw/mwgui/inventorywindow.cpp | 8 ++++++++ apps/openmw/mwgui/pickpocketitemmodel.cpp | 8 ++++++++ apps/openmw/mwgui/tradeitemmodel.cpp | 7 +++++++ apps/openmw/mwmechanics/actors.cpp | 5 +++++ components/esm/cellref.hpp | 4 ++-- 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ffd81e98b2..c14971f6e2 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -163,6 +163,14 @@ namespace MWGui MWWorld::Ptr object = item.mBase; int count = item.mCount; + // Bound items may not be moved + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); + return; + } + if (item.mType == ItemStack::Type_Equipped) { MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 16be5f6cca..13ee4396d0 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -40,6 +40,14 @@ namespace MWGui for (size_t i = 0; igetItemCount(); ++i) { const ItemStack& item = mSourceModel->getItem(i); + + // Bound items may not be stolen + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + continue; + } + if (std::find(mHiddenItems.begin(), mHiddenItems.end(), item) == mHiddenItems.end() && item.mType != ItemStack::Type_Equipped) mItems.push_back(item); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index e836355d3e..5c12843da0 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -154,6 +154,13 @@ namespace MWGui if(!MWWorld::Class::get(base).canSell(base, services)) continue; + // Bound items may not be bought + if (item.mBase.getCellRef().mRefID.size() > 6 + && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") + { + continue; + } + // don't show equipped items if(mMerchant.getTypeName() == typeid(ESM::NPC).name()) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 13b5da11a3..840167e3a6 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -695,6 +695,11 @@ namespace MWMechanics iter->second->kill(); + // Reset magic effects and recalculate derived effects + // One case where we need this is to make sure bound items are removed upon death + stats.setMagicEffects(MWMechanics::MagicEffects()); + calculateCreatureStatModifiers(iter->first, 0); + ++mDeathCount[cls.getId(iter->first)]; if(cls.isEssential(iter->first)) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 47cb0b99ed..5f1066cf8d 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -20,7 +20,7 @@ namespace ESM public: int mRefnum; // Reference number - std::string mRefID; // ID of object being referenced + std::string mRefID; // ID of object being referenced (must be lowercase) float mScale; // Scale applied to mesh @@ -89,4 +89,4 @@ namespace ESM }; } -#endif \ No newline at end of file +#endif From 5b0a4c94245cb632801e3798effc0d3998ff8202 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 01:42:30 +0100 Subject: [PATCH 103/251] Get rid of unused file and some other cruft. --- CMakeLists.txt | 2 - libs/openengine/bullet/CMotionState.cpp | 46 ---------------------- libs/openengine/bullet/CMotionState.h | 52 ------------------------- libs/openengine/bullet/physic.cpp | 10 +---- libs/openengine/bullet/physic.hpp | 17 -------- 5 files changed, 2 insertions(+), 125 deletions(-) delete mode 100644 libs/openengine/bullet/CMotionState.cpp delete mode 100644 libs/openengine/bullet/CMotionState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ee4a0246b2..64f8121c49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,8 +99,6 @@ set(OENGINE_BULLET ${LIBDIR}/openengine/bullet/BtOgreExtras.h ${LIBDIR}/openengine/bullet/BtOgreGP.h ${LIBDIR}/openengine/bullet/BtOgrePG.h - ${LIBDIR}/openengine/bullet/CMotionState.cpp - ${LIBDIR}/openengine/bullet/CMotionState.h ${LIBDIR}/openengine/bullet/physic.cpp ${LIBDIR}/openengine/bullet/physic.hpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp diff --git a/libs/openengine/bullet/CMotionState.cpp b/libs/openengine/bullet/CMotionState.cpp deleted file mode 100644 index c20415884a..0000000000 --- a/libs/openengine/bullet/CMotionState.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "CMotionState.h" -#include "physic.hpp" - -#include -#include -#include - -namespace OEngine { -namespace Physic -{ - - CMotionState::CMotionState(PhysicEngine* eng,std::string name) - : isPC(false) - , isNPC(true) - { - pEng = eng; - tr.setIdentity(); - pName = name; - } - - void CMotionState::getWorldTransform(btTransform &worldTrans) const - { - worldTrans = tr; - } - - void CMotionState::setWorldTransform(const btTransform &worldTrans) - { - tr = worldTrans; - - PhysicEvent evt; - evt.isNPC = isNPC; - evt.isPC = isPC; - evt.newTransform = tr; - evt.RigidBodyName = pName; - - if(isPC) - { - pEng->PEventList.push_back(evt); - } - else - { - pEng->NPEventList.push_back(evt); - } - } - -}} diff --git a/libs/openengine/bullet/CMotionState.h b/libs/openengine/bullet/CMotionState.h deleted file mode 100644 index 3508ab4ef1..0000000000 --- a/libs/openengine/bullet/CMotionState.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef OENGINE_CMOTIONSTATE_H -#define OENGINE_CMOTIONSTATE_H - -#include -#include - -namespace OEngine { -namespace Physic -{ - class PhysicEngine; - - /** - * A CMotionState is associated with a single RigidBody. - * When the RigidBody is moved by bullet, bullet will call the function setWorldTransform. - * for more info, see the bullet Wiki at btMotionState. - */ - class CMotionState:public btMotionState - { - public: - - CMotionState(PhysicEngine* eng,std::string name); - - /** - * Return the position of the RigidBody. - */ - virtual void getWorldTransform(btTransform &worldTrans) const; - - /** - * Function called by bullet when the RigidBody is moved. - * It add an event to the EventList of the PhysicEngine class. - */ - virtual void setWorldTransform(const btTransform &worldTrans); - - protected: - PhysicEngine* pEng; - btTransform tr; - bool isNPC; - bool isPC; - - std::string pName; - }; - - struct PhysicEvent - { - bool isNPC; - bool isPC; - btTransform newTransform; - std::string RigidBodyName; - }; - -}} -#endif diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index e33edda183..4e80088bf7 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -3,7 +3,6 @@ #include #include #include -#include "CMotionState.h" #include "OgreRoot.h" #include "btKinematicCharacterController.h" #include "BtOgrePG.h" @@ -318,9 +317,7 @@ namespace Physic btVector3 scl(triSize, triSize, 1); hfShape->setLocalScaling(scl); - CMotionState* newMotionState = new CMotionState(this,name); - - btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,hfShape); + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,0,hfShape); RigidBody* body = new RigidBody(CI,name); body->getWorldTransform().setOrigin(btVector3( (x+0.5)*triSize*(sqrtVerts-1), (y+0.5)*triSize*(sqrtVerts-1), (maxh+minh)/2.f)); @@ -401,12 +398,9 @@ namespace Physic else shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale)); - //create the motionState - CMotionState* newMotionState = new CMotionState(this,name); - //create the real body btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo - (0,newMotionState, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); + (0,0, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); RigidBody* body = new RigidBody(CI,name); body->mPlaceable = placeable; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f28f95ccb8..6cd7244b85 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -38,7 +38,6 @@ namespace MWWorld namespace OEngine { namespace Physic { - class CMotionState; struct PhysicEvent; class PhysicEngine; class RigidBody; @@ -157,17 +156,7 @@ namespace Physic private: void disableCollisionBody(); void enableCollisionBody(); -public: -//HACK: in Visual Studio 2010 and presumably above, this structures alignment -// must be 16, but the built in operator new & delete don't properly -// perform this alignment. -#if _MSC_VER >= 1600 - void * operator new (size_t Size) { return _aligned_malloc (Size, 16); } - void operator delete (void * Data) { _aligned_free (Data); } -#endif - - private: OEngine::Physic::RigidBody* mBody; OEngine::Physic::RigidBody* mRaycastingBody; @@ -329,12 +318,6 @@ public: const btVector3 &origin, btCollisionObject *object); - //event list of non player object - std::list NPEventList; - - //event list affecting the player - std::list PEventList; - //Bullet Stuff btOverlappingPairCache* pairCache; btBroadphaseInterface* broadphase; From 0f2b2fabdb35125e85ec648ad334b6bae40a81fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 02:03:11 +0100 Subject: [PATCH 104/251] Implement water walking --- apps/openmw/mwworld/physicssystem.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 451e093aee..2a7f5948e4 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -577,10 +577,28 @@ namespace MWWorld float oldHeight = iter->first.getRefData().getPosition().pos[2]; + bool waterCollision = false; + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects() + .get(ESM::MagicEffect::WaterWalking).mMagnitude + && cell->hasWater() + && !world->isUnderwater(iter->first.getCell(), + Ogre::Vector3(iter->first.getRefData().getPosition().pos))) + waterCollision = true; + + btStaticPlaneShape planeShape(btVector3(0,0,1), waterlevel); + btCollisionObject object; + object.setCollisionShape(&planeShape); + + if (waterCollision) + mEngine->dynamicsWorld->addCollisionObject(&object); + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), waterlevel, mEngine); + if (waterCollision) + mEngine->dynamicsWorld->removeCollisionObject(&object); + float heightDiff = newpos.z - oldHeight; if (heightDiff < 0) From 993edf03844cbf5b614371b031daf55db57b9550 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 02:27:48 +0100 Subject: [PATCH 105/251] Bug #1063: Safety check in moveObject. Required when items in containers execute script commands like setAtStart etc. --- apps/openmw/mwworld/worldimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7152707807..d02e1022e7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -911,7 +911,7 @@ namespace MWWorld ptr.getRefData().setCount(0); } } - if (haveToMove) + if (haveToMove && ptr.getRefData().getBaseNode()) { mRendering->moveObject(ptr, vec); mPhysics->moveObject (ptr); From c558e12212ace15ec20097b1e365a9bdfcc052aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 03:06:48 +0100 Subject: [PATCH 106/251] Don't try to move objects that are not in a cell --- .../mwscript/transformationextensions.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 47a632ae98..e441809778 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -207,6 +207,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -275,6 +279,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -336,6 +344,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + if (ptr.getRefData().getHandle() == "player") { MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true); @@ -605,6 +617,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + + if (!ptr.isInCell()) + return; + ptr.getRefData().getLocalRotation().rot[0] = 0; ptr.getRefData().getLocalRotation().rot[1] = 0; ptr.getRefData().getLocalRotation().rot[2] = 0; @@ -624,6 +640,9 @@ namespace MWScript { const MWWorld::Ptr& ptr = R()(runtime); + if (!ptr.isInCell()) + return; + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); @@ -659,6 +678,9 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); + if (!ptr.isInCell()) + return; + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration()); From e6c0e187bcd1614294bdfac84e53f33b707aa878 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 15:42:57 +0100 Subject: [PATCH 107/251] Closes #1073: Check for all gold types in canSell. For containers, only gold_001 is relevant, but items in the world can be sold as well. --- apps/openmw/mwclass/misc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 5e216fed4a..d211891035 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -242,7 +242,11 @@ namespace MWClass item.get(); return !ref->mBase->mData.mIsKey && (npcServices & ESM::NPC::Misc) - && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_001"); + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_001") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_005") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_010") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_025") + && !Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_100"); } float Miscellaneous::getWeight(const MWWorld::Ptr &ptr) const From 0990ca4b75fa4dba73f72cbd14742415ee259ff4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 15:58:00 +0100 Subject: [PATCH 108/251] Closes #1072: Fix extra light being added multiple times when showCarriedLeft(true) is called repeatedly --- apps/openmw/mwrender/npcanimation.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ddbdde83a6..b1455f0dc6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -664,11 +664,12 @@ void NpcAnimation::showCarriedLeft(bool show) { Ogre::Vector3 glowColor = getEnchantmentColor(*iter); std::string mesh = MWWorld::Class::get(*iter).getModel(*iter); - addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor); - - if (iter->getTypeName() == typeid(ESM::Light).name()) - addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get()->mBase); + if (addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, + mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor)) + { + if (iter->getTypeName() == typeid(ESM::Light).name()) + addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get()->mBase); + } } else removeIndividualPart(ESM::PRT_Shield); From 12de351febd378fdb453f99bf0f49607898cce17 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 16:38:23 +0100 Subject: [PATCH 109/251] Check if levitation is enabled before levitating --- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwworld/worldimp.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 2ba0566dd9..f4e15423d3 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -848,7 +848,8 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0) + else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0 && + world->isLevitationEnabled()) { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d02e1022e7..148c8f3016 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1606,7 +1606,8 @@ namespace MWWorld return false; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); - if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0) + if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0 + && isLevitationEnabled()) return true; // TODO: Check if flying creature From 29acc3f72290a152be14ae6760b39f401dd9c81d Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 16:56:31 +0100 Subject: [PATCH 110/251] Fix particles being too small. Looks like this should actually be size*2. --- components/nifogre/ogrenifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 5f9674c91e..63e9057664 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -854,7 +854,7 @@ class NIFObjectLoader { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - partsys->setDefaultDimensions(partctrl->size, partctrl->size); + partsys->setDefaultDimensions(partctrl->size*2, partctrl->size*2); if(!partctrl->emitter.empty()) { From 94d2ec8e4e87c791462c6942fce88f81dae1aa43 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 20:02:28 +0100 Subject: [PATCH 111/251] Add missing spells update --- apps/openmw/mwmechanics/activespells.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index dc79901b0e..e9786fb431 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -172,6 +172,7 @@ namespace MWMechanics void ActiveSpells::purgeAll() { mSpells.clear(); + mSpellsChanged = true; } void ActiveSpells::purgeEffect(short effectId) @@ -187,6 +188,6 @@ namespace MWMechanics effectIt++; } } - + mSpellsChanged = true; } } From 596e0c8a499ea3e61f52b017ab5ffe83c4f7047a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 20:15:07 +0100 Subject: [PATCH 112/251] Correct Dispel effect (use magnitude as chance) --- apps/openmw/mwmechanics/activespells.cpp | 11 +++++++++-- apps/openmw/mwmechanics/activespells.hpp | 4 ++-- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index e9786fb431..05877e454f 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -169,9 +169,16 @@ namespace MWMechanics } } - void ActiveSpells::purgeAll() + void ActiveSpells::purgeAll(float chance) { - mSpells.clear(); + for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ) + { + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + mSpells.erase(it++); + else + ++it; + } mSpellsChanged = true; } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index b3f499c6b2..92faf790f2 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -82,8 +82,8 @@ namespace MWMechanics /// Remove all active effects with this id void purgeEffect (short effectId); - /// Remove all active effects - void purgeAll (); + /// Remove all active effects, if roll succeeds (for each effect) + void purgeAll (float chance); bool isSpellActive (std::string id) const; ///< case insensitive diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0b897c165c..735652163e 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -236,7 +236,7 @@ namespace MWMechanics else if (effectId == ESM::MagicEffect::CureCorprusDisease) target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease(); else if (effectId == ESM::MagicEffect::Dispel) - target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(); + target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); else if (effectId == ESM::MagicEffect::RemoveCurse) target.getClass().getCreatureStats(target).getSpells().purgeCurses(); From b017a3be3e4e88d8aea6150287893f3677d6fb69 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 2 Jan 2014 20:20:57 +0100 Subject: [PATCH 113/251] Bug #1074: Inventory paperdoll obscures armour rating Changed size of inventory paperdoll. Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwrender/characterpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 5e659ca1d7..a0ba01b37b 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -128,7 +128,7 @@ namespace MWRender InventoryPreview::InventoryPreview(MWWorld::Ptr character) - : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) + : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 62, -200), Ogre::Vector3(0, 62, 0)) , mSelectionBuffer(NULL) { } From 79e972bdb6b9d5545347a2ef8c2708adebaf34a8 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 2 Jan 2014 21:54:41 +0200 Subject: [PATCH 114/251] resolved accompanying minor problems --- apps/openmw/mwmechanics/character.cpp | 127 +++++++++++++------------- apps/openmw/mwmechanics/character.hpp | 8 +- 2 files changed, 67 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index aa1a812aa0..8e2e348b96 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -160,28 +160,34 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { - if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) + if(MWWorld::Class::get(mPtr).isActor()) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); - - if(mHitState == CharState_None) + if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) { - mHitState = CharState_Hit; - if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) - && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) - mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation - else + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); + + if(mHitState == CharState_None) { - int iHit = rand() % (sHitListSize-1); - mCurrentHit = sHitList[iHit]; + mHitState = CharState_Hit; + if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) + && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) + { + mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation + mHitState = CharState_KnockDown; + } + else + { + int iHit = rand() % (sHitListSize-1); + mCurrentHit = sHitList[iHit]; + } + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - } - else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) - { - mCurrentHit.erase(); - mHitState = CharState_None; + else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) + { + mCurrentHit.erase(); + mHitState = CharState_None; + } } const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); @@ -374,16 +380,24 @@ MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &s void CharacterController::playRandomDeath(float startpoint) { - if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) + if(MWWorld::Class::get(mPtr).isNpc()) { - mDeathState = CharState_SwimDeath; - mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr)) + { + mDeathState = CharState_SwimDeath; + mCurrentDeath = sDeathList[sDeathListSize-1]; //last in the list is 'swimdeath' + } + else + { + int num = rand() % (sDeathListSize-1); + mDeathState = static_cast(CharState_Death1 + num); + mCurrentDeath = sDeathList[num]; + } } else { - int num = rand() % (sDeathListSize-1); - mDeathState = static_cast(CharState_Death1 + num); - mCurrentDeath = sDeathList[num]; + mDeathState = CharState_Death1; + mCurrentDeath = "death1"; } mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, @@ -442,6 +456,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim } refreshCurrentAnims(mIdleState, mMovementState, true); + if(mDeathState != CharState_None) { playRandomDeath(1.0f); @@ -458,25 +473,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) mPtr = ptr; } - -void CharacterController::playWeaponAnim(const std::string& start, const std::string& stop, - float speed, bool autoDisable, bool disablePrevious, float startpoint, bool currentWeapon) -{ - std::string weapgroup; - if (currentWeapon) - weapgroup = mCurrentWeapon; - else - getWeaponGroup(mWeaponType, weapgroup); - - if (disablePrevious) - mAnimation->disable(weapgroup); - - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_All, autoDisable, - speed, start, stop, - startpoint, 0); -} - bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); @@ -662,14 +658,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_ThowWeapon) - { mAttackType = "shoot"; - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" start", mAttackType+" attach", - 0.0f, 0); - mUpperBodyState = UpperCharState_StartToAttach; - } else { int attackType = stats.getAttackType(); @@ -682,13 +671,13 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mAttackType = "slash"; else mAttackType = "thrust"; + } mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); - mUpperBodyState = UpperCharState_StartToMinAttack; - } + mUpperBodyState = UpperCharState_StartToMinAttack; } } animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); @@ -739,11 +728,13 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { mUpperBodyState = UpperCharState_WeapEquiped; - if(mHitState != CharState_None) //don't allow to continue playing hit animation after actor had attacked during it + //don't allow to continue playing hit animation on UpperBody after actor had attacked during it + if(mHitState != CharState_None) { mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody); - mCurrentHit.clear(); + //commenting out following 2 lines will give a bit different combat dynamics(slower) mHitState = CharState_None; + mCurrentHit.clear(); } } else if(mUpperBodyState == UpperCharState_UnEquipingWeap) @@ -759,11 +750,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; - case UpperCharState_StartToAttach: //only bows, crossbows, throwing weapons here - start = mAttackType+" attach"; - stop = mAttackType+" min attack"; - mUpperBodyState = UpperCharState_StartToMinAttack; - break; case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -813,17 +799,27 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } + if(!animPlaying) + { + mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); + } //if playing combat animation and lowerbody is not busy switch to whole body animation - if(weaptype != WeapType_None && complete>0.0f) + if(weaptype != WeapType_None && animPlaying) { if( mMovementState != CharState_None || mJumpState != JumpState_None || mHitState != CharState_None || MWBase::Environment::get().getWorld()->isSwimming(mPtr) || cls.getStance(mPtr, MWWorld::Class::Sneak)) + { + mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); + } else + { + mAnimation->setAccumulation(Ogre::Vector3(0.0f, 0.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); + } } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); @@ -1067,8 +1063,13 @@ void CharacterController::update(float duration) refreshCurrentAnims(idlestate, movestate, forcestateupdate); - rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); - world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); + if(mHitState != CharState_KnockDown) + { + rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); + world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); + } + else + world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); world->queueMovement(mPtr, vec); movement = vec; @@ -1166,7 +1167,7 @@ void CharacterController::forceStateUpdate() refreshCurrentAnims(mIdleState, mMovementState, true); if(mDeathState != CharState_None) { - playRandomDeath(0.0f); + playRandomDeath(); } } @@ -1175,7 +1176,7 @@ void CharacterController::kill() if(mDeathState != CharState_None) return; - playRandomDeath(0.0f); + playRandomDeath(); if(mAnimation) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 3cf34b747b..b7c29e9ef3 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -90,7 +90,8 @@ enum CharacterState { CharState_Death5, CharState_SwimDeath, - CharState_Hit + CharState_Hit, + CharState_KnockDown }; enum WeaponType { @@ -114,7 +115,6 @@ enum UpperBodyCharacterState { UpperCharState_UnEquipingWeap, UpperCharState_WeapEquiped, UpperCharState_StartToMinAttack, - UpperCharState_StartToAttach, UpperCharState_MinAttackToMaxAttack, UpperCharState_MaxAttackToMinHit, UpperCharState_MinHitToHit, @@ -176,12 +176,10 @@ class CharacterController void clearAnimQueue(); bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); - void playWeaponAnim(const std::string& start, const std::string& stop, - float speed = 1.0f, bool autoDisable = true, bool disablePrevious = false, float startpoint = 0.0f, bool currentWeapon = true); void updateVisibility(); - void playRandomDeath(float startpoint); + void playRandomDeath(float startpoint = 0.0f); public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); From 299690631f39104988c1ed2c1cedd8342dc5bd87 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 21:21:28 +0100 Subject: [PATCH 115/251] Implement SoulTrap magic effect --- apps/openmw/mwgui/spellicons.cpp | 3 +- apps/openmw/mwgui/spellicons.hpp | 3 +- apps/openmw/mwmechanics/activespells.cpp | 6 +- apps/openmw/mwmechanics/activespells.hpp | 8 ++- apps/openmw/mwmechanics/actors.cpp | 70 ++++++++++++++++++++++++ apps/openmw/mwmechanics/magiceffects.hpp | 3 +- apps/openmw/mwmechanics/repair.cpp | 14 +---- apps/openmw/mwmechanics/spellcasting.cpp | 6 +- apps/openmw/mwmechanics/spells.cpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- 10 files changed, 94 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index e93e96c4b2..891206532e 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -22,7 +22,8 @@ namespace MWGui { void EffectSourceVisitor::visit (MWMechanics::EffectKey key, - const std::string& sourceName, float magnitude, float remainingTime) + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime) { MagicEffectInfo newEffectSource; newEffectSource.mKey = key; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index a29e2a00ab..1bb80f3d4c 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -43,7 +43,8 @@ namespace MWGui std::map > mEffectSources; virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, float magnitude, float remainingTime = -1); + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime = -1); }; class SpellIcons diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 05877e454f..1cdb74407a 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -126,7 +126,8 @@ namespace MWMechanics return mSpells; } - void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, const std::string &displayName) + void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, + const std::string &displayName, const std::string& casterHandle) { bool exists = false; for (TContainer::const_iterator it = begin(); it != end(); ++it) @@ -139,6 +140,7 @@ namespace MWMechanics params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mEffects = effects; params.mDisplayName = displayName; + params.mCasterHandle = casterHandle; if (!exists || stack) mSpells.insert (std::make_pair(id, params)); @@ -164,7 +166,7 @@ namespace MWMechanics float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(effectIt->mKey, name, magnitude, remainingTime); + visitor.visit(effectIt->mKey, name, it->second.mCasterHandle, magnitude, remainingTime); } } } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 92faf790f2..57f224f6fd 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -37,8 +37,8 @@ namespace MWMechanics MWWorld::TimeStamp mTimeStamp; std::string mDisplayName; - // TODO: To handle CASTER_LINKED flag (spell is purged when caster dies), - // we should probably store a handle to the caster here. + // Handle to the caster that that inflicted this spell on us + std::string mCasterHandle; }; typedef std::multimap TContainer; @@ -76,8 +76,10 @@ namespace MWMechanics /// \param stack If false, the spell is not added if one with the same ID exists already. /// \param effects /// \param displayName Name for display in magic menu. + /// \param casterHandle /// - void addSpell (const std::string& id, bool stack, std::vector effects, const std::string& displayName); + void addSpell (const std::string& id, bool stack, std::vector effects, + const std::string& displayName, const std::string& casterHandle); /// Remove all active effects with this id void purgeEffect (short effectId); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 79dd985628..68804c1d75 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -88,6 +88,68 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) namespace MWMechanics { + + class SoulTrap : public MWMechanics::EffectSourceVisitor + { + MWWorld::Ptr mCreature; + MWWorld::Ptr mActor; + public: + SoulTrap(MWWorld::Ptr trappedCreature) + : mCreature(trappedCreature) {} + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime = -1) + { + if (key.mId != ESM::MagicEffect::Soultrap) + return; + if (magnitude <= 0) + return; + + MWBase::World* world = MWBase::Environment::get().getWorld(); + + MWWorld::Ptr caster = world->searchPtrViaHandle(casterHandle); + if (caster.isEmpty() || !caster.getClass().isActor()) + return; + + static const float fSoulgemMult = world->getStore().get().find("fSoulgemMult")->getFloat(); + + float creatureSoulValue = mCreature.get()->mBase->mData.mSoul; + + // Use the smallest soulgem that is large enough to hold the soul + MWWorld::ContainerStore& container = caster.getClass().getContainerStore(caster); + MWWorld::ContainerStoreIterator gem = container.end(); + float gemCapacity = std::numeric_limits().max(); + std::string soulgemFilter = "misc_soulgem"; // no other way to check for soulgems? :/ + for (MWWorld::ContainerStoreIterator it = container.begin(MWWorld::ContainerStore::Type_Miscellaneous); + it != container.end(); ++it) + { + const std::string& id = it->getCellRef().mRefID; + if (id.size() >= soulgemFilter.size() + && id.substr(0,soulgemFilter.size()) == soulgemFilter) + { + float thisGemCapacity = it->get()->mBase->mData.mValue * fSoulgemMult; + if (thisGemCapacity >= creatureSoulValue && thisGemCapacity < gemCapacity + && it->getCellRef().mSoul.empty()) + { + gem = it; + gemCapacity = thisGemCapacity; + } + } + } + + if (gem == container.end()) + return; + + // Set the soul on just one of the gems, not the whole stack + gem->getContainerStore()->unstack(*gem, caster); + gem->getCellRef().mSoul = mCreature.getCellRef().mRefID; + + if (caster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sSoultrapSuccess}"); + } + }; + void Actors::updateActor (const MWWorld::Ptr& ptr, float duration) { // magic effects @@ -705,6 +767,13 @@ namespace MWMechanics iter->second->kill(); + // Apply soultrap + if (iter->first.getTypeName() == typeid(ESM::Creature).name()) + { + SoulTrap soulTrap (iter->first); + stats.getActiveSpells().visitEffectSources(soulTrap); + } + // Reset magic effects and recalculate derived effects // One case where we need this is to make sure bound items are removed upon death stats.setMagicEffects(MWMechanics::MagicEffects()); @@ -714,6 +783,7 @@ namespace MWMechanics if(cls.isEssential(iter->first)) MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); + } } diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 2c1b363b7d..45abda21d9 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -56,7 +56,8 @@ namespace MWMechanics struct EffectSourceVisitor { virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, float magnitude, float remainingTime = -1) = 0; + const std::string& sourceName, const std::string& casterHandle, + float magnitude, float remainingTime = -1) = 0; }; /// \brief Effects currently affecting a NPC or creature diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 38b2a48d7b..5e8a46fd4a 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -24,21 +24,13 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWWorld::LiveCellRef *ref = mTool.get(); + // unstack tool if required + player.getClass().getContainerStore(player).unstack(mTool, player); + // reduce number of uses left int uses = (mTool.getCellRef().mCharge != -1) ? mTool.getCellRef().mCharge : ref->mBase->mData.mUses; mTool.getCellRef().mCharge = uses-1; - // unstack tool if required - if (mTool.getRefData().getCount() > 1 && uses == ref->mBase->mData.mUses) - { - MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); - MWWorld::ContainerStoreIterator it = store.add(mTool, player); - it->getRefData().setCount(mTool.getRefData().getCount()-1); - it->getCellRef().mCharge = -1; - - mTool.getRefData().setCount(1); - } - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 735652163e..64a8240300 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -154,7 +154,8 @@ namespace MWMechanics ActiveSpells::Effect effect_ = effect; effect_.mMagnitude *= -1; effects.push_back(effect_); - caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, effects, mSourceName); + caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, + effects, mSourceName, caster.getRefData().getHandle()); } } } @@ -198,7 +199,8 @@ namespace MWMechanics inflict(caster, target, reflectedEffects, range, true); if (appliedLastingEffects.size()) - target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName); + target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, + mSourceName, caster.getRefData().getHandle()); } void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, short effectId, float magnitude) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 0088bcb603..f231a558c9 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -192,7 +192,7 @@ namespace MWMechanics effectIt != list.mList.end(); ++effectIt, ++i) { float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i]; - visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude); } } } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2c7c05317d..509c34afb3 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -607,7 +607,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; - visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), "", magnitude); ++i; } From eba60858dde7ccb0a4ed3911c4ca227951d1cf2e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 21:48:33 +0100 Subject: [PATCH 116/251] Closes #1078: Show stat bar text even when 0 --- apps/openmw/mwgui/widgets.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index c37ae15fa1..0df6c54773 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -528,14 +528,9 @@ namespace MWGui if (mBarTextWidget) { - if (mValue >= 0 && mMax > 0) - { - std::stringstream out; - out << mValue << "/" << mMax; - static_cast(mBarTextWidget)->setCaption(out.str().c_str()); - } - else - static_cast(mBarTextWidget)->setCaption(""); + std::stringstream out; + out << mValue << "/" << mMax; + static_cast(mBarTextWidget)->setCaption(out.str().c_str()); } } void MWDynamicStat::setTitle(const std::string& text) From 623cdef69fbd72d650f370e7faf207c22aaba1f6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 2 Jan 2014 21:49:04 +0100 Subject: [PATCH 117/251] Code cleanup --- apps/openmw/mwclass/npc.cpp | 18 +++++++++--------- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 22 +++++++++++----------- apps/openmw/mwmechanics/creaturestats.cpp | 6 +++--- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 10 +++++----- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f4e15423d3..f2481195bf 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -450,8 +450,8 @@ namespace MWClass (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); hitchance *= stats.getFatigueTerm(); - hitchance += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyAttack)).mMagnitude - - mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude - + mageffects.get(ESM::MagicEffect::Blind).mMagnitude; hitchance -= otherstats.getEvasion(); if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) @@ -848,11 +848,11 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0 && + else if(mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && world->isLevitationEnabled()) { float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + - mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude); + mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed = std::max(0.0f, flySpeed); @@ -863,7 +863,7 @@ namespace MWClass float swimSpeed = walkSpeed; if(Npc::getStance(ptr, Run, false)) swimSpeed = runSpeed; - swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).mMagnitude; + 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(); moveSpeed = swimSpeed; @@ -897,7 +897,7 @@ namespace MWClass float x = fJumpAcrobaticsBase->getFloat() + std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); x += 3.0f * b * fJumpAcroMultiplier->getFloat(); - x += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Jump)).mMagnitude * 64; + x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64; x *= encumbranceTerm; if(Npc::getStance(ptr, Run, false)) @@ -920,7 +920,7 @@ namespace MWClass { const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); - const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Jump)).mMagnitude; + 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(); @@ -1025,8 +1025,8 @@ namespace MWClass if(!stats.isWerewolf()) { weight = getContainerStore(ptr).getWeight(); - weight -= stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Feather)).mMagnitude; - weight += stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Burden)).mMagnitude; + weight -= stats.getMagicEffects().get(ESM::MagicEffect::Feather).mMagnitude; + weight += stats.getMagicEffects().get(ESM::MagicEffect::Burden).mMagnitude; if(weight < 0.0f) weight = 0.0f; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 2467f6c40d..0a05cd22b0 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -154,7 +154,7 @@ namespace MWGui float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1; - bool stunted = (stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0); + bool stunted = (stats.getMagicEffects().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0); float fRestMagicMult = store.get().find("fRestMagicMult")->getFloat(); float hourlyMagickaDelta = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 68804c1d75..21a6dfd840 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -278,7 +278,7 @@ namespace MWMechanics { // the actor is sleeping, restore health and magicka - bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0; + bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; DynamicStat health = stats.getHealth(); health.setCurrent (health.getCurrent() + 0.1 * endurance); @@ -329,23 +329,23 @@ namespace MWMechanics for(int i = 0;i < 3;++i) { DynamicStat stat = creatureStats.getDynamic(i); - stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyHealth+i)).mMagnitude - - effects.get(EffectKey(ESM::MagicEffect::DrainHealth+i)).mMagnitude); + stat.setModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).mMagnitude - + effects.get(ESM::MagicEffect::DrainHealth+i).mMagnitude); - float currentDiff = creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::RestoreHealth+i)).mMagnitude - - creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::DamageHealth+i)).mMagnitude - - creatureStats.getMagicEffects().get(EffectKey(ESM::MagicEffect::AbsorbHealth+i)).mMagnitude; + float currentDiff = creatureStats.getMagicEffects().get(ESM::MagicEffect::RestoreHealth+i).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::DamageHealth+i).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::AbsorbHealth+i).mMagnitude; stat.setCurrent(stat.getCurrent() + currentDiff * duration); creatureStats.setDynamic(i, stat); } // Apply disintegration (reduces item health) - float disintegrateWeapon = effects.get(EffectKey(ESM::MagicEffect::DisintegrateWeapon)).mMagnitude; + float disintegrateWeapon = effects.get(ESM::MagicEffect::DisintegrateWeapon).mMagnitude; if (disintegrateWeapon > 0) disintegrateSlot(ptr, MWWorld::InventoryStore::Slot_CarriedRight, disintegrateWeapon*duration); - float disintegrateArmor = effects.get(EffectKey(ESM::MagicEffect::DisintegrateArmor)).mMagnitude; + float disintegrateArmor = effects.get(ESM::MagicEffect::DisintegrateArmor).mMagnitude; if (disintegrateArmor > 0) { // According to UESP @@ -377,7 +377,7 @@ namespace MWMechanics DynamicStat health = creatureStats.getHealth(); for (unsigned int i=0; i::iterator it = boundItemsMap.begin(); it != boundItemsMap.end(); ++it) { bool found = creatureStats.mBoundItems.find(it->first) != creatureStats.mBoundItems.end(); - int magnitude = creatureStats.getMagicEffects().get(EffectKey(it->first)).mMagnitude; + int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; if (found != (magnitude > 0)) { std::string item = "bound_" + it->second; @@ -472,7 +472,7 @@ namespace MWMechanics for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it) { bool found = creatureStats.mSummonedCreatures.find(it->first) != creatureStats.mSummonedCreatures.end(); - int magnitude = creatureStats.getMagicEffects().get(EffectKey(it->first)).mMagnitude; + int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; if (found != (magnitude > 0)) { if (magnitude > 0) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 95bd405b1d..fa55be9b93 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -228,8 +228,8 @@ namespace MWMechanics void CreatureStats::setMagicEffects(const MagicEffects &effects) { - if (effects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude - != mMagicEffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude) + if (effects.get(ESM::MagicEffect::FortifyMaximumMagicka).mMagnitude + != mMagicEffects.get(ESM::MagicEffect::FortifyMaximumMagicka).mMagnitude) mRecalcDynamicStats = true; mMagicEffects = effects; @@ -343,7 +343,7 @@ namespace MWMechanics float evasion = (getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + (getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); evasion *= getFatigueTerm(); - evasion += mMagicEffects.get(EffectKey(ESM::MagicEffect::Sanctuary)).mMagnitude; + evasion += mMagicEffects.get(ESM::MagicEffect::Sanctuary).mMagnitude; return evasion; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f6e69b7260..a40535030d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -328,7 +328,7 @@ void RenderingManager::update (float duration, bool paused) MWWorld::Ptr player = world->getPlayer().getPlayer(); - int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; mRendering.getFader()->setFactor(std::max(0.f, 1.f-(blind / 100.f))); setAmbientMode(); @@ -593,7 +593,7 @@ void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) mAmbientColor = colour; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude; + int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye).mMagnitude; Ogre::ColourValue final = colour; final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f)); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 148c8f3016..b410da2d83 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1606,7 +1606,7 @@ namespace MWWorld return false; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); - if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Levitate)).mMagnitude > 0 + if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).mMagnitude > 0 && isLevitationEnabled()) return true; @@ -1626,7 +1626,7 @@ namespace MWWorld return false; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); - if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::SlowFall)).mMagnitude > 0) + if(stats.getMagicEffects().get(ESM::MagicEffect::SlowFall).mMagnitude > 0) return true; return false; @@ -2390,11 +2390,11 @@ namespace MWWorld const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float dist=0; if (type == World::Detect_Creature) - dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectAnimal)).mMagnitude; + dist = effects.get(ESM::MagicEffect::DetectAnimal).mMagnitude; else if (type == World::Detect_Key) - dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectKey)).mMagnitude; + dist = effects.get(ESM::MagicEffect::DetectKey).mMagnitude; else if (type == World::Detect_Enchantment) - dist = effects.get(MWMechanics::EffectKey(ESM::MagicEffect::DetectEnchantment)).mMagnitude; + dist = effects.get(ESM::MagicEffect::DetectEnchantment).mMagnitude; if (!dist) return; From 32ff3b530c8b3e7b3dd57b26841b96b9a0a24666 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 01:59:15 +0100 Subject: [PATCH 118/251] Change all instances of skill/attribute values to use an appropriate typedef. --- apps/openmw/mwbase/windowmanager.hpp | 8 ++++---- apps/openmw/mwgui/charactercreation.cpp | 12 ++++++------ apps/openmw/mwgui/charactercreation.hpp | 4 ++-- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/review.cpp | 8 ++++---- apps/openmw/mwgui/review.hpp | 6 +++--- apps/openmw/mwgui/statswindow.cpp | 8 ++++---- apps/openmw/mwgui/statswindow.hpp | 6 +++--- apps/openmw/mwgui/widgets.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 12 ++++++------ apps/openmw/mwgui/windowmanagerimp.hpp | 12 ++++++------ apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 +++++----- apps/openmw/mwmechanics/creaturestats.hpp | 8 ++++---- apps/openmw/mwmechanics/npcstats.cpp | 4 ++-- apps/openmw/mwmechanics/npcstats.hpp | 4 ++-- apps/openmw/mwmechanics/stat.hpp | 3 +++ apps/openmw/mwscript/statsextensions.cpp | 4 ++-- 18 files changed, 59 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 1300efc753..6c85be5fd2 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -137,8 +137,8 @@ namespace MWBase virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) = 0; /// Set value for the given ID. - virtual void setValue (const std::string& id, const MWMechanics::Stat& value) = 0; - virtual void setValue (int parSkill, const MWMechanics::Stat& value) = 0; + virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value) = 0; + virtual void setValue (int parSkill, const MWMechanics::SkillValue& value) = 0; virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; @@ -236,8 +236,8 @@ namespace MWBase virtual void onFrame (float frameDuration) = 0; /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map > getPlayerSkillValues() = 0; - virtual std::map > getPlayerAttributeValues() = 0; + virtual std::map getPlayerSkillValues() = 0; + virtual std::map getPlayerAttributeValues() = 0; virtual SkillList getPlayerMinorSkills() = 0; virtual SkillList getPlayerMajorSkills() = 0; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index b829f219d7..04507cfc61 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -75,7 +75,7 @@ namespace MWGui mGenerateClassSpecializations[2] = 0; } - void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat& value) + void CharacterCreation::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { if (mReviewDialog) { @@ -113,7 +113,7 @@ namespace MWGui } } - void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { if (mReviewDialog) mReviewDialog->setSkillValue(parSkill, value); @@ -229,8 +229,8 @@ namespace MWGui } { - std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); + std::map attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); + for (std::map::iterator it = attributes.begin(); it != attributes.end(); ++it) { mReviewDialog->setAttribute(static_cast (it->first), it->second); @@ -238,8 +238,8 @@ namespace MWGui } { - std::map > skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); - for (std::map >::iterator it = skills.begin(); + std::map skills = MWBase::Environment::get().getWindowManager()->getPlayerSkillValues(); + for (std::map::iterator it = skills.begin(); it != skills.end(); ++it) { mReviewDialog->setSkillValue(static_cast (it->first), it->second); diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index b80aaae41c..924f40c282 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -31,9 +31,9 @@ namespace MWGui //Show a dialog void spawnDialog(const char id); - void setValue (const std::string& id, const MWMechanics::Stat& value); + void setValue (const std::string& id, const MWMechanics::AttributeValue& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); + void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); void configureSkills (const SkillList& major, const SkillList& minor); void doRenderUpdate(); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index a772b3a15a..e3d8f5dd7b 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -166,7 +166,7 @@ namespace MWGui // increase attributes for (int i=0; i<3; ++i) { - MWMechanics::Stat attribute = creatureStats.getAttribute(mSpentAttributes[i]); + MWMechanics::AttributeValue attribute = creatureStats.getAttribute(mSpentAttributes[i]); attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); if (attribute.getBase() >= 100) diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index dfc86a547b..b1c85e1c60 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -74,7 +74,7 @@ namespace MWGui for (int i = 0; i < ESM::Skill::Length; ++i) { - mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); + mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); } @@ -152,7 +152,7 @@ namespace MWGui mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } - void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) + void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value) { std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); if (attr == mAttributeWidgets.end()) @@ -161,7 +161,7 @@ namespace MWGui attr->second->setAttributeValue(value); } - void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) + void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value) { mSkillValues[skillId] = value; MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; @@ -279,7 +279,7 @@ namespace MWGui continue; assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; float base = stat.getBase(); float modified = stat.getModified(); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 1c24fec745..5d0767d217 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -38,10 +38,10 @@ namespace MWGui void setMagicka(const MWMechanics::DynamicStat& value); void setFatigue(const MWMechanics::DynamicStat& value); - void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value); + void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value); void configureSkills(const SkillList& major, const SkillList& minor); - void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value); + void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value); virtual void open(); @@ -85,7 +85,7 @@ namespace MWGui std::map mAttributeWidgets; SkillList mMajorSkills, mMinorSkills, mMiscSkills; - std::map > mSkillValues; + std::map mSkillValues; std::map mSkillWidgetMap; std::string mName, mRaceId, mBirthSignId; ESM::Class mKlass; diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index ab6096b7e6..17583b1c05 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -61,7 +61,7 @@ namespace MWGui for (int i = 0; i < ESM::Skill::Length; ++i) { - mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); + mSkillValues.insert(std::pair(i, MWMechanics::SkillValue())); mSkillWidgetMap.insert(std::pair(i, (MyGUI::TextBox*)NULL)); } @@ -102,7 +102,7 @@ namespace MWGui adjustWindowCaption(); } - void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) + void StatsWindow::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { static const char *ids[] = { @@ -179,7 +179,7 @@ namespace MWGui } } - void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) + void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) { mSkillValues[parSkill] = value; MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; @@ -358,7 +358,7 @@ namespace MWGui continue; assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; + const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; float base = stat.getBase(); float modified = stat.getModified(); int progressPercent = (modified - float(static_cast(modified))) * 100; diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 49b44a2e16..28d96ca90f 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -26,11 +26,11 @@ namespace MWGui void setPlayerName(const std::string& playerName); /// Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::Stat& value); + void setValue (const std::string& id, const MWMechanics::AttributeValue& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); + void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); void configureSkills (const SkillList& major, const SkillList& minor); void setReputation (int reputation) { if (reputation != mReputation) mChanged = true; this->mReputation = reputation; } @@ -61,7 +61,7 @@ namespace MWGui MyGUI::ScrollView* mSkillView; SkillList mMajorSkills, mMinorSkills, mMiscSkills; - std::map > mSkillValues; + std::map mSkillValues; std::map mSkillWidgetMap; std::map mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 1630ab3c9f..adc56f423f 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -133,7 +133,7 @@ namespace MWGui public: MWAttribute(); - typedef MWMechanics::Stat AttributeValue; + typedef MWMechanics::AttributeValue AttributeValue; void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 90ced66d3c..3accd925f6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -249,12 +249,12 @@ namespace MWGui // Setup player stats for (int i = 0; i < ESM::Attribute::Length; ++i) { - mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::Stat())); + mPlayerAttributes.insert(std::make_pair(ESM::Attribute::sAttributeIds[i], MWMechanics::AttributeValue())); } for (int i = 0; i < ESM::Skill::Length; ++i) { - mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::Stat())); + mPlayerSkillValues.insert(std::make_pair(ESM::Skill::sSkillIds[i], MWMechanics::SkillValue())); } // Set up visibility @@ -544,7 +544,7 @@ namespace MWGui } } - void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) + void WindowManager::setValue (const std::string& id, const MWMechanics::AttributeValue& value) { mStatsWindow->setValue (id, value); mCharGen->setValue(id, value); @@ -575,7 +575,7 @@ namespace MWGui } - void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) + void WindowManager::setValue (int parSkill, const MWMechanics::SkillValue& value) { /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we /// allow custom skills. @@ -1178,12 +1178,12 @@ namespace MWGui return mGuiModes.back(); } - std::map > WindowManager::getPlayerSkillValues() + std::map WindowManager::getPlayerSkillValues() { return mPlayerSkillValues; } - std::map > WindowManager::getPlayerAttributeValues() + std::map WindowManager::getPlayerAttributeValues() { return mPlayerAttributes; } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index b332ffc364..a3b135ad4e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -152,8 +152,8 @@ namespace MWGui virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); ///< Set value for the given ID. - virtual void setValue (const std::string& id, const MWMechanics::Stat& value); - virtual void setValue (int parSkill, const MWMechanics::Stat& value); + virtual void setValue (const std::string& id, const MWMechanics::AttributeValue& value); + virtual void setValue (int parSkill, const MWMechanics::SkillValue& value); virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); @@ -229,8 +229,8 @@ namespace MWGui virtual void onFrame (float frameDuration); /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - virtual std::map > getPlayerSkillValues(); - virtual std::map > getPlayerAttributeValues(); + virtual std::map getPlayerSkillValues(); + virtual std::map getPlayerAttributeValues(); virtual SkillList getPlayerMinorSkills(); virtual SkillList getPlayerMajorSkills(); @@ -346,9 +346,9 @@ namespace MWGui // Various stats about player as needed by window manager std::string mPlayerName; std::string mPlayerRaceId; - std::map > mPlayerAttributes; + std::map mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; + std::map mPlayerSkillValues; MyGUI::Gui *mGui; // Gui std::vector mGuiModes; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 21a6dfd840..30ea6aa2cb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -317,7 +317,7 @@ namespace MWMechanics // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { - Stat stat = creatureStats.getAttribute(i); + AttributeValue stat = creatureStats.getAttribute(i); stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyAttribute, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::DrainAttribute, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::AbsorbAttribute, i)).mMagnitude); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index fa55be9b93..6fc8f25979 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -74,7 +74,7 @@ namespace MWMechanics - gmst.find ("fFatigueMult")->getFloat() * (1-normalised); } - const Stat &CreatureStats::getAttribute(int index) const + const AttributeValue &CreatureStats::getAttribute(int index) const { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); @@ -158,20 +158,20 @@ namespace MWMechanics void CreatureStats::setAttribute(int index, int base) { - MWMechanics::Stat current = getAttribute(index); + AttributeValue current = getAttribute(index); current.setBase(base); setAttribute(index, current); } - void CreatureStats::setAttribute(int index, const Stat &value) + void CreatureStats::setAttribute(int index, const AttributeValue &value) { if (index < 0 || index > 7) { throw std::runtime_error("attribute index is out of range"); } - const Stat& currentValue = !mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]; + const AttributeValue& currentValue = !mIsWerewolf ? mAttributes[index] : mWerewolfAttributes[index]; - if (value.getModified() != currentValue.getModified()) + if (value != currentValue) { if (index != ESM::Attribute::Luck && index != ESM::Attribute::Personality diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 4d9be01324..8a525523dd 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -18,7 +18,7 @@ namespace MWMechanics /// class CreatureStats { - Stat mAttributes[8]; + AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; Spells mSpells; @@ -49,7 +49,7 @@ namespace MWMechanics protected: bool mIsWerewolf; - Stat mWerewolfAttributes[8]; + AttributeValue mWerewolfAttributes[8]; public: CreatureStats(); @@ -65,7 +65,7 @@ namespace MWMechanics bool canUsePower (const std::string& power) const; void usePower (const std::string& power); - const Stat & getAttribute(int index) const; + const AttributeValue & getAttribute(int index) const; const DynamicStat & getHealth() const; @@ -94,7 +94,7 @@ namespace MWMechanics MagicEffects & getMagicEffects(); - void setAttribute(int index, const Stat &value); + void setAttribute(int index, const AttributeValue &value); // Shortcut to set only the base void setAttribute(int index, int base); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 1fdefc84f9..c4b8b0b7aa 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -84,7 +84,7 @@ void MWMechanics::NpcStats::setMovementFlag (Flag flag, bool state) mMovementFlags &= ~flag; } -const MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) const +const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) const { if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); @@ -92,7 +92,7 @@ const MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) cons return (!mIsWerewolf ? mSkill[index] : mWerewolfSkill[index]); } -MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) +MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill (int index) { if (index<0 || index>=ESM::Skill::Length) throw std::runtime_error ("skill index out of range"); diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 6b7efa5b72..df0f084ceb 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -94,8 +94,8 @@ namespace MWMechanics void setMovementFlag (Flag flag, bool state); - const Stat& getSkill (int index) const; - Stat& getSkill (int index); + const SkillValue& getSkill (int index) const; + SkillValue& getSkill (int index); const std::map& getFactionRanks() const; std::map& getFactionRanks(); diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index cb6c09014b..c7778a3d8d 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -205,6 +205,9 @@ namespace MWMechanics { return !(left==right); } + + typedef Stat AttributeValue; + typedef Stat SkillValue; } #endif diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 45e5b0f187..f32d602dad 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -124,7 +124,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWMechanics::Stat attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); + MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); attribute.setModified (value, 0); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } @@ -146,7 +146,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWMechanics::Stat attribute = MWWorld::Class::get(ptr) + MWMechanics::AttributeValue attribute = MWWorld::Class::get(ptr) .getCreatureStats(ptr) .getAttribute(mIndex); From b42240be6d80d8fe125a774110a1882c6103b8e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 03:46:30 +0100 Subject: [PATCH 119/251] Implement Damage/restore skill/attribute effects. Use dedicated classes for skill and attribute values (instead of Stat) since there are some important differences. --- apps/openmw/mwclass/npc.cpp | 28 ++++++------- apps/openmw/mwgui/review.cpp | 6 +-- apps/openmw/mwgui/statswindow.cpp | 9 ++-- apps/openmw/mwgui/tradewindow.cpp | 4 +- apps/openmw/mwgui/widgets.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 4 +- apps/openmw/mwmechanics/npcstats.cpp | 27 +++++------- apps/openmw/mwmechanics/npcstats.hpp | 4 +- apps/openmw/mwmechanics/spellcasting.cpp | 40 ++++++++++++++++-- apps/openmw/mwmechanics/spellcasting.hpp | 2 +- apps/openmw/mwmechanics/stat.hpp | 41 ++++++++++++++++++- apps/openmw/mwscript/statsextensions.cpp | 27 +++++------- apps/openmw/mwworld/worldimp.cpp | 2 +- 14 files changed, 126 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f2481195bf..307ad3d7cf 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -179,29 +179,29 @@ namespace for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex) { - if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) - { - raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; - break; - } + if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) + { + raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; + break; + } } for (int k = 0; k < 5; ++k) { - // is this a minor or major skill? - if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) - { - majorMultiplier = 1.0f; - break; - } + // is this a minor or major skill? + if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) + { + majorMultiplier = 1.0f; + break; + } } // is this skill in the same Specialization as the class? const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillIndex); if (skill->mData.mSpecialization == class_->mData.mSpecialization) { - specMultiplier = 0.5f; - specBonus = 5; + specMultiplier = 0.5f; + specBonus = 5; } npcStats.getSkill(skillIndex).setBase( @@ -210,7 +210,7 @@ namespace + 5 + raceBonus + specBonus - + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100.0f)); + + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100)); } } } diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index b1c85e1c60..e27e40ae64 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -65,7 +65,7 @@ namespace MWGui getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); - attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue(0, 0)); + attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue()); } // Setup skills @@ -280,8 +280,8 @@ namespace MWGui assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); + int base = stat.getBase(); + int modified = stat.getModified(); std::string state = "normal"; if (modified > base) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 17583b1c05..549cf65abf 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -359,21 +359,19 @@ namespace MWGui assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - int progressPercent = (modified - float(static_cast(modified))) * 100; + int base = stat.getBase(); + int modified = stat.getModified(); + int progressPercent = stat.getProgress() * 100; const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); const ESM::Skill* skill = esmStore.get().find(skillId); - assert(skill); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; const ESM::Attribute* attr = esmStore.get().find(skill->mData.mAttribute); - assert(attr); std::string state = "normal"; if (modified > base) @@ -484,7 +482,6 @@ namespace MWGui ESM::RankData rankData = faction->mData.mRankData[it->second+1]; const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); - assert(attr1 && attr2); text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index d544b83cf4..e6cbbf968b 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -296,10 +296,10 @@ namespace MWGui const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c1 = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float e1 = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f1 = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 0df6c54773..b30cf2bae8 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -178,7 +178,7 @@ namespace MWGui } if (mAttributeValueWidget) { - AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); + int modified = mValue.getModified(), base = mValue.getBase(); static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); if (modified > base) mAttributeValueWidget->_setWidgetState("increased"); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 30ea6aa2cb..ec3b861374 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -523,7 +523,7 @@ namespace MWMechanics // skills for(int i = 0;i < ESM::Skill::Length;++i) { - Stat& skill = npcStats.getSkill(i); + SkillValue& skill = npcStats.getSkill(i); skill.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifySkill, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::DrainSkill, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::AbsorbSkill, i)).mMagnitude); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7740240b6b..2e18fae63a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -499,10 +499,10 @@ namespace MWMechanics // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = std::max(0, std::min(getDerivedDisposition(ptr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(),100)); - float a = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index c4b8b0b7aa..0a0b51270d 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -197,34 +197,25 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if(mIsWerewolf) return; - float base = getSkill (skillIndex).getBase(); + MWMechanics::SkillValue value = getSkill (skillIndex); - int level = static_cast (base); + value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType)); - base += getSkillGain (skillIndex, class_, usageType); - - if (static_cast (base)!=level) + if (value.getProgress()>=1) { // skill leveled up increaseSkill(skillIndex, class_, false); } - else - getSkill (skillIndex).setBase (base); } void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) { - float base = getSkill (skillIndex).getBase(); + int base = getSkill (skillIndex).getBase(); - int level = static_cast (base); - - if (level >= 100) + if (base >= 100) return; - if (preserveProgress) - base += 1; - else - base = level+1; + base += 1; // if this is a major or minor skill of the class, increase level progress bool levelProgress = false; @@ -260,6 +251,8 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas } getSkill (skillIndex).setBase (base); + if (!preserveProgress) + getSkill(skillIndex).setProgress(0); } int MWMechanics::NpcStats::getLevelProgress () const @@ -387,7 +380,7 @@ void MWMechanics::NpcStats::setWerewolf (bool set) // Oh, Bethesda. It's "Intelligence". std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : ESM::Attribute::sAttributeNames[i]); - mWerewolfAttributes[i].setModified(int(gmst.find(name)->getFloat()), 0); + mWerewolfAttributes[i].setBase(int(gmst.find(name)->getFloat())); } for(size_t i = 0;i < ESM::Skill::Length;i++) @@ -401,7 +394,7 @@ void MWMechanics::NpcStats::setWerewolf (bool set) // "Mercantile"! >_< std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : ESM::Skill::sSkillNames[i]); - mWerewolfSkill[i].setModified(int(gmst.find(name)->getFloat()), 0); + mWerewolfSkill[i].setBase(int(gmst.find(name)->getFloat())); } } mIsWerewolf = set; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index df0f084ceb..552422d843 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -45,8 +45,8 @@ namespace MWMechanics DrawState_ mDrawState; int mDisposition; unsigned int mMovementFlags; - Stat mSkill[27]; - Stat mWerewolfSkill[27]; + SkillValue mSkill[27]; + SkillValue mWerewolfSkill[27]; int mBounty; std::set mExpelled; std::map mFactionReputation; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 64a8240300..1de4e50c4d 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -135,7 +135,8 @@ namespace MWMechanics float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; magnitude *= magnitudeMult; - if (target.getClass().isActor() && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + if (target.getClass().isActor() && hasDuration) { ActiveSpells::Effect effect; effect.mKey = MWMechanics::EffectKey(*effectIt); @@ -160,7 +161,17 @@ namespace MWMechanics } } else - applyInstantEffect(target, effectIt->mEffectID, magnitude); + applyInstantEffect(target, EffectKey(*effectIt), magnitude); + + // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent. + // This was probably just done to have the effect visible in the magic menu for a while + // to notify the player they've been damaged? + if (effectIt->mEffectID == ESM::MagicEffect::DamageAttribute + || effectIt->mEffectID == ESM::MagicEffect::DamageSkill + || effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute + || effectIt->mEffectID == ESM::MagicEffect::RestoreSkill + ) + applyInstantEffect(target, EffectKey(*effectIt), magnitude); if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) { @@ -203,8 +214,9 @@ namespace MWMechanics mSourceName, caster.getRefData().getHandle()); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, short effectId, float magnitude) + void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, MWMechanics::EffectKey effect, float magnitude) { + short effectId = effect.mId; if (!target.getClass().isActor()) { if (effectId == ESM::MagicEffect::Lock) @@ -227,6 +239,28 @@ namespace MWMechanics } else { + if (effectId == ESM::MagicEffect::DamageAttribute || effectId == ESM::MagicEffect::RestoreAttribute) + { + int attribute = effect.mArg; + AttributeValue value = target.getClass().getCreatureStats(target).getAttribute(attribute); + if (effectId == ESM::MagicEffect::DamageAttribute) + value.damage(magnitude); + else + value.restore(magnitude); + target.getClass().getCreatureStats(target).setAttribute(attribute, value); + } + else if (effectId == ESM::MagicEffect::DamageSkill || effectId == ESM::MagicEffect::RestoreSkill) + { + if (target.getTypeName() != typeid(ESM::NPC).name()) + return; + int skill = effect.mArg; + SkillValue& value = target.getClass().getNpcStats(target).getSkill(skill); + if (effectId == ESM::MagicEffect::DamageSkill) + value.damage(magnitude); + else + value.restore(magnitude); + } + if (effectId == ESM::MagicEffect::CurePoison) target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); else if (effectId == ESM::MagicEffect::CureParalyzation) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index e2efaa140b..a55c45fd16 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -203,7 +203,7 @@ namespace MWMechanics void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); - void applyInstantEffect (const MWWorld::Ptr& target, short effectId, float magnitude); + void applyInstantEffect (const MWWorld::Ptr& target, MWMechanics::EffectKey effect, float magnitude); }; } diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index c7778a3d8d..77b3f63645 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -206,8 +206,45 @@ namespace MWMechanics return !(left==right); } - typedef Stat AttributeValue; - typedef Stat SkillValue; + class AttributeValue + { + int mBase; + int mModifier; + int mDamage; + + public: + AttributeValue() : mBase(0), mModifier(0), mDamage(0) {} + + int getModified() const { return std::max(0, mBase - mDamage + mModifier); } + int getBase() const { return mBase; } + int getModifier() const { return mModifier; } + + void setBase(int base) { mBase = std::max(0, base); } + void setModifier(int mod) { mModifier = mod; } + + void damage(int damage) { mDamage += damage; } + void restore(int amount) { mDamage -= std::min(mDamage, amount); } + int getDamage() const { return mDamage; } + }; + + class SkillValue : public AttributeValue + { + float mProgress; + public: + float getProgress() const { return mProgress; } + void setProgress(float progress) { mProgress = progress; } + }; + + inline bool operator== (const AttributeValue& left, const AttributeValue& right) + { + return left.getBase() == right.getBase() + && left.getModifier() == right.getModifier() + && left.getDamage() == right.getDamage(); + } + inline bool operator!= (const AttributeValue& left, const AttributeValue& right) + { + return !(left == right); + } } #endif diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index f32d602dad..a899b05f0d 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -125,7 +125,7 @@ namespace MWScript runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); - attribute.setModified (value, 0); + attribute.setBase (value - (attribute.getModified() - attribute.getBase())); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -150,11 +150,7 @@ namespace MWScript .getCreatureStats(ptr) .getAttribute(mIndex); - value += - attribute.getModified(); - - attribute - .setModified (value, 0, 100); + attribute.setBase (std::min(100, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -346,12 +342,10 @@ namespace MWScript const ESM::Class& class_ = *MWBase::Environment::get().getWorld()->getStore().get().find (ref->mBase->mClass); - float level = 0; - float progress = std::modf (stats.getSkill (mIndex).getBase(), &level); + float level = stats.getSkill(mIndex).getBase(); + float progress = stats.getSkill(mIndex).getProgress(); - float modifier = stats.getSkill (mIndex).getModifier(); - - int newLevel = static_cast (value-modifier); + int newLevel = value - (stats.getSkill(mIndex).getModified() - stats.getSkill(mIndex).getBase()); if (newLevel<0) newLevel = 0; @@ -362,8 +356,8 @@ namespace MWScript if (progress>=1) progress = 0.999999999; - stats.getSkill (mIndex).set (newLevel + progress); - stats.getSkill (mIndex).setModifier (modifier); + stats.getSkill (mIndex).setBase (newLevel); + stats.getSkill (mIndex).setProgress(progress); } }; @@ -383,11 +377,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - value += MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - getModified(); + MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats(ptr); - MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - setModified (value, 0, 100); + stats.getSkill(mIndex). + setBase (std::min(100, stats.getSkill(mIndex).getBase() + value)); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b410da2d83..083c1cf0e4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2000,7 +2000,7 @@ namespace MWWorld const Store &gmst = getStore().get(); MWMechanics::NpcStats &stats = Class::get(actor).getNpcStats(actor); - stats.getSkill(ESM::Skill::Acrobatics).setModified(gmst.find("fWerewolfAcrobatics")->getFloat(), 0); + stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getFloat()); } bool World::getGodModeState() From 93e1a2df73a45ef60963f8b59a6398ac76ef5d12 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 04:09:52 +0100 Subject: [PATCH 120/251] Implement Cast script instruction (shrines work now) --- apps/openmw/mwscript/docs/vmformat.txt | 5 +++-- apps/openmw/mwscript/miscextensions.cpp | 23 +++++++++++++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index cf533451c6..bedbb4ad23 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -358,5 +358,6 @@ op 0x2000223: GetLineOfSightExplicit op 0x2000224: ToggleAI op 0x2000225: ToggleAIExplicit op 0x2000226: COE - -opcodes 0x2000227-0x3ffffff unused +op 0x2000227: Cast +op 0x2000228: Cast, explicit +opcodes 0x2000229-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8e2a8af8c4..6dadf395dc 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -23,6 +23,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellcasting.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -700,6 +701,26 @@ namespace MWScript } }; + template + class OpCast : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string spell = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral (runtime[0].mInteger)); + runtime.pop(); + + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); + + MWMechanics::CastSpell cast(ptr, target); + cast.cast(spell); + } + }; void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -761,6 +782,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode); interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation); interpreter.installSegment5 (Compiler::Misc::opcodeEnableLevitation, new OpEnableLevitation); + interpreter.installSegment5 (Compiler::Misc::opcodeCast, new OpCast); + interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index e95f6f6985..a512d7889f 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -222,6 +222,7 @@ namespace Compiler extensions.registerInstruction ("activate", "", opcodeActivate); extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit); extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit); + extensions.registerInstruction ("cast", "SS", opcodeCast, opcodeCastExplicit); extensions.registerInstruction ("togglecollisionboxes", "", opcodeToggleCollisionBoxes); extensions.registerInstruction ("togglecollisiongrid", "", opcodeToggleCollisionDebug); extensions.registerInstruction ("tcb", "", opcodeToggleCollisionBoxes); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index b7d44a851e..24201d9a60 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -228,6 +228,8 @@ namespace Compiler const int opcodeToggleGodMode = 0x200021f; const int opcodeDisableLevitation = 0x2000220; const int opcodeEnableLevitation = 0x2000221; + const int opcodeCast = 0x2000227; + const int opcodeCastExplicit = 0x2000228; } namespace Sky From 366801f3d513a203bc30580e5a704bddbccf437f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 04:44:50 +0100 Subject: [PATCH 121/251] Implement explodeSpell instruction (like Cast, with caster = target) --- apps/openmw/mwmechanics/spellcasting.cpp | 3 ++- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 18 ++++++++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 1de4e50c4d..7849eb1651 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -434,7 +434,8 @@ namespace MWMechanics int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] if (!fail && roll >= successChance) { - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); + if (mCaster.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); fail = true; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index bedbb4ad23..3d7b281f86 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -360,4 +360,6 @@ op 0x2000225: ToggleAIExplicit op 0x2000226: COE op 0x2000227: Cast op 0x2000228: Cast, explicit -opcodes 0x2000229-0x3ffffff unused +op 0x2000229: ExplodeSpell +op 0x200022a: ExplodeSpell, explicit +opcodes 0x200022b-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 6dadf395dc..ea4824c299 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -722,6 +722,22 @@ namespace MWScript } }; + template + class OpExplodeSpell : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string spell = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWMechanics::CastSpell cast(ptr, ptr); + cast.cast(spell); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -784,6 +800,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeEnableLevitation, new OpEnableLevitation); interpreter.installSegment5 (Compiler::Misc::opcodeCast, new OpCast); interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast); + interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell); + interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index a512d7889f..47a653451e 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -223,6 +223,7 @@ namespace Compiler extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit); extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit); extensions.registerInstruction ("cast", "SS", opcodeCast, opcodeCastExplicit); + extensions.registerInstruction ("explodespell", "S", opcodeExplodeSpell, opcodeExplodeSpellExplicit); extensions.registerInstruction ("togglecollisionboxes", "", opcodeToggleCollisionBoxes); extensions.registerInstruction ("togglecollisiongrid", "", opcodeToggleCollisionDebug); extensions.registerInstruction ("tcb", "", opcodeToggleCollisionBoxes); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 24201d9a60..9bc256413c 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -230,6 +230,8 @@ namespace Compiler const int opcodeEnableLevitation = 0x2000221; const int opcodeCast = 0x2000227; const int opcodeCastExplicit = 0x2000228; + const int opcodeExplodeSpell = 0x2000229; + const int opcodeExplodeSpellExplicit = 0x200022a; } namespace Sky From 7d8ca91286413cfacbfeea28142e25d8070beacb Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 04:56:54 +0100 Subject: [PATCH 122/251] Implement RemoveSpellEffects instruction --- apps/openmw/mwmechanics/activespells.cpp | 6 ++++++ apps/openmw/mwmechanics/activespells.hpp | 5 ++++- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/statsextensions.cpp | 19 +++++++++++++++++++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 3 +++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 1cdb74407a..2a71659742 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -150,6 +150,12 @@ namespace MWMechanics mSpellsChanged = true; } + void ActiveSpells::removeEffects(const std::string &id) + { + mSpells.erase(Misc::StringUtils::lowerCase(id)); + mSpellsChanged = true; + } + void ActiveSpells::visitEffectSources(EffectSourceVisitor &visitor) const { for (TContainer::const_iterator it = begin(); it != end(); ++it) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 57f224f6fd..2ddb4ec556 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -81,7 +81,10 @@ namespace MWMechanics void addSpell (const std::string& id, bool stack, std::vector effects, const std::string& displayName, const std::string& casterHandle); - /// Remove all active effects with this id + /// Removes the active effects from this spell/potion/.. with \a id + void removeEffects (const std::string& id); + + /// Remove all active effects with this effect id void purgeEffect (short effectId); /// Remove all active effects, if roll succeeds (for each effect) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 3d7b281f86..25bf3429f6 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -362,4 +362,6 @@ op 0x2000227: Cast op 0x2000228: Cast, explicit op 0x2000229: ExplodeSpell op 0x200022a: ExplodeSpell, explicit -opcodes 0x200022b-0x3ffffff unused +op 0x200022b: RemoveSpellEffects +op 0x200022c: RemoveSpellEffects, explicit +opcodes 0x200022d-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index a899b05f0d..6aa19681d1 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -459,6 +459,22 @@ namespace MWScript } }; + template + class OpRemoveSpellEffects : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string spellid = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).getActiveSpells().removeEffects(spellid); + } + }; + template class OpGetSpell : public Interpreter::Opcode0 { @@ -1135,6 +1151,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpell, new OpRemoveSpell); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellExplicit, new OpRemoveSpell); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffects, new OpRemoveSpellEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit, + new OpRemoveSpellEffects); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 47a653451e..0fc0da9d06 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -391,6 +391,8 @@ namespace Compiler extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); + extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, + opcodeRemoveSpellEffectsExplicit); extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 9bc256413c..671bf2ff7e 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -369,6 +369,9 @@ namespace Compiler const int opcodeIsWerewolfExplicit = 0x20001fe; const int opcodeGetWerewolfKills = 0x20001e2; + + const int opcodeRemoveSpellEffects = 0x200022b; + const int opcodeRemoveSpellEffectsExplicit = 0x200022c; } namespace Transformation From b4230f716e41eb0cb86cb062cd61cbfd4a19e2f8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 05:19:10 +0100 Subject: [PATCH 123/251] Implement RemoveEffects instruction --- apps/openmw/mwmechanics/spells.cpp | 5 +++-- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/statsextensions.cpp | 20 ++++++++++++++++++++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 2 ++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index f231a558c9..21781c530c 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -34,13 +34,14 @@ namespace MWMechanics random.resize(spell->mEffects.mList.size()); for (unsigned int i=0; i (std::rand()) / RAND_MAX; - mSpells.insert (std::make_pair (spellId, random)); + mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random)); } } void Spells::remove (const std::string& spellId) { - TContainer::iterator iter = mSpells.find (spellId); + std::string lower = Misc::StringUtils::lowerCase(spellId); + TContainer::iterator iter = mSpells.find (lower); if (iter!=mSpells.end()) mSpells.erase (iter); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 25bf3429f6..cf27b7d6eb 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -364,4 +364,6 @@ op 0x2000229: ExplodeSpell op 0x200022a: ExplodeSpell, explicit op 0x200022b: RemoveSpellEffects op 0x200022c: RemoveSpellEffects, explicit -opcodes 0x200022d-0x3ffffff unused +op 0x200022d: RemoveEffects +op 0x200022e: RemoveEffects, explicit +opcodes 0x200022f-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 6aa19681d1..b1c9698cac 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -475,6 +475,22 @@ namespace MWScript } }; + template + class OpRemoveEffects : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer effectId = runtime[0].mInteger; + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).getActiveSpells().purgeEffect(effectId); + } + }; + template class OpGetSpell : public Interpreter::Opcode0 { @@ -1155,6 +1171,10 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit, new OpRemoveSpellEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffects, new OpRemoveEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffectsExplicit, + new OpRemoveEffects); + interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 0fc0da9d06..c113a32754 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -393,6 +393,8 @@ namespace Compiler opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, opcodeRemoveSpellEffectsExplicit); + extensions.registerInstruction ("removeeffects", "l", opcodeRemoveEffects, + opcodeRemoveEffectsExplicit); extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 671bf2ff7e..4cd9ec3fff 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -372,6 +372,8 @@ namespace Compiler const int opcodeRemoveSpellEffects = 0x200022b; const int opcodeRemoveSpellEffectsExplicit = 0x200022c; + const int opcodeRemoveEffects = 0x200022d; + const int opcodeRemoveEffectsExplicit = 0x200022e; } namespace Transformation From 55c5d7cee445bf7ae83580370f3b11c1694f44f6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 15:54:23 +0100 Subject: [PATCH 124/251] Implement Resurrect instruction --- apps/openmw/mwmechanics/creaturestats.cpp | 6 ++++-- apps/openmw/mwscript/docs/vmformat.txt | 2 ++ apps/openmw/mwscript/statsextensions.cpp | 16 +++++++++++++++- components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 2 ++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 6fc8f25979..a21796ce68 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -266,8 +266,10 @@ namespace MWMechanics if (mDead) { if (mDynamic[0].getCurrent()<1) - mDynamic[0].setCurrent (1); - + { + mDynamic[0].setModified(mDynamic[0].getModified(), 1); + mDynamic[0].setCurrent(1); + } if (mDynamic[0].getCurrent()>=1) mDead = false; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index cf27b7d6eb..40377327a6 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -366,4 +366,6 @@ op 0x200022b: RemoveSpellEffects op 0x200022c: RemoveSpellEffects, explicit op 0x200022d: RemoveEffects op 0x200022e: RemoveEffects, explicit +op 0x200022f: Resurrect +op 0x2000230: Resurrect, explicit opcodes 0x200022f-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index b1c9698cac..f053e9a97e 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1104,6 +1104,18 @@ namespace MWScript } }; + template + class OpResurrect : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + ptr.getClass().getCreatureStats(ptr).resurrect(); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { for (int i=0; i); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveSpellEffectsExplicit, new OpRemoveSpellEffects); - + interpreter.installSegment5 (Compiler::Stats::opcodeResurrect, new OpResurrect); + interpreter.installSegment5 (Compiler::Stats::opcodeResurrectExplicit, + new OpResurrect); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffects, new OpRemoveEffects); interpreter.installSegment5 (Compiler::Stats::opcodeRemoveEffectsExplicit, new OpRemoveEffects); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c113a32754..ceffa4c9ae 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -395,6 +395,8 @@ namespace Compiler opcodeRemoveSpellEffectsExplicit); extensions.registerInstruction ("removeeffects", "l", opcodeRemoveEffects, opcodeRemoveEffectsExplicit); + extensions.registerInstruction ("resurrect", "", opcodeResurrect, + opcodeResurrectExplicit); extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 4cd9ec3fff..a211f167a5 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -374,6 +374,8 @@ namespace Compiler const int opcodeRemoveSpellEffectsExplicit = 0x200022c; const int opcodeRemoveEffects = 0x200022d; const int opcodeRemoveEffectsExplicit = 0x200022e; + const int opcodeResurrect = 0x200022f; + const int opcodeResurrectExplicit = 0x2000230; } namespace Transformation From b22dd40b41e5098c3c829603c538f6afedb18ab1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 17:06:05 +0100 Subject: [PATCH 125/251] Implement Paralyze magic effect --- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 5 +++++ apps/openmw/mwmechanics/aicombat.cpp | 3 +++ apps/openmw/mwmechanics/aiescort.cpp | 1 + apps/openmw/mwmechanics/aitravel.cpp | 1 + apps/openmw/mwmechanics/aiwander.cpp | 1 + apps/openmw/mwmechanics/character.cpp | 7 +++++-- 7 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 307ad3d7cf..d665523730 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -517,7 +517,8 @@ namespace MWClass MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } - healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f); + healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f) + || (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0); if(stats.isWerewolf()) { healthdmg = true; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ec3b861374..108be6159e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -797,7 +797,12 @@ namespace MWMechanics iter->second->updateContinuousVfx(); for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( + ESM::MagicEffect::Paralyze).mMagnitude > 0) + iter->second->skipAnim(); iter->second->update(duration); + } } } void Actors::restoreDynamicStats() diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 39a97df6c1..6f643aa68c 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -91,6 +91,8 @@ namespace MWMechanics mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + + // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; @@ -105,6 +107,7 @@ namespace MWMechanics float directionResult = sqrt(directionX * directionX + directionY * directionY); zAngle = Ogre::Radian( acos(directionY / directionResult) * sgn(asin(directionX / directionResult)) ).valueDegrees(); + // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); mPathFinder.clearPath(); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 3615c8546e..5099625c06 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -161,6 +161,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; mMaxDist = 470; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 08d7586388..f56c759967 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -97,6 +97,7 @@ namespace MWMechanics } float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + // TODO: use movement settings instead of rotating directly world->rotateObject(actor, 0, 0, zAngle, false); movement.mPosition[1] = 1; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index f66f7ca620..93c94a3f49 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -236,6 +236,7 @@ namespace MWMechanics if(mWalking) { float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + // TODO: use movement settings instead of rotating directly world->rotateObject(actor, 0, 0, zAngle, false); MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 351e33f13e..8a73c98afa 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -955,9 +955,12 @@ void CharacterController::update(float duration) refreshCurrentAnims(idlestate, movestate, forcestateupdate); rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); - world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); - world->queueMovement(mPtr, vec); + if (!mSkipAnim) + { + world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); + world->queueMovement(mPtr, vec); + } movement = vec; } else if(cls.getCreatureStats(mPtr).isDead()) From 617a65cdd22b8c93d44b8b6082f629fd2346736d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 17:39:57 +0100 Subject: [PATCH 126/251] Print message to dialogue window after successful trade --- apps/openmw/mwgui/tradewindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index e6cbbf968b..6000de858a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -24,6 +24,7 @@ #include "containeritemmodel.hpp" #include "tradeitemmodel.hpp" #include "countdialog.hpp" +#include "dialogue.hpp" namespace MWGui { @@ -340,6 +341,9 @@ namespace MWGui addOrRemoveGold(-mCurrentBalance, mPtr); } + MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse( + MWBase::Environment::get().getWorld()->getStore().get().find("sBarterDialog5")->getString()); + std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); From e9bd43e2da541ba2ae21ac9a2ad803dce9ccfefc Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 20:54:14 +0100 Subject: [PATCH 127/251] Use GMSTs for bound items/creatures --- apps/openmw/mwmechanics/actors.cpp | 89 ++++++++++++++++-------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 108be6159e..f4cf1d4823 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -408,17 +408,17 @@ namespace MWMechanics static std::map boundItemsMap; if (boundItemsMap.empty()) { - boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = "battle_axe"; - boundItemsMap[ESM::MagicEffect::BoundBoots] = "boots"; - boundItemsMap[ESM::MagicEffect::BoundCuirass] = "cuirass"; - boundItemsMap[ESM::MagicEffect::BoundDagger] = "dagger"; - boundItemsMap[ESM::MagicEffect::BoundGloves] = "gauntlet"; // Note: needs both _left and _right variants, see below - boundItemsMap[ESM::MagicEffect::BoundHelm] = "helm"; - boundItemsMap[ESM::MagicEffect::BoundLongbow] = "longbow"; - boundItemsMap[ESM::MagicEffect::BoundLongsword] = "longsword"; - boundItemsMap[ESM::MagicEffect::BoundMace] = "mace"; - boundItemsMap[ESM::MagicEffect::BoundShield] = "shield"; - boundItemsMap[ESM::MagicEffect::BoundSpear] = "spear"; + boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = "sMagicBoundBattleAxeID"; + boundItemsMap[ESM::MagicEffect::BoundBoots] = "sMagicBoundBootsID"; + boundItemsMap[ESM::MagicEffect::BoundCuirass] = "sMagicBoundCuirassID"; + boundItemsMap[ESM::MagicEffect::BoundDagger] = "sMagicBoundDaggerID"; + boundItemsMap[ESM::MagicEffect::BoundGloves] = "sMagicBoundLeftGauntletID"; // Note: needs RightGauntlet variant too (see below) + boundItemsMap[ESM::MagicEffect::BoundHelm] = "sMagicBoundHelmID"; + boundItemsMap[ESM::MagicEffect::BoundLongbow] = "sMagicBoundLongbowID"; + boundItemsMap[ESM::MagicEffect::BoundLongsword] = "sMagicBoundLongswordID"; + boundItemsMap[ESM::MagicEffect::BoundMace] = "sMagicBoundMaceID"; + boundItemsMap[ESM::MagicEffect::BoundShield] = "sMagicBoundShieldID"; + boundItemsMap[ESM::MagicEffect::BoundSpear] = "sMagicBoundSpearID"; } for (std::map::iterator it = boundItemsMap.begin(); it != boundItemsMap.end(); ++it) @@ -427,11 +427,13 @@ namespace MWMechanics int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude; if (found != (magnitude > 0)) { - std::string item = "bound_" + it->second; + std::string itemGmst = it->second; + std::string item = MWBase::Environment::get().getWorld()->getStore().get().find( + itemGmst)->getString(); if (it->first == ESM::MagicEffect::BoundGloves) { - adjustBoundItem(item + "_left", magnitude > 0, ptr); - adjustBoundItem(item + "_right", magnitude > 0, ptr); + adjustBoundItem("sMagicBoundLeftGauntletID", magnitude > 0, ptr); + adjustBoundItem("sMagicBoundRightGauntletID", magnitude > 0, ptr); } else adjustBoundItem(item, magnitude > 0, ptr); @@ -447,26 +449,28 @@ namespace MWMechanics static std::map summonMap; if (summonMap.empty()) { - summonMap[ESM::MagicEffect::SummonAncestralGhost] = "ancestor_ghost_summon"; - summonMap[ESM::MagicEffect::SummonBear] = "BM_bear_black_summon"; - summonMap[ESM::MagicEffect::SummonBonelord] = "bonelord_summon"; - summonMap[ESM::MagicEffect::SummonBonewalker] = "bonewalker_summon"; - summonMap[ESM::MagicEffect::SummonBonewolf] = "BM_wolf_bone_summon"; - summonMap[ESM::MagicEffect::SummonCenturionSphere] = "centurion_sphere_summon"; - summonMap[ESM::MagicEffect::SummonClannfear] = "clannfear_summon"; - summonMap[ESM::MagicEffect::SummonDaedroth] = "daedroth_summon"; - summonMap[ESM::MagicEffect::SummonDremora] = "dremora_summon"; - summonMap[ESM::MagicEffect::SummonFabricant] = "fabricant_summon"; - summonMap[ESM::MagicEffect::SummonFlameAtronach] = "atronach_flame_summon"; - summonMap[ESM::MagicEffect::SummonFrostAtronach] = "atronach_frost_summon"; - summonMap[ESM::MagicEffect::SummonGoldenSaint] = "golden saint_summon"; - summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "bonewalker_greater_summ"; - summonMap[ESM::MagicEffect::SummonHunger] = "hunger_summon"; - summonMap[ESM::MagicEffect::SummonScamp] = "scamp_summon"; - summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "skeleton_summon"; - summonMap[ESM::MagicEffect::SummonStormAtronach] = "atronach_storm_summon"; - summonMap[ESM::MagicEffect::SummonWingedTwilight] = "winged twilight_summon"; - summonMap[ESM::MagicEffect::SummonWolf] = "BM_wolf_grey_summon"; + summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID"; + summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID"; + summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID"; + summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID"; + summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID"; + summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID"; + summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID"; + summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID"; + summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID"; + summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID"; + summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID"; + summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID"; + summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID"; + summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID"; + summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID"; + summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID"; + summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID"; + summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID"; + summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID"; + summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID"; + summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID"; + summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID"; } for (std::map::iterator it = summonMap.begin(); it != summonMap.end(); ++it) @@ -489,15 +493,20 @@ namespace MWMechanics ipos.rot[1] = 0; ipos.rot[2] = 0; - MWWorld::CellStore* store = ptr.getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), it->second, 1); - ref.getPtr().getCellRef().mPos = ipos; + std::string creatureID = + MWBase::Environment::get().getWorld()->getStore().get().find(it->second)->getString(); - // TODO: Add AI to follow player and fight for him + if (!creatureID.empty()) + { + MWWorld::CellStore* store = ptr.getCell(); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); + ref.getPtr().getCellRef().mPos = ipos; - creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); + // TODO: Add AI to follow player and fight for him + creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); + } } else { From c0fbcf413f54d2cf75301f8c86855b0ad77a6f0a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 22:44:35 +0100 Subject: [PATCH 128/251] Use the playermagic and playerfighting control switches --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 1ad4095155..9090c97479 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -676,7 +676,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Not allowed before the magic window is accessible - if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic)) + if (!mControlSwitch["playermagic"]) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); @@ -691,7 +691,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; // Not allowed before the inventory window is accessible - if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) + if (!mControlSwitch["playerfighting"]) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); From 2a7d610f87f40d56d28156603bafd2f4b69b5981 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 22:55:17 +0100 Subject: [PATCH 129/251] Implement GetSpellReadied instruction --- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 17 ++++++++++++++++- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 40377327a6..b4ac8e154d 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -368,4 +368,6 @@ op 0x200022d: RemoveEffects op 0x200022e: RemoveEffects, explicit op 0x200022f: Resurrect op 0x2000230: Resurrect, explicit -opcodes 0x200022f-0x3ffffff unused +op 0x2000231: GetSpellReadied +op 0x2000232: GetSpellReadied, explicit +opcodes 0x2000233-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ea4824c299..8d435959b1 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -465,7 +465,20 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push(MWWorld::Class::get(ptr).getNpcStats (ptr).getDrawState () == MWMechanics::DrawState_Weapon); + runtime.push(ptr.getClass().getNpcStats (ptr).getDrawState () == MWMechanics::DrawState_Weapon); + } + }; + + template + class OpGetSpellReadied : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + runtime.push(ptr.getClass().getNpcStats (ptr).getDrawState () == MWMechanics::DrawState_Spell); } }; @@ -776,6 +789,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawn, new OpGetWeaponDrawn); interpreter.installSegment5 (Compiler::Misc::opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadied, new OpGetSpellReadied); + interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellReadiedExplicit, new OpGetSpellReadied); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffects, new OpGetSpellEffects); interpreter.installSegment5 (Compiler::Misc::opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); interpreter.installSegment5 (Compiler::Misc::opcodeGetCurrentTime, new OpGetCurrentTime); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index ceffa4c9ae..43dcf0ba46 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -251,6 +251,7 @@ namespace Compiler extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); + extensions.registerFunction ("getspellreadied", 'l', "", opcodeGetSpellReadied, opcodeGetSpellReadiedExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index a211f167a5..8742ba946d 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -205,6 +205,8 @@ namespace Compiler const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; const int opcodeGetWeaponDrawnExplicit = 0x20001d8; + const int opcodeGetSpellReadied = 0x2000231; + const int opcodeGetSpellReadiedExplicit = 0x2000232; const int opcodeGetSpellEffects = 0x20001db; const int opcodeGetSpellEffectsExplicit = 0x20001dc; const int opcodeGetCurrentTime = 0x20001dd; From be2ebc5cac8a23b9feb18d6d374597a3c6b39294 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Jan 2014 23:33:14 +0100 Subject: [PATCH 130/251] Closes #1081: Implement disease contraction --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwclass/npc.cpp | 4 +++ apps/openmw/mwmechanics/disease.hpp | 53 +++++++++++++++++++++++++++++ apps/openmw/mwworld/actionopen.cpp | 4 +++ 4 files changed, 62 insertions(+) create mode 100644 apps/openmw/mwmechanics/disease.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 57773507fe..8c8a1b3248 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,6 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting + disease ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d665523730..08947c4c0f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -21,6 +21,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/disease.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -604,6 +605,9 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } + if (!attacker.isEmpty()) + MWMechanics::diseaseContact(ptr, attacker); + if(damage > 0.0f) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp new file mode 100644 index 0000000000..d3ea825cf5 --- /dev/null +++ b/apps/openmw/mwmechanics/disease.hpp @@ -0,0 +1,53 @@ +#ifndef OPENMW_MECHANICS_DISEASE_H +#define OPENMW_MECHANICS_DISEASE_H + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" + +namespace MWMechanics +{ + + /// Call when \a actor has got in contact with \a carrier (e.g. hit by him, or loots him) + inline void diseaseContact (MWWorld::Ptr actor, MWWorld::Ptr carrier) + { + if (!carrier.getClass().isActor()) + return; + + float fDiseaseXferChance = + MWBase::Environment::get().getWorld()->getStore().get().find( + "fDiseaseXferChance")->getFloat(); + + Spells& spells = carrier.getClass().getCreatureStats(carrier).getSpells(); + for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(it->first); + if (spell->mData.mType == ESM::Spell::ST_Disease + || spell->mData.mType == ESM::Spell::ST_Blight) + { + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < fDiseaseXferChance) + { + // Contracted disease! + actor.getClass().getCreatureStats(actor).getSpells().add(it->first); + + if (actor.getRefData().getHandle() == "player") + { + std::string msg = "sMagicContractDisease"; + msg = MWBase::Environment::get().getWorld()->getStore().get().find(msg)->getString(); + if (msg.find("%s") != std::string::npos) + msg.replace(msg.find("%s"), 2, spell->mName); + MWBase::Environment::get().getWindowManager()->messageBox(msg); + } + } + } + } + } +} + + +#endif diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index 728b6e32bf..e9d8b47161 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -5,6 +5,8 @@ #include "../mwgui/container.hpp" +#include "../mwmechanics/disease.hpp" + #include "class.hpp" #include "containerstore.hpp" @@ -21,6 +23,8 @@ namespace MWWorld if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return; + MWMechanics::diseaseContact(actor, getTarget()); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container); MWBase::Environment::get().getWindowManager()->getContainerWindow()->open(getTarget(), mLoot); } From 09d491d1baeb0d08a94ed4ce6cd74f25c838cf2e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 01:13:19 +0100 Subject: [PATCH 131/251] Move a piece of functionality to its appropriate place --- apps/openmw/mwgui/bookwindow.cpp | 14 ++++++++++++++ apps/openmw/mwgui/bookwindow.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 11 +---------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index efe089689b..c28ef96ef1 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -44,6 +44,11 @@ namespace MWGui adjustButton(mNextPageButton); adjustButton(mPrevPageButton); + mLeftPage->setNeedMouseFocus(true); + mLeftPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); + mRightPage->setNeedMouseFocus(true); + mRightPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); + if (mNextPageButton->getSize().width == 64) { // english button has a 7 pixel wide strip of garbage on its right edge @@ -54,6 +59,14 @@ namespace MWGui center(); } + void BookWindow::onMouseWheel(MyGUI::Widget *_sender, int _rel) + { + if (_rel < 0) + nextPage(); + else + prevPage(); + } + void BookWindow::clearPages() { for (std::vector::iterator it=mPages.begin(); @@ -89,6 +102,7 @@ namespace MWGui parent = mRightPage; MyGUI::Widget* pageWidget = parent->createWidgetReal("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast(i)); + pageWidget->setNeedMouseFocus(false); parser.parsePage(*it, pageWidget, mLeftPage->getSize().width); mPages.push_back(pageWidget); ++i; diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index ef87dd9c4c..f8821ab50b 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -25,6 +25,7 @@ namespace MWGui void onPrevPageButtonClicked (MyGUI::Widget* sender); void onCloseButtonClicked (MyGUI::Widget* sender); void onTakeButtonClicked (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); void updatePages(); void clearPages(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9090c97479..da0a349d0b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -19,7 +20,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwgui/bookwindow.hpp" #include "../mwmechanics/creaturestats.hpp" using namespace ICS; @@ -591,15 +591,6 @@ namespace MWInput mMouseWheel = int(arg.z); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); - - //if the player is reading a book and flicking the mouse wheel - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Book && arg.zrel) - { - if (arg.zrel < 0) - MWBase::Environment::get().getWindowManager()->getBookWindow()->nextPage(); - else - MWBase::Environment::get().getWindowManager()->getBookWindow()->prevPage(); - } } if (mMouseLookEnabled) From 12691040d1a50f84cd23f61840bdb5f4587cb165 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 02:49:10 +0100 Subject: [PATCH 132/251] Fix incorrect disposition testing and get rid of of a related hack that is no longer needed. --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 11 ++++------- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 +- apps/openmw/mwdialogue/filter.cpp | 4 +++- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 3951cedcbc..b979865628 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -251,7 +251,7 @@ namespace MWDialogue } } - void DialogueManager::executeTopic (const std::string& topic, bool randomResponse) + void DialogueManager::executeTopic (const std::string& topic) { Filter filter (mActor, mChoice, mTalkedTo); @@ -262,12 +262,9 @@ namespace MWDialogue MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - std::vector infos = filter.list (dialogue, true, true); - - if (!infos.empty()) + const ESM::DialInfo* info = filter.search(dialogue, true); + if (info) { - const ESM::DialInfo* info = infos[randomResponse ? std::rand() % infos.size() : 0]; - parseText (info->mResponse); std::string title; @@ -509,7 +506,7 @@ namespace MWDialogue text = "Bribe"; } - executeTopic (text + (success ? " Success" : " Fail"), true); + executeTopic (text + (success ? " Success" : " Fail")); } int DialogueManager::getTemporaryDispositionChange() const diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 1b7abed45a..c9aad10220 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -44,7 +44,7 @@ namespace MWDialogue bool compile (const std::string& cmd,std::vector& code); void executeScript (const std::string& script); - void executeTopic (const std::string& topic, bool randomResponse=false); + void executeTopic (const std::string& topic); public: diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 11dccde42d..9d08debffb 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -5,6 +5,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -133,7 +134,8 @@ bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info, bool invert if (isCreature) return true; - int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); + int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor) + + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(); // For service refusal, the disposition check is inverted. However, a value of 0 still means "always succeed". return invert ? (info.mData.mDisposition == 0 || actorDisposition < info.mData.mDisposition) : (actorDisposition >= info.mData.mDisposition); From 557652112f044e1823fe9bd36d59f24e5f135d30 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 04:21:44 +0100 Subject: [PATCH 133/251] Show the target HP bar also when casting a heal effect (same as MCP) --- apps/openmw/mwmechanics/spellcasting.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7849eb1651..52fb0805a5 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -68,6 +68,12 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effectIt->mEffectID); + // If player is healing someone, show the target's HP bar + if (caster.getRefData().getHandle() == "player" && target != caster + && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth + && target.getClass().isActor()) + MWBase::Environment::get().getWindowManager()->setEnemy(target); + float magnitudeMult = 1; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) { From 710a1e2f37fa51614b18bf2352cc6ebf722c9f77 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 04:39:06 +0100 Subject: [PATCH 134/251] Smash case for manual reference IDs as well, consistent with references in data files --- apps/openmw/mwgui/inventoryitemmodel.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwworld/manualref.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 672ea9c16f..a16e67a7f5 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -72,7 +72,7 @@ void InventoryItemModel::update() // NOTE: Don't show WerewolfRobe objects in the inventory, or allow them to be taken. // Vanilla likely uses a hack like this since there's no other way to prevent it from // being shown or taken. - if(item.getCellRef().mRefID == "WerewolfRobe") + if(item.getCellRef().mRefID == "werewolfrobe") continue; ItemStack newItem (item, this, item.getRefData().getCount()); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index c14971f6e2..70295c4c71 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -425,7 +425,7 @@ namespace MWGui // NOTE: Don't allow users to select WerewolfRobe objects in the inventory. Vanilla // likely uses a hack like this since there's no other way to prevent it from being // taken. - if(item.getCellRef().mRefID == "WerewolfRobe") + if(item.getCellRef().mRefID == "werewolfrobe") return MWWorld::Ptr(); return item; } diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 1cdcd8484e..0b21fd78b4 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -64,7 +64,7 @@ namespace MWWorld // initialise ESM::CellRef& cellRef = mPtr.getCellRef(); - cellRef.mRefID = name; + cellRef.mRefID = Misc::StringUtils::lowerCase(name); cellRef.mRefnum = -1; cellRef.mScale = 1; cellRef.mFactIndex = 0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 083c1cf0e4..c23c63e2e7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1967,11 +1967,11 @@ namespace MWWorld { InventoryStore &inv = actor.getClass().getInventoryStore(actor); - inv.equip(InventoryStore::Slot_Robe, inv.ContainerStore::add("WerewolfRobe", 1, actor), actor); + inv.equip(InventoryStore::Slot_Robe, inv.ContainerStore::add("werewolfrobe", 1, actor), actor); } else { - actor.getClass().getContainerStore(actor).remove("WerewolfRobe", 1, actor); + actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); } if(actor.getRefData().getHandle() == "player") From 1b96c5d2668cdde9f3c84caaa61d4b851bfe8960 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 05:13:53 +0100 Subject: [PATCH 135/251] Console improvements: Show scrollbar, allow copying text from the history --- apps/openmw/mwgui/console.cpp | 2 -- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +++ files/mygui/openmw_console.layout | 5 ++++- files/mygui/openmw_console.skin.xml | 13 ------------- files/mygui/openmw_font.xml | 25 +------------------------ 5 files changed, 8 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index b8d20709d9..c15cc7b1d4 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -119,8 +119,6 @@ namespace MWGui // Set up the log window mHistory->setOverflowToTheLeft(true); - mHistory->setEditStatic(true); - mHistory->setVisibleVScroll(true); // compiler Compiler::registerExtensions (mExtensions, mConsoleOnlyScripts); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index da0a349d0b..8f19fb02eb 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -497,6 +497,9 @@ namespace MWInput edit->deleteTextSelection(); } } + } + if (edit && !edit->getEditStatic()) + { if (arg.keysym.sym == SDLK_c && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL))) { std::string text = edit->getTextSelection(); diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index 7d24a283e1..0c9a97d043 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -7,9 +7,12 @@ - + + + + diff --git a/files/mygui/openmw_console.skin.xml b/files/mygui/openmw_console.skin.xml index 219cce39ae..470451a0ed 100644 --- a/files/mygui/openmw_console.skin.xml +++ b/files/mygui/openmw_console.skin.xml @@ -2,19 +2,6 @@ - - - - - - - - - - - - - diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index 726bfb281c..e4037561d7 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -1,31 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - + From af3569665c8ed1abdc7f61afba2b174738cf6492 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 05:19:08 +0100 Subject: [PATCH 136/251] Remove unused font --- files/mygui/CMakeLists.txt | 1 - files/mygui/EBGaramond-Regular.ttf | Bin 405476 -> 0 bytes 2 files changed, 1 deletion(-) delete mode 100644 files/mygui/EBGaramond-Regular.ttf diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 70fdf42489..ef223a6177 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -8,7 +8,6 @@ set(MYGUI_FILES black.png core.skin core.xml - EBGaramond-Regular.ttf openmw_alchemy_window.layout openmw_book.layout openmw_box.skin.xml diff --git a/files/mygui/EBGaramond-Regular.ttf b/files/mygui/EBGaramond-Regular.ttf deleted file mode 100644 index 3f6f6c191d9d47870201c2979caf8acbb41a7e63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 405476 zcmeFa2bfhw(l_3H^1XA@+&noaCg))?Fu*VjX&7LD0Z9TPIfD`vR20R;BBHCvqN^;6 zEFvNzqGC>%b6^lrL6=ojgggIV*SRRW>%L*%_y2s)_dMTRx=weWJ{_v7tE#)J8^#%9 zZUBWP_UhHzZ^DunG8p4Ih>h*t(6iSgd}l7gjd0t04;(PqKV|jHj7dK-=FRLqcu4nR ze_$D7fkei7of$AVzvz-%@7&DT=vaI|X8g3VGj|--lM%ig1)Lr~e{Q1O-FJp*X$SB= zZ{o~J(|XqD-Hq_Kj9D(7GY|BHkNd8QvGH@6a=dBs#bYNJCF@r) zwOM>$HW?A#R#!U0s1Hk>JZF7+#BF>#{D0<;GMVq=kHNp6?}z_!{y6*x_+j{8mzFRtEtQrsSz4w%$hfjo*~uj3 zFUnuw-=#bZ{~qO0`1dIX;eS&3n#sxue#u{S$DJ(f1RC7rmv)5!ocI-`7@c;ZA8yRmf53QtAH&6HuM|<*i134Eq&qt zyS(ISV`pE=ywfh7b}5UVbIIb1St6AJfVv4BGd2)3Ai|@m> zDdkBeQl(Tc^^%&U7O71dElrT7NHeAR(&f@h>1yeE=@x0DbeFVM+9vIk_DK7sr=;hl zSEaY452VkeFQk*wchVV|$(n4F-EvTlms8~~a)DeXSIgbyM!CP-DvyxI$P?wM@@#pb zyi~pt6tzQb2h0#qt|I@_1m_TJruY`}9~F?d6C6!3l#-7Se!1?~7|G0`oML_?m@goI zpZu!`?jyKBK)E~l?;_ZXAmzy~Q=UfeZO|I=?Q_B}ZMwjpLjH{+o+k*uG)8^%ykBmj z^xf(0x8%2y|0uyL2(}WeA-Ip=y8=pPp7a%eiQMxntxS$ng{^8urUNA9# zVV2IR9A%dj@DTZH=lD|n`t)jOH1aG96 z%Tx_mDx{c!1RVrz1kE}ND6trD(i)7>2G*BFv;J%pOJ+;)D`M;LD+WJZ4?Zj8Ln+&V zUm4qtUpadYzY2C7zpf-DDtS4tVpaSeeh;fx?pGdQ-IVRhcGg3A5YnIla^NA>6B6JN z)~K9Teq>F`8RZ=7r*tZvY!Gq%7M<&y4b~N1VXe9a5_yO*5WIf|YH1_s!$|JfA#t8$ z4w5x)$dQ+sM|od)pCu?CDW9-JjGB*R|W^!%e%t~9iu;7E8GaUF>n*%rozpJTL`xl?n<~d zaO>dK!)=0t&cW}4+YYx2ZZF&cxI=I+!X1Wt2ks-d4!AGjpmXrkaAzT|B{%~P5?u1a zMZ+b+rNiaG6~R@&)xtHvHDQd0#7JQ?Xf(wTG{@#N@^2#8E+E@Z{&MousAL2=jU%2w zZx<09O={KM1!P~7zlvZ6#q3i*Mf|lC(?rnxDvHKF zLH4$?2WfsJ_^om?%E~4|=sQ2vVB=1P}iT14pR6X4}nHUw|3*g1Ql-Y_D9z zB>52JT6b>cF7D^C=&xj+$@6##uY|vz_u|dGg}3q1d;-E#_)I<@&&%Of@~ipvc-{iH z5$-Oyt$Z8biRT`;{cumgJrDOPe~aOde*pIx|AL>y^E-Y9JuRY{1Ui;%lAHWNDPBsI zx=5g3sSK_ft~(s)SL!c;dZiJRZj3ZhnkvnfK)2A{m~^GIMgq-B>)|%RZIrQy3G^v}KBd!eXJtt?WQXjPqvb?k()@yudG=p_E?>$aYd4s4RX#4rV@P&83e;8g`0O-av4ifYJ(r)KY0J#S@0P`IeyR z=NEhxe%m6_%O?KKwwt(JLit>n{zAWr+b5~qu9Sm={8TPzP1GueU@JjtHR}$Ulg83n z2DHsA)`ew5m(OGQtbi3l?i54PltIE&u&%_ft0A8nSTEL_H9|%;vlcd#4P$MPQR^U^ zo`aNm5c>2FkSAwV4!L2~BXpZ?*B!c3cj=M3NB8PJJ)j5mC_P$_(PQ;EJqgkU+ADv7 zzo@*Tyvh`W#JCCK8INZw`a6XgfD-keA!^T?1jIZ;Xu69r-oc9@Et$}nQU4M^h8i85n(A84b8fa4Y^k@hnrT@G9Vd@{J-!F$9z-26^VAr32U?wDCF25q?045`Rkh z6lHSMMa(=oWax*0X@Wb+&&qj#9jMO)_B?x)y~RFYpRq3><-TKQprmVW_&UCxZ{nNzeSACL#rHzi9^xnv@iO7(_YpMxuaN&5f-wS0R8MaDt0+E6K)I3L=2FaQf++;E2~HsR8o?a`N@o5~ zkiTmuG&#OS_!YA)RG%<@y!6%k1gR{^Y$=r@QM3U=u_%mf=t>x`#moXuQ^TC#E`2ca1#gLm2Hb)a6!p(<+{=ink zT@42vf`4ea9iQF!9kB;&?6Z12nIa@9D;#IFmMS5KEc2#ID1#%hixLbT|iD_ zhaI69qCHMyA>6+vJ(#eG($D(!TXta%yXb8xL34Cuk^e&ar#>~mk__m-3iMYSS}kfW z{`R2m=W8y)zj8Pc659L)d=45>(2N#Zy!hj(<`X_}Kfp3GEHrxY_xHRIVd92({=yH# z>7WgXKa)4&35|%t#3RKEp08p>2oURRJWaVl;E`ad_7eQ1n$UTdXyD)cB*Bn|^(mgF ziIgeYD}YyM^N?n@hP5>Inzj=$F?+PD;IGlv!vBPJ7Vt8WC*P!93;%wC zXH~K0^_*G=cv2Pe%B?Iwnis+l`S3VJ)TdZT9bQ9l7QrP{+j#_sij_Iuq=*v75OgR< zk=qf<^N5&7vTs!{;Bg_z*l7X(6$Y4}@8l0-uJ)AjjPk7VobrP5lJYXeVVXSRN8TY{t&6d^*_ag29{D)5OWo0nU<70uGhYQ|yQW8e`uo4#ZF$*v-l^#7t8`C#a)U>QJ_^3)cxJh;>3P ziFHEW3>^o5SSL)xL#z$Th*7aVr$gtkK(mOzsIxN%b3((2gfesg+kqsT((jD-BDOXXqr2k^wJpV!M{`cB>e#LY;t(fkn71QaoVj2=F zro0!em`?vc(0=cK(Ek4p9)15y`S|~r@(KJ0`J|06nlqk#GJfpTx$N`tW9MAVz8*h* z+Dvw8{QQDK&H#(J23X8(fMwi0etuyg500N-RLJ82i+Jkz`4t7c3t%BHm@xH{NxW>r z^cmB5^~Bj@$Mf!!MNAXGSUzy-^m)_xQ0&c2EU}GMg`+AyO`%F?+&v zzIvwcUpMEH=@a=)bEb`-$v4cIgSy=bSj4vgmh$`O%qc41JLb$AH;3<@GjHY`zE5Pt z4-(~<66{7$15`0Otr(XM=Ak$-)8Lpf37AbVk6=DQNH@e65-cKEOt6BWA5fwZEm8?7 z$T7p122m_j&N!_n?V<73oTS>(FNdfi(>M>Iph?SEu*arZ% zv0kzUdjK0Dh407g3qX=vM-7zC+kw#!XGE-WJ zxyaShI;`|>#yrI2nH+0o)MBY2{KRUvsHfBtehxL;FHH!CSJIA#gqcdE86$^N%0uZ5 zmq&!-XNSY{!{O^GtjM>>Zz<7YRw&9*)`Xweho85EpBv4umAk@WA)!S217WF0&x~EHP`GSsM=T4?hovpNGQlUp2#7dEsZc2eKMW3e4&s zjt}E8Yjik%TsVHZ`OJZ=6XoaM6n^e7p9M>)9Hrp;;tl2d%0Dqvm!AngL*}zW3O_YU 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%0AXjnz^wYyL zQi=}5TkJL#o>se!cPcTpQltTAK!N!QC=!4*A2TDf07?0TM-vb>)lNu&?|SK*_*8=t!2t|fQ$eyd_#;fIFp zzbu^D_1vEdKXO#hSaz(x>z20OrnYSlZ&+|{RTKZ#gN1+p`uRd(#1Jysbh2Jk{ZvJ^ zZe}p9=Vez#+q|r=BIRFk|ArQvDC21M%`~UJqsbn9n^}LuAD-AU`or0GoISWSGt}1m z!iha^Jh*n@X+(Lz8&$X`e1@W{(^^$xUI`Z+>Jo44AG;(DBgT3fzq%FvuPDw%Ytmd) z6^W8OE@g=>f`5Es2#-W72@VEWnCJ9d4mdRkzakbgIs-|^3&O``A3g+C28Z-;>Z7Et z!Dy1!eP!!iY$BoLpxi?0u{wqj*;}!Vt0SSkT}Im(630u1;)M}ezee8^s1N zTeJscH?A<^E8AKt729i4&FqWV5Ao@nkF;8pnpM;V^PSvNIEId#3$<)$I`NIgOTMx3 zG#>aLNt#a>(JJE2CfdA!9V)#CvT`^A3Lb7^Ejn>}7KxwHz;d#dMNfe@S$$r)a!cP; zm9j4#^;l~-qrLE^Lnl}?B4D2O$(2j{hVZhE!@-p`QS<+nwG3Myu zS%Pf)jS08>rkhFFC&$?gI$4rrom;Qfx*VZ{TET5fSzIP57Sx&S$~%XQOlHZONwV=k zy@Rcfx-6qDk;=v(d*H6G%c5X(9lOPD(rN)2PSuV&=*PJ8sya~cTb7}Q;88)5ol{kk%XOIft%BXFz?wUw8^-_@ACju+ z`ZC;ODwGCa#4Zv2OL3VfJDRNJKrEo8%eHvR0yFHv=9xYYNxC}OV-#g?P3TAA{(QYp zPTn|jCRw%?-|a!}F4Pr@M^_k9*pVkaan-6?15#ISz=X;RHli!zAAFziqo+Hm@mWoP_!=dQCF*rhs7K z`%s^F<`G`4*Wo5@uR)B?j+WzzX=R6A7m)U`ZNL!I=r7Dh)SPm}NvOc`C~$@3pspUE zu#6$;bi#d1_zpC1N(TzRCpi@WtpH~wao$GB@pQ^-n%HkA*hTD%Pe3asyMY;sxmk-O zXq_5&UBi#DI`RHMNaidOltEM=um+0(F-Ey@*tWt^`+D05BGQ6ZZ5O?aQ@5k=Q0G)k zVAR`L zZ!Y@#Bva|oXq{3)<@0Rpo7<=nC4*S+?i|o*jW|%#tPihbAH&IxIFPbA=4dintY410 zj7F4b+cienZy4=}^F@C!_yW!!Jg?e`y$%vTAJOSc)Lv4F!SgHKBt*Ng3Xo)Wuq_;7b%_Ik|S!d#m9y5c=KUp$ptd_cOx@@dfZz|@@ z0zcS(;0G$>FXD=$SSnVMjZ+zC+4hzp3$uGN*ChL_Y*{|1?5S`CUa9Pz)SJiVDIB%P zm-v&&^E!dK>>zQHcu`y@OZ<}*2H`A6u$r7^&1KpvVCyA@bBAOk`Uz zIzovYl@W}CAA&Tf{65ZQtdV`3PO$P;UM~DE(Wf!Sni_m^)%s9%QBq8x=~jxSHBCld zXOf+=Pq=Bdz~h`#R5VB&hT9dZ+My=sUzv|pcU{cNHpXxwL(o>s@CKcLLS`+ijmS*9 z){-m~T56?9D9xtFQCFmRFU+0xoS&Dp7RjOBu~la@qs$D4CyRp4fb+I=1{bGsIs&OP zS=m*1iyS>e?My{ynhE$Oh>aE(ptAp2od}x0Qjk-oHZ>)w%wlSI0a>~R-Jel|^s?7o z;VRdprpp%bO{hY`Daj(sA}XmrRqYA*>pgCDnc8GR=u(kw%Gxqx@2VF8k9T8jbAyD> znGh*0t*snGOvwbq4KOB|pc)?T0EKVxvX-D-3)G&;yb>BB)0DGVacpC59CJYX{CN}C z+)B`9N37y_1^8O7Qee&6!n+2$+mw5*4#g;p-N4CVHyWjoAu*Ifqorn7bFPPSx_m5J zrNc=|X+e^NBgc4ygcH37l3%b9oSA2~bKb&}g`d`>bQ5Ty!^yOLFS|JV=;m;IKl9Rd zRKH8VFMMqE#9iu%W|kGVFwduhejU}Z1fCy7LAXXnq_IItdi(}XAx2q(hmKKY#(u@$ z12`@K9P0@`K=nO|e^Qz=6&>P)+{!>oAq79!gjfL;;b72m#j=dh7+w{wsx1g|PyR3UJe+D^>_hHx?iE#qYBh2+ zrDv}>)Y}*_Qr=$_@Z_`w)S+SdFuZ7B34*^Es!Bw0lwz)tW_s~H$O;rZ8Aya0#vNr# zO1Lw?`_RK-g;|U$7AhQw0*lJF(uh#8lnxm2aHy3>q!;-StWl?R8Z|6fgS*kAb7&+E z{$ztr$9ETQWB#x#EwCD1)PR6^a$>s0$+KqD)saGX)i#ZYbU(Fd$32dy<@6r(*4V;A z%qFCQYP(i%ceXHJ%|@*zy`-)-%6b{r)AM1E>~q_sp89TA&~B*7x^+^;eQ|p{Y@S(o zc_SmMWdX7k_lgK8bQKwG_oh0X+h((C`MH||%^Zi&g<4{f%Jrbx925LTt#bz-g zjz2v5#Yxya*+jZraw@inE}dm}hux+1;XKsyimgQA_whe(7Lk>e*gQdXV)8-CGPh1q z%u04%6xQI%FKzh7O|DE=@0=scmfnB4wl!|oNp<@d-Sl;*!L@H_?G0^oj{ReQ5dJ2HN_J+Z<$LP}sMRH@FWDFKp)kb?V7c0DBkNje9KG7hsZZ~=;T)D#7&@+N~$kpP4EkVNs$F)8mtcD>{IZD zN(7uWdnHU`#fMKQqlL6^yges;x^GKGgH# z$#b=7t+lf7kN@kLsq^Dhd&b`5PjJtuW~c^K3&FRC250s+TEMsC#1{*%4t4%aRrFxl z(nHc&oWFPll2>P~X6TV{c$n!~&1`Dnm#&>N`|7VOT1f@<#&~oi)%8{`{PH#Q_xc(l z_%}u3`w696<*>n~xo7`fcNYF*_ZQD@)zl6gADDM)aieIfHxpq*{XVc<%=I=+dhpoX z!BZny(G>J*AhHdzY=Bn;CvRwFvyMGG`aRv(_8s`<=xc1J_T2X4TN@oXnQPIRTn zVz|zD}zTJx=xAf#1`s;^)oc zV~N>{zBovJYTwxO49@IiKhC7|CMUc=3X9VO;-P0HR(zqcYW!!f9ubs~`}dNw@N7+m zeq0@N>YbVQ9MZowI!a2UQ!h7n|C7ckl=R+b0I%hM*PH;+X>5+NqEZkXk;zxaT^vI| zJb~baN(u=u#_7Nr$x}5$0xzNyuupQ@TRXf5v$4FxxHKD|v2UppIPR5`r$-cnx8J#R z-;8*6nbFY|%UiJZ?dAJ`?v^2U-QwGl}^y>LZNWq5wIadE3W{ zxfKgf%TlEF@X|LdjKTcj1ka^r0!%CgXO@rsk^d01vKlZ*5I;bihMwJncds0|ZD8R>lNsKh!rQi44vmP~cCS&d z)tLm7VANX7PQ$|@cUS$I8yBp6aCd(F_pkboyTHpM3EqbD6olHFyf`sgEgJl@we@fA zS^LD^tq(1`@#P_TIWBA0h+6gPp^#B@i!RZsft8OWboiB}u~F8_yg_215@MiglIkNq zy?kDepyccX#zvXfaHkyJ2grMJFOBqUe|Ja5!IjQ)ByDbEUK?*?K^u5i_)=bv8Sn2= zzF{U~ko0%)>FT((cpf9^f3fTvr0PPrxMr}_zXEwd?=U|n<5!UWcKW{2UV4xI?Ao&a z4*DK5oKjtRYM(te13SjwkK!bkx5nR-bWh}WSxWD1fMKh$59HiVb(FrZ z8t9Me90!+EasvTDng!9X`@L<0kfK;k~M!;f^L>GqUrTmdwVx7p;DD z@6N9+UH;%!e3en{13W)wUsP3N1tyvcOk`D2+bJoZMTygtfdM|DeS-%vi$olv!srwI zaGL76<}?kpOD3bP%O1{G>&<$xcPsmpXwolkDO{KFN{mgXw+ez)5C4eXmI1%6*Dz*L zGP9Z-nG19qHiO*0R?H=?1wSOYpB#$%WdLCa#s`1MA{_Ii4Y#ar zTDrPxa0CrVTN-R?o@_w z_5yM_8-PP`GtK2>8iTL`w{SF5xZMaqH(|^epGrx#ipe3Gb+P zpxI+7>@~3lQw7Dhtz#cBm)M7qcZqx!5;Ka!YA6YEvVpZc$yq5*M9Gkb5l$pLocEV* z->}1Aw%BXubjQ81j;cOgu)!QTcb&P?UHdgl*ng^WhmkAXHm_N)(c3MRbFzVkfJblU zw2{iwl{g4Q?%{-(-J%Jb)9Z*7VO3esdky;>@?>Lxlh=x)g)k>2L`>6NlSSQ)&Joih z`vzT&d9HI>^TNT7T+eTMH_Y2~)3HNGZa(+}K}yC37fkoteTvNpf~Fy3(k2h+C7UtQAdY^_>QSeL zn-$oI4UQEK`D?#ogIKSOSXdW0TW2nbQd{Czp$axofokARqK;4CFzh`=WiNVCh*nif z1}1tKGf)|Asi^9%@6^^;S2w%9v}WW6PqscGN|nBh<@$vK`zq6wI&NFi)6vw>*3l*- zZTw7ldTmv{F45RoueH13or^5pw|XGbQX8=GQpDYoi%cKr?~3;%eWIo-Sl8IGtf!%F zc9O{EAaTKU~;hD?HFWJCYB$6vFkZ}XX7^=?>@%P#By%8)oX zH=FVEkJ0<>@NCwo^8hW0dArOdIoHfBRsAP_k+#7z+H}!2sA^zKa=^wdVXXY)v<;ro zrn}fCA(h(Ha*I@jVjDc8&GcfM>hYXsk(tq{oT>BsS zZ_ze*Mw{N^IDv5;$-^yROnmWHIq9mmWKGhY5tIu4%EW=3(GsJS%f zC^x`pi{s!KZTe{&>Dhd9g~K3}gUcOZ+J(l&TT z8$cCpCgCqMDJyp?ZDV6w(FXWS+mzuin^P5vZED#Z+5oz=O&R{Os2$~Qq2q{bE!qIW zbUtPH%hsv>$lXNSG_rMQ0|?V`%J7%XGA8bJ+Q!3X(FRbabSylh9iUCWSAsUHS^z)xkN6*=q9O(O zh8(&wpDNMj)LEj<5f3$tJ8J%M%tzA}Ra*s1ym_Rha(Z>hB5@yVcxY=&bk^p(MmBtN z3w_}CEW79Yk{JhX*}k$N(Y9>KzNIm}r8`YnhC`CtPFa7m)O%IlQA5=}8R@8|vUi*5C9f ziwYr}wkNhOcU+cJWLzs(5>T3wTvBu>_TkFHTNa%O(VWedvpqVG$s`N@d9HBbf93}Y zztoChhKIVw?B1|^&HRPu!g{~m)x7qB6{laA#S|I}?;vZ3WBF*%5V6~|nf|BrdXE-o zJ(4cF*1dRg<=N#uzOc_fvNZqtE!*!Qb6-C8B)1fE$Fcht9V8}d9XHyEzXARpB8C)U z4(*|MP#(l2`HdRyh;-tg(8D5$wb>P4Th@1U{+87|$Ey!MeD8nV)&9juDCk?ECvjYE(t8RODcRo-x=vwpS!M$HH z86CcA$rjCIXI1qq>+cQ21Y=Yd!25Tw*hmbi%Yr$Ri)H{_qWBSwgMb(FhC7!8KZusE z_z_7)JCs49tddXwIot0OaED{py44E`_iuye##LotyPef>iy)7_&*A#;R?(!<`<9sO zlJuO`5YyZ)%IbL5m=9N%VO1n=jJcEg*Y6Z{2F57xY#!Sb$6-#fo*`*+^i zJEzun;Wx~Qo*jKvIJq*qlu?;ca_wlaY2$l&wahVxd@)Yjw(cvd_x#{QzuOo8J1_{$ z>qG85|28-|(aqCIyP~_0xgc;PfbvQnT+Bvs3FVrQ$q7FYHF1DBoLHHOuN;l>HoZn# z6WRFe;op2`)q^`aHh<%lHPZ_V&572VX1OV`gYAc)J`kgLZBctZDyv&Wh+v0F}!Zpk<7_ZfnDjbi`&}ou3i#}$ySG)NO z)_@?1hFSIj#i z%W-S7rBCBBOYAe%ht|*DzqGGnW?eoIF{EAQYIk+4J{#C{|Gci#%OdSH_5PI2AzEwm zBWv0go^0>BddS-lOF0tI>n-LJb(?3by?x7UcU`pFQDvxA6Gs+sCN=grcOQ68Le)rc zrqVdB@3Le?f?`5Z6K>qv>|k&vUny*4U|I2j|9is&AMDrH`>K3Fm)3n>crhl zo?Y{={@IbA9K7$Uk^UR@)@xtt-n|#|TY%Wn>!?#OK~EB&_E;64g(BHXyD>hUTKpog?EG(BJMYMw z$M*eX-{GJ9;QL38zp(C}!F^A1dpft?HSg;8Z@T*D2X?%6ywG*{`M{)+yWqtLd zCL-z1XlE$ms_w|JUJ&AfWN56hW;U-qnC_cV*R1G8o3~{AME5*S#Gl z@%~iK5iFb!M`cq$%lu8@e%TQCji?h4z}N5^?!c+h-+HXcSmW@kslIk5=yv#RlEw}gLIV9pIk97$|IjGU#D&Aq698P|{2=ZR z64+s0y4_-I4LQDPa(KIEbaxdVcTTrE>*6ujwb7ln`o#QrTB{DnUrM)fBYMVVaxW9r zdQnPQ9kaXIF7$u5@CS>z(iUkGg==P3&mYPSg(^heAE}Kzl_Czwq8z$`C+FkT0fyDF zGC4#9qM`v3AK*AY1qRTYDoS$5z_1@d5N&xas&;C{hOQM)UA6W3ZP)(Q>eXNU-ny>B zuC~2Bnf|!(u|4%2HvZPQzQ$7j#8v}bMWzLJE#CUdiR<1yv+niB&Za}7n%Q68-nM@8 zv<(j|*g|D@VFB01jZoP=fvWuh z6@En|Py#pB#|;Kw(8`TW5ka;NZ&2TES;*B4TWqo-f=ufo!3N^ZCpjq}u?7A*oO$Mj z?k7E)iMovv&F3z$*)+fg4;vT}QYLUxL9R(d^$n_95jYx2bg$&1!126{Fw#A3Yklji z-ae*dSv$^Yv9@a9iTnNGRD9#TBOA{8e1UzpWz%y{+%dTIZmq}EVClx8&$EsV=jI=8 zoZj2VitSrAwL5E_O`2?d%e&hv7Pbzb=)8Snpd}R8bMF6P@6E&HD2}}0uA{4}tE;Q; z`<}jLx~J#9PiZ8LuF+^Tx<*0>A%P@>KnM^Lw=u|E3o?hnUAi!Y1~80^JhE@LjU ztc@`*VAgnT)@zn!yi~zxS{2@nidOY4X>RnURs15s{e@-6wx` z?^yctyW;H~i4N}o-7myiU3p!&6>D_?FM}|5igSAjT&%UNglQqYi2iMMpt_Z?wHw2p zx%-thhhE-S+e`3fV9}}5SKE0lo@)2s^x(mtTy^!1nH~9c1FH`pS?H^GZ+ZPk_ss1j zfi5XL+;T<~7iLAP7=8G<)4#i|(!cYHM{k{X&W8Q>81ng7+v%^i)13YSx%;c_^jF*I zueQ@)ZKwaA*-m?B--q8aZn41!tvu*gDSjYQjF|^8i1E+-NtAS+E_d{NX znbIQAUO1MaH&*1T8|BE!G{TMY^f8JwQ5^v%Zb31%$jQ+`qm#$U8Xg2Xd1k|nas~YA zd;QifctrXpWV_&UOtntR_T-v=YY+R|XZnRkk1q9&d!nw<)}^wBhc=?(P{w=3Zdv7f zPrIo7;JeRzHTg%#`JslBocJxPT^i-Z7yY{YBjrWp?0#0%17%Kp0J&KN#JNP0mS;Sg z^z>5Ow@FW8K8L;hgs>g_+JQWrh>^T=S;Elw1gVp}k%he9*PDQ{$NWaPqD4*b%CyMc zxQ9cRC*hLl>GrY*k8Jbm>86XybeW4CXu?jxPngb+2dPrP7Z8|lf;kMkd%L(TO@h8+ zd}V`>75aT$!K5QOyl06lXhI69KXfIw5&>Ufo!FC{(=Ir}skW#fmtE7F&fBZW)&@o8 zMZp%C@`^rHl)_?US`Z3?Rcq>BwwV{D3^!4E#bI5#rs+7G-wONqoU@Q(^==79qkCoe6;6M_%Wvbe>Yr*^rWx<;EX-W{bk4p$`k1Ij1c6Ws!wivs$bWL^^sj z+!Y1#%#q35^QZpqH6qFT=-mKprXK#yY)5-jq0|6ue1o3kCA_(Ml%a^}R1 ztJdvO_?#;zIrzh`YZ(RSa95r>vi!2s7A?LweZktzS6;Mf?G;@AQGZ%@`A6>x2|vt0 z{-d|Ocs|Orxg=QHXWtXvJ-b_kc~4=-F8fGh@GRt#l$l;AKp3)co0Gj&ptxG z;sp{*%fpPjR*v4f^coIGvI&^2K;|_QhN{;B;$g+l4R00Z1iY8HI%xQgAtu7pYesx) z7rDH_Sfsr->+jYbircPrhgu`!ll>k=9Sn5mdK2QYm%kPD>AD(H)tyVEv8@Y6{0{GV zNtf>e7d)!)bIak$Nc1>acOKpwYwnLI8%Gyz>v14){Z41bv#hs}>zm--QU`tO7dc(= ze7fw8i-OfH$r;TtJRO#8L%t$jx@}nTjpH@1Q`b_VjC*J)Ki)gAE+A>XGmAn01s!;l z5*B&8BDxewAGf*MhdNVz6YX7UFy429bD@;pnH_3Oj;>|?6+UhG4%R6a=gUuVuqZ^D zxi;xw33S1AyfhSYiK^_B-JBI4{IH|h=AMoOn;X3OLcHL~33hhN3ug_L54kPy88Lbe z!|%bO+v3p0gWqwtF>|91TsDz`0= zoSL$X+kNdJt~`m<&5U+CXm1s^Q2a31{}_KP$81vL3IpD%;FcmaI~YK|nlDc(PDeyq zdf@csN*woO)?UwTysmP|Qbl(O3%RMAZr~cmtybBlO;!Gh(e`l_ZcJ#0pIEv-O7~%C z6$$2hdvHs11TSe|uOYDuLX$x@;F0}&+nQA^$uI&s$@xiH)U$zLw%6u5L2t&@WSi{f z&qv-QWF9;q3yq!8a9We_q}7?6Z}+Tenh<$cI#g!$PR@S7KhLj&uMv0gb?VdH5dfSj z=pAm2V8Sw=3Wp;63ro76dqnVR_$jh+>|pEH+3rjgYZ4(%A$AepA%fn%sBZupN%%_IO5!gd*{? z)}N)RUQrop53rkP9PHVbxp8ii;?hwp;vU0dz=cXslRGs{$y~HSz>VIQmC!IJy6j4< zpEJgBdiH(G@6nE0Ojz1~F^r#D*wV7FC)@Bk)FFTLRSCD$5%t*c!Mtzz0QVM; zh#g$5yBe4F5^4jQQELgei~eQMh%+7%jTrV`kAw7w>TkUyGv;|328=XPQ(`i=X|{9XJgjgynbd-PiAc32_D1iUxbgNY~@RPj}H@{S}^J=ovYvrO(DrKh>6g~(bUc3CCLPYM+e*m8(kGMA1@o%E-4xI5T z55m2}{CK@b#Ns^SUnAUB{v_ER^c}v-h8K2o-=gciuoBD!Y_8@vgGw+9e^iKFfJEEd z(f044?JN!*#dGC)(Ck7UGl)8Yc2g(#jBKs+CRGmaX!xb>Nblh%t(wb?^J3u#dWVSQ zqW>uuR4wp<5R{x{rX=u|jnLh0f!-tI2A%R?z=-`^9X~Jmzv)$kZ0z*xG>17>XboqT z-pob@E{dkmj7Y5Y!|V3U(+dvnYJVcFW3* zH$K>dn5fhoe%Qolm}wIRnB83G|L9LS99FSosWDG z>?@jN6}<{BUyXM^KXvZZEw66*9$!qC3-)cv_%Yr>X;s68=UQbR>X+YR3%Zeo z``vI)ODX3%Ihv1e(DS82Ow5Ra7Ql5OIr#1EE0;UZogqgh-54oShHL9QPa z3W!C!n9ju3PmHD{dLNB8vr%WKUC`a7`p-quu+~*Uy*P8Q+Q~E3{gM zFu%Ku{T*Ib)@)ph$HvIUlyQUfD0aHaBg&KDi=)5LuGntP)<|U2Ngv;=Pbm&~`;o+0 zRjhJ{Qr_Bk>Dq1tRB$1ohRP9@)ut$HFt&F660iJGv8#gTzUDCZ~mts-q<(DpMKxA&pyY? zh=)b%rVH|QuaL1E2fi+7sp!xStQeBu*&gJ{|i zCtxu?puiMfru;)1In{CHsF!z%RwvSJI_z%V(LUwM7I1;1P`W~$Rzpb~$Y=r4xv}z3 zBe9u$q1@TV`n;ywHRc(6#^nj%z~7?@9xXX=pB7_T>z|LdTV1+5VAsa|ex+zdJm{5w z)86V-T*D5$S6Jm!6vmLIQjH-9#MnOJaI|abpf3Ld_K!IDa5nhB-l{BWXMDhVX)pjC z!GDP*P%&yO!Ky+%XOoW}JR5&FdR*%`L24CaMTYD5`b7i=aU}NWv67AViY`&q5RgM} z*LyTanc6sb;OD}xw!YlsqQ@y|7 zD_fduE*_a_k^F>9HUyea{`%Hyf8LZCRXenVis@_(b%xkLfpfdCTnc>PWI3gk6=!xXKi0<)}i;#kuK9U8jy? zSUVx@2)e;?y;P?&%CX6U6@KAiKGae!h<{OzTm&50_=h^;>_KY>POf_ zWUZb=J3C8VUDSuvj%E1d?<&&zPSq><;^9}CO10)Gr6wAK+3(DbbN8TbcD5g*x{YUY z)m_UFB{vC_&VqWdL7}|~(sv)1>nKzGjFFwq2r3gz#wU}?MM>M@Y}n0%CBI$p=dm!_0bq?{p!#+?`Sv7YpTcr_Q@TadU&MEj^`tafdsq z4W$aXtgrHpPn9Hbx|Ay{3Z}N@3gz}f-sre*bh6wMmYmMWp@D%Q|DUQC`>F1gyu({B zQXG}Xa6+#*5HlWXg`~LbJ}&KxY`AW-R$T|=QVZWy$00f2^v}BHzof4(C=*}UI`{#` zrvrS+u{&glLtsA0fcF9&0$13YFCV?mvGv{*9nRG>@`T*&iiX_IM1~ig$5nnh{6H?z z(;e@GM6ZrfpN0nchYvs2ltC2rWI7kl?(+B`+<0JCEb#rMaBUE6c*4ZuOa4}z!FX*& zyN)ZMU0zHFl2Nw&kerat?Z%6_LW|Y##bN(pC$!!G!auQSFz~jrn0idI*{|lF*1jCneeWwrRN*#zRP)zkbcim=hjI&w!bh$J+FW?&~zWWN1nuu| zw(g|QtD?^y&I)Ci-&nc)JYKhjx&7yaivlJ$vnjD>J&Qfd$fc7ERO1+6SkX&EZ))K! z(D>kiV}s4Sw|VZv&6f=?zwS8e#!E+=CeASTF|A9b+Ey-|bD(@yd_=U`;pfjJ_l~wrGzU5lf9E&{oAAtb9k0A3 z-UHj!NjcnxBV>D%rN>5_UGQt+1SSA@{DI4obW7RFE=p1mZkDf-t%YD6{AbEl-#Oud zlb4<|QQUIhKHolPZKZ2ledo?#?tX^1bH_Svwu2NGaec^tG!h8;0PKgBI9Wy!NIh z?{G%en_Z28Lb^Ga8ta46_h`h)cXV$~boZuG>6ydlEtL9PoteRO!W|SjUHi$gG)f;E z)00ogt#WsHc;BeMJJOPB@4rpSPbS^BV{CfAONgxKV+R6VW8URMfl@itia~MJP8iY- zC2(qk%jfACEYjS8TV03>YhcMr_oqq!Vybg(9cCAj$xI;qk?s3rWw4>aZDrHqfDt0G z6|JiUuiLN#aF3qM*4;a>?rsnG;tSXNm5f7GVh#j=LUg>rIQK|=zy{k}3n8YLY?FDL zfES7}&+d-kRa36&!s=Yb2irxlpr;Wpxod6IscJ3{Qc1|6twFqf<8-{xbqlZ>ao*Cw z)`2vwKN)%-=%RSX@HLa(i$@9QM$HVbDN|Sz*kog}IcKD^$t7%*B8QuS>itP)8azS!3_sX1X47=1$Vco_M>Z9xT$agjhZJ{m5ce9<+JsGI_bzAOoqbn)9BpN} z7be53t!&d``jgOWo)A=3MbZ(Ol7{`ghc(!2uekwAh&By7#Ms@Kc|u|Z)vdl;jQ?;M zBoV&{mw(y03(jqh;0hK6gyLPom1Vn4W#K#U`m4QwBqxko5?t=ms{1IlWm1^ z!8hK#0OPbQ_lg>oR7c}>KLkePrx#hhvei-fWoEdP^(_v>j?oI`s8tY7IaQIZI}~-o zwG{3o-?C6}siV-#zsjHD3PfCu@dgn(f;(z+Fb*k!_`s%&R{5Ulx{O-~MZ;blXxFia zzZ>rltp2bK!H8t(frlL72L9$T7jc})N7f#dl}hDp2XBAZ<*-G+nd?@KLfkh)PPo|* z$;Vb8{c0WMya+J6$TaPVFDyO*2C~?jWwHn@f^`e> z2yq+{$w-)!z2fvJLUn4wwQv*@b%U3(K2n`mv=#@F|+&cj^A;TSc4ABTF5g#*9Dc z3FWC+(BUsVd!H;%sh=^`QgL_?D zQTedjx72+*0xnMaw&zxr7tPBeR+ea2Td&`q&Mk7g8$C%xa&vmMwWaG*TON=cqNClj z@5Gx|O0qg|>@l6f$N(Z$*sp-UjJH`u=hn!lTXqg@n&}YmSeA!xg>Pyqw5{p%AuWmM z7M=6_*M92xy-*@08rvIYCD_25DYuoM$vg5jF$;lg2{IAzFZxHm`v%STp3O15Uw7-l zt5c~rLN1{-7}Aws!1>X(CT}3FlNZukJs(%a|q@mLYD12Nq4J=mM!c8R)@YGt1o za^!8WRK3DFn;ju2*gGkS+laQX+-CubvFoOnINe=n13kt!>>K=xL+HBK)9I^%Xvwm@Px z!Z9W{(e(YSIoz@Pxis-qIX%xA~%5y;g^lUpR1FBZ8=<^Iz6nDtkD=BX-^ge2(W)H_KDp zg~LyFKENEPn4rCQ46h@qXA8P&c9+GMUb+(_Rq75ez%}!a2h|He5a{=)rq_B zO}*W-eyl09_{P>rekU8RFk(r(23|Knu99rtU{_|ECrK9eTCmi(%23tU(6;bhXYT5e z8jzaQ-_bU*V&g=yKO#!@$d%K_UGm-;UmC<|SlN?~?p(NWPZ&=WY}u995I^+U_xSU1 zH>VpKFY-_Kq}aJ3@|Hl+7D#SUGt=8Kl%(4lhdy8pv+!vcneqj%JzKT$Z#LxI(|Gf3 zv!xn4!+S2h{rYgDpO?yt!(CWeWT}<$fZy3Ht7>f3!2N;d*7&u>V^(+;%aYIQxa|1P z+@>gro2L;p!sZna>R1vAE7}kLVM*B;jtFo`bFX=u)(-q+QQxgS2=33b_jue(;8hf- z*2;rfxc=FAlH-)I%L*3~nhCE8xJRPdOAi<5+)(n|b;juNo_OUyv`tGEcc`vl%L#+q zt~!I0?Jp?px%QRspK;s7U{?e0Ub(MZjoI|Jme2lNDTG21jQ}wPW$;jyEiDm zEeE~!o=n{8Q20!)FuZCa-`bP%lzljO_m+}lg3fYI6nx&TTLcX8%}u1+fcw*sDJP*=y%ENyvxeeXKbEq zufcF4M!F$q_#^aKvKCs*00wNgVSYI_x6y%uguNIqUszuN#H$O}^7ZAyOre<6?b=z* zLxVl--2=Df+I^Xz94*8y+mb#(9iDVJ8Z+X-V=|%iq9T|6#yP_$O}CBzdSzcSE;|b@ zJ?TDy|Mpn9cVhlf$AU_$+}ap$CmJ|caxlC8m;{I0bE+-1;ZusMB_f8-IPuEBNNc2< z1s6bWR*cQ(G0&L~q208D%z>c^ZZ1t#9l{(VS^^9?@4*rfBss?n2>AKYfn-R2T@EFY zhhlgtjMos#Yxq|`)2#=$@lcPX@w1j~-`?mzv}>q7E{DDNllI9)^Ii`6ts1su_9sV{ zpIYJI5vy3xa*B|c7_59Wv3VSFoI|4*3O?*>{gC615XH}^Qv*4B8e-)vqsxJ_{->~b`lSc!+?c$15>prCp8s-6e4ep|rf zLC#B6xkI$d*N6NP`$JHX{Xu@24PqsTe%Zy@8X?B$8(cG7H`W*ySBP@Y^nQ{Yz!o;d zfGu+VH!=D;MnB=Tzisy>F#0-1f5aU8fGBSHQzOn#PHN_WD{SF4+8`qL`zw6Vat_*X zBJ<!>#R5fca`zxpG9=3qKWcT^(UUu3n z`&_t{Cxr^Wtjh761;MmH|n$wQQwpc(hOWD4ISoCkTEKa+Yyna*( zBKSHrv&iLl&h#Bhnzp}Tk-l}2R&LEq-<8ozELmWnk`B0vICc0yKf z=Y6zibo}XcsV-z+b-pugip?yxFtL>k95Q&rPjs70Q}1|hwj3_S{O00>L~c0$-#pAq zUX^d$-?!-aoh4*nHP#}n^m6xz-4h>v(I2lZP}aEreJoH>-rI(2sq^d#AMc)AWa@w8 z*fZF@Q{_Q+)fD?I`8cr=(5^v^GE@exectfu`J*G#r4w$x{^FCayOVoz_sW?IcduG= z(dc#C&baf6-KXCLno{L0{tVuQbC6Nc9PBRGKr=|kf6OsO&ca$R4Xv6y&qqt1moyP$ zOZRV1#~hB)3tbm6zpZ5VetT^=SoSiXJsCY!u>!IJTHE9Z~SJkAH9k_kjdyX*TYp#G-RnE<1hUC?*Bo5m(;iZ{Q`o zt7Gv#dfL5A@1Tp=#?n(2b1;P2_gH~Ocs2>hTaACQO+~KmudIOky^zG!IvH|o_l2d% zP|ILM3Pw^9SyB3q*>TfD7nE0Dzv#HF6TM3obFbR7I)9Gm@Ee-TE?5QJ(Q-->j^cf? z-*c}Y;@5E5wgX>@q|aHt_JU>IclQj_S^~3$dzRPfJ$j5{Ycf|uZ5A7Iv9^>1eX_4k z!OS$gv?LLf{{?%V$K&IMH7(gb##ue0m|nOb(U$YN<~vT{TA$CjssbiTKId;Hbhdc= z3!K7F+9SQOpuZoNA~;KQ_K)1fe3aTkaf&UB8O)o3_JCh(%^qQo{7AZ(!^q%SRSV8@ zLSLbKrm5-Dnaq^rO2q`RC!1_7O*gk3Sdm>Wy9>I@DsitDVujpLw6CyiO~R3Flw7#5 zUCJkVV!g59<^`*g-a_7{hgpC=v`K<4xd$pZdutjX{|q&Sb^|y>4F!nJIW$scH<}FA zQFKjq8o~}yxjox^`qzoLQ)d%ZacP54bXjeJEq*Edzo+Ni`G#1dOUFU|Sh&2Pi_6I} zEOLa4(-ko~B_kOEx6T#T3zGIhq1%Sc8ZJAv>jVB8-h&*h@JX}%&5DCTY-@~9#-d=T zl-T1!;Dr)X)&H4b$2@2r;Ecv>6B^_c&e-a$C+@ux6ehD`MwKs*h9J zo3g3UU^wD-!SoVG`g%lOjp8tz6X4sG=S!lbEIuAl>eYN)ShsxqUs?kQfu{HoAyiYk z2489!Q(R45Kk$TEIBhS&!Ac%5Oq%EVLnwgUd${YR%4sEovl9+C&wdZb-yi9Hs(UV{ z9hl1m6Mh~68E%oxj>fTUaM^ z&&R^7g5*fPb8 zcM7-59FY(^CQ{XrpkTWwGz=MIt1=v!Ox3C`E}+%R2CJ~knM#DDliV4n6pqNEH685Ao^9i^iSbYF}x63LG`8hLF;W71&4QlS5zWs$;9>4Jyye z0gpW@&Dhl`CCHSLyVz+?zrXUof}_#av|f}|TV_}hKdaYEs4eBto{2>6cZCbmoQ%5L z4`13D_O;+zR`2z`v{U0IQ>@+0!SzdCq&ZX$vUao8h;5%x;n^UvWt@$o@q8aKN4PwO z3jbyo4A>A~b4q&iVnNANKC(wY8!I+=H4m;Ru z+bf^9!&l~N1e#!5Z{C#b?E1G*&q&3;$86GX1lkH#aqxUA9<^0)<EZS&Qtxs6w9%KnQ~W5D@!@$>dyKwJoy4sZdEe^(>^X(eVRS}IujpyMBG-oQ zc+sQ2_BVPal6G`vX!NQ6{Z5H%>b?YU@*IoiLHS=Z`4*!OWxI+ z+~H%_()?$4WFII-j`s?7<@5ZqWo;qeD~Y8umf;qeO}7@$0(s~+b9^##m@Dio*o$aJ zCbNRXG5WW`wk?apU>|oWcQM#kIX(7O{LjVyVbIG}KIDDEB<|QQhrZP6!)p-G!`Y-5 zB<-<9C`jH8V;RAMYC5@0Evm{+^Oh1B3`}B}KXKndelpw}mmR~))TwNXE%Lj>e9e>f z+to;euP_*nFAW~MaN8}*nil$k_CR4U+m&rv={w}e1{0qB-0R0)#!tkC%f86K#NOrV ziu`c6m7C|*Ja%o78uFJjvDWCIfBLqQ*FSME<#)AsZ0>Y>CO+J>*njQC?aRi}*~L3( zJRYdLCd>%8Sc({rgp=*jvvFv zef~hXyHVDHnZy@#m#=c{|JOUIg5f`eTsm=Et{1+ru9ilR#^NDW-+R>9M=Cp0PXD}5 zu@c*n^z{3^Eg?Dd`~Swj$nQyC>T*Of(VIQ!&1HYu+eq^fXDMvmV)GGe1lb$4>DVpI zeR3OV9eeZ0)l7JGZdt?n++Dek-y?KlHo~u9t{>=};qR+1(pW;6&H^dbt<2eni6>Q; zzBw5t4V|ro+;q5<(UkP=OgI@0W_G7lhtMslnnRW1X)885WPk5=S~DjVf{A!2y`#bA z%an!ANVw(77Fo|U#-fdxkY5fjQmytte&s3mpEj{<$KUQ=T}-IB?6fefv}8)5NHL?? zIxg-AGNqlx;(3_gj((hpeym|<*||^X1a$!o{?lzY&MNB5X}Ixzlx2n2%x^ed5T4lT^-3O>W}aIj%VT66VAqTLL%s|pGyOW}n{RZ5?l zDHk%QrtNM9*>77Amg9+BD4a{WI@WjiL*?9F#3f2*qTy^JYzr?`rDRKR04ertqait# zEOca(&W?2*{y-^rT28Vhhvy{`7k-{9MT>w2Xrr*$a-Z;1!r&X2OHEA3Z5IuUonOfLgy#u^Z(uGnF^d-9`VrA-;Qlw46y6~W zzJa-%FzQ2lPFkL{3$>V~e3^R`d5I`yDPIQe3c}$v>tJUwmk4`sQe{P;Z?r$ecX7WG zzD!tr1AC=O^UOUbZ0ZYFX|g_SIHC`KLpbb^wfON7?)#Rn3%@2>_y(=3Obo8gn{mZM zzXTy0`}b1~wD9?KVn7YU;ZJ)m>Q#Nei@Nhiz{(Jd5Ew}N%Bn-ZRxz@zsQG{7nBgXhFlMcRtxz5B` zYZxCNLJU!(&G-i9dJ}^iD<+*H=o}CdR2RO1VPUYDtT=2{S1bnp#2pEv3Fop`lFbV8uB+N6VTU~V$$cxq$X zj5a@O$|b&mL3l}ObFkLtM(!zI7w)9?;2W4Pn3$EzaZ8@nH9L#CerU>%h3dM+#H?MB zaT@KBfLSm6i0I%ObiPO!MABO^FD=jIAb`wXE4)Cs1j+2Jgu{t{Z9KeuKliHeEMf2s zTDO_?wdIpxqdo=otrUJp7<>bByGdtZYn6{WFx@5w-@tsy#Ef@TF>%CneZp)HzJa;J z#7r%zVv_s-ubOo54a}V;roT33h)-+zxrs4i)!t=d;88MUX!Z^6ZOcn0#=^Y~I$t(1 zh%{%)8Gg$>X3l?u&fQgvy^3M|I{k=#{jG_~d#f0M@8FXr9eks%drZuh#wtcZCW1eh ze8x90UokNwwRxBVe_k{BgKuEIN*KhN4OU}Od(oaP=GfyKn6H_1Lbcou4ctBaxG9VH z2IlJ~osno&roDU{_p=%u7K8U2CT4-xVXn17wAm*-N8^rfVD2^R>e8$I;^0xdMEC_^ z@D0p2O-w_L&o1hhAwT#A<~|eSuFZK1Wc9n%@giCMRuxmFgVX;`e!7NX@qWK;Vpc}X z_E1KMADaC#=Fj~mW_^u6_&axXjSl1C116>~SM8Svm8u^f^ZMj zg>PWKYhskzT()q3&n@B*JsUB&@eR!POpIK^M1*CQt4$fhH!y!^V)QDXEn(~#n$S;l z@D0o(gi#*sMsNyaP0;xs%=h0C2H(JZ-^Adph}Xbqh&%m`NeAD+JZjdJs`XvQxc8cS zHNJs)%%n5in>N>98|b`k(!n<{KQJ-5WY}zv6LRP=>EIif$IZIhgH_r1i2E91K^y(D za9;!F3Bsrkjh7HRp|);HptaEK8@_>i(xlZ<n!b)PH;f^OV`9PNZ2k`fuUB zZaLS);2W5yO-#0iv7xRiAMp*$56!x=wK21!t{ct1;TxD|OiaEei#$KXy(KK5_TU?s zADNi#wY7{#T}y;96T|2{YhpTU{bFMuJ7VmgBMj5`tMTUr?mn(Vm`8N*4LZ-8n4z9V z)#$sQ`zE(Q7$gk7fqB8i^mf24M*FIae!YeFzD!>64a|#Xdk`|-lzRu?Z~4Bkh%opD z=Eo*xF3*er`M6&=*=P^R$4e##8KF%&0{S&ym~Ud3O#i)!>8kOWgG|STF2dj&n3qiq zLhqY&6v*pSCI;WY{DX<{*4oVYEaPq+>leO(`H6`csEvifeS^zZ>2Tiw=BFm6RI3YN zFf6|l28a&6fqBKmw1ledadE%2ydnIKF!%=MXC@|7lZ|2I$k}bKLHGvdRTHDs_!9t~ zw@o_u2Il7`rclcTkQXf6G-CR*e&HLK*Gx>X#>0=-^}h(i;sGGpCXKs?`#v&UzD{)T z4LYxzm^Q!HW?*K(pF2e4*FatP2IdX3uB5BlchXD5V+dpDC2yK^){nVNI>y>z)Mcz4 zzaWhIPwkj#BG+tU z@D0o_P0ZTr9%bpRJY_kESD1+FkGw==2~k{s(-NY1^o_GWv)qRa^1vWR6gU1{gJHx;dB8+JX5vHN?87^r#i(wiV29^*ob(Rox`&8u-?oqtC2Zr(tzzPE9 zV^$E+3ss)veuX%#z=jw-SV2UuW(9#BYx{<}w!;Df#j&i_lz#(JnyPic@}Y9Fd{C~=K5h9T-iM$ZIhn}np>nc%P@bg*ki<5?!1Mxq0I zhcI>aPUUZrkAm7t`6^)T5Q3~7w513d2WG#)+FE2%4oe5+HA{zhu2XvnqcaRF9m1HF z4q=?gi?M9>H;j(+H^9KsG3sF}JL*Zz`nc^zJt;;5Ru0iHtsHY~pXF8?G(wC9tQ?|2 zRt{)1qpi;x^484w4hx59m=+GvXsoQp>23#W2gxlg9Kx6u4#UjOqOCtPRE{x8^BOL5Lh-ugDe}+&{5uPl$$CM#fH=}gS(}PA&hCQ5GIJW`mpj~EQ5^KuvVxZvQ|(JS9yWE1Tnr)j^O@esZcpt zDkyhRUq}{QtQ=Mfm6Mf%atq|`yLDp$3&kim=Gg29l_&V=x^h@2R8H0j#_}-u@I#{y zG?uVTsGKYlqW>&+b=~h_l~6fZB`Ejc_ZL_;v-W#f`(Tk!IawqqUs{=lES=8Em$GtL zBUDb-2+F-!i~h@4i)b8RiBLINA}EjG_g^u7PwO(Q5Gp4t1m(%uv$z60)SyqY3=4$H z$pS&SrE(m%sABk$ER{>p0@xpfsIxz2U$b0o$P3j0>w^$veSk(7YqvK0BUVTFN1y=9 z1LZZ#gT`5}JdHVdfMIA(!tx-DX?YMvLmr`b3_2R41IvTzAVW|vS&NVP3Td*_;V_F(! ze_p8a&Bpo+OM~hmO9S=TQO}J=Ur3*Tl|kiXWuTm|JkPx~`wy%g{6FCLurR2cEDS1t zfmaWx~3kaq|m4y%I7 z$*MrPfU&=S_Cr=Ke28*b6jV+Y1(iR;&7Y;*k^-}|VNFmuSraJdAfxfwC)w}GqK73x zFT3U?Bi^#2E?yhUM#4k!VHuH$V$k0%7XTNPzidr_|*AGqku~1#GAP7@;Mgq)w(KrK?2m=cOm^urBWHAAmy;d~*C-_Ev zup)pv;*3O?;a(O0*&NpyU~V((t2-k>eJe%dOi!WuU`Y_2x-$}Bx=oBu7+4d8sXHS< z-o_`)_QZ)tuqX&qcSa&?;8l}OlIXyyAWYpEiLk-)a}#6aql9Han7T6(fwR11Vk~&~ zF0(Td!qlCSpv{k&^WUHY3&X(Fd|BuVclr_if|Ws-x-$}C6`wTe2*N7V1xtf4b!Q~P zD((*^pB2Ku+8|8b8439Fn#rFO@dp+MFg1$cZwwPm&Z`1{ggXoaOLF3-QKf{lk zvWRbBV093kx-$}CCHJ!$9hPSkRtI6~&PdQ^pJ@2OgVbhN9aLA{8HuooyF@%p^1}(6 zKnG_egsD3tp?(?i<7X$KV8F20|P6BFm-1n z;Li`uei`!zmIz_$&Paf{x<-fb5Y`A`>dr`jxj=l6`sE=$!y+L}-5Cl0w&mwmo-j*! zb`A%tgfMkyBv|kM%UtigwBEroAxzyF3Fv&qq!S@Juuce5cSa&2nxbf&=O;x8b-_X* zOx+m?|8LwPyd7eGMGOCLz`#Z!Or4D)o@BY&l(C3-5-_k*2vc`Pf;~eMXNXRi_6%4m zz|<@iT7Qvi&Z-bb7uendONB6XXC%li`;JLRqujEvRH&}HGZKt@uen#tv_`>NAv$$u zB%t%QNe5Y;Q5UQg!qlCSKn^`79Vf{ltQD%O?u>+g4*6bdr_o_OTS=n-5H6n7WtA_6NV$1hGj#Tx-$~U>r*C1A$f&$LzucV67X5ZeL*@S zp?<-_AxzyF3I76@to1poe>xPj=XLogn!?%+gyW&`S(#5EFQwtosodf+a{d=(Sg-Nn7T6(G09Df=TcpHF$p@bddr{`N4RDali(i#29^+E%rg?)sY7l+;Ueon!~#aX&$+vS?3prq8%!_!kTd|d z-Ek`pS6Xp<+I*J7O6hJZyVT9X#2Np<4PQisy>9aM#;hI=>S1es{fP$@B`6&XhGO1p zQyVvCa-lCceODlT;KqET+L-mWo*zB+73;x${e@n8$di|QFWUU79dGxq zi$vWaO$)iB5&Mb~_o|J(ugx{vIN6S*al4jp&fWyHRdW8)*6ck{1W9d=FRE8Sy#HecEsIGS(SvaNH+ za|a6vN7T0SjKRhp;(0i+@%+)->$zS{>e!SB>if9oSuAW1g5id9JyX`chjHqLtkL^cFQT5(lYnZD zNAT3(I$s^PDwn}hhV*Xloi4fTh1Gj*pSj@r&Rl$9U&C~0U~LhPyEcr6u5$X!ER{U} zYisior{wkI_g}GpYS&DG;uSR1=m&TBh<@y)e!$d3KiYyu zKls`AXFn1PIKx~5|ElufhUN1m$QxpxVur}oxEOR7A1%g{V|EdoJ&^e%Ko1^h;AT5= zHwRdBZ6niS4_g6ACNIPbfL$Ec*Ab|1y zorRYeLG%H(#<-1p%H3U=25z)C(+4K#(Qr;x-MDKk3S7eyuifD-M+%QNFF0lIMBA|| zysFhPbKrz8Ooy**YE5@`W&9_O7Sjv+2bVoPjHmm&&tY@vb|;JS=dpo}a#rNNJJIO1 z3SwAy`&_nI%T&*j#z;dF*b2%nRkN3kcyVby%rT=T@aKrPux`SIJS zpDo$GW!qJwFCN|egu*{w=~%FC|9v~I{b3htKjm5%#)aE0yTQW- zdW&i9ET1ScSf^PQYI9MCAhG#nK6XR^#@hPLF#054up`CY z)qb+IL5-=%j-}cWV#=<&Y}KX4`{#M4s%l(Y=BzR;oQ!~HBg=_rBtuh}hrh)*lOe#E=0^L&FTK;6>KK{@2hV(!y5?e z;iS)f!MHJ3d)AgV>E3d*>69f4Gu<9-;#NIvcVckOx!GZB3icf+_O$dEGq*RlyCD)0 zBf8tGs#!fEhKG%@6@Q7by%Fv1VlgPuE+etHjyh>XBq7vs$!cdILmVUOs|exYk^>CddUO{Xr|n2DA>j%7OBJrVBqLVN)hUaZ7T zYnSY$5<3E~%N{}IRJ~{t6-SRR6(^dji@4J*^L(_jF2Kp{9{-=8+uB7O?vG0E1V zIel0r?qMZI0MNg1%60T0QW4lwV*-aH7T(&F0vrDHUIyOo6>g{Q)px;ai$WKBd>L$w?cI42O z*Il)K;=1v2AQx&1$6FFUJTr;%ch7C79~+9pcW(aH^&93lw=B=M=0oYBw#k;-J|Om^ zjn|-!oBeDquuym2gz|~16Vt5_mX#FrPXK)KI#`pQc3b#A%@Rjin+8;SR#02J?TsQynJ>gJOX?I8t!O1z2N9k+X?*bWmI+gq>n*P7s?PeORkx2zRcXO}`6XUCsr$m-hLOyo%0qvF;`T8~!=!Ir)9Q}p zbB@Iu7HDjKx58Gu68$nxlP#T$fm8X7^3Rj zl4wswbcfYyi%G_U>ho8!+b-em>oQi|m@_iU@s-bQ+n%+@N^{bOH4`}?-xVjoV-{PU z@*$Y<@DI!i6 z&p*C9gSi0X!t2o^g0{S?a!u}5-*schbC1iOQ15$e6VV;9Sj}{*YI<&aSWVdqR_^@m z@%Es>vX1V`n5afn(ZSX?-U1y(?1YZuM0Psm!MM+ev0jU&Zed;n0bT^GxClRCcpvzw zPrY&J%=OQo_Pt8w(s%F3Ek3Y(!O8O)CO)@$<$-?T$vw}Xf7-LxuI23CzxwxwDvMU# za>=TtXZ_8RZTIing|j+y9&Z=!#mzG5{vk8}!2e_$1G_1Px#8V`8f_X1y1?D~KiJT+ z4-QO!;nL|Xm(L5OHQmv?=%gvH3vohV3@rXX+ST;bywjJiIJC;+bm2+6cG}uwH}Lid z+epCYiX{I8d}f(d5$CW2dA_ihbXZ_rB19*mSy`E@hgoCH+RWy&Oti$KxR0ek=lOGg zz`0ZN_bf)PRH5+Xp2x2J?%Kf(oqA^9m&Z@3{KqdU-#X{y(fKpS_vdb@JXxH&WO~U- zec{H5bC)kXbwNgWvhwdgn7sMWo`|3-yzj)`)#V1n)=tbXuGo5gbD`9{aLeZN$FKSQ zWfkB0FJHEL<-u#Gr*1rV+4vb(l1=x3xKFsta-wA~`f$n)pV0?eO`!H0YKWoCvIy9j zBCqpoKl`7IEf#Ho#c?$3Y(HN<@U7!kh5hZj#@A%L&5@A$*~0Ez=dG>fKp+J2c)0OD z7;&uWH}71qboZ{sse*9V-<-DQ{D9On6Bl*ZE?5t6dW$&FvWaKN12} zuUxh4)TIk%GASnO6SME~4`AH;AnP$hcSEpiX7hY|GLXSdwpkc;_v`lNwS~g|%yQsr*nm z-O-wg&A;H(^%wNEhQiwtna-ikss7YpzSSStc<-w7hGp>(kb?S_QHU}-TdSuB_T&ERrRd%}0QTbr~%w3KwVjhDEG+cu5ZOPzj4$kh&Zu(dqE9g+ zGIB{Qez`YNj`#=t{%aDcJ3XNs0w6?Le|EF}EIp2&s@#~{J=CHnlRbq+KJIg0$5 zamkhKu6%yMV(x@u_gr7_>i!gnoY4o#hd%5_A8>mCGWYT3F`byAzk5GH9mY^VK|+{& z1n?8cGpRG#1P>^pAIU8nN3ep-2m~Zga6M3p^8CHomLvPP+E;X)ol0Ekix{1pL?``` za>Mw{_*GiW8%_s2nNlFK(P~r0jgnvUx&u)J1oV4?u84A_qd3!ZB)vhe_iV)%vYjEh zg+k0x&lY3?Y^!{J$pm*oQ%^l#yeC~!xiLA@-4o6oJ1~wYf_9|)dB?XP-gSh z__@#JmQFeAskt&eh=t>I#JLmPjl@?U=JF``x(9q+K>1=?kUEMvH;+_ySgzuaa~!*o zlVe(9oo|@ADDZQ@S^i^V{W{VYfIWDWS}wjlE(i_P#Olg_!Xw8{G@ ze~r>*FW6(c*EVNT$0bKg%isQOYM!lbzPLT}#~-WwCQK zdW1_kmC?I{zhyGXgLfKux0A^v=_W=*9i|61^z{F66N%vZpnS z)U#B#W&26K#*pvuR^d@vds>*?o|`kQASv1EAsjR_7_n>@5g=3){(_=CArY)GB{6-G zx|Dw;x716%%5h(BWqoJEIeMrZ>GyKChV4!72Qx8uM6ubuT5HxBNJ_f~xXH+;^;l50 zs+GuC+rjlMo@hQ7`?;=n1swT>5k8tiR+DfmGIMGpuIn+1JHe^HZ)Q3(!c6G>EeH%vDYwEBz#2z>jYv;}Q4}@*!{~vbd zM08cIzYcrGiN~Zgu^!vXKVHMyaWFMU_ss2^(vIqzRH8eNf2f9aa`770F;CsKX*$S) zBZxTAs`amxr~Av)4i~qp9=~_nh6EDy*6^0LSsUMg7ZzxD8#^m|p4)5*f**E}Qyv^k zYvx2qT_=Xojtj)}nKumU@Q!?Jl0f($Q#9zYMxS1nxcD{z=?lSmAI2A9$ z=I9N@aAHXBSXjOH8~rDo6ZxI1IQ5}Um`>IzuPh9kuGO7alw}O*Hd^SR7Sg#I!yT;Q z3X0A08qusG>)V*CwK1=#HQ*(C+!*%;#zVX1 zJ*qcAE$qRl8+|SS=P=q=Fl2Osa6#bIhg$Oy^raeigkdkO!`islkHXeu6!{B2;0s27 zh_1@D)?s^F8qlYDx;dk*gtc2fsKfTPW}-D2CAvLE{|W2F%Lv8+)?UbHOD;@IsLCk# z@CSa}XfyFa;jSZmocORNR+S}&`*9u4#%GSeRmX|pPBr>P^i*zt9d1*$DoYG^fzeLF zIk}BBochq}XuT|PkmKb>-3ZPH8R2dxES$2C_vP7Y-MQHh#2bwHlLJnU3JdWLLz1I} zTQ?9l#+2rZR<=NskGSQ0Qqaf_+MxAiOqHN$!LQy*E>DcJ~3RU!B4`e+#uC& z;06uJH|cfp&sWzDMd$9R!!;Y{>?W?2|3eMu!3!KF9|palXmxzp+VOoM$#68+gNV<2 z3U~=e8RUN5%F)=|yInSiZ!-7^Y+lheGK$7()Q;0&%%s}fyvg7t;T*f>aD5?!vZ}*< z+2{+^t#e^&qd~D;djp@Cl@O=#>1rFn?<ZLmaCT?74Vf`SS zkGsKgJ~(L9+nCbo*7MVh_EA4n?rfsxqV`P|s_k=NJTk_5;b7xo{{!Q*v7!!Rt*G)j zNNY6BgCLFD>vOnvBl1pd+|u>q_U;_6FHk>j5;s#n9+s_E$bagq=A# zd5*iwVgX0IggmP(&+UV}tBON?tQ;9I19-(*YPnfKH}c3 z(!oCd9P}DipMe{U*42kN2KK7_D*rEg?*d92_zxGki;ZhMMXu$iY-;zVv7}(R%%hHqEbtHu%)MHX~mXSthB|7r+7+_ zv_(rTCjaluvv=4;)SkER=l$PK^v5&LtTk(`Su?X{p4rbO$%v5!=ymu?8DUQURdZEm zaO^dHtlQn0tMZcrdNZkKwWC+XRP;Wl^*Vf`G6H&)ycB0V=-3-;Bn8PEGbqrHRR4`| z#Nx3)3IBZR@QxamK|cxR{hWERufN9_O?`5iqo#}< zJ~%Bd%yi}`XK&Qi#NJJkTu;1juIKhIb&cx3e}WwvcHW(vN5+24&;LQcWUIHG`gcv# zPdr!n{a-Je^@b~#5atI<*Rz|d_Ewio5AnFnkortxtp5f8{d@N32l3k7DF5&O-cWah zz5(DmZUD&Yy8$4>e*?hpXAXUJk>0JnxB4mbV)M$K^VPQOtdN+9l<=$&<2$Kwk6*e2 zATDEH;K%Ii<07tj2SA#$yBp}AZqujw_KmZCx;8toj#TTNxz1S9V=mmt4&s}e5_l(+;+yHjTFn>1)1s;-j=TlFz}%wv zTAgbP%@^&T?-if-`9+!P!XnTniuYH}c+l6yHCb^`Pd9 zlGpn9ywA@Z9K0U%^H8hB|Q)i;WPf|%)gp1lItnuONVbWn$){P?ccQgdgP<@Ajo)_cZlfoni`)Vhqj4T9#z9&@mvpUvg@MQ1(cd3Ms+ zoSrT7J1 z#Upme0`J#yuIGa4r^YS2kA2UHsd4Lb=q@EcL({o^)$e~ju7l`mM7y6E-}jt@7yrgN zbl&^o4)JMvdEfi<^mupZyx+kcqQ*Jp`~0KE+F?UK^X*e~HOAb}6b9Pg8AGNXBf5VK zhVFg7bxB=HbYT7P5uU6*p76kXMg2U#RPChYWNRM1pX2h!IzP{YeLO=n&*~M|^6TE^ zJooqUPv@J>eKF?&Q~N>3i+oL=m>%H@rtA7? zy84xrw;#u=$_3^GJ*IbN#q>nO?@KrI6G?9Vf%kEgF+Iq|`% zi?yXj?Q-iRKwsl$F-kulzi^&YDN<=+a-*hEj09B3J*>U&ibHqUQ4H zj<0n+)vt!0XRl5Vv_*gVL_a;y77kta2VH)Wrn64*_Xmfr?b7tg(7n&sr3JTzGpB1` z>M=e=%c#x{=-^cSVRM*uaU{?m2EuWd2=n3>OKhM|t##x2tQF|u5`YSNM zvStmQU#DvxwT&7V5okj_e>(mtQhjL#JnV_a1#ENZy8fD8sp+MCebJ#mrtHx4DoxK1 z4_rGrbp6f=O`oah6Zv7OZ#JR@^>o!&6>j6{y zN0*rek#E28YWzN#(6w(geYU2LONsOZ)3wc-K1b6Fc=p3j=Xx}FO;Drh<5S}zg3IeV z>GE?meNslCPI^rc)aPq8eK_A@cFH?+ZJ+MT^E5qYV8A|yuI|4aEy z)34U_p@RZzUA^WAiXZbeeNJvbKd0&se_o^g8IM1QntJ^4J|=g_0*I`U7=4%OJ1EHC z3pIUYbg;h%1^IiCrjJVs)JI+42d{BHqv_+5gZ-Ip-RkrKu7M)u0lCP!^K+W7AUv?{ z%;+6wE_LQg?e`^`zN9qQX%}a$KZP1zhbGTuPq3?D^K;S!$EO* znWm3P42%U;kKpy}a!oHyyS)5w`ucK%rcb0NJAUL;{o(gU?RV!?oj2)oCnoDSp<~Gk z7(CAwqy+S<{fZQ){+iyb>BR#B`knH+Uut@brjN}G)c=ycm0BPDi2VvZj&wh|9tQ98 z`B}-dY#=^3JeT@vtL7;l&MlOIIOC7aC;Iy1DtL&^WhHr8>G8aHLWLu>hpS{g8no7H zBir+A%d)`rhhFOi`M+K3+O#2uXcVwVwPEmjutW3QxhX$|kH+<_iPiP@rSW-#=DGX! zBBD-!N3Rx^32KdTr51`_ejp-%<12rTKil=3g=-FovCZ zO1HJ{8yg&Xsev)<&~-a&`i+{ta8O_jJM>?vHrDiw(7n%pX0TeU1$^r8=zgGiZqhtW z1*#MD@z8$LgZl8zTF)Xr!0U_=y`FZ)uwHN9qUm*geb|{xb-U=fbdy8R5A<<|uIFP- zzZJUo`Kz*m=VQI5cG}*j;vFkq`e9i{V4OL09e*|bc1<6j6o|hLUAL{KZ`SmgIRP7V z90`h3cR=SUJ?9V{(zM;#D^|ZY2nykMs&K>5LzS*JfHqny&pO2qEq-gw*^4irkafz18hb#Ugu;l+8A7Th>BJH0W_zR}F`rKDXmdCv9mQQpic8AJNXiB!+#I6ii6 z{&j8~!rOa&d5vk=Q#UM_lvg|)IYTn0WQIn^Uq5H^HEAinEaZ5tSB)3#SNN_F-*XD; zL)>bkTZ{W{GTqgY36U{tkF1M{OpFMtTQk?X$Tu>pZ{0L2A#M2J(OfdH^<OE%^z#2?blQCwF01oV{;MeSTO}e9z46?COj0QDON_BT|sd zJFd-Du6Rj7F3*nPYyX2BiGjzw^lgUhf7eDv-Itm2_{gGxsjjHlnM;#WpH9h`XT6gg z6Ze44Ms!?MSi@4Y6qz^Lm1dLuBZ)w!Tgx2eczlqSz1P^qmy$AiZU~D_wto~II%HB* zXzEpzyTuMSN4t`A|6xIdEg9-TNdJg(Dm!ZALC@T(yJJEVBctzbTHX*7nP8tCTC{L@ zdcxc-w_THvu6$)z@(!(`5{iD8(jTkUE9ILOPn)`As%vOjLE*G%h3dkV-xAX89J9_P zpVIfgyP&LW!IYlg>w`;3PtnOKEG11DXOA+zz`MG-1^MuPeyS96U02V5aNtYp2a>X)2#Ku5iYbVZ*0R&^DRoV&1J4t?&IF5bOND5oqy1 z5Onp=FjnR!guB;et6o(0RL>#RrvhhoAyG2ny)xaWeo+;4@=K?WPPE>nkKLsEg44(J z*WvX2d3c#-p$WNlL*wiN6Ryn-mMq9-CXVfDu`{rF`iawMl@isn&}| zv+H&|9f&jj0j=(X)1Tu}_kqSIo*h1V%&?hDpRCJ@iYQ;4WiK5yctAnN{3TzD4~eiM z7Zndy7ud)Xz7g@ht4@C}?Fw-VsgqnfbOp^7CZB_}y(W{zel=O-t(!A0v@3}HY4jxe0J#WcZEaiuY z1;vBaW`vgcC36w)D^al^%!vg~izf!|)AYxJNTW7=)WY19$5RH+wxVK3%+E=EJSCxI znDwQMh`BYRM#n{kPnk4g=rCl~_KY)@_TH|2o2chYH4yy$SKWA~yvxsZxZw53gt+V^ zBO)X&bK>C9<#uFxQkAFYVorKwRAiiINZz2u)w!dJl17IYBR8J8=DdATBKz0hfB*Ku z=pEDJQW8Vwghg6EiheyhVQ6;r#RRo8&G!ebZy4e)%NvMj6PiNbLh0O$x?o_WcTQ+x zN?gSb>qi5l6D~$)4^4=EU6=J*_Zz>kf1~{#roRyw=)*h|#?Ww{5#TBQ-`zVPHNw5P zAfM29!0?17XWScYkG^X_cvR#a6H2qQ|2Qx#G-SYCQP|_P9ybf^RXVm`9)tTlrTPjn zI43gd3xiYlr20mt6NB#=obq%kA^7p+nAjV~W@g4m#ojh%s?(ND)>-pLyY}N^@DAfh zXzbwhp31P;RJ%4hG;?@VXu>#bX|ne5{;kKgEm3;zRWaCkHcLGZ!SAG_i*eJw$ojD* z4Uv2-bU;?=wN2NSW(|l;vv0{BJ2pFdz=*W+$-{9c5Ry1SZ`in8!eo?|`D^K5d z6`eVF$So6w=S`fLH++Jo46IpYhw)5Q6v*(b!)O!wYRjh#~_uc#{-nv*+r z_Hg@bUFnpjYbTbD%$+(uYxp>|8>f8iw406@+J?Zq!aeTHn>upvt`n^&1ge|FIv|1^xc6MM?Sn3va69zK>XN~(mYuvtX0Xp6J|Fvt};J*C7a=qns z-EG#o-_Uc1{yuh`{zx8S-FY2{UgkP;o_BI?)}%qxN?W?ptXMnxTN~S-b}ufh$sH0u zJvt-rhGk)qgV#Q?mc11A?99#P%j$YOUbr7%le!y0)Wpip%ZjD5^v$Je=LKGMU4ECt z%kSAkJX{mQt9~_5JLuWTm+o-nWl_E7U6H)+ZVGQ?so~0bR#in=ta|r|5u@I4!LuX! zS!d@;!+$%pT2aI}&)W5o34@oY{jZhm8++b!l;FJf3?xLsiq(#EHz8flEadUjL#wj0s3F!Xl3tP z))!nmWin3>d7hn9J~~$2C!;^rsNQg42BIKK1~tL(cy!-GL);mzZi~?qqrn%hA?tM& z&q%4;YYgKX5z~BQ2KXYjH+9}TwK6@$bh|wF)`k^-eq2a~r#N>=_4L+^NtMgT*2J(eX`_Y|k50elrU>q$2#<(0 z<2(ho8UrjVePBZI6ygX)9NGAVMY4s#u0Z#$& zSS7WJb9|y-zQO}3A?ne}41+KC+EjgTCJ$K*HixGsSfTgvO=JFc@}=|an(>()yD%)> zFhip}38tZ5H4|f34gYp1Z-_CCs8Oy+w-xICogL}sO){}5(XN=NxG+`^aiM$;#Yhh; zv^|;QYX%MHr3hi3kPuH;&-@WFsWr7-3x+0*y?Rnwp%EI!^SGMb$IR(pi|Sc1;VH3RZ)`$zXjoL*bYoYH#}m^tKQ<$g zSM|7DE>BdbLylGLtLkO9Q?CfVkKlQ>B!j)xtl?Su6u%h3QEI~CUJ9oJ=7nKiMcX~TQodR$k^o|r>@im1-K+!2Kn2YtjF zcSX9dYQ6K(?DCHCVN<71hG~R(=hGXqQbO$GL&uLW{^Hav+C8WwJg8zR{oT@YcZkQz z-_V`^nK>i0Ya-LRrBv^Os{O-H`@Q~S>5PwMz45O5?LywXv;_av&MKdP|FSdH1({yp zJ1g=e9o&)<)r0Q%kwu{MjIm9Rh~))T`r1$}33;l7AQ3x;x9r&L?eQLH{ryw5cGX{Y zSDrkZU62ssnzm}(%CF46`O&%&b=mO)`G&H`%!nH9&R%uz)<50*XwP51{;(1ANYmf0 zN(oPE_`>&QKiBi}!5fbm-Yi~FesuHif%4Hg_U+tr#onxzLq8st?Egj=axUZB?B{d#VEufoKl8cg zDcf4B-zAkAs;*xQD1IF>{EeFOt-A(XL;{ z#U_qP%%3!QWMci`nVl&M>Y}1XkMDZ)n&of2x@SdNd0~FJFEunRE+l{B+_B3m=hv>E zUei82B_uS=v!KcR_RSFq;j!K*UtC)D!pXk8xXi?1*NkYoZJ9eFBtEsYJ3lHV$(=Q` zxoT(XlA=NRcUOid`0^V{$JPzLZ)HSLsK=8!ZDieT6Gt`P^_`}1D`$@wnvv)66iuzT zs&vuHMWxrz8C(^^tmUambS1eqJR6@JUp^#l@?zeNv}Ur$ojW}{IXTk9izA*`TpVgy zE^kTcNOQ`J#!-WF3WI!pliY>RH{YDA;;u{W5VJSr*4I!h3oh*wDSvRY1=(*kdp+nqAJy=MCQ+WD2s$IjiD z9}<@qn(8ahFDy@6vFFt{mS6K|*Z9#|I2Nf*| z&l_1<;&oY8Xz}7F5l@4%BMWL0rw`>co|9%ZPbsTJ`Y-nEcph0{7(Q~e|Y5!ecT6llOH*=Tw{4lchWY0bA z2R`WeQDWYV&upwL``m&Dox?e6*G!@GKV7DW1@-9TXNH?j4sB5)~5` z>yB`FL%d-jDZcq?8TkKK-KKhT*?{cj* zR=R&8+2rz2(dF&JOn0ea{QJaeGrqu>kdi!L*20O)#tiX!L$VU%hr|v{Nf{J3CeP)H zat|MpGB~R6>IoS$-L5p%G9wa`28Mfm8S#VXyY%AJki}NB`HDSU?>p+fRbz~rZ9`mc z723y|pHCe!cu>!j@uPFI?BP5PoHNnLc15LU&9Z4y>XvUjZd_-E>bjARaeKIbhdOYb zr0-q)L^^$<)|ziUX}ziS>)(Ei;GdpGSq+5)igG_3o1HjesP$%eQpU`P`;N>uNP@SVOC4*wqrmS-hBU-&mMfIxQj9 z8#8E9Zr1p$@ezfQ33Z>lp=`|yD;HV=!bW@3ZSRnb52u>zv*SjO&#f93JuoYxu=Ih% zq?|c-EnM})Lhir&TknTPx@(47%W}6)j`6GB*R5S~ZdWqiX2)bqRz_-KY}lpyT9V-} zFdG$rvf5vX@}Bo|d!Nq^JmrhrSJjuE&71vl;X51--(aMApC6yz_pEL2KE>x+8l?X| z>wupV{Z|!SNx$_uzy2Iglz5GF_|<)--sgw#8neFVj2#;sUR5_wRPSNUt9F*X&riq* z;^ke$7tA>GX(gBMgt>XIqUvpvh6V0@PT+a^TUmcYPoUmhaY{^x%r~9;l2*>h(ofFu z8ZLO3Ncg9GdUm>!%wI@?^V?{mTdg^= z)i_tHM70`Xs?p2QxS$^f?fmQRS#Zbr^um(D(M5|YmKRN)TT)m)@9dq6r#*3VYS^f; zvocE;ghrMYOf>#bJ!*7$d0~mV=~DiW>*KpWuiN45Pk!RP7*?0FU${hX_qm=6-u7ed z_#!f1`!pH$L|ynp?-gbIKg15kN#9?{(=NkRv9#Rjqk0VOpZ(bfuZt>u&kXZuwxcs2 zOHv7r$vrRsw4wGSBMPK^YY){j5^@Y*QM1zp4HYo-bKfs@icf$ zW4^ggR`NBt`BsHYcc)67dz#EQT4kjg;u&OH&Q{leCw{>7p*j8yd{G< z=9?R+(_!9pv;f_sCCYfc_aD@4psK6;W@M6Yy-m5#i<|e!<(r4$YnI{W1C)E2-)|QQ zuiS(7u%xK8)h#*ZKV+0qCO2@b#D}%=56AcRQRemE$ZSpfCbGUNqwqzoD?=JotDAFs z-!tF9@A%W)D1~N|ETP@9@cm?aAL)msmh)W2uYEc~;_>N65R$BYT!4S$gMokJgMsep z_%{OoX7-0=%3|$z<>O!|AJZ20O6p%BiB|$T^YLvwNDPMZ?mfXEF4AqdUWq={^KeC@vqJiU#Rw=pSi|SE|T-xd%vstAN?twKFBY&GLZhP zc%nYx7zsU6wWIQ7|KnwOw6pTFYU|)*h)F;AgXA-|;_2V<{g3!KlRlsDaYx4DV8%n# zM~?Ix)pn}?{4+;2j;Xt)#xY|_AE|dXV=Auy(X7I6{(1!Dt1;y5miW*@iRaGvn2?DQ z@4nyhZODfb5i(CA-2U;R`c0r6`pXH(`?#ah)4$I#=G0hA^!JVD`}&B#Edx3NMF?ux@&j$>LO^Zh`bskVAHPodO!tmm}xSZ@ayOZr{#6Vgkesk>a%Gk)qhAN5Sr zjX?dW`;1kdq74A*_NM?JN&TnFsz1d$upg8eYTHNYS4VE{kSL&JdO^UpNu(80L8&@Y zQ2lfv=FerUYaLHf&*=S)dqeLB<^hhWjGuStZ;wh2UtWD0`bFlx96M8TT+LEW%q+Bj zF5}!Aq}pA`oOpozPMNIuLPkrDJ6CMt`UK=GcDG3^@pT0Jqww!oK1%$(+~0oPJpD zV6Gko{j8c>tXPRQe+PXO<%Sd2o8?C2a~~afGdOP`4#ktc6@KPR`(~-6jxQ)(lo_mb z-YkQaJ=!K!SAgvUb^SBBo<6|(fjVn6^>&4PU-ChxJ-7pO0_Gw_q=-neo?-= z2j2z!_kfI6@y=GRRXQ zISSlYo#TEIJ?L^}$`Vfo$6JXvBdF{5L7R*)kMrHsN5!^MWuj{?ZT+r{vPV0xzR6nY zIrb<-Qm|{3^&b99Wv(6~IeZ(l(i+E_;8x<_lcYOnt2BK5MPl`%*bUU0!G4o+6G0*J zXK=iqZ|tE<;}Gi*rGG4K_(hp$yeA`!_j+G9_V*s(`+9EbILi7v?Smc4CanwobWyz~ zP%)16Ry=E-H~Sy^=Eh5NVPHX;HAZR}9eb+hf{Zk;HHDiSPX2xN^WB+;aBj^3%R{yw0|BGkcVFiwXxOwGc z@4JEc`Dw>MeErztKRI`*b+D!8O#d-3mwwujb#pxZFud;=n0r5F-c{=fHP@?oTIo}L zjJY@}5Zl!;FehF4*n9TM`BzxS`RAJc{GZkbKaKA6vA*?JRR6aA-+kVHZvCgv18wm! zN9Ai3|33+8p86!Hx#LQ3jp{w3@z>s$T;B)NKw|H!YM%KdgjDq&QSf+sUkW)67J>ZU zSHTGdHAgwYr%%_v*p2}`A9wt; zcImU@W9t7&byl=My{~k#M)_zNFSztediVw@rcZpiER{ zR9^v4fv5d=g5wivU8G>m>wQVB%iu4doG$Z`KF}Zg7S&|}npG{4E(k+i`+3+n|jzRn5PiaaTD*`MZvSM9$jAg5LV zS+y)|7n}3t2IEa5-FQ<{!Dy?K^~SxDs-W~~oqv{L?CTVpuS>CAA;qjeeyTA?hV}83 z$uR4P1D^R1o}U?LuaWeycv+xeeZJ>+uB&<{(?0pGFVbF%^gf7XJ=yyKYo|o=6V+Vn zq`6Cr^Ux+q3cE{q$-fL{Ef;0G8GA>VFE(=gokVf{73G@1T4*2VioO?}Gr%(TTV{b~ z(yxPeg{a5FEaMoP-Rw88&I$0Ty$8k1_4`0%4n$`mI)@@F3Z5urML~~3Rur6w6w)1FR$NzQ(>^yxy;x$oDmGH1FVi1c1-=7;ECAKxDR}xxAG50kjO(#7*D; z?enhOpsycT`?1ztOgS&|GmxKw{21hCAYbioWgw#z`5DO1P<+TwL4FGIhbS8IGmxKw z{K3c{jQn`yk41hId|u?|vwt=abb+C2FAYS4m0&a&40yszrh+I?0^&gd@PJ~F3Sxlb z^@0Dbe0DO|L~rA#w;01u;*TtewVK%XQhTazOOo}VOy@deI6nEO_Fn&4dAvLA2IB$t zaoZ%xT#AlkGL^C;43quEhh?3bRY z?l`=!$s+n_t@U~Cbp9*PV0P2)!nNLC@%LBRBR(h-?71@8I8OQFz0X;9v#09ed9{_| zCO$;l(a3q5IfVChT3@AHn-p3;ezEu&&$w(PR{nsmob4sP@C*d`*~Yu_B{LSA-lZKM zkjX%dPSUwD%Dh+7jdvu)yj3ENH<)w(ojx#@U&kJkaAPrL*-5~rSn83)UaU>NZLXsn zUtcnh%5ZFqG#-)x#snEUzpRLl&>)F;C*{|xX z^s?`Klf^eb!9@H$7JuhbJ`8_n;%^`RPR8H4_&XJUC*yA~{vLvF!|`_<{?5hU@z^%M{2haR@Bh#C?|)|Q4)CZof?8wgbp>k! zl^=Y)ZkP3*vmPT}C(1JWVaCusU>4`L)VPMQZf4)f{9^9{zLP$Y@jKCVn7zF@{QkB< zYM8So+D}RibMHic>#DZPNWTY-`P!Yq++N0<+(7*N1M^E+?}aNt*@NyP^kxJDy&1vK zeB9HF-kkmj)D_*Cm!UxnMRQM{51E ze`6l`o46EgzU$8QhSqtd_Eve??oV&?L}gn*@0Hs8vbI+B_P6(CdV|~8Utgs^(6+(H z;5Jt2pmvTAw6Q+opWrtB@3t@ca{_Ivj=^mk@aezOzQ|AaL-)0y^KjCEJar9nrQcYB ze_sjyZRHlqP7NwQnzVwtE>+iigFxVVsEM?KDx@6=-dwg6+Gz7z(#x(&5lvl@)J7k~&_i!x@&R1!5zkvct z^*d7l>h~e9|JppCqgn&@U7uTvo!|2N%L<&Ecd!<{Lz3+Sy*~%v4&-0y*vGe$Yno%i z17LlB7g!nR_pY>FuwF1`%8Lqpv|>`hIK=9>fyl(sI)zsk<2~Blvq>a07aXHH>_UV! zgX3Ht#{+)74u?OY()o!j@(e$bU7)*gCq+z$NNf?yO=QHKr|A!hB%m*WvWdt@JSUR0 zRV2BDpGEfLHr$iO^>Z3B(%~Pto34^8lGz}Vl_!!7Z3xIk#?UDu!z)GdE{f#u5GmLv zQi#nXuz4i;qmEKF^o%|xGIkGLt43t}R*?yum$m|UCvF7TJ}Dj`Z!(yI4O8GRJ4+Yd zB~mUT6&$A@5t)$(nnkL@!CsN8&|7^{WEOd|(LaYe*PIfWdzxk@zYe>vUMe!5^97_A zZWdXzU*t3Ez(KZWwuoGt?8k1A>o$qh9}-zsBC>oVpj=}b==K8}nvl058k`qt*#?la z@|?)^9Iq#@wHRy_Syc*-iL@S7!(YI!o$XevCg?}C8 z){(cKy58s$QD0=+xC5LOxruT&LAx27Z>IjYY!lf;dee51TZ_Q~k=v$-+`dudj(D&c zoEN!sm&jeY;JC;ZY`goE$Y=M6+~WbwB3n6b<@{dC+)J6yr2)=AUnjDy9P9+h`NBGI zTI4>;+z;>l$oV3?Us@{i<&z>0L<7ow1>3&bEwa-CHi|q5?}O<5S{~RZ@{kW~5!n?E z_KQ561vq~M{zoYHD0O=DkjU;Dk*}W>`35|XQRcBTB73SuzG(w=KE74tTiCZ38}@Dz zd7?z*$vSXRJx|#K z>qNd24>pRtK>iB{M7~=LpdCc-_u&6N`VRTP4&l}}fXo-6|A4YTI3w~y_62Rk_WY(p=rqLUGI?pNA&;kw8)eSpD!lL>EWbSuxz( z#0Vi2hQR0H=uxYMgJOhI#=A?5uo^MKPl*w+S&XP?a7>JtW-(%?h!JJRj`=6WC_sK8x(e5cQG~3bePWF80m}02Ph;dB zF-8f%-ci_591V_$F&g@q1~JB#1J1{hKW>*8XJbv;JogT7*~5hIoJZwr!e0KHi>afGdLs0f?Tj$jD^&9kq4X< zV=>2T8^lQYsB~*v~9@ShWz_-#kimH_n#4C`$aLnyjYAK zd15@!AjVftiLtX%j0Z_Scv_6Fq5Es_JhV-WU6k3iM~sI{#ds8+uY<=Hh_MGe-YUko zc8ReUIeV$s6FbFt@|+m^$bb5f7|-I%=PCD{tzx{eLyYep7UO%^_5HnK{2RIsZ5HDP zMPmFYSBxL0iE#w}m!Thp{!?VVf{tSxUtKK5&(Zq}WE@BCFVXcH`L9#%*ZakI#P~gO-f9-(?L0C5W49P*_KES%MKS*96XPs;{)DXmIw{7x$Hn;T zHZk7wi19vlUO?VIz=z20*)OIPfz#|%akNXsbd`(g-XUhl1u;X{iRnEcW_YQX5glSi z@h#iv^J2zs6f++F1b7oqh?%@e%v7J41D1-Jwp+{$$_*rc&|xt%kBT|?l$cp(#mqh? zW=@@$LvzLCrxCLNnMKGR*&ybq<6;)?6?1g8m}6`}-WYU`Md#RNfSi&%F~^~IJl_Q# zzemgol>nKg$l=x}bK-6>Cm~}p_D%5tXe{^5shh=|)(XyuS$;^&iYzgwhXe8}v9+o} z%$d>Pte97A6SI0Lpq{gkGaJ6yFa8H;8u_RT8?r^T#m74zy`G3QhE zni4S=a9o%r=As2+eulE2*(K&;0XxOKmi%jXi@5}yOUS>DIxfY|dK;V*bJ-~|mmd+c zVZWG-@HbV8xdPcMj*HoRNX(XvJmj!R%!5`bgifCddjRnF6M?Q!mEn_yc@#-vTx#e z6X!Q0{}$4>oE3A^W-)K=7V|db+@1xFin$rtcOd6Z^6uIp=9WA$?}q2IC1P&H{(E!9 z{M;@vKVL28Hgs-V2QG;Dg}q|l*C6Ko9&kv^?agdR)PQqhehK@&Tnf&JxdXlj>csqt z4^Zx_o5b814UUQVAmzTcRLqC4{UP{vEdXc5eE6uCkAO$_iMbp3yH8$@uT$ph(7$m& z%*UpPxhEW;^PAZ4P4s^&7eL>;SIj5iebNU`i}}>a=&|C%!8Eso&qxd4ZcJ0yomf44~zMOgJS*=-owS< zh?qah1L*t_W&eE&pv;f!z%en8;GZMtdMQiHmu;|B%%3EKy<#3E@8}^he~PU?-7euOHHi7>a29zH_`LrTHx%3inOstG%u?E6FaHm*4@_fg|8njug%wlj_tie0P$|7$_ zG(bmAIiLNCe zKBI6-ti{N`HVqsSYe}!yWmx|Rue!~&58aIj6g#H!h#A-ez zR?9K5R-O>+`jcX{o)(KZX|?YX>xLS!h>2ELu~^*#kh2+-->a8|7K zg9CU|d+2heXL?>3INcYs4;ZH@+e#kylDI4#zl*moCl zw-kX3V%=Q|HiN@rebxqbV7FNJOc84^?7)=LHmLa92D!m8gNpq z`&-4@u7Lb6BIk=G;D}gX;`k+wU*0Iz4$?dJi}e7yzXH!!PKfo@ZQ!t2T$}#us6@sV zfISbQ<3VhF5F5G9wZ7I2c8T>+G*}FFgL9X#i?X}0aToj#d%#Yy9zn(<+k@WC_W?}u-H zE~o^}U<=p-j)1dbJsS?N{aIu`+X}XdbpU-YWQlb!T&#avEEX}^I($eh;;_b%3u3)= zQLLZTh;@{_pW^eMq4$-uV!c`|*3a_*>EqCjcZ2O ziN!Up^&08d&Wm*dIVZM*Gh)3S53v1p(!^)$*WKWNSZ{bhGdL{PN%Bwb0Op#)+N9cb%Cf3;wvHrA8tp8HjC)PP%5T=m+Z{W{0 zV*RBEoDl1;_~|?{-=qHTbNt&Du`a}m^>=Le0KR|h5Q}SXt49F5y^F;b0jI<^c8hJ6 zi*1#PZMTZ;>JZz#4(tF2!AWpY?2t523KoM+U>7(9kim7j?ePKRaJ_DOHiO;ZFgPuC zs13MtKC}`vgDv2^*xpTIhmjw4RP68uKwbp&2+Bn66*~%AR5ie+sO{hYI4S%v4zfTc zfEEpnJx@FO1h^n}OftaM7;KF}N6ap87@QG1HXIazI3_9GjhM!qxOki41IKm*kh1AHd*YFctH8_HrOQg1mu?b0DKdV zi9P9v*psJ#b7D^+uk4`MQ#Xn|trDCUyL^|}(~-m8tX)aDD#}+a2HU|Iv1cv-+rV+L zui7bgHL|Mriao0pK%Y(dIh)0_z*;{tS7GDSNFA&~xokv6nOe=v;T$*PR!8DfIfiVlS%$yTxAKDt5y* zupgWN7sYPO0ySVAKu+T!a9Zpp>eYn4CTLCFU(js#&;Yi8 zz2F!)FLrA(C3UDrl>qu~x9#ptY5N#b6WI4UT}bVz)Y0``C-;EdR7 zJRlcTf@ZK4>;=cbd9k@pwAUhgEwa~kgY95HI1bK>y)GUUgF4U+c7Ou_ySc8k*ZV*@ zKsM_rdp)w(WA}RO-ay$6l-)qt4V2xm9Z+@yWpAV`d;a!~Q@~QN3G4!gz$vje3h;p` zU@6!Hc7em-jMz7MKpv<8$i4~LH|+-}z(uid_JK+O?PmOS^C5u0ZlT;Ql)Hs;w?Mmv z`rL95oDzEzzTA`xszC=p<|gdjguS;4fOadiTcObTcOJWmU>7(H&WL??I4A;jU>(>A4uR8R zf7S!?0JP6S`z*B2Li_LF9_+l=2FSVhs2}G!`tf;muy0^*gYOIFVzXyp-@ird?Nh}5 z;xVzmv`6eOSBt&lgxC-46Z@<2V(&!O&Rt?ZxLxe8QKyHtioL5??1z)VX|W%n%x+|U zy;SUPEEW4PA3(-q)Zww+0J(dh@1for$k?+9P;QT+T@?G9S%5O%>;}|Z<1oi_Vn3b+ zDgm^|k=KW#q|b}}E$H8Z{;du_c5qan{I@8-7y90E&~6I;MO za6-5l0_1`ku+EQN9FG8GK9%eTWuKz#QGOAiV<|^5d-7-$ur_ z;dvU`(>npOpQb*~VDB^7`waFzgT2pS?=z<<@ve=%4L(7AsiWVNvA^p9xu6=ff~{a5I1Vm|eJ~AB z_Fx_82HU}YZ~{R89_7E62WkNH??L|_^zR)9=f(bhGC=S3!vi% z)bR(@@dtasQE*o5ABKZGPzn|UeE7qi;1D=1_F)@P{%|?K#=~2{9&iMl5&K8Epc%mb z@5x{R*a1$8{bS1gxE0`&A72prND0^mPK*6g8fXC6`qBxpUq;T$=zST!pP=g}b6AUFvwiv8;>fbL%(5&MnG z%W)DuuG#G09{VVMN8Ty$CS~8G?C;U@`(|)T?6-Vi3RnuT`z`oRd%%`Y!2#%$e;eMn zOFjkE=k0A^A2{KF>p=TI(EA_VU=N^qz#FbP5!h=$9T8JwO!cQDB{lXwe>z&iV)+$EYmE_a?DPI~tR%+1=%gWr#J+}& zhHYVaczf%OJ@4h(UMqlvp*dp z@g-cPYnicJ>*i zb{x9`$Y);{o-sO|KstJyQ?EpwU#s&|NJoxaqSFI3PpeL+YaXuLkuy-Ixxy#SUN!uV z{rNimh|2e9o5$_bX-ED+l@53EkLa|vf86nDosIR~jSaqK>wPmC+dEhI%2zivwKaD7 z*0yvv`=+GDnQY;0TJ?3>fp z=$ldB+S*dz+0dBhTT?Kips2_M={tPVUu+GOn zYkiq3W|U`EObr-YRFdyEV_JL1`p%XW&E5VAI&Lg02zW9tFVB}-x_0f_f)&ca4UT^c z>bnaYm#qj?eWPzhfuAyE#j5(2)`I2jtA^%zbt}|1b~Sdc!TriFzS;HoKB&G0UTD*DT5WY(0}a~U+~}KERqdP8(b(oNS33-OzJQ-b&|r=* ze_17Fd3#5T>L87+?Q8RV^|TY2^{rj)zWOy7TEDEd(djJpzUgIief8brz0KX-9peiN zyOwvhbaZzWbWt5@UN~oZwb$#-|6l*}YWr&{X8WeknO*0ru9{XcdtODb`M!ML$T7a@ zjmtV$*LSX04LJ5+Qs}Lzttgu{wYmbgH2PMwQ*U2;llJ9D{OHT2f# z>W+@~&Tg$uQo*x{@9HFY*}XlwActKQJW0Bpp}>lvNCwaqOIWu>pnx2k?U4e#q}rcw<~+pkhW zIDxaHzO$Q9(be42;dIn?`dDX|?vRZ>oIy9C({$+y^z}wZGS(n?HQh50SFLWKi{<%N zwKudhsm`V>LnAfmY+1Hi#V2KO>v~^3-MYPPg*u{(bDZ7Q-tFsZZ`Co3%vD{Dt!o;) z3VgJfS1VINXL&0+RKd3OK1M^!8pr9X()ee2eVa0886%}t8Pd3FSz|+kO7$}X!wTCw z9d&waDtTRjLFe}`zHYAX)~Y)FL)Y7ehZ#10b5*@n$n9^5Hrnrd)n=FIU3uQ-_O3QdZy5(YoFXuXbSjawY(}tL~6gWp$VGEzbALL^vlcoxv5LTa;H!ubN#|S2bt$ zJa6Xo;CPjZ4NdeN)k~F_u0}mVnp#@X-&adKrigtJ*IP-mH+JTB4ZV`>%7M$7WIE|L ztLi(iSN*EX*R^_ivvLQOq<4Fro`J&l)t$>RUn$I^Bec+;{P9cmMjYW(S&u)elKIh~ z99#$GC8x=|IvSVzJGGz^J)yoJ%-K$lGFF!vJEPRl3glT8aAY1oWCz=Ks?Zj7ae;3dy>wU|c z>)UY53i?^+s(KXxJ5{y{lYt(1xieIc^|m#x^)xpOy1Q|Cb6d;u`d07S&KA{*sCNcj2c4J3Slir=|J(Y)mcKh+ zW)tdbDr#p{&6~$S@D1@zn=`xIKPYM%J6E-I=`{=elVI9N<BI z?9xKZFD)>2T;?=Ak$wDpq5GDako^-zV?7hEY7hcLn`%sF=46S$@<8P~%S^&C3pX8= zSbz7e^K^{wf>F<9fxy<_^ZQeUzHTB}emxT?E0o4{-GOM=yn0oATRv-x2ECYTCZR%c zd#7@yiZ%rQ&i0PZ7PXpLg^`SmOYEx}yD8Ysnxdt#wV_L|1ywnv01?XwW30rSNPgM$ z-Hx&TGXD9O3xXDc^_rH(wa)ygl+#-~X`NAYjrNZ;=f}4R#kB30#c8ivJUi0`alXsf zxUPd4vZdRnhI2P7A;wbB0Q8SM6=9s9+sS-RY*j(eJFTxloG9v_RN!Fr`$Kb^TCy;6 zs&U~D1-OyP2#2WYPfZajNOw?*z@$2YGykbdtA$kYP_>NFe9CP9knXQqlbjX46W4-x z)fnm)YDbvTR!6IJ__XF{Urs@ey|^lA2hRV}p$LUwD$s>Y;!0crP^%z8UTK=CUB+4_;~w zNhK}?C$$9B>s6Iak~rPknCD%!x=XL4f+tDZfkhGx_tE8$69jc3$Ay>8t`5JR(=fmD zvb7g$m@BWnh!_9d+A9$40}GMB+RJ-s?RA+aa8R9^WoDPD_15KAb;}xwm}+6Sn$Dma zVNH7rEBYqY)`312h&qAjtk$kxRcppOvdgRHO{*@enpIKjt*fkXuE*xhnO=8IS#5={ zYM!sAcFz2&@``d_X4yQ>GxL1cRMl0^xw_5=M{U{cx`kXI_{wH4^j%doyFAZZv4AVB zdGma8YJF9+YO1R$%JY0xv!_*GU0yYNhHon6X3tSqe6yTcry1J~^S95i3%^WTq%Vw7&c6Qb5>9uI7 zm{l>m&Wlyk=F}{#t(s9;mq(#GGV^?OwPocMv&w3(%2O83p~|&B%~F7s$naInhtD^! zvaGt=2i4mr!&f<{x*YDQ6#AxirePqQxSPt8izL<*^D5?EO$K~kzY5x*vO=4V&1L*QO?3^` z4zN>0C8usqZC#%!*Hq1`$n%xeR?Wjb-s!b-W+9xWpoD7lt8pQ^=wgn&s##Q?qUj6t zz77GGD(!U~S6)$8jX?Uv?2qymeEjA?(56RGiO;|N5|qgXQXtn)l-bfQow7>mrIpk& zS#Ly0BfG0@@(#E}nMsj?zR$Ab0CN&+NHqz>-cpG`I%v5A``DH1plC_!=t^8L{Y85me z?|)ZmC8b46&7q8vr)+7H%G*8q+@c-OF-JOUK)pHF5YP1)y@F`7D( z+!5>+E6x@u%3@W%m2;(|6*zWQV6mg4lblt$M#>LGQX;i*tw6G}yz>gR^`Svo;FIy- zlO*~2$#P1Jplm*R@{!}%``?i61>kCwr6xotk#h?Es*xV+31^3+IEy|@4`wR1KUjozTKp!$mH z_nr9G8A*ZB;bW|-o~2sl%I#Z#B(K!z7IMacGp>9x4_!^zyH;DOB(&&}qDI4VB(`at zs=uoK-=OuYc5l|&=1G-QLz{zl74M}H>mxr{g7RMf{-j2iLa=3*>kU|=Jko)@Km<|a zs~ww_R44WXIMoR3f~NA<0OiqoT0)J-;K)+X*>ox6cOAvq&G_=_e$kDd@w8qcQkLuf z(gC##E7dqqt>d(LA$6Wk9hG0b^tb;z{`1m$0sq&~^0RfF=5VYdRSj*L&Yj1VMT(RDH2Sk3T2+ zIku=i>&!{ZbXg^-MUPz{wg$#u2U6RaRU7GTs-LMI9k60GQaf~66|VwyyHr;7PiJOz zBD9w?6^E3iN>;!t^~kQ*)~>>W<>Uv(j2dli_)*QDPOTNyi9f3Tfq6|up5U?OSm@Zk zhO){ZN~)@vvdKA9@xqHWf!LsEPMmVQc|H6}zw)k{r=3|jP(NkAiixTpF4Gdb;Bs0@ z#iKwSR7(Xc`M)~54#2pIqy4r&or(*_6cacjOtUO>x|1c*Rc?*3aTm5sv8A(Y3(J_;EVV`j9aD1mAO{*B@U@Uj>~;yhvt z)t=(WC6HK-tI#S( z!i?<-_C;fx%rlKJ_P!d4qK0V{Pe!Kd2RH(Ig1;tfN3E?_`%ANOF|>!#9L^i!KaL!& zKehUhPf$6;WVC^FPjK816nA974#uHn9P320A?V*u&}t8i0=1jyfn`h3ZnZDf6%wrp zWg?1`-6q~#wQ$rKt%#1GUML?|k6NovR*QZ>BM&IM4F1-@A0qE+=+)7cNdDRJ)zQ!< zj>G2AF8L~APSP8+H5^645L_8>{;=G$=aSB(lA3)`r#8mTL}x)BdOPZHJhXyX_rDwW z!~)UV5n~Yh&>wI`-W6?wJRDD2Z6=^iT)PwNpqZq#I}^q`Su=2LML(Q^HZZzGD@ioO z^#xZ18hEyhB@-*9c}e=d99qD06tq%0o2-qRpR3=A6(msy#S_Vy z8(EWw{*aHZhE(lZNSKe&2I3`_Lj7=lG0r(K5&We;F~iJs?xq+0Cz-|3mm&w>1s ztP_tU>n}z z$?AyEdE9@qjAV^P;1GLJBITnW;Rs-yiugAMGr1d%;nf)OMVi+%!eKOu5g+!QB2Qd@ z(f-luv2HrrBJGp8d+u=}?eQ#Z6`ZLo1{}b1Gem@JbS(V!g>?UZNUow zT0e1SaLuQgBi*rwNnDh~feBWSCD2tew2x%oDEg_@8n5T!I)rvWwvYjiMgcJ-Y4iE@ zu1ccV2l*t@-NSJSt%7)jW)838Aypm&shwOUNXj7I zJv-8-wHeFeI+ymoDq7(Ab?rKdvT@BHuAH$Ybo*|Q@qLIt$x1D|y~x1~{pgCqI> zIln+#`EO?!7*(OqR=tiQ2J~_AOq|KY7VsL@c!>6^X;bj7XeA6T1Kljn-# zF;2o3(T-?@_K(|T)i@f zs~ugVz_l4?cUQ$St7CwXKcWu);?;JPMe~aziFQ14z}9iy!QP@oq|`UG$4x<6a<)jG zP}P8XF&tV(%ttG#MywPc9r)~f9NNXcQN)r~fN+#GXjb(!5`{PG4)5$qzARZ6h$&Xm3drRDyF>?B8zaD|#5lAXN)T zTdIxz$OaBH9?x%GNiFO(=})tF;E_h1ll3R*5nX$DKJP($dbalvzQR{zq20u8k{Nt9 zI^u{uJ36A7I8fw-YNtP#oet^anf4*ecI3DFi;{~aKgR)%DjE`g(X zwH0j$SD5O^r`MbPV;zcRlXV$e#+4SYPA79s_)h)CbF9^X0863Qi($pUGcxS;S&%}l zllx<|=|EtJDMOxaK`8}2tPhkKQa;SO^V{_uWt541?fj}9x>S~!X=E{_I)X1R@3 zP!8|C4MGmKjce<&z%7CD%b+dviDI;l?`h#VB9>eQzu40i;hr7JM9an+sKo&An8rKe zy)17T)Izy-zhwv4^P-r_aVWzfc|Ko>!)*aI9b zyxM_dg=fesKsJ_G1pQhQdV!KqJ1mK#vIdU#K&x0Er7j7%)H56toMr4wwH3sAluiA> zJLKnbduaKn|5EseEn{6=-S_VYUdLJvWl$=%-3ye)9!CxDNv21KX1r>D*z10fg8fCk zt8$7lM2)b=cv>_)ni0X5g`!!}!LXnBkm%6pF!qWO7x1My zIwqP2*9nh=Bfjp(ddNP?S^Ydr@(Var@^@&g>(N)EuYraCCi-1;6I?eN zg-CpRn8ka+f-Vl{M0ibY5~BXy(XZi*@iMT4%cCpcS*eGk```=LSHfQHYog!63$;HW ztZ*V0bs{6`qo?(@ zVwG4e)`$UdZuIZyspzNC&%|1>P7I3m;yiJ_xIkPeHi(U4NDPaT*d#{8X0b(V6{F%J zu}zFc9~9fg4lyovMn8{!AublX#Dtg>Q{ob_TU;vkh|9#~;)UW0aizFQTrI8<*NW@J zi^TQf2JvEXqqs@z6*r5Qh+D+1;x=)+xI?^D+$mlrUM^lC?h>yQuM)2ocZ=7E*NWGP zd&IrsK5@Tzy?BFoqj-~evv`Ymt9YAuyLg9qr+Almw=gv_>1_f_?!5<_=otX_?P&%cuG7i{v)1| zkrYx&CABotN+)AkCo{5MHpoWVB&W&ga)z8K4~pI*XUT)*YEJ`mI$+P5QdA3|4m&$YGGPzu?kSpaXxmvD~1M*zC zR<4tSa=koHo-Z$u7s?HCqa2dMvLrXj5xH4zkz3`cyhv`7V{*IPA;;xTd9mCjC*-7@ zl9$Nc@>01+UM4S>FO*lvE9F)4YI%*kR$eDxB(Ik@$QR2S$@}H&GRDM=|PCh0-FTWtaC?A(!l3$izkx$64%CE_<%WueU%5TYU%kRkV%J0eV%OA)e z${)!e%b&=f%Ad)f%U{S}%3sM}%iqY~%HPS~%Rk5`NIt_IzyeQdQ`9KQ~j!_yjrBrQj68uYKdB^&QZ(MaO6J6xen# zZdZ4xm#RC}%hb!&E7V=;mFiXS6!+cgHR`qMb?P2Ou98dXIXqdY^hYJo)`W^&xl${3Gxr_#^6L>f`DY>XYhI z>eK2o>QVJs^*QyJ`n>vr`l5PVeMx;;eMLQ?zN)^azOKHZzNx;YzOBBazN@~czOR0u zeyDz=eyo0?eyV<^ey)C@eyM(?eyx6^eye_`ey{$Zo>YHSe^P%|e^Gx`e^Y-~|4{!_ z|5E=}PpPNXf7CNN(n3qEwAMyj?Q{&URm|vm-JlzFlb)uh>lu2cK1k2f2kY7T5PhgV zOdqc2=p*zCAPPB3XZ2itv~JeN=z02BeVjgCpP*0F^L2|}pj-7xx(yyqB2NAzZRZQfQrsxQ*p^qAhRcj$4wQ(vri z=?OgvuW-9W@79;7fWd-cuwCGZNZ zTlH=Fc72C_slHRcOut;eLf@rdsb8gEt?$;a(XZ97)A#6mqX(h~^?myO=)KYVqEAK- zMem6|6i6k~_51Y)^au5a^oR9F^hfn0`eXXz`V;W-pHJye>(A&%^=I|x^ke$-`V0Dt z`f>dw{bl_X{e=Fi{+j-}{)Yah{+9l>{*L~x{+|B6{(=6X{*nH%{)zsn{+a%{{)PUf z{+0f<{*C^v{+<54{)2u}|55)*|5^V<|5g7@|6Tt>|5N`<|64z$pVt4;&zQ&vBaJfJ z7-Nkyv8gi|Q*RnfqiHhJ%ycuu%rpm?S>|9f+Zd@k;_%gqbT z73NBFmATqnW3DyVnHQPs%?;+o=0w&na`Uq zm@k^g&6muV%~#A5=Bwsw=IiDg=9}hQ=G*2w=DX&5=KJOc=7;7-=EvqI=BMUo=I7=Y z=9lJI=GW#o=C|f|=J)0g=1KEM^C$CX^B411^EdN%^AGb+^Dpyn^OSko{Kq_FBP*=5 z3O>_nthLU@w$5g3y=}0Kw#iPj)9nm9(;j4J*@NwDdx$;M9%c`>bLElWd#K*>;<^1>0d4+D_YLyY0#L6nm;Y&7N-0 zuxHvH+iUx5zb#sC7umDyVtck-Vwc);>@vIDuCOcZD!baQu>+Kut8||Cyo9$cdTkYHI+wD8-JMFvdyKUL- zvk%w@?L+oG_PzFf_F?;e`vLnw`yu;b`w{z5`-uIR{kZ*v{iOYr{j~jzebj!|e$GB- zKX1Qazi1z~U$S4eU$IZvuiCHKuiJ0fZ`yC!Z`<$K@7nL#@7o{PAKD+;AKRbUpW2_< zpW9#9U)o>UU)$f<-`d~V-`hXfC+#2YpX{IQU+iD)-|XM*KkPs4zwE#5Q}${5AN!1p zoN&@9r=4-uITyP+mvQy3!8N)jH_c6VGu%vfkelTWcC+0f?ofA_JKW82N4OWbBi&Ih z>*l(nU9&sJ&2z`P2C%aSJsqQp) zx;w+2>3UqR>vR3C=)7Cx&T@<0*=~tj>dtY?+;X?Vt#qs0YPZG>xO3fFx6Tc^_3k`( zzPrF(=r*{GZpaP0lH24)+-A4MZFQsWBDc+rx$SO;8+SY1#cr3IaFcGzUE+4TOWhuK znY-M*&|TrKbXU2n-8Jr7cb$8YyWZX4UhHmkH@Us;X7>_zi@Vj`=5BX)xR<&+-OJp| z-7DN(?v?IU?$z#Y_Zs(F_d0ivyVu?4?suU zWw*~g;2v}jx%asDy7#$<-TU1K+y~u<+=tyq+(+Fb?qlxb?i22l?o;m5?lbOD_gVKj z_n7;<`-1zTd)$4=ec64*J>kCUzUIE}zTv*{@zURL0e&BxSe&l}ae&T-W ze&&Aee&K%Ue&v4ce&c@Ye&>Gg{@|W;e{_Fxe|CRye|3Lze|P_I|8)Oy|8`Hgr`>u69?ytp#s|f-;)CPa@geb{@nP}d@tpXG z_yzHi@lkO$o*N$>H^;}s^WtOUMJ|#XiJ}o{yJ|jLe?umQjzPLXw#y(yYpA|2T&yJVGOXG9mW%2TOMZ7Xz6|auh z!~^lU@!EJ@JQ%N!&x_BGFNiOUH^dv`p?Ekh#hc=hcyqiZ-Wrd_7scD+v3PsDBOZ@; z#uvxC;)!@No{BGtcgL5;d*aLD%i|ZuSHxGwSH)My*TmPx*TpZ2ua9qtUmV{U-xTkS zZ;oFQ-xA*%-xl8<-x0qwzB7JV{POq}@m=vN<5$J6j_;0N6TdcoU3^b`Z+u^Tzk|2s zw6*oL5$$bfnrGVKdSL52yg|YZ5w$(TyWo8+b|?_nGrk4()o*h{MC^++?#2jFTcj380;ykA?MVHWYC5y1(m-ZQCE2hDb_s7EqRuvKNip?jRqNJot)}%^ z!$Z6DR`{`tC*ch$BNpE50K_e3Om2{ zC8>@yY^!0mz)OO5jKPNH(UM&{RDu@=*|9(xmZr;%r5L-E252l0y%e<9WAI~_QT-iM ze_5(Z!;TbVmr?y4G{8HCc8nam!ixaaMjgDL)=aqKJ21CtV)S(*p*`JD(b^7BCDy^L?HF6t3y{mQB4P{&MhP>&n?W` zHB@;r5WNN`auO$UfF=@N<$@DAz`DR2Bf>-ukS7J!~mhM@)XX&1`_bktMXL`_*Xp)C2N!L>hS#Aj4mjW(czkFhB z2;OTE(&N>1=smmujwwlRZzJjLZ6v+Djik4?k@WU98vFJ(8vFJ(>P>qaY2V&P+PAln z_U&z?eR~^e-`+;rx3`h@?Kzg8WBECjpJVwsmY-w!IhLR6Y*?Lg@rH>MLmK6{Cpqp( zjwR<)<^qm*6WWhU>yCVJhg5Z)>j$>q;tz#v{-3k>~ly^L*snx*E5Rk8cZar%2TzUGh9D zd7hO#&q|(WCC{^x=UK_~tmJuC@;obfo|Qb$N}gvW53>R*On4PyRd_phxt*pk&jX+5 zfzR{6=XrqhJivJ#;5-j-o(DM31Dr3=K;{eFexaigUXurJihG!pp=kQMVXE4LZIY-Vg{O1Th1t!kOjUN48L*8KY3VMuG_vbT7Q& z$1H+ZU=2Bzqt`>00yzfWPBS^AQIJ^zZ@L)*+}%FBX-J+kC6`Ufb)$fOL9$#is#a|s zH>*eC^_FVQ(3Im|s1;jBRUiDVm>6xqc9I@U;{pkv8LFx~lF}fC4v@Oo|B#wBMaPq> zA=8BVdKikEEvSrvSC&ls2C8n^JW zuZX4M=G!* z71)sq>_`Q6qyjrqfgP#9j#S9eVo}J^qEyJyqEz5Tt-y;~AxDc+AxDc+ffu;~FLH%; zT9gXytbaTCOrf0?r2_kAfqk>UzFA=3EU<4Dcu_3yqFCTXvA{lCV4p3p&lcEc3+%H6 z_Sph2iUnR23wiS0LY~%!LY@}o0xy<@JnNrl{qwYt74ozw7xJusp7qbuqFTt)qFgAj z{sq>b*Nj4e_2;#tP+<85mS5oUFR=Uq%P+9}0?RM3`~u4_u>1nc?_l{IEWd-tzk}s> zu>Kt^zk}s>u>1~|-@)=bSbhh~?_l{IEWd;0cLe#le)5N0k^BH?=m*fy51^qRKtn%( zhJFAI{Qw&J0W|ajXy^yf&<~)YA3#GtfQEhm4gCO0{V0+jDt0MGKt4}fR+rZ|FJnK(>06gnYegHh{&wfx`82m6-T*&&8H#0{V)*}#QxKdhcC-H7tbaG_-_7#7S$;Rm?`HYkEWexOceDI% zmfy{C>2g)B*u!#rc-(tfZV$`tVYxjlw}<8Su-qP&+rx5uSZ)u??P0k+talH~?`6Gv zSw3Ah%N2WBelN@KW%<1^6Atbc$QD6_Q11zI<*I$<Es^D zv3xqY2cG5A$vyBapHA+9XZdt;pDX&D9V7?b69{=3o!Y}umhM@)XX$jR564-$C#jy% z+%uYcMsv?-?itNJqq%1^_l)MA(cCkddq#85Xzm%!J)^m2H1~|=p3&ShntMib&uH!$ z%{`;JXY}@r-k#ChGkSYQZ_nuM8Ld5|wP&>UjMkpf+A~^vMr+S#?HR2-qqS$W_Ken^ z(b_Xwdq!)|XzdxTJ)^Z}wDyeFp3&MfT6;!o&uHx#tv#c)XSDW=)}GPYGg^B_YtLxy z8Ld5|wP&>UjMkpf+A~^vMrY6H>=~UsqqApp_KeP+(bzK@dq!i=XzUq{J)^N_H1>?f zo>AB{`g%rP&uHrzZ9Su{XSDTltl5qpfGO^^CTj(bhBCdPZB%XzLkmJ)^B>wDpX(p3&Ab+WIb9WqlW|vc8K}8Q(># ztnZ>##&?mQdq!8!=;|3=J)^5A5_%6djw&nW8|Wj&*;XO#7fvYt`aGs=2KSKRQvqp4>!^^B&TQPeYvdPY&tDC!wSJ)@{+6!navo>9~@ih4#-&nW5{MLnaaXVmnJ znx0Y9GirK9P0y(587)1drDwGCjFz6!(lc6mMoZ6V=@~6OqorrG^o*9C(b6+odPYmn zXz3X(J)@;(wDff8JLl=rH&FH)#zoJ#=ouG1=mwc2R{WG z{1j;LQ=q|5fd+X%gP#Hocnmb)G0+eV0S$NzG{^-Sa2aTj3pB_Dn#c{&0DOms<>zvl z&7)hU;0qpbVID&PIK(1z3+?vN9e8JM014_qpG_G(FASekmbQUPJ+e_<3v6t2hpj57x)(PN4zlt2C0uAHt z3pD@W{$X5!hIs`V<`rnr3uu^EprKu$VP3)PyzMEr4vkH!(uPU3dBZLj?uu;~Wja<* zeE4llr7Ih!@fWSGg0As*lec zNoM*2Q<(%XfTn6&Sgxrm4cTecqoK`d)x&{rOk~i}YA4c13$1BxVGY+=!}Zi~eZ-l~ zt^LITGupal6+AfD+SjwjjNxyMStdp{(b$um&tt8|{yT z*3^aH?eLu;b4lRCFY=k-3!!M!7WgCpJW?<`Hi4dGS5gB5SV67XuoYYj|F*^83gM8N z48FD%{9_yZ4Z|P$-bL&m2_0sBE7NV$Q~nnkio*?y)`^nCdlo?BaN7bC;m!plGJNv_ z3*r3>pq9&qyBL^{cOHPo;a&zL>gaX`q|Ejy{M`|LFCB$6+;0eXYcP8!W>~n*0VEE$ zIFN_C96&OBlLB+#UI!Am-4VD6ByqTJfr)Sj2@EY`CLU(P2gC6Wn)#{5aftf`o;;Pgrh% zZW3V*+=D^_x1j<@I)po8z}Hf_c zaucJMhD2rp9@*JJ$r1cm92JDmwQyyYu``hY36imMq9SAGL?Q!IBm>Hk3@De#fMg;A z3bG7Hk&MwDn@JB$1R0P>WI&Q-gjA4$<$??>$1*U0Z^WQ2l2Mo0x2V>_m{2N@xOGBCk1FiA2fg)%}plo862jF2Q5 zpZEy;nBr%UA*6Dq2YaZ=z;2{6reSYV{0!<;I*0m} z%9$Rzn968MdJ5lu!{=_epUiPDiJMmGBk@g14-=OA$91`PT({CU<|{qp%1OV7t*3A< zSb5{cQ}A^WSU>|e4MukuK5hcvSqgmpCj1H>IZgw7-vghF3kRBpw&RzLVCfC)^w0`^ zXKajIrU^bj2TyyAY^LJV0vo=N6UsLZO~415Mklrfu5Rbp6nuyfd>Fp11NVMIvVQ9x zcutGE0kTHNNmM30M3R(-WjQ39Ch#+R;|W`bACRFk@M(`BQmz4>K!cCukT#$wvi(GgL_Cp2M9w0zn8?{gmJnG=+q{IxZX&db+dafx7DyB73|~EC1B~HmxsX3CPIyxFM!1Zir0E4Uw&J!*IOnhRCMf5c#SbB1dkBDRx6lCT5ft20rD6$V0uv4Uw(5VK`QGLu3;-3~6@5 zP$zN2aDd$q(@j-3q&nf^27Cy2$L6XVBA0eUPH{ zbwlJB4I!1FAtVzw!~*pR8e$P%<*9UlKS-wBFtkW$h-DZJAz5+5(1R)(!l8hMKoJd* z02(3zG(-Yuhy>6O37{boKtm*ehDZPnkpLPZ0W?GcXov*R5DB0m56O37{boKtm*ehDZPnkpLPZ0W?Gc z2!aIA5DB0m5 z8zP&yVMr%vh;gWNpK?RslAsjM*OH(VL|1}RNGEOxg%dZ#WD1f@VQCPjZ2nMA*lmfw^6bJ^TK)?-=U{DGKgHj+Elmfw^6h^-`D235T2c*vjM@N6bJ^TKrko; zf5)4X#U{DGKgHj+El;RMi zf>O9RKv5ZA8Z4!x!A?pVtfZvDMoJpQmNeK$ zNrQEiG}uN-gJqO7*hNW$Rg^T?L`j21lr-2wNrN?%G}uB(gC&$S*g;8y6_hmCKuLoI zlr+qLNyF@yG|YWT!_1fJ@T>u^IzcL2y@eE9<*n>N3kUcdq|{J(6dAJPu(bjbW(cdp zW;M6~3a8{)Bbi-DRT_oQStlFYphyE0rf1Tz{Ai^Uqm}Y}4u~7!u>5GMJY1{9QK^G3 zZo=jwOyQ@NNsIAH1LHLU>)`9RRJZ}YS6vZZzkOY31-c(I{ z4fyT(@K8P0+>?}w@$=9)DUGS?zfd52a~Qjt;cb9ei?#rUOxn?enWdzurquR7IM7(# z_7{qUtu~mV&3}}_{eO_k@YX-fq-}qYYOHSe3q`6TX_p^lHl;TAVcjiZe-_rQZtnwb zT6$9-R!i;W1J-Q~s|QSLGUOTFzy}BGX!9PVGQ4jOYlO@9U_`J>0-*@a-Hf8WH>I}l z0Xr?daWC+xy?emJMQYs7gGbW4^}>;4z#Q(=3pthPrrmm2E4@b#%j`&MS=f>X8E~aL z>|n!VXW%MMuBbXWDKiKG`9Scc&I zJ&!eM%N?W~Zn(o7+H41@4DYiGnMp6>u)z+prq}GM3&kt8(3ILo2S=Jx+vk97*poJT z+D8Z3jn(~gz|~!r^wxztF|g9~K(>bUu%uxbENNK%O1g+Yu%4DQga#!Ifk8<_SWwas z6qGcC1SJgtK}kb6P|^?#lr)3_B@KZRF!#kR>HQLM!sSIyq#>~E?N>ge#GaP8FZek9_ z5<98uPo^~V#t77T9w6Wc`^>uYjzGpDYG06yKYsb+4P3t za3pEAj+ch_Wc6eFqrbCI@ml~W^RVtbDiPFsd%^=B28A2tiY6@VPp&}Ur z_#8C@PoM1B)s#wC9-V=K#z$wWV76&wD)qzza@B<%xX>W%NNZias<(b(C)}f;aiRRS zZnQy4=L)HGZ#A7y<>yoR9ic8=I-&a;@I+F05(3tP@Gu3eLJ+vN<=~0{o|DbkGBr8| zmtMAvh1(<`cxEMs8i1@&Xl!IVhKz78B`FkgDupcG*KEtp*zY!MTQ2>4#zZMcPr%TS z)iCX7*7%4Rv=Wl=lo%u%s^_+Cxi~y%F}ic8EtY(1w|ZrI}>WWh5&gU^BN zKB#6dG7a4g5l-YQJC1>GtnO3= zZdQ7`Yo&>FCKQ^6H`o%n3>hJ0iK2m$7fcf#!6PT6s!{amrAl4S|=wr!>dTfw~p?Z zg!clAXP$vK0N4Ka7`%HI{`BL|JY^=-#m_wX^!HNfCn9)xY)f`iIlFOlb2(bEZe(yl zSwKd%{K$&3K6ZUs7tdQaw|VZIz3Z~&6)V=wEuT3!CtL2qRM+5Owwx&rZ7Ls+Y^Ink zw_&0UOFXh-U3PPJ@7^IOvSQsv$jOF6m|BRbg&XH=92^{+Q;y~j4zg?zE?ctDj-EFJ zT`+ws)|E|jZ`n5Y&Y3%Ruq-w%D65uc=udXjJ~P~##Ul@7ggkLBB>S>^vwNYreQjpm z-j(Y%u9!2lYH(fiARIe$^*T5(2YX*pQ@503XxqVGgnxZy*SB(AIqsYP0E}hd#@_Pm zkt3i-rUi~+r)05I7RBL>{R_%A$&2PU@3VT|#%ytK^AHYO&@!5XBT>%I0eOkQvYOXC z)DLxC%RXa@WibTlxCL~|!VvYHjiV2V=H9__J#wpns|RjDxvnMqu#SfDrvy^U4SgH4 zdpBmw4b8pH3(A?6eNkiIz`A{njeXE)Z@F&%ASxs0^|BuOGLYU^4?krwyEzLTfxbZr zJ+F5!j2N^SpE$P}>LzKJlTaVp2*(DYe~X}3i#C?894VxxXy>%F*c$z-y3U z$QG3Ap*yCyW?fWnZ0^l&gjV0v)Fj~b)V;lXH}1>m`Q_31bB+eB8lYhP`~~I4mVE+g zQ_DVybXv-t$K8VUwE&D9egIe}Eq_bM~#Yhis*;j{jc1zjK|6j^J1Z2;K zI){R6q=$iQq=$oSq;o(v(j!1N(iebiq(_2mq(^~lq*;)SbZ!f1+O#p-2cx(VhYtQ< zxvn{TDzMGvnIlJ_^)4tM-BO-AzdZNE1?A?JY<3aMdty?}LtV|;y#wp^&zytRj;Rc( zm|bo@u`CX53nMr$t#R$4V_UKv!CQ`NiOP!X791dvhskL6Lt&-pKfSqY-?8Fg=+*Hp z*;AnhNvVxs218v7$|tn69&+k}@`(pH3X@-g67ymFqS^DZt=UDvm*l*~d-pDCUer7^ zTo&M=Ckk=!ED+EFZZjL~4*m~E%eFXwWN&M8Hhbz`Xl6n6aJDrRfRzBprkiaE@p`Ek3euKobwMlbt@ z=0JMmAou_j8v?nm+>kDl`6Vwz)04E0qhGTx&fJYdt3-w@W1JB3S z;TM*)In8qit1ZIBwW2;*kYSExeVR`NHBSoJY}Pl!kj;nrUr=rf>O;>Yk505^ zU2FDKSbWK%ctk*aW%%YnXBNZJp|U8Jnulq9YUTlj?kp(hSXf^&q8o9o2L+NrZ*OVN zwxZ@9R-97@TlXC;W`RlPE7>sa)$BqgJCshU)|5E~L>^3*04os8{6Ved<6-tseUALo zz`n(-gUTmBnbTX!^MQ1tSR2#IsCIZUj@<}k4io5MtBm>0C#WE#;T!Mn4q;5sy1 z=U_XXPKNcIGMPhoZ8$qXKu>G<_>ai zd$6}%(4}7NEhhS~x0vY1-eTg+7PQdv8IbJ8*+>kdh;_Rm*JE8wEW)~&I1B4y;$)nF zxoBai8xK-t%vWLyCquDS*a9Y2V+)vAgDqg<3>@f-99019eh8$2{I~WVR`Ha zL~h<$o>hc};`ogaGY!B(m3VYnHupc6$rwHV!TRa=J7YHd4nCldkBh7NAg8U mcZ|3Zf|oOITo<@wk%M@FSO~KQ9~RGCU)HzmGw|hx=zjoaQiNsz From 16838595a3b124cb05303aded67a3b38bbf08ade Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 05:24:32 +0100 Subject: [PATCH 137/251] Another console fix: discard text markup when getting the command line's text --- apps/openmw/mwgui/console.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index c15cc7b1d4..f3805b255a 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -213,7 +213,7 @@ namespace MWGui { std::vector matches; listNames(); - mCommandLine->setCaption(complete( mCommandLine->getCaption(), matches )); + mCommandLine->setCaption(complete( mCommandLine->getOnlyText(), matches )); #if 0 int i = 0; for(std::vector::iterator it=matches.begin(); it < matches.end(); ++it,++i ) @@ -232,7 +232,7 @@ namespace MWGui { // If the user was editing a string, store it for later if(mCurrent == mCommandHistory.end()) - mEditString = mCommandLine->getCaption(); + mEditString = mCommandLine->getOnlyText(); if(mCurrent != mCommandHistory.begin()) { @@ -257,7 +257,7 @@ namespace MWGui void Console::acceptCommand(MyGUI::EditBox* _sender) { - const std::string &cm = mCommandLine->getCaption(); + const std::string &cm = mCommandLine->getOnlyText(); if(cm.empty()) return; // Add the command to the history, and set the current pointer to From 18d4cdb2ac56ec7c5929f157ea8e2a34bb8ad618 Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Sat, 4 Jan 2014 07:27:50 +0200 Subject: [PATCH 138/251] Fix for VS2010-Debug: manual assignment operator and cctor added for MMWorld::ContainerStoreIterator --- apps/openmw/mwworld/containerstore.cpp | 40 ++++++++++++++++++++++++++ apps/openmw/mwworld/containerstore.hpp | 8 ++++++ 2 files changed, 48 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 686e790a38..39fda7ab9a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -555,6 +555,11 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *contain MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Weapon), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mWeapon(iterator){} +MWWorld::ContainerStoreIterator::ContainerStoreIterator( const ContainerStoreIterator& src ) +{ + copy(src); +} + void MWWorld::ContainerStoreIterator::incType() { if (mType==0) @@ -807,6 +812,41 @@ const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStor return mContainer; } +void MWWorld::ContainerStoreIterator::copy(const ContainerStoreIterator& src) +{ + mType = src.mType; + mMask = src.mMask; + mContainer = src.mContainer; + mPtr = src.mPtr; + + switch (mType) + { + case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break; + case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break; + case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break; + case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break; + case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break; + case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break; + case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break; + case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break; + case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break; + case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break; + case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break; + case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break; + case -1: break; + default: assert(0); + } +} + +MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator=( const ContainerStoreIterator& rhs ) +{ + if (this!=&rhs) + { + copy(rhs); + } + return *this; +} + bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) { return left.isEqual (right); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b34c710063..780d461997 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -143,6 +143,10 @@ namespace MWWorld MWWorld::CellRefList::List::iterator mRepair; MWWorld::CellRefList::List::iterator mWeapon; + public: + + ContainerStoreIterator(const ContainerStoreIterator& src); + private: ContainerStoreIterator (ContainerStore *container); @@ -165,6 +169,8 @@ namespace MWWorld ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + void copy (const ContainerStoreIterator& src); + void incType(); void nextType(); @@ -189,6 +195,8 @@ namespace MWWorld ContainerStoreIterator operator++ (int); + ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); + bool isEqual (const ContainerStoreIterator& iter) const; int getType() const; From 70233950124f9f53b1809eb565141120b57029ad Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Sat, 4 Jan 2014 07:30:43 +0200 Subject: [PATCH 139/251] Windows-specific definition to omit inclusion of unused stuff from Windows.h --- apps/openmw/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 2bf48c1bb9..3129e6bd3c 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -11,6 +11,7 @@ #include // For OutputDebugString +#define WIN32_LEAN_AND_MEAN #include // makes __argc and __argv available on windows #include From 116f125f05b9fa12f76267068f169b66fa8e15dc Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Sat, 4 Jan 2014 07:41:23 +0200 Subject: [PATCH 140/251] Windows-specific definition to deny SDL from providing it's own WinMain() --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f8121c49..5cb2fd5b2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,6 +183,9 @@ if (WIN32) set(Boost_USE_STATIC_LIBS ON) set(PLATFORM_INCLUDE_DIR "platform") add_definitions(-DBOOST_ALL_NO_LIB) + + # Suppress WinMain(), provided by SDL + add_definitions(-DSDL_MAIN_HANDLED) else (WIN32) set(PLATFORM_INCLUDE_DIR "") find_path (UUID_INCLUDE_DIR uuid/uuid.h) From f4517c8221184dc85904724b97ca86bdc59f0a91 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 20:02:29 +0100 Subject: [PATCH 141/251] For dialogue filtering, use the Vampirism magic effect instead of the untouched NpcStats::mVampire --- apps/openmw/mwdialogue/filter.cpp | 3 ++- apps/openmw/mwmechanics/npcstats.cpp | 11 ----------- apps/openmw/mwmechanics/npcstats.hpp | 5 ----- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 9d08debffb..92738ec0ed 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -514,7 +514,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_PcVampire: - return MWWorld::Class::get (player).getNpcStats (player).isVampire(); + return MWWorld::Class::get (player).getCreatureStats(player).getMagicEffects(). + get(ESM::MagicEffect::Vampirism).mMagnitude > 0; case SelectWrapper::Function_TalkedToPc: diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 0a0b51270d..94dd971867 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -28,7 +28,6 @@ MWMechanics::NpcStats::NpcStats() , mBounty (0) , mLevelProgress(0) , mDisposition(0) -, mVampire (0) , mReputation(0) , mWerewolfKills (0) , mProfit(0) @@ -318,16 +317,6 @@ void MWMechanics::NpcStats::setFactionReputation (const std::string& faction, in mFactionReputation[faction] = value; } -bool MWMechanics::NpcStats::isVampire() const -{ - return mVampire; -} - -void MWMechanics::NpcStats::setVampire (bool set) -{ - mVampire = set; -} - int MWMechanics::NpcStats::getReputation() const { return mReputation; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 552422d843..586e068322 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -50,7 +50,6 @@ namespace MWMechanics int mBounty; std::set mExpelled; std::map mFactionReputation; - bool mVampire; int mReputation; int mWerewolfKills; int mProfit; @@ -135,10 +134,6 @@ namespace MWMechanics void setFactionReputation (const std::string& faction, int value); - bool isVampire() const; - - void setVampire (bool set); - bool hasSkillsForRank (const std::string& factionId, int rank) const; bool isWerewolf() const; From c4e4a8fb57869d74b7c4ac169fa2ed2debe48501 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 20:43:57 +0100 Subject: [PATCH 142/251] Closes #1083: Fix werewolf change handling --- apps/openmw/mwrender/characterpreview.cpp | 12 +++-------- apps/openmw/mwrender/npcanimation.cpp | 26 +++++++++++++++++------ apps/openmw/mwrender/npcanimation.hpp | 8 +++++++ apps/openmw/mwworld/inventorystore.cpp | 5 +++++ apps/openmw/mwworld/worldimp.cpp | 10 +++++++-- 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index a0ba01b37b..643225515b 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -140,8 +140,7 @@ namespace MWRender void InventoryPreview::update(int sizeX, int sizeY) { - // TODO: can we avoid this. Vampire state needs to be updated. - mAnimation->rebuild(); + mAnimation->updateParts(); MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); @@ -177,12 +176,8 @@ namespace MWRender groupname = "inventoryhandtohand"; } - // TODO see above - //if(groupname != mCurrentAnimGroup) - //{ - mCurrentAnimGroup = groupname; - mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); - //} + mCurrentAnimGroup = groupname; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) @@ -194,7 +189,6 @@ namespace MWRender else if(mAnimation->getInfo("torch")) mAnimation->disable("torch"); - mAnimation->updateParts(); mAnimation->runAnimation(0.0f); mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b1455f0dc6..dbb5a08688 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -119,7 +119,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mShowWeapons(false), mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), - mAlpha(1.f) + mAlpha(1.f), + mNpcType(Type_Normal) { mNpc = mPtr.get()->mBase; @@ -157,8 +158,8 @@ void NpcAnimation::updateNpcBase() const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - bool isWerewolf = mPtr.getClass().getNpcStats(mPtr).isWerewolf(); - bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).mMagnitude; + bool isWerewolf = (mNpcType == Type_Werewolf); + bool isVampire = (mNpcType == Type_Vampire); if (isWerewolf) { @@ -167,7 +168,7 @@ void NpcAnimation::updateNpcBase() } else { - if (vampire) + if (isVampire) mHeadModel = getVampireHead(mNpc->mRace, mNpc->mFlags & ESM::NPC::Female); else mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; @@ -221,11 +222,24 @@ void NpcAnimation::updateNpcBase() } void NpcAnimation::updateParts() -{ +{ mAlpha = 1.f; const MWWorld::Class &cls = MWWorld::Class::get(mPtr); MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + NpcType curType = Type_Normal; + if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).mMagnitude > 0) + curType = Type_Vampire; + if (cls.getNpcStats(mPtr).isWerewolf()) + curType = Type_Werewolf; + + if (curType != mNpcType) + { + mNpcType = curType; + rebuild(); + return; + } + static const struct { int mSlot; int mBasePriority; @@ -329,7 +343,7 @@ void NpcAnimation::updateParts() static const int Flag_Female = 1<<0; static const int Flag_FirstPerson = 1<<1; - bool isWerewolf = cls.getNpcStats(mPtr).isWerewolf(); + bool isWerewolf = (mNpcType == Type_Werewolf); int flags = (isWerewolf ? -1 : 0); if(!mNpc->isMale()) flags |= Flag_Female; diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 28bb81dddf..8edcc04be6 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -55,6 +55,14 @@ private: bool mShowWeapons; bool mShowCarriedLeft; + enum NpcType + { + Type_Normal, + Type_Werewolf, + Type_Vampire + }; + NpcType mNpcType; + int mVisibilityFlags; int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 509c34afb3..2944f00d46 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -139,8 +139,13 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) { + // Only *one* change event should be fired + mUpdatesEnabled = false; for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) unequipSlot(slot, actor); + mUpdatesEnabled = true; + fireEquipmentChangedEvent(); + updateMagicEffects(actor); } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c23c63e2e7..621cd75eac 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1960,6 +1960,10 @@ namespace MWWorld npcStats.setWerewolf(werewolf); + // This is a bit dangerous. Equipped items other than WerewolfRobe may reference + // bones that do not even exist with the werewolf object root. + // Therefore, make sure to unequip everything at once, and only fire the change event + // (which will rebuild the animation parts) afterwards. unequipAll will do this for us. MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); invStore.unequipAll(actor); @@ -1974,6 +1978,10 @@ namespace MWWorld actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); } + // NpcAnimation::updateParts will already rebuild the animation when it detects change of Npc type. + // the following is just for reattaching the camera properly. + mRendering->rebuildPtr(actor); + if(actor.getRefData().getHandle() == "player") { // Update the GUI only when called on the player @@ -1991,8 +1999,6 @@ namespace MWWorld windowManager->unsetForceHide(MWGui::GW_Magic); } } - - mRendering->rebuildPtr(actor); } void World::applyWerewolfAcrobatics(const Ptr &actor) From 92234cf78301348c6423fa8791cf8ee8f3937f50 Mon Sep 17 00:00:00 2001 From: pvdk Date: Sat, 4 Jan 2014 21:46:38 +0100 Subject: [PATCH 143/251] Changed crashcatcher's uname system info retrieval and cleaned indentation --- apps/openmw/crashcatcher.cpp | 599 ++++++++++++++++++----------------- 1 file changed, 302 insertions(+), 297 deletions(-) diff --git a/apps/openmw/crashcatcher.cpp b/apps/openmw/crashcatcher.cpp index 6663306663..d7ebc0d47b 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -42,78 +43,78 @@ static char altstack[SIGSTKSZ]; static struct { - int signum; - pid_t pid; - int has_siginfo; - siginfo_t siginfo; - char buf[1024]; + int signum; + pid_t pid; + int has_siginfo; + siginfo_t siginfo; + char buf[1024]; } crash_info; static const struct { - const char *name; - int signum; + const char *name; + int signum; } signals[] = { - { "Segmentation fault", SIGSEGV }, - { "Illegal instruction", SIGILL }, - { "FPU exception", SIGFPE }, - { "System BUS error", SIGBUS }, - { NULL, 0 } +{ "Segmentation fault", SIGSEGV }, +{ "Illegal instruction", SIGILL }, +{ "FPU exception", SIGFPE }, +{ "System BUS error", SIGBUS }, +{ NULL, 0 } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigill_codes[] = { -#ifndef __FreeBSD__ - { ILL_ILLOPC, "Illegal opcode" }, - { ILL_ILLOPN, "Illegal operand" }, - { ILL_ILLADR, "Illegal addressing mode" }, - { ILL_ILLTRP, "Illegal trap" }, - { ILL_PRVOPC, "Privileged opcode" }, - { ILL_PRVREG, "Privileged register" }, - { ILL_COPROC, "Coprocessor error" }, - { ILL_BADSTK, "Internal stack error" }, -#endif - { 0, NULL } + #ifndef __FreeBSD__ + { ILL_ILLOPC, "Illegal opcode" }, + { ILL_ILLOPN, "Illegal operand" }, + { ILL_ILLADR, "Illegal addressing mode" }, + { ILL_ILLTRP, "Illegal trap" }, + { ILL_PRVOPC, "Privileged opcode" }, + { ILL_PRVREG, "Privileged register" }, + { ILL_COPROC, "Coprocessor error" }, + { ILL_BADSTK, "Internal stack error" }, + #endif + { 0, NULL } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigfpe_codes[] = { - { FPE_INTDIV, "Integer divide by zero" }, - { FPE_INTOVF, "Integer overflow" }, - { FPE_FLTDIV, "Floating point divide by zero" }, - { FPE_FLTOVF, "Floating point overflow" }, - { FPE_FLTUND, "Floating point underflow" }, - { FPE_FLTRES, "Floating point inexact result" }, - { FPE_FLTINV, "Floating point invalid operation" }, - { FPE_FLTSUB, "Subscript out of range" }, - { 0, NULL } + { FPE_INTDIV, "Integer divide by zero" }, + { FPE_INTOVF, "Integer overflow" }, + { FPE_FLTDIV, "Floating point divide by zero" }, + { FPE_FLTOVF, "Floating point overflow" }, + { FPE_FLTUND, "Floating point underflow" }, + { FPE_FLTRES, "Floating point inexact result" }, + { FPE_FLTINV, "Floating point invalid operation" }, + { FPE_FLTSUB, "Subscript out of range" }, + { 0, NULL } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigsegv_codes[] = { -#ifndef __FreeBSD__ - { SEGV_MAPERR, "Address not mapped to object" }, - { SEGV_ACCERR, "Invalid permissions for mapped object" }, -#endif - { 0, NULL } + #ifndef __FreeBSD__ + { SEGV_MAPERR, "Address not mapped to object" }, + { SEGV_ACCERR, "Invalid permissions for mapped object" }, + #endif + { 0, NULL } }; static const struct { - int code; - const char *name; + int code; + const char *name; } sigbus_codes[] = { -#ifndef __FreeBSD__ - { BUS_ADRALN, "Invalid address alignment" }, - { BUS_ADRERR, "Non-existent physical address" }, - { BUS_OBJERR, "Object specific hardware error" }, -#endif - { 0, NULL } + #ifndef __FreeBSD__ + { BUS_ADRALN, "Invalid address alignment" }, + { BUS_ADRERR, "Non-existent physical address" }, + { BUS_OBJERR, "Object specific hardware error" }, + #endif + { 0, NULL } }; static int (*cc_user_info)(char*, char*); @@ -121,314 +122,318 @@ static int (*cc_user_info)(char*, char*); static void gdb_info(pid_t pid) { - char respfile[64]; - char cmd_buf[128]; - FILE *f; - int fd; + char respfile[64]; + char cmd_buf[128]; + FILE *f; + int fd; - /* Create a temp file to put gdb commands into */ - strcpy(respfile, "gdb-respfile-XXXXXX"); - if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) - { - fprintf(f, "attach %d\n" - "shell echo \"\"\n" - "shell echo \"* Loaded Libraries\"\n" - "info sharedlibrary\n" - "shell echo \"\"\n" - "shell echo \"* Threads\"\n" - "info threads\n" - "shell echo \"\"\n" - "shell echo \"* FPU Status\"\n" - "info float\n" - "shell echo \"\"\n" - "shell echo \"* Registers\"\n" - "info registers\n" - "shell echo \"\"\n" - "shell echo \"* Backtrace\"\n" - "thread apply all backtrace full\n" - "detach\n" - "quit\n", pid); - fclose(f); + /* Create a temp file to put gdb commands into */ + strcpy(respfile, "gdb-respfile-XXXXXX"); + if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) + { + fprintf(f, "attach %d\n" + "shell echo \"\"\n" + "shell echo \"* Loaded Libraries\"\n" + "info sharedlibrary\n" + "shell echo \"\"\n" + "shell echo \"* Threads\"\n" + "info threads\n" + "shell echo \"\"\n" + "shell echo \"* FPU Status\"\n" + "info float\n" + "shell echo \"\"\n" + "shell echo \"* Registers\"\n" + "info registers\n" + "shell echo \"\"\n" + "shell echo \"* Backtrace\"\n" + "thread apply all backtrace full\n" + "detach\n" + "quit\n", pid); + fclose(f); - /* Run gdb and print process info. */ - snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile); - printf("Executing: %s\n", cmd_buf); - fflush(stdout); + /* Run gdb and print process info. */ + snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile); + printf("Executing: %s\n", cmd_buf); + fflush(stdout); - system(cmd_buf); - /* Clean up */ - remove(respfile); - } - else - { - /* Error creating temp file */ - if(fd >= 0) - { - close(fd); - remove(respfile); - } - printf("!!! Could not create gdb command file\n"); - } - fflush(stdout); + system(cmd_buf); + /* Clean up */ + remove(respfile); + } + else + { + /* Error creating temp file */ + if(fd >= 0) + { + close(fd); + remove(respfile); + } + printf("!!! Could not create gdb command file\n"); + } + fflush(stdout); } static void sys_info(void) { #ifdef __unix__ - system("echo \"System: `uname -a`\""); - putchar('\n'); - fflush(stdout); + struct utsname info; + if(!uname(&info)) + printf("!!! Failed to get system information\n"); + else + printf("System: %s %s %s %s %s\n", + info.sysname, info.nodename, info.release, info.version, info.machine); + + fflush(stdout); #endif } - static size_t safe_write(int fd, const void *buf, size_t len) { - size_t ret = 0; - while(ret < len) - { - ssize_t rem; - if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1) - { - if(errno == EINTR) - continue; - break; - } - ret += rem; - } - return ret; + size_t ret = 0; + while(ret < len) + { + ssize_t rem; + if((rem=write(fd, (const char*)buf+ret, len-ret)) == -1) + { + if(errno == EINTR) + continue; + break; + } + ret += rem; + } + return ret; } static void crash_catcher(int signum, siginfo_t *siginfo, void *context) { //ucontext_t *ucontext = (ucontext_t*)context; - pid_t dbg_pid; - int fd[2]; + pid_t dbg_pid; + int fd[2]; - /* Make sure the effective uid is the real uid */ - if(getuid() != geteuid()) - { - raise(signum); - return; - } + /* Make sure the effective uid is the real uid */ + if(getuid() != geteuid()) + { + raise(signum); + return; + } - safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1); - if(pipe(fd) == -1) - { - safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1); - raise(signum); - return; - } + safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err)-1); + if(pipe(fd) == -1) + { + safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err)-1); + raise(signum); + return; + } - crash_info.signum = signum; - crash_info.pid = getpid(); - crash_info.has_siginfo = !!siginfo; - if(siginfo) - crash_info.siginfo = *siginfo; - if(cc_user_info) - cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf)); + crash_info.signum = signum; + crash_info.pid = getpid(); + crash_info.has_siginfo = !!siginfo; + if(siginfo) + crash_info.siginfo = *siginfo; + if(cc_user_info) + cc_user_info(crash_info.buf, crash_info.buf+sizeof(crash_info.buf)); - /* Fork off to start a crash handler */ - switch((dbg_pid=fork())) - { - /* Error */ - case -1: - safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1); - raise(signum); - return; + /* Fork off to start a crash handler */ + switch((dbg_pid=fork())) + { + /* Error */ + case -1: + safe_write(STDERR_FILENO, fork_err, sizeof(fork_err)-1); + raise(signum); + return; - case 0: - dup2(fd[0], STDIN_FILENO); - close(fd[0]); - close(fd[1]); + case 0: + dup2(fd[0], STDIN_FILENO); + close(fd[0]); + close(fd[1]); - execl(argv0, argv0, crash_switch, NULL); + execl(argv0, argv0, crash_switch, NULL); - safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); - _exit(1); + safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); + _exit(1); - default: + default: #ifdef __linux__ - prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0); + prctl(PR_SET_PTRACER, dbg_pid, 0, 0, 0); #endif - safe_write(fd[1], &crash_info, sizeof(crash_info)); - close(fd[0]); - close(fd[1]); + safe_write(fd[1], &crash_info, sizeof(crash_info)); + close(fd[0]); + close(fd[1]); - /* Wait; we'll be killed when gdb is done */ - do { - int status; - if(waitpid(dbg_pid, &status, 0) == dbg_pid && - (WIFEXITED(status) || WIFSIGNALED(status))) - { - /* The debug process died before it could kill us */ - raise(signum); - break; - } - } while(1); - } + /* Wait; we'll be killed when gdb is done */ + do { + int status; + if(waitpid(dbg_pid, &status, 0) == dbg_pid && + (WIFEXITED(status) || WIFSIGNALED(status))) + { + /* The debug process died before it could kill us */ + raise(signum); + break; + } + } while(1); + } } static void crash_handler(const char *logfile) { - const char *sigdesc = ""; + const char *sigdesc = ""; int i; - if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1) - { - fprintf(stderr, "!!! Failed to retrieve info from crashed process\n"); - exit(1); - } + if(fread(&crash_info, sizeof(crash_info), 1, stdin) != 1) + { + fprintf(stderr, "!!! Failed to retrieve info from crashed process\n"); + exit(1); + } - /* Get the signal description */ - for(i = 0;signals[i].name;++i) - { - if(signals[i].signum == crash_info.signum) - { - sigdesc = signals[i].name; - break; - } - } + /* Get the signal description */ + for(i = 0;signals[i].name;++i) + { + if(signals[i].signum == crash_info.signum) + { + sigdesc = signals[i].name; + break; + } + } - if(crash_info.has_siginfo) - { - switch(crash_info.signum) - { - case SIGSEGV: - for(i = 0;sigsegv_codes[i].name;++i) - { - if(sigsegv_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigsegv_codes[i].name; - break; - } - } - break; + if(crash_info.has_siginfo) + { + switch(crash_info.signum) + { + case SIGSEGV: + for(i = 0;sigsegv_codes[i].name;++i) + { + if(sigsegv_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigsegv_codes[i].name; + break; + } + } + break; - case SIGFPE: - for(i = 0;sigfpe_codes[i].name;++i) - { - if(sigfpe_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigfpe_codes[i].name; - break; - } - } - break; + case SIGFPE: + for(i = 0;sigfpe_codes[i].name;++i) + { + if(sigfpe_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigfpe_codes[i].name; + break; + } + } + break; - case SIGILL: - for(i = 0;sigill_codes[i].name;++i) - { - if(sigill_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigill_codes[i].name; - break; - } - } - break; + case SIGILL: + for(i = 0;sigill_codes[i].name;++i) + { + if(sigill_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigill_codes[i].name; + break; + } + } + break; - case SIGBUS: - for(i = 0;sigbus_codes[i].name;++i) - { - if(sigbus_codes[i].code == crash_info.siginfo.si_code) - { - sigdesc = sigbus_codes[i].name; - break; - } - } - break; - } - } - fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum); - if(crash_info.has_siginfo) - fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr); - fputc('\n', stderr); + case SIGBUS: + for(i = 0;sigbus_codes[i].name;++i) + { + if(sigbus_codes[i].code == crash_info.siginfo.si_code) + { + sigdesc = sigbus_codes[i].name; + break; + } + } + break; + } + } + fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum); + if(crash_info.has_siginfo) + fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr); + fputc('\n', stderr); - if(logfile) - { - /* Create crash log file and redirect shell output to it */ - if(freopen(logfile, "wa", stdout) != stdout) - { - fprintf(stderr, "!!! Could not create %s following signal\n", logfile); - exit(1); - } - fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid); + if(logfile) + { + /* Create crash log file and redirect shell output to it */ + if(freopen(logfile, "wa", stdout) != stdout) + { + fprintf(stderr, "!!! Could not create %s following signal\n", logfile); + exit(1); + } + fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid); - printf("*** Fatal Error ***\n" - "%s (signal %i)\n", sigdesc, crash_info.signum); - if(crash_info.has_siginfo) - printf("Address: %p\n", crash_info.siginfo.si_addr); - fputc('\n', stdout); - fflush(stdout); - } + printf("*** Fatal Error ***\n" + "%s (signal %i)\n", sigdesc, crash_info.signum); + if(crash_info.has_siginfo) + printf("Address: %p\n", crash_info.siginfo.si_addr); + fputc('\n', stdout); + fflush(stdout); + } - sys_info(); + sys_info(); - crash_info.buf[sizeof(crash_info.buf)-1] = '\0'; - printf("%s\n", crash_info.buf); - fflush(stdout); + crash_info.buf[sizeof(crash_info.buf)-1] = '\0'; + printf("%s\n", crash_info.buf); + fflush(stdout); - if(crash_info.pid > 0) - { - gdb_info(crash_info.pid); - kill(crash_info.pid, SIGKILL); - } + if(crash_info.pid > 0) + { + gdb_info(crash_info.pid); + kill(crash_info.pid, SIGKILL); + } - if(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 !"; SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); - } - exit(0); + } + exit(0); } int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, const char *logfile, int (*user_info)(char*, char*)) { - struct sigaction sa; - stack_t altss; - int retval; + struct sigaction sa; + stack_t altss; + int retval; - if(argc == 2 && strcmp(argv[1], crash_switch) == 0) - crash_handler(logfile); + if(argc == 2 && strcmp(argv[1], crash_switch) == 0) + crash_handler(logfile); - cc_user_info = user_info; + cc_user_info = user_info; - if(argv[0][0] == '/') - snprintf(argv0, sizeof(argv0), "%s", argv[0]); - else - { - getcwd(argv0, sizeof(argv0)); - retval = strlen(argv0); - snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); - } + if(argv[0][0] == '/') + snprintf(argv0, sizeof(argv0), "%s", argv[0]); + else + { + getcwd(argv0, sizeof(argv0)); + retval = strlen(argv0); + snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]); + } - /* Set an alternate signal stack so SIGSEGVs caused by stack overflows - * still run */ - altss.ss_sp = altstack; - altss.ss_flags = 0; - altss.ss_size = sizeof(altstack); - sigaltstack(&altss, NULL); + /* Set an alternate signal stack so SIGSEGVs caused by stack overflows + * still run */ + altss.ss_sp = altstack; + altss.ss_flags = 0; + altss.ss_size = sizeof(altstack); + sigaltstack(&altss, NULL); - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = crash_catcher; - sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK; - sigemptyset(&sa.sa_mask); + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = crash_catcher; + sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&sa.sa_mask); - retval = 0; - while(num_signals--) - { + retval = 0; + while(num_signals--) + { if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT && - *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) - { - *signals = 0; - retval = -1; - } - ++signals; - } - return retval; + *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) + { + *signals = 0; + retval = -1; + } + ++signals; + } + return retval; } From 634a53211c43ab9136e6744baf641d3a56494a33 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 4 Jan 2014 22:56:06 +0100 Subject: [PATCH 144/251] Make sure materials are built before trying to determine their transparency --- apps/openmw/mwrender/animation.cpp | 2 ++ apps/openmw/mwrender/npcanimation.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index faf9d979ae..a9b76093c9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -185,6 +185,7 @@ public: for(unsigned int i = 0;i < numsubs;++i) { Ogre::SubEntity* subEnt = entity->getSubEntity(i); + sh::Factory::getInstance()._ensureMaterial(subEnt->getMaterial()->getName(), "Default"); subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? mTransQueue : mSolidQueue); } } @@ -1188,6 +1189,7 @@ public: unsigned int numsubs = ent->getNumSubEntities(); for(unsigned int i = 0;i < numsubs;++i) { + sh::Factory::getInstance()._ensureMaterial(ent->getSubEntity(i)->getMaterial()->getName(), "Default"); if(ent->getSubEntity(i)->getMaterial()->isTransparent()) return true; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index dbb5a08688..75744af38c 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -750,6 +750,7 @@ void NpcAnimation::preRender(Ogre::Camera *camera) void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene) { + sh::Factory::getInstance()._ensureMaterial(ent->getSubEntity(0)->getMaterial()->getName(), "Default"); ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main); From 14b70a3ce61482b425060e5a9523f539d8f90ae1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 01:34:35 +0100 Subject: [PATCH 145/251] Implement AI related magic effects (calm, frenzy, rally, demoralize, turn undead) --- apps/opencs/model/world/columns.cpp | 4 +-- apps/openmw/mwclass/creature.cpp | 8 +++--- apps/openmw/mwclass/npc.cpp | 8 +++--- apps/openmw/mwdialogue/filter.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 26 ++++++++++++++++++- apps/openmw/mwmechanics/creaturestats.cpp | 10 +++++-- apps/openmw/mwmechanics/creaturestats.hpp | 17 +++++++----- .../mwmechanics/mechanicsmanagerimp.cpp | 16 +++++++++--- apps/openmw/mwscript/aiextensions.cpp | 17 ++++++++---- components/esm/loadcrea.hpp | 2 +- 10 files changed, 81 insertions(+), 30 deletions(-) diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 7a13137205..9c0d5b0fd5 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -263,7 +263,7 @@ namespace static const char *sCreatureTypes[] = { - "Creature", "Deadra", "Undead", "Humanoid", 0 + "Creature", "Daedra", "Undead", "Humanoid", 0 }; static const char *sWeaponTypes[] = @@ -342,4 +342,4 @@ std::vector CSMWorld::Columns::getEnums (ColumnId column) } return enums; -} \ No newline at end of file +} diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index b225441aaf..4506285ef4 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -84,10 +84,10 @@ namespace MWClass data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage); - data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); - data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight); - data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee); - data->mCreatureStats.setAiSetting (3, ref->mBase->mAiData.mAlarm); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee); + data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 08947c4c0f..8a32f58b9b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -309,10 +309,10 @@ namespace MWClass data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage); - data->mNpcStats.setAiSetting (0, ref->mBase->mAiData.mHello); - data->mNpcStats.setAiSetting (1, ref->mBase->mAiData.mFight); - data->mNpcStats.setAiSetting (2, ref->mBase->mAiData.mFlee); - data->mNpcStats.setAiSetting (3, ref->mBase->mAiData.mAlarm); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, ref->mBase->mAiData.mFight); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee); + data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 92738ec0ed..6f16a79ca9 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -279,7 +279,8 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con case SelectWrapper::Function_AiSetting: - return MWWorld::Class::get (mActor).getCreatureStats (mActor).getAiSetting (select.getArgument()); + return MWWorld::Class::get (mActor).getCreatureStats (mActor).getAiSetting ( + (MWMechanics::CreatureStats::AiSetting)select.getArgument()).getModified(); case SelectWrapper::Function_PcAttribute: diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f4cf1d4823..0a3f219396 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -172,7 +172,7 @@ namespace MWMechanics float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0]) +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); - float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(1); + float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); float disp = 100; //creatures don't have disposition, so set it to 100 by default if(ptr.getTypeName() == typeid(ESM::NPC).name()) { @@ -341,6 +341,30 @@ namespace MWMechanics creatureStats.setDynamic(i, stat); } + // AI setting modifiers + int creature = !ptr.getClass().isNpc(); + if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Humanoid) + creature = false; + // Note: the Creature variants only work on normal creatures, not on daedra or undead creatures. + if (!creature || ptr.get()->mBase->mData.mType == ESM::Creature::Creatures) + { + Stat stat = creatureStats.getAiSetting(CreatureStats::AI_Fight); + stat.setModifier(creatureStats.getMagicEffects().get(ESM::MagicEffect::FrenzyHumanoid+creature).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid+creature).mMagnitude); + creatureStats.setAiSetting(CreatureStats::AI_Fight, stat); + + stat = creatureStats.getAiSetting(CreatureStats::AI_Flee); + stat.setModifier(creatureStats.getMagicEffects().get(ESM::MagicEffect::DemoralizeHumanoid+creature).mMagnitude + - creatureStats.getMagicEffects().get(ESM::MagicEffect::RallyHumanoid+creature).mMagnitude); + creatureStats.setAiSetting(CreatureStats::AI_Flee, stat); + } + if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Undead) + { + Stat stat = creatureStats.getAiSetting(CreatureStats::AI_Flee); + stat.setModifier(creatureStats.getMagicEffects().get(ESM::MagicEffect::TurnUndead).mMagnitude); + creatureStats.setAiSetting(CreatureStats::AI_Flee, stat); + } + // Apply disintegration (reduces item health) float disintegrateWeapon = effects.get(ESM::MagicEffect::DisintegrateWeapon).mMagnitude; if (disintegrateWeapon > 0) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a21796ce68..85f6cfdbc0 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -122,7 +122,7 @@ namespace MWMechanics return mLevel; } - int CreatureStats::getAiSetting (int index) const + Stat CreatureStats::getAiSetting (AiSetting index) const { assert (index>=0 && index<4); return mAiSettings[index]; @@ -240,12 +240,18 @@ namespace MWMechanics mAttackingOrSpell = attackingOrSpell; } - void CreatureStats::setAiSetting (int index, int value) + void CreatureStats::setAiSetting (AiSetting index, Stat value) { assert (index>=0 && index<4); mAiSettings[index] = value; } + void CreatureStats::setAiSetting (AiSetting index, int base) + { + Stat stat(base); + setAiSetting(index, stat); + } + bool CreatureStats::isDead() const { return mDead; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 8a525523dd..322970e738 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -24,7 +24,7 @@ namespace MWMechanics Spells mSpells; ActiveSpells mActiveSpells; MagicEffects mMagicEffects; - int mAiSettings[4]; + Stat mAiSettings[4]; AiSequence mAiSequence; float mLevelHealthBonus; bool mDead; @@ -85,9 +85,6 @@ namespace MWMechanics int getLevel() const; - int getAiSetting (int index) const; - ///< 0: hello, 1 fight, 2 flee, 3 alarm - Spells & getSpells(); ActiveSpells & getActiveSpells(); @@ -125,8 +122,16 @@ namespace MWMechanics void setLevel(int level); - void setAiSetting (int index, int value); - ///< 0: hello, 1 fight, 2 flee, 3 alarm + enum AiSetting + { + AI_Hello, + AI_Fight, + AI_Flee, + AI_Alarm + }; + void setAiSetting (AiSetting index, Stat value); + void setAiSetting (AiSetting index, int base); + Stat getAiSetting (AiSetting index) const; const AiSequence& getAiSequence() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2e18fae63a..97764890f4 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -605,8 +605,12 @@ namespace MWMechanics { float s = int(r * fPerDieRollMult * fPerTempMult); - npcStats.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + int(std::max(iPerMinChange, s))))); - npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + int(std::min(-iPerMinChange, -s))))); + int flee = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Flee).getBase(); + int fight = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Fight).getBase(); + npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, + std::max(0, std::min(100, flee + int(std::max(iPerMinChange, s))))); + npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, + std::max(0, std::min(100, fight + int(std::min(-iPerMinChange, -s))))); } float c = -std::abs(int(r * fPerDieRollMult)); @@ -640,8 +644,12 @@ namespace MWMechanics { float s = c * fPerDieRollMult * fPerTempMult; - npcStats.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + std::min(-int(iPerMinChange), int(-s))))); - npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + std::max(int(iPerMinChange), int(s))))); + int flee = npcStats.getAiSetting (CreatureStats::AI_Flee).getBase(); + int fight = npcStats.getAiSetting (CreatureStats::AI_Fight).getBase(); + npcStats.setAiSetting (CreatureStats::AI_Flee, + std::max(0, std::min(100, flee + std::min(-int(iPerMinChange), int(-s))))); + npcStats.setAiSetting (CreatureStats::AI_Fight, + std::max(0, std::min(100, fight + std::max(int(iPerMinChange), int(s))))); } x = int(-c * fPerDieRollMult); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 1cdbaa008d..966a064c74 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -225,7 +225,8 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push(MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (mIndex)); + runtime.push(MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting ( + (MWMechanics::CreatureStats::AiSetting)mIndex).getModified()); } }; template @@ -241,8 +242,11 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (mIndex, - MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (mIndex) + value); + MWMechanics::CreatureStats::AiSetting setting + = MWMechanics::CreatureStats::AiSetting(mIndex); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (setting, + MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (setting).getBase() + value); } }; template @@ -258,8 +262,11 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (mIndex, - value); + MWMechanics::CreatureStats::AiSetting setting = (MWMechanics::CreatureStats::AiSetting)mIndex; + + MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(setting); + stat.setModified(value, 0); + ptr.getClass().getCreatureStats(ptr).setAiSetting(setting, stat); } }; diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 99c4f52257..9e48f7c1a2 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -40,7 +40,7 @@ struct Creature enum Type { Creatures = 0, - Deadra = 1, + Daedra = 1, Undead = 2, Humanoid = 3 }; From 44e96fcaaa37dc0cf133e4cd17eeda39f4329af0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 01:56:36 +0100 Subject: [PATCH 146/251] Implement Charm magic effect --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a3f219396..2cbfc020ec 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -228,7 +228,7 @@ namespace MWMechanics now += creatureStats.getActiveSpells().getMagicEffects(); - MagicEffects diff = MagicEffects::diff (creatureStats.getMagicEffects(), now); + //MagicEffects diff = MagicEffects::diff (creatureStats.getMagicEffects(), now); creatureStats.setMagicEffects(now); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 97764890f4..67f42fe0a9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -481,6 +481,8 @@ namespace MWMechanics if (playerStats.getDrawState() == MWMechanics::DrawState_Weapon) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispWeaponDrawn")->getFloat(); + x += ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Charm).mMagnitude; + int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used return effective_disposition; } From ce013315ba89d4150d8291632363a021567b12d8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 15:38:12 +0100 Subject: [PATCH 147/251] Nothing to see here, move along. Fine... it's flying cliff racers. But did you really want to know? --- apps/openmw/mwclass/creature.cpp | 8 ++++++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwworld/class.cpp | 6 ++++++ apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 8 ++++++-- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4506285ef4..98919d6f41 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -413,6 +413,14 @@ namespace MWClass return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); } + bool Creature::isFlying(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Flies; + } + int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) { if(name == "left") diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 0d8694ff81..34e19ebdc7 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -99,6 +99,8 @@ namespace MWClass isActor() const { return true; } + + virtual bool isFlying (const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index ffe81a4ac8..0119cdea5f 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -372,4 +372,10 @@ namespace MWWorld return newPtr; } + + bool Class::isFlying(const Ptr &ptr) const + { + return false; + } + } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index d737c18a22..08d769fbe0 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -306,6 +306,8 @@ namespace MWWorld return false; } + virtual bool isFlying(const MWWorld::Ptr& ptr) const; + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 621cd75eac..ba76eee88a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1605,13 +1605,17 @@ namespace MWWorld if(!ptr.getClass().isActor()) return false; + if (ptr.getClass().getCreatureStats(ptr).isDead()) + return false; + + if (ptr.getClass().isFlying(ptr)) + return true; + const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); if(stats.getMagicEffects().get(ESM::MagicEffect::Levitate).mMagnitude > 0 && isLevitationEnabled()) return true; - // TODO: Check if flying creature - const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(ptr.getRefData().getHandle()); if(!actor || !actor->getCollisionMode()) return true; From b8e93437645b16995ba6e28694e5392aa9067187 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 16:52:06 +0100 Subject: [PATCH 148/251] Extend NifOverrides to allow material overrides, useful for texture modding e.g. adding normal maps without having to edit the affected meshes --- apps/openmw/engine.cpp | 16 ++++-- components/nifogre/material.cpp | 7 ++- components/nifoverrides/nifoverrides.cpp | 72 +++++++++++++++++++----- components/nifoverrides/nifoverrides.hpp | 19 ++++++- 4 files changed, 90 insertions(+), 24 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3c2423345b..2a5ab3f079 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -309,12 +309,16 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) // load nif overrides NifOverrides::Overrides nifOverrides; - if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg")) - nifOverrides.loadTransparencyOverrides(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg"); - else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg")) - nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg"); - - settings.setBool("hardware cursors", "GUI", true); + std::string transparencyOverrides = "/transparency-overrides.cfg"; + std::string materialOverrides = "/material-overrides.cfg"; + if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + transparencyOverrides)) + nifOverrides.loadTransparencyOverrides(mCfgMgr.getLocalPath().string() + transparencyOverrides); + else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + transparencyOverrides)) + nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + transparencyOverrides); + if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + materialOverrides)) + nifOverrides.loadMaterialOverrides(mCfgMgr.getLocalPath().string() + materialOverrides); + else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + materialOverrides)) + nifOverrides.loadMaterialOverrides(mCfgMgr.getGlobalPath().string() + materialOverrides); return settingspath; } diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index 8ae86b64a8..be6ccbed64 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -157,7 +157,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, ctrls = ctrls->next; } } - needTangents = !texName[Nif::NiTexturingProperty::BumpTexture].empty(); // Alpha modifiers if(alphaprop) @@ -407,6 +406,12 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); // depth_func??? + if (!texName[0].empty()) + NifOverrides::Overrides::getMaterialOverrides(texName[0], instance); + + // Don't use texName, as it may be overridden + needTangents = !sh::retrieveValue(instance->getProperty("normalMap"), instance).get().empty(); + return name; } diff --git a/components/nifoverrides/nifoverrides.cpp b/components/nifoverrides/nifoverrides.cpp index 191b4ac2fe..972cf1b843 100644 --- a/components/nifoverrides/nifoverrides.cpp +++ b/components/nifoverrides/nifoverrides.cpp @@ -4,14 +4,51 @@ #include <../components/misc/stringops.hpp> +#include "../extern/shiny/Main/MaterialInstance.hpp" + +#include + using namespace NifOverrides; -Ogre::ConfigFile Overrides::mTransparencyOverrides = Ogre::ConfigFile(); +Overrides::TransparencyOverrideMap Overrides::mTransparencyOverrides = Overrides::TransparencyOverrideMap(); +Overrides::MaterialOverrideMap Overrides::mMaterialOverrides = Overrides::MaterialOverrideMap(); void Overrides::loadTransparencyOverrides (const std::string& file) { - mTransparencyOverrides.load(file); + Ogre::ConfigFile cf; + cf.load(file); + + Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); + while (seci.hasMoreElements()) + { + Ogre::String sectionName = seci.peekNextKey(); + mTransparencyOverrides[sectionName] = + Ogre::StringConverter::parseInt(cf.getSetting("alphaRejectValue", sectionName)); + seci.getNext(); + } +} + +void Overrides::loadMaterialOverrides(const std::string &file) +{ + Ogre::ConfigFile cf; + cf.load(file); + + Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); + while (seci.hasMoreElements()) + { + Ogre::String sectionName = seci.peekNextKey(); + + Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext(); + Ogre::ConfigFile::SettingsMultiMap::iterator i; + std::map overrides; + for (i = settings->begin(); i != settings->end(); ++i) + { + overrides[i->first] = i->second; + } + mMaterialOverrides[sectionName] = overrides; + } + } TransparencyResult Overrides::getTransparencyOverride(const std::string& texture) @@ -19,20 +56,25 @@ TransparencyResult Overrides::getTransparencyOverride(const std::string& texture TransparencyResult result; result.first = false; - std::string tex = texture; - Misc::StringUtils::toLower(tex); - - Ogre::ConfigFile::SectionIterator seci = mTransparencyOverrides.getSectionIterator(); - while (seci.hasMoreElements()) + TransparencyOverrideMap::iterator it = mTransparencyOverrides.find(Misc::StringUtils::lowerCase(texture)); + if (it != mTransparencyOverrides.end()) { - Ogre::String sectionName = seci.peekNextKey(); - if (sectionName == tex) - { - result.first = true; - result.second = Ogre::StringConverter::parseInt(mTransparencyOverrides.getSetting("alphaRejectValue", sectionName)); - break; - } - seci.getNext(); + result.first = true; + result.second = it->second; } + return result; } + +void Overrides::getMaterialOverrides(const std::string &texture, sh::MaterialInstance* material) +{ + MaterialOverrideMap::iterator it = mMaterialOverrides.find(Misc::StringUtils::lowerCase(texture)); + if (it != mMaterialOverrides.end()) + { + const std::map& overrides = it->second; + for (std::map::const_iterator it = overrides.begin(); it != overrides.end(); ++it) + { + material->setProperty(it->first, sh::makeProperty(it->second)); + } + } +} diff --git a/components/nifoverrides/nifoverrides.hpp b/components/nifoverrides/nifoverrides.hpp index ba2e4cc3c3..edff876d44 100644 --- a/components/nifoverrides/nifoverrides.hpp +++ b/components/nifoverrides/nifoverrides.hpp @@ -3,19 +3,34 @@ #include +namespace sh +{ + class MaterialInstance; +} + namespace NifOverrides { typedef std::pair TransparencyResult; - /// \brief provide overrides for some model / texture properties that bethesda has chosen poorly + /// Allows to provide overrides for some material properties in NIF files. + /// NIFs are a bit limited in that they don't allow specifying a material externally, which is + /// painful for texture modding. + /// We also use this to patch up transparency settings in certain NIFs that bethesda has chosen poorly. class Overrides { public: - static Ogre::ConfigFile mTransparencyOverrides; + typedef std::map TransparencyOverrideMap; + static TransparencyOverrideMap mTransparencyOverrides; + + typedef std::map > MaterialOverrideMap; + static MaterialOverrideMap mMaterialOverrides; + void loadTransparencyOverrides (const std::string& file); + void loadMaterialOverrides (const std::string& file); static TransparencyResult getTransparencyOverride(const std::string& texture); + static void getMaterialOverrides (const std::string& texture, sh::MaterialInstance* instance); }; } From c004fb778f69abacfc77e2f056db07a65f688fde Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 18:22:29 +0100 Subject: [PATCH 149/251] Get rid of underscore defines. They are reserved by the standard. --- apps/openmw/mwinput/inputmanagerimp.hpp | 4 ++-- apps/openmw/mwrender/activatoranimation.hpp | 4 ++-- apps/openmw/mwrender/actors.hpp | 4 ++-- apps/openmw/mwrender/animation.hpp | 4 ++-- apps/openmw/mwrender/creatureanimation.hpp | 4 ++-- apps/openmw/mwrender/debugging.hpp | 4 ++-- apps/openmw/mwrender/globalmap.hpp | 4 ++-- apps/openmw/mwrender/localmap.hpp | 4 ++-- apps/openmw/mwrender/npcanimation.hpp | 4 ++-- apps/openmw/mwrender/objects.hpp | 4 ++-- apps/openmw/mwrender/occlusionquery.hpp | 4 ++-- apps/openmw/mwrender/renderinginterface.hpp | 4 ++-- apps/openmw/mwrender/renderingmanager.hpp | 4 ++-- components/settings/settings.hpp | 4 ++-- extern/oics/ICSChannel.h | 6 +++--- extern/oics/ICSControl.h | 4 ++-- extern/oics/ICSInputControlSystem.h | 4 ++-- extern/sdl4ogre/OISCompat.h | 4 ++-- extern/sdl4ogre/cursormanager.hpp | 4 ++-- extern/sdl4ogre/sdlcursormanager.hpp | 4 ++-- extern/sdl4ogre/sdlinputwrapper.hpp | 4 ++-- libs/openengine/bullet/BtOgreExtras.h | 4 ++-- libs/openengine/bullet/BtOgreGP.h | 4 ++-- libs/openengine/bullet/BtOgrePG.h | 4 ++-- libs/openengine/bullet/BulletShapeLoader.h | 4 ++-- 25 files changed, 51 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d4693ff28c..4eaee9b690 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -1,5 +1,5 @@ -#ifndef _MWINPUT_MWINPUTMANAGERIMP_H -#define _MWINPUT_MWINPUTMANAGERIMP_H +#ifndef MWINPUT_MWINPUTMANAGERIMP_H +#define MWINPUT_MWINPUTMANAGERIMP_H #include "../mwgui/mode.hpp" diff --git a/apps/openmw/mwrender/activatoranimation.hpp b/apps/openmw/mwrender/activatoranimation.hpp index f3ea38f447..eb3e5815e7 100644 --- a/apps/openmw/mwrender/activatoranimation.hpp +++ b/apps/openmw/mwrender/activatoranimation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_ACTIVATORANIMATION_H -#define _GAME_RENDER_ACTIVATORANIMATION_H +#ifndef GAME_RENDER_ACTIVATORANIMATION_H +#define GAME_RENDER_ACTIVATORANIMATION_H #include "animation.hpp" diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index d91321843b..af71525fa7 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_ACTORS_H -#define _GAME_RENDER_ACTORS_H +#ifndef GAME_RENDER_ACTORS_H +#define GAME_RENDER_ACTORS_H #include diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 72d1c100ef..573f769c33 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_ANIMATION_H -#define _GAME_RENDER_ANIMATION_H +#ifndef GAME_RENDER_ANIMATION_H +#define GAME_RENDER_ANIMATION_H #include #include diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index 0c277d1985..a902df5d83 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_CREATUREANIMATION_H -#define _GAME_RENDER_CREATUREANIMATION_H +#ifndef GAME_RENDER_CREATUREANIMATION_H +#define GAME_RENDER_CREATUREANIMATION_H #include "animation.hpp" diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index 4a574017ce..39be34cb02 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_MWSCENE_H -#define _GAME_RENDER_MWSCENE_H +#ifndef GAME_RENDER_MWSCENE_H +#define GAME_RENDER_MWSCENE_H #include #include diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index dd3787b62b..aad9adcc42 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_GLOBALMAP_H -#define _GAME_RENDER_GLOBALMAP_H +#ifndef GAME_RENDER_GLOBALMAP_H +#define GAME_RENDER_GLOBALMAP_H #include diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 5384896407..638469d2d7 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_LOCALMAP_H -#define _GAME_RENDER_LOCALMAP_H +#ifndef GAME_RENDER_LOCALMAP_H +#define GAME_RENDER_LOCALMAP_H #include diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 8edcc04be6..6f0403b9d4 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_NPCANIMATION_H -#define _GAME_RENDER_NPCANIMATION_H +#ifndef GAME_RENDER_NPCANIMATION_H +#define GAME_RENDER_NPCANIMATION_H #include "animation.hpp" diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 8a50745032..665a1e6570 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDER_OBJECTS_H -#define _GAME_RENDER_OBJECTS_H +#ifndef GAME_RENDER_OBJECTS_H +#define GAME_RENDER_OBJECTS_H #include #include diff --git a/apps/openmw/mwrender/occlusionquery.hpp b/apps/openmw/mwrender/occlusionquery.hpp index 983361c187..6974f37b96 100644 --- a/apps/openmw/mwrender/occlusionquery.hpp +++ b/apps/openmw/mwrender/occlusionquery.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_OCCLUSION_QUERY_H -#define _GAME_OCCLUSION_QUERY_H +#ifndef GAME_OCCLUSION_QUERY_H +#define GAME_OCCLUSION_QUERY_H #include #include diff --git a/apps/openmw/mwrender/renderinginterface.hpp b/apps/openmw/mwrender/renderinginterface.hpp index 8ae2c0f8f9..02f3c804a0 100644 --- a/apps/openmw/mwrender/renderinginterface.hpp +++ b/apps/openmw/mwrender/renderinginterface.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDERING_INTERFACE_H -#define _GAME_RENDERING_INTERFACE_H +#ifndef GAME_RENDERING_INTERFACE_H +#define GAME_RENDERING_INTERFACE_H namespace MWRender { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b13e546e83..37488b157a 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -1,5 +1,5 @@ -#ifndef _GAME_RENDERING_MANAGER_H -#define _GAME_RENDERING_MANAGER_H +#ifndef GAME_RENDERING_MANAGER_H +#define GAME_RENDERING_MANAGER_H #include "sky.hpp" #include "debugging.hpp" diff --git a/components/settings/settings.hpp b/components/settings/settings.hpp index e9858eb945..b7c7d59a92 100644 --- a/components/settings/settings.hpp +++ b/components/settings/settings.hpp @@ -1,5 +1,5 @@ -#ifndef _COMPONENTS_SETTINGS_H -#define _COMPONENTS_SETTINGS_H +#ifndef COMPONENTS_SETTINGS_H +#define COMPONENTS_SETTINGS_H #include diff --git a/extern/oics/ICSChannel.h b/extern/oics/ICSChannel.h index f98f0d94d3..5ec6cd575b 100644 --- a/extern/oics/ICSChannel.h +++ b/extern/oics/ICSChannel.h @@ -24,8 +24,8 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------- */ -#ifndef _Channel_H_ -#define _Channel_H_ +#ifndef OICS_Channel_H_ +#define OICS_Channel_H_ #include "ICSPrerequisites.h" @@ -119,4 +119,4 @@ namespace ICS } -#endif \ No newline at end of file +#endif diff --git a/extern/oics/ICSControl.h b/extern/oics/ICSControl.h index 7939c86b95..ebf75a3fef 100644 --- a/extern/oics/ICSControl.h +++ b/extern/oics/ICSControl.h @@ -24,8 +24,8 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------- */ -#ifndef _Control_H_ -#define _Control_H_ +#ifndef OICS_Control_H_ +#define OICS_Control_H_ #include "ICSPrerequisites.h" diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index f42f9c0b5f..907cba5fc0 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -24,8 +24,8 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------- */ -#ifndef _InputControlSystem_H_ -#define _InputControlSystem_H_ +#ifndef OICS_InputControlSystem_H_ +#define OICS_InputControlSystem_H_ #include "ICSPrerequisites.h" diff --git a/extern/sdl4ogre/OISCompat.h b/extern/sdl4ogre/OISCompat.h index 3cffa143db..a0acc5837a 100644 --- a/extern/sdl4ogre/OISCompat.h +++ b/extern/sdl4ogre/OISCompat.h @@ -1,5 +1,5 @@ -#ifndef _OIS_SDL_COMPAT_H -#define _OIS_SDL_COMPAT_H +#ifndef OIS_SDL_COMPAT_H +#define OIS_SDL_COMPAT_H #include #include diff --git a/extern/sdl4ogre/cursormanager.hpp b/extern/sdl4ogre/cursormanager.hpp index 35ec92a706..3036b236be 100644 --- a/extern/sdl4ogre/cursormanager.hpp +++ b/extern/sdl4ogre/cursormanager.hpp @@ -1,5 +1,5 @@ -#ifndef _SDL4OGRE_CURSOR_MANAGER_H -#define _SDL4OGRE_CURSOR_MANAGER_H +#ifndef SDL4OGRE_CURSOR_MANAGER_H +#define SDL4OGRE_CURSOR_MANAGER_H #include #include diff --git a/extern/sdl4ogre/sdlcursormanager.hpp b/extern/sdl4ogre/sdlcursormanager.hpp index 7ba69f013e..7e3e59b4a5 100644 --- a/extern/sdl4ogre/sdlcursormanager.hpp +++ b/extern/sdl4ogre/sdlcursormanager.hpp @@ -1,5 +1,5 @@ -#ifndef _SDL4OGRE_CURSORMANAGER_H -#define _SDL4OGRE_CURSORMANAGER_H +#ifndef SDL4OGRE_CURSORMANAGER_H +#define SDL4OGRE_CURSORMANAGER_H #include diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp index a2b698f860..f08e3eff6b 100644 --- a/extern/sdl4ogre/sdlinputwrapper.hpp +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -1,5 +1,5 @@ -#ifndef _SDL4OGRE_SDLINPUTWRAPPER_H -#define _SDL4OGRE_SDLINPUTWRAPPER_H +#ifndef SDL4OGRE_SDLINPUTWRAPPER_H +#define SDL4OGRE_SDLINPUTWRAPPER_H #include diff --git a/libs/openengine/bullet/BtOgreExtras.h b/libs/openengine/bullet/BtOgreExtras.h index b20a3ff984..9572b8a7b7 100644 --- a/libs/openengine/bullet/BtOgreExtras.h +++ b/libs/openengine/bullet/BtOgreExtras.h @@ -13,8 +13,8 @@ * ===================================================================================== */ -#ifndef _BtOgreShapes_H_ -#define _BtOgreShapes_H_ +#ifndef BtOgreShapes_H_ +#define BtOgreShapes_H_ #include "btBulletDynamicsCommon.h" #include "OgreSimpleRenderable.h" diff --git a/libs/openengine/bullet/BtOgreGP.h b/libs/openengine/bullet/BtOgreGP.h index 4ce2f181ee..dde606a4f2 100644 --- a/libs/openengine/bullet/BtOgreGP.h +++ b/libs/openengine/bullet/BtOgreGP.h @@ -14,8 +14,8 @@ * ===================================================================================== */ -#ifndef _BtOgrePG_H_ -#define _BtOgrePG_H_ +#ifndef BtOgrePG_H_ +#define BtOgrePG_H_ #include "btBulletDynamicsCommon.h" #include "BtOgreExtras.h" diff --git a/libs/openengine/bullet/BtOgrePG.h b/libs/openengine/bullet/BtOgrePG.h index 9ff069a8f9..2e42fe1f91 100644 --- a/libs/openengine/bullet/BtOgrePG.h +++ b/libs/openengine/bullet/BtOgrePG.h @@ -14,8 +14,8 @@ * ===================================================================================== */ -#ifndef _BtOgreGP_H_ -#define _BtOgreGP_H_ +#ifndef BtOgreGP_H_ +#define BtOgreGP_H_ #include "btBulletDynamicsCommon.h" #include "OgreSceneNode.h" diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index 98cda859db..0e5c652260 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -1,5 +1,5 @@ -#ifndef _BULLET_SHAPE_LOADER_H_ -#define _BULLET_SHAPE_LOADER_H_ +#ifndef OPENMW_BULLET_SHAPE_LOADER_H_ +#define OPENMW_BULLET_SHAPE_LOADER_H_ #include #include From 45847c67ad02b3e62036cc822d159b313f05df8d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 18:38:21 +0100 Subject: [PATCH 150/251] Lock NIF cache when loading an interior cell as well. Should improve load performance. --- apps/openmw/mwworld/scene.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dab272f7c0..b3a9c9bb3f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -350,6 +350,7 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + Nif::NIFFile::CacheLock lock; MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.5); mRendering.enableTerrain(false); From b3764c504a010b1aaea7104cd24cb5e709482c78 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 19:08:12 +0100 Subject: [PATCH 151/251] Implement GetPcJumping instruction --- apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 13 +++++++++++++ components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b4ac8e154d..504a8638bf 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -370,4 +370,5 @@ op 0x200022f: Resurrect op 0x2000230: Resurrect, explicit op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit -opcodes 0x2000233-0x3ffffff unused +op 0x2000233: GetPcJumping +opcodes 0x2000234-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8d435959b1..54a8139d8a 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -58,6 +58,18 @@ namespace MWScript } }; + class OpGetPcJumping : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + runtime.push (!world->isOnGround(player) && !world->isFlying(player)); + } + }; + class OpWakeUpPc : public Interpreter::Opcode0 { public: @@ -771,6 +783,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (Compiler::Misc::opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (Compiler::Misc::opcodeGetPcSleep, new OpGetPcSleep); + interpreter.installSegment5 (Compiler::Misc::opcodeGetPcJumping, new OpGetPcJumping); interpreter.installSegment5 (Compiler::Misc::opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (Compiler::Misc::opcodePlayBink, new OpPlayBink); interpreter.installSegment5 (Compiler::Misc::opcodeGetLocked, new OpGetLocked); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 43dcf0ba46..6194be5327 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -241,6 +241,7 @@ namespace Compiler extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); + extensions.registerFunction ("getpcjumping", 'l', "", opcodeGetPcJumping); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerInstruction ("playbink", "Sl", opcodePlayBink); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 8742ba946d..46524c7cdd 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -188,6 +188,7 @@ namespace Compiler const int opcodeDontSaveObject = 0x2000153; const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; + const int opcodeGetPcJumping = 0x2000233; const int opcodeWakeUpPc = 0x20001a2; const int opcodeGetLocked = 0x20001c7; const int opcodeGetLockedExplicit = 0x20001c8; From 73f8161d1ea273e514f0fcdec89378c67b88862f Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 19:40:05 +0100 Subject: [PATCH 152/251] Fix spell deletion not resetting the selected spell correctly --- apps/openmw/mwgui/spellwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index e2817c38b8..2ca783bfc7 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -393,7 +393,7 @@ namespace MWGui MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); - if (spells.getSelectedSpell() == mSpellToDelete) + if (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == mSpellToDelete) MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); spells.remove(mSpellToDelete); From dde2cd5d5a2e4a5dc66f45e29180f163ecd5d162 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 20:53:45 +0100 Subject: [PATCH 153/251] Fix some code that still used setCount directly instead of using the ContainerStore interface. Also fix a related annoyance with the interface. --- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwgui/containeritemmodel.cpp | 5 +---- apps/openmw/mwgui/inventoryitemmodel.cpp | 5 +---- apps/openmw/mwgui/inventorywindow.cpp | 7 ++----- apps/openmw/mwmechanics/enchanting.cpp | 2 +- apps/openmw/mwworld/actiontake.cpp | 5 +---- apps/openmw/mwworld/containerstore.cpp | 26 +++++++++++++----------- apps/openmw/mwworld/containerstore.hpp | 6 +++--- apps/openmw/mwworld/inventorystore.cpp | 16 ++++----------- apps/openmw/mwworld/inventorystore.hpp | 2 +- 10 files changed, 29 insertions(+), 47 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8a32f58b9b..9d3002dfdc 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -613,7 +613,7 @@ namespace MWClass // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. - MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + MWBase::Environment::get().getDialogueManager()->say(ptr, "thief"); if(object.isEmpty()) { diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index 6b0fbd8903..bcb8440bfb 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -76,10 +76,7 @@ void ContainerItemModel::copyItem (const ItemStack& item, size_t count) const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) throw std::runtime_error("Item to copy needs to be from a different container!"); - int origCount = item.mBase.getRefData().getCount(); - item.mBase.getRefData().setCount(count); - source.getClass().getContainerStore(source).add(item.mBase, source); - item.mBase.getRefData().setCount(origCount); + source.getClass().getContainerStore(source).add(item.mBase, count, source); } void ContainerItemModel::removeItem (const ItemStack& item, size_t count) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index a16e67a7f5..d26feba88a 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -42,10 +42,7 @@ void InventoryItemModel::copyItem (const ItemStack& item, size_t count) { if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) throw std::runtime_error("Item to copy needs to be from a different container!"); - int origCount = item.mBase.getRefData().getCount(); - item.mBase.getRefData().setCount(count); - mActor.getClass().getContainerStore(mActor).add(item.mBase, mActor); - item.mBase.getRefData().setCount(origCount); + mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 70295c4c71..21da53c6df 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -364,10 +364,7 @@ namespace MWGui MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); MWWorld::ContainerStoreIterator it = invStore.begin(); - int origCount = ptr.getRefData().getCount(); - ptr.getRefData().setCount(mDragAndDrop->mDraggedCount); - it = invStore.add(ptr, mPtr); - ptr.getRefData().setCount(origCount); + it = invStore.add(ptr, mDragAndDrop->mDraggedCount, mPtr); mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); ptr = *it; @@ -521,7 +518,7 @@ namespace MWGui // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::Ptr newObject = *MWWorld::Class::get (player).getContainerStore (player).add (object, player); + MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player); // remove from world MWBase::Environment::get().getWorld()->deleteObject (object); diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 7e11acdb0c..3991454dbc 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -90,7 +90,7 @@ namespace MWMechanics // Add the new item to player inventory and remove the old one store.remove(mOldItemPtr, 1, player); - store.add(newItemPtr, player); + store.add(newItemPtr, 1, player); if(!mSelfEnchanting) payForEnchantment(); diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index d3c4aa2f63..867a046cf2 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -14,10 +14,7 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { - // insert into player's inventory - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPtr ("player", true); - - MWWorld::Class::get (player).getContainerStore (player).add (getTarget(), player); + actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 686e790a38..2217666473 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -81,7 +81,7 @@ void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) { if (ptr.getRefData().getCount() <= 1) return; - addNewStack(ptr)->getRefData().setCount(ptr.getRefData().getCount()-1); + addNewStack(ptr, ptr.getRefData().getCount()-1); remove(ptr, ptr.getRefData().getCount()-1, container); } @@ -123,12 +123,12 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count); - return add(ref.getPtr(), actorPtr); + return add(ref.getPtr(), count, actorPtr); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr) { - MWWorld::ContainerStoreIterator it = addImp(itemPtr); + MWWorld::ContainerStoreIterator it = addImp(itemPtr, count); MWWorld::Ptr item = *it; // we may have copied an item from the world, so reset a few things first @@ -165,7 +165,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr return it; } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, int count) { int type = getType(ptr); @@ -180,20 +180,20 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); + int realCount = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) { - iter->getRefData().setCount(iter->getRefData().getCount() + count); + iter->getRefData().setCount(iter->getRefData().getCount() + realCount); flagAsModified(); return iter; } } MWWorld::ManualRef ref(esmStore, "Gold_001", count); - return addNewStack(ref.getPtr()); + return addNewStack(ref.getPtr(), count); } // determine whether to stack or not @@ -202,17 +202,17 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) if (stacks(*iter, ptr)) { // stack - iter->getRefData().setCount( iter->getRefData().getCount() + ptr.getRefData().getCount() ); + iter->getRefData().setCount( iter->getRefData().getCount() + count ); flagAsModified(); return iter; } } // if we got here, this means no stacking - return addNewStack(ptr); + return addNewStack(ptr, count); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& ptr, int count) { ContainerStoreIterator it = begin(); @@ -232,6 +232,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& case Type_Weapon: weapons.mList.push_back (*ptr.get()); it = ContainerStoreIterator(this, --weapons.mList.end()); break; } + it->getRefData().setCount(count); + flagAsModified(); return it; } @@ -343,7 +345,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: else { ref.getPtr().getCellRef().mOwner = owner; - addImp (ref.getPtr()); + addImp (ref.getPtr(), count); } } catch (std::logic_error& e) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b34c710063..ed0fc8b8fb 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -51,7 +51,7 @@ namespace MWWorld MWWorld::CellRefList weapons; mutable float mCachedWeight; mutable bool mWeightUpToDate; - ContainerStoreIterator addImp (const Ptr& ptr); + ContainerStoreIterator addImp (const Ptr& ptr, int count); void addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance=0, bool topLevel=true); public: @@ -64,7 +64,7 @@ namespace MWWorld ContainerStoreIterator end(); - virtual ContainerStoreIterator add (const Ptr& itemPtr, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. @@ -91,7 +91,7 @@ namespace MWWorld ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). protected: - ContainerStoreIterator addNewStack (const Ptr& ptr); + ContainerStoreIterator addNewStack (const Ptr& ptr, int count); ///< Add the item to this container (do not try to stack it onto existing items) virtual void flagAsModified(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2944f00d46..2c0cf5feb4 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -75,9 +75,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor return *this; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr) { - const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, actorPtr); + const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr); // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") @@ -118,11 +118,7 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite // unstack item pointed to by iterator if required if (iterator!=end() && !slots_.second && iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped { - // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 - int count = iterator->getRefData().getCount(); - iterator->getRefData().setCount(count-1); - addNewStack(*iterator); - iterator->getRefData().setCount(1); + unstack(*iterator, actor); } mSlots[slot] = iterator; @@ -274,11 +270,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) // unstack item pointed to by iterator if required if (iter->getRefData().getCount() > 1) { - // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 - int count = iter->getRefData().getCount(); - iter->getRefData().setCount(count-1); - addNewStack(*iter); - iter->getRefData().setCount(1); + unstack(*iter, actor); } } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e764f64fb9..abd5f5ab32 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,7 +113,7 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); - virtual ContainerStoreIterator add (const Ptr& itemPtr, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). /// From 2f35e5a04ef828d4e99e28e0be74b175c766d13d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 5 Jan 2014 22:23:53 +0100 Subject: [PATCH 154/251] Stop merchants from autoequipping items sold to them --- apps/openmw/mwworld/containerstore.cpp | 7 +++++-- apps/openmw/mwworld/containerstore.hpp | 6 ++++-- apps/openmw/mwworld/inventorystore.cpp | 9 +++++++-- apps/openmw/mwworld/inventorystore.hpp | 4 +++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2217666473..ba8936bf70 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -123,10 +123,10 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count); - return add(ref.getPtr(), count, actorPtr); + return add(ref.getPtr(), count, actorPtr, true); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) { MWWorld::ContainerStoreIterator it = addImp(itemPtr, count); MWWorld::Ptr item = *it; @@ -140,6 +140,9 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.getCellRef().mPos.pos[1] = 0; item.getCellRef().mPos.pos[2] = 0; + if (setOwner) + item.getCellRef().mOwner = actorPtr.getCellRef().mRefID; + std::string script = MWWorld::Class::get(item).getScript(item); if(script != "") { diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index ed0fc8b8fb..83e490e374 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -64,7 +64,7 @@ namespace MWWorld ContainerStoreIterator end(); - virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. @@ -72,10 +72,12 @@ namespace MWWorld /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! /// + /// @param setOwner Set the owner of the added item to \a actorPtr? + /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. ContainerStoreIterator add(const std::string& id, int count, const Ptr& actorPtr); - ///< Utility to construct a ManualRef and call add(ptr, actorPtr) + ///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true) int remove(const std::string& itemId, int count, const Ptr& actor); ///< Remove \a count item(s) designated by \a itemId from this container. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2c0cf5feb4..422265c0e5 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -75,9 +75,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor return *this; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) { - const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr); + const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner); // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") @@ -183,6 +183,11 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) continue; } + // Only autoEquip if we are the original owner of the item. + // This stops merchants from auto equipping anything you sell to them. + if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID)) + continue; + int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); std::pair, bool> itemsSlots = diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index abd5f5ab32..067c8261e1 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,7 +113,7 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); - virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr); + virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false); ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// Auto-equip items if specific conditions are fulfilled (see the implementation). /// @@ -122,6 +122,8 @@ namespace MWWorld /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! /// + /// @param setOwner Set the owner of the added item to \a actorPtr? + /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); From 4ad43fdf92f935c33be9894f52231e13ec287102 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Jan 2014 19:01:05 +0100 Subject: [PATCH 155/251] Closes #1088: Quick&dirty fix for NIF filters not working properly with some mods --- components/nifogre/ogrenifloader.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 63e9057664..0c1fdfbee8 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -1209,20 +1209,27 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo if(isskinned) { + // Apparently both are allowed. Sigh. + // This could also mean that filters are supposed to work on the actual node + // hierarchy, rather than just trishapes, and the 'tri ' should be omitted? std::string filter = "@shape=tri "+bonename; + std::string filter2 = "@shape="+bonename; Misc::StringUtils::toLower(filter); + Misc::StringUtils::toLower(filter2); for(size_t i = 0;i < scene->mEntities.size();i++) { Ogre::Entity *entity = scene->mEntities[i]; if(entity->hasSkeleton()) { if(entity == scene->mSkelBase || - entity->getMesh()->getName().find(filter) != std::string::npos) + entity->getMesh()->getName().find(filter) != std::string::npos + || entity->getMesh()->getName().find(filter2) != std::string::npos) parentNode->attachObject(entity); } else { - if(entity->getMesh()->getName().find(filter) == std::string::npos) + if(entity->getMesh()->getName().find(filter) == std::string::npos + || entity->getMesh()->getName().find(filter2) == std::string::npos) entity->detachFromParent(); } } From 2591ff2d5a4adde5d3e2dc5b80ae1fd94a43bab5 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Mon, 6 Jan 2014 22:00:01 +0200 Subject: [PATCH 156/251] bug repairing --- apps/openmw/mwmechanics/character.cpp | 30 +++++++++++++++++---------- apps/openmw/mwrender/animation.cpp | 15 +++++++++++++- apps/openmw/mwrender/animation.hpp | 1 + 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 299c9dc996..d93ae9519c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -249,7 +249,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat else { mAnimation->disable(mCurrentJump); - mCurrentJump.clear(); + //mCurrentJump.clear(); + mCurrentJump = jump; mAnimation->play(jump, Priority_Jump, jumpgroup, true, 1.0f, "loop stop", "stop", 0.0f, 0); } @@ -678,6 +679,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mUpperBodyState = UpperCharState_StartToMinAttack; } } + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); } else @@ -708,7 +710,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } stats.setAttackStrength(complete); - + mAnimation->disable(mCurrentWeapon); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, @@ -725,7 +727,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun mUpperBodyState == UpperCharState_CastingSpell) { mUpperBodyState = UpperCharState_WeapEquiped; - //don't allow to continue playing hit animation on UpperBody after actor had attacked during it if(mHitState != CharState_None) { @@ -748,6 +749,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; + /*case UpperCharState_MinAttackToMaxAttack: + if(!mAnimation->isPlaying(mCurrentWeapon)) + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + 1e-9f, mAttackType+" min attack", mAttackType+" max attack", 0.99f, ~0ul); + break;*/ case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -797,12 +804,8 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } - if(!animPlaying) - { - mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); - } //if playing combat animation and lowerbody is not busy switch to whole body animation - if(weaptype != WeapType_None && animPlaying) + if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) { if( mMovementState != CharState_None || mJumpState != JumpState_None || @@ -810,15 +813,14 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun MWBase::Environment::get().getWorld()->isSwimming(mPtr) || cls.getStance(mPtr, MWWorld::Class::Sneak)) { - mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); } else { - mAnimation->setAccumulation(Ogre::Vector3(0.0f, 0.0f, 0.0f)); mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); } } + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() @@ -1001,7 +1003,13 @@ void CharacterController::update(float duration) else { if(!(vec.z > 0.0f)) - mJumpState = JumpState_None; + { + if(!mAnimation->isPlaying(mCurrentJump)) + { + mJumpState = JumpState_None; + mCurrentJump.clear(); + } + } vec.z = 0.0f; if(std::abs(vec.x/2.0f) > std::abs(vec.y)) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 664b0343de..3e682399e6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -562,6 +562,7 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi /* Translate the accumulation root back to compensate for the move. */ mAccumRoot->setPosition(-off); + mAccumRootPosUpd=true; } bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) @@ -851,7 +852,7 @@ void Animation::disable(const std::string &groupname) Ogre::Vector3 Animation::runAnimation(float duration) { Ogre::Vector3 movement(0.0f); - + mAccumRootPosUpd = false; AnimStateMap::iterator stateiter = mStates.begin(); while(stateiter != mStates.end()) { @@ -945,6 +946,18 @@ Ogre::Vector3 Animation::runAnimation(float duration) updateEffects(duration); + if (!mAccumRootPosUpd) + { + for(stateiter = mStates.begin();stateiter != mStates.end(); stateiter++) + { + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + { + updatePosition(stateiter->second.mTime, stateiter->second.mTime, movement); + break; + } + } + } + return movement; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index f5f79dd72b..013b494003 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -128,6 +128,7 @@ protected: NifOgre::ObjectScenePtr mObjectRoot; AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; + bool mAccumRootPosUpd; Ogre::Node *mNonAccumRoot; NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; From 68b87714bb41a779b46803f91facbc6b9d6d97cd Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Jan 2014 22:14:11 +0100 Subject: [PATCH 157/251] Addition to 2f35e5a04ef828d: companions should still auto equip --- apps/openmw/mwworld/inventorystore.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 422265c0e5..2aee74eeeb 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -79,13 +79,13 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, { const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner); - // Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves + // Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves if ((actorPtr.getRefData().getHandle() != "player") && !(MWWorld::Class::get(actorPtr).getNpcStats(actorPtr).isWerewolf()) && !actorPtr.getClass().getCreatureStats(actorPtr).isDead()) { std::string type = itemPtr.getTypeName(); - if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) + if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name()) || (type == typeid(ESM::Weapon).name())) autoEquip(actorPtr); } @@ -185,7 +185,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) // Only autoEquip if we are the original owner of the item. // This stops merchants from auto equipping anything you sell to them. - if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID)) + // ...unless this is a companion, he should always equip items given to him. + if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID) && + (actor.getClass().getScript(actor).empty() || + !actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))) continue; int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test); From 4a3d148a48f24a61e9d0849d8246b14f3b463cf1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 00:37:52 +0100 Subject: [PATCH 158/251] Fixes #1089 (skill increases) --- apps/openmw/mwmechanics/stat.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 77b3f63645..e66cf86de7 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -231,6 +231,7 @@ namespace MWMechanics { float mProgress; public: + SkillValue() : mProgress(0) {} float getProgress() const { return mProgress; } void setProgress(float progress) { mProgress = progress; } }; From 29c823b9d435ff9b5cb8c2015f0230f209ad164e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 00:51:09 +0100 Subject: [PATCH 159/251] Implement awareness check function. Use this for combat AI and GetDetected instruction. --- apps/openmw/mwbase/mechanicsmanager.hpp | 3 + apps/openmw/mwmechanics/actors.cpp | 73 +++++++++---------- .../mwmechanics/mechanicsmanagerimp.cpp | 70 ++++++++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 3 + apps/openmw/mwscript/aiextensions.cpp | 16 ++-- apps/openmw/mwworld/worldimp.cpp | 7 -- 6 files changed, 121 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 3ab234de1c..90f0caee61 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -88,6 +88,9 @@ namespace MWBase virtual int countDeaths (const std::string& id) const = 0; ///< Return the number of deaths for actors with the given ID. + /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! + virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0; + enum PersuasionType { PT_Admire, diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2cbfc020ec..4c9c73ca16 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -158,50 +158,49 @@ namespace MWMechanics calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr, duration); - if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) + // AI + if(MWBase::Environment::get().getMechanicsManager()->isAIActive()) { - // AI - if(MWBase::Environment::get().getMechanicsManager()->isAIActive()) + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + //engage combat or not? + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if(ptr != player && !creatureStats.isHostile()) { - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - //engage combat or not? - if(ptr != MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && !creatureStats.isHostile()) + ESM::Position playerpos = player.getRefData().getPosition(); + ESM::Position actorpos = ptr.getRefData().getPosition(); + float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0]) + +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) + +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); + float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); + float disp = 100; //creatures don't have disposition, so set it to 100 by default + if(ptr.getTypeName() == typeid(ESM::NPC).name()) { - ESM::Position playerpos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); - ESM::Position actorpos = ptr.getRefData().getPosition(); - float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0]) - +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) - +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); - float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); - float disp = 100; //creatures don't have disposition, so set it to 100 by default - if(ptr.getTypeName() == typeid(ESM::NPC).name()) - { - disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr); - } - bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - if( ( (fight == 100 ) - || (fight >= 95 && d <= 3000) - || (fight >= 90 && d <= 2000) - || (fight >= 80 && d <= 1000) - || (fight >= 80 && disp <= 40) - || (fight >= 70 && disp <= 35 && d <= 1000) - || (fight >= 60 && disp <= 30 && d <= 1000) - || (fight >= 50 && disp == 0) - || (fight >= 40 && disp <= 10 && d <= 500) ) - && LOS - ) - { - creatureStats.getAiSequence().stack(AiCombat("player")); - creatureStats.setHostile(true); - } + disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr); + } + bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr); + if( ( (fight == 100 ) + || (fight >= 95 && d <= 3000) + || (fight >= 90 && d <= 2000) + || (fight >= 80 && d <= 1000) + || (fight >= 80 && disp <= 40) + || (fight >= 70 && disp <= 35 && d <= 1000) + || (fight >= 60 && disp <= 30 && d <= 1000) + || (fight >= 50 && disp == 0) + || (fight >= 40 && disp <= 10 && d <= 500) ) + && LOS + ) + { + creatureStats.getAiSequence().stack(AiCombat("player")); + creatureStats.setHostile(true); } - - creatureStats.getAiSequence().execute (ptr,duration); } - // fatigue restoration - calculateRestoration(ptr, duration); + creatureStats.getAiSequence().execute (ptr,duration); } + + // fatigue restoration + calculateRestoration(ptr, duration); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 67f42fe0a9..07d41505b6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -12,6 +12,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include + #include "spellcasting.hpp" namespace MWMechanics @@ -725,4 +727,72 @@ namespace MWMechanics { return mAI; } + + bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) + { + const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); + + CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + + float invisibility = stats.getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude; + if (invisibility > 0) + return false; + + float sneakTerm = 0; + if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak)) + { + static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); + static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); + float sneak = 0; + if (ptr.getClass().isNpc()) + sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified(); + int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float bootWeight = 0; + if (ptr.getClass().isNpc()) + { + MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); + MWWorld::ContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); + if (it != inv.end()) + bootWeight = it->getClass().getWeight(*it); + } + sneakTerm = fSneakSkillMult * sneak + 0.2 * agility + 0.1 * luck + bootWeight * fSneakBootMult; + } + + static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat(); + static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat(); + + Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos); + Ogre::Vector3 pos2 (observer.getRefData().getPosition().pos); + float distTerm = fSneakDistBase + fSneakDistMult * pos1.distance(pos2); + + float chameleon = stats.getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude; + float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; + + CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); + int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); + int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; + int obsSneak = 0; + if (observer.getClass().isNpc()) + obsSneak = observer.getClass().getNpcStats(observer).getSkill(ESM::Skill::Sneak).getModified(); + + float obsTerm = obsSneak + 0.2 * obsAgility + 0.1 * obsLuck - obsBlind; + + // is ptr behind the observer? + static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat(); + static float fSneakViewMult = store.find("fSneakViewMult")->getFloat(); + float y = 0; + Ogre::Vector3 vec = pos1 - pos2; + Ogre::Radian angle = observer.getRefData().getBaseNode()->getOrientation().yAxis().angleBetween(vec); + if (angle < Ogre::Degree(90)) + y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult; + else + y = obsTerm * observerStats.getFatigueTerm() * fSneakViewMult; + + float target = x - y; + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + + return (roll >= target); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index ec03b457b2..63111f1ccb 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -98,6 +98,9 @@ namespace MWMechanics void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC + /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! + virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer); + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 966a064c74..213f138823 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -358,19 +358,21 @@ namespace MWScript }; template - class OpGetDetected : public Interpreter::Opcode1 + class OpGetDetected : public Interpreter::Opcode0 { public: - virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + virtual void execute (Interpreter::Runtime& runtime) { - + MWWorld::Ptr observer = R()(runtime); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - Interpreter::Type_Integer value = false; // TODO replace with implementation + MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); - std::cout << "AiGetDetected: " << actorID << ", " << value << std::endl; + Interpreter::Type_Integer value = + MWBase::Environment::get().getWorld()->getLOS(observer, actor) && + MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer); runtime.push (value); } @@ -432,8 +434,8 @@ namespace MWScript new OpGetAiPackageDone); interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage); interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage); - interpreter.installSegment3 (Compiler::Ai::opcodeGetDetected, new OpGetDetected); - interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); + interpreter.installSegment5 (Compiler::Ai::opcodeGetDetected, new OpGetDetected); + interpreter.installSegment5 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ba76eee88a..2d3d3d07f4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1840,13 +1840,6 @@ namespace MWWorld bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) { - // This is a placeholder! Needs to go into an NPC awareness check function (see - // https://wiki.openmw.org/index.php?title=Research:NPC_AI_Behaviour#NPC_Awareness_Check ) - if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude) - return false; - if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude > 100) - return false; - Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); float* pos1 = npc.getRefData().getPosition().pos; Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); From 887db76ed2084fb7b7d1b3e1a0832f9c20494381 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 01:20:13 +0100 Subject: [PATCH 160/251] Don't consider swimming or in-air characters as sneaking --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 07d41505b6..884294df3d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -739,7 +739,9 @@ namespace MWMechanics return false; float sneakTerm = 0; - if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak)) + if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak) + && !MWBase::Environment::get().getWorld()->isSwimming(ptr) + && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); From 5c7e39a92f0a59c108d87141b1715c741f9f98e3 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Tue, 7 Jan 2014 04:12:37 +0400 Subject: [PATCH 161/251] Implemented script commands StartCombat, StopCombat, GetTarget. Also renamed one field of AIWander class because it's not longer unknown. --- apps/esmtool/record.cpp | 4 +- apps/openmw/mwmechanics/aiactivate.cpp | 38 ++++++++--------- apps/openmw/mwmechanics/aicombat.cpp | 7 ++- apps/openmw/mwmechanics/aicombat.hpp | 4 +- apps/openmw/mwmechanics/aiescort.cpp | 2 +- apps/openmw/mwmechanics/aifollow.cpp | 34 +++++++-------- apps/openmw/mwmechanics/aipackage.hpp | 13 +++++- apps/openmw/mwmechanics/aisequence.cpp | 17 +++++++- apps/openmw/mwmechanics/aisequence.hpp | 9 +++- apps/openmw/mwmechanics/aitravel.cpp | 2 +- apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwscript/aiextensions.cpp | 59 ++++++++++++++++++++++++++ components/compiler/extensions0.cpp | 3 ++ components/compiler/opcodes.hpp | 6 +++ components/esm/aipackage.hpp | 2 +- 15 files changed, 154 insertions(+), 48 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index cc09452c91..a041bb2de1 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -13,8 +13,8 @@ void printAIPackage(ESM::AIPackage p) std::cout << " Distance: " << p.mWander.mDistance << std::endl; std::cout << " Duration: " << p.mWander.mDuration << std::endl; std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl; - if (p.mWander.mUnk != 1) - std::cout << " Unknown: " << (int)p.mWander.mUnk << std::endl; + if (p.mWander.mShouldRepeat != 1) + std::cout << " Unknown: " << (int)p.mWander.mShouldRepeat << std::endl; std::cout << " Idle: "; for (int i = 0; i != 8; i++) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index ee0dcf96e5..531ba55686 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -1,21 +1,21 @@ #include "aiactivate.hpp" -#include +#include -MWMechanics::AiActivate::AiActivate(const std::string &objectId) -: mObjectId(objectId) -{ -} -MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const -{ - return new AiActivate(*this); -} -bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) -{ - std::cout << "AiActivate completed.\n"; - return true; -} - -int MWMechanics::AiActivate::getTypeId() const -{ - return 4; -} +MWMechanics::AiActivate::AiActivate(const std::string &objectId) +: mObjectId(objectId) +{ +} +MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const +{ + return new AiActivate(*this); +} +bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) +{ + std::cout << "AiActivate completed.\n"; + return true; +} + +int MWMechanics::AiActivate::getTypeId() const +{ + return TypeIdActivate; +} diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6f643aa68c..3ce3665805 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -135,7 +135,7 @@ namespace MWMechanics int AiCombat::getTypeId() const { - return 5; + return TypeIdCombat; } unsigned int AiCombat::getPriority() const @@ -143,6 +143,11 @@ namespace MWMechanics return 1; } + const std::string &AiCombat::getTargetId() const + { + return mTargetId; + } + AiCombat *MWMechanics::AiCombat::clone() const { return new AiCombat(*this); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index fa71e261fc..82efc043b9 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -23,6 +23,8 @@ namespace MWMechanics virtual unsigned int getPriority() const; + const std::string &getTargetId() const; + private: std::string mTargetId; @@ -33,4 +35,4 @@ namespace MWMechanics }; } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5099625c06..d91e02be2b 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -179,7 +179,7 @@ namespace MWMechanics int AiEscort::getTypeId() const { - return 2; + return TypeIdEscort; } } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 73bf9259af..c5b1f1bf3c 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -1,7 +1,7 @@ #include "aifollow.hpp" -#include +#include -MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) +MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId) { } @@ -10,18 +10,18 @@ MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &ce { } -MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const -{ - return new AiFollow(*this); -} - - bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) -{ - std::cout << "AiFollow completed.\n"; - return true; -} - - int MWMechanics::AiFollow::getTypeId() const -{ - return 3; -} +MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const +{ + return new AiFollow(*this); +} + + bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) +{ + std::cout << "AiFollow completed.\n"; + return true; +} + + int MWMechanics::AiFollow::getTypeId() const +{ + return TypeIdFollow; +} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 5832198dad..74c77bf978 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -12,7 +12,16 @@ namespace MWMechanics class AiPackage { public: - + enum TypeId { + TypeIdNone = -1, + TypeIdWander = 0, + TypeIdTravel = 1, + TypeIdEscort = 2, + TypeIdFollow = 3, + TypeIdActivate = 4, + TypeIdCombat = 5 + }; + virtual ~AiPackage(); virtual AiPackage *clone() const = 0; @@ -21,7 +30,7 @@ namespace MWMechanics ///< \return Package completed? virtual int getTypeId() const = 0; - ///< 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate + ///< @see enum TypeId virtual unsigned int getPriority() const {return 0;} ///< higher number is higher priority (0 beeing the lowest) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 6d461e5f63..5d0686084b 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -56,6 +56,21 @@ int MWMechanics::AiSequence::getTypeId() const return mPackages.front()->getTypeId(); } +bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const +{ + if (getTypeId() != AiPackage::TypeIdCombat) + return false; + const AiCombat *combat = static_cast(mPackages.front()); + targetActorId = combat->getTargetId(); + return true; +} + +void MWMechanics::AiSequence::stopCombat() +{ + while (getTypeId() == AiPackage::TypeIdCombat) + mPackages.erase (mPackages.begin()); +} + bool MWMechanics::AiSequence::isPackageDone() const { return mDone; @@ -114,7 +129,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) std::vector idles; 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.mUnk); + package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat); } else if (it->mType == ESM::AI_Escort) { diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 0976ef0995..d65c316160 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -34,7 +34,14 @@ namespace MWMechanics virtual ~AiSequence(); int getTypeId() const; - ///< -1: None, 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate, 5 Combat + ///< @see enum AiPackage::TypeId + + bool getCombatTarget (std::string &targetActorId) const; + ///< Return true and assign target if combat package is currently + /// active, return false otherwise + + void stopCombat(); + ///< Removes all combat packages until first non-combat or stack empty. bool isPackageDone() const; ///< Has a package been completed during the last update? diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index f56c759967..db577a32fb 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -106,7 +106,7 @@ namespace MWMechanics int AiTravel::getTypeId() const { - return 1; + return TypeIdTravel; } } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 93c94a3f49..e1f65a5092 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -254,7 +254,7 @@ namespace MWMechanics int AiWander::getTypeId() const { - return 0; + return TypeIdWander; } void AiWander::stopWalking(const MWWorld::Ptr& actor) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 966a064c74..3c95e9ca11 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -16,6 +16,7 @@ #include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/aitravel.hpp" #include "../mwmechanics/aiwander.hpp" +#include "../mwmechanics/aicombat.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -399,6 +400,58 @@ namespace MWScript } }; + template + class OpGetTarget : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime &runtime) + { + MWWorld::Ptr actor = R()(runtime); + std::string testedTargetId = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + std::string currentTargetId; + + bool targetsAreEqual = false; + if (creatureStats.getAiSequence().getCombatTarget (currentTargetId)) + { + if (currentTargetId == testedTargetId) + targetsAreEqual = true; + } + runtime.push(int(targetsAreEqual)); + } + }; + + template + class OpStartCombat : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime &runtime) + { + MWWorld::Ptr actor = R()(runtime); + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + creatureStats.getAiSequence().stack(MWMechanics::AiCombat(actorID)); + if (actorID == "player") + creatureStats.setHostile(true); + } + }; + + template + class OpStopCombat : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr actor = R()(runtime); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + creatureStats.getAiSequence().stopCombat(); + } + }; + template class OpToggleAI : public Interpreter::Opcode0 { @@ -436,6 +489,12 @@ namespace MWScript interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight); interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight); + interpreter.installSegment5 (Compiler::Ai::opcodeGetTarget, new OpGetTarget); + interpreter.installSegment5 (Compiler::Ai::opcodeGetTargetExplicit, new OpGetTarget); + interpreter.installSegment5 (Compiler::Ai::opcodeStartCombat, new OpStartCombat); + interpreter.installSegment5 (Compiler::Ai::opcodeStartCombatExplicit, new OpStartCombat); + interpreter.installSegment5 (Compiler::Ai::opcodeStopCombat, new OpStopCombat); + interpreter.installSegment5 (Compiler::Ai::opcodeStopCombatExplicit, new OpStopCombat); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI); interpreter.installSegment5 (Compiler::Ai::opcodeToggleAIExplicit, new OpToggleAI); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 6194be5327..4ad9dfc5e9 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -61,12 +61,15 @@ namespace Compiler extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI); + extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit); + extensions.registerInstruction("stopcombat", "", opcodeStopCombat, opcodeStopCombatExplicit); extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); extensions.registerFunction ("getalarm", 'l', "", opcodeGetAlarm, opcodeGetAlarmExplicit); extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction ("getlos", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); + extensions.registerFunction("gettarget", 'l', "c", opcodeGetTarget, opcodeGetTargetExplicit); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 46524c7cdd..f84614c37e 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -53,6 +53,12 @@ namespace Compiler const int opcodeGetLineOfSightExplicit = 0x2000223; const int opcodeToggleAI = 0x2000224; const int opcodeToggleAIExplicit = 0x2000225; + const int opcodeGetTarget = 0x2000b23; + const int opcodeGetTargetExplicit = 0x2000b24; + const int opcodeStartCombat = 0x2000b25; + const int opcodeStartCombatExplicit = 0x2000b26; + const int opcodeStopCombat = 0x2000b27; + const int opcodeStopCombatExplicit = 0x2000b28; } namespace Animation diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index b06cb529a7..8a31aadf55 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -30,7 +30,7 @@ namespace ESM short mDuration; unsigned char mTimeOfDay; unsigned char mIdle[8]; - unsigned char mUnk; + unsigned char mShouldRepeat; }; struct AITravel From d536ff3cdcca8d203bdc0575962f0dd582805a77 Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Tue, 7 Jan 2014 04:58:59 +0400 Subject: [PATCH 162/251] printAIPackage: changed field name from Unknown to ShouldRepeat too. --- apps/esmtool/record.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index a041bb2de1..a5664c1c86 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -14,7 +14,7 @@ void printAIPackage(ESM::AIPackage p) std::cout << " Duration: " << p.mWander.mDuration << std::endl; std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl; if (p.mWander.mShouldRepeat != 1) - std::cout << " Unknown: " << (int)p.mWander.mShouldRepeat << std::endl; + std::cout << " Should repeat: " << (bool)p.mWander.mShouldRepeat << std::endl; std::cout << " Idle: "; for (int i = 0; i != 8; i++) From d5a0ff17fde7204c6ac1ec3021a79ccb02237b1a Mon Sep 17 00:00:00 2001 From: Sergey Shambir Date: Tue, 7 Jan 2014 05:06:20 +0400 Subject: [PATCH 163/251] MWScript: updated vmformat.txt, changed opcodes to fix sequence. Opcodes for StartCombat, StopCombat, GetTarget now follow the last previous opcode. --- apps/openmw/mwscript/docs/vmformat.txt | 8 +++++++- components/compiler/opcodes.hpp | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 504a8638bf..3dc5492bf5 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -371,4 +371,10 @@ op 0x2000230: Resurrect, explicit op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit op 0x2000233: GetPcJumping -opcodes 0x2000234-0x3ffffff unused +op 0x2000234: GetTarget +op 0x2000235: GetTargetExplicit +op 0x2000236: StartCombat +op 0x2000237: StartCombatExplicit +op 0x2000238: StopCombat +op 0x2000239: StopCombatExplicit +opcodes 0x2000239-0x3ffffff unused diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index f84614c37e..6f8f26e97b 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -53,12 +53,12 @@ namespace Compiler const int opcodeGetLineOfSightExplicit = 0x2000223; const int opcodeToggleAI = 0x2000224; const int opcodeToggleAIExplicit = 0x2000225; - const int opcodeGetTarget = 0x2000b23; - const int opcodeGetTargetExplicit = 0x2000b24; - const int opcodeStartCombat = 0x2000b25; - const int opcodeStartCombatExplicit = 0x2000b26; - const int opcodeStopCombat = 0x2000b27; - const int opcodeStopCombatExplicit = 0x2000b28; + const int opcodeGetTarget = 0x2000234; + const int opcodeGetTargetExplicit = 0x2000235; + const int opcodeStartCombat = 0x2000236; + const int opcodeStartCombatExplicit = 0x2000237; + const int opcodeStopCombat = 0x2000238; + const int opcodeStopCombatExplicit = 0x2000239; } namespace Animation From ea3ee4407fa3d6600e967f1d440984fce7d4bb73 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 02:48:44 +0100 Subject: [PATCH 164/251] oops, didn't mean to commit this --- apps/openmw/mwclass/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9d3002dfdc..8a32f58b9b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -613,7 +613,7 @@ namespace MWClass // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. - MWBase::Environment::get().getDialogueManager()->say(ptr, "thief"); + MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); if(object.isEmpty()) { From 780bf5a2cdcc0bdb642901685165f6882c4bbec8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 03:01:33 +0100 Subject: [PATCH 165/251] Implement pickpocket detection. Play a voiced dialogue entry when detected. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/container.cpp | 57 +++++++++++++++++- apps/openmw/mwgui/container.hpp | 6 ++ apps/openmw/mwmechanics/pickpocket.cpp | 67 ++++++++++++++++++++++ apps/openmw/mwmechanics/pickpocket.hpp | 30 ++++++++++ files/mygui/openmw_container_window.layout | 1 + 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 apps/openmw/mwmechanics/pickpocket.cpp create mode 100644 apps/openmw/mwmechanics/pickpocket.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8c8a1b3248..77d442eef0 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease + disease pickpocket ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b7c6e3367d..31cfd8bc94 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -6,10 +6,13 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwmechanics/pickpocket.hpp" + #include "countdialog.hpp" #include "tradewindow.hpp" #include "inventorywindow.hpp" @@ -123,6 +126,7 @@ namespace MWGui , mSelectedItem(-1) , mModel(NULL) , mSortModel(NULL) + , mPickpocketDetected(false) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); getWidget(mTakeButton, "TakeButton"); @@ -171,6 +175,9 @@ namespace MWGui void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { + if (!onTakeItem(mModel->getItem(mSelectedItem), count)) + return; + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } @@ -208,6 +215,7 @@ namespace MWGui void ContainerWindow::open(const MWWorld::Ptr& container, bool loot) { + mPickpocketDetected = false; mPtr = container; if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) @@ -230,6 +238,28 @@ namespace MWGui setTitle(MWWorld::Class::get(container).getName(container)); } + void ContainerWindow::close() + { + WindowBase::close(); + + // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) + if (!MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) + && !mPickpocketDetected // If it was already detected while taking an item, no need to check now + ) + { + MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + mPtr); + if (pickpocket.finish()) + { + // TODO: crime + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + mPickpocketDetected = true; + return; + } + } + } + void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) @@ -255,8 +285,13 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } - playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount); - mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount); + const ItemStack& item = mModel->getItem(i); + + if (!onTakeItem(item, item.mCount)) + break; + + playerModel->copyItem(item, item.mCount); + mModel->removeItem(item, item.mCount); } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); @@ -283,4 +318,22 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + bool ContainerWindow::onTakeItem(const ItemStack &item, int count) + { + if (dynamic_cast(mModel)) + { + MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + mPtr); + if (pickpocket.pick(item.mBase, count)) + { + // TODO: crime + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + mPickpocketDetected = true; + return false; + } + } + return true; + } + } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 243f77aa56..f934d8828a 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -52,10 +52,13 @@ namespace MWGui ContainerWindow(DragAndDrop* dragAndDrop); void open(const MWWorld::Ptr& container, bool loot=false); + virtual void close(); private: DragAndDrop* mDragAndDrop; + bool mPickpocketDetected; + MWGui::ItemView* mItemView; SortFilterItemModel* mSortModel; ItemModel* mModel; @@ -73,6 +76,9 @@ namespace MWGui void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); + /// @return is taking the item allowed? + bool onTakeItem(const ItemStack& item, int count); + virtual void onReferenceUnavailable(); }; } diff --git a/apps/openmw/mwmechanics/pickpocket.cpp b/apps/openmw/mwmechanics/pickpocket.cpp new file mode 100644 index 0000000000..8e8a70d885 --- /dev/null +++ b/apps/openmw/mwmechanics/pickpocket.cpp @@ -0,0 +1,67 @@ +#include "pickpocket.hpp" + +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "npcstats.hpp" + +namespace MWMechanics +{ + + Pickpocket::Pickpocket(const MWWorld::Ptr &thief, const MWWorld::Ptr &victim) + : mThief(thief) + , mVictim(victim) + { + } + + float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add) + { + NpcStats& stats = ptr.getClass().getNpcStats(ptr); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float sneak = stats.getSkill(ESM::Skill::Sneak).getModified(); + return (add + 0.2 * agility + 0.1 * luck + sneak) * stats.getFatigueTerm(); + } + + bool Pickpocket::getDetected(float valueTerm) + { + float x = getChanceModifier(mThief); + float y = getChanceModifier(mVictim, valueTerm); + + float t = 2*x - y; + + NpcStats& pcStats = mThief.getClass().getNpcStats(mThief); + float pcSneak = pcStats.getSkill(ESM::Skill::Sneak).getModified(); + int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get() + .find("iPickMinChance")->getInt(); + int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get() + .find("iPickMaxChance")->getInt(); + + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (t < pcSneak / iPickMinChance) + { + return (roll > int(pcSneak / iPickMinChance)); + } + else + { + t = std::min(float(iPickMaxChance), t); + return (roll > int(t)); + } + } + + bool Pickpocket::pick(MWWorld::Ptr item, int count) + { + float stackValue = item.getClass().getValue(item) * count; + float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get() + .find("fPickPocketMod")->getFloat(); + float valueTerm = 10 * fPickPocketMod * stackValue; + + return getDetected(valueTerm); + } + + bool Pickpocket::finish() + { + return getDetected(0.f); + } + +} diff --git a/apps/openmw/mwmechanics/pickpocket.hpp b/apps/openmw/mwmechanics/pickpocket.hpp new file mode 100644 index 0000000000..4de1e37f84 --- /dev/null +++ b/apps/openmw/mwmechanics/pickpocket.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_MECHANICS_PICKPOCKET_H +#define OPENMW_MECHANICS_PICKPOCKET_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + class Pickpocket + { + public: + Pickpocket (const MWWorld::Ptr& thief, const MWWorld::Ptr& victim); + + /// Steal some items + /// @return Was the thief detected? + bool pick (MWWorld::Ptr item, int count); + /// End the pickpocketing process + /// @return Was the thief detected? + bool finish (); + + private: + bool getDetected(float valueTerm); + float getChanceModifier(const MWWorld::Ptr& ptr, float add=0); + MWWorld::Ptr mThief; + MWWorld::Ptr mVictim; + }; + +} + +#endif diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 06cc04ebeb..87651b0f26 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -3,6 +3,7 @@ + From 9afa8e952e1d54f77c8f5506646ec1de95e51d27 Mon Sep 17 00:00:00 2001 From: Dmitriy 'Endorph' Shkurskiy Date: Tue, 7 Jan 2014 14:32:14 +0200 Subject: [PATCH 166/251] tabs -> spaces --- apps/openmw/mwworld/containerstore.cpp | 54 +++++++++++++------------- apps/openmw/mwworld/containerstore.hpp | 8 ++-- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 39fda7ab9a..f86ccfb540 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -557,7 +557,7 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *contain MWWorld::ContainerStoreIterator::ContainerStoreIterator( const ContainerStoreIterator& src ) { - copy(src); + copy(src); } void MWWorld::ContainerStoreIterator::incType() @@ -814,37 +814,37 @@ const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStor void MWWorld::ContainerStoreIterator::copy(const ContainerStoreIterator& src) { - mType = src.mType; - mMask = src.mMask; - mContainer = src.mContainer; - mPtr = src.mPtr; + mType = src.mType; + mMask = src.mMask; + mContainer = src.mContainer; + mPtr = src.mPtr; - switch (mType) - { - case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break; - case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break; - case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break; - case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break; - case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break; - case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break; - case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break; - case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break; - case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break; - case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break; - case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break; - case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break; - case -1: break; - default: assert(0); - } + switch (mType) + { + case MWWorld::ContainerStore::Type_Potion: mPotion = src.mPotion; break; + case MWWorld::ContainerStore::Type_Apparatus: mApparatus = src.mApparatus; break; + case MWWorld::ContainerStore::Type_Armor: mArmor = src.mArmor; break; + case MWWorld::ContainerStore::Type_Book: mBook = src.mBook; break; + case MWWorld::ContainerStore::Type_Clothing: mClothing = src.mClothing; break; + case MWWorld::ContainerStore::Type_Ingredient: mIngredient = src.mIngredient; break; + case MWWorld::ContainerStore::Type_Light: mLight = src.mLight; break; + case MWWorld::ContainerStore::Type_Lockpick: mLockpick = src.mLockpick; break; + case MWWorld::ContainerStore::Type_Miscellaneous: mMiscellaneous = src.mMiscellaneous; break; + case MWWorld::ContainerStore::Type_Probe: mProbe = src.mProbe; break; + case MWWorld::ContainerStore::Type_Repair: mRepair = src.mRepair; break; + case MWWorld::ContainerStore::Type_Weapon: mWeapon = src.mWeapon; break; + case -1: break; + default: assert(0); + } } MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator=( const ContainerStoreIterator& rhs ) { - if (this!=&rhs) - { - copy(rhs); - } - return *this; + if (this!=&rhs) + { + copy(rhs); + } + return *this; } bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 780d461997..a4c30edd45 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -143,10 +143,6 @@ namespace MWWorld MWWorld::CellRefList::List::iterator mRepair; MWWorld::CellRefList::List::iterator mWeapon; - public: - - ContainerStoreIterator(const ContainerStoreIterator& src); - private: ContainerStoreIterator (ContainerStore *container); @@ -187,6 +183,8 @@ namespace MWWorld public: + ContainerStoreIterator(const ContainerStoreIterator& src); + Ptr *operator->() const; Ptr operator*() const; @@ -195,7 +193,7 @@ namespace MWWorld ContainerStoreIterator operator++ (int); - ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); + ContainerStoreIterator& operator= (const ContainerStoreIterator& rhs); bool isEqual (const ContainerStoreIterator& iter) const; From 90b55c8d4b344c6bd26f0cd7a24b91b97a2bfce7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 16:37:51 +0100 Subject: [PATCH 167/251] Use Ogre's asin/acos functions which will protect against NaNs --- apps/openmw/mwmechanics/aicombat.cpp | 8 ++++---- apps/openmw/mwmechanics/pathfinding.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6f643aa68c..df45b7133e 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -12,13 +12,13 @@ #include "creaturestats.hpp" #include "npcstats.hpp" -#include "OgreMath.h" +#include namespace { - static float sgn(float a) + static float sgn(Ogre::Radian a) { - if(a > 0) + if(a.valueDegrees() > 0) return 1.0; return -1.0; } @@ -106,7 +106,7 @@ namespace MWMechanics float directionY = dest.mY - start.mY; float directionResult = sqrt(directionX * directionX + directionY * directionY); - zAngle = Ogre::Radian( acos(directionY / directionResult) * sgn(asin(directionX / directionResult)) ).valueDegrees(); + zAngle = Ogre::Radian( Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult)) ).valueDegrees(); // TODO: use movement settings instead of rotating directly MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index ff266f9ae7..18e66d2280 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -53,9 +53,9 @@ namespace return sqrt(x * x + y * y + z * z); } - static float sgn(float a) + static float sgn(Ogre::Radian a) { - if(a > 0) + if(a.valueRadians() > 0) return 1.0; return -1.0; } @@ -196,7 +196,7 @@ namespace MWMechanics float directionY = nextPoint.mY - y; float directionResult = sqrt(directionX * directionX + directionY * directionY); - return Ogre::Radian(acos(directionY / directionResult) * sgn(asin(directionX / directionResult))).valueDegrees(); + return Ogre::Radian(Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult))).valueDegrees(); } bool PathFinder::checkWaypoint(float x, float y, float z) From d97615d5d87604caeffc1ad303e0ef6ea510680d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 18:05:44 +0100 Subject: [PATCH 168/251] Support separate specular maps --- files/materials/objects.mat | 7 +++++++ files/materials/objects.shader | 25 +++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 751b512431..2281226b0c 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -8,6 +8,7 @@ material openmw_objects_base diffuseMap black.png normalMap emissiveMap + specMap darkMap use_emissive_map false use_detail_map false @@ -44,6 +45,7 @@ material openmw_objects_base emissiveMap $emissiveMap detailMap $detailMap diffuseMap $diffuseMap + specMap $specMap darkMap $darkMap env_map $env_map env_map_color $env_map_color @@ -107,6 +109,11 @@ material openmw_objects_base anim_texture2 textures\magicitem\caust.dds 32 2 colour_op add } + + texture_unit specMap + { + direct_texture $specMap + } texture_unit shadowMap0 { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 93368f1f68..ed75babdd4 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -14,11 +14,14 @@ #define NEED_DEPTH #endif +#define SPECULAR 1 + #define NORMAL_MAP @shPropertyHasValue(normalMap) #define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) #define DETAIL_MAP @shPropertyHasValue(detailMap) #define DIFFUSE_MAP @shPropertyHasValue(diffuseMap) #define DARK_MAP @shPropertyHasValue(darkMap) +#define SPEC_MAP @shPropertyHasValue(specMap) && SPECULAR #define PARALLAX @shPropertyBool(use_parallax) #define PARALLAX_SCALE 0.04 @@ -38,8 +41,6 @@ #define ENV_MAP @shPropertyBool(env_map) -#define SPECULAR 1 - #define NEED_NORMAL (!VERTEX_LIGHTING || ENV_MAP) || SPECULAR #ifdef SH_VERTEX_SHADER @@ -273,6 +274,10 @@ shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color) #endif +#if SPEC_MAP + shSampler2D(specMap) +#endif + #if ENV_MAP || SPECULAR || PARALLAX shUniform(float3, cameraPosObjSpace) @shAutoConstant(cameraPosObjSpace, camera_position_object_space) #endif @@ -511,8 +516,20 @@ float NdotL = max(dot(normal, light0Dir), 0); float3 halfVec = normalize (light0Dir + eyeDir); - float3 specular = pow(max(dot(normal, halfVec), 0), matShininess) * lightSpec0 * matSpec; - shOutputColour(0).xyz += specular * shadow * diffuse.a; + float shininess = matShininess; +#if SPEC_MAP + float4 specTex = shSample(specMap, UV.xy); + shininess *= (specTex.a); +#endif + + float3 specular = pow(max(dot(normal, halfVec), 0), shininess) * lightSpec0 * matSpec; +#if SPEC_MAP + specular *= specTex.xyz; +#else + specular *= diffuse.a; +#endif + + shOutputColour(0).xyz += specular * shadow; #endif #if FOG From c4ab2f417aec0504910c8df334c26f76dd5329e3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 18:11:19 +0100 Subject: [PATCH 169/251] Fix exception closing container window --- apps/openmw/mwgui/container.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 31cfd8bc94..b4de0aa625 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -242,9 +242,11 @@ namespace MWGui { WindowBase::close(); - // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) - if (!MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) - && !mPickpocketDetected // If it was already detected while taking an item, no need to check now + if (dynamic_cast(mModel) + // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) + && !MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) + // If it was already detected while taking an item, no need to check now + && !mPickpocketDetected ) { MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), From d2d76f4f4787a2029860e38906e138036114ca76 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 19:21:19 +0100 Subject: [PATCH 170/251] Fix disintegration bug --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4c9c73ca16..74b459e6a1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -80,7 +80,7 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) return true; } } - return true; + return false; } From 3c0080d2c17bcfd8a02e372ecb300ab942c53139 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 19:49:16 +0100 Subject: [PATCH 171/251] Implement theft detection --- apps/openmw/mwbase/mechanicsmanager.hpp | 21 ++++++ apps/openmw/mwgui/container.cpp | 21 ++++-- apps/openmw/mwgui/inventorywindow.cpp | 3 + apps/openmw/mwmechanics/actors.hpp | 12 +++- .../mwmechanics/mechanicsmanagerimp.cpp | 66 +++++++++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 12 ++++ apps/openmw/mwworld/actiontake.cpp | 4 +- 7 files changed, 129 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 90f0caee61..a0abb8e485 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -91,6 +91,27 @@ namespace MWBase /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0; + enum OffenseType + { + OT_Theft, // Taking items owned by an NPC or a faction you are not a member of + OT_Assault, // Attacking a peaceful NPC + OT_Murder, // Murdering a peaceful NPC + OT_Trespassing, // Staying in a cell you are not allowed in (where is this defined?) + OT_SleepingInOwnedBed, // Sleeping in a bed owned by an NPC or a faction you are not a member of + OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft) + }; + /** + * @brief Commit a crime. If any actors witness the crime and report it, + * reportCrime will be called automatically. + * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + */ + virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0) = 0; + virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0) = 0; + /// Utility to check if taking this item is illegal and calling commitCrime if so + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + enum PersuasionType { PT_Admire, diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b4de0aa625..aa80388f34 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -7,9 +7,11 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/pickpocket.hpp" @@ -249,11 +251,12 @@ namespace MWGui && !mPickpocketDetected ) { - MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), - mPtr); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { - // TODO: crime + MWBase::Environment::get().getMechanicsManager()->reportCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Pickpocket); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; @@ -322,19 +325,25 @@ namespace MWGui bool ContainerWindow::onTakeItem(const ItemStack &item, int count) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if (dynamic_cast(mModel)) { - MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), - mPtr); + MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.pick(item.mBase, count)) { - // TODO: crime + int value = item.mBase.getClass().getValue(item.mBase) * count; + MWBase::Environment::get().getMechanicsManager()->reportCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; return false; } } + else + { + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count); + } return true; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 21da53c6df..33814455a9 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -533,6 +534,8 @@ namespace MWGui if (i == mTradeModel->getItemCount()) throw std::runtime_error("Added item not found"); mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); + + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); } MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 83aff63e3c..7046543e6a 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -25,9 +25,6 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; - PtrControllerMap mActors; - std::map mDeathCount; void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); @@ -50,6 +47,11 @@ namespace MWMechanics Actors(); ~Actors(); + typedef std::map PtrControllerMap; + + PtrControllerMap::const_iterator begin() { return mActors.begin(); } + PtrControllerMap::const_iterator end() { return mActors.end(); } + /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) void updateMagicEffects (const MWWorld::Ptr& ptr) { adjustMagicEffects(ptr); } @@ -88,6 +90,10 @@ namespace MWMechanics void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + + private: + PtrControllerMap mActors; + }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 884294df3d..94ea660675 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -728,6 +728,72 @@ namespace MWMechanics return mAI; } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) + { + const std::string& owner = item.getCellRef().mOwner; + if (owner.empty()) + return; + const std::string& faction = item.getCellRef().mFaction; + if (faction.empty()) + return; + const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); + if (factions.find(Misc::StringUtils::lowerCase(faction)) != factions.end()) + return; + + MWWorld::Ptr victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); + + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); + } + + void MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + { + bool reported=false; + for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) + { + if (it->first != ptr && awarenessCheck(ptr, it->first)) + { + // NPCs will always curse you when they notice you steal their items, even if they don't report the crime + if (it->first == victim && type == OT_Theft) + { + MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); + } + + // Actor has witnessed a crime. Will he report it? + // (not sure, is > 0 correct?) + if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0 + // This is a bit inconsistent, but AFAIK assaulted NPCs can not report if they are alone + && (type != OT_Assault || it->first != victim) + ) + { + reported=true; + break; + } + } + } + + if (reported) + reportCrime(ptr, victim, type, arg); + } + + void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + { + // Bounty for each type of crime + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + arg = 5; + else if (type == OT_Pickpocket) + arg = 25; + else if (type == OT_Assault) + arg = 40; + else if (type == OT_Murder) + arg = 1000; + + MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); + ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + + arg); + + // TODO: make any guards in the area try to arrest the player + } + bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 63111f1ccb..198a62d841 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -101,6 +101,18 @@ namespace MWMechanics /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer); + /** + * @brief Commit a crime. If any actors witness the crime and report it, + * reportCrime will be called automatically. + * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + */ + virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); + virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); + /// Utility to check if taking this item is illegal and calling commitCrime if so + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 867a046cf2..548a949819 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "class.hpp" #include "containerstore.hpp" @@ -14,8 +15,9 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { + MWBase::Environment::get().getMechanicsManager()->itemTaken( + actor, getTarget(), getTarget().getRefData().getCount()); actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); - MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } } From 0285d18fc22bd2b16b99c2dd6af580cbeeb1a9aa Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 20:24:01 +0100 Subject: [PATCH 172/251] Respect items belonging to a faction --- apps/openmw/mwclass/activator.cpp | 4 ++++ apps/openmw/mwclass/apparatus.cpp | 1 + apps/openmw/mwclass/armor.cpp | 1 + apps/openmw/mwclass/book.cpp | 1 + apps/openmw/mwclass/clothing.cpp | 1 + apps/openmw/mwclass/container.cpp | 3 ++- apps/openmw/mwclass/creature.cpp | 4 +++- apps/openmw/mwclass/ingredient.cpp | 1 + apps/openmw/mwclass/light.cpp | 1 + apps/openmw/mwclass/lockpick.cpp | 1 + apps/openmw/mwclass/misc.cpp | 1 + apps/openmw/mwclass/npc.cpp | 4 +++- apps/openmw/mwclass/potion.cpp | 1 + apps/openmw/mwclass/probe.cpp | 1 + apps/openmw/mwclass/repair.cpp | 1 + apps/openmw/mwclass/weapon.cpp | 1 + .../mwmechanics/mechanicsmanagerimp.cpp | 21 ++++++++++++------- apps/openmw/mwworld/containerstore.cpp | 12 ++++++----- apps/openmw/mwworld/containerstore.hpp | 4 ++-- components/esm/cellref.hpp | 4 ++-- 20 files changed, 49 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 583cb08d39..8bc104d254 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -96,7 +96,11 @@ namespace MWClass std::string text; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) + { + text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); + } info.text = text; return info; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 53c62273dc..b3a1af288f 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -128,6 +128,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } info.text = text; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 0fae1b05c3..13d19d86d3 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -252,6 +252,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 1da9209706..429d912593 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -140,6 +140,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index ffa96260df..b3278d07a7 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -195,6 +195,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 55b15a7d29..d129e617d9 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -53,7 +53,7 @@ namespace MWClass ptr.get(); data->mContainerStore.fill( - ref->mBase->mInventory, ptr.getCellRef().mOwner, MWBase::Environment::get().getWorld()->getStore()); + ref->mBase->mInventory, ptr.getCellRef().mOwner, ptr.getCellRef().mFaction, MWBase::Environment::get().getWorld()->getStore()); // store ptr.getRefData().setCustomData (data.release()); @@ -216,6 +216,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 98919d6f41..864a756c52 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -95,9 +95,11 @@ namespace MWClass data->mCreatureStats.getSpells().add (*iter); // inventory - data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), + data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); + // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. + // (except for gold you gave him) data->mContainerStore.add("gold_001", ref->mBase->mData.mGold, ptr); // store diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 4296d4e1b6..c6ec3deb98 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -149,6 +149,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 6a6133cb92..cc56ec4c8f 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -187,6 +187,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index e1dc5b2e14..795b660527 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -145,6 +145,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index d211891035..e5120462e6 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -184,6 +184,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8a32f58b9b..24caed3f5f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -320,12 +320,14 @@ namespace MWClass data->mNpcStats.getSpells().add (*iter); // inventory - data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), + data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); // store ptr.getRefData().setCustomData (data.release()); + // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. + // (except for gold you gave him) getContainerStore(ptr).add("gold_001", gold, ptr); getInventoryStore(ptr).autoEquip(ptr); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index e276c58aa5..457a1c6963 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -153,6 +153,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index b54464acdc..4209c1431b 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -144,6 +144,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index ce2b4ff10c..5f2065c3cc 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -148,6 +148,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 5e93e0d81b..82935673c5 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -355,6 +355,7 @@ namespace MWClass if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->mRef.mOwner, "Owner"); + text += MWGui::ToolTips::getMiscString(ref->mRef.mFaction, "Faction"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 94ea660675..6f15becff3 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -731,16 +731,23 @@ namespace MWMechanics void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) { const std::string& owner = item.getCellRef().mOwner; - if (owner.empty()) - return; + bool isOwned = !owner.empty(); + const std::string& faction = item.getCellRef().mFaction; - if (faction.empty()) - return; - const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); - if (factions.find(Misc::StringUtils::lowerCase(faction)) != factions.end()) + bool isFactionOwned = false; + if (!faction.empty()) + { + const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); + if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) + isFactionOwned = true; + } + + if (!isOwned && !isFactionOwned) return; - MWWorld::Ptr victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); + MWWorld::Ptr victim; + if (!owner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 3d5af7bf1a..ee4080755c 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -279,19 +279,20 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor return count - toRemove; } -void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store) +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const std::string& faction, const MWWorld::ESMStore& store) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); ++iter) { std::string id = iter->mItem.toString(); - addInitialItem(id, owner, iter->mCount); + addInitialItem(id, owner, faction, iter->mCount); } flagAsModified(); } -void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, int count, unsigned char failChance, bool topLevel) +void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, + int count, unsigned char failChance, bool topLevel) { count = std::abs(count); /// \todo implement item restocking (indicated by negative count) @@ -312,7 +313,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) { for (int i=0; i Date: Tue, 7 Jan 2014 20:43:08 +0100 Subject: [PATCH 173/251] Get rid of a hack --- apps/openmw/mwrender/renderingmanager.cpp | 8 +------- apps/openmw/mwworld/cells.cpp | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a40535030d..a68c6af28b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -19,7 +19,6 @@ #include -#include #include #include @@ -407,12 +406,7 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store) { - const MWWorld::Store &lands = - MWBase::Environment::get().getWorld()->getStore().get(); - - if(store->mCell->mData.mFlags & ESM::Cell::HasWater - || ((store->mCell->isExterior()) - && !lands.search(store->mCell->getGridX(),store->mCell->getGridY()) )) // always use water, if the cell does not have land. + if(store->mCell->mData.mFlags & ESM::Cell::HasWater) { mWater->changeCell(store->mCell); mWater->setActive(true); diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 865c0d01fc..621ff3b313 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -79,7 +79,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) // Cell isn't predefined. Make one on the fly. ESM::Cell record; - record.mData.mFlags = 0; + record.mData.mFlags = ESM::Cell::HasWater; record.mData.mX = x; record.mData.mY = y; record.mWater = 0; From 728365b48dadeec68279fe445a6bb45f70491299 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 21:07:02 +0100 Subject: [PATCH 174/251] Remove an unused hook --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 3 --- apps/openmw/mwrender/characterpreview.hpp | 2 -- apps/openmw/mwrender/externalrendering.hpp | 23 ---------------------- apps/openmw/mwrender/renderingmanager.cpp | 6 ------ apps/openmw/mwrender/renderingmanager.hpp | 3 --- apps/openmw/mwworld/worldimp.cpp | 5 ----- apps/openmw/mwworld/worldimp.hpp | 2 -- 8 files changed, 1 insertion(+), 45 deletions(-) delete mode 100644 apps/openmw/mwrender/externalrendering.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 77d442eef0..eb5b71ec34 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -19,7 +19,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows - characterpreview externalrendering globalmap videoplayer ripplesimulation refraction + characterpreview globalmap videoplayer ripplesimulation refraction terrainstorage renderconst ) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3a88971149..39919012e6 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,7 +40,6 @@ namespace ESM namespace MWRender { - class ExternalRendering; class Animation; } @@ -366,8 +365,6 @@ namespace MWBase virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; - virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; - virtual int canRest() = 0; ///< check if the player is allowed to rest \n /// 0 - yes \n diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index b2dfc96795..cd30cdf466 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -6,8 +6,6 @@ #include -#include "externalrendering.hpp" - #include "../mwworld/ptr.hpp" namespace OEngine diff --git a/apps/openmw/mwrender/externalrendering.hpp b/apps/openmw/mwrender/externalrendering.hpp deleted file mode 100644 index 33c9afd875..0000000000 --- a/apps/openmw/mwrender/externalrendering.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef GAME_RENDERING_EXTERNALRENDERING_H -#define GAME_RENDERING_EXTERNALRENDERING_H - -namespace Ogre -{ - class SceneManager; -} - -namespace MWRender -{ - /// \brief Base class for out of world rendering - class ExternalRendering - { - public: - - virtual void setup (Ogre::SceneManager *sceneManager) = 0; - - virtual ~ExternalRendering() {} - }; -} - -#endif - diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a68c6af28b..d950b84141 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -39,7 +39,6 @@ #include "localmap.hpp" #include "water.hpp" #include "npcanimation.hpp" -#include "externalrendering.hpp" #include "globalmap.hpp" #include "videoplayer.hpp" #include "terrainstorage.hpp" @@ -932,11 +931,6 @@ bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, boo return mLocalMap->isPositionExplored(nX, nY, x, y, interior); } -void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rendering) -{ - rendering.setup (mRendering.getScene()); -} - Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) { Animation *anim = mActors->getAnimation(ptr); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 37488b157a..9f77f0a3c9 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -48,7 +48,6 @@ namespace MWRender class Shadows; class LocalMap; class Water; - class ExternalRendering; class GlobalMap; class VideoPlayer; class Animation; @@ -204,8 +203,6 @@ public: bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored - void setupExternalRendering (MWRender::ExternalRendering& rendering); - Animation* getAnimation(const MWWorld::Ptr &ptr); void playVideo(const std::string& name, bool allowSkipping); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2d3d3d07f4..b72433e5af 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1705,11 +1705,6 @@ namespace MWWorld mPhysics->addActor(mPlayer->getPlayer()); } - void World::setupExternalRendering (MWRender::ExternalRendering& rendering) - { - mRendering->setupExternalRendering (rendering); - } - int World::canRest () { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1aecb6fb64..92a9931571 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -456,8 +456,6 @@ namespace MWWorld virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable); - virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); - virtual int canRest(); ///< check if the player is allowed to rest \n /// 0 - yes \n From c85c2cff4ee13558ec484e1c086182cd1f5150b3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 01:24:06 +0100 Subject: [PATCH 175/251] Fix disposition changes from trades not applying properly --- apps/openmw/mwbase/dialoguemanager.hpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 14 +++++++++++++- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 4 ++-- apps/openmw/mwmechanics/creaturestats.cpp | 3 ++- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++++ 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 58731d1c78..971bc3b4e9 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -51,7 +51,7 @@ namespace MWBase virtual void persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; - virtual void applyTemporaryDispositionChange (int delta) = 0; + virtual void applyDispositionChange (int delta) = 0; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index b979865628..0eb20c50a7 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -126,6 +126,8 @@ namespace MWDialogue void DialogueManager::startDialogue (const MWWorld::Ptr& actor) { mLastTopic = ""; + mPermanentDispositionChange = 0; + mTemporaryDispositionChange = 0; mChoice = -1; mIsInChoice = false; @@ -514,9 +516,19 @@ namespace MWDialogue return mTemporaryDispositionChange; } - void DialogueManager::applyTemporaryDispositionChange(int delta) + void DialogueManager::applyDispositionChange(int delta) { + int oldTemp = mTemporaryDispositionChange; mTemporaryDispositionChange += delta; + // don't allow increasing beyond 100 or decreasing below 0 + int curDisp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); + if (curDisp + mTemporaryDispositionChange < 0) + mTemporaryDispositionChange = -curDisp; + else if (curDisp + mTemporaryDispositionChange > 100) + mTemporaryDispositionChange = 100 - curDisp; + + int diff = mTemporaryDispositionChange - oldTemp; + mPermanentDispositionChange += diff; } bool DialogueManager::checkServiceRefused() diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index c9aad10220..5baf20a0e6 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -76,7 +76,7 @@ namespace MWDialogue virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; - virtual void applyTemporaryDispositionChange (int delta); + virtual void applyDispositionChange (int delta); }; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 6000de858a..f67ea1a3fc 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -319,7 +319,7 @@ namespace MWGui messageBox("#{sNotifyMessage9}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterFailDisposition); + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition); return; } @@ -328,7 +328,7 @@ namespace MWGui } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterSuccessDisposition); + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); // make the item transfer mTradeModel->transferItems(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 85f6cfdbc0..b5b9b7156a 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -248,7 +248,8 @@ namespace MWMechanics void CreatureStats::setAiSetting (AiSetting index, int base) { - Stat stat(base); + Stat stat = getAiSetting(index); + stat.setBase(base); setAiSetting(index, stat); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 6f15becff3..89b3fef83b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -749,11 +749,15 @@ namespace MWMechanics if (!owner.empty()) victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); + // TODO: expell from faction + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } void MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { + // TODO: expell from faction + bool reported=false; for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) { From 2bb21f2f765f2513836be1473caaeb182218862d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 02:35:36 +0100 Subject: [PATCH 176/251] Don't copy NpcStats/CreatureStats unnecessarily. Fixes a bug: taunt actions would not correctly increase the target's fight rating. --- apps/openmw/mwgui/charactercreation.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.hpp | 9 ++++----- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 9 +++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 04507cfc61..fb593650d1 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -221,7 +221,7 @@ namespace MWGui { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats stats = MWWorld::Class::get(player).getCreatureStats(player); + const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); mReviewDialog->setHealth ( stats.getHealth() ); mReviewDialog->setMagicka( stats.getMagicka() ); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0a05cd22b0..5d1c4b4b6a 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -149,7 +149,7 @@ namespace MWGui // I'm making the assumption here that the # of hours rested is calculated when rest is started // TODO: the rougher logic here (calculating the hourly deltas) should really go into helper funcs elsewhere MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats stats = MWWorld::Class::get(player).getCreatureStats(player); + const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 322970e738..6e08046384 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -46,7 +46,6 @@ namespace MWMechanics bool mRecalcDynamicStats; std::map mUsedPowers; - protected: bool mIsWerewolf; AttributeValue mWerewolfAttributes[8]; @@ -124,10 +123,10 @@ namespace MWMechanics enum AiSetting { - AI_Hello, - AI_Fight, - AI_Flee, - AI_Alarm + AI_Hello = 0, + AI_Fight = 1, + AI_Flee = 2, + AI_Alarm = 3 }; void setAiSetting (AiSetting index, Stat value); void setAiSetting (AiSetting index, int base); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 89b3fef83b..0058fac6fc 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -424,7 +424,7 @@ namespace MWMechanics int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr) { - MWMechanics::NpcStats npcSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); + const MWMechanics::NpcStats& npcSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); float x = npcSkill.getBaseDisposition(); MWWorld::LiveCellRef* npc = ptr.get(); @@ -539,7 +539,7 @@ namespace MWMechanics const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); - MWMechanics::NpcStats npcStats = MWWorld::Class::get(npc).getNpcStats(npc); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(npc).getNpcStats(npc); MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); @@ -613,6 +613,8 @@ namespace MWMechanics int fight = npcStats.getAiSetting(MWMechanics::CreatureStats::AI_Fight).getBase(); npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, std::max(0, std::min(100, flee + int(std::max(iPerMinChange, s))))); + // TODO: initiate combat and quit dialogue if fight rating is too high + // or should setAiSetting handle this? npcStats.setAiSetting (MWMechanics::CreatureStats::AI_Fight, std::max(0, std::min(100, fight + int(std::min(-iPerMinChange, -s))))); } @@ -644,10 +646,9 @@ namespace MWMechanics float c = std::abs(int(target1 - roll)); - if (roll <= target1) + if (success) { float s = c * fPerDieRollMult * fPerTempMult; - int flee = npcStats.getAiSetting (CreatureStats::AI_Flee).getBase(); int fight = npcStats.getAiSetting (CreatureStats::AI_Fight).getBase(); npcStats.setAiSetting (CreatureStats::AI_Flee, From 46519062d3614f2d5259bb34bc781b22a4d4f263 Mon Sep 17 00:00:00 2001 From: mrcheko Date: Wed, 8 Jan 2014 16:05:14 +0200 Subject: [PATCH 177/251] hit recoils/knockdowns feature --- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 57 ++++++++------------------- apps/openmw/mwmechanics/character.hpp | 1 + apps/openmw/mwrender/animation.cpp | 14 ------- apps/openmw/mwrender/animation.hpp | 1 - 5 files changed, 19 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b23cb08142..4fb00a032a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -614,7 +614,7 @@ namespace MWClass // something, alert the character controller, scripts, etc. MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); - getCreatureStats(ptr).setAttacked(true); + getCreatureStats(ptr).setAttacked(true);//used in CharacterController if(object.isEmpty()) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d93ae9519c..a4b4f24717 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -158,6 +158,7 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { + //hit recoils/knockdown animations handling if(MWWorld::Class::get(mPtr).isActor()) { if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) @@ -166,19 +167,20 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if(mHitState == CharState_None) { - mHitState = CharState_Hit; if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) { - mCurrentHit = sHitList[sHitListSize-1]; //knockdown animation mHitState = CharState_KnockDown; + mCurrentHit = sHitList[sHitListSize-1]; + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } else { + mHitState = CharState_Hit; int iHit = rand() % (sHitListSize-1); mCurrentHit = sHitList[iHit]; + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) @@ -249,8 +251,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat else { mAnimation->disable(mCurrentJump); - //mCurrentJump.clear(); - mCurrentJump = jump; + mCurrentJump.clear(); mAnimation->play(jump, Priority_Jump, jumpgroup, true, 1.0f, "loop stop", "stop", 0.0f, 0); } @@ -685,7 +686,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun else { animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); - if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) + if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown) { if(mAttackType != "shoot") { @@ -710,7 +711,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun } } stats.setAttackStrength(complete); - + mAnimation->disable(mCurrentWeapon); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, @@ -718,6 +719,11 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun 1.0f-complete, 0); mUpperBodyState = UpperCharState_MaxAttackToMinHit; } + else if (mHitState == CharState_KnockDown) + { + mUpperBodyState = UpperCharState_WeapEquiped; + mAnimation->disable(mCurrentWeapon); + } } if(!animPlaying) @@ -728,7 +734,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun { mUpperBodyState = UpperCharState_WeapEquiped; //don't allow to continue playing hit animation on UpperBody after actor had attacked during it - if(mHitState != CharState_None) + if(mHitState == CharState_Hit) { mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody); //commenting out following 2 lines will give a bit different combat dynamics(slower) @@ -749,12 +755,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun stop = mAttackType+" max attack"; mUpperBodyState = UpperCharState_MinAttackToMaxAttack; break; - /*case UpperCharState_MinAttackToMaxAttack: - if(!mAnimation->isPlaying(mCurrentWeapon)) - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - 1e-9f, mAttackType+" min attack", mAttackType+" max attack", 0.99f, ~0ul); - break;*/ case UpperCharState_MaxAttackToMinHit: if(mAttackType == "shoot") { @@ -803,23 +803,6 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun weapSpeed, start, stop, 0.0f, 0); } } - - //if playing combat animation and lowerbody is not busy switch to whole body animation - if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) - { - if( mMovementState != CharState_None || - mJumpState != JumpState_None || - mHitState != CharState_None || - MWBase::Environment::get().getWorld()->isSwimming(mPtr) || - cls.getStance(mPtr, MWWorld::Class::Sneak)) - { - mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody); - } - else - { - mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All); - } - } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); @@ -1002,14 +985,8 @@ void CharacterController::update(float duration) } else { - if(!(vec.z > 0.0f)) - { - if(!mAnimation->isPlaying(mCurrentJump)) - { - mJumpState = JumpState_None; - mCurrentJump.clear(); - } - } + if(!(vec.z > 0.0f)) + mJumpState = JumpState_None; vec.z = 0.0f; if(std::abs(vec.x/2.0f) > std::abs(vec.y)) @@ -1076,7 +1053,7 @@ void CharacterController::update(float duration) rot *= duration * Ogre::Math::RadiansToDegrees(1.0f); world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); } - else + else //avoid z-rotating for knockdown world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); world->queueMovement(mPtr, vec); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index b7c29e9ef3..438f542f02 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -30,6 +30,7 @@ enum Priority { Priority_Movement, Priority_Hit, Priority_Weapon, + Priority_Knockdown, Priority_Torch, Priority_Death, diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3e682399e6..8ce751286a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -562,7 +562,6 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi /* Translate the accumulation root back to compensate for the move. */ mAccumRoot->setPosition(-off); - mAccumRootPosUpd=true; } bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) @@ -852,7 +851,6 @@ void Animation::disable(const std::string &groupname) Ogre::Vector3 Animation::runAnimation(float duration) { Ogre::Vector3 movement(0.0f); - mAccumRootPosUpd = false; AnimStateMap::iterator stateiter = mStates.begin(); while(stateiter != mStates.end()) { @@ -946,18 +944,6 @@ Ogre::Vector3 Animation::runAnimation(float duration) updateEffects(duration); - if (!mAccumRootPosUpd) - { - for(stateiter = mStates.begin();stateiter != mStates.end(); stateiter++) - { - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) - { - updatePosition(stateiter->second.mTime, stateiter->second.mTime, movement); - break; - } - } - } - return movement; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 013b494003..f5f79dd72b 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -128,7 +128,6 @@ protected: NifOgre::ObjectScenePtr mObjectRoot; AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; - bool mAccumRootPosUpd; Ogre::Node *mNonAccumRoot; NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; From b99ca92fee0fe6bd76042a4f8e3824a1db3e6ffe Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 17:18:13 +0100 Subject: [PATCH 178/251] Add missing line of sight check --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0058fac6fc..c5b5e6401a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -762,7 +762,9 @@ namespace MWMechanics bool reported=false; for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) { - if (it->first != ptr && awarenessCheck(ptr, it->first)) + if (it->first != ptr && + MWBase::Environment::get().getWorld()->getLOS(ptr, it->first) && + awarenessCheck(ptr, it->first)) { // NPCs will always curse you when they notice you steal their items, even if they don't report the crime if (it->first == victim && type == OT_Theft) From 7b33f6f2acb02df45575dbfd5215c29e672798cd Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 17:19:43 +0100 Subject: [PATCH 179/251] Detect crime of sleeping in other NPC's beds. ShowRestMenu needs to support an explicit/implicit reference for this. --- apps/openmw/mwbase/mechanicsmanager.hpp | 6 +- .../mwmechanics/mechanicsmanagerimp.cpp | 62 +++++++++++++------ .../mwmechanics/mechanicsmanagerimp.hpp | 6 +- apps/openmw/mwscript/docs/vmformat.txt | 4 +- apps/openmw/mwscript/guiextensions.cpp | 28 ++++++++- components/compiler/extensions0.cpp | 2 +- components/compiler/opcodes.hpp | 1 + 7 files changed, 85 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index a0abb8e485..24e955cdf1 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -104,13 +104,17 @@ namespace MWBase * @brief Commit a crime. If any actors witness the crime and report it, * reportCrime will be called automatically. * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + * @return was the crime reported? */ - virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + /// Attempt sleeping in a bed. If this is illegal, call commitCrime. + /// @return was it illegal, and someone saw you doing it? + virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; enum PersuasionType { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c5b5e6401a..b125b2899b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -16,6 +16,27 @@ #include "spellcasting.hpp" +namespace +{ + /// @return is \a ptr allowed to take/use \a item or is it a crime? + bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) + { + const std::string& owner = item.getCellRef().mOwner; + bool isOwned = !owner.empty(); + + const std::string& faction = item.getCellRef().mFaction; + bool isFactionOwned = false; + if (!faction.empty()) + { + const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); + if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) + isFactionOwned = true; + } + + return (!isOwned && !isFactionOwned); + } +} + namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -729,33 +750,35 @@ namespace MWMechanics return mAI; } + bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed) + { + if (isAllowedToUse(ptr, bed)) + return false; + MWWorld::Ptr victim; + if (!bed.getCellRef().mOwner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(bed.getCellRef().mOwner, true); + + if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage64}"); + return true; + } + else + return false; + } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) { - const std::string& owner = item.getCellRef().mOwner; - bool isOwned = !owner.empty(); - - const std::string& faction = item.getCellRef().mFaction; - bool isFactionOwned = false; - if (!faction.empty()) - { - const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); - if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) - isFactionOwned = true; - } - - if (!isOwned && !isFactionOwned) + if (isAllowedToUse(ptr, item)) return; - MWWorld::Ptr victim; - if (!owner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); - - // TODO: expell from faction + if (!item.getCellRef().mOwner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } - void MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { // TODO: expell from faction @@ -787,6 +810,7 @@ namespace MWMechanics if (reported) reportCrime(ptr, victim, type, arg); + return reported; } void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 198a62d841..cec08fa92d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -105,13 +105,17 @@ namespace MWMechanics * @brief Commit a crime. If any actors witness the crime and report it, * reportCrime will be called automatically. * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + * @return was the crime reported? */ - virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + /// Attempt sleeping in a bed. If this is illegal, call commitCrime. + /// @return was it illegal, and someone saw you doing it? + virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); virtual void forceStateUpdate(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 504a8638bf..a6349c4da4 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -371,4 +371,6 @@ op 0x2000230: Resurrect, explicit op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit op 0x2000233: GetPcJumping -opcodes 0x2000234-0x3ffffff unused +op 0x2000234: ShowRestMenu, explicit +opcodes 0x2000235-0x3ffffff unused + diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 6c89a0d1ce..ebba2a492a 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -11,11 +11,15 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" + #include "interpretercontext.hpp" +#include "ref.hpp" namespace MWScript { @@ -45,6 +49,27 @@ namespace MWScript } }; + template + class OpShowRestMenu : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + // FIXME: No way to tell if we have a reference before trying to get it, and it will + // cause an exception is there isn't one :( + MWWorld::Ptr bed; + try { + bed = R()(runtime); + } + catch(std::runtime_error&) { + } + + if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + bed)) + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_RestBed); + } + }; + class OpShowDialogue : public Interpreter::Opcode0 { MWGui::GuiMode mDialogue; @@ -172,7 +197,8 @@ namespace MWScript new OpEnableRest ()); interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenu, - new OpShowDialogue (MWGui::GM_RestBed)); + new OpShowRestMenu); + interpreter.installSegment5 (Compiler::Gui::opcodeShowRestMenuExplicit, new OpShowRestMenu); interpreter.installSegment5 (Compiler::Gui::opcodeGetButtonPressed, new OpGetButtonPressed); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 6194be5327..e35a32ffa4 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -198,7 +198,7 @@ namespace Compiler extensions.registerInstruction ("enablerest", "", opcodeEnableRest); extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest); - extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu); + extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu, opcodeShowRestMenuExplicit); extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 46524c7cdd..eb2240964a 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -161,6 +161,7 @@ namespace Compiler const int opcodeEnableStatsMenu = 0x2000016; const int opcodeEnableRest = 0x2000017; const int opcodeShowRestMenu = 0x2000018; + const int opcodeShowRestMenuExplicit = 0x2000234; const int opcodeGetButtonPressed = 0x2000137; const int opcodeToggleFogOfWar = 0x2000145; const int opcodeToggleFullHelp = 0x2000151; From 098f9712f19787881ad0121b7d7debb8ce839b80 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 18:39:44 +0100 Subject: [PATCH 180/251] Add getPlayerPtr() utility method. Reduces dependencies a lot. --- apps/openmw/engine.cpp | 4 +-- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwclass/armor.cpp | 1 - apps/openmw/mwclass/clothing.cpp | 1 - apps/openmw/mwclass/container.cpp | 3 +- apps/openmw/mwclass/door.cpp | 5 ++- apps/openmw/mwclass/ingredient.cpp | 3 +- apps/openmw/mwclass/potion.cpp | 5 ++- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 +- apps/openmw/mwdialogue/filter.cpp | 11 +++---- apps/openmw/mwgui/alchemywindow.cpp | 7 ++-- apps/openmw/mwgui/bookwindow.cpp | 3 +- apps/openmw/mwgui/charactercreation.cpp | 5 ++- apps/openmw/mwgui/container.cpp | 7 ++-- apps/openmw/mwgui/dialogue.cpp | 3 +- apps/openmw/mwgui/enchantingdialog.cpp | 7 ++-- apps/openmw/mwgui/hud.cpp | 9 +++-- apps/openmw/mwgui/inventorywindow.cpp | 13 ++++---- apps/openmw/mwgui/levelupdialog.cpp | 7 ++-- apps/openmw/mwgui/mapwindow.cpp | 6 ++-- apps/openmw/mwgui/merchantrepair.cpp | 5 ++- apps/openmw/mwgui/quickkeysmenu.cpp | 11 +++---- apps/openmw/mwgui/recharge.cpp | 5 ++- apps/openmw/mwgui/referenceinterface.cpp | 4 +-- apps/openmw/mwgui/repair.cpp | 3 +- apps/openmw/mwgui/scrollwindow.cpp | 3 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 5 ++- apps/openmw/mwgui/spellcreationdialog.cpp | 7 ++-- apps/openmw/mwgui/spellicons.cpp | 3 +- apps/openmw/mwgui/spellwindow.cpp | 11 +++---- apps/openmw/mwgui/statswindow.cpp | 8 ++--- apps/openmw/mwgui/tradewindow.cpp | 4 +-- apps/openmw/mwgui/trainingwindow.cpp | 5 ++- apps/openmw/mwgui/travelwindow.cpp | 5 ++- apps/openmw/mwgui/waitdialog.cpp | 7 ++-- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/actors.cpp | 5 ++- apps/openmw/mwmechanics/aicombat.cpp | 1 - apps/openmw/mwmechanics/aiescort.cpp | 3 +- apps/openmw/mwmechanics/aisequence.cpp | 3 +- apps/openmw/mwmechanics/aitravel.cpp | 3 +- apps/openmw/mwmechanics/aiwander.cpp | 1 - apps/openmw/mwmechanics/character.cpp | 1 - apps/openmw/mwmechanics/enchanting.cpp | 7 ++-- .../mwmechanics/mechanicsmanagerimp.cpp | 22 ++++++------- apps/openmw/mwmechanics/npcstats.cpp | 1 - apps/openmw/mwmechanics/repair.cpp | 5 ++- apps/openmw/mwmechanics/security.cpp | 1 - apps/openmw/mwmechanics/spellcasting.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 7 ++-- apps/openmw/mwrender/ripplesimulation.cpp | 6 ++-- apps/openmw/mwscript/cellextensions.cpp | 11 +++---- apps/openmw/mwscript/containerextensions.cpp | 5 ++- apps/openmw/mwscript/controlextensions.cpp | 5 ++- apps/openmw/mwscript/dialogueextensions.cpp | 3 +- apps/openmw/mwscript/guiextensions.cpp | 3 +- apps/openmw/mwscript/interpretercontext.cpp | 15 ++++----- apps/openmw/mwscript/miscextensions.cpp | 3 +- apps/openmw/mwscript/statsextensions.cpp | 33 +++++++++---------- .../mwscript/transformationextensions.cpp | 4 +-- apps/openmw/mwsound/soundmanagerimp.cpp | 5 ++- apps/openmw/mwworld/actionequip.cpp | 4 +-- apps/openmw/mwworld/actionread.cpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 6 ++-- apps/openmw/mwworld/scene.cpp | 10 +++--- apps/openmw/mwworld/weather.cpp | 4 +-- apps/openmw/mwworld/worldimp.cpp | 21 +++++++----- apps/openmw/mwworld/worldimp.hpp | 1 + 69 files changed, 174 insertions(+), 224 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2a5ab3f079..b59b6a2fde 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -511,13 +511,13 @@ void OMW::Engine::activate() MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); boost::shared_ptr action = - MWWorld::Class::get (ptr).activate (ptr, MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWWorld::Class::get (ptr).activate (ptr, MWBase::Environment::get().getWorld()->getPlayerPtr()); interpreterContext.activate (ptr, action); std::string script = MWWorld::Class::get (ptr).getScript (ptr); - MWBase::Environment::get().getWorld()->breakInvisibility(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWBase::Environment::get().getWorld()->breakInvisibility(MWBase::Environment::get().getWorld()->getPlayerPtr()); if (!script.empty()) { diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 39919012e6..611bd913bf 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -112,6 +112,7 @@ namespace MWBase virtual const MWWorld::Fallback *getFallback () const = 0; virtual MWWorld::Player& getPlayer() = 0; + virtual MWWorld::Ptr getPlayerPtr() = 0; virtual const MWWorld::ESMStore& getStore() const = 0; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 13d19d86d3..5e37426c9a 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -17,7 +17,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwworld/player.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index b3278d07a7..62fc26a71e 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -14,7 +14,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" -#include "../mwworld/player.hpp" #include "../mwgui/tooltips.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index d129e617d9..f89a6bce0f 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -16,7 +16,6 @@ #include "../mwworld/actionopen.hpp" #include "../mwworld/actiontrap.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwgui/tooltips.hpp" @@ -108,7 +107,7 @@ namespace MWClass const std::string lockedSound = "LockedChest"; const std::string trapActivationSound = "Disarm Trap Fail"; - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); bool needKey = ptr.getCellRef().mLockLevel>0; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 3adb4f6fc3..f99cffe04a 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" @@ -97,7 +96,7 @@ namespace MWClass if (needKey && hasKey) { - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + if(actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); ptr.getCellRef().mLockLevel = 0; // using a key disarms the trap @@ -118,7 +117,7 @@ namespace MWClass { // teleport door /// \todo remove this if clause once ActionTeleport can also support other actors - if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) + if (MWBase::Environment::get().getWorld()->getPlayerPtr()==actor) { boost::shared_ptr action(new MWWorld::ActionTeleport (ref->mRef.mDestCell, ref->mRef.mDoorDest)); diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index c6ec3deb98..faf29bc83c 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -12,7 +12,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/actioneat.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/nullaction.hpp" #include "../mwmechanics/npcstats.hpp" @@ -153,7 +152,7 @@ namespace MWClass text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 457a1c6963..d68db4e454 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -13,7 +13,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/nullaction.hpp" #include "../mwgui/tooltips.hpp" @@ -133,7 +132,7 @@ namespace MWClass info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects); // hide effects the player doesnt know about - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); int i=0; @@ -167,7 +166,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayerPtr(); // remove used potion (assume it is present in inventory) ptr.getContainerStore()->remove(ptr, 1, actor); diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 0eb20c50a7..2fce7e327a 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -30,7 +30,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "../mwgui/dialogue.hpp" @@ -493,7 +492,7 @@ namespace MWDialogue else if (curDisp + mTemporaryDispositionChange > 100) mTemporaryDispositionChange = 100 - curDisp; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); std::string text; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 6f16a79ca9..af676d643d 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -8,7 +8,6 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" @@ -93,7 +92,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const { - const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // check player faction if (!info.mPcFaction.empty()) @@ -212,7 +211,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c case SelectWrapper::Function_PcHealthPercent: { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() / MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified(); @@ -222,7 +221,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c case SelectWrapper::Function_PcDynamicStat: { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); float value = MWWorld::Class::get (player).getCreatureStats (player). getDynamic (select.getArgument()).getCurrent(); @@ -246,7 +245,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) const { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); switch (select.getFunction()) { @@ -420,7 +419,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) const { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); switch (select.getFunction()) { diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 09f692e4f5..ddbd3f120d 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -8,7 +8,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "inventoryitemmodel.hpp" @@ -143,9 +142,9 @@ namespace MWGui void AlchemyWindow::open() { - mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); - InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + InventoryItemModel* model = new InventoryItemModel(MWBase::Environment::get().getWorld()->getPlayerPtr()); mSortModel = new SortFilterItemModel(model); mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients); mItemView->setModel (mSortModel); @@ -154,7 +153,7 @@ namespace MWGui int index = 0; - mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mAlchemy.setAlchemist (MWBase::Environment::get().getWorld()->getPlayerPtr()); for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy.beginTools()); iter!=mAlchemy.endTools() && index (mApparatus.size()); ++iter, ++index) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index c28ef96ef1..98d963b229 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/player.hpp" #include "formatting.hpp" @@ -138,7 +137,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0); MWWorld::ActionTake take(mBook); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + take.execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book); } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index fb593650d1..1a32260749 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -13,7 +13,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" -#include "../mwworld/player.hpp" namespace { @@ -47,7 +46,7 @@ namespace void updatePlayerHealth() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player); creatureStats.updateHealth(); @@ -220,7 +219,7 @@ namespace MWGui mReviewDialog->setBirthSign(mPlayerBirthSignId); { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); mReviewDialog->setHealth ( stats.getHealth() ); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index aa80388f34..28f6dd4894 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -10,7 +10,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/pickpocket.hpp" @@ -223,7 +222,7 @@ namespace MWGui if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) { // we are stealing stuff - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); mModel = new PickpocketItemModel(player, new InventoryItemModel(container)); } else @@ -251,7 +250,7 @@ namespace MWGui && !mPickpocketDetected ) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { @@ -325,7 +324,7 @@ namespace MWGui bool ContainerWindow::onTakeItem(const ItemStack &item, int count) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (dynamic_cast(mModel)) { MWMechanics::Pickpocket pickpocket(player, mPtr); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 914302d84e..0460900eda 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -12,7 +12,6 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwdialogue/dialoguemanagerimp.hpp" @@ -69,7 +68,7 @@ namespace MWGui void PersuasionDialog::onPersuade(MyGUI::Widget *sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWBase::MechanicsManager::PersuasionType type; if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index d2e914d17e..cb57b51b1e 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" @@ -106,7 +105,7 @@ namespace MWGui void EnchantingDialog::startSelfEnchanting(MWWorld::Ptr soulgem) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); mEnchanting.setSelfEnchanting(true); mEnchanting.setEnchanter(player); @@ -149,7 +148,7 @@ namespace MWGui mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel); mItemSelectionDialog->setVisible(true); - mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyEnchantable); } @@ -236,7 +235,7 @@ namespace MWGui mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onSoulSelected); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onSoulCancel); mItemSelectionDialog->setVisible(true); - mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones); //MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}"); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 8ef5e59d08..7cd9e22569 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -6,7 +6,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -242,7 +241,7 @@ namespace MWGui if (world->canPlaceObject(mouseX, mouseY)) world->placeObject(object, mouseX, mouseY, mDragAndDrop->mDraggedCount); else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object, mDragAndDrop->mDraggedCount); + world->dropObjectOnGround(world->getPlayerPtr(), object, mDragAndDrop->mDraggedCount); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); @@ -320,7 +319,7 @@ namespace MWGui void HUD::onWeaponClicked(MyGUI::Widget* _sender) { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); @@ -332,7 +331,7 @@ namespace MWGui void HUD::onMagicClicked(MyGUI::Widget* _sender) { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sWerewolfRefusal}"); @@ -517,7 +516,7 @@ namespace MWGui mWeapStatus->setProgressPosition(0); MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); if (MWWorld::Class::get(player).getNpcStats(player).isWerewolf()) mWeapImage->setImageTexture("icons\\k\\tx_werewolf_hand.dds"); else diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 33814455a9..27be9339bb 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -10,7 +10,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" @@ -34,7 +33,7 @@ namespace MWGui , mTrading(false) , mLastXSize(0) , mLastYSize(0) - , mPreview(MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) + , mPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()) , mPreviewDirty(true) , mDragAndDrop(dragAndDrop) , mSelectedItem(-1) @@ -85,7 +84,7 @@ namespace MWGui void InventoryWindow::updatePlayer() { - mPtr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); mSortModel = new SortFilterItemModel(mTradeModel); mItemView->setModel(mSortModel); @@ -277,7 +276,7 @@ namespace MWGui void InventoryWindow::open() { - mPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + mPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); updateEncumbranceBar(); @@ -373,7 +372,7 @@ namespace MWGui boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); - action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); // this is necessary for books/scrolls: if they are already in the player's inventory, // the "Take" button should not be visible. @@ -433,7 +432,7 @@ namespace MWGui void InventoryWindow::updateEncumbranceBar() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); float capacity = MWWorld::Class::get(player).getCapacity(player); float encumbrance = MWWorld::Class::get(player).getEncumbrance(player); @@ -518,7 +517,7 @@ namespace MWGui // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player); // remove from world MWBase::Environment::get().getWorld()->deleteObject (object); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index e3d8f5dd7b..e55d9d8cad 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -6,7 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" @@ -59,7 +58,7 @@ namespace MWGui void LevelupDialog::setAttributeValues() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); @@ -115,7 +114,7 @@ namespace MWGui void LevelupDialog::open() { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); @@ -155,7 +154,7 @@ namespace MWGui void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 2f34df2360..ba6114262a 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -294,7 +294,7 @@ namespace MWGui std::vector markers; MWBase::World* world = MWBase::Environment::get().getWorld(); world->listDetectedReferences( - world->getPlayer().getPlayer(), + world->getPlayerPtr(), markers, MWBase::World::DetectionType(type)); if (markers.empty()) return; @@ -515,8 +515,8 @@ namespace MWGui // For interiors, position is set by WindowManager via setGlobalMapPlayerPosition if (MWBase::Environment::get().getWorld ()->isCellExterior ()) { - Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); - Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); + Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayerPtr().getRefData ().getBaseNode ()->_getDerivedPosition (); + Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayerPtr().getRefData ().getBaseNode ()->_getDerivedOrientation (); Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); float worldX, worldY; diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 4da1668209..20eb3a615a 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -36,7 +35,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) int currentY = 0; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); @@ -119,7 +118,7 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) int price = boost::lexical_cast(sender->getUserString("Price")); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getContainerStore(player).remove("gold_001", price, player); startRepair(mActor); diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index b8f52cd1fb..77127f59b8 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -2,7 +2,6 @@ #include -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" #include "../mwmechanics/spellcasting.hpp" @@ -126,7 +125,7 @@ namespace MWGui mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); } mItemSelectionDialog->setVisible(true); - mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mAssignDialog->setVisible (false); } @@ -267,7 +266,7 @@ namespace MWGui QuickKeyType type = *button->getUserData(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); if (type == Type_Item || type == Type_MagicItem) @@ -311,7 +310,7 @@ namespace MWGui boost::shared_ptr action = MWWorld::Class::get(item).use(item); - action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); // this is necessary for books/scrolls: if they are already in the player's inventory, // the "Take" button should not be visible. @@ -344,7 +343,7 @@ namespace MWGui // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); + action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); } store.setSelectedEnchantItem(it); @@ -430,7 +429,7 @@ namespace MWGui const int spellHeight = 18; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index b700360bac..683406d9eb 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -85,7 +84,7 @@ void Recharge::updateView() int currentY = 0; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) @@ -141,7 +140,7 @@ void Recharge::onItemClicked(MyGUI::Widget *sender) MWWorld::Ptr item = *sender->getUserData(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index 86a85be18e..756cd57367 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -3,8 +3,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" -#include "../mwworld/player.hpp" - namespace MWGui { ReferenceInterface::ReferenceInterface() @@ -18,7 +16,7 @@ namespace MWGui void ReferenceInterface::checkReferenceAvailable() { - MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); // check if player has changed cell, or count of the reference has become 0 if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 0bd4b0995f..d729ee7fa0 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -6,7 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -88,7 +87,7 @@ void Repair::updateRepairView() int currentY = 0; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 48931b18e7..e1970004ce 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -6,7 +6,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/player.hpp" #include "formatting.hpp" @@ -90,7 +89,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0); MWWorld::ActionTake take(mScroll); - take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + take.execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll); } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index bbd28b2de6..f285b01caa 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -103,7 +102,7 @@ namespace MWGui bool SpellBuyingWindow::playerHasSpell(const std::string &id) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::Spells& playerSpells = MWWorld::Class::get (player).getCreatureStats (player).getSpells(); for (MWMechanics::Spells::TIterator it = playerSpells.begin(); it != playerSpells.end(); ++it) { @@ -119,7 +118,7 @@ namespace MWGui if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()>=price) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index b9324fea17..3a17d50aa1 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -7,7 +7,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/spellcasting.hpp" @@ -342,7 +341,7 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getContainerStore(player).remove("gold_001", boost::lexical_cast(mPriceLabel->getCaption()), player); @@ -414,7 +413,7 @@ namespace MWGui mPriceLabel->setCaption(boost::lexical_cast(int(price))); - float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWBase::Environment::get().getWorld()->getPlayerPtr()); mSuccessChance->setCaption(boost::lexical_cast(int(chance))); } @@ -441,7 +440,7 @@ namespace MWGui { // get the list of magic effects that are known to the player - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 891206532e..0cd665a871 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -9,7 +9,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -40,7 +39,7 @@ namespace MWGui { // TODO: Tracking add/remove/expire would be better than force updating every frame - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 2ca783bfc7..21257ef9c1 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -5,7 +5,6 @@ #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actionequip.hpp" @@ -81,7 +80,7 @@ namespace MWGui // retrieve all player spells, divide them into Powers and Spells and sort them std::vector spellList; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); @@ -298,7 +297,7 @@ namespace MWGui void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWWorld::Ptr item = *_sender->getUserData(); @@ -320,7 +319,7 @@ namespace MWGui // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping MWWorld::ActionEquip action(item); - action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); + action.execute (MWBase::Environment::get().getWorld ()->getPlayerPtr()); // since we changed equipping status, update the inventory window MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); @@ -335,7 +334,7 @@ namespace MWGui void SpellWindow::onSpellSelected(MyGUI::Widget* _sender) { std::string spellId = _sender->getUserString("Spell"); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); if (MyGUI::InputManager::getInstance().isShiftPressed()) @@ -389,7 +388,7 @@ namespace MWGui void SpellWindow::onDeleteSpellAccept() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 549cf65abf..17bb24e838 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -6,8 +6,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" @@ -224,7 +224,7 @@ namespace MWGui if (!mMainWidget->getVisible()) return; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); // level progress @@ -424,7 +424,7 @@ namespace MWGui MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ESMStore &store = world->getStore(); const ESM::NPC *player = - world->getPlayer().getPlayer().get()->mBase; + world->getPlayerPtr().get()->mBase; // race tooltip const ESM::Race* playerRace = store.get().find(player->mRace); @@ -452,7 +452,7 @@ namespace MWGui if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); const std::set &expelled = PCstats.getExpelled(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index f67ea1a3fc..0111623be7 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -16,8 +16,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwworld/player.hpp" - #include "inventorywindow.hpp" #include "itemview.hpp" #include "sortfilteritemmodel.hpp" @@ -271,7 +269,7 @@ namespace MWGui return; } - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(mCurrentBalance > mCurrentMerchantOffer) { diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 04eddcb173..fc96d31bb7 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -9,7 +9,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -72,7 +71,7 @@ namespace MWGui MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator (); MyGUI::Gui::getInstance ().destroyWidgets (widgets); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::Store &gmst = @@ -115,7 +114,7 @@ namespace MWGui { int skillId = *sender->getUserData(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::ESMStore &store = diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index dd5da4522f..7f223c505f 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -9,7 +9,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -57,7 +56,7 @@ namespace MWGui } else { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); price = d/gmst.find("fTravelMult")->getFloat(); @@ -121,7 +120,7 @@ namespace MWGui int price; iss >> price; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); @@ -218,7 +217,7 @@ namespace MWGui void WaitDialog::setCanRest (bool canRest) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified()) && (stats.getHealth().getCurrent() >= stats.getHealth().getModified()) @@ -272,7 +271,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_RestBed); mWaiting = false; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &pcstats = MWWorld::Class::get(player).getNpcStats(player); // trigger levelup if possible diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8f19fb02eb..77b72fc0ac 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -355,7 +355,7 @@ namespace MWInput // if player tried to start moving, but can't (due to being overencumbered), display a notification. if (triedToMove) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mOverencumberedMessageDelay -= dt; if (MWWorld::Class::get(player).getEncumbrance(player) >= MWWorld::Class::get(player).getCapacity(player)) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 74b459e6a1..0a4adb6e2f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -11,7 +11,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actionequip.hpp" @@ -163,7 +162,7 @@ namespace MWMechanics { CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); //engage combat or not? - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(ptr != player && !creatureStats.isHostile()) { ESM::Position playerpos = player.getRefData().getPosition(); @@ -585,7 +584,7 @@ namespace MWMechanics ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration); // Play a drowning sound as necessary for the player - if(ptr == world->getPlayer().getPlayer()) + if(ptr == world->getPlayerPtr()) { MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager(); if(!sndmgr->getSoundPlaying(MWWorld::Ptr(), "drown")) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index df45b7133e..32b0063b63 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -3,7 +3,6 @@ #include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/timestamp.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5099625c06..35f9f19932 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -3,7 +3,6 @@ #include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/timestamp.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -83,7 +82,7 @@ namespace MWMechanics return true; } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position pos = actor.getRefData().getPosition(); bool cellChange = actor.getCell()->mCell->mData.mX != cellX || actor.getCell()->mCell->mData.mY != cellY; const ESM::Pathgrid *pathgrid = diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 6d461e5f63..139f544898 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -15,7 +15,6 @@ #include "npcstats.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" void MWMechanics::AiSequence::copy (const AiSequence& sequence) { @@ -63,7 +62,7 @@ bool MWMechanics::AiSequence::isPackageDone() const void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration) { - if(actor != MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr()) { if (!mPackages.empty()) { diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index f56c759967..73b38dd130 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -5,7 +5,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" namespace { @@ -38,7 +37,7 @@ namespace MWMechanics Movement &movement = actor.getClass().getMovementSettings(actor); const ESM::Cell *cell = actor.getCell()->mCell; - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->mCell->mData.mX) { int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 93c94a3f49..7df88c076b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -3,7 +3,6 @@ #include "movement.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8a73c98afa..50dad9cc00 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -33,7 +33,6 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 3991454dbc..f428bd4b06 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -1,5 +1,4 @@ #include "enchanting.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -53,7 +52,7 @@ namespace MWMechanics bool Enchanting::create() { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); ESM::Enchantment enchantment; enchantment.mData.mCharge = getGemCharge(); @@ -213,7 +212,7 @@ namespace MWMechanics return 0; const float enchantCost = getEnchantPoints(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::NpcStats &stats = MWWorld::Class::get(player).getNpcStats(player); int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); @@ -297,7 +296,7 @@ namespace MWMechanics void Enchanting::payForEnchantment() const { - const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); store.remove("gold_001", getEnchantPrice(), player); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index b125b2899b..47dd7858c3 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -41,7 +41,7 @@ namespace MWMechanics { void MechanicsManager::buildPlayer() { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); MWMechanics::NpcStats& npcStats = MWWorld::Class::get (ptr).getNpcStats (ptr); @@ -251,7 +251,7 @@ namespace MWMechanics { // Uses ingame time, but scaled to real time duration /= MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getInventoryStore(player).rechargeItems(duration); } @@ -332,7 +332,7 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager(); const ESM::NPC *player = - world->getPlayer().getPlayer().get()->mBase; + world->getPlayerPtr().get()->mBase; const ESM::Race *race = world->getStore().get().find(player->mRace); @@ -358,7 +358,7 @@ namespace MWMechanics // HACK? The player has been changed, so a new Animation object may // have been made for them. Make sure they're properly updated. - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); mActors.removeActor(ptr); mActors.addActor(ptr); } @@ -377,7 +377,7 @@ namespace MWMechanics MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mName = name; world->createRecord(player); @@ -390,7 +390,7 @@ namespace MWMechanics MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mRace = race; player.mHead = head; @@ -416,7 +416,7 @@ namespace MWMechanics MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mClass = id; world->createRecord(player); @@ -433,7 +433,7 @@ namespace MWMechanics const ESM::Class *ptr = world->createRecord(cls); ESM::NPC player = - *world->getPlayer().getPlayer().get()->mBase; + *world->getPlayerPtr().get()->mBase; player.mClass = ptr->mId; world->createRecord(player); @@ -449,7 +449,7 @@ namespace MWMechanics float x = npcSkill.getBaseDisposition(); MWWorld::LiveCellRef* npc = ptr.get(); - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::LiveCellRef* player = playerPtr.get(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); @@ -517,7 +517,7 @@ namespace MWMechanics const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(ptr).getNpcStats(ptr); - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); // I suppose the temporary disposition change _has_ to be considered here, @@ -562,7 +562,7 @@ namespace MWMechanics MWMechanics::NpcStats& npcStats = MWWorld::Class::get(npc).getNpcStats(npc); - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); float persTerm = playerStats.getAttribute(ESM::Attribute::Personality).getModified() diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 94dd971867..9b70e3347d 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -15,7 +15,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 5e8a46fd4a..1b17f83056 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -8,7 +8,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -20,7 +19,7 @@ namespace MWMechanics void Repair::repair(const MWWorld::Ptr &itemToRepair) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::LiveCellRef *ref = mTool.get(); @@ -77,7 +76,7 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) // tool used up? if (mTool.getCellRef().mCharge == 0) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); store.remove(mTool, 1, player); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index c373e83f51..0769e13df5 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -1,7 +1,6 @@ #include "security.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 52fb0805a5..21b6d0d779 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,8 +7,8 @@ #include "../mwworld/containerstore.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/actionteleport.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/animation.hpp" diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 643225515b..f7333db35c 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -10,7 +10,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -219,7 +218,7 @@ namespace MWRender // -------------------------------------------------------------------------------------------------- RaceSelectionPreview::RaceSelectionPreview() - : CharacterPreview(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + : CharacterPreview(MWBase::Environment::get().getWorld()->getPlayerPtr(), 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 6, -35), Ogre::Vector3(0,125,0)) , mRef(&mBase) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d950b84141..11d06704e2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -33,7 +33,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/ptr.hpp" -#include "../mwworld/player.hpp" #include "shadows.hpp" #include "localmap.hpp" @@ -324,7 +323,7 @@ void RenderingManager::update (float duration, bool paused) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude; mRendering.getFader()->setFactor(std::max(0.f, 1.f-(blind / 100.f))); @@ -585,7 +584,7 @@ void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) { mAmbientColor = colour; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye).mMagnitude; Ogre::ColourValue final = colour; final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f)); @@ -737,7 +736,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec else if (it->second == "max viewing distance" && it->first == "Viewing distance") { if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior()) - configureFog(*MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()); + configureFog(*MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()); } else if (it->first == "Video" && ( it->second == "resolution x" diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 47fbc8ddf6..e5db8346f7 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -10,8 +10,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/player.hpp" - namespace MWRender { @@ -154,11 +152,11 @@ void RippleSimulation::addImpulses() /// \todo it should be more efficient to render all emitters at once for (std::vector::iterator it=mEmitters.begin(); it !=mEmitters.end(); ++it) { - if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) + if (it->mPtr == MWBase::Environment::get().getWorld ()->getPlayerPtr()) { // fetch a new ptr (to handle cell change etc) // for non-player actors this is done in updateObjectCell - it->mPtr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + it->mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); } float* _currentPos = it->mPtr.getRefData().getPosition().pos; Ogre::Vector3 currentPos (_currentPos[0], _currentPos[1], _currentPos[2]); diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index f26602f7a7..e0d41cd195 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -12,7 +12,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - #include "../mwworld/player.hpp" #include "interpretercontext.hpp" @@ -89,7 +88,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { bool interior = - !MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->isExterior(); + !MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell->isExterior(); runtime.push (interior ? 1 : 0); } @@ -104,7 +103,7 @@ namespace MWScript std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell; + const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell; std::string current = cell->mName; @@ -129,7 +128,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); runtime.push (cell->mWaterLevel); } }; @@ -142,7 +141,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); @@ -160,7 +159,7 @@ namespace MWScript { Interpreter::Type_Float level = runtime[0].mFloat; - MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); if (cell->mCell->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 53f4c23c97..5234eaea39 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -21,7 +21,6 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/player.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -55,7 +54,7 @@ namespace MWScript MWWorld::Ptr itemPtr = *ptr.getClass().getContainerStore (ptr).add (item, count, ptr); // Spawn a messagebox (only for items added to player's inventory and if player is talking to someone) - if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer() ) + if (ptr == MWBase::Environment::get().getWorld ()->getPlayerPtr() ) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory std::string msgBox; @@ -133,7 +132,7 @@ namespace MWScript // Spawn a messagebox (only for items removed from player's inventory) if ((numRemoved > 0) - && (ptr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer())) + && (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory std::string msgBox; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index e46302b183..7697ab6199 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -11,7 +11,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" @@ -144,7 +143,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Run)); } }; @@ -155,7 +154,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Sneak)); } }; diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 5e797ee589..a882ae05e7 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -13,7 +13,6 @@ #include "../mwbase/journal.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" @@ -183,7 +182,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getNpcStats (ptr).isSameFaction (MWWorld::Class::get(player).getNpcStats (player))); } diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index ebba2a492a..e51bdcf690 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -11,7 +11,6 @@ #include #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -64,7 +63,7 @@ namespace MWScript catch(std::runtime_error&) { } - if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayerPtr(), bed)) MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_RestBed); } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index b8fc9ed477..aa9e320086 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -14,7 +14,6 @@ #include "../mwbase/inputmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" @@ -248,28 +247,28 @@ namespace MWScript std::string InterpreterContext::getPCName() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - ESM::NPC player = *world->getPlayer().getPlayer().get()->mBase; + ESM::NPC player = *world->getPlayerPtr().get()->mBase; return player.mName; } std::string InterpreterContext::getPCRace() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - std::string race = world->getPlayer().getPlayer().get()->mBase->mRace; + std::string race = world->getPlayerPtr().get()->mBase->mRace; return world->getStore().get().find(race)->mName; } std::string InterpreterContext::getPCClass() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - std::string class_ = world->getPlayer().getPlayer().get()->mBase->mClass; + std::string class_ = world->getPlayerPtr().get()->mBase->mClass; return world->getStore().get().find(class_)->mName; } std::string InterpreterContext::getPCRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; @@ -288,7 +287,7 @@ namespace MWScript std::string InterpreterContext::getPCNextRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; @@ -316,7 +315,7 @@ namespace MWScript int InterpreterContext::getPCBounty() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); return MWWorld::Class::get (player).getNpcStats (player).getBounty(); } @@ -387,7 +386,7 @@ namespace MWScript if (!mAction.get()) throw std::runtime_error ("activation failed, because no action to perform"); - mAction->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mAction->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); mActivationHandled = true; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 54a8139d8a..61d286ae3e 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -18,7 +18,6 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwmechanics/npcstats.hpp" @@ -65,7 +64,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); runtime.push (!world->isOnGround(player) && !world->isFlying(player)); } }; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index f053e9a97e..2ade3200eb 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -21,7 +21,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -391,7 +390,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); runtime.push (static_cast (MWWorld::Class::get (player).getNpcStats (player).getBounty())); } }; @@ -403,7 +402,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setBounty(runtime[0].mFloat); runtime.pop(); @@ -417,7 +416,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setBounty(runtime[0].mFloat + MWWorld::Class::get (player).getNpcStats (player).getBounty()); runtime.pop(); @@ -539,7 +538,7 @@ namespace MWScript Misc::StringUtils::toLower(factionID); if(factionID != "") { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) == MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = 0; @@ -568,7 +567,7 @@ namespace MWScript Misc::StringUtils::toLower(factionID); if(factionID != "") { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) == MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = 0; @@ -601,7 +600,7 @@ namespace MWScript Misc::StringUtils::toLower(factionID); if(factionID != "") { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) != MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] -1; @@ -637,7 +636,7 @@ namespace MWScript } } Misc::StringUtils::toLower(factionID); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) != MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) @@ -740,7 +739,7 @@ namespace MWScript Misc::StringUtils::toLower (factionId); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); runtime.push ( MWWorld::Class::get (player).getNpcStats (player).getFactionReputation (factionId)); } @@ -776,7 +775,7 @@ namespace MWScript Misc::StringUtils::toLower (factionId); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, value); } }; @@ -811,7 +810,7 @@ namespace MWScript Misc::StringUtils::toLower (factionId); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, MWWorld::Class::get (player).getNpcStats (player).getFactionReputation (factionId)+ value); @@ -870,7 +869,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (MWWorld::Class::get(ptr).getNpcStats (ptr).getWerewolfKills ()); } @@ -903,7 +902,7 @@ namespace MWScript } } Misc::StringUtils::toLower(factionID); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); @@ -949,7 +948,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); @@ -985,7 +984,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); @@ -1011,7 +1010,7 @@ namespace MWScript { factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // no-op when executed on the player if (ptr == player) @@ -1038,7 +1037,7 @@ namespace MWScript { factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // no-op when executed on the player if (ptr == player) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index e441809778..81aff9958a 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -16,8 +16,8 @@ #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" +#include "../mwworld/player.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -482,7 +482,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr actor = pc - ? MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + ? MWBase::Environment::get().getWorld()->getPlayerPtr() : R()(runtime); std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 2e52739ac5..28a3aae371 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -8,7 +8,6 @@ #include "../mwbase/world.hpp" #include "../mwworld/esmstore.hpp" -#include "../mwworld/player.hpp" #include "sound_output.hpp" #include "sound_decoder.hpp" @@ -479,7 +478,7 @@ namespace MWSound static std::string regionName = ""; static float sTimePassed = 0.0; MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Ptr player = world->getPlayer().getPlayer(); + const MWWorld::Ptr player = world->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->mCell; sTimePassed += duration; @@ -547,7 +546,7 @@ namespace MWSound startRandomTitle(); MWWorld::Ptr player = - MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWBase::Environment::get().getWorld()->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->mCell; Environment env = Env_Normal; diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 0d091e7425..6a0b4eec7d 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -24,7 +24,7 @@ namespace MWWorld std::pair result = MWWorld::Class::get (object).canBeEquipped (object, actor); // display error message if the player tried to equip something - if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer()) + if (!result.second.empty() && actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->messageBox(result.second); switch(result.first) @@ -84,7 +84,7 @@ namespace MWWorld std::string script = MWWorld::Class::get(object).getScript(object); /* Set OnPCEquip Variable on item's script, if the player is equipping it, and it has a script with that variable declared */ - if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && script != "") + if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && script != "") object.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); } } diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 6d5d9d8fde..67755259e0 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -34,7 +34,7 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->getBookWindow()->open(getTarget()); } - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); // Skill gain from books diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index ee4080755c..08d29b86bd 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -148,7 +148,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr { CellStore *cell; - Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); if(&(MWWorld::Class::get (player).getContainerStore (player)) == this) { @@ -305,8 +305,8 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; const std::vector& items = levItem->mList; - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerLevel = player.getClass().getCreatureStats(player).getLevel(); failChance += levItem->mChanceNone; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b3a9c9bb3f..3607b82769 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -166,10 +166,10 @@ namespace MWWorld void Scene::playerCellChange(MWWorld::CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos) { MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr old = world->getPlayer().getPlayer(); + MWWorld::Ptr old = world->getPlayerPtr(); world->getPlayer().setCell(cell); - MWWorld::Ptr player = world->getPlayer().getPlayer(); + MWWorld::Ptr player = world->getPlayerPtr(); mRendering.updatePlayerPtr(player); if (adjustPlayerPos) { @@ -369,14 +369,14 @@ namespace MWWorld if(!loadcell) { MWBase::World *world = MWBase::Environment::get().getWorld(); - world->moveObject(world->getPlayer().getPlayer(), position.pos[0], position.pos[1], position.pos[2]); + world->moveObject(world->getPlayerPtr(), position.pos[0], position.pos[1], position.pos[2]); float x = Ogre::Radian(position.rot[0]).valueDegrees(); float y = Ogre::Radian(position.rot[1]).valueDegrees(); float z = Ogre::Radian(position.rot[2]).valueDegrees(); - world->rotateObject(world->getPlayer().getPlayer(), x, y, z); + world->rotateObject(world->getPlayerPtr(), x, y, z); - MWWorld::Class::get(world->getPlayer().getPlayer()).adjustPosition(world->getPlayer().getPlayer()); + MWWorld::Class::get(world->getPlayerPtr()).adjustPosition(world->getPlayerPtr()); world->getFader()->fadeIn(0.5f); return; } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 513dcf6c72..c34beb3f2d 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -665,7 +665,7 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather; - std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; + std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell->mRegion; if (Misc::StringUtils::ciEqual(region, playerRegion)) setWeather(weather); } @@ -696,7 +696,7 @@ void WeatherManager::switchToNextWeather(bool instantly) MWBase::World* world = MWBase::Environment::get().getWorld(); if (world->isCellExterior() || world->isCellQuasiExterior()) { - std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayer().getPlayer().getCell()->mCell->mRegion); + std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->mCell->mRegion); if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b72433e5af..8bb2e7956f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -753,16 +753,16 @@ namespace MWWorld void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { - removeContainerScripts(getPlayer().getPlayer()); + removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position); - addContainerScripts(getPlayer().getPlayer(), getPlayer().getPlayer().getCell()); + addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } void World::changeToExteriorCell (const ESM::Position& position) { - removeContainerScripts(getPlayer().getPlayer()); + removeContainerScripts(getPlayerPtr()); mWorldScene->changeToExteriorCell(position); - addContainerScripts(getPlayer().getPlayer(), getPlayer().getPlayer().getCell()); + addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); } void World::markCellAsUnchanged() @@ -869,7 +869,7 @@ namespace MWWorld int cellY = newCell.mCell->getGridY(); mWorldScene->changeCell(cellX, cellY, pos, false); } - addContainerScripts (getPlayer().getPlayer(), &newCell); + addContainerScripts (getPlayerPtr(), &newCell); } else { @@ -1493,9 +1493,9 @@ namespace MWWorld cell = mCells.getExterior(cellX, cellY); } else - cell = getPlayer().getPlayer().getCell(); + cell = getPlayerPtr().getCell(); - ESM::Position pos = getPlayer().getPlayer().getRefData().getPosition(); + ESM::Position pos = getPlayerPtr().getRefData().getPosition(); pos.pos[0] = result.second[0]; pos.pos[1] = result.second[1]; pos.pos[2] = result.second[2]; @@ -2035,7 +2035,7 @@ namespace MWWorld std::string message; bool fail = false; - bool isPlayer = (actor == getPlayer().getPlayer()); + bool isPlayer = (actor == getPlayerPtr()); std::string selectedSpell = stats.getSpells().getSelectedSpell(); @@ -2415,4 +2415,9 @@ namespace MWWorld // with the Telekinesis effect. return feet * 22; } + + MWWorld::Ptr World::getPlayerPtr() + { + return mPlayer->getPlayer(); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92a9931571..51ad3a045c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -187,6 +187,7 @@ namespace MWWorld virtual const Fallback *getFallback() const; virtual Player& getPlayer(); + virtual MWWorld::Ptr getPlayerPtr(); virtual const MWWorld::ESMStore& getStore() const; From 19d63f392ff184ffa6835ccd452143606209c5a7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 18:59:00 +0100 Subject: [PATCH 181/251] Clean up the NpcStats expelled interface. Show message box when expelled. --- apps/openmw/mwdialogue/filter.cpp | 4 +--- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/npcstats.cpp | 20 +++++++++++++++---- apps/openmw/mwmechanics/npcstats.hpp | 6 ++++-- apps/openmw/mwscript/statsextensions.cpp | 20 +++---------------- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index af676d643d..4f478afce4 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -507,9 +507,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co std::string faction = MWWorld::Class::get (mActor).getNpcStats (mActor).getFactionRanks().begin()->first; - std::set& expelled = MWWorld::Class::get (player).getNpcStats (player).getExpelled(); - - return expelled.find (faction)!=expelled.end(); + return player.getClass().getNpcStats(player).getExpelled(faction); } case SelectWrapper::Function_PcVampire: diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 47dd7858c3..53422fbb17 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -470,7 +470,7 @@ namespace MWMechanics it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); ++it) { if(Misc::StringUtils::lowerCase(it->mFaction) == Misc::StringUtils::lowerCase(npcFaction) - && playerStats.getExpelled().find(Misc::StringUtils::lowerCase(it->mFaction)) == playerStats.getExpelled().end()) + && !playerStats.getExpelled(it->mFaction)) reaction = it->mReaction; } rank = playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 9b70e3347d..289fcc70fe 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -108,14 +108,26 @@ std::map& MWMechanics::NpcStats::getFactionRanks() return mFactionRank; } -const std::set& MWMechanics::NpcStats::getExpelled() const +bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const { - return mExpelled; + return mExpelled.find(Misc::StringUtils::lowerCase(factionID)) != mExpelled.end(); } -std::set& MWMechanics::NpcStats::getExpelled() +void MWMechanics::NpcStats::expell(const std::string& factionID) { - return mExpelled; + std::string lower = Misc::StringUtils::lowerCase(factionID); + if (mExpelled.find(lower) == mExpelled.end()) + { + std::string message = "#{sExpelledMessage}"; + message += MWBase::Environment::get().getWorld()->getStore().get().find(factionID)->mName; + MWBase::Environment::get().getWindowManager()->messageBox(message); + mExpelled.insert(lower); + } +} + +void MWMechanics::NpcStats::clearExpelled(const std::string& factionID) +{ + mExpelled.erase(Misc::StringUtils::lowerCase(factionID)); } bool MWMechanics::NpcStats::isSameFaction (const NpcStats& npcStats) const diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 586e068322..5fd358c858 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -99,8 +99,10 @@ namespace MWMechanics const std::map& getFactionRanks() const; std::map& getFactionRanks(); - const std::set& getExpelled() const; - std::set& getExpelled(); + const std::set& getExpelled() const { return mExpelled; } + bool getExpelled(const std::string& factionID) const; + void expell(const std::string& factionID); + void clearExpelled(const std::string& factionID); bool isSameFaction (const NpcStats& npcStats) const; ///< Do *this and \a npcStats share a faction? diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 2ade3200eb..56b3095763 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -905,15 +905,7 @@ namespace MWScript MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { - std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - if (expelled.find (factionID) != expelled.end()) - { - runtime.push(1); - } - else - { - runtime.push(0); - } + runtime.push(player.getClass().getNpcStats(player).getExpelled(factionID)); } else { @@ -951,9 +943,7 @@ namespace MWScript MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { - std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - Misc::StringUtils::toLower(factionID); - expelled.insert(factionID); + player.getClass().getNpcStats(player).expell(factionID); } } }; @@ -986,11 +976,7 @@ namespace MWScript } MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") - { - std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - Misc::StringUtils::toLower(factionID); - expelled.erase (factionID); - } + player.getClass().getNpcStats(player).clearExpelled(factionID); } }; From 9baa1bef7854e249dab21c4b5575d376df440736 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 19:26:03 +0100 Subject: [PATCH 182/251] Expell player from faction when committing a crime against a faction member --- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 28f6dd4894..bccc7120fb 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -255,7 +255,7 @@ namespace MWGui if (pickpocket.finish()) { MWBase::Environment::get().getMechanicsManager()->reportCrime( - player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Pickpocket); + player, mPtr, MWBase::MechanicsManager::OT_Pickpocket); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 53422fbb17..429683b97f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -829,6 +829,18 @@ namespace MWMechanics ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + arg); + // If committing a crime against a faction member, expell from the faction + if (!victim.isEmpty() && victim.getClass().isNpc()) + { + 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))) + { + ptr.getClass().getNpcStats(ptr).expell(factionID); + } + } + // TODO: make any guards in the area try to arrest the player } From 154fae9f251de3a589c75ee1291c4cc21bce7aac Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 19:52:57 +0100 Subject: [PATCH 183/251] Don't suppress exceptions thrown while running scripts --- apps/openmw/mwscript/scriptmanagerimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 14fe5b7fd6..8b6b7ed2a3 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -114,10 +114,8 @@ namespace MWScript } catch (const std::exception& e) { - std::cerr << "execution of script " << name << " failed." << std::endl; - - if (mVerbose) - std::cerr << "(" << e.what() << ")" << std::endl; + std::cerr << "Execution of script " << name << " failed:" << std::endl; + std::cerr << e.what() << std::endl; iter->second.first.clear(); // don't execute again. } From bf02b77c1dc008c022b9e5ef294b77b5f53f5fa7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 19:57:13 +0100 Subject: [PATCH 184/251] Closes #1090: Don't throw an exception if a cell has no region in GetPCCell --- apps/openmw/mwscript/cellextensions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index e0d41cd195..0e3b379d84 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -107,7 +107,8 @@ namespace MWScript std::string current = cell->mName; - if (!(cell->mData.mFlags & ESM::Cell::Interior) && current.empty()) + if (!(cell->mData.mFlags & ESM::Cell::Interior) && current.empty() + && !cell->mRegion.empty()) { const ESM::Region *region = MWBase::Environment::get().getWorld()->getStore().get().find (cell->mRegion); From 3127b8518e321ec2ad5dce758c0960bd5acec03f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Jan 2014 20:15:54 +0100 Subject: [PATCH 185/251] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index bd0c6ca743..561931cde9 100644 --- a/credits.txt +++ b/credits.txt @@ -24,6 +24,7 @@ Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) darkf +Dmitry Shkurskiy (endorph) Douglas Diniz (Dgdiniz) Douglas Mencken (dougmencken) Edmondo Tommasina (edmondo) From c55f9bd0aaa931afde650849856eb0371778440a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 20:19:47 +0100 Subject: [PATCH 186/251] GetWaterLevel fix (similar to MCP): if there is no water in the cell, return -FLT_MAX to prevent mods from incorrectly thinking the player is underwater. --- apps/openmw/mwscript/cellextensions.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 0e3b379d84..5de95d1d4d 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -130,7 +130,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); - runtime.push (cell->mWaterLevel); + if (cell->mCell->hasWater()) + runtime.push (cell->mWaterLevel); + else + runtime.push (-std::numeric_limits().max()); } }; From 372cd437d199529c9d4d2409d0692689eeaafd03 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 22:58:36 +0100 Subject: [PATCH 187/251] Add a utility function for counting items in a container --- apps/openmw/mwscript/containerextensions.cpp | 10 ++-------- apps/openmw/mwworld/containerstore.cpp | 9 +++++++++ apps/openmw/mwworld/containerstore.hpp | 3 +++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 5234eaea39..202ec64647 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -87,15 +87,9 @@ namespace MWScript std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); - Interpreter::Type_Integer sum = 0; - - for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) - sum += iter->getRefData().getCount(); - - runtime.push (sum); + runtime.push (store.count(item)); } }; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 08d29b86bd..1583f26e1c 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -77,6 +77,15 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() return ContainerStoreIterator (this); } +int MWWorld::ContainerStore::count(const std::string &id) +{ + int total=0; + for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, id)) + total += iter->getRefData().getCount(); + return total; +} + void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) { if (ptr.getRefData().getCount() <= 1) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index e4e10b3273..9a97f9c1c9 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -92,6 +92,9 @@ namespace MWWorld void unstack (const Ptr& ptr, const Ptr& container); ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). + /// @return How many items with refID \a id are in this container? + int count (const std::string& id); + protected: ContainerStoreIterator addNewStack (const Ptr& ptr, int count); ///< Add the item to this container (do not try to stack it onto existing items) From 0f5dc5917695d61953bce045f82919b4063f253e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 23:37:46 +0100 Subject: [PATCH 188/251] Remove useless dependencies on InventoryWindow for getting player gold. Don't use string literals for gold_001 id, just to be sure. --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 10 +++---- apps/openmw/mwgui/enchantingdialog.cpp | 7 ++--- apps/openmw/mwgui/inventorywindow.cpp | 13 ---------- apps/openmw/mwgui/inventorywindow.hpp | 2 -- apps/openmw/mwgui/merchantrepair.cpp | 12 ++++----- apps/openmw/mwgui/spellbuyingwindow.cpp | 19 +++++++++----- apps/openmw/mwgui/spellcreationdialog.cpp | 10 +++---- apps/openmw/mwgui/tradeitemmodel.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 26 +++++++++++-------- apps/openmw/mwgui/tradewindow.hpp | 4 +-- apps/openmw/mwgui/trainingwindow.cpp | 14 +++++----- apps/openmw/mwgui/travelwindow.cpp | 20 +++++++++----- apps/openmw/mwmechanics/enchanting.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 1 + apps/openmw/mwworld/containerstore.cpp | 6 +++-- apps/openmw/mwworld/containerstore.hpp | 2 ++ 18 files changed, 82 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 864a756c52..480c335c2a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -100,7 +100,7 @@ namespace MWClass // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - data->mContainerStore.add("gold_001", ref->mBase->mData.mGold, ptr); + data->mContainerStore.add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); // store ptr.getRefData().setCustomData (data.release()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 24caed3f5f..f3b396a708 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -328,7 +328,7 @@ namespace MWClass // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - getContainerStore(ptr).add("gold_001", gold, ptr); + getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, gold, ptr); getInventoryStore(ptr).autoEquip(ptr); } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 0460900eda..8269e8364b 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -20,7 +20,6 @@ #include "list.hpp" #include "tradewindow.hpp" #include "spellbuyingwindow.hpp" -#include "inventorywindow.hpp" #include "travelwindow.hpp" #include "bookpage.hpp" @@ -75,17 +74,17 @@ namespace MWGui else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; else if (sender == mBribe10Button) { - player.getClass().getContainerStore(player).remove("gold_001", 10, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 10, player); type = MWBase::MechanicsManager::PT_Bribe10; } else if (sender == mBribe100Button) { - player.getClass().getContainerStore(player).remove("gold_001", 100, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 100, player); type = MWBase::MechanicsManager::PT_Bribe100; } else /*if (sender == mBribe1000Button)*/ { - player.getClass().getContainerStore(player).remove("gold_001", 1000, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 1000, player); type = MWBase::MechanicsManager::PT_Bribe1000; } @@ -99,7 +98,8 @@ namespace MWGui WindowModal::open(); center(); - int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); mBribe10Button->setEnabled (playerGold >= 10); mBribe100Button->setEnabled (playerGold >= 100); diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index cb57b51b1e..4d79875a28 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/containerstore.hpp" #include "itemselection.hpp" #include "container.hpp" -#include "inventorywindow.hpp" #include "sortfilteritemmodel.hpp" @@ -289,7 +288,9 @@ namespace MWGui mEnchanting.setNewItemName(mName->getCaption()); mEnchanting.setEffect(mEffectList); - if (mEnchanting.getEnchantPrice() > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + if (mEnchanting.getEnchantPrice() > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 27be9339bb..0ad156cdf3 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -447,19 +447,6 @@ namespace MWGui updateEncumbranceBar(); } - int InventoryWindow::getPlayerGold() - { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - for (MWWorld::ContainerStoreIterator it = invStore.begin(); - it != invStore.end(); ++it) - { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) - return it->getRefData().getCount(); - } - return 0; - } - void InventoryWindow::setTrading(bool trading) { mTrading = trading; diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 94ecfd4c8a..112e737fab 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -31,8 +31,6 @@ namespace MWGui void pickUpObject (MWWorld::Ptr object); - int getPlayerGold(); - MyGUI::IntCoord getAvatarScreenCoord(); MWWorld::Ptr getAvatarSelectedItem(int x, int y); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 20eb3a615a..8b811ff501 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -11,8 +11,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" -#include "inventorywindow.hpp" - namespace MWGui { @@ -36,6 +34,8 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) int currentY = 0; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); int categories = MWWorld::ContainerStore::Type_Weapon | MWWorld::ContainerStore::Type_Armor; for (MWWorld::ContainerStoreIterator iter (store.begin(categories)); @@ -69,7 +69,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) MyGUI::Button* button = mList->createWidget( - (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, currentY, 0, @@ -79,7 +79,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) currentY += 18; - button->setEnabled(price<=MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()); + button->setEnabled(price<=playerGold); button->setUserString("Price", boost::lexical_cast(price)); button->setUserData(*iter); button->setCaptionWithReplacing(name); @@ -92,7 +92,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) mList->setCanvasSize (MyGUI::IntSize(mList->getWidth(), std::max(mList->getHeight(), currentY))); mGoldLabel->setCaptionWithReplacing("#{sGold}: " - + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + + boost::lexical_cast(playerGold)); } void MerchantRepair::onMouseWheel(MyGUI::Widget* _sender, int _rel) @@ -119,7 +119,7 @@ void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender) int price = boost::lexical_cast(sender->getUserString("Price")); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - player.getClass().getContainerStore(player).remove("gold_001", price, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); startRepair(mActor); } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index f285b01caa..4be7919a92 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -13,8 +13,6 @@ #include "../mwmechanics/creaturestats.hpp" -#include "inventorywindow.hpp" - namespace MWGui { const int SpellBuyingWindow::sLineHeight = 18; @@ -42,9 +40,12 @@ namespace MWGui int price = spell->mData.mCost*store.get().find("fSpellValueMult")->getFloat(); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + MyGUI::Button* toAdd = mSpellsView->createWidget( - (price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", + (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, @@ -116,13 +117,16 @@ namespace MWGui { int price = *_sender->getUserData(); - if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()>=price) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + if (playerGold>=price) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); - player.getClass().getContainerStore(player).remove("gold_001", price, player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); startSpellBuying(mPtr); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); @@ -136,7 +140,10 @@ namespace MWGui void SpellBuyingWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 3a17d50aa1..857dd76d5b 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -13,7 +13,6 @@ #include "tooltips.hpp" #include "class.hpp" -#include "inventorywindow.hpp" namespace { @@ -333,7 +332,10 @@ namespace MWGui return; } - if (boost::lexical_cast(mPriceLabel->getCaption()) > MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + if (boost::lexical_cast(mPriceLabel->getCaption()) > playerGold) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage18}"); return; @@ -341,9 +343,7 @@ namespace MWGui mSpell.mName = mNameEdit->getCaption(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - player.getClass().getContainerStore(player).remove("gold_001", boost::lexical_cast(mPriceLabel->getCaption()), player); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, boost::lexical_cast(mPriceLabel->getCaption()), player); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 5c12843da0..28216ad145 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -149,7 +149,7 @@ namespace MWGui if(!mMerchant.isEmpty()) { MWWorld::Ptr base = item.mBase; - if(Misc::StringUtils::ciEqual(base.getCellRef().mRefID, "gold_001")) + if(Misc::StringUtils::ciEqual(base.getCellRef().mRefID, MWWorld::ContainerStore::sGoldId)) continue; if(!MWWorld::Class::get(base).canSell(base, services)) continue; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0111623be7..1cd4c1c7c0 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -210,11 +210,11 @@ namespace MWGui if (amount > 0) { - store.add("gold_001", amount, actor); + store.add(MWWorld::ContainerStore::sGoldId, amount, actor); } else { - store.remove("gold_001", - amount, actor); + store.remove(MWWorld::ContainerStore::sGoldId, - amount, actor); } } @@ -251,8 +251,11 @@ namespace MWGui return; } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + // check if the player can afford this - if (mCurrentBalance < 0 && MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold() < std::abs(mCurrentBalance)) + if (mCurrentBalance < 0 && playerGold < std::abs(mCurrentBalance)) { // user notification MWBase::Environment::get().getWindowManager()-> @@ -269,8 +272,6 @@ namespace MWGui return; } - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); - if(mCurrentBalance > mCurrentMerchantOffer) { //if npc is a creature: reject (no haggle) @@ -292,8 +293,8 @@ namespace MWGui float clampedDisposition = std::max(0,std::min(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),100)); - const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); - const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); + const MWMechanics::NpcStats &sellerStats = mPtr.getClass().getNpcStats(mPtr); + const MWMechanics::NpcStats &playerStats = player.getClass().getNpcStats(player); float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); @@ -322,7 +323,7 @@ namespace MWGui } //skill use! - MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); + player.getClass().skillUsageSucceeded(player, ESM::Skill::Mercantile, 0); } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); @@ -335,7 +336,7 @@ namespace MWGui // transfer the gold if (mCurrentBalance != 0) { - addOrRemoveGold(mCurrentBalance, playerPtr); + addOrRemoveGold(mCurrentBalance, player); addOrRemoveGold(-mCurrentBalance, mPtr); } @@ -396,7 +397,10 @@ namespace MWGui void TradeWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(playerGold)); if (mCurrentBalance > 0) { @@ -445,7 +449,7 @@ namespace MWGui MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr); for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, MWWorld::ContainerStore::sGoldId)) merchantGold += it->getRefData().getCount(); } return merchantGold; diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 7c11bd5394..1a8999e6e5 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -28,8 +28,6 @@ namespace MWGui void startTrade(const MWWorld::Ptr& actor); - void addOrRemoveGold(int gold, const MWWorld::Ptr& actor); - void onFrame(float frameDuration); void borrowItem (int index, size_t count); @@ -95,6 +93,8 @@ namespace MWGui void onIncreaseButtonTriggered(); void onDecreaseButtonTriggered(); + void addOrRemoveGold(int gold, const MWWorld::Ptr& actor); + void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index fc96d31bb7..e196e93f2b 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -14,7 +14,6 @@ #include "../mwmechanics/npcstats.hpp" -#include "inventorywindow.hpp" #include "tooltips.hpp" namespace MWGui @@ -40,7 +39,10 @@ namespace MWGui { mPtr = actor; - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats (actor); @@ -71,7 +73,6 @@ namespace MWGui MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator (); MyGUI::Gui::getInstance ().destroyWidgets (widgets); - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::Store &gmst = @@ -82,7 +83,7 @@ namespace MWGui int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer (mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); - std::string skin = (price > MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; + std::string skin = (price > playerGold) ? "SandTextGreyedOut" : "SandTextButton"; MyGUI::Button* button = mTrainingOptions->createWidget(skin, MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); @@ -115,6 +116,7 @@ namespace MWGui int skillId = *sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::ESMStore &store = @@ -123,7 +125,7 @@ namespace MWGui int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()removeGuiMode (GM_Training); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7f223c505f..2f844c3fd2 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include "../mwbase/environment.hpp" @@ -12,8 +14,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" -#include "inventorywindow.hpp" - namespace MWGui { const int TravelWindow::sLineHeight = 18; @@ -50,13 +50,15 @@ namespace MWGui const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + if(interior) { price = gmst.find("fMagesGuildTravel")->getFloat(); } else { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position PlayerPos = player.getRefData().getPosition(); float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) ); price = d/gmst.find("fTravelMult")->getFloat(); @@ -64,7 +66,7 @@ namespace MWGui price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - MyGUI::Button* toAdd = mDestinationsView->createWidget((price>MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mDestinationsView->createWidget((price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); @@ -121,12 +123,13 @@ namespace MWGui iss >> price; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if (MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold()getFader ()->fadeOut(1); ESM::Position pos = *_sender->getUserData(); @@ -165,7 +168,10 @@ namespace MWGui void TravelWindow::updateLabels() { - mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); + int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index f428bd4b06..9ccc69f901 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -299,6 +299,6 @@ namespace MWMechanics const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); - store.remove("gold_001", getEnchantPrice(), player); + store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 429683b97f..2a613b7ffe 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -802,6 +802,7 @@ namespace MWMechanics && (type != OT_Assault || it->first != victim) ) { + // TODO: stats.setAlarmed(true) on NPCs within earshot reported=true; break; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 1583f26e1c..70a6fe8567 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -63,6 +63,8 @@ namespace } } +const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; + MWWorld::ContainerStore::ContainerStore() : mCachedWeight (0), mWeightUpToDate (false) {} MWWorld::ContainerStore::~ContainerStore() {} @@ -196,7 +198,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { - if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) + if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, MWWorld::ContainerStore::sGoldId)) { iter->getRefData().setCount(iter->getRefData().getCount() + realCount); flagAsModified(); @@ -204,7 +206,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, } } - MWWorld::ManualRef ref(esmStore, "Gold_001", count); + MWWorld::ManualRef ref(esmStore, MWWorld::ContainerStore::sGoldId, count); return addNewStack(ref.getPtr(), count); } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 9a97f9c1c9..936468f8dc 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -35,6 +35,8 @@ namespace MWWorld static const int Type_All = 0xffff; + static const std::string sGoldId; + private: MWWorld::CellRefList potions; From d7f69205f652bad7a2f043ca357fbf61cd6d9022 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Jan 2014 23:56:40 +0100 Subject: [PATCH 189/251] Don't play "Menu Click" sound for disabled (= cannot receive input) buttons, plus some redundancy fixes --- apps/openmw/mwgui/merchantrepair.cpp | 3 +-- apps/openmw/mwgui/spellbuyingwindow.cpp | 21 ++++++++------------- apps/openmw/mwgui/trainingwindow.cpp | 9 ++------- apps/openmw/mwgui/travelwindow.cpp | 3 ++- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- files/mygui/openmw_text.skin.xml | 8 -------- 6 files changed, 14 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 8b811ff501..3c3335d8b0 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -68,8 +68,7 @@ void MerchantRepair::startRepair(const MWWorld::Ptr &actor) MyGUI::Button* button = - mList->createWidget( - (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", + mList->createWidget("SandTextButton", 0, currentY, 0, diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 4be7919a92..68aecf28dd 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -45,13 +45,14 @@ namespace MWGui MyGUI::Button* toAdd = mSpellsView->createWidget( - (price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", + "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default ); + toAdd->setEnabled(price<=playerGold); mCurrentY += sLineHeight; @@ -118,19 +119,13 @@ namespace MWGui int price = *_sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + spells.add (mSpellsWidgetMap.find(_sender)->second); + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); + startSpellBuying(mPtr); - if (playerGold>=price) - { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& spells = stats.getSpells(); - spells.add (mSpellsWidgetMap.find(_sender)->second); - player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); - startSpellBuying(mPtr); - - MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); - } + MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); } void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index e196e93f2b..709bc0fb95 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -83,11 +83,10 @@ namespace MWGui int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer (mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); - std::string skin = (price > playerGold) ? "SandTextGreyedOut" : "SandTextButton"; - - MyGUI::Button* button = mTrainingOptions->createWidget(skin, + 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->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); @@ -116,7 +115,6 @@ namespace MWGui int skillId = *sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); const MWWorld::ESMStore &store = @@ -125,9 +123,6 @@ namespace MWGui int price = pcStats.getSkill (skillId).getBase() * store.get().find("iTrainingMod")->getInt (); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); - if (playerGoldgetBarterOffer(mPtr,price,true); - MyGUI::Button* toAdd = mDestinationsView->createWidget((price>playerGold) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + toAdd->setEnabled(price<=playerGold); mCurrentY += sLineHeight; if(interior) toAdd->setUserString("interior","y"); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 77b72fc0ac..c2efa0c331 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -555,7 +555,7 @@ namespace MWInput if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) { MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); - if (b) + if (b && b->getEnabled()) { MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); } diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 6a1dea60bf..15287bc74e 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -17,14 +17,6 @@ - - - - - - - - From 9bf7bf529c948b02a63596a17e9ca30dbeff2af3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 00:40:25 +0100 Subject: [PATCH 190/251] Implement crime-related dialogue globals as they are described in MSFD --- apps/openmw/mwbase/world.hpp | 4 ++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 6 ++++++ apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 21 +++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ 5 files changed, 36 insertions(+) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 611bd913bf..b719ce4d60 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -448,6 +448,10 @@ namespace MWBase /// @note This also works for references in containers. virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, DetectionType type) = 0; + + /// Update the value of some globals according to the world state, which may be used by dialogue entries. + /// This should be called when initiating a dialogue. + virtual void updateDialogueGlobals() = 0; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 2fce7e327a..b2107c3296 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -143,6 +143,7 @@ namespace MWDialogue //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI updateTopics(); + updateGlobals(); //greeting const MWWorld::Store &dialogs = @@ -298,6 +299,11 @@ namespace MWDialogue } } + void DialogueManager::updateGlobals() + { + MWBase::Environment::get().getWorld()->updateDialogueGlobals(); + } + void DialogueManager::updateTopics() { std::list keywordList; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 5baf20a0e6..c32a5dbd89 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -40,6 +40,7 @@ namespace MWDialogue void parseText (const std::string& text); void updateTopics(); + void updateGlobals(); bool compile (const std::string& cmd,std::vector& code); void executeScript (const std::string& script); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8bb2e7956f..46511f5bcd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2420,4 +2420,25 @@ namespace MWWorld { return mPlayer->getPlayer(); } + + void World::updateDialogueGlobals() + { + MWWorld::Ptr player = getPlayerPtr(); + int bounty = player.getClass().getNpcStats(player).getBounty(); + int playerGold = player.getClass().getContainerStore(player).count(ContainerStore::sGoldId); + + float fCrimeGoldDiscountMult = getStore().get().find("fCrimeGoldDiscountMult")->getFloat(); + float fCrimeGoldTurnInMult = getStore().get().find("fCrimeGoldTurnInMult")->getFloat(); + + int discount = bounty*fCrimeGoldDiscountMult; + int turnIn = bounty * fCrimeGoldTurnInMult; + + mGlobalVariables->setInt("pchascrimegold", (bounty <= playerGold) ? 1 : 0); + + mGlobalVariables->setInt("pchasgolddiscount", (discount <= playerGold) ? 1 : 0); + mGlobalVariables->setInt("crimegolddiscount", discount); + + mGlobalVariables->setInt("crimegoldturnin", turnIn); + mGlobalVariables->setInt("pchasturnin", (turnIn <= playerGold) ? 1 : 0); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 51ad3a045c..ba7dc66b69 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -533,6 +533,10 @@ namespace MWWorld /// @note This also works for references in containers. virtual void listDetectedReferences (const MWWorld::Ptr& ptr, std::vector& out, DetectionType type); + + /// Update the value of some globals according to the world state, which may be used by dialogue entries. + /// This should be called when initiating a dialogue. + virtual void updateDialogueGlobals(); }; } From 925d28e4a7b812ffc2cafe020b61fe93230ffd98 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Thu, 9 Jan 2014 00:46:25 +0100 Subject: [PATCH 191/251] crashcatcher uname error checking --- 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 d7ebc0d47b..65a0369193 100644 --- a/apps/openmw/crashcatcher.cpp +++ b/apps/openmw/crashcatcher.cpp @@ -177,7 +177,7 @@ static void sys_info(void) { #ifdef __unix__ struct utsname info; - if(!uname(&info)) + if(uname(&info)) printf("!!! Failed to get system information\n"); else printf("System: %s %s %s %s %s\n", From baf55df7a11a7e199121f0a653eca4414bcd4c0e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 01:34:10 +0100 Subject: [PATCH 192/251] Gold fixes (when did this break?) --- apps/openmw/mwclass/misc.cpp | 4 +++- apps/openmw/mwgui/inventorywindow.cpp | 2 -- apps/openmw/mwworld/containerstore.cpp | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index e5120462e6..a8a6c55ec5 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -93,7 +93,9 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - int value = (ptr.getCellRef().mGoldValue == 1) ? ref->mBase->mData.mValue : ptr.getCellRef().mGoldValue; + int value = ref->mBase->mData.mValue; + if (ptr.getCellRef().mGoldValue > 1 && ptr.getRefData().getCount() == 1) + value = ptr.getCellRef().mGoldValue; if (ptr.getCellRef().mSoul != "") { diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0ad156cdf3..7781c8526d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -499,8 +499,6 @@ namespace MWGui return; int count = object.getRefData().getCount(); - if (object.getCellRef().mGoldValue > 1) - count = object.getCellRef().mGoldValue; // add to player inventory // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 70a6fe8567..744971985c 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -134,7 +134,11 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count); - return add(ref.getPtr(), count, actorPtr, true); + // a bit pointless to set owner for the player + if (actorPtr.getRefData().getHandle() != "player") + return add(ref.getPtr(), count, actorPtr, true); + else + return add(ref.getPtr(), count, actorPtr, false); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) @@ -194,7 +198,9 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr, || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - int realCount = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); + int realCount = ptr.getRefData().getCount(); + if (ptr.getCellRef().mGoldValue > 1 && realCount == 1) + realCount = ptr.getCellRef().mGoldValue; for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { From 6f9113fe882c183d572bcffe846af1a63becd071 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 01:49:58 +0100 Subject: [PATCH 193/251] Add preliminary implementation of PayFine, PayFineThief and GoToJail instructions --- apps/openmw/mwbase/world.hpp | 5 ++- apps/openmw/mwgui/messagebox.cpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 8 ++--- apps/openmw/mwrender/camera.cpp | 1 + apps/openmw/mwscript/docs/vmformat.txt | 5 ++- apps/openmw/mwscript/miscextensions.cpp | 40 ++++++++++++++++++++++++ apps/openmw/mwworld/worldimp.cpp | 8 ++++- apps/openmw/mwworld/worldimp.hpp | 5 ++- components/compiler/extensions0.cpp | 3 ++ components/compiler/opcodes.hpp | 3 ++ 10 files changed, 65 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b719ce4d60..43e526ecba 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -431,11 +431,10 @@ namespace MWBase virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) = 0; - /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) - /// closest to \a worldPos. + /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, - const std::string& id, Ogre::Vector3 worldPos) = 0; + const std::string& id) = 0; enum DetectionType { diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 378e76e467..8e518668bd 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -10,6 +10,7 @@ namespace MWGui MessageBoxManager::MessageBoxManager () { + // TODO: fMessageTimePerChar mMessageBoxSpeed = 0.1; mInterMessageBoxe = NULL; mStaticMessageBox = NULL; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 21b6d0d779..a1cdb8b611 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -287,17 +287,13 @@ namespace MWMechanics if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled()) return; - Ogre::Vector3 worldPos; - if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(target.getCell(), worldPos)) - worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); - if (effectId == ESM::MagicEffect::DivineIntervention) { - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker", worldPos); + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker"); } else if (effectId == ESM::MagicEffect::AlmsiviIntervention) { - MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker", worldPos); + MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker"); } else if (effectId == ESM::MagicEffect::Mark) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9a35725ee3..dda9797eff 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -228,6 +228,7 @@ namespace MWRender void Camera::setSneakOffset() { + // TODO: iFirstPersonSneakDelta if(mAnimation) mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -9.8f)); } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index a6349c4da4..64a9d427a8 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -372,5 +372,8 @@ op 0x2000231: GetSpellReadied op 0x2000232: GetSpellReadied, explicit op 0x2000233: GetPcJumping op 0x2000234: ShowRestMenu, explicit -opcodes 0x2000235-0x3ffffff unused +op 0x2000235: GoToJail +op 0x2000236: PayFine +op 0x2000237: PayFineThief +opcodes 0x2000238-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 61d286ae3e..03dc5c14c3 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -762,6 +762,43 @@ namespace MWScript } }; + class OpGoToJail : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World* world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayerPtr(); + world->teleportToClosestMarker(player, "prisonmarker"); + player.getClass().getNpcStats(player).setBounty(0); + // TODO: pass time, change skills, show messagebox + // TODO: move stolen items to closest evidence chest + // iDaysinPrisonMod + } + }; + + class OpPayFine : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + player.getClass().getNpcStats(player).setBounty(0); + + // TODO: move stolen items to closest evidence chest + } + }; + + class OpPayFineThief : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + player.getClass().getNpcStats(player).setBounty(0); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -785,6 +822,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetPcJumping, new OpGetPcJumping); interpreter.installSegment5 (Compiler::Misc::opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (Compiler::Misc::opcodePlayBink, new OpPlayBink); + interpreter.installSegment5 (Compiler::Misc::opcodePayFine, new OpPayFine); + interpreter.installSegment5 (Compiler::Misc::opcodePayFineThief, new OpPayFineThief); + interpreter.installSegment5 (Compiler::Misc::opcodeGoToJail, new OpGoToJail); interpreter.installSegment5 (Compiler::Misc::opcodeGetLocked, new OpGetLocked); interpreter.installSegment5 (Compiler::Misc::opcodeGetLockedExplicit, new OpGetLocked); interpreter.installSegment5 (Compiler::Misc::opcodeGetEffect, new OpGetEffect); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 46511f5bcd..f61938f92b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2273,6 +2273,8 @@ namespace MWWorld bool World::findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result) { + if (cell->isExterior()) + return false; MWWorld::CellRefList& doors = cell->mDoors; CellRefList::List& refList = doors.mList; @@ -2293,8 +2295,12 @@ namespace MWWorld } void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, - const std::string& id, Ogre::Vector3 worldPos) + const std::string& id) { + Ogre::Vector3 worldPos; + if (!findInteriorPositionInWorldSpace(ptr.getCell(), worldPos)) + worldPos = mPlayer->getLastKnownExteriorPosition(); + MWWorld::Ptr closestMarker; float closestDistance = FLT_MAX; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index ba7dc66b69..cc087a89f1 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -522,11 +522,10 @@ namespace MWWorld virtual bool findInteriorPositionInWorldSpace(MWWorld::CellStore* cell, Ogre::Vector3& result); - /// Teleports \a ptr to the reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) - /// closest to \a worldPos. + /// Teleports \a ptr to the closest reference of \a id (e.g. DivineMarker, PrisonMarker, TempleMarker) /// @note id must be lower case virtual void teleportToClosestMarker (const MWWorld::Ptr& ptr, - const std::string& id, Ogre::Vector3 worldPos); + const std::string& id); /// List all references (filtered by \a type) detected by \a ptr. The range /// is determined by the current magnitude of the "Detect X" magic effect belonging to \a type. diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index e35a32ffa4..03510d833c 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -244,6 +244,9 @@ namespace Compiler extensions.registerFunction ("getpcjumping", 'l', "", opcodeGetPcJumping); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerInstruction ("playbink", "Sl", opcodePlayBink); + extensions.registerInstruction ("payfine", "", opcodePayFine); + extensions.registerInstruction ("payfinethief", "", opcodePayFineThief); + extensions.registerInstruction ("gotojail", "", opcodeGoToJail); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index eb2240964a..3a4c8135db 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -223,6 +223,9 @@ namespace Compiler const int opcodeGetStandingActorExplicit = 0x200020f; const int opcodeGetWindSpeed = 0x2000212; const int opcodePlayBink = 0x20001f7; + const int opcodeGoToJail = 0x2000235; + const int opcodePayFine = 0x2000236; + const int opcodePayFineThief = 0x2000237; const int opcodeHitOnMe = 0x2000213; const int opcodeHitOnMeExplicit = 0x2000214; const int opcodeDisableTeleporting = 0x2000215; From 223c1d5a389c03f4f483d6d6ed90798cc37d2041 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 01:55:49 +0100 Subject: [PATCH 194/251] Use GMSTs for crime bounty --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2a613b7ffe..511fe65447 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -803,6 +803,7 @@ namespace MWMechanics ) { // TODO: stats.setAlarmed(true) on NPCs within earshot + // fAlarmRadius ? reported=true; break; } @@ -816,15 +817,18 @@ namespace MWMechanics void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { + const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); // Bounty for each type of crime if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) - arg = 5; + arg = store.find("iCrimeTresspass")->getInt(); else if (type == OT_Pickpocket) - arg = 25; + arg = store.find("iCrimePickPocket")->getInt(); else if (type == OT_Assault) - arg = 40; + arg = store.find("iCrimeAttack")->getInt(); else if (type == OT_Murder) - arg = 1000; + arg = store.find("iCrimeKilling")->getInt(); + else if (type == OT_Theft) + arg *= store.find("fCrimeStealing")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() From 768d9f72378da0863a252614081cf9326c8fd6e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 02:14:08 +0100 Subject: [PATCH 195/251] Scripting: Add an optional 'required' parameter to getReference (default: true). If required=false, it will not throw an exception if there's no reference. Fixes PcExpell not working without a reference like it's supposed to, and makes the code nicer for some others (use required=false instead of catching the exception) --- apps/openmw/mwscript/guiextensions.cpp | 9 +-------- apps/openmw/mwscript/interpretercontext.cpp | 12 ++++++------ apps/openmw/mwscript/interpretercontext.hpp | 6 +++--- apps/openmw/mwscript/miscextensions.cpp | 10 ++++------ apps/openmw/mwscript/ref.hpp | 6 +++--- apps/openmw/mwscript/statsextensions.cpp | 6 ++---- 6 files changed, 19 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index e51bdcf690..b5e2bd2931 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -54,14 +54,7 @@ namespace MWScript public: virtual void execute (Interpreter::Runtime& runtime) { - // FIXME: No way to tell if we have a reference before trying to get it, and it will - // cause an exception is there isn't one :( - MWWorld::Ptr bed; - try { - bed = R()(runtime); - } - catch(std::runtime_error&) { - } + MWWorld::Ptr bed = R()(runtime, false); if (bed.isEmpty() || !MWBase::Environment::get().getMechanicsManager()->sleepInBed(MWBase::Environment::get().getWorld()->getPlayerPtr(), bed)) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index aa9e320086..18baf0bda9 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -23,7 +23,7 @@ namespace MWScript { MWWorld::Ptr InterpreterContext::getReference ( - const std::string& id, bool activeOnly) + const std::string& id, bool activeOnly, bool doThrow) { if (!id.empty()) { @@ -31,7 +31,7 @@ namespace MWScript } else { - if (mReference.isEmpty()) + if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); return mReference; @@ -39,7 +39,7 @@ namespace MWScript } const MWWorld::Ptr InterpreterContext::getReference ( - const std::string& id, bool activeOnly) const + const std::string& id, bool activeOnly, bool doThrow) const { if (!id.empty()) { @@ -47,7 +47,7 @@ namespace MWScript } else { - if (mReference.isEmpty()) + if (mReference.isEmpty() && doThrow) throw std::runtime_error ("no implicit reference"); return mReference; @@ -498,8 +498,8 @@ namespace MWScript ptr.getRefData().getLocals().mFloats[index] = value; } - MWWorld::Ptr InterpreterContext::getReference() + MWWorld::Ptr InterpreterContext::getReference(bool required) { - return getReference ("", true); + return getReference ("", true, required); } } diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 9c7b443ae1..04546faed7 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -33,9 +33,9 @@ namespace MWScript bool mActivationHandled; boost::shared_ptr mAction; - MWWorld::Ptr getReference (const std::string& id, bool activeOnly); + MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true); - const MWWorld::Ptr getReference (const std::string& id, bool activeOnly) const; + const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const; public: @@ -150,7 +150,7 @@ namespace MWScript virtual void setMemberFloat (const std::string& id, const std::string& name, float value); - MWWorld::Ptr getReference(); + MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 03dc5c14c3..a403d76edf 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -699,13 +699,11 @@ namespace MWScript public: virtual void execute(Interpreter::Runtime& runtime) { - // No way to tell if we have a reference before trying to get it, and it will - // cause an exception is there isn't one :( - try { - MWWorld::Ptr ptr = R()(runtime); + MWWorld::Ptr ptr = R()(runtime, false); + if (!ptr.isEmpty()) printLocalVars(runtime, ptr); - } - catch(std::runtime_error&) { + else + { // No reference, no problem. printGlobalVars(runtime); } diff --git a/apps/openmw/mwscript/ref.hpp b/apps/openmw/mwscript/ref.hpp index 81b1d5ef99..7958391395 100644 --- a/apps/openmw/mwscript/ref.hpp +++ b/apps/openmw/mwscript/ref.hpp @@ -16,7 +16,7 @@ namespace MWScript { struct ExplicitRef { - MWWorld::Ptr operator() (Interpreter::Runtime& runtime) const + MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required=true) const { std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -27,12 +27,12 @@ namespace MWScript struct ImplicitRef { - MWWorld::Ptr operator() (Interpreter::Runtime& runtime) const + MWWorld::Ptr operator() (Interpreter::Runtime& runtime, bool required=true) const { MWScript::InterpreterContext& context = static_cast (runtime.getContext()); - return context.getReference(); + return context.getReference(required); } }; } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 56b3095763..d6c2cb8947 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -921,8 +921,6 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime); - std::string factionID = ""; if(arg0 >0 ) { @@ -931,6 +929,7 @@ namespace MWScript } else { + MWWorld::Ptr ptr = R()(runtime); if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) { factionID = ""; @@ -955,8 +954,6 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWWorld::Ptr ptr = R()(runtime); - std::string factionID = ""; if(arg0 >0 ) { @@ -965,6 +962,7 @@ namespace MWScript } else { + MWWorld::Ptr ptr = R()(runtime); if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) { factionID = ""; From aa855e9524115d27c0dda59c69a7d0166f95e396 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 20:56:24 +0100 Subject: [PATCH 196/251] Include some required Ogre headers explicitely --- apps/opencs/view/render/scenewidget.cpp | 1 + apps/openmw/mwrender/characterpreview.cpp | 1 + apps/openmw/mwrender/occlusionquery.cpp | 1 + apps/openmw/mwrender/shadows.cpp | 1 + apps/openmw/mwrender/water.cpp | 1 + components/nifogre/ogrenifloader.cpp | 4 +++- libs/openengine/ogre/renderer.cpp | 1 + 7 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index c8b37e9bb0..620586bd2d 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace CSVRender { diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index f7333db35c..2dbc72e264 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index a69511acd7..2461034717 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 21bbe51b63..9ebb0ab087 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 0a4db30e9c..9e3105168b 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "sky.hpp" #include "renderingmanager.hpp" diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 0c1fdfbee8..5a76b702eb 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -36,6 +35,9 @@ #include #include #include +#include +#include +#include #include diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 9e5ec5414d..c86697497f 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include From 546b0cee76477aff7409d90a6059d1591cad46e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 22:17:51 +0100 Subject: [PATCH 197/251] Closes #1077: Replace tags before trying to get the message length --- apps/openmw/mwgui/messagebox.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 8e518668bd..72a5bd0b07 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -63,7 +63,8 @@ namespace MWGui { MessageBox *box = new MessageBox(*this, message); box->mCurrentTime = 0; - box->mMaxTime = message.length()*mMessageBoxSpeed; + std::string realMessage = MyGUI::LanguageManager::getInstance().replaceTags(message); + box->mMaxTime = realMessage.length()*mMessageBoxSpeed; if(stat) mStaticMessageBox = box; From d41f27451b9bab6172c7a86ee5d9be025336a58d Mon Sep 17 00:00:00 2001 From: mrcheko Date: Thu, 9 Jan 2014 23:36:40 +0200 Subject: [PATCH 198/251] appropriate camera vanity<>preview mode switch + hit recoils fix --- apps/openmw/mwmechanics/character.cpp | 6 ++++++ apps/openmw/mwrender/animation.cpp | 11 ++++++----- apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/camera.cpp | 10 +++++----- apps/openmw/mwrender/npcanimation.cpp | 3 +++ 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index a4b4f24717..75fdc2550c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -179,6 +179,12 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mHitState = CharState_Hit; int iHit = rand() % (sHitListSize-1); mCurrentHit = sHitList[iHit]; + if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) + { + //only 4 different hit animations if player is in 1st person + int iHit = rand() % (sHitListSize-3); + mCurrentHit = sHitList[iHit]; + } mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8ce751286a..1d48683616 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -387,7 +387,6 @@ Ogre::Node *Animation::getNode(const std::string &name) return NULL; } - NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) { NifOgre::TextKeyMap::const_iterator iter(keys.begin()); @@ -1008,14 +1007,16 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) mSkelBase->detachObjectFromBone(obj); } -bool Animation::isPlaying(Group group) const +bool Animation::allowSwitchViewMode() const { for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) { - if(stateiter->second.mGroups == group) - return true; + if(stateiter->second.mGroups == Group_UpperBody + || (stateiter->first.size()==4 && stateiter->first.find("hit") != std::string::npos) + || (stateiter->first.find("knock") != std::string::npos) ) + return false; } - return false; + return true; } void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index f5f79dd72b..a682f39601 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -258,7 +258,7 @@ public: /** Returns true if the named animation group is playing. */ bool isPlaying(const std::string &groupname) const; - bool isPlaying(Group group) const; + bool allowSwitchViewMode() const; /** Gets info about the given animation group. * \param groupname Animation group to check. diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9a35725ee3..9babb0b08b 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -107,7 +107,7 @@ namespace MWRender void Camera::update(float duration, bool paused) { - if (!mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (mAnimation->allowSwitchViewMode()) { // Now process the view changes we queued earlier if (mVanityToggleQueued) @@ -144,7 +144,7 @@ namespace MWRender { // Changing the view will stop all playing animations, so if we are playing // anything important, queue the view change for later - if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (!mAnimation->allowSwitchViewMode()) { mViewModeToggleQueued = true; return; @@ -171,7 +171,7 @@ namespace MWRender { // Changing the view will stop all playing animations, so if we are playing // anything important, queue the view change for later - if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (!mPreviewMode) { mVanityToggleQueued = true; return false; @@ -205,7 +205,7 @@ namespace MWRender void Camera::togglePreviewMode(bool enable) { - if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody)) + if (mFirstPersonView && !mAnimation->allowSwitchViewMode()) return; if(mPreviewMode == enable) @@ -358,7 +358,7 @@ namespace MWRender Ogre::TagPoint *tag = mAnimation->attachObjectToBone("Head", mCamera); tag->setInheritOrientation(false); } - else + else { mAnimation->setViewMode(NpcAnimation::VM_Normal); mCameraNode->attachObject(mCamera); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b1455f0dc6..edb6688ff8 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -140,6 +140,9 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { assert(viewMode != VM_HeadOnly); + if(mViewMode == viewMode) + return; + mViewMode = viewMode; rebuild(); } From bfdca3b73816831d45e86c2e806462c04fab5be2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Jan 2014 23:13:31 +0100 Subject: [PATCH 199/251] Fix needTangents not being set for cached/shared materials --- components/nifogre/material.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index be6ccbed64..e2cc3712b6 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -277,6 +277,8 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, if (itr != sMaterialMap.end()) { // a suitable material exists already - use it + sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(itr->second); + needTangents = !sh::retrieveValue(instance->getProperty("normalMap"), instance).get().empty(); return itr->second; } // not found, create a new one From ffcb7fb280f737e1f678dbb8401b9316d643881b Mon Sep 17 00:00:00 2001 From: mrcheko Date: Fri, 10 Jan 2014 19:04:07 +0200 Subject: [PATCH 200/251] fix for bug scrawl mentioned --- apps/openmw/mwmechanics/character.cpp | 4 ++-- apps/openmw/mwrender/animation.hpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 75fdc2550c..019054af24 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -181,7 +181,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mCurrentHit = sHitList[iHit]; if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) { - //only 4 different hit animations if player is in 1st person + //only 3 different hit animations if player is in 1st person int iHit = rand() % (sHitListSize-3); mCurrentHit = sHitList[iHit]; } @@ -489,7 +489,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun const bool isWerewolf = stats.isWerewolf(); bool forcestateupdate = false; - if(weaptype != mWeaponType) + if(weaptype != mWeaponType && mHitState != CharState_KnockDown) { forcestateupdate = true; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index a682f39601..4ba68dd857 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -258,6 +258,7 @@ public: /** Returns true if the named animation group is playing. */ bool isPlaying(const std::string &groupname) const; + //Checks if playing any animation which shouldn't be stopped when switching camera view modes bool allowSwitchViewMode() const; /** Gets info about the given animation group. From 627dcddb333a2d8e881184bf1f6a1dd72d8d15b6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 20:53:53 +0100 Subject: [PATCH 201/251] Do not allow training skills above their governing attribute --- apps/openmw/mwgui/trainingwindow.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 709bc0fb95..24be5363d6 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -130,6 +130,14 @@ namespace MWGui return; } + // You can not train a skill above its governing attribute + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillId); + if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase()) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage17}"); + return; + } + // increase skill MWWorld::LiveCellRef *playerRef = player.get(); From f5a70dccf0f65269fa992503f9d809d8ba56f155 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 21:13:03 +0100 Subject: [PATCH 202/251] Fix title of buttons in generate class result dialog --- apps/openmw/mwgui/class.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 6c46f21763..7965669f19 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -29,11 +29,12 @@ namespace MWGui MyGUI::Button* backButton; getWidget(backButton, "BackButton"); + backButton->setCaptionWithReplacing("#{sMessageQuestionAnswer3}"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); MyGUI::Button* okButton; getWidget(okButton, "OKButton"); - okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", "")); + okButton->setCaptionWithReplacing("#{sMessageQuestionAnswer2}"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); } From 3bf36515d52dcd7cd632a37f29d603eedf662259 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 21:26:24 +0100 Subject: [PATCH 203/251] Implement Trespassing crime --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ .../mwmechanics/mechanicsmanagerimp.cpp | 26 ++++++++++++------- .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwmechanics/security.cpp | 3 +++ apps/openmw/mwmechanics/spellcasting.cpp | 12 +++++---- apps/openmw/mwmechanics/spellcasting.hpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- 7 files changed, 32 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 24e955cdf1..85fcfc75ba 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -112,6 +112,8 @@ namespace MWBase OffenseType type, int arg=0) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so + virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0; /// Attempt sleeping in a bed. If this is illegal, call commitCrime. /// @return was it illegal, and someone saw you doing it? virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 511fe65447..002cd561d6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -19,7 +19,7 @@ namespace { /// @return is \a ptr allowed to take/use \a item or is it a crime? - bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) + bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) { const std::string& owner = item.getCellRef().mOwner; bool isOwned = !owner.empty(); @@ -33,6 +33,9 @@ namespace isFactionOwned = true; } + if (!item.getCellRef().mOwner.empty()) + victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); + return (!isOwned && !isFactionOwned); } } @@ -752,11 +755,9 @@ namespace MWMechanics bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed) { - if (isAllowedToUse(ptr, bed)) - return false; MWWorld::Ptr victim; - if (!bed.getCellRef().mOwner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(bed.getCellRef().mOwner, true); + if (isAllowedToUse(ptr, bed, victim)) + return false; if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) { @@ -767,14 +768,19 @@ namespace MWMechanics return false; } + void MechanicsManager::objectOpened(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item) + { + MWWorld::Ptr victim; + if (isAllowedToUse(ptr, item, victim)) + return; + commitCrime(ptr, victim, OT_Trespassing); + } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) { - if (isAllowedToUse(ptr, item)) - return; MWWorld::Ptr victim; - if (!item.getCellRef().mOwner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); - + if (isAllowedToUse(ptr, item, victim)) + return; commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index cec08fa92d..569cd2fca0 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -113,6 +113,8 @@ namespace MWMechanics OffenseType type, int arg=0); /// Utility to check if taking this item is illegal and calling commitCrime if so virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so + virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item); /// Attempt sleeping in a bed. If this is illegal, call commitCrime. /// @return was it illegal, and someone saw you doing it? virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index 0769e13df5..2e5eaecfde 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" @@ -45,6 +46,7 @@ namespace MWMechanics resultMessage = "#{sLockImpossible}"; else { + MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, lock); int roll = static_cast (std::rand()) / RAND_MAX * 100; if (roll <= x) { @@ -86,6 +88,7 @@ namespace MWMechanics resultMessage = "#{sTrapImpossible}"; else { + MWBase::Environment::get().getMechanicsManager()->objectOpened(mActor, trap); int roll = static_cast (std::rand()) / RAND_MAX * 100; if (roll <= x) { diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a1cdb8b611..8c4c4d292e 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -4,7 +4,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" - +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/actionteleport.hpp" @@ -167,7 +167,7 @@ namespace MWMechanics } } else - applyInstantEffect(target, EffectKey(*effectIt), magnitude); + applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent. // This was probably just done to have the effect visible in the magic menu for a while @@ -177,7 +177,7 @@ namespace MWMechanics || effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute || effectIt->mEffectID == ESM::MagicEffect::RestoreSkill ) - applyInstantEffect(target, EffectKey(*effectIt), magnitude); + applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) { @@ -220,7 +220,7 @@ namespace MWMechanics mSourceName, caster.getRefData().getHandle()); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, MWMechanics::EffectKey effect, float magnitude) + void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude) { short effectId = effect.mId; if (!target.getClass().isActor()) @@ -232,11 +232,13 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Open) { - // TODO: This is a crime if (target.getCellRef().mLockLevel <= magnitude) { if (target.getCellRef().mLockLevel > 0) + { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); + MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); + } target.getCellRef().mLockLevel = 0; } else diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index a55c45fd16..438b65575f 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -203,7 +203,7 @@ namespace MWMechanics void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); - void applyInstantEffect (const MWWorld::Ptr& target, MWMechanics::EffectKey effect, float magnitude); + void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, MWMechanics::EffectKey effect, float magnitude); }; } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2aee74eeeb..b11c22c72d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -392,7 +392,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) // Apply instant effects MWMechanics::CastSpell cast(actor, actor); if (magnitude) - cast.applyInstantEffect(actor, effectIt->mEffectID, magnitude); + cast.applyInstantEffect(actor, actor, effectIt->mEffectID, magnitude); } if (magnitude) From 2744cde40fea2ff24f009740fd64512900b752b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:27:31 +0100 Subject: [PATCH 204/251] Use a few additional GMSTs --- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 5 ++--- apps/openmw/mwgui/messagebox.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- apps/openmw/mwmechanics/actors.cpp | 8 ++++++-- files/mygui/openmw_spell_buying_window.layout | 2 +- files/mygui/openmw_travel_window.layout | 4 ++-- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 4d79875a28..d9d2a2ea8a 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -257,7 +257,7 @@ namespace MWGui { if (mEffects.size() <= 0) { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage30}"); + MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu11}"); return; } diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 72a5bd0b07..b0abaef142 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -8,13 +8,12 @@ namespace MWGui { - MessageBoxManager::MessageBoxManager () + MessageBoxManager::MessageBoxManager (float timePerChar) { - // TODO: fMessageTimePerChar - mMessageBoxSpeed = 0.1; mInterMessageBoxe = NULL; mStaticMessageBox = NULL; mLastButtonPressed = -1; + mMessageBoxSpeed = timePerChar; } MessageBoxManager::~MessageBoxManager () diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 0288f366ce..e82a51642d 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -22,7 +22,7 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (); + MessageBoxManager (float timePerChar); ~MessageBoxManager (); void onFrame (float frameDuration); void createMessageBox (const std::string& message, bool stat = false); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3accd925f6..e4297f8fcd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -207,7 +207,8 @@ namespace MWGui mConsole = new Console(w,h, mConsoleOnlyScripts); trackWindow(mConsole, "console"); mJournal = JournalWindow::create(JournalViewModel::create ()); - mMessageBoxManager = new MessageBoxManager(); + mMessageBoxManager = new MessageBoxManager( + MWBase::Environment::get().getWorld()->getStore().get().find("fMessageTimePerChar")->getFloat()); mInventoryWindow = new InventoryWindow(mDragAndDrop); mTradeWindow = new TradeWindow(); trackWindow(mTradeWindow, "barter"); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0a4adb6e2f..afc94255bb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -581,7 +581,8 @@ namespace MWMechanics if(timeLeft == 0.0f) { // If drowning, apply 3 points of damage per second - ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration); + static const float fSuffocationDamage = world->getStore().get().find("fSuffocationDamage")->getFloat(); + ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - fSuffocationDamage*duration); // Play a drowning sound as necessary for the player if(ptr == world->getPlayerPtr()) @@ -593,7 +594,10 @@ namespace MWMechanics } } else - stats.setTimeToStartDrowning(20); + { + static const float fHoldBreathTime = world->getStore().get().find("fHoldBreathTime")->getFloat(); + stats.setTimeToStartDrowning(fHoldBreathTime); + } } void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout index 1510372ddb..b24a476c4c 100644 --- a/files/mygui/openmw_spell_buying_window.layout +++ b/files/mygui/openmw_spell_buying_window.layout @@ -11,7 +11,7 @@ - + diff --git a/files/mygui/openmw_travel_window.layout b/files/mygui/openmw_travel_window.layout index db3fa24a08..683d47fe71 100644 --- a/files/mygui/openmw_travel_window.layout +++ b/files/mygui/openmw_travel_window.layout @@ -11,7 +11,7 @@ - + @@ -32,4 +32,4 @@ - \ No newline at end of file + From 15e48107f7b5ee62e47be7d6d2ab17759395065e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:39:01 +0100 Subject: [PATCH 205/251] Use i1stPersonSneakDelta + some cleanup --- apps/openmw/mwmechanics/spellcasting.hpp | 12 ++++++------ apps/openmw/mwrender/camera.cpp | 5 ++--- apps/openmw/mwrender/camera.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 4 +++- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 438b65575f..a1ae254f61 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -19,12 +19,12 @@ namespace MWMechanics inline int spellSchoolToSkill(int school) { std::map schoolSkillMap; // maps spell school to skill id - schoolSkillMap[0] = 11; // alteration - schoolSkillMap[1] = 13; // conjuration - schoolSkillMap[3] = 12; // illusion - schoolSkillMap[2] = 10; // destruction - schoolSkillMap[4] = 14; // mysticism - schoolSkillMap[5] = 15; // restoration + schoolSkillMap[0] = ESM::Skill::Alteration; + schoolSkillMap[1] = ESM::Skill::Conjuration; + schoolSkillMap[3] = ESM::Skill::Illusion; + schoolSkillMap[2] = ESM::Skill::Destruction; + schoolSkillMap[4] = ESM::Skill::Mysticism; + schoolSkillMap[5] = ESM::Skill::Restoration; assert(schoolSkillMap.find(school) != schoolSkillMap.end()); return schoolSkillMap[school]; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index dda9797eff..d579c97933 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -226,11 +226,10 @@ namespace MWRender mCamera->setPosition(0.f, 0.f, offset); } - void Camera::setSneakOffset() + void Camera::setSneakOffset(float offset) { - // TODO: iFirstPersonSneakDelta if(mAnimation) - mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -9.8f)); + mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -offset)); } float Camera::getYaw() diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index d31d9e56c0..808f817cf5 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -87,7 +87,7 @@ namespace MWRender /// As animation is tied to the camera, this needs /// to be set each frame after the animation is /// applied. - void setSneakOffset(); + void setSneakOffset(float offset); bool isFirstPerson() const { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 11d06704e2..0c5e053c9e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -351,8 +351,10 @@ void RenderingManager::update (float duration, bool paused) bool isInAir = !world->isOnGround(player); bool isSwimming = world->isSwimming(player); + static const int i1stPersonSneakDelta = MWBase::Environment::get().getWorld()->getStore().get() + .find("i1stPersonSneakDelta")->getInt(); if(isSneaking && !(isSwimming || isInAir)) - mCamera->setSneakOffset(); + mCamera->setSneakOffset(i1stPersonSneakDelta); mOcclusionQuery->update(duration); From d9d6f376193700f51688251f354887cf07641222 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:44:40 +0100 Subject: [PATCH 206/251] Use iVoiceHitOdds --- apps/openmw/mwclass/npc.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 01fe61472a..1c5c302d80 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -615,7 +615,13 @@ namespace MWClass // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. - MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + int chance = store.get().find("iVoiceHitOdds")->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll < chance) + { + MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + } getCreatureStats(ptr).setAttacked(true);//used in CharacterController if(object.isEmpty()) From 621e52f09d47758cb41b3e081e0d2b4069e3b53b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:48:42 +0100 Subject: [PATCH 207/251] Play "attack" voiced dialogue entries randomly based on iVoiceAttackOdds. --- apps/openmw/mwmechanics/aicombat.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 32b0063b63..34b7217557 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -7,6 +7,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" @@ -118,6 +119,17 @@ namespace MWMechanics } if( mTimer > 1) { + 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"); + } + } + MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(true); mTimer = 0; } From e9f63270d95b85dc2aca449e64f6ba71a0eb9628 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Jan 2014 22:51:42 +0100 Subject: [PATCH 208/251] Speed fix for "on target" spells --- apps/openmw/mwworld/worldimp.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f61938f92b..738c71a7df 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2174,14 +2174,13 @@ namespace MWWorld Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - // TODO: Why -rot.z, but not -rot.x? + // TODO: Why -rot.z, but not -rot.x? (note: same issue in MovementSolver::move) Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); orient = orient * Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); - // This is just a guess, probably wrong - static float fProjectileMinSpeed = getStore().get().find("fProjectileMinSpeed")->getFloat(); - static float fProjectileMaxSpeed = getStore().get().find("fProjectileMaxSpeed")->getFloat(); - float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * it->second.mSpeed; + + static float fTargetSpellMaxSpeed = getStore().get().find("fTargetSpellMaxSpeed")->getFloat(); + float speed = fTargetSpellMaxSpeed * it->second.mSpeed; Ogre::Vector3 direction = orient.yAxis(); direction.normalise(); From d01f89b153921ecc8729ccf9777fbb8c3e785fd7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 00:24:21 +0100 Subject: [PATCH 209/251] Rewrite some awful code --- apps/openmw/mwbase/windowmanager.hpp | 3 --- apps/openmw/mwgui/container.cpp | 11 +++++++++ apps/openmw/mwgui/container.hpp | 1 + apps/openmw/mwgui/messagebox.cpp | 20 +++++++--------- apps/openmw/mwgui/messagebox.hpp | 3 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 11 --------- apps/openmw/mwgui/windowmanagerimp.hpp | 2 -- apps/openmw/mwinput/inputmanagerimp.cpp | 31 +------------------------ apps/openmw/mwinput/inputmanagerimp.hpp | 1 - 9 files changed, 22 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 6c85be5fd2..c39de44006 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -227,9 +227,6 @@ namespace MWBase virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false) = 0; virtual void staticMessageBox(const std::string& message) = 0; virtual void removeStaticMessageBox() = 0; - - virtual void enterPressed () = 0; - virtual void activateKeyPressed () = 0; virtual int readPressedButton() = 0; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bccc7120fb..d22842b575 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -139,6 +139,7 @@ namespace MWGui mDisposeCorpseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); + mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ContainerWindow::onKeyPressed); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); setCoord(200,0,600,300); @@ -234,11 +235,21 @@ namespace MWGui mItemView->setModel (mSortModel); + MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); + // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last // or we end up using a possibly invalid model. setTitle(MWWorld::Class::get(container).getName(container)); } + void ContainerWindow::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) + { + if (_key == MyGUI::KeyCode::Space) + onCloseButtonClicked(mCloseButton); + if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter) + onTakeAllButtonClicked(mTakeButton); + } + void ContainerWindow::close() { WindowBase::close(); diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index f934d8828a..ce4707af6b 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -75,6 +75,7 @@ namespace MWGui void onCloseButtonClicked(MyGUI::Widget* _sender); void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); + void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); /// @return is taking the item allowed? bool onTakeItem(const ItemStack& item, int count); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index b0abaef142..ae01f42931 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -127,12 +127,6 @@ namespace MWGui mMessageBoxSpeed = speed; } - void MessageBoxManager::okayPressed () - { - if(mInterMessageBoxe != NULL) - mInterMessageBoxe->okayPressed(); - } - int MessageBoxManager::readPressedButton () { int pressed = mLastButtonPressed; @@ -333,23 +327,25 @@ namespace MWGui } } - } - - void InteractiveMessageBox::okayPressed() - { + // Set key focus to "Ok" button std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); std::vector::const_iterator button; for(button = mButtons.begin(); button != mButtons.end(); ++button) { if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) { - buttonActivated(*button); - MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); + MyGUI::InputManager::getInstance().setKeyFocusWidget(*button); + (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); break; } } + } + void InteractiveMessageBox::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) + { + if (_key == MyGUI::KeyCode::Return || _key == MyGUI::KeyCode::NumpadEnter || _key == MyGUI::KeyCode::Space) + buttonActivated(_sender); } void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index e82a51642d..caa37008ca 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -33,7 +33,6 @@ namespace MWGui bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - void okayPressed(); int readPressedButton (); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; @@ -74,7 +73,6 @@ namespace MWGui { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); - void okayPressed (); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); @@ -82,6 +80,7 @@ namespace MWGui private: void buttonActivated (MyGUI::Widget* _widget); + void onKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char); MessageBoxManager& mMessageBoxManager; MyGUI::EditBox* mMessageWidget; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e4297f8fcd..cda146e8c0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -677,17 +677,6 @@ namespace MWGui mMessageBoxManager->removeStaticMessageBox(); } - void WindowManager::enterPressed () - { - mMessageBoxManager->okayPressed(); - } - - void WindowManager::activateKeyPressed () - { - mMessageBoxManager->okayPressed(); - mCountDialog->cancel(); - } - int WindowManager::readPressedButton () { return mMessageBoxManager->readPressedButton(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a3b135ad4e..9838a667f9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -222,8 +222,6 @@ namespace MWGui virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector(), bool showInDialogueModeOnly = false); virtual void staticMessageBox(const std::string& message); virtual void removeStaticMessageBox(); - virtual void enterPressed (); - virtual void activateKeyPressed (); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual void onFrame (float frameDuration); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c2efa0c331..f0feba89f8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -195,14 +195,7 @@ namespace MWInput case A_Activate: resetIdleTime(); - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container) - toggleContainer (); - else - MWBase::Environment::get().getWindowManager()->activateKeyPressed(); - } - else + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) activate(); break; case A_Journal: @@ -511,13 +504,6 @@ namespace MWInput mInputBinder->keyPressed (arg); - if((arg.keysym.sym == SDLK_RETURN || arg.keysym.sym == SDLK_KP_ENTER) - && MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - // Pressing enter when a messagebox is prompting for "ok" will activate the ok button - MWBase::Environment::get().getWindowManager()->enterPressed(); - } - OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); if (kc != OIS::KC_UNASSIGNED) @@ -730,21 +716,6 @@ namespace MWInput // .. but don't touch any other mode, except container. } - void InputManager::toggleContainer() - { - if (MyGUI::InputManager::getInstance ().isModalAny()) - return; - - if(MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container) - MWBase::Environment::get().getWindowManager()->popGuiMode(); - else - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container); - } - - } - void InputManager::toggleConsole() { if (MyGUI::InputManager::getInstance ().isModalAny()) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 4eaee9b690..d41b4c3f3b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -173,7 +173,6 @@ namespace MWInput void toggleSpell(); void toggleWeapon(); void toggleInventory(); - void toggleContainer(); void toggleConsole(); void screenshot(); void toggleJournal(); From c64dc2c8312d1177fdeb38b11adea7c776e40b9e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 00:51:03 +0100 Subject: [PATCH 210/251] Terrain specular mapping: use a "_diffusespec" postfix to indicate specular information is present in the alpha channel. Use alpha directly instead of 1-alpha. --- components/terrain/material.cpp | 3 +++ components/terrain/storage.cpp | 9 +++++++++ components/terrain/storage.hpp | 1 + files/materials/terrain.shader | 26 +++++++++++++------------- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 5dcdb7fed8..be468866ba 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -281,6 +281,7 @@ namespace Terrain // normal map (optional) bool useNormalMap = mNormalMapping && !mLayerList[layerOffset+i].mNormalMap.empty() && !renderCompositeMap; bool useParallax = useNormalMap && mParallaxMapping && layer.mParallax; + bool useSpecular = layer.mSpecular; if (useNormalMap) { anyNormalMaps = true; @@ -292,6 +293,8 @@ namespace Terrain sh::makeProperty (new sh::BooleanValue(useNormalMap))); p->mShaderProperties.setProperty ("use_parallax_" + Ogre::StringConverter::toString(i), sh::makeProperty (new sh::BooleanValue(useParallax))); + p->mShaderProperties.setProperty ("use_specular_" + Ogre::StringConverter::toString(i), + sh::makeProperty (new sh::BooleanValue(useSpecular))); boost::hash_combine(normalMaps, useNormalMap); boost::hash_combine(normalMaps, useNormalMap && layer.mParallax); diff --git a/components/terrain/storage.cpp b/components/terrain/storage.cpp index 9d6b44de8d..398ebac014 100644 --- a/components/terrain/storage.cpp +++ b/components/terrain/storage.cpp @@ -475,6 +475,7 @@ namespace Terrain LayerInfo info; info.mParallax = false; + info.mSpecular = false; info.mDiffuseMap = "textures\\" + texture; std::string texture_ = texture; boost::replace_last(texture_, ".", "_nh."); @@ -491,6 +492,14 @@ namespace Terrain info.mNormalMap = "textures\\" + texture_; } + texture_ = texture; + boost::replace_last(texture_, ".", "_diffusespec."); + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_)) + { + info.mDiffuseMap = "textures\\" + texture_; + info.mSpecular = true; + } + mLayerInfoMap[texture] = info; return info; diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 68fae01afa..18d05b100c 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -16,6 +16,7 @@ namespace Terrain std::string mDiffuseMap; std::string mNormalMap; bool mParallax; // Height info in normal map alpha channel? + bool mSpecular; // Specular info in diffuse map alpha channel? }; /// We keep storage of terrain data abstract here since we need different implementations for game and editor diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 86eef36ffa..1436de0c35 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -337,6 +337,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; float2 layerUV = float2(UV.x, 1.f-UV.y) * 16; // Reverse Y, required to get proper tangents float2 thisLayerUV; float4 normalTex; + float4 diffuseTex; float3 eyeDir = normalize(cameraPos.xyz - worldPos); #if PARALLAX @@ -358,19 +359,18 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS ); #endif -#if IS_FIRST_PASS - #if @shIterator == 0 - // first layer of first pass is the base layer and doesn't need a blend map - albedo = shSample(diffuseMap0, layerUV); - #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, thisLayerUV), blendValues@shPropertyString(blendmap_component_@shIterator)); - #endif + diffuseTex = shSample(diffuseMap@shIterator, layerUV); +#if !@shPropertyBool(use_specular_@shIterator) + diffuseTex.a = 0; +#endif + +#if @shIterator == 0 +albedo = diffuseTex; #else - #if @shIterator == 0 - albedo = shSample(diffuseMap@shIterator, layerUV); - #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, thisLayerUV), blendValues@shPropertyString(blendmap_component_@shIterator)); - #endif +albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_component_@shIterator)); +#endif + +#if !IS_FIRST_PASS previousAlpha *= 1.f-blendValues@shPropertyString(blendmap_component_@shIterator); #endif @@ -448,7 +448,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5; float3 halfVec = normalize (light0Dir + eyeDir); float3 specular = pow(max(dot(normal, halfVec), 0), 32) * lightSpec0; - shOutputColour(0).xyz += specular * (1.f-albedo.a) * shadow; + shOutputColour(0).xyz += specular * (albedo.a) * shadow; #endif #if FOG From 727aa30347a345e7a61bebffa3e05d94586622ea Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 02:06:54 +0100 Subject: [PATCH 211/251] Dead actors are unaware of everything --- 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 002cd561d6..2ce196fdd7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -857,6 +857,9 @@ namespace MWMechanics bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) { + if (observer.getClass().getCreatureStats(observer).isDead()) + return false; + const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); From d4a98ffc270e6e19504c4da1615bf26d4b352ef6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 02:27:44 +0100 Subject: [PATCH 212/251] Increase fight rating when being the victim of a crime --- .../mwmechanics/mechanicsmanagerimp.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2ce196fdd7..57b8767239 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -840,6 +840,25 @@ namespace MWMechanics ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + arg); + if (!victim.isEmpty()) + { + int fight = 0; + // Increase in fight rating for each type of crime + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + fight = store.find("iFightTrespass")->getFloat(); + else if (type == OT_Pickpocket) + fight = store.find("iFightPickpocket")->getInt(); + else if (type == OT_Assault) + fight = store.find("iFightAttack")->getInt(); + else if (type == OT_Murder) + fight = store.find("iFightKilling")->getInt(); + else if (type == OT_Theft) + fight = store.find("fFightStealing")->getFloat(); + // Not sure if this should be permanent? + fight = victim.getClass().getCreatureStats(victim).getAiSetting(CreatureStats::AI_Fight).getBase() + fight; + victim.getClass().getCreatureStats(victim).setAiSetting(CreatureStats::AI_Fight, fight); + } + // If committing a crime against a faction member, expell from the faction if (!victim.isEmpty() && victim.getClass().isNpc()) { From 12944f2459a13296b71ee4c9ea2bafac2d549256 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:07:35 +0100 Subject: [PATCH 213/251] Fix an auto equipping bug that allowed twohanded weapon and shield at the same time --- apps/openmw/mwworld/inventorystore.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index b11c22c72d..3e81da2122 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -165,7 +165,6 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { const MWMechanics::NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); TSlots slots_; initSlots (slots_); @@ -266,10 +265,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) case 0: continue; case 2: - invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, actor); + slots_[MWWorld::InventoryStore::Slot_CarriedLeft] = end(); break; case 3: - invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor); + // Prefer keeping twohanded weapon break; } From 909494ff3524a93a450d63656fe450dd863cdda5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:08:16 +0100 Subject: [PATCH 214/251] Implement Assault crimes. In other words, NPCs now fight back! --- apps/openmw/mwclass/npc.cpp | 4 ++++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 11 ++++++----- apps/openmw/mwmechanics/spellcasting.cpp | 7 +++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 1c5c302d80..68b7d41f5c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -587,6 +587,10 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. + // Attacking peaceful NPCs is a crime + if (ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); + if(!successful) { // TODO: Handle HitAttemptOnMe script function diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 57b8767239..901b6e4144 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -786,7 +786,8 @@ namespace MWMechanics bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { - // TODO: expell from faction + if (ptr.getRefData().getHandle() != "player") + return false; bool reported=false; for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) @@ -803,10 +804,7 @@ namespace MWMechanics // Actor has witnessed a crime. Will he report it? // (not sure, is > 0 correct?) - if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0 - // This is a bit inconsistent, but AFAIK assaulted NPCs can not report if they are alone - && (type != OT_Assault || it->first != victim) - ) + if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0) { // TODO: stats.setAlarmed(true) on NPCs within earshot // fAlarmRadius ? @@ -836,6 +834,9 @@ namespace MWMechanics else if (type == OT_Theft) arg *= store.find("fCrimeStealing")->getFloat(); + // TODO: In some cases (type == Assault), if no NPCs are within earshot, the report will have no effect. + // however other crime types seem to be always produce a bounty. + MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + arg); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 8c4c4d292e..b8c36d2138 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -57,6 +57,7 @@ namespace MWMechanics ESM::EffectList reflectedEffects; std::vector appliedLastingEffects; bool firstAppliedEffect = true; + bool anyHarmfulEffect = false; for (std::vector::const_iterator effectIt (effects.mList.begin()); effectIt!=effects.mList.end(); ++effectIt) @@ -77,6 +78,8 @@ namespace MWMechanics float magnitudeMult = 1; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) { + anyHarmfulEffect = true; + // If player is attempting to cast a harmful spell, show the target's HP bar if (caster.getRefData().getHandle() == "player" && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); @@ -218,6 +221,10 @@ namespace MWMechanics if (appliedLastingEffects.size()) target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, caster.getRefData().getHandle()); + + if (anyHarmfulEffect && target.getClass().isActor() + && target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); } void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, MWMechanics::EffectKey effect, float magnitude) From bf6d302fbadbfdf3399e12a2fbfaca6d06406bec Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:29:41 +0100 Subject: [PATCH 215/251] Confiscate stolen items when caught --- apps/openmw/mwbase/world.hpp | 3 ++ apps/openmw/mwscript/miscextensions.cpp | 5 ++-- apps/openmw/mwworld/cells.cpp | 12 ++++++++ apps/openmw/mwworld/cells.hpp | 6 ++++ apps/openmw/mwworld/worldimp.cpp | 40 +++++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 ++ 6 files changed, 66 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 43e526ecba..10e25b376b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -451,6 +451,9 @@ namespace MWBase /// Update the value of some globals according to the world state, which may be used by dialogue entries. /// This should be called when initiating a dialogue. virtual void updateDialogueGlobals() = 0; + + /// Moves all stolen items from \a ptr to the closest evidence chest. + virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0; }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a403d76edf..f69f2e7cb0 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -769,8 +769,8 @@ namespace MWScript MWWorld::Ptr player = world->getPlayerPtr(); world->teleportToClosestMarker(player, "prisonmarker"); player.getClass().getNpcStats(player).setBounty(0); + world->confiscateStolenItems(player); // TODO: pass time, change skills, show messagebox - // TODO: move stolen items to closest evidence chest // iDaysinPrisonMod } }; @@ -782,8 +782,7 @@ namespace MWScript { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); - - // TODO: move stolen items to closest evidence chest + MWBase::Environment::get().getWorld()->confiscateStolenItems(player); } }; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 621ff3b313..575e10f049 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -271,3 +271,15 @@ void MWWorld::Cells::getExteriorPtrs(const std::string &name, std::vector &out) +{ + for (std::map::iterator iter = mInteriors.begin(); + iter!=mInteriors.end(); ++iter) + { + Ptr ptr = getPtrAndCache (name, iter->second); + if (!ptr.isEmpty()) + out.push_back(ptr); + } + +} diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 31de2f60e8..afa7c03f28 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -56,6 +56,12 @@ namespace MWWorld /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. /// @note name must be lower case void getExteriorPtrs (const std::string& name, std::vector& out); + + /// Get all Ptrs referencing \a name in interior cells + /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. + /// @note name must be lower case + void getInteriorPtrs (const std::string& name, std::vector& out); + }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 738c71a7df..c5315afe66 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2446,4 +2446,44 @@ namespace MWWorld mGlobalVariables->setInt("crimegoldturnin", turnIn); mGlobalVariables->setInt("pchasturnin", (turnIn <= playerGold) ? 1 : 0); } + + void World::confiscateStolenItems(const Ptr &ptr) + { + Ogre::Vector3 playerPos; + if (!findInteriorPositionInWorldSpace(ptr.getCell(), playerPos)) + playerPos = mPlayer->getLastKnownExteriorPosition(); + + MWWorld::Ptr closestChest; + float closestDistance = FLT_MAX; + + std::vector chests; + mCells.getInteriorPtrs("stolen_goods", chests); + + Ogre::Vector3 chestPos; + for (std::vector::iterator it = chests.begin(); it != chests.end(); ++it) + { + if (!findInteriorPositionInWorldSpace(it->getCell(), chestPos)) + continue; + + float distance = playerPos.squaredDistance(chestPos); + if (distance < closestDistance) + { + closestDistance = distance; + closestChest = *it; + } + } + + if (!closestChest.isEmpty()) + { + ContainerStore& store = ptr.getClass().getContainerStore(ptr); + for (ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (!it->getCellRef().mOwner.empty() && it->getCellRef().mOwner != "player") + { + closestChest.getClass().getContainerStore(closestChest).add(*it, it->getRefData().getCount(), closestChest); + store.remove(*it, it->getRefData().getCount(), ptr); + } + } + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index cc087a89f1..8c091de50f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -536,6 +536,9 @@ namespace MWWorld /// Update the value of some globals according to the world state, which may be used by dialogue entries. /// This should be called when initiating a dialogue. virtual void updateDialogueGlobals(); + + /// Moves all stolen items from \a ptr to the closest evidence chest. + virtual void confiscateStolenItems(const MWWorld::Ptr& ptr); }; } From 9127839cc1b9e2c71aede268b1e2c0a1220a019a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 03:33:17 +0100 Subject: [PATCH 216/251] Add a searchPtr method as required for getting an owner, which may already be dead and disposed of. --- apps/openmw/mwbase/world.hpp | 4 ++++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 15 ++++++++++----- apps/openmw/mwworld/worldimp.hpp | 4 ++++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 10e25b376b..2fb5f02eb6 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -157,6 +157,10 @@ namespace MWBase ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. + virtual MWWorld::Ptr searchPtr (const std::string& name, bool activeOnly) = 0; + ///< Return a pointer to a liveCellRef with the given name. + /// \param activeOnly do non search inactive cells. + virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0; ///< Return a pointer to a liveCellRef with the given Ogre handle. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 901b6e4144..76ba2ff16c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -34,7 +34,7 @@ namespace } if (!item.getCellRef().mOwner.empty()) - victim = MWBase::Environment::get().getWorld()->getPtr(item.getCellRef().mOwner, true); + victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().mOwner, true); return (!isOwned && !isFactionOwned); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c5315afe66..ca85d7a7f4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -485,8 +485,9 @@ namespace MWWorld mLocalScripts.remove (ref); } - Ptr World::getPtr (const std::string& name, bool activeOnly) + Ptr World::searchPtr (const std::string& name, bool activeOnly) { + Ptr ret; // the player is always in an active cell. if (name=="player") { @@ -514,12 +515,16 @@ namespace MWWorld if (!activeOnly) { - Ptr ptr = mCells.getPtr (lowerCaseName); - - if (!ptr.isEmpty()) - return ptr; + ret = mCells.getPtr (lowerCaseName); } + return ret; + } + Ptr World::getPtr (const std::string& name, bool activeOnly) + { + Ptr ret = searchPtr(name, activeOnly); + if (!ret.isEmpty()) + return ret; throw std::runtime_error ("unknown ID: " + name); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8c091de50f..d5ccd7625d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -232,6 +232,10 @@ namespace MWWorld ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. + virtual Ptr searchPtr (const std::string& name, bool activeOnly); + ///< Return a pointer to a liveCellRef with the given name. + /// \param activeOnly do non search inactive cells. + virtual Ptr getPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle. From 9ddee8fd8c6b00e7ff5f4904118056b02bdf30d2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 04:03:13 +0100 Subject: [PATCH 217/251] Autocalculate NPC reputation as according to research wiki --- apps/openmw/mwclass/npc.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 68b7d41f5c..12ed2dd09b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -307,6 +307,17 @@ namespace MWClass autoCalculateSkills(ref->mBase, data->mNpcStats); } + if (data->mNpcStats.getFactionRanks().size()) + { + static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get() + .find("iAutoRepFacMod")->getInt(); + static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get() + .find("iAutoRepLevMod")->getInt(); + int rank = data->mNpcStats.getFactionRanks().begin()->second; + + data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); + } + data->mNpcStats.getAiSequence().fill(ref->mBase->mAiPackage); data->mNpcStats.setAiSetting (MWMechanics::CreatureStats::AI_Hello, ref->mBase->mAiData.mHello); From ce6aab89cf9d7dde54f994aba21c7fda228ff269 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 04:58:30 +0100 Subject: [PATCH 218/251] Fix a possible permutation issue --- components/terrain/material.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index be468866ba..8e78d22166 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -297,6 +297,7 @@ namespace Terrain sh::makeProperty (new sh::BooleanValue(useSpecular))); boost::hash_combine(normalMaps, useNormalMap); boost::hash_combine(normalMaps, useNormalMap && layer.mParallax); + boost::hash_combine(normalMaps, useSpecular); if (i+layerOffset > 0) { From 3896c88403c71cfef013fa9cc713fe5a8a910910 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 05:58:05 +0100 Subject: [PATCH 219/251] Use VFX_DefaultCast / VFX_DefaultHit if the magic effect does not specify any --- apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 7 ++++++- apps/openmw/mwmechanics/spellcasting.cpp | 18 ++++++++++-------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index afc94255bb..468a218927 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -525,7 +525,7 @@ namespace MWMechanics ref.getPtr().getCellRef().mPos = ipos; // TODO: Add AI to follow player and fight for him - + // TODO: VFX_SummonStart, VFX_SummonEnd creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 99067a6b72..fed3f485f2 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -592,7 +592,12 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun const ESM::MagicEffect *effect; effect = store.get().find(effectentry.mEffectID); - const ESM::Static* castStatic = store.get().find (effect->mCasting); + const ESM::Static* castStatic; + if (!effect->mCasting.empty()) + castStatic = store.get().find (effect->mCasting); + else + castStatic = store.get().find ("VFX_DefaultCast"); + mAnimation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex); castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Hands"); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index b8c36d2138..850c4dcf51 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -200,15 +200,17 @@ namespace MWMechanics } // Add VFX + const ESM::Static* castStatic; if (!magicEffect->mHit.empty()) - { - const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); - bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx; - // Note: in case of non actor, a free effect should be fine as well - MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); - if (anim) - anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); - } + castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); + else + castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_DefaultHit"); + + bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx; + // Note: in case of non actor, a free effect should be fine as well + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); + if (anim) + anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); } // TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World. From a2ba0dde31036bb45646488a6d4bc6348c65a443 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 06:47:58 +0100 Subject: [PATCH 220/251] Implemented GoToJail --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwscript/miscextensions.cpp | 7 +-- apps/openmw/mwworld/worldimp.cpp | 79 ++++++++++++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 3 + 4 files changed, 84 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 2fb5f02eb6..fe40fab24f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -458,6 +458,8 @@ namespace MWBase /// Moves all stolen items from \a ptr to the closest evidence chest. virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0; + + virtual void goToJail () = 0; }; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index f69f2e7cb0..bb3600a272 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -766,12 +766,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayerPtr(); - world->teleportToClosestMarker(player, "prisonmarker"); - player.getClass().getNpcStats(player).setBounty(0); - world->confiscateStolenItems(player); - // TODO: pass time, change skills, show messagebox - // iDaysinPrisonMod + world->goToJail(); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ca85d7a7f4..620058c65f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -216,7 +216,7 @@ namespace MWWorld mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (mActivationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(false), - mFacedDistance(FLT_MAX), mGodMode(false) + mFacedDistance(FLT_MAX), mGodMode(false), mGoToJail(false) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -259,6 +259,9 @@ namespace MWWorld void World::startNewGame() { + mGoToJail = false; + mLevitationEnabled = true; + mTeleportEnabled = true; mWorldScene->changeToVoid(); mStore.clearDynamic(); @@ -1272,6 +1275,9 @@ namespace MWWorld mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); } + if (mGoToJail && !paused) + goToJail(); + updateWeather(duration); mWorldScene->update (duration, paused); @@ -2491,4 +2497,75 @@ namespace MWWorld } } } + + void World::goToJail() + { + if (!mGoToJail) + { + // Save for next update, since the player should be able to read the dialog text first + mGoToJail = true; + return; + } + else + { + mGoToJail = false; + + MWWorld::Ptr player = getPlayerPtr(); + teleportToClosestMarker(player, "prisonmarker"); + int bounty = player.getClass().getNpcStats(player).getBounty(); + player.getClass().getNpcStats(player).setBounty(0); + confiscateStolenItems(player); + + int iDaysinPrisonMod = getStore().get().find("iDaysinPrisonMod")->getInt(); + int days = std::max(1, bounty / iDaysinPrisonMod); + + advanceTime(days * 24); + + std::set skills; + for (int day=0; day (RAND_MAX) + 1) * ESM::Skill::Length; + skills.insert(skill); + + MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); + if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) + value.setBase(std::min(100, value.getBase()+1)); + else + value.setBase(value.getBase()-1); + } + + const Store& gmst = getStore().get(); + + std::string message; + if (days == 1) + message = gmst.find("sNotifyMessage42")->getString(); + else + message = gmst.find("sNotifyMessage43")->getString(); + + std::stringstream dayStr; + dayStr << days; + if (message.find("%d") != std::string::npos) + message.replace(message.find("%d"), 2, dayStr.str()); + + for (std::set::iterator it = skills.begin(); it != skills.end(); ++it) + { + std::string skillName = gmst.find(ESM::Skill::sSkillNameIds[*it])->getString(); + std::stringstream skillValue; + skillValue << player.getClass().getNpcStats(player).getSkill(*it).getBase(); + std::string skillMsg = gmst.find("sNotifyMessage44")->getString(); + if (*it == ESM::Skill::Sneak || *it == ESM::Skill::Security) + skillMsg = gmst.find("sNotifyMessage39")->getString(); + + if (skillMsg.find("%s") != std::string::npos) + skillMsg.replace(skillMsg.find("%s"), 2, skillName); + if (skillMsg.find("%d") != std::string::npos) + skillMsg.replace(skillMsg.find("%d"), 2, skillValue.str()); + message += "\n" + skillMsg; + } + + std::vector buttons; + buttons.push_back("#{sOk}"); + MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d5ccd7625d..634cc8d6b6 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -151,6 +151,7 @@ namespace MWWorld bool mTeleportEnabled; bool mLevitationEnabled; + bool mGoToJail; /// Called when \a object is moved to an inactive cell void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr); @@ -543,6 +544,8 @@ namespace MWWorld /// Moves all stolen items from \a ptr to the closest evidence chest. virtual void confiscateStolenItems(const MWWorld::Ptr& ptr); + + virtual void goToJail (); }; } From 828e5841d86ce3b4afed7b2637913981ff662883 Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 11 Jan 2014 11:00:34 +0100 Subject: [PATCH 221/251] Windows SDL fix --- apps/launcher/main.cpp | 1 + apps/openmw/engine.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 156bbf65bc..fabf77d901 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -16,6 +16,7 @@ int main(int argc, char *argv[]) { SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + SDL_SetMainReady(); if (SDL_Init(SDL_INIT_VIDEO) != 0) { qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError()); diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b59b6a2fde..5d04b985fd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -157,6 +157,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) //kindly ask SDL not to trash our OGL context //might this be related to http://bugzilla.libsdl.org/show_bug.cgi?id=748 ? SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + SDL_SetMainReady(); if(SDL_Init(flags) != 0) { throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError())); From 0609b71df9daf2a1248889c15617d99b12c71bdd Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 11 Jan 2014 11:57:07 +0100 Subject: [PATCH 222/251] fix pathfinding --- apps/openmw/mwmechanics/pathfinding.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 18e66d2280..c8bc9b49cf 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -150,6 +150,7 @@ namespace MWMechanics void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const ESM::Pathgrid *pathGrid, float xCell, float yCell, bool allowShortcuts) { + mPath.clear(); if(allowShortcuts) { if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, From 3db299f1b2c8074d9febbe257066f1c610bf617d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 21:04:31 +0100 Subject: [PATCH 223/251] Fix fall damage crash --- apps/openmw/mwclass/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 12ed2dd09b..41c6de40cc 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -599,7 +599,7 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. // Attacking peaceful NPCs is a crime - if (ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); if(!successful) From 224f288359bfc6a523fdcfc4d338ecf47574096c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 21:30:09 +0100 Subject: [PATCH 224/251] Don't attempt to change disposition for creatures --- apps/openmw/mwgui/tradewindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 1cd4c1c7c0..14024dec6b 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -318,7 +318,8 @@ namespace MWGui messageBox("#{sNotifyMessage9}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition); + if (mPtr.getClass().isNpc()) + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterFailDisposition); return; } @@ -327,7 +328,8 @@ namespace MWGui } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); - MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); + if (mPtr.getClass().isNpc()) + MWBase::Environment::get().getDialogueManager()->applyDispositionChange(iBarterSuccessDisposition); // make the item transfer mTradeModel->transferItems(); From dddc0979a294aa516aeb21ab4c01ac29f04315e4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 22:04:46 +0100 Subject: [PATCH 225/251] Fix another fatigue cap issue --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 850c4dcf51..de078f9efd 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -437,7 +437,7 @@ namespace MWMechanics DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = mCaster.getClass().getEncumbrance(mCaster) / mCaster.getClass().getCapacity(mCaster); float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult); - fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss)); + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue); bool fail = false; From 44b2380874007c7dfb29b1b4c9d1a85fab6bfed5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 22:26:26 +0100 Subject: [PATCH 226/251] Closes #947: Decrease fatigue when running, swimming and attacking --- apps/openmw/mwclass/npc.cpp | 14 +++++++++ apps/openmw/mwmechanics/character.cpp | 37 ++++++++++++++++++++++-- apps/openmw/mwmechanics/spellcasting.cpp | 3 +- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 41c6de40cc..dc219f3730 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -436,6 +436,20 @@ namespace MWClass if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) weapon = MWWorld::Ptr(); + // 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(); + MWMechanics::DynamicStat fatigue = getCreatureStats(ptr).getFatigue(); + const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); + float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; + if (!weapon.isEmpty()) + fatigueLoss += weapon.getClass().getWeight(weapon) * getNpcStats(ptr).getAttackStrength() * fWeaponFatigueMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + getCreatureStats(ptr).setFatigue(fatigue); + + float dist = 100.0f * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b29ae29144..0856ada0c7 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -900,6 +900,41 @@ void CharacterController::update(float duration) } } + // reduce fatigue + const MWWorld::Store &gmst = world->getStore().get(); + float fatigueLoss = 0; + static const float fFatigueRunBase = gmst.find("fFatigueRunBase")->getFloat(); + static const float fFatigueRunMult = gmst.find("fFatigueRunMult")->getFloat(); + static const float fFatigueSwimWalkBase = gmst.find("fFatigueSwimWalkBase")->getFloat(); + static const float fFatigueSwimRunBase = gmst.find("fFatigueSwimRunBase")->getFloat(); + static const float fFatigueSwimWalkMult = gmst.find("fFatigueSwimWalkMult")->getFloat(); + static const float fFatigueSwimRunMult = gmst.find("fFatigueSwimRunMult")->getFloat(); + static const float fFatigueSneakBase = gmst.find("fFatigueSneakBase")->getFloat(); + static const float fFatigueSneakMult = gmst.find("fFatigueSneakMult")->getFloat(); + + const float encumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr); + if (encumbrance < 1) + { + if (sneak) + fatigueLoss = fFatigueSneakBase + encumbrance * fFatigueSneakMult; + else + { + if (inwater) + { + if (!isrunning) + fatigueLoss = fFatigueSwimWalkBase + encumbrance * fFatigueSwimWalkMult; + else + fatigueLoss = fFatigueSwimRunBase + encumbrance * fFatigueSwimRunMult; + } + if (isrunning) + fatigueLoss = fFatigueRunBase + encumbrance * fFatigueRunMult; + } + } + fatigueLoss *= duration; + DynamicStat fatigue = cls.getCreatureStats(mPtr).getFatigue(); + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0); + cls.getCreatureStats(mPtr).setFatigue(fatigue); + if(sneak || inwater || flying) vec.z = 0.0f; @@ -916,8 +951,6 @@ void CharacterController::update(float duration) cls.getCreatureStats(mPtr).land(); } - const MWWorld::Store &gmst = world->getStore().get(); - forcestateupdate = (mJumpState != JumpState_Falling); mJumpState = JumpState_Falling; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index de078f9efd..be67b65928 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -437,8 +437,7 @@ namespace MWMechanics DynamicStat fatigue = stats.getFatigue(); const float normalizedEncumbrance = mCaster.getClass().getEncumbrance(mCaster) / mCaster.getClass().getCapacity(mCaster); float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult); - fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); - stats.setFatigue(fatigue); + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue); bool fail = false; From 10ddea45e9f66a03b957608e2aef9f90ec2c1ecf Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Jan 2014 23:11:23 +0100 Subject: [PATCH 227/251] Move crime from onHit to hit, since failed hits are apparently also a crime --- apps/openmw/mwclass/npc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index dc219f3730..86f0fbc3bf 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -468,6 +468,10 @@ namespace MWClass if(ptr.getRefData().getHandle() == "player") MWBase::Environment::get().getWindowManager()->setEnemy(victim); + // Attacking peaceful NPCs is a crime + if (victim.getClass().isNpc() && victim.getClass().getCreatureStats(victim).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + MWBase::Environment::get().getMechanicsManager()->commitCrime(ptr, victim, MWBase::MechanicsManager::OT_Assault); + int weapskill = ESM::Skill::HandToHand; if(!weapon.isEmpty()) weapskill = get(weapon).getEquipmentSkill(weapon); @@ -612,10 +616,6 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. - // Attacking peaceful NPCs is a crime - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) - MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); - if(!successful) { // TODO: Handle HitAttemptOnMe script function From 921ef6cd9c7d36a15bf38dcd5ff2bac005c81dae Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 00:42:27 +0100 Subject: [PATCH 228/251] Closes #1093: Show weapon when initializing the character controller with a weapon equipped --- 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 0856ada0c7..e5236f02b4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -441,6 +441,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim { getWeaponGroup(mWeaponType, mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; + mAnimation->showWeapons(true); } } From 1d19d36bd6a14e55a7ea90c155f386ebf235d4a4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 01:20:37 +0100 Subject: [PATCH 229/251] Remove unused magic effect flags and update esmtool output --- apps/esmtool/labels.cpp | 39 +++++++++-------------------- apps/openmw/mwmechanics/alchemy.cpp | 2 +- components/esm/loadmgef.hpp | 6 +---- 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 7b1fc7fb21..56c9a2fadb 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -764,34 +764,19 @@ std::string magicEffectFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; - // Enchanting & SpellMaking occur on the same list of effects. - // "EXTRA SPELL" appears in the construction set under both the - // spell making and enchanting tabs as an allowed effect. Since - // most of the effects without this flags are defective in various - // ways, it's still very unclear what these flag bits are. - if (flags & ESM::MagicEffect::SpellMaking) properties += "SpellMaking "; - if (flags & ESM::MagicEffect::Enchanting) properties += "Enchanting "; - if (flags & 0x00000040) properties += "RangeNoSelf "; - if (flags & 0x00000080) properties += "RangeTouch "; - if (flags & 0x00000100) properties += "RangeTarget "; - if (flags & 0x00001000) properties += "Unknown2 "; - if (flags & 0x00000001) properties += "AffectSkill "; - if (flags & 0x00000002) properties += "AffectAttribute "; + if (flags & ESM::MagicEffect::TargetAttribute) properties += "TargetAttribute "; + if (flags & ESM::MagicEffect::TargetSkill) properties += "TargetSkill "; if (flags & ESM::MagicEffect::NoDuration) properties += "NoDuration "; - if (flags & 0x00000008) properties += "NoMagnitude "; - if (flags & 0x00000010) properties += "Negative "; - if (flags & 0x00000020) properties += "Unknown1 "; - // ESM componet says 0x800 is negative, but none of the magic - // effects have this flags set. - if (flags & ESM::MagicEffect::Negative) properties += "Unused "; - // Since only Chameleon has this flag it could be anything - // that uniquely distinguishes Chameleon. - if (flags & 0x00002000) properties += "Chameleon "; - if (flags & 0x00004000) properties += "Bound "; - if (flags & 0x00008000) properties += "Summon "; - // Calm, Demoralize, Frenzy, Lock, Open, Rally, Soultrap, Turn Unded - if (flags & 0x00010000) properties += "Unknown3 "; - if (flags & 0x00020000) properties += "Absorb "; + if (flags & ESM::MagicEffect::NoMagnitude) properties += "NoMagnitude "; + if (flags & ESM::MagicEffect::Harmful) properties += "Harmful "; + if (flags & ESM::MagicEffect::ContinuousVfx) properties += "ContinuousVFX "; + if (flags & ESM::MagicEffect::CastSelf) properties += "CastSelf "; + if (flags & ESM::MagicEffect::CastTouch) properties += "CastTouch "; + if (flags & ESM::MagicEffect::CastTarget) properties += "CastTarget "; + if (flags & ESM::MagicEffect::UncappedDamage) properties += "UncappedDamage "; + if (flags & ESM::MagicEffect::NonRecastable) properties += "NonRecastable "; + if (flags & ESM::MagicEffect::Unreflectable) properties += "Unreflectable "; + if (flags & ESM::MagicEffect::CasterLinked) properties += "CasterLinked "; if (flags & 0xFFFC0000) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index f994c28b84..af58e9ee0b 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -62,7 +62,7 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const { bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude); bool duration = !(flags & ESM::MagicEffect::NoDuration); - bool negative = flags & (ESM::MagicEffect::Negative | ESM::MagicEffect::Harmful); + bool negative = flags & (ESM::MagicEffect::Harmful); int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic; diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 77056b9ec6..8281f4969d 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -28,11 +28,7 @@ struct MagicEffect UncappedDamage = 0x1000, // Negates multiple cap behaviours. Allows an effect to reduce an attribute below zero; removes the normal minimum effect duration of 1 second. NonRecastable = 0x4000, // Does not land if parent spell is already affecting target. Shows "you cannot re-cast" message for self target. Unreflectable = 0x10000, // Cannot be reflected, the effect always lands normally. - CasterLinked = 0x20000, // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells. - SpellMaking = 0x0200, - Enchanting = 0x0400, - Negative = 0x0800 // A harmful effect. Will determine whether - // eg. NPCs regard this spell as an attack. (same as 0x10?) + CasterLinked = 0x20000 // Must quench if caster is dead, or not an NPC/creature. Not allowed in containter/door trap spells. }; enum MagnitudeDisplayType { From fb778f8ecd76a703e0da6c2aa260d142f63d4490 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 04:09:51 +0100 Subject: [PATCH 230/251] Use fEncumbranceStrMult --- apps/openmw/mwclass/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 86f0fbc3bf..4aa27e38a3 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1055,7 +1055,8 @@ namespace MWClass float Npc::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return stats.getAttribute(0).getModified()*5; + static const float fEncumbranceStrMult = MWBase::Environment::get().getWorld()->getStore().get().find("fEncumbranceStrMult")->getFloat(); + return stats.getAttribute(0).getModified()*fEncumbranceStrMult; } float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const From 767c72e619b1217842bd87e4dc72793310f4b751 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 10:04:06 +0100 Subject: [PATCH 231/251] Fix diagonal movement being faster than forward movement --- apps/openmw/mwmechanics/character.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index e5236f02b4..86785ec228 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -863,6 +863,7 @@ void CharacterController::update(float duration) bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); + vec.normalise(); if(mHitState != CharState_None && mJumpState == JumpState_None) vec = Ogre::Vector3(0.0f); Ogre::Vector3 rot = cls.getRotationVector(mPtr); @@ -1119,9 +1120,11 @@ void CharacterController::update(float duration) else moved = Ogre::Vector3(0.0f); - // Ensure we're moving in generally the right direction + // Ensure we're moving in generally the right direction... if(mMovementSpeed > 0.f) { + float l = moved.length(); + if((movement.x < 0.0f && movement.x < moved.x*2.0f) || (movement.x > 0.0f && movement.x > moved.x*2.0f)) moved.x = movement.x; @@ -1131,7 +1134,12 @@ void CharacterController::update(float duration) if((movement.z < 0.0f && movement.z < moved.z*2.0f) || (movement.z > 0.0f && movement.z > moved.z*2.0f)) moved.z = movement.z; + // but keep the original speed + float newLength = moved.length(); + if (newLength > 0) + moved *= (l / newLength); } + // Update movement if(moved.squaredLength() > 1.0f) world->queueMovement(mPtr, moved); From f78b846f9e88347097c1de8e7cacdd09627be2f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Jan 2014 10:21:49 +0100 Subject: [PATCH 232/251] Handle CasterLinked magic effect flag --- apps/openmw/mwmechanics/activespells.cpp | 18 ++++++++++++++++++ apps/openmw/mwmechanics/activespells.hpp | 3 +++ apps/openmw/mwmechanics/actors.cpp | 7 +++++++ apps/openmw/mwmechanics/spellcasting.cpp | 5 +++-- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 2a71659742..994798b0bb 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -205,4 +205,22 @@ namespace MWMechanics } mSpellsChanged = true; } + + void ActiveSpells::purge(const std::string &actorHandle) + { + for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) + { + for (std::vector::iterator effectIt = it->second.mEffects.begin(); + effectIt != it->second.mEffects.end();) + { + const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mKey.mId); + if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked + && it->second.mCasterHandle == actorHandle) + effectIt = it->second.mEffects.erase(effectIt); + else + effectIt++; + } + } + mSpellsChanged = true; + } } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 2ddb4ec556..7a40afb4cb 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -90,6 +90,9 @@ namespace MWMechanics /// Remove all active effects, if roll succeeds (for each effect) void purgeAll (float chance); + /// Remove all effects with CASTER_LINKED flag that were cast by \a actorHandle + void purge (const std::string& actorHandle); + bool isSpellActive (std::string id) const; ///< case insensitive diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 468a218927..6f710988e8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -814,6 +814,13 @@ namespace MWMechanics stats.setMagicEffects(MWMechanics::MagicEffects()); calculateCreatureStatModifiers(iter->first, 0); + // Make sure spell effects with CasterLinked flag are removed + for(PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) + { + MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); + spells.purge(iter->first.getRefData().getHandle()); + } + ++mDeathCount[cls.getId(iter->first)]; if(cls.isEssential(iter->first)) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index be67b65928..a0e91791ba 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -164,8 +164,9 @@ namespace MWMechanics ActiveSpells::Effect effect_ = effect; effect_.mMagnitude *= -1; effects.push_back(effect_); + // Also make sure to set casterHandle = target, so that the effect on the caster gets purged when the target dies caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - effects, mSourceName, caster.getRefData().getHandle()); + effects, mSourceName, target.getRefData().getHandle()); } } } @@ -224,7 +225,7 @@ namespace MWMechanics target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, caster.getRefData().getHandle()); - if (anyHarmfulEffect && target.getClass().isActor() + if (anyHarmfulEffect && target.getClass().isActor() && target != caster && target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); } From 7983b07b106e6898c14f5277326d88b64384394b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 06:15:22 +0100 Subject: [PATCH 233/251] Get bk_treasuryreport script to work properly: - OnPcEquip needs to be set on *using* any item, not just equipping - Handle PcSkipEquip - Execute item's script once immediately after setting OnPcEquip - Do not set OnPcEquip when an item that has skipped equipping sets pcskipequip back to 0 and gets equipped --- apps/openmw/mwgui/inventorywindow.cpp | 60 ++++++++++++++++++++------- apps/openmw/mwgui/inventorywindow.hpp | 4 ++ apps/openmw/mwgui/quickkeysmenu.cpp | 14 +------ apps/openmw/mwworld/actionequip.cpp | 6 --- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7781c8526d..7139c1b2cc 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -13,6 +13,8 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" +#include "../mwscript/interpretercontext.hpp" +#include "../mwbase/scriptmanager.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" @@ -351,6 +353,48 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned); } + void InventoryWindow::useItem(const MWWorld::Ptr &ptr) + { + const std::string& script = ptr.getClass().getScript(ptr); + + // If the item has a script, set its OnPcEquip to 1 + if (!script.empty() + // Another morrowind oddity: when an item has skipped equipping and pcskipequip is reset to 0 afterwards, + // the next time it is equipped will work normally, but will not set onpcequip + && (ptr != mSkippedToEquip || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)) + ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); + + // Give the script a chance to run once before we do anything else + // this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this) + if (!script.empty()) + { + MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); + MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); + } + + if (script.empty() || ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 0) + { + boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); + + action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); + + // this is necessary for books/scrolls: if they are already in the player's inventory, + // the "Take" button should not be visible. + // NOTE: the take button is "reset" when the window opens, so we can safely do the following + // without screwing up future book windows + MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); + + mSkippedToEquip = MWWorld::Ptr(); + } + else + mSkippedToEquip = ptr; + + mItemView->update(); + + notifyContentChanged(); + } + void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender) { if (mDragAndDrop->mIsOnDragAndDrop) @@ -369,21 +413,7 @@ namespace MWGui mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); ptr = *it; } - - boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); - - action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); - - // this is necessary for books/scrolls: if they are already in the player's inventory, - // the "Take" button should not be visible. - // NOTE: the take button is "reset" when the window opens, so we can safely do the following - // without screwing up future book windows - MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); - MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); - - mItemView->update(); - - notifyContentChanged(); + useItem(ptr); } else { diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 112e737fab..7e5a0fe105 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -46,6 +46,8 @@ namespace MWGui void updatePlayer(); + void useItem(const MWWorld::Ptr& ptr); + void setGuiMode(GuiMode mode); private: @@ -74,6 +76,8 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; + MWWorld::Ptr mSkippedToEquip; + GuiMode mGuiMode; int mLastXSize; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 77127f59b8..61e414fc4e 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -308,19 +308,7 @@ namespace MWGui { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - boost::shared_ptr action = MWWorld::Class::get(item).use(item); - - action->execute (MWBase::Environment::get().getWorld()->getPlayerPtr()); - - // this is necessary for books/scrolls: if they are already in the player's inventory, - // the "Take" button should not be visible. - // NOTE: the take button is "reset" when the window opens, so we can safely do the following - // without screwing up future book windows - MWBase::Environment::get().getWindowManager()->getBookWindow()->setTakeButtonShow(false); - MWBase::Environment::get().getWindowManager()->getScrollWindow()->setTakeButtonShow(false); - - // since we changed equipping status, update the inventory window - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView(); + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); } else if (type == Type_MagicItem) { diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 6a0b4eec7d..348b09ad99 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -80,11 +80,5 @@ namespace MWWorld break; } } - - std::string script = MWWorld::Class::get(object).getScript(object); - - /* Set OnPCEquip Variable on item's script, if the player is equipping it, and it has a script with that variable declared */ - if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && script != "") - object.getRefData().getLocals().setVarByInt(script, "onpcequip", 1); } } From 63cd70f810ea12f1f9ba16141b6e36921bcf33a0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 13 Jan 2014 10:03:25 +0100 Subject: [PATCH 234/251] some junk removal --- apps/openmw/mwworld/actionequip.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 348b09ad99..d34773bd51 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -54,8 +54,6 @@ namespace MWWorld assert(it != invStore.end()); - bool equipped = false; - // equip the item in the first free slot for (std::vector::const_iterator slot=slots_.first.begin(); slot!=slots_.first.end(); ++slot) @@ -68,7 +66,6 @@ namespace MWWorld if (slot == --slots_.first.end()) { invStore.equip(*slot, it, actor); - equipped = true; break; } @@ -76,7 +73,6 @@ namespace MWWorld { // slot is not occupied invStore.equip(*slot, it, actor); - equipped = true; break; } } From 83872f6bf58f143b2bd97d833b5f0f870cc72766 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 01:42:19 +0100 Subject: [PATCH 235/251] Knockdown / hit recovery improvements. Use formula and GMSTs from research wiki for knockdown determination. Hand-to-hand automatically knocks out when fatigue empty. --- apps/openmw/mwclass/npc.cpp | 30 +++++++++++++- apps/openmw/mwclass/npc.hpp | 3 ++ apps/openmw/mwmechanics/character.cpp | 48 +++++++++++------------ apps/openmw/mwmechanics/creaturestats.cpp | 22 ++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 9 ++++- 5 files changed, 85 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4aa27e38a3..e08a3c07ea 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -242,6 +242,9 @@ namespace MWClass fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); fWereWolfRunMult = gmst.find("fWereWolfRunMult"); + fKnockDownMult = gmst.find("fKnockDownMult"); + iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); + iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); inited = true; } @@ -651,7 +654,20 @@ namespace MWClass { MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); } - getCreatureStats(ptr).setAttacked(true);//used in CharacterController + getCreatureStats(ptr).setAttacked(true); + + // Check for knockdown + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + { + getCreatureStats(ptr).setKnockedDown(true); + + } + else + getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? if(object.isEmpty()) { @@ -726,6 +742,15 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } + + if (object.isEmpty()) + { + // Hand-to-hand automatically knocks down when running out of fatigue + if (getCreatureStats(ptr).getFatigue().getCurrent() < 0) + { + getCreatureStats(ptr).setKnockedDown(true); + } + } } void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const @@ -1286,4 +1311,7 @@ namespace MWClass 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; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index c39ca42ef4..22a9632e85 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -33,6 +33,9 @@ namespace MWClass 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; public: diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 86785ec228..07859d57c5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -157,40 +157,40 @@ public: void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { - //hit recoils/knockdown animations handling - if(MWWorld::Class::get(mPtr).isActor()) + // hit recoils/knockdown animations handling + if(mPtr.getClass().isActor()) { - if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getAttacked()) + bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); + bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); + if(mHitState == CharState_None) { - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).setAttacked(false); - - if(mHitState == CharState_None) + if(knockdown) { - if(mJumpState != JumpState_None && !MWBase::Environment::get().getWorld()->isFlying(mPtr) - && !MWBase::Environment::get().getWorld()->isSwimming(mPtr) ) + mHitState = CharState_KnockDown; + mCurrentHit = sHitList[sHitListSize-1]; + mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); + } + else if (recovery) + { + mHitState = CharState_Hit; + int iHit = rand() % (sHitListSize-1); + mCurrentHit = sHitList[iHit]; + if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) { - mHitState = CharState_KnockDown; - mCurrentHit = sHitList[sHitListSize-1]; - mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); - } - else - { - mHitState = CharState_Hit; - int iHit = rand() % (sHitListSize-1); + //only 3 different hit animations if player is in 1st person + int iHit = rand() % (sHitListSize-3); mCurrentHit = sHitList[iHit]; - if(mPtr.getRefData().getHandle()=="player" && !mAnimation->hasAnimation(mCurrentHit)) - { - //only 3 different hit animations if player is in 1st person - int iHit = rand() % (sHitListSize-3); - mCurrentHit = sHitList[iHit]; - } - mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } + mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0); } } - else if(mHitState != CharState_None && !mAnimation->isPlaying(mCurrentHit)) + else if(!mAnimation->isPlaying(mCurrentHit)) { mCurrentHit.erase(); + if (knockdown) + mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(false); + if (recovery) + mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false); mHitState = CharState_None; } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index b5b9b7156a..aadce499ea 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mAttackType(AT_Chop), mIsWerewolf(false), - mFallHeight(0), mRecalcDynamicStats(false) + mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -402,4 +402,24 @@ namespace MWMechanics } return false; } + + void CreatureStats::setKnockedDown(bool value) + { + mKnockdown = value; + } + + bool CreatureStats::getKnockedDown() const + { + return mKnockdown; + } + + void CreatureStats::setHitRecovery(bool value) + { + mHitRecovery = value; + } + + bool CreatureStats::getHitRecovery() const + { + return mHitRecovery; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 6e08046384..1f82a9b5c0 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -34,7 +34,9 @@ namespace MWMechanics bool mAlarmed; bool mAttacked; bool mHostile; - bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + bool mAttackingOrSpell; + bool mKnockdown; + bool mHitRecovery; float mFallHeight; @@ -186,6 +188,11 @@ namespace MWMechanics float getEvasion() const; + void setKnockedDown(bool value); + bool getKnockedDown() const; + void setHitRecovery(bool value); + bool getHitRecovery() const; + void setLastHitObject(const std::string &objectid); const std::string &getLastHitObject() const; From 3a1b6dd35455d0f0ee33bbfa76493306ee997287 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 01:47:10 +0100 Subject: [PATCH 236/251] Handle fCombatKODamageMult and fCombatCriticalStrikeMult. Fix SelectWrapper Function_Detected. --- apps/openmw/mwclass/npc.cpp | 22 ++++++++++------------ apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwworld/class.cpp | 5 ----- apps/openmw/mwworld/class.hpp | 5 ----- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e08a3c07ea..19561e53d6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -518,12 +518,6 @@ namespace MWClass weapon.getCellRef().mCharge = weapmaxhealth; damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth; } - if(!othercls.hasDetected(victim, ptr)) - { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); - MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); - MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); - } if (!MWBase::Environment::get().getWorld()->getGodModeState()) weapon.getCellRef().mCharge -= std::min(std::max(1, @@ -545,12 +539,6 @@ namespace MWClass float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat(); damage = stats.getSkill(weapskill).getModified(); damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); - if(!othercls.hasDetected(victim, ptr)) - { - damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); - MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); - MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); - } healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f) || (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0); @@ -577,6 +565,16 @@ namespace MWClass if(ptr.getRefData().getHandle() == "player") skillUsageSucceeded(ptr, weapskill, 0); + bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); + if(!detected) + { + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } + if (othercls.getCreatureStats(victim).getKnockedDown()) + damage *= gmst.find("fCombatKODamageMult")->getFloat(); + // Apply "On hit" enchanted weapons std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; if (!enchantmentName.empty()) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 4f478afce4..d7b7df983c 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -525,7 +525,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_Detected: - return MWWorld::Class::get (mActor).hasDetected (mActor, player); + return MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, mActor); case SelectWrapper::Function_Attacked: diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 0119cdea5f..934dae015f 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -232,11 +232,6 @@ namespace MWWorld return false; } - bool Class::hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const - { - return true; - } - float Class::getArmorRating (const MWWorld::Ptr& ptr) const { throw std::runtime_error("Class does not support armor rating"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 08d769fbe0..c0b010eae8 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -240,11 +240,6 @@ namespace MWWorld /// /// (default implementation: return false) - virtual bool hasDetected (const MWWorld::Ptr& ptr, const MWWorld::Ptr& ptr2) const; - ///< Has \æ ptr detected \a ptr2? - /// - /// (default implementation: return false) - virtual std::string getUpSoundId (const Ptr& ptr) const; ///< Return the up sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) From 413bf127de15af153a28849bbefa7d57f4c62a6d Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 02:54:54 +0100 Subject: [PATCH 237/251] Allow drain fatigue effect to reduce below zero --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6f710988e8..e2255f8cb4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -334,7 +334,7 @@ namespace MWMechanics float currentDiff = creatureStats.getMagicEffects().get(ESM::MagicEffect::RestoreHealth+i).mMagnitude - creatureStats.getMagicEffects().get(ESM::MagicEffect::DamageHealth+i).mMagnitude - creatureStats.getMagicEffects().get(ESM::MagicEffect::AbsorbHealth+i).mMagnitude; - stat.setCurrent(stat.getCurrent() + currentDiff * duration); + stat.setCurrent(stat.getCurrent() + currentDiff * duration, i == 2); creatureStats.setDynamic(i, stat); } From cd06b2177dc1344acedd51adcdc22bce0f512110 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 02:55:18 +0100 Subject: [PATCH 238/251] Automatically knock down when fatigue goes below zero --- apps/openmw/mwclass/npc.cpp | 9 --------- apps/openmw/mwmechanics/creaturestats.cpp | 3 +++ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 19561e53d6..a7ff92cf5e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -740,15 +740,6 @@ namespace MWClass fatigue.setCurrent(fatigue.getCurrent() - damage, true); getCreatureStats(ptr).setFatigue(fatigue); } - - if (object.isEmpty()) - { - // Hand-to-hand automatically knocks down when running out of fatigue - if (getCreatureStats(ptr).getFatigue().getCurrent() < 0) - { - getCreatureStats(ptr).setKnockedDown(true); - } - } } void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index aadce499ea..ba6f0ba047 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -207,6 +207,9 @@ namespace MWMechanics mDynamic[index] = value; + if (index == 2 && value.getCurrent() < 0) + setKnockedDown(true); + if (index==0 && mDynamic[index].getCurrent()<1) { if (!mDead) From 73268a8606df642fe360e028b491eccebdc2f608 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 07:05:52 +0100 Subject: [PATCH 239/251] Fix skill progress not working --- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwmechanics/npcstats.cpp | 2 +- apps/openmw/mwmechanics/stat.hpp | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 17bb24e838..37128c43f3 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -185,7 +185,7 @@ namespace MWGui MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; if (widget) { - float modified = value.getModified(), base = value.getBase(); + int modified = value.getModified(), base = value.getBase(); std::string text = boost::lexical_cast(std::floor(modified)); std::string state = "normal"; if (modified > base) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 289fcc70fe..f77b042716 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -207,7 +207,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if(mIsWerewolf) return; - MWMechanics::SkillValue value = getSkill (skillIndex); + MWMechanics::SkillValue& value = getSkill (skillIndex); value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType)); diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index e66cf86de7..75ac6939aa 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -246,6 +246,18 @@ namespace MWMechanics { return !(left == right); } + + inline bool operator== (const SkillValue& left, const SkillValue& right) + { + return left.getBase() == right.getBase() + && left.getModifier() == right.getModifier() + && left.getDamage() == right.getDamage() + && left.getProgress() == right.getProgress(); + } + inline bool operator!= (const SkillValue& left, const SkillValue& right) + { + return !(left == right); + } } #endif From ba27b693f80091241f4979ef6876883afa69912b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Jan 2014 07:27:08 +0100 Subject: [PATCH 240/251] Increase sneak skill on successful pickpocket --- apps/openmw/mwgui/container.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index d22842b575..858378c03f 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -349,6 +349,8 @@ namespace MWGui mPickpocketDetected = true; return false; } + else + player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1); } else { From 0d0005c433b5b17ea0647c2c769030e4c21f8084 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 02:20:13 +0100 Subject: [PATCH 241/251] Fix fatigue not restoring when waiting --- apps/openmw/mwbase/mechanicsmanager.hpp | 5 +++-- apps/openmw/mwgui/trainingwindow.cpp | 2 ++ apps/openmw/mwgui/travelwindow.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 3 +-- apps/openmw/mwmechanics/actors.cpp | 12 +++++------- apps/openmw/mwmechanics/actors.hpp | 4 ++-- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 5 +++-- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 85fcfc75ba..d1472de388 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -76,8 +76,9 @@ namespace MWBase virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. - virtual void restoreDynamicStats() = 0; - ///< If the player is sleeping, this should be called every hour. + virtual void rest(bool sleep) = 0; + ///< If the player is sleeping or waiting, this should be called every hour. + /// @param sleep is the player sleeping or waiting? virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0; ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 24be5363d6..bee76992af 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -154,6 +154,8 @@ namespace MWGui // advance time MWBase::Environment::get().getWorld ()->advanceTime (2); + MWBase::Environment::get().getMechanicsManager()->rest(false); + MWBase::Environment::get().getMechanicsManager()->rest(false); MWBase::Environment::get().getWorld ()->getFader()->fadeOut(0.25); mFadeTimeRemaining = 0.5; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index dcf54d25a3..c314ce1fda 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -148,7 +148,7 @@ namespace MWGui int hours = static_cast(d /MWBase::Environment::get().getWorld()->getStore().get().find("fTravelTimeMult")->getFloat()); for(int i = 0;i < hours;i++) { - MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); + MWBase::Environment::get().getMechanicsManager ()->rest (true); } MWBase::Environment::get().getWorld()->advanceTime(hours); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index e71ed4247c..5058e53eef 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -253,8 +253,7 @@ namespace MWGui if (mCurHour <= mHours) { MWBase::Environment::get().getWorld ()->advanceTime (1); - if (mSleeping) - MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); + MWBase::Environment::get().getMechanicsManager ()->rest (mSleeping); } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e2255f8cb4..0fc7210b25 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -199,7 +199,7 @@ namespace MWMechanics } // fatigue restoration - calculateRestoration(ptr, duration); + calculateRestoration(ptr, duration, false); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) @@ -257,7 +257,7 @@ namespace MWMechanics creatureStats.setFatigue(fatigue); } - void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) + void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep) { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; @@ -272,10 +272,9 @@ namespace MWMechanics if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; - if (duration == 3600) + if (sleep) { // the actor is sleeping, restore health and magicka - bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; DynamicStat health = stats.getHealth(); @@ -294,7 +293,6 @@ namespace MWMechanics } // restore fatigue - float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); @@ -847,10 +845,10 @@ namespace MWMechanics } } } - void Actors::restoreDynamicStats() + void Actors::restoreDynamicStats(bool sleep) { for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) - calculateRestoration(iter->first, 3600); + calculateRestoration(iter->first, 3600, sleep); } int Actors::countDeaths (const std::string& id) const diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 7046543e6a..382aa54005 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -36,7 +36,7 @@ namespace MWMechanics void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration); void calculateNpcStatModifiers (const MWWorld::Ptr& ptr); - void calculateRestoration (const MWWorld::Ptr& ptr, float duration); + void calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep); void updateDrowning (const MWWorld::Ptr& ptr, float duration); @@ -79,7 +79,7 @@ namespace MWMechanics ///< This function is normally called automatically during the update process, but it can /// also be called explicitly at any time to force an update. - void restoreDynamicStats(); + void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. int countDeaths (const std::string& id) const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 76ba2ff16c..456b3a8f9e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -370,9 +370,9 @@ namespace MWMechanics mObjects.update(duration, paused); } - void MechanicsManager::restoreDynamicStats() + void MechanicsManager::rest(bool sleep) { - mActors.restoreDynamicStats (); + mActors.restoreDynamicStats (sleep); } void MechanicsManager::setPlayerName (const std::string& name) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 569cd2fca0..25e556e6a2 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -81,8 +81,9 @@ namespace MWMechanics virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. - virtual void restoreDynamicStats(); - ///< If the player is sleeping, this should be called every hour. + virtual void rest(bool sleep); + ///< If the player is sleeping or waiting, this should be called every hour. + /// @param sleep is the player sleeping or waiting? virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying); ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. From 95651857f3eb90a5c6e0ee1879c7f2154c471508 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 02:52:34 +0100 Subject: [PATCH 242/251] Fix code duplication --- apps/openmw/mwbase/mechanicsmanager.hpp | 3 + apps/openmw/mwgui/waitdialog.cpp | 38 +-------- apps/openmw/mwmechanics/actors.cpp | 77 +++++++++++++------ apps/openmw/mwmechanics/actors.hpp | 3 + .../mwmechanics/mechanicsmanagerimp.cpp | 5 ++ .../mwmechanics/mechanicsmanagerimp.hpp | 3 + 6 files changed, 69 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index d1472de388..726c8cf04a 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -80,6 +80,9 @@ namespace MWBase ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? + virtual int getHoursToRest() const = 0; + ///< Calculate how many hours the player needs to rest in order to be fully healed + virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0; ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 5058e53eef..1ad703790a 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -144,43 +144,7 @@ namespace MWGui void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender) { - // we need to sleep for a specific time, and since that isn't calculated yet, we'll do it here - // I'm making the assumption here that the # of hours rested is calculated when rest is started - // TODO: the rougher logic here (calculating the hourly deltas) should really go into helper funcs elsewhere - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - - float hourlyHealthDelta = stats.getAttribute(ESM::Attribute::Endurance).getModified() * 0.1; - - bool stunted = (stats.getMagicEffects().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0); - float fRestMagicMult = store.get().find("fRestMagicMult")->getFloat(); - float hourlyMagickaDelta = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); - - // this massive duplication is why it has to be put into helper functions instead - float fFatigueReturnBase = store.get().find("fFatigueReturnBase")->getFloat(); - float fFatigueReturnMult = store.get().find("fFatigueReturnMult")->getFloat(); - float fEndFatigueMult = store.get().find("fEndFatigueMult")->getFloat(); - float capacity = MWWorld::Class::get(player).getCapacity(player); - float encumbrance = MWWorld::Class::get(player).getEncumbrance(player); - float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - float hourlyFatigueDelta = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); - hourlyFatigueDelta *= 3600 * fEndFatigueMult * stats.getAttribute(ESM::Attribute::Endurance).getModified(); - - float healthHours = hourlyHealthDelta >= 0.0 - ? (stats.getHealth().getBase() - stats.getHealth().getCurrent()) / hourlyHealthDelta - : 1.0f; - float magickaHours = stunted ? 0.0 : - hourlyMagickaDelta >= 0.0 - ? (stats.getMagicka().getBase() - stats.getMagicka().getCurrent()) / hourlyMagickaDelta - : 1.0f; - float fatigueHours = hourlyFatigueDelta >= 0.0 - ? (stats.getFatigue().getBase() - stats.getFatigue().getCurrent()) / hourlyFatigueDelta - : 1.0f; - - int autoHours = int(std::ceil( std::max(std::max(healthHours, magickaHours), std::max(fatigueHours, 1.0f)) )); // this should use a variadic max if possible + int autoHours = MWBase::Environment::get().getMechanicsManager()->getHoursToRest(); startWaiting(autoHours); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0fc7210b25..a0be8357d7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -82,6 +82,23 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate) return false; } +void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float& magicka) +{ + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); + + bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + health = 0.1 * endurance; + + magicka = 0; + if (!stunted) + { + float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); + magicka = fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified(); + } +} } @@ -261,37 +278,32 @@ namespace MWMechanics { if (ptr.getClass().getCreatureStats(ptr).isDead()) return; - CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - - float capacity = MWWorld::Class::get(ptr).getCapacity(ptr); - float encumbrance = MWWorld::Class::get(ptr).getEncumbrance(ptr); - float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - if (sleep) { - // the actor is sleeping, restore health and magicka - bool stunted = stats.getMagicEffects ().get(ESM::MagicEffect::StuntedMagicka).mMagnitude > 0; + float health, magicka; + getRestorationPerHourOfSleep(ptr, health, magicka); - DynamicStat health = stats.getHealth(); - health.setCurrent (health.getCurrent() + 0.1 * endurance); - stats.setHealth (health); + DynamicStat stat = stats.getHealth(); + stat.setCurrent(stat.getCurrent() + health); + stats.setHealth(stat); - if (!stunted) - { - float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); - - DynamicStat magicka = stats.getMagicka(); - magicka.setCurrent (magicka.getCurrent() - + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified()); - stats.setMagicka (magicka); - } + stat = stats.getMagicka(); + stat.setCurrent(stat.getCurrent() + magicka); + stats.setMagicka(stat); } + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + float capacity = ptr.getClass().getCapacity(ptr); + float encumbrance = ptr.getClass().getEncumbrance(ptr); + float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); + if (normalizedEncumbrance > 1) + normalizedEncumbrance = 1; + // restore fatigue float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); @@ -303,6 +315,7 @@ namespace MWMechanics DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); + } void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration) @@ -851,6 +864,24 @@ namespace MWMechanics calculateRestoration(iter->first, 3600, sleep); } + int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const + { + float healthPerHour, magickaPerHour; + getRestorationPerHourOfSleep(ptr, healthPerHour, magickaPerHour); + + CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + + float healthHours = healthPerHour >= 0 + ? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour + : 1.0f; + float magickaHours = magickaPerHour >= 0 + ? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour + : 1.0f; + + int autoHours = std::ceil(std::max(1.f, std::max(healthHours, magickaHours))); + return autoHours; + } + int Actors::countDeaths (const std::string& id) const { std::map::const_iterator iter = mDeathCount.find(id); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 382aa54005..b7544dad41 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -81,6 +81,9 @@ namespace MWMechanics void restoreDynamicStats(bool sleep); ///< If the player is sleeping, this should be called every hour. + + int getHoursToRest(const MWWorld::Ptr& ptr) const; + ///< Calculate how many hours the given actor needs to rest in order to be fully healed int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 456b3a8f9e..d6dd73bd08 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -375,6 +375,11 @@ namespace MWMechanics mActors.restoreDynamicStats (sleep); } + int MechanicsManager::getHoursToRest() const + { + return mActors.getHoursToRest(mWatched); + } + void MechanicsManager::setPlayerName (const std::string& name) { MWBase::World *world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 25e556e6a2..469123df98 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -85,6 +85,9 @@ namespace MWMechanics ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? + virtual int getHoursToRest() const; + ///< Calculate how many hours the player needs to rest in order to be fully healed + virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying); ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. From 2196ce427aa7b2eb0d40cae7ccf82c060f5b7103 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 03:08:37 +0100 Subject: [PATCH 243/251] Closes #556: Link movie volume to 'master' volume slider, instead of 'music'. --- apps/openmw/mwsound/soundmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 28a3aae371..a7ee968314 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -156,11 +156,12 @@ namespace MWSound volume *= mFootstepsVolume; break; case Play_TypeMusic: - case Play_TypeMovie: volume *= mMusicVolume; break; case Play_TypeMask: break; + default: + break; } return volume; } From 396efd580b74dadb34fe3b5156e3d79d3949321e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 03:26:56 +0100 Subject: [PATCH 244/251] Fix a leftover of the old coordinate system --- apps/openmw/mwsound/openal_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 4ee754b35d..9dc0b8c5db 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -403,7 +403,7 @@ void OpenAL_SoundStream::update() alSourcef(mSource, AL_GAIN, gain); alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]); + alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); From 9de3abcb5f8cd5a5f7486cdf73b5729fc870761d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 03:34:11 +0100 Subject: [PATCH 245/251] Closes #1105: Do not reduce magicka if unable to cast --- apps/openmw/mwworld/worldimp.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 620058c65f..f9899c3a20 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2075,8 +2075,11 @@ namespace MWWorld } // Reduce mana - magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); - stats.setMagicka(magicka); + if (!fail) + { + magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); + stats.setMagicka(magicka); + } } if (isPlayer && fail) From 6aa56354c0eaba9f7c6a2aa4a892cc8d65f8832e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 05:24:58 +0100 Subject: [PATCH 246/251] Revert "Bug #991: Don't autoequip items with harmful permanent enchantments" This is no longer needed, since merchants no longer equip items sold to them (2f35e5a04ef828d4e99e28e0be74b175c766d13d). Also, items with harmful enchantments that are initially in the NPCs inventory *must* be equipped (e.g. slave bracers) This reverts commit 71d9755ef167a25ea3ce8098325b44e0811b6bf8. --- apps/openmw/mwworld/inventorystore.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 3e81da2122..e8938b2c0e 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -195,29 +195,6 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) std::pair, bool> itemsSlots = MWWorld::Class::get (*iter).getEquipmentSlots (*iter); - // Skip items that have *only* harmful permanent effects - if (!test.getClass().getEnchantment(test).empty()) - { - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Enchantment* enchantment = store.get().find(test.getClass().getEnchantment(test)); - bool harmfulEffect = false; - bool usefulEffect = false; - if (enchantment->mData.mType == ESM::Enchantment::ConstantEffect) - { - for (std::vector::const_iterator it = enchantment->mEffects.mList.begin(); - it != enchantment->mEffects.mList.end(); ++it) - { - const ESM::MagicEffect* effect = store.get().find(it->mEffectID); - if (effect->mData.mFlags & ESM::MagicEffect::Harmful) - harmfulEffect = true; - else - usefulEffect = true; - } - } - if (harmfulEffect && !usefulEffect) - continue; - } - for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { From 90b92a8f41a95cde1bf239052fe4a61e63dfc9e9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 05:37:06 +0100 Subject: [PATCH 247/251] Move levelled list code out of ContainerStore --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/levelledlist.hpp | 78 +++++++++++++++++++++++ apps/openmw/mwworld/containerstore.cpp | 79 ++++++------------------ apps/openmw/mwworld/containerstore.hpp | 2 +- 4 files changed, 98 insertions(+), 63 deletions(-) create mode 100644 apps/openmw/mwmechanics/levelledlist.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index eb5b71ec34..4da5e29970 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease pickpocket + disease pickpocket levelledlist ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp new file mode 100644 index 0000000000..af5f71f51b --- /dev/null +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -0,0 +1,78 @@ +#ifndef OPENMW_MECHANICS_LEVELLEDLIST_H +#define OPENMW_MECHANICS_LEVELLEDLIST_H + +#include "../mwworld/ptr.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +namespace MWMechanics +{ + + /// @return ID of resulting item, or empty if none + std::string getLevelledItem (const ESM::LeveledListBase* levItem, unsigned char failChance=0) + { + const std::vector& items = levItem->mList; + + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int playerLevel = player.getClass().getCreatureStats(player).getLevel(); + + failChance += levItem->mChanceNone; + + float random = static_cast (std::rand()) / RAND_MAX; + if (random < failChance/100.f) + return std::string(); + + std::vector candidates; + int highestLevel = 0; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (it->mLevel > highestLevel) + highestLevel = it->mLevel; + } + + std::pair highest = std::make_pair(-1, ""); + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + if (playerLevel >= it->mLevel + && (levItem->mFlags & ESM::LeveledListBase::AllLevels || it->mLevel == highestLevel)) + { + candidates.push_back(it->mId); + if (it->mLevel >= highest.first) + highest = std::make_pair(it->mLevel, it->mId); + } + + } + if (candidates.empty()) + return std::string(); + std::string item = candidates[std::rand()%candidates.size()]; + + // Is this another levelled item or a real item? + try + { + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item, 1); + if (ref.getPtr().getTypeName() != typeid(ESM::ItemLevList).name() + && ref.getPtr().getTypeName() != typeid(ESM::CreatureLevList).name()) + { + return item; + } + else + { + if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name()) + return getLevelledItem(ref.getPtr().get()->mBase, failChance); + else + return getLevelledItem(ref.getPtr().get()->mBase, failChance); + } + } + catch (std::logic_error& e) + { + // Vanilla doesn't fail on nonexistent items in levelled lists + std::cerr << "Warning: ignoring nonexistent item '" << item << "'" << std::endl; + return std::string(); + } + } + +} + +#endif diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 744971985c..864bb2a0d3 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -5,17 +5,11 @@ #include #include -#include - -#include -#include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/scriptmanager.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/levelledlist.hpp" #include "manualref.hpp" #include "refdata.hpp" @@ -309,72 +303,35 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std:: } void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, - int count, unsigned char failChance, bool topLevel) + int count, bool topLevel) { count = std::abs(count); /// \todo implement item restocking (indicated by negative count) - try + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); + + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { - ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); + const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; - if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) { - const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; - const std::vector& items = levItem->mList; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - int playerLevel = player.getClass().getCreatureStats(player).getLevel(); - - failChance += levItem->mChanceNone; - - if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) - { - for (int i=0; i (std::rand()) / RAND_MAX; - if (random >= failChance/100.f) - { - std::vector candidates; - int highestLevel = 0; - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) - { - if (it->mLevel > highestLevel) - highestLevel = it->mLevel; - } - - std::pair highest = std::make_pair(-1, ""); - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) - { - if (playerLevel >= it->mLevel - && (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel)) - { - candidates.push_back(it->mId); - if (it->mLevel >= highest.first) - highest = std::make_pair(it->mLevel, it->mId); - } - - } - if (candidates.empty()) - return; - std::string item = candidates[std::rand()%candidates.size()]; - addInitialItem(item, owner, faction, count, failChance, false); - } + for (int i=0; i()->mBase); + if (id.empty()) + return; + addInitialItem(id, owner, faction, count, false); } } - catch (std::logic_error& e) + else { - // Vanilla doesn't fail on nonexistent items in levelled lists - std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl; - return; + ref.getPtr().getCellRef().mOwner = owner; + ref.getPtr().getCellRef().mFaction = faction; + addImp (ref.getPtr(), count); } } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 936468f8dc..0a17287408 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -54,7 +54,7 @@ namespace MWWorld mutable float mCachedWeight; mutable bool mWeightUpToDate; ContainerStoreIterator addImp (const Ptr& ptr, int count); - void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, unsigned char failChance=0, bool topLevel=true); + void addInitialItem (const std::string& id, const std::string& owner, const std::string& faction, int count, bool topLevel=true); public: From 26d972280f7f555855b5782066a830373cee9486 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 06:03:34 +0100 Subject: [PATCH 248/251] Fix a few text defines --- components/interpreter/defines.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 5774c96aec..6f3b5bb5aa 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -64,7 +64,7 @@ namespace Interpreter{ retval << context.getActionBinding("#{sRestKey}"); } else if((found = Check(temp, "actionmenumode", &i, &start))){ - retval << context.getActionBinding("#{sJournal}"); + retval << context.getActionBinding("#{sInventory}"); } else if((found = Check(temp, "actionactivate", &i, &start))){ retval << context.getActionBinding("#{sActivate}"); @@ -88,10 +88,10 @@ namespace Interpreter{ retval << context.getActionBinding("#{sBack}"); } else if((found = Check(temp, "actionuse", &i, &start))){ - retval << "PLACEHOLDER_ACTION_USE"; + retval << context.getActionBinding("#{sUse}"); } else if((found = Check(temp, "actionrun", &i, &start))){ - retval << "PLACEHOLDER_ACTION_RUN"; + retval << context.getActionBinding("#{sRun}"); } else if((found = Check(temp, "pcclass", &i, &start))){ retval << context.getPCClass(); From b8583124e0f49097f362b1b9e5a2054f246813be Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 06:13:30 +0100 Subject: [PATCH 249/251] Correction for RemoveSoulgem instruction --- apps/openmw/mwscript/containerextensions.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 202ec64647..7bac7cdbea 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -282,7 +282,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); + const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index bb3600a272..637159475f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -371,15 +371,20 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = R()(runtime); std::string soul = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); - - store.remove(soul, 1, ptr); + for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + { + if (::Misc::StringUtils::ciEqual(it->getCellRef().mSoul, soul)) + { + store.remove(*it, 1, ptr); + return; + } + } } }; From 69381c49c71f18cd7e58bb71682c5cbbb7abf215 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 07:39:44 +0100 Subject: [PATCH 250/251] Added a todo comment --- 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 d6dd73bd08..1738f9fdd1 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -901,6 +901,9 @@ namespace MWMechanics static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->getFloat(); float sneak = 0; + // TODO: According to Hrnchamd Research:Movement, "Creatures have generalized combat, magic and stealth + // stats which substitute for the specific skills (in the same way as specializations)." + // This probably applies to a large part of the code base. if (ptr.getClass().isNpc()) sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified(); int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); From 52b9ebff9dc40545efa4e3676b5f42bd40ce7d7f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Jan 2014 07:40:17 +0100 Subject: [PATCH 251/251] Closes #1092: Implement sleep interruption. Fix levelled list flags for creatures. Change World::copyObjectToCell to search for the correct cell. --- apps/esmtool/labels.cpp | 22 ++++++--- apps/esmtool/labels.hpp | 3 +- apps/esmtool/record.cpp | 8 ++-- apps/openmw/mwbase/world.hpp | 3 ++ apps/openmw/mwgui/waitdialog.cpp | 35 ++++++++++++++- apps/openmw/mwgui/waitdialog.hpp | 3 ++ apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 1 + apps/openmw/mwmechanics/levelledlist.hpp | 16 ++++--- apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 57 ++++++++++++++++++++---- apps/openmw/mwworld/worldimp.hpp | 5 ++- components/esm/loadlevlist.hpp | 36 +++++++++------ 13 files changed, 150 insertions(+), 43 deletions(-) diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 56c9a2fadb..d270a9534c 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -717,16 +717,26 @@ std::string landFlags(int flags) return properties; } -std::string leveledListFlags(int flags) +std::string itemListFlags(int flags) { std::string properties = ""; if (flags == 0) properties += "[None] "; - if (flags & ESM::LeveledListBase::AllLevels) properties += "AllLevels "; - // This flag apparently not present on creature lists... - if (flags & ESM::LeveledListBase::Each) properties += "Each "; + if (flags & ESM::ItemLevList::AllLevels) properties += "AllLevels "; + if (flags & ESM::ItemLevList::Each) properties += "Each "; int unused = (0xFFFFFFFF ^ - (ESM::LeveledListBase::AllLevels| - ESM::LeveledListBase::Each)); + (ESM::ItemLevList::AllLevels| + ESM::ItemLevList::Each)); + if (flags & unused) properties += "Invalid "; + properties += str(boost::format("(0x%08X)") % flags); + return properties; +} + +std::string creatureListFlags(int flags) +{ + std::string properties = ""; + if (flags == 0) properties += "[None] "; + if (flags & ESM::CreatureLevList::AllLevels) properties += "AllLevels "; + int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels); if (flags & unused) properties += "Invalid "; properties += str(boost::format("(0x%08X)") % flags); return properties; diff --git a/apps/esmtool/labels.hpp b/apps/esmtool/labels.hpp index 48d7b249bd..007f933164 100644 --- a/apps/esmtool/labels.hpp +++ b/apps/esmtool/labels.hpp @@ -50,7 +50,8 @@ std::string cellFlags(int flags); std::string containerFlags(int flags); std::string creatureFlags(int flags); std::string landFlags(int flags); -std::string leveledListFlags(int flags); +std::string creatureListFlags(int flags); +std::string itemListFlags(int flags); std::string lightFlags(int flags); std::string magicEffectFlags(int flags); std::string npcFlags(int flags); diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index a5664c1c86..b68004f1f6 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -834,7 +834,8 @@ template<> void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; - std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl; + std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl; + std::cout << " Chance none: " << mData.mChanceNone << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) @@ -846,11 +847,12 @@ template<> void Record::print() { std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; - std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl; + std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl; + std::cout << " Chance none: " << mData.mChanceNone << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl; std::vector::iterator iit; for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) - std::cout << " Inventory: Count: " << iit->mLevel + std::cout << " Inventory: Level: " << iit->mLevel << " Item: " << iit->mId << std::endl; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fe40fab24f..83fcba87cc 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -460,6 +460,9 @@ namespace MWBase virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0; virtual void goToJail () = 0; + + /// Spawn a random creature from a levelled list next to the player + virtual void spawnRandomCreature(const std::string& creatureList) = 0; }; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 1ad703790a..f52771ffe8 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -48,6 +48,7 @@ namespace MWGui , mRemainingTime(0.05) , mCurHour(0) , mManualHours(1) + , mInterruptAt(-1) { getWidget(mDateTimeText, "DateTimeText"); getWidget(mRestText, "RestText"); @@ -156,7 +157,8 @@ namespace MWGui void WaitDialog::startWaiting(int hoursToWait) { - MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.2); + MWBase::World* world = MWBase::Environment::get().getWorld(); + world->getFader ()->fadeOut(0.2); setVisible(false); mProgressBar.setVisible (true); @@ -164,6 +166,30 @@ namespace MWGui mCurHour = 0; mHours = hoursToWait; + // FIXME: move this somewhere else? + mInterruptAt = -1; + MWWorld::Ptr player = world->getPlayerPtr(); + if (mSleeping && player.getCell()->isExterior()) + { + std::string regionstr = player.getCell()->mCell->mRegion; + if (!regionstr.empty()) + { + const ESM::Region *region = world->getStore().get().find (regionstr); + if (!region->mSleepList.empty()) + { + float fSleepRandMod = world->getStore().get().find("fSleepRandMod")->getFloat(); + int x = std::rand()/ (static_cast (RAND_MAX) + 1) * hoursToWait; // [0, hoursRested] + float y = fSleepRandMod * hoursToWait; + if (x > y) + { + float fSleepRestMod = world->getStore().get().find("fSleepRestMod")->getFloat(); + mInterruptAt = int(fSleepRestMod * hoursToWait); + mInterruptCreatureList = region->mSleepList; + } + } + } + } + mRemainingTime = 0.05; mProgressBar.setProgress (0, mHours); } @@ -206,6 +232,13 @@ namespace MWGui if (!mWaiting) return; + if (mCurHour == mInterruptAt) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sSleepInterrupt}"); + MWBase::Environment::get().getWorld()->spawnRandomCreature(mInterruptCreatureList); + stopWaiting(); + } + mRemainingTime -= dt; while (mRemainingTime < 0) diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 2723f7a805..d96649af6f 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -51,6 +51,9 @@ namespace MWGui int mManualHours; // stores the hours to rest selected via slider float mRemainingTime; + int mInterruptAt; + std::string mInterruptCreatureList; + WaitDialogProgressBar mProgressBar; void onUntilHealedButtonClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a0be8357d7..99381a204e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -515,7 +515,7 @@ namespace MWMechanics if (magnitude > 0) { ESM::Position ipos = ptr.getRefData().getPosition(); - Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); + Ogre::Vector3 pos(ipos.pos); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); const float distance = 50; pos = pos + distance*rot.yAxis(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 07859d57c5..9a3983b077 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -860,6 +860,7 @@ void CharacterController::update(float duration) bool onground = world->isOnGround(mPtr); bool inwater = world->isSwimming(mPtr); bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run); + isrunning = true; bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); bool flying = world->isFlying(mPtr); Ogre::Vector3 vec = cls.getMovementVector(mPtr); diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index af5f71f51b..d65503011a 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -11,7 +11,7 @@ namespace MWMechanics { /// @return ID of resulting item, or empty if none - std::string getLevelledItem (const ESM::LeveledListBase* levItem, unsigned char failChance=0) + inline std::string getLevelledItem (const ESM::LeveledListBase* levItem, bool creature, unsigned char failChance=0) { const std::vector& items = levItem->mList; @@ -20,29 +20,33 @@ namespace MWMechanics failChance += levItem->mChanceNone; - float random = static_cast (std::rand()) / RAND_MAX; - if (random < failChance/100.f) + int random = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (random < failChance) return std::string(); std::vector candidates; int highestLevel = 0; for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { - if (it->mLevel > highestLevel) + if (it->mLevel > highestLevel && it->mLevel <= playerLevel) highestLevel = it->mLevel; } + // For levelled creatures, the flags are swapped. This file format just makes so much sense. + bool allLevels = levItem->mFlags & ESM::ItemLevList::AllLevels; + if (creature) + allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels; + std::pair highest = std::make_pair(-1, ""); for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { if (playerLevel >= it->mLevel - && (levItem->mFlags & ESM::LeveledListBase::AllLevels || it->mLevel == highestLevel)) + && (allLevels || it->mLevel == highestLevel)) { candidates.push_back(it->mId); if (it->mLevel >= highest.first) highest = std::make_pair(it->mLevel, it->mId); } - } if (candidates.empty()) return std::string(); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 864bb2a0d3..475eeb8f40 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -321,7 +321,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: } else { - std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase); + std::string id = MWMechanics::getLevelledItem(ref.getPtr().get()->mBase, false); if (id.empty()) return; addInitialItem(id, owner, faction, count, false); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f9899c3a20..ebc1044bc4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -27,6 +27,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/spellcasting.hpp" +#include "../mwmechanics/levelledlist.hpp" #include "../mwrender/sky.hpp" @@ -1538,23 +1539,28 @@ namespace MWWorld } - Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos, bool adjustPos) + Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, ESM::Position pos, bool adjustPos) { - /// \todo add searching correct cell for position specified - MWWorld::Ptr dropped = - MWWorld::Class::get(object).copyToCell(object, cell, pos); - if (object.getClass().isActor() || adjustPos) { Ogre::Vector3 min, max; if (mPhysics->getObjectAABB(object, min, max)) { - float *pos = dropped.getRefData().getPosition().pos; - pos[0] -= (min.x + max.x) / 2; - pos[1] -= (min.y + max.y) / 2; - pos[2] -= min.z; + pos.pos[0] -= (min.x + max.x) / 2; + pos.pos[1] -= (min.y + max.y) / 2; + pos.pos[2] -= min.z; } } + if (cell.isExterior()) + { + int cellX, cellY; + positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY); + cell = *mCells.getExterior(cellX, cellY); + } + + MWWorld::Ptr dropped = + MWWorld::Class::get(object).copyToCell(object, cell, pos); + if (mWorldScene->isCellActive(cell)) { if (dropped.getRefData().isEnabled()) { mWorldScene->addObjectToScene(dropped); @@ -2571,4 +2577,37 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); } } + + void World::spawnRandomCreature(const std::string &creatureList) + { + const ESM::CreatureLevList* list = getStore().get().find(creatureList); + + int iNumberCreatures = getStore().get().find("iNumberCreatures")->getInt(); + int numCreatures = 1 + std::rand()/ (static_cast (RAND_MAX) + 1) * iNumberCreatures; // [1, iNumberCreatures] + + for (int i=0; igetPlayer().getRefData().getPosition(); + Ogre::Vector3 pos(ipos.pos); + Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); + const float distance = 50; + pos = pos + distance*rot.yAxis(); + ipos.pos[0] = pos.x; + ipos.pos[1] = pos.y; + ipos.pos[2] = pos.z; + ipos.rot[0] = 0; + ipos.rot[1] = 0; + ipos.rot[2] = 0; + + MWWorld::CellStore* cell = mPlayer->getPlayer().getCell(); + MWWorld::ManualRef ref(getStore(), selectedCreature, 1); + ref.getPtr().getCellRef().mPos = ipos; + + safePlaceObject(ref.getPtr(),*cell,ipos); + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 634cc8d6b6..92975400a4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -114,7 +114,7 @@ namespace MWWorld bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed - Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos, bool adjustPos=true); + Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, ESM::Position pos, bool adjustPos=true); void updateWindowManager (); void performUpdateSceneQueries (); @@ -546,6 +546,9 @@ namespace MWWorld virtual void confiscateStolenItems(const MWWorld::Ptr& ptr); virtual void goToJail (); + + /// Spawn a random creature from a levelled list next to the player + virtual void spawnRandomCreature(const std::string& creatureList); }; } diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index 9dcc6177a1..a4e1b85c2d 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -20,20 +20,6 @@ class ESMWriter; struct LeveledListBase { - enum Flags - { - - Each = 0x01, // Select a new item each time this - // list is instantiated, instead of - // giving several identical items - // (used when a container has more - // than one instance of one leveled - // list.) - AllLevels = 0x02 // Calculate from all levels <= player - // level, not just the closest below - // player. - }; - int mFlags; unsigned char mChanceNone; // Chance that none are selected (0-100) std::string mId; @@ -61,6 +47,14 @@ struct CreatureLevList: LeveledListBase { static unsigned int sRecordId; + enum Flags + { + + AllLevels = 0x01 // Calculate from all levels <= player + // level, not just the closest below + // player. + }; + CreatureLevList() { mRecName = "CNAM"; @@ -71,6 +65,20 @@ struct ItemLevList: LeveledListBase { static unsigned int sRecordId; + enum Flags + { + + Each = 0x01, // Select a new item each time this + // list is instantiated, instead of + // giving several identical items + // (used when a container has more + // than one instance of one leveled + // list.) + AllLevels = 0x02 // Calculate from all levels <= player + // level, not just the closest below + // player. + }; + ItemLevList() { mRecName = "INAM";