diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index fb34e4c883..87a5dde27a 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -248,4 +248,12 @@ namespace MWClass return info; } + + std::string Armor::getEnchantment (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->enchant; + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index c757587e5e..3ff101a5e5 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -55,6 +55,9 @@ namespace MWClass virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; ///< Return the put down sound Id + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 620b664cc4..a242874df3 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -201,4 +201,12 @@ namespace MWClass return info; } + + std::string Clothing::getEnchantment (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->enchant; + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index afb1849995..9b9322969a 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -49,6 +49,9 @@ namespace MWClass virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; ///< Return the put down sound Id + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index fcfaebcb7a..0ddf19d3cc 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -339,4 +339,12 @@ namespace MWClass return info; } + + std::string Weapon::getEnchantment (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->enchant; + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 9c5f43bd3f..c27a39620d 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -55,6 +55,9 @@ namespace MWClass virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; ///< Return the put down sound Id + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string }; } diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 282fba3543..7baf589c44 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -883,4 +883,11 @@ namespace MWDialogue } return factionID; } + + void DialogueManager::goodbye() + { + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + + win->goodbye(); + } } diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanager.hpp index a3e37987db..992175c0cf 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.hpp +++ b/apps/openmw/mwdialogue/dialoguemanager.hpp @@ -56,6 +56,8 @@ namespace MWDialogue void askQuestion(std::string question,int choice); + void goodbye(); + ///get the faction of the actor you are talking with std::string getFaction(); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 2e74290555..45163017a6 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -39,6 +39,7 @@ std::string::size_type find_str_ci(const std::string& str, const std::string& su DialogueWindow::DialogueWindow(WindowManager& parWindowManager) : WindowBase("openmw_dialogue_window_layout.xml", parWindowManager) + , mEnabled(true) { // Centre dialog center(); @@ -64,6 +65,7 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager) MyGUI::ButtonPtr byeButton; getWidget(byeButton, "ByeButton"); byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); + byeButton->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGoodbye")->str); getWidget(pDispositionBar, "Disposition"); getWidget(pDispositionText,"DispositionText"); @@ -81,6 +83,10 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) size_t cursorPosition = t->getCursorPosition(lastPressed); MyGUI::UString color = history->getColorAtPos(cursorPosition); + + if (!mEnabled && color == "#572D21") + MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); + if(color != "#B29154") { UString key = history->getColorTextAt(cursorPosition); @@ -119,11 +125,15 @@ void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) void DialogueWindow::onSelectTopic(std::string topic) { + if (!mEnabled) return; + MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); } void DialogueWindow::startDialogue(std::string npcName) { + mEnabled = true; + topicsList->setEnabled(true); static_cast(mMainWidget)->setCaption(npcName); adjustWindowCaption(); } @@ -224,3 +234,10 @@ void DialogueWindow::updateOptions() pDispositionText->eraseText(0,pDispositionText->getTextLength()); pDispositionText->addText("#B29154"+std::string("40/100")+"#B29154"); } + +void DialogueWindow::goodbye() +{ + history->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGoodbye")->str); + topicsList->setEnabled(false); + mEnabled = false; +} diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index a8fed7d31e..a29e737997 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -45,6 +45,7 @@ namespace MWGui void addText(std::string text); void addTitle(std::string text); void askQuestion(std::string question); + void goodbye(); protected: void onSelectTopic(std::string topic); @@ -60,6 +61,8 @@ namespace MWGui */ std::string parseText(std::string text); + bool mEnabled; + DialogueHistory* history; Widgets::MWList* topicsList; MyGUI::ProgressPtr pDispositionBar; diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index b99d55999c..ec8ab59b41 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -115,6 +115,16 @@ namespace MWScript } }; + class OpGoodbye : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime& runtime) + { + MWBase::Environment::get().getDialogueManager()->goodbye(); + } + }; + const int opcodeJournal = 0x2000133; const int opcodeSetJournalIndex = 0x2000134; const int opcodeGetJournalIndex = 0x2000135; @@ -122,6 +132,7 @@ namespace MWScript const int opcodeChoice = 0x2000a; const int opcodeForceGreeting = 0x200014f; const int opcodeForceGreetingExplicit = 0x2000150; + const int opcodeGoodbye = 0x2000152; void registerExtensions (Compiler::Extensions& extensions) { @@ -133,6 +144,7 @@ namespace MWScript extensions.registerInstruction("forcegreeting","",opcodeForceGreeting); extensions.registerInstruction("forcegreeting","",opcodeForceGreeting, opcodeForceGreetingExplicit); + extensions.registerInstruction("goodbye", "", opcodeGoodbye); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -144,6 +156,7 @@ namespace MWScript interpreter.installSegment3 (opcodeChoice,new OpChoice); interpreter.installSegment5 (opcodeForceGreeting, new OpForceGreeting); interpreter.installSegment5 (opcodeForceGreetingExplicit, new OpForceGreeting); + interpreter.installSegment5 (opcodeGoodbye, new OpGoodbye); } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 9922cb21eb..06e9a0ca73 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -145,5 +145,6 @@ op 0x200014e: ModDisposition, explicit reference op 0x200014f: ForceGreeting op 0x2000150: ForceGreeting, explicit reference op 0x2000151: ToggleFullHelp -op 0x2000152: ToggleCompositors -opcodes 0x2000153-0x3ffffff unused +op 0x2000152: Goodbye +op 0x2000153: ToggleCompositors +opcodes 0x2000154-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 95129bd763..1467b0aeb4 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -224,7 +224,7 @@ namespace MWScript const int opcodeFadeTo = 0x200013e; const int opcodeToggleWater = 0x2000144; const int opcodeTogglePathgrid = 0x2000146; - const int opcodeToggleCompositors = 0x2000152; + const int opcodeToggleCompositors = 0x2000153; void registerExtensions (Compiler::Extensions& extensions) { diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 78ed5ca2e5..151d913bed 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -181,4 +181,9 @@ namespace MWWorld { return false; } + + std::string Class::getEnchantment (const Ptr& ptr) const + { + return ""; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index e69b8f2ac9..d7a2d2d759 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -136,7 +136,7 @@ namespace MWWorld ///< Set or unset a stance. virtual bool getStance (const Ptr& ptr, Stance stance, bool ignoreForce = false) const; - ////< Check if a stance is active or not. + ///< Check if a stance is active or not. virtual float getSpeed (const Ptr& ptr) const; ///< Return movement speed. @@ -179,6 +179,10 @@ namespace MWWorld virtual std::string getDownSoundId (const Ptr& ptr) const; ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string + /// (default implementation: return empty string) }; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2800b6f3c1..dcddc333b0 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -9,6 +9,7 @@ #include "manualref.hpp" #include "refdata.hpp" +#include "class.hpp" namespace { @@ -44,11 +45,43 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() return ContainerStoreIterator (this); } +bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) const +{ + /// \todo add current weapon/armor health, remaining lockpick/repair uses, current enchantment charge here as soon as they are implemented + if ( ptr1.mCellRef->refID == ptr2.mCellRef->refID + && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks + && ptr1.mCellRef->owner == ptr2.mCellRef->owner + && ptr1.mCellRef->soul == ptr2.mCellRef->soul + && ptr1.mCellRef->charge == ptr2.mCellRef->charge) + return true; + + return false; +} + void MWWorld::ContainerStore::add (const Ptr& ptr) { - /// \todo implement item stacking + int type = getType(ptr); - switch (getType (ptr)) + // determine whether to stack or not + for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) + { + if (stacks(*iter, ptr)) + { + // stack + iter->getRefData().setCount( iter->getRefData().getCount() + ptr.getRefData().getCount() ); + + flagAsModified(); + return; + } + } + + // if we got here, this means no stacking + addImpl(ptr); +} + +void MWWorld::ContainerStore::addImpl (const Ptr& ptr) +{ + switch (getType(ptr)) { case Type_Potion: potions.list.push_back (*ptr.get()); break; case Type_Apparatus: appas.list.push_back (*ptr.get()); break; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index da5424fe08..f0e9d7e4ab 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -67,13 +67,23 @@ namespace MWWorld ContainerStoreIterator end(); void add (const Ptr& ptr); - ///< Add the item pointed to by \a ptr to this container. + ///< 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. /// /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! + protected: + void addImpl (const Ptr& ptr); + ///< Add the item to this container (no stacking) + + virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2) const; + ///< @return true if the two specified objects can stack with each other + /// @note ptr1 is the item that is already in this container + + public: + void fill (const ESM::InventoryList& items, const ESMS::ESMStore& store); ///< Insert items into *this. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 576f2371f9..6cf35ac644 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -59,17 +59,38 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite if (iterator.getContainerStore()!=this) throw std::runtime_error ("attempt to equip an item that is not in the inventory"); + std::pair, bool> slots; if (iterator!=end()) { - std::pair, bool> slots = Class::get (*iterator).getEquipmentSlots (*iterator); + slots = Class::get (*iterator).getEquipmentSlots (*iterator); if (std::find (slots.first.begin(), slots.first.end(), slot)==slots.first.end()) throw std::runtime_error ("invalid slot"); } - /// \todo restack item previously in this slot (if required) + // restack item previously in this slot (if required) + if (mSlots[slot] != end()) + { + for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) + { + if (stacks(*iter, *mSlots[slot])) + { + iter->getRefData().setCount( iter->getRefData().getCount() + mSlots[slot]->getRefData().getCount() ); + mSlots[slot]->getRefData().setCount(0); + break; + } + } + } - /// \todo unstack item pointed to by iterator if required) + // 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); + addImpl(*iterator); + iterator->getRefData().setCount(1); + } mSlots[slot] = iterator; @@ -147,7 +168,18 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) } } - /// \todo unstack, if reqquired (itemsSlots.second) + if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped + { + // 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); + addImpl(*iter); + iter->getRefData().setCount(1); + } + } slots[*iter2] = iter; break; @@ -168,3 +200,20 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) flagAsModified(); } } + +bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) const +{ + bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); + if (!canStack) + return false; + + // don't stack if the item being checked against is currently equipped. + for (TSlots::const_iterator iter (mSlots.begin()); + iter!=mSlots.end(); ++iter) + { + if (ptr1 == **iter) + return false; + } + + return true; +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 5eeaf570d0..4162c7e2e8 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -64,6 +64,13 @@ namespace MWWorld void autoEquip (const MWMechanics::NpcStats& stats); ///< Auto equip items according to stats and item value. + + protected: + + virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2) const; + ///< @return true if the two specified objects can stack with each other + /// @note ptr1 is the item that is already in this container + }; } diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index ef45b6543e..d5f322ebd4 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -28,10 +28,10 @@ ConfigurationManager::ConfigurationManager() { setupTokensMapping(); - mPluginsCfgPath = mFixedPath.getGlobalPath() / pluginsCfgFile; + mPluginsCfgPath = mFixedPath.getLocalPath() / pluginsCfgFile; if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) { - mPluginsCfgPath = mFixedPath.getLocalPath() / pluginsCfgFile; + mPluginsCfgPath = mFixedPath.getGlobalPath() / pluginsCfgFile; if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) { std::cerr << "Failed to find " << pluginsCfgFile << " file!" << std::endl;