#include "tradewindow.hpp" #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.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 { const float TradeWindow::sBalanceChangeInitialPause = 0.5; const float TradeWindow::sBalanceChangeInterval = 0.1; TradeWindow::TradeWindow() : WindowBase("openmw_trade_window.layout") , ContainerBase(NULL) // no drag&drop , mCurrentBalance(0) , mBalanceButtonsState(BBS_None) , mBalanceChangePause(0.0) { // items the NPC is wearing should not be for trade mDisplayEquippedItems = false; MyGUI::ScrollView* itemView; MyGUI::Widget* containerWidget; getWidget(containerWidget, "Items"); getWidget(itemView, "ItemView"); setWidgets(containerWidget, itemView); getWidget(mFilterAll, "AllButton"); getWidget(mFilterWeapon, "WeaponButton"); getWidget(mFilterApparel, "ApparelButton"); getWidget(mFilterMagic, "MagicButton"); getWidget(mFilterMisc, "MiscButton"); getWidget(mMaxSaleButton, "MaxSaleButton"); getWidget(mCancelButton, "CancelButton"); getWidget(mOfferButton, "OfferButton"); getWidget(mPlayerGold, "PlayerGold"); getWidget(mMerchantGold, "MerchantGold"); getWidget(mIncreaseButton, "IncreaseButton"); getWidget(mDecreaseButton, "DecreaseButton"); getWidget(mTotalBalance, "TotalBalance"); getWidget(mTotalBalanceLabel, "TotalBalanceLabel"); getWidget(mBottomPane, "BottomPane"); mFilterAll->setStateSelected(true); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked); mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked); mMaxSaleButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onMaxSaleButtonClicked); mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onIncreaseButtonPressed); mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &TradeWindow::onDecreaseButtonPressed); mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &TradeWindow::onBalanceButtonReleased); setCoord(400, 0, 400, 300); static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &TradeWindow::onWindowResize); } void TradeWindow::startTrade(MWWorld::Ptr actor) { setTitle(MWWorld::Class::get(actor).getName(actor)); mCurrentBalance = 0; mCurrentMerchantOffer = 0; MWBase::Environment::get().getWindowManager()->getInventoryWindow()->startTrade(); mBoughtItems.clear(); ContainerBase::openContainer(actor); updateLabels(); drawItems(); } void TradeWindow::onFilterChanged(MyGUI::Widget* _sender) { if (_sender == mFilterAll) setFilter(ContainerBase::Filter_All); else if (_sender == mFilterWeapon) setFilter(ContainerBase::Filter_Weapon); else if (_sender == mFilterApparel) setFilter(ContainerBase::Filter_Apparel); else if (_sender == mFilterMagic) setFilter(ContainerBase::Filter_Magic); else if (_sender == mFilterMisc) setFilter(ContainerBase::Filter_Misc); mFilterAll->setStateSelected(false); mFilterWeapon->setStateSelected(false); mFilterApparel->setStateSelected(false); mFilterMagic->setStateSelected(false); mFilterMisc->setStateSelected(false); static_cast(_sender)->setStateSelected(true); } void TradeWindow::onWindowResize(MyGUI::Window* _sender) { drawItems(); } void TradeWindow::addOrRemoveGold(int amount) { bool goldFound = false; MWWorld::Ptr gold; MWWorld::ContainerStore& playerStore = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getContainerStore(); for (MWWorld::ContainerStoreIterator it = playerStore.begin(); it != playerStore.end(); ++it) { if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) { goldFound = true; gold = *it; } } if (goldFound) { gold.getRefData().setCount(gold.getRefData().getCount() + amount); } else { assert(amount > 0); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); ref.getPtr().getRefData().setCount(amount); playerStore.add(ref.getPtr()); } } void TradeWindow::onFrame(float frameDuration) { if (!mMainWidget->getVisible() || mBalanceButtonsState == BBS_None) return; mBalanceChangePause -= frameDuration; if (mBalanceChangePause < 0.0) { mBalanceChangePause += sBalanceChangeInterval; if (mBalanceButtonsState == BBS_Increase) onIncreaseButtonTriggered(); else if (mBalanceButtonsState == BBS_Decrease) onDecreaseButtonTriggered(); } } void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender) { const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); // were there any items traded at all? MWWorld::ContainerStore& playerBought = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getBoughtItems(); MWWorld::ContainerStore& merchantBought = getBoughtItems(); if (playerBought.begin() == playerBought.end() && merchantBought.begin() == merchantBought.end()) { // user notification MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog11}"); return; } // check if the player can afford this if (mCurrentBalance < 0 && MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold() < std::abs(mCurrentBalance)) { // user notification MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog1}"); return; } // check if the merchant can afford this if (mCurrentBalance > 0 && getMerchantGold() < mCurrentBalance) { // user notification MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog2}"); 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}"); 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(0,std::min(int(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),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}"); int iBarterFailDisposition = gmst.find("iBarterFailDisposition")->getInt(); MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterFailDisposition); return; } //skill use! MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterSuccessDisposition); // success! make the item transfer. MWWorld::ContainerStore& playerBoughtItems = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getBoughtItems(); for (MWWorld::ContainerStoreIterator it = playerBoughtItems.begin(); it != playerBoughtItems.end(); ++it) { if (Misc::StringUtils::ciEqual(it->getCellRef().mOwner, MWWorld::Class::get(mPtr).getId(mPtr))) it->getCellRef().mOwner = ""; } transferBoughtItems(); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->transferBoughtItems(); // add or remove gold from the player. if (mCurrentBalance != 0) addOrRemoveGold(mCurrentBalance); std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { // i give you back your stuff! returnBoughtItems(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getContainerStore()); // now gimme back my stuff! MWBase::Environment::get().getWindowManager()->getInventoryWindow()->returnBoughtItems(MWWorld::Class::get(mPtr).getContainerStore(mPtr)); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); } void TradeWindow::onMaxSaleButtonClicked(MyGUI::Widget* _sender) { mCurrentBalance = getMerchantGold(); updateLabels(); } void TradeWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { mBalanceButtonsState = BBS_Increase; mBalanceChangePause = sBalanceChangeInitialPause; onIncreaseButtonTriggered(); } void TradeWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { mBalanceButtonsState = BBS_Decrease; mBalanceChangePause = sBalanceChangeInitialPause; onDecreaseButtonTriggered(); } void TradeWindow::onBalanceButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id) { mBalanceButtonsState = BBS_None; } void TradeWindow::onIncreaseButtonTriggered() { if(mCurrentBalance<=-1) mCurrentBalance -= 1; if(mCurrentBalance>=1) mCurrentBalance += 1; updateLabels(); } void TradeWindow::onDecreaseButtonTriggered() { if(mCurrentBalance<-1) mCurrentBalance += 1; if(mCurrentBalance>1) mCurrentBalance -= 1; updateLabels(); } void TradeWindow::updateLabels() { mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold())); if (mCurrentBalance > 0) { mTotalBalanceLabel->setCaptionWithReplacing("#{sTotalSold}"); mTotalBalance->setCaption(boost::lexical_cast(mCurrentBalance)); } else { mTotalBalanceLabel->setCaptionWithReplacing("#{sTotalCost}"); mTotalBalance->setCaption(boost::lexical_cast(-mCurrentBalance)); } mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + boost::lexical_cast(getMerchantGold())); } bool TradeWindow::npcAcceptsItem(MWWorld::Ptr item) { if (Misc::StringUtils::ciEqual(item.getCellRef().mRefID, "gold_001")) return false; int services = 0; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->mBase->mHasAI) services = ref->mBase->mAiData.mServices; } else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) { MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->mBase->mHasAI) services = ref->mBase->mAiData.mServices; } return MWWorld::Class::get(item).canSell(item, services); } std::vector TradeWindow::itemsToIgnore() { std::vector items; MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { if (!npcAcceptsItem(*it)) items.push_back(*it); } return items; } void TradeWindow::sellToNpc(MWWorld::Ptr item, int count, bool boughtItem) { int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, boughtItem); mCurrentBalance += diff; mCurrentMerchantOffer += diff; updateLabels(); } void TradeWindow::buyFromNpc(MWWorld::Ptr item, int count, bool soldItem) { int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, !soldItem); mCurrentBalance -= diff; mCurrentMerchantOffer -= diff; updateLabels(); } void TradeWindow::onReferenceUnavailable() { // remove both Trade and Dialogue (since you always trade with the NPC/creature that you have previously talked to) MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } int TradeWindow::getMerchantGold() { int merchantGold; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->mBase->mNpdt52.mGold == -10) merchantGold = ref->mBase->mNpdt12.mGold; else merchantGold = ref->mBase->mNpdt52.mGold; } else // ESM::Creature { MWWorld::LiveCellRef* ref = mPtr.get(); merchantGold = ref->mBase->mData.mGold; } return merchantGold; } }