diff --git a/.gitignore b/.gitignore index 26ba80e1a..9734ac35c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ Makefile makefile data *.kdev4 +CMakeLists.txt.user diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 750fc2fff..9f9b5af70 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -76,6 +76,12 @@ namespace MWBase virtual void restoreDynamicStats() = 0; ///< If the player is sleeping, this should be called every hour. + + 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. + + virtual int getDerivedDisposition(const MWWorld::Ptr& ptr) = 0; + ///< Calculate the diposition of an NPC toward the player. virtual int countDeaths (const std::string& id) const = 0; ///< Return the number of deaths for actors with the given ID. diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3a7cb0874..5416945e7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -29,6 +29,7 @@ namespace ESM struct Class; struct Potion; struct Spell; + struct NPC; } namespace MWRender @@ -230,24 +231,28 @@ namespace MWBase ///< Toggle a render mode. ///< \return Resulting mode - virtual std::pair<std::string, const ESM::Potion *> createRecord (const ESM::Potion& record) + virtual const ESM::Potion *createRecord (const ESM::Potion& record) = 0; ///< Create a new recrod (of type potion) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record - virtual std::pair<std::string, const ESM::Spell *> createRecord (const ESM::Spell& record) + virtual const ESM::Spell *createRecord (const ESM::Spell& record) = 0; ///< Create a new recrod (of type spell) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record - virtual std::pair<std::string, const ESM::Class *> createRecord (const ESM::Class& record) + virtual const ESM::Class *createRecord (const ESM::Class& record) = 0; ///< Create a new recrod (of type class) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record) = 0; ///< Create a new recrod (of type cell) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record + + virtual const ESM::NPC *createRecord(const ESM::NPC &record) = 0; + ///< Create a new recrod (of type npc) in the ESM store. + /// \return pointer to created record virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number = 1) = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e58de6079..6a516bcc8 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -95,6 +95,7 @@ namespace MWClass data->mCreatureStats.setFatigue (ref->mBase->mNpdt52.mFatigue); data->mCreatureStats.setLevel(ref->mBase->mNpdt52.mLevel); + data->mNpcStats.setDisposition(ref->mBase->mNpdt52.mDisposition); } else { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index e12a22247..15852198f 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -464,7 +464,7 @@ namespace MWDialogue || services & ESM::NPC::Misc) windowServices |= MWGui::DialogueWindow::Service_Trade; - if( !mActor.get<ESM::NPC>()->mBase->mTransport.empty()) + if(mActor.getTypeName() == typeid(ESM::NPC).name() && !mActor.get<ESM::NPC>()->mBase->mTransport.empty()) windowServices |= MWGui::DialogueWindow::Service_Travel; if (services & ESM::NPC::Spells) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 253e0779c..a054f34dd 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -559,6 +559,7 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) klass.mData.mSkills[i][1] = majorSkills[i]; klass.mData.mSkills[i][0] = minorSkills[i]; } + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); mPlayerClass = klass; mWM->setPlayerClass(klass); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index b8022f629..e1baaf8e0 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -12,6 +12,7 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "dialogue_history.hpp" #include "widgets.hpp" @@ -50,7 +51,7 @@ std::string::size_type find_str_ci(const std::string& str, const std::string& su DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_dialogue_window.layout", parWindowManager) - , mEnabled(true) + , mEnabled(false) , mServices(0) { // Centre dialog @@ -299,10 +300,13 @@ void DialogueWindow::updateOptions() mTopicsList->clear(); mHistory->eraseText(0, mHistory->getTextLength()); - mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(40); - mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+std::string("40/100")+"#B29154"); + if (mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + } } void DialogueWindow::goodbye() @@ -316,3 +320,14 @@ void DialogueWindow::onReferenceUnavailable() { mWindowManager.removeGuiMode(GM_Dialogue); } + +void DialogueWindow::onFrame() +{ + if(mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + } +} diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 3a89409ca..bb9acf5db 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -47,6 +47,7 @@ namespace MWGui void addTitle(std::string text); void askQuestion(std::string question); void goodbye(); + void onFrame(); // make sure to call these before setKeywords() void setServices(int services) { mServices = services; } diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 399ff74a3..45890b89f 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -108,7 +108,8 @@ namespace MWGui void LevelupDialog::open() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); @@ -119,20 +120,13 @@ namespace MWGui setAttributeValues(); - // set class image - const ESM::Class& playerClass = MWBase::Environment::get().getWorld ()->getPlayer ().getClass (); - // retrieve the ID to this class - std::string classId; - const MWWorld::Store<ESM::Class> &classes = - MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>(); + const ESM::NPC *playerData = player.get<ESM::NPC>()->mBase; - MWWorld::Store<ESM::Class>::iterator it = classes.begin(); - for (; it != classes.end(); ++it) - { - if (playerClass.mName == it->mName) - classId = it->mId; - } - mClassImage->setImageTexture ("textures\\levelup\\" + classId + ".dds"); + // set class image + const ESM::Class *cls = + world->getStore().get<ESM::Class>().find(playerData->mClass); + + mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); /// \todo replace this with INI-imported texts int level = creatureStats.getLevel ()+1; diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 813a21b12..a41f401a5 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" @@ -52,9 +53,9 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Spell* spell = store.get<ESM::Spell>().find(spellId); - + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId); int price = spell->mData.mCost*store.get<ESM::GameSetting>().find("fSpellValueMult")->getFloat(); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); MyGUI::Button* toAdd = mSpellsView->createWidget<MyGUI::Button>( @@ -67,7 +68,6 @@ namespace MWGui ); mCurrentY += sLineHeight; - /// \todo price adjustment depending on merchantile skill toAdd->setUserData(price); toAdd->setCaptionWithReplacing(spell->mName+" - "+boost::lexical_cast<std::string>(price)+"#{sgp}"); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 2ece05cb6..69d69519f 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -7,6 +7,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" @@ -336,12 +337,12 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); - std::pair<std::string, const ESM::Spell*> result = MWBase::Environment::get().getWorld()->createRecord(mSpell); + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->createRecord(mSpell); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); - spells.add (result.first); + spells.add (spell->mId); MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); @@ -399,8 +400,7 @@ namespace MWGui float fSpellMakingValueMult = store.get<ESM::GameSetting>().find("fSpellMakingValueMult")->getFloat(); - /// \todo mercantile - int price = int(y) * fSpellMakingValueMult; + int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,int(y) * fSpellMakingValueMult,true); mPriceLabel->setCaption(boost::lexical_cast<std::string>(int(price))); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 9d6842068..4b47bb025 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -253,7 +253,10 @@ void StatsWindow::onFrame () setFactions(PCstats.getFactionRanks()); - setBirthSign(MWBase::Environment::get().getWorld()->getPlayer().getBirthsign()); + const std::string &signId = + MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); + + setBirthSign(signId); if (mChanged) updateSkillArea(); @@ -426,11 +429,13 @@ void StatsWindow::updateSkillArea() if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::ESMStore &store = world->getStore(); + const ESM::NPC *player = + world->getPlayer().getPlayer().get<ESM::NPC>()->mBase; // race tooltip - const ESM::Race* playerRace = - store.get<ESM::Race>().find (MWBase::Environment::get().getWorld()->getPlayer().getRace()); + const ESM::Race* playerRace = store.get<ESM::Race>().find(player->mRace); MyGUI::Widget* raceWidget; getWidget(raceWidget, "RaceText"); @@ -440,11 +445,14 @@ void StatsWindow::updateSkillArea() // class tooltip MyGUI::Widget* classWidget; - const ESM::Class& playerClass = MWBase::Environment::get().getWorld()->getPlayer().getClass(); + + const ESM::Class *playerClass = + store.get<ESM::Class>().find(player->mClass); + getWidget(classWidget, "ClassText"); - ToolTips::createClassToolTip(classWidget, playerClass); + ToolTips::createClassToolTip(classWidget, *playerClass); getWidget(classWidget, "Class_str"); - ToolTips::createClassToolTip(classWidget, playerClass); + ToolTips::createClassToolTip(classWidget, *playerClass); if (!mFactions.empty()) { diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 6498cd712..0707ad985 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -6,10 +6,16 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" + +#include "../mwworld/player.hpp" + #include "inventorywindow.hpp" namespace MWGui @@ -52,6 +58,8 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked); mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked); + mIncreaseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onIncreaseButtonClicked); + mDecreaseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onDecreaseButtonClicked); setCoord(400, 0, 400, 300); @@ -63,6 +71,7 @@ namespace MWGui setTitle(MWWorld::Class::get(actor).getName(actor)); mCurrentBalance = 0; + mCurrentMerchantOffer = 0; mWindowManager.getInventoryWindow()->startTrade(); @@ -135,6 +144,9 @@ namespace MWGui void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender) { + const MWWorld::Store<ESM::GameSetting> &gmst = + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); + // were there any items traded at all? MWWorld::ContainerStore& playerBought = mWindowManager.getInventoryWindow()->getBoughtItems(); MWWorld::ContainerStore& merchantBought = getBoughtItems(); @@ -178,6 +190,56 @@ namespace MWGui return; } + if(mCurrentBalance > mCurrentMerchantOffer) + { + //if npc is a creature: reject (no haggle) + if (mPtr.getTypeName() != typeid(ESM::NPC).name()) + { + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sNotifyMessage9}", std::vector<std::string>()); + return; + } + + int a = abs(mCurrentMerchantOffer); + int b = abs(mCurrentBalance); + int d = 0; + if (mCurrentMerchantOffer<0) d = int(100 * (a - b) / a); + else d = int(100 * (b - a) / a); + + float clampedDisposition = std::max<int>(0,std::min<int>(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)),100)); + + MWMechanics::NpcStats sellerSkill = MWWorld::Class::get(mPtr).getNpcStats(mPtr); + MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(mPtr).getCreatureStats(mPtr); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); + MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); + + float a1 = std::min(playerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + 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(sellerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + 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); + + float pcTerm = (clampedDisposition - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm(); + float npcTerm = (d1 + e1 + f1) * sellerStats.getFatigueTerm(); + float x = gmst.find("fBargainOfferMulti")->getFloat() * d + gmst.find("fBargainOfferBase")->getFloat(); + if (mCurrentMerchantOffer<0) x += abs(int(pcTerm - npcTerm)); + else x += abs(int(npcTerm - pcTerm)); + + int roll = std::rand()%100 + 1; + if(roll > x) //trade refused + { + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sNotifyMessage9}", std::vector<std::string>()); + /// \todo adjust npc temporary disposition by iBarterSuccessDisposition or iBarterFailDisposition + return ; + } + } + + +/// \todo adjust npc temporary disposition by iBarterSuccessDisposition or iBarterFailDisposition + // success! make the item transfer. transferBoughtItems(); mWindowManager.getInventoryWindow()->transferBoughtItems(); @@ -202,6 +264,20 @@ namespace MWGui mWindowManager.removeGuiMode(GM_Barter); } + void TradeWindow::onIncreaseButtonClicked(MyGUI::Widget* _sender) + { + if(mCurrentBalance<=-1) mCurrentBalance -= 1; + if(mCurrentBalance>=1) mCurrentBalance += 1; + updateLabels(); + } + + void TradeWindow::onDecreaseButtonClicked(MyGUI::Widget* _sender) + { + if(mCurrentBalance<-1) mCurrentBalance += 1; + if(mCurrentBalance>1) mCurrentBalance -= 1; + updateLabels(); + } + void TradeWindow::updateLabels() { mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast<std::string>(mWindowManager.getInventoryWindow()->getPlayerGold())); @@ -320,19 +396,17 @@ namespace MWGui void TradeWindow::sellToNpc(MWWorld::Ptr item, int count) { - /// \todo price adjustment depending on merchantile skill - - mCurrentBalance -= MWWorld::Class::get(item).getValue(item) * count; + mCurrentBalance -= MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,true); + mCurrentMerchantOffer -= MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,true); updateLabels(); } void TradeWindow::buyFromNpc(MWWorld::Ptr item, int count) { - /// \todo price adjustment depending on merchantile skill - - mCurrentBalance += MWWorld::Class::get(item).getValue(item) * count; + mCurrentBalance += MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,false); + mCurrentMerchantOffer += MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,false); updateLabels(); } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 4ec55045c..db386d8b6 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -55,11 +55,14 @@ namespace MWGui MyGUI::TextBox* mMerchantGold; int mCurrentBalance; + int mCurrentMerchantOffer; void onWindowResize(MyGUI::Window* _sender); void onFilterChanged(MyGUI::Widget* _sender); void onOfferButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender); + void onIncreaseButtonClicked(MyGUI::Widget* _sender); + void onDecreaseButtonClicked(MyGUI::Widget* _sender); // don't show items that the NPC has equipped in his trade-window. virtual bool ignoreEquippedItems() { return true; } diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 1e9841afb..ba39ee601 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -7,6 +7,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" @@ -78,8 +79,8 @@ namespace MWGui for (int i=0; i<3; ++i) { - /// \todo mercantile skill - int price = pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (); + int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer + (mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); std::string skin = (price > mWindowManager.getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton"; @@ -119,8 +120,8 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - /// \todo mercantile skill int price = pcStats.getSkill (skillId).getBase() * store.get<ESM::GameSetting>().find("iTrainingMod")->getInt (); + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); if (mWindowManager.getInventoryWindow()->getPlayerGold()<price) return; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 0e7ebaaa3..abbc6172f 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -69,9 +69,10 @@ namespace MWGui price = d/gmst.find("fTravelMult")->getFloat(); } + price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true); + MyGUI::Button* toAdd = mDestinationsView->createWidget<MyGUI::Button>((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; - /// \todo price adjustment depending on merchantile skill if(interior) toAdd->setUserString("interior","y"); else diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8e4187f7f..963a1fe17 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -83,7 +83,6 @@ WindowManager::WindowManager( , mSpellCreationDialog(NULL) , mEnchantingDialog(NULL) , mTrainingWindow(NULL) - , mPlayerClass() , mPlayerName() , mPlayerRaceId() , mPlayerAttributes() @@ -499,8 +498,7 @@ void WindowManager::setValue (const std::string& id, int value) void WindowManager::setPlayerClass (const ESM::Class &class_) { - mPlayerClass = class_; - mStatsWindow->setValue("class", mPlayerClass.mName); + mStatsWindow->setValue("class", class_.mName); } void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) @@ -584,6 +582,8 @@ void WindowManager::onFrame (float frameDuration) mDragAndDrop->mDraggedWidget->setPosition(MyGUI::InputManager::getInstance().getMousePosition()); } + mDialogueWindow->onFrame(); + mInventoryWindow->onFrame(); mStatsWindow->onFrame(); @@ -774,6 +774,13 @@ void WindowManager::pushGuiMode(GuiMode mode) if (mode==GM_Inventory && mAllowed==GW_None) return; + + // If this mode already exists somewhere in the stack, just bring it to the front. + if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) + { + mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); + } + mGuiModes.push_back(mode); bool gameMode = !isGuiMode(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index aa796343e..2e684b5da 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -261,7 +261,6 @@ namespace MWGui /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager - ESM::Class mPlayerClass; std::string mPlayerName; std::string mPlayerRaceId; std::map<int, MWMechanics::Stat<int> > mPlayerAttributes; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 5ccdc5750..c07c60209 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -279,7 +279,7 @@ void MWMechanics::Alchemy::addPotion (const std::string& name) newRecord.mEffects.mList = mEffects; - record = MWBase::Environment::get().getWorld()->createRecord (newRecord).second; + record = MWBase::Environment::get().getWorld()->createRecord (newRecord); } MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), record->mId); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 95e721c01..e94adf458 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -232,4 +232,14 @@ namespace MWMechanics mDead = false; } } + + bool CreatureStats::hasCommonDisease() const + { + return mSpells.hasCommonDisease(); + } + + bool CreatureStats::hasBlightDisease() const + { + return mSpells.hasBlightDisease(); + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 671dcd439..cdeee6853 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -109,6 +109,10 @@ namespace MWMechanics bool isDead() const; void resurrect(); + + bool hasCommonDisease() const; + + bool hasBlightDisease() const; }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 94418c522..3b5a80ef3 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -37,16 +37,17 @@ namespace MWMechanics creatureStats.getAttribute(5).setBase (player->mNpdt52.mEndurance); creatureStats.getAttribute(6).setBase (player->mNpdt52.mPersonality); creatureStats.getAttribute(7).setBase (player->mNpdt52.mLuck); + + const MWWorld::ESMStore &esmStore = + MWBase::Environment::get().getWorld()->getStore(); // race if (mRaceSelected) { const ESM::Race *race = - MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find ( - MWBase::Environment::get().getWorld()->getPlayer().getRace() - ); + esmStore.get<ESM::Race>().find(player->mRace); - bool male = MWBase::Environment::get().getWorld()->getPlayer().isMale(); + bool male = (player->mFlags & ESM::NPC::Female) == 0; for (int i=0; i<8; ++i) { @@ -89,11 +90,13 @@ namespace MWMechanics } // birthsign - if (!MWBase::Environment::get().getWorld()->getPlayer().getBirthsign().empty()) + const std::string &signId = + MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); + + if (!signId.empty()) { const ESM::BirthSign *sign = - MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find ( - MWBase::Environment::get().getWorld()->getPlayer().getBirthsign()); + esmStore.get<ESM::BirthSign>().find(signId); for (std::vector<std::string>::const_iterator iter (sign->mPowers.mList.begin()); iter!=sign->mPowers.mList.end(); ++iter) @@ -105,11 +108,12 @@ namespace MWMechanics // class if (mClassSelected) { - const ESM::Class& class_ = MWBase::Environment::get().getWorld()->getPlayer().getClass(); + const ESM::Class *class_ = + esmStore.get<ESM::Class>().find(player->mClass); for (int i=0; i<2; ++i) { - int attribute = class_.mData.mAttribute[i]; + int attribute = class_->mData.mAttribute[i]; if (attribute>=0 && attribute<8) { creatureStats.getAttribute(attribute).setBase ( @@ -123,7 +127,7 @@ namespace MWMechanics for (int i2=0; i2<5; ++i2) { - int index = class_.mData.mSkills[i2][i]; + int index = class_->mData.mSkills[i2][i]; if (index>=0 && index<27) { @@ -134,12 +138,12 @@ namespace MWMechanics } const MWWorld::Store<ESM::Skill> &skills = - MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>(); + esmStore.get<ESM::Skill>(); MWWorld::Store<ESM::Skill>::iterator iter = skills.begin(); for (; iter != skills.end(); ++iter) { - if (iter->mData.mSpecialization==class_.mData.mSpecialization) + if (iter->mData.mSpecialization==class_->mData.mSpecialization) { int index = iter->mIndex; @@ -265,13 +269,19 @@ namespace MWMechanics // basic player profile; should not change anymore after the creation phase is finished. MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); - - MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Player &player = world->getPlayer(); + + MWBase::World *world = MWBase::Environment::get().getWorld(); + const ESM::NPC *player = + world->getPlayer().getPlayer().get<ESM::NPC>()->mBase; - winMgr->setValue ("name", player.getName()); - winMgr->setValue ("race", world->getStore().get<ESM::Race>().find (player.getRace())->mName); - winMgr->setValue ("class", player.getClass().mName); + const ESM::Race *race = + world->getStore().get<ESM::Race>().find(player->mRace); + const ESM::Class *cls = + world->getStore().get<ESM::Class>().find(player->mClass); + + winMgr->setValue ("name", player->mName); + winMgr->setValue ("race", race->mName); + winMgr->setValue ("class", cls->mName); mUpdatePlayer = false; @@ -280,8 +290,8 @@ namespace MWMechanics for (int i=0; i<5; ++i) { - minorSkills[i] = player.getClass().mData.mSkills[i][0]; - majorSkills[i] = player.getClass().mData.mSkills[i][1]; + minorSkills[i] = cls->mData.mSkills[i][0]; + majorSkills[i] = cls->mData.mSkills[i][1]; } winMgr->configureSkills (majorSkills, minorSkills); @@ -297,14 +307,33 @@ namespace MWMechanics void MechanicsManager::setPlayerName (const std::string& name) { - MWBase::Environment::get().getWorld()->getPlayer().setName (name); + MWBase::World *world = MWBase::Environment::get().getWorld(); + + ESM::NPC player = + *world->getPlayer().getPlayer().get<ESM::NPC>()->mBase; + player.mName = name; + + world->createRecord(player); + mUpdatePlayer = true; } void MechanicsManager::setPlayerRace (const std::string& race, bool male) { - MWBase::Environment::get().getWorld()->getPlayer().setGender (male); - MWBase::Environment::get().getWorld()->getPlayer().setRace (race); + MWBase::World *world = MWBase::Environment::get().getWorld(); + + ESM::NPC player = + *world->getPlayer().getPlayer().get<ESM::NPC>()->mBase; + + player.mRace = race; + + player.mFlags |= ESM::NPC::Female; + if (male) { + player.mFlags ^= ESM::NPC::Female; + } + + world->createRecord(player); + mRaceSelected = true; buildPlayer(); mUpdatePlayer = true; @@ -312,29 +341,148 @@ namespace MWMechanics void MechanicsManager::setPlayerBirthsign (const std::string& id) { - MWBase::Environment::get().getWorld()->getPlayer().setBirthsign (id); + MWBase::Environment::get().getWorld()->getPlayer().setBirthSign(id); buildPlayer(); mUpdatePlayer = true; } void MechanicsManager::setPlayerClass (const std::string& id) { - MWBase::Environment::get().getWorld()->getPlayer().setClass ( - *MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find (id) - ); + MWBase::World *world = MWBase::Environment::get().getWorld(); + + ESM::NPC player = + *world->getPlayer().getPlayer().get<ESM::NPC>()->mBase; + player.mClass = id; + + world->createRecord(player); + mClassSelected = true; buildPlayer(); mUpdatePlayer = true; } - void MechanicsManager::setPlayerClass (const ESM::Class& class_) + void MechanicsManager::setPlayerClass (const ESM::Class &cls) { - MWBase::Environment::get().getWorld()->getPlayer().setClass (class_); + MWBase::World *world = MWBase::Environment::get().getWorld(); + + const ESM::Class *ptr = world->createRecord(cls); + + ESM::NPC player = + *world->getPlayer().getPlayer().get<ESM::NPC>()->mBase; + player.mClass = ptr->mId; + + world->createRecord(player); + mClassSelected = true; buildPlayer(); mUpdatePlayer = true; } - + + std::string toLower (const std::string& name) + { + std::string lowerCase; + + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + return lowerCase; + } + + int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr) + { + MWMechanics::NpcStats npcSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); + float x = npcSkill.getDisposition(); + + MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>(); + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::LiveCellRef<ESM::NPC>* player = playerPtr.get<ESM::NPC>(); + MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); + MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); + + if (toLower(npc->mBase->mRace) == toLower(player->mBase->mRace)) x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispRaceMod")->getFloat(); + + x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispPersonalityMult")->getFloat() + * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispPersonalityBase")->getFloat()); + + float reaction = 0; + int rank = 0; + std::string npcFaction = ""; + if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first; + + if (playerSkill.getFactionRanks().find(toLower(npcFaction)) != playerSkill.getFactionRanks().end()) + { + for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(toLower(npcFaction))->mReactions.begin(); + it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(toLower(npcFaction))->mReactions.end(); it++) + { + if(toLower(it->mFaction) == toLower(npcFaction)) reaction = it->mReaction; + } + rank = playerSkill.getFactionRanks().find(toLower(npcFaction))->second; + } + else if (npcFaction != "") + { + for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(toLower(npcFaction))->mReactions.begin(); + it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(toLower(npcFaction))->mReactions.end();it++) + { + if(playerSkill.getFactionRanks().find(toLower(it->mFaction)) != playerSkill.getFactionRanks().end() ) + { + if(it->mReaction<reaction) reaction = it->mReaction; + } + } + rank = 0; + } + else + { + reaction = 0; + rank = 0; + } + x += (MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispFactionRankMult")->getFloat() * rank + + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispFactionRankBase")->getFloat()) + * MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispFactionMod")->getFloat() * reaction; + + /// \todo implement bounty and disease + //x -= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispCrimeMod") * pcBounty; + //if (pc has a disease) x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispDiseaseMod"); + if (playerSkill.getDrawState() == MWMechanics::DrawState_::DrawState_Weapon) x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispWeaponDrawn")->getFloat(); + + int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used + return effective_disposition; + } + + int MechanicsManager::getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) + { + if (ptr.getTypeName() == typeid(ESM::Creature).name()) + return basePrice; + + MWMechanics::NpcStats sellerSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); + MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); + MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); + + int clampedDisposition = std::min(getDerivedDisposition(ptr),100); + float a = std::min(playerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + 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(sellerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + 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); + + float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); + float npcTerm = (d + e + f) * sellerStats.getFatigueTerm(); + float buyTerm = 0.01 * (100 - 0.5 * (pcTerm - npcTerm)); + float sellTerm = 0.01 * (50 - 0.5 * (npcTerm - pcTerm)); + + float x; + if(buying) x = buyTerm; + else x = std::min(buyTerm, sellTerm); + int offerPrice; + if (x < 1) offerPrice = int(x * basePrice); + if (x >= 1) offerPrice = basePrice + int((x - 1) * basePrice); + offerPrice = std::max(1, offerPrice); + return offerPrice; + } + int MechanicsManager::countDeaths (const std::string& id) const { return mActors.countDeaths (id); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 38536d3bd..0344d951c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -79,6 +79,12 @@ namespace MWMechanics virtual void restoreDynamicStats(); ///< If the player is sleeping, this should be called every hour. + 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. + + virtual int getDerivedDisposition(const MWWorld::Ptr& ptr); + ///< Calculate the diposition of an NPC toward the player. + virtual int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index c0da99aaf..5b2ce739f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -18,8 +18,9 @@ #include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() -: mMovementFlags (0), mDrawState (DrawState_Nothing) -, mLevelProgress(0) +: mMovementFlags (0), mDrawState (DrawState_Nothing), mBounty (0) +, mLevelProgress(0), mDisposition(0) + { mSkillIncreases.resize (ESM::Attribute::Length); for (int i=0; i<ESM::Attribute::Length; ++i) @@ -36,6 +37,16 @@ void MWMechanics::NpcStats::setDrawState (DrawState_ state) mDrawState = state; } +int MWMechanics::NpcStats::getDisposition() const +{ + return mDisposition; +} + +void MWMechanics::NpcStats::setDisposition(int disposition) +{ + mDisposition = disposition; +} + bool MWMechanics::NpcStats::getMovementFlag (Flag flag) const { return mMovementFlags & flag; @@ -238,3 +249,13 @@ bool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const { return mUsedIds.find (id)!=mUsedIds.end(); } + +int MWMechanics::NpcStats::getBounty() const +{ + return mBounty; +} + +void MWMechanics::NpcStats::setBounty (int bounty) +{ + mBounty = bounty; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 48e63d7b6..35af4afa0 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -43,8 +43,10 @@ namespace MWMechanics std::map<std::string, int> mFactionRank; DrawState_ mDrawState; + int mDisposition; unsigned int mMovementFlags; Stat<float> mSkill[27]; + int mBounty; int mLevelProgress; // 0-10 @@ -60,6 +62,10 @@ namespace MWMechanics void setDrawState (DrawState_ state); + int getDisposition() const; + + void setDisposition(int disposition); + bool getMovementFlag (Flag flag) const; void setMovementFlag (Flag flag, bool state); @@ -92,6 +98,10 @@ namespace MWMechanics void flagAsUsed (const std::string& id); bool hasBeenUsed (const std::string& id) const; + + int getBounty() const; + + void setBounty (int bounty); }; } diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 3ff10cdb8..ef084f479 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -75,4 +75,32 @@ namespace MWMechanics { return mSelectedSpell; } + + bool Spells::hasCommonDisease() const + { + for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) + { + const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (*iter); + + if (spell->mData.mFlags & ESM::Spell::ST_Disease) + return true; + } + + return false; + } + + bool Spells::hasBlightDisease() const + { + for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) + { + const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (*iter); + + if (spell->mData.mFlags & ESM::Spell::ST_Blight) + return true; + } + + return false; + } } diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index d90f5b502..12308661b 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -55,6 +55,10 @@ namespace MWMechanics const std::string getSelectedSpell() const; ///< May return an empty string. + + bool hasCommonDisease() const; + + bool hasBlightDisease() const; }; } diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 0a6ea01b3..9917254ee 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -146,8 +146,13 @@ namespace MWWorld } template <class T> - T *insert(const T &x) { + const T *insert(const T &x) { Store<T> &store = const_cast<Store<T> &>(get<T>()); + if (store.search(x.mId) != 0) { + std::ostringstream msg; + msg << "Try to override existing record '" << x.mId << "'"; + throw std::runtime_error(msg.str()); + } T record = x; std::ostringstream id; @@ -168,10 +173,30 @@ namespace MWWorld }; template <> - inline ESM::Cell *ESMStore::insert<ESM::Cell>(const ESM::Cell &cell) { + inline const ESM::Cell *ESMStore::insert<ESM::Cell>(const ESM::Cell &cell) { return mCells.insert(cell); } + template <> + inline const ESM::NPC *ESMStore::insert<ESM::NPC>(const ESM::NPC &npc) { + if (StringUtils::ciEqual(npc.mId, "player")) { + return mNpcs.insert(npc); + } else if (mNpcs.search(npc.mId) != 0) { + std::ostringstream msg; + msg << "Try to override existing record '" << npc.mId << "'"; + throw std::runtime_error(msg.str()); + } + ESM::NPC record = npc; + + std::ostringstream id; + id << "$dynamic" << mDynamicCount++; + record.mId = id.str(); + + ESM::NPC *ptr = mNpcs.insert(record); + mIds[ptr->mId] = ESM::REC_NPC_; + return ptr; + } + template <> inline const Store<ESM::Activator> &ESMStore::get<ESM::Activator>() const { return mActivators; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a1318f727..3414ba448 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -13,33 +13,16 @@ namespace MWWorld { - Player::Player (const ESM::NPC *player, const MWBase::World& world) : - mCellStore (0), mClass (0), - mAutoMove (false), mForwardBackward (0) + Player::Player (const ESM::NPC *player, const MWBase::World& world) + : mCellStore(0), + mAutoMove(false), + mForwardBackward (0) { mPlayer.mBase = player; mPlayer.mRef.mRefID = "player"; - mName = player->mName; - mMale = !(player->mFlags & ESM::NPC::Female); - mRace = player->mRace; float* playerPos = mPlayer.mData.getPosition().pos; playerPos[0] = playerPos[1] = playerPos[2] = 0; - - /// \todo Do not make a copy of classes defined in esm/p records. - mClass = new ESM::Class (*world.getStore().get<ESM::Class>().find (player->mClass)); - } - - Player::~Player() - { - delete mClass; - } - - void Player::setClass (const ESM::Class& class_) - { - ESM::Class *new_class = new ESM::Class (class_); - delete mClass; - mClass = new_class; } void Player::setDrawState (MWMechanics::DrawState_ state) @@ -102,5 +85,4 @@ namespace MWWorld MWWorld::Ptr ptr = getPlayer(); return MWWorld::Class::get(ptr).getNpcStats(ptr).getDrawState(); } - } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 68df2ec6d..1c1ef76cf 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -1,8 +1,6 @@ #ifndef GAME_MWWORLD_PLAYER_H #define GAME_MWWORLD_PLAYER_H -#include "OgreCamera.h" - #include "../mwworld/cellstore.hpp" #include "../mwworld/refdata.hpp" #include "../mwworld/ptr.hpp" @@ -21,21 +19,17 @@ namespace MWWorld /// \brief NPC object representing the player and additional player data class Player { - LiveCellRef<ESM::NPC> mPlayer; - MWWorld::CellStore *mCellStore; - std::string mName; - bool mMale; - std::string mRace; - std::string mBirthsign; - ESM::Class *mClass; - bool mAutoMove; - int mForwardBackward; + LiveCellRef<ESM::NPC> mPlayer; + MWWorld::CellStore *mCellStore; + std::string mSign; + + bool mAutoMove; + int mForwardBackward; + public: Player(const ESM::NPC *player, const MWBase::World& world); - ~Player(); - void setCell (MWWorld::CellStore *cellStore) { mCellStore = cellStore; @@ -47,55 +41,16 @@ namespace MWWorld return ptr; } - void setName (const std::string& name) - { - mName = name; + void setBirthSign(const std::string &sign) { + mSign = sign; } - void setGender (bool male) - { - mMale = male; + const std::string &getBirthSign() const { + return mSign; } - void setRace (const std::string& race) - { - mRace = race; - } - - void setBirthsign (const std::string& birthsign) - { - mBirthsign = birthsign; - } - - void setClass (const ESM::Class& class_); - void setDrawState (MWMechanics::DrawState_ state); - std::string getName() const - { - return mName; - } - - bool isMale() const - { - return mMale; - } - - std::string getRace() const - { - return mRace; - } - - std::string getBirthsign() const - { - return mBirthsign; - } - - const ESM::Class& getClass() const - { - return *mClass; - } - bool getAutoMove() const { return mAutoMove; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 97b61b528..a406a39de 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -168,26 +168,6 @@ namespace MWWorld } } - T *search(const std::string &id) { - std::string key = StringUtils::lowerCase(id); - typename Dynamic::iterator dit = mDynamic.find(key); - - if (dit != mDynamic.end()) { - return &dit->second; - } - return 0; - } - - T *find(const std::string &id) { - T *ptr = search(id); - if (ptr == 0) { - std::ostringstream msg; - msg << "Object '" << id << "' not found (non-const)"; - throw std::runtime_error(msg.str()); - } - return ptr; - } - T *insert(const T &item) { std::string id = StringUtils::lowerCase(item.mId); std::pair<typename Dynamic::iterator, bool> result = @@ -523,43 +503,6 @@ namespace MWWorld } } - ESM::Cell *search(const std::string &id) { - std::string key = StringUtils::lowerCase(id); - DynamicInt::iterator it = mDynamicInt.find(key); - if (it != mDynamicInt.end()) { - return &it->second; - } - return 0; - } - - ESM::Cell *find(const std::string &id) { - ESM::Cell *ptr = search(id); - if (ptr == 0) { - std::ostringstream msg; - msg << "Interior '" << id << "' not found (non-const)"; - throw std::runtime_error(msg.str()); - } - return ptr; - } - - ESM::Cell *search(int x, int y) { - DynamicExt::iterator it = mDynamicExt.find(std::make_pair(x, y)); - if (it != mDynamicExt.end()) { - return &it->second; - } - return 0; - } - - ESM::Cell *find(int x, int y) { - ESM::Cell *ptr = search(x, y); - if (ptr == 0) { - std::ostringstream msg; - msg << "Exterior at (" << x << ", " << y << ") not found (non-const"; - throw std::runtime_error(msg.str()); - } - return ptr; - } - ESM::Cell *insert(const ESM::Cell &cell) { if (search(cell) != 0) { std::ostringstream msg; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9f5b7520c..af5745744 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -779,22 +779,19 @@ namespace MWWorld return mRendering->toggleRenderMode (mode); } - std::pair<std::string, const ESM::Potion *> World::createRecord (const ESM::Potion& record) + const ESM::Potion *World::createRecord (const ESM::Potion& record) { - const ESM::Potion *ptr = mStore.insert(record); - return std::make_pair(ptr->mId, ptr); + return mStore.insert(record); } - std::pair<std::string, const ESM::Class *> World::createRecord (const ESM::Class& record) + const ESM::Class *World::createRecord (const ESM::Class& record) { - const ESM::Class *ptr = mStore.insert(record); - return std::make_pair(ptr->mId, ptr); + return mStore.insert(record); } - std::pair<std::string, const ESM::Spell *> World::createRecord (const ESM::Spell& record) + const ESM::Spell *World::createRecord (const ESM::Spell& record) { - const ESM::Spell *ptr = mStore.insert(record); - return std::make_pair(ptr->mId, ptr); + return mStore.insert(record); } const ESM::Cell *World::createRecord (const ESM::Cell& record) @@ -802,6 +799,11 @@ namespace MWWorld return mStore.insert(record); } + const ESM::NPC *World::createRecord(const ESM::NPC &record) + { + return mStore.insert(record); + } + void World::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2b2ad7821..0962c292c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -250,21 +250,26 @@ namespace MWWorld ///< Toggle a render mode. ///< \return Resulting mode - virtual std::pair<std::string, const ESM::Potion *> createRecord (const ESM::Potion& record); + virtual const ESM::Potion *createRecord (const ESM::Potion& record); ///< Create a new recrod (of type potion) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record - virtual std::pair<std::string, const ESM::Spell *> createRecord (const ESM::Spell& record); + virtual const ESM::Spell *createRecord (const ESM::Spell& record); ///< Create a new recrod (of type spell) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record - virtual std::pair<std::string, const ESM::Class *> createRecord (const ESM::Class& record); + virtual const ESM::Class *createRecord (const ESM::Class& record); ///< Create a new recrod (of type class) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record virtual const ESM::Cell *createRecord (const ESM::Cell& record); ///< Create a new recrod (of type cell) in the ESM store. - /// \return ID, pointer to created record + /// \return pointer to created record + + virtual const ESM::NPC *createRecord(const ESM::NPC &record); + ///< Create a new recrod (of type npc) in the ESM store. + /// \return pointer to created record + virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number = 1); diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index d2ec7ca82..42f6a8e68 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -69,7 +69,7 @@ btVector3 ManualBulletShapeLoader::getbtVector(Ogre::Vector3 &v) void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) { - cShape = static_cast<BulletShape *>(resource); + cShape = static_cast<OEngine::Physic::BulletShape *>(resource); resourceName = cShape->getName(); cShape->mCollide = false; mBoundingBox = NULL; @@ -314,8 +314,8 @@ void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape *shape, int flags void ManualBulletShapeLoader::load(const std::string &name,const std::string &group) { // Check if the resource already exists - Ogre::ResourcePtr ptr = BulletShapeManager::getSingleton().getByName(name, group); + Ogre::ResourcePtr ptr = OEngine::Physic::BulletShapeManager::getSingleton().getByName(name, group); if (!ptr.isNull()) return; - BulletShapeManager::getSingleton().create(name,group,true,this); + OEngine::Physic::BulletShapeManager::getSingleton().create(name,group,true,this); } diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index 82ac227a0..2190fda1b 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -48,7 +48,7 @@ namespace NifBullet /** *Load bulletShape from NIF files. */ -class ManualBulletShapeLoader : public BulletShapeLoader +class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader { public: @@ -104,7 +104,7 @@ private: - BulletShape* cShape;//current shape + OEngine::Physic::BulletShape* cShape;//current shape btTriangleMesh *mTriMesh; btBoxShape *mBoundingBox; btBvhTriangleMeshShape* currentShape;//the shape curently under construction diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index dd3bca692..071a5ee8a 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -1,6 +1,8 @@ #include "BulletShapeLoader.h" - +namespace OEngine { +namespace Physic +{ BulletShape::BulletShape(Ogre::ResourceManager* creator, const Ogre::String &name, Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual, @@ -64,21 +66,24 @@ size_t BulletShape::calculateSize() const //============================================================================================================= -template<> BulletShapeManager *Ogre::Singleton<BulletShapeManager>::msSingleton = 0; +BulletShapeManager *BulletShapeManager::sThis = 0; BulletShapeManager *BulletShapeManager::getSingletonPtr() { - return msSingleton; + return sThis; } BulletShapeManager &BulletShapeManager::getSingleton() { - assert(msSingleton); - return(*msSingleton); + assert(sThis); + return(*sThis); } BulletShapeManager::BulletShapeManager() { + assert(!sThis); + sThis = this; + mResourceType = "BulletShape"; // low, because it will likely reference other resources @@ -92,6 +97,8 @@ BulletShapeManager::~BulletShapeManager() { // and this is how we unregister it Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType); + + sThis = 0; } BulletShapePtr BulletShapeManager::load(const Ogre::String &name, const Ogre::String &group) @@ -124,3 +131,6 @@ void BulletShapeLoader::loadResource(Ogre::Resource *resource) void BulletShapeLoader::load(const std::string &name,const std::string &group) {} + +} +} diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index 21d21777a..d9bd5879a 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -5,8 +5,10 @@ #include <OgreResourceManager.h> #include <btBulletCollisionCommon.h> #include <OgreVector3.h> -//For some reason, Ogre Singleton cannot be used in another namespace, that's why there is no namespace here. -//But the risk of name collision seems pretty low here. + +namespace OEngine { +namespace Physic +{ /** *Define a new resource which describe a Shape usable by bullet.See BulletShapeManager for how to get/use them. @@ -107,7 +109,7 @@ public: *Important Note: i have no idea of what happen if you try to load two time the same resource without unloading. *It won't crash, but it might lead to memory leaks(I don't know how Ogre handle this). So don't do it! */ -class BulletShapeManager : public Ogre::ResourceManager, public Ogre::Singleton<BulletShapeManager> +class BulletShapeManager : public Ogre::ResourceManager { protected: @@ -116,6 +118,16 @@ protected: const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, const Ogre::NameValuePairList *createParams); + static BulletShapeManager *sThis; + +private: + /** \brief Explicit private copy constructor. This is a forbidden operation.*/ + BulletShapeManager(const BulletShapeManager &); + + /** \brief Private operator= . This is a forbidden operation. */ + BulletShapeManager& operator=(const BulletShapeManager &); + + public: BulletShapeManager(); @@ -139,4 +151,7 @@ public: virtual void load(const std::string &name,const std::string &group); }; +} +} + #endif diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d78e25ce7..b39ba53a2 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -251,7 +251,6 @@ namespace Physic PhysicEngine::~PhysicEngine() { - HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); for (; hf_it != mHeightFieldMap.end(); ++hf_it) { @@ -293,6 +292,8 @@ namespace Physic delete broadphase; delete pairCache; delete mShapeLoader; + + delete BulletShapeManager::getSingletonPtr(); } void PhysicEngine::addHeightField(float* heights,