1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00
OpenMW/apps/openmw/mwgui/inventorywindow.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

855 lines
31 KiB
C++
Raw Normal View History

2012-04-21 10:51:01 +02:00
#include "inventorywindow.hpp"
2018-02-24 15:13:14 +03:00
#include <cmath>
#include <stdexcept>
2015-01-10 02:50:43 +01:00
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
2015-01-10 02:50:43 +01:00
#include <MyGUI_ImageBox.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_Window.h>
2015-05-20 02:18:20 +02:00
#include <osg/Texture2D>
#include <components/misc/strings/algorithm.hpp>
2015-07-07 19:16:32 +02:00
2015-05-20 02:18:20 +02:00
#include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/values.hpp>
2015-01-31 23:27:34 +01:00
#include "../mwbase/environment.hpp"
2014-01-07 19:49:16 +01:00
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/actionequip.hpp"
2013-05-11 18:38:27 +02:00
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/npcstats.hpp"
2015-08-21 21:12:39 +12:00
2013-05-11 18:38:27 +02:00
#include "countdialog.hpp"
#include "draganddrop.hpp"
2013-05-11 18:38:27 +02:00
#include "inventoryitemmodel.hpp"
#include "itemview.hpp"
#include "settings.hpp"
2013-05-11 18:38:27 +02:00
#include "sortfilteritemmodel.hpp"
#include "tooltips.hpp"
2013-05-11 18:38:27 +02:00
#include "tradeitemmodel.hpp"
#include "tradewindow.hpp"
namespace
{
bool isRightHandWeapon(const MWWorld::Ptr& item)
{
if (item.getClass().getType() != ESM::Weapon::sRecordId)
return false;
std::vector<int> equipmentSlots = item.getClass().getEquipmentSlots(item).first;
return (!equipmentSlots.empty() && equipmentSlots.front() == MWWorld::InventoryStore::Slot_CarriedRight);
}
}
2012-04-21 10:51:01 +02:00
namespace MWGui
{
namespace
{
WindowSettingValues getModeSettings(GuiMode mode)
{
switch (mode)
{
case GM_Container:
return makeInventoryContainerWindowSettingValues();
case GM_Companion:
return makeInventoryCompanionWindowSettingValues();
case GM_Barter:
return makeInventoryBarterWindowSettingValues();
default:
return makeInventoryWindowSettingValues();
}
}
}
2012-04-21 10:51:01 +02:00
InventoryWindow::InventoryWindow(
DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem)
2013-05-11 18:38:27 +02:00
: WindowPinnableBase("openmw_inventory_window.layout")
, mDragAndDrop(dragAndDrop)
, mSelectedItem(-1)
2018-10-09 10:21:12 +04:00
, mSortModel(nullptr)
, mTradeModel(nullptr)
2013-07-31 21:40:29 +02:00
, mGuiMode(GM_Inventory)
, mLastXSize(0)
, mLastYSize(0)
2022-05-29 13:24:48 +02:00
, mPreview(std::make_unique<MWRender::InventoryPreview>(parent, resourceSystem, MWMechanics::getPlayer()))
, mTrading(false)
, mUpdateTimer(0.f)
2012-04-21 10:51:01 +02:00
{
2022-05-29 13:24:48 +02:00
mPreviewTexture
= std::make_unique<osgMyGUI::OSGTexture>(mPreview->getTexture(), mPreview->getTextureStateSet());
2015-05-20 02:18:20 +02:00
mPreview->rebuild();
2014-09-13 04:07:40 +02:00
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord
+= MyGUI::newDelegate(this, &InventoryWindow::onWindowResize);
2012-05-12 21:28:04 +02:00
getWidget(mAvatar, "Avatar");
2012-09-13 00:54:32 +02:00
getWidget(mAvatarImage, "AvatarImage");
2012-05-12 21:28:04 +02:00
getWidget(mEncumbranceBar, "EncumbranceBar");
getWidget(mFilterAll, "AllButton");
getWidget(mFilterWeapon, "WeaponButton");
getWidget(mFilterApparel, "ApparelButton");
getWidget(mFilterMagic, "MagicButton");
getWidget(mFilterMisc, "MiscButton");
getWidget(mLeftPane, "LeftPane");
getWidget(mRightPane, "RightPane");
2013-03-16 22:53:33 +01:00
getWidget(mArmorRating, "ArmorRating");
getWidget(mFilterEdit, "FilterEdit");
2012-05-12 21:44:33 +02:00
mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked);
2015-05-20 02:18:20 +02:00
mAvatarImage->setRenderItemTexture(mPreviewTexture.get());
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
2013-05-11 18:38:27 +02:00
getWidget(mItemView, "ItemView");
mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected);
mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &InventoryWindow::onBackgroundSelected);
2012-05-12 22:44:12 +02:00
2012-05-12 21:44:33 +02:00
mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterMagic->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterMisc->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged);
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &InventoryWindow::onNameFilterChanged);
mFilterAll->setStateSelected(true);
2013-12-03 18:42:35 +01:00
setGuiMode(mGuiMode);
adjustPanes();
}
void InventoryWindow::adjustPanes()
{
const float aspect = 0.5; // fixed aspect ratio for the avatar image
int leftPaneWidth = static_cast<int>((mMainWidget->getSize().height - 44 - mArmorRating->getHeight()) * aspect);
mLeftPane->setSize(leftPaneWidth, mMainWidget->getSize().height - 44);
mRightPane->setCoord(mLeftPane->getPosition().left + leftPaneWidth + 4, mRightPane->getPosition().top,
mMainWidget->getSize().width - 12 - leftPaneWidth - 15, mMainWidget->getSize().height - 44);
2013-05-11 18:38:27 +02:00
}
2013-05-15 17:54:18 +02:00
void InventoryWindow::updatePlayer()
{
mPtr = MWBase::Environment::get().getWorld()->getPlayerPtr();
2022-08-31 18:49:50 +02:00
auto tradeModel = std::make_unique<TradeItemModel>(std::make_unique<InventoryItemModel>(mPtr), MWWorld::Ptr());
mTradeModel = tradeModel.get();
if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings
2022-08-31 18:49:50 +02:00
mSortModel->setSourceModel(std::move(tradeModel));
else
2022-08-31 19:03:45 +02:00
{
auto sortModel = std::make_unique<SortFilterItemModel>(std::move(tradeModel));
mSortModel = sortModel.get();
mItemView->setModel(std::move(sortModel));
}
mSortModel->setNameFilter(mFilterEdit->getCaption());
mFilterAll->setStateSelected(true);
mFilterWeapon->setStateSelected(false);
mFilterApparel->setStateSelected(false);
mFilterMagic->setStateSelected(false);
mFilterMisc->setStateSelected(false);
2015-05-20 02:18:20 +02:00
mPreview->updatePtr(mPtr);
mPreview->rebuild();
mPreview->update();
dirtyPreview();
updatePreviewSize();
updateEncumbranceBar();
mItemView->update();
notifyContentChanged();
}
void InventoryWindow::clear()
{
mPtr = MWWorld::Ptr();
2018-10-09 10:21:12 +04:00
mTradeModel = nullptr;
mSortModel = nullptr;
mItemView->setModel(nullptr);
2013-05-15 17:54:18 +02:00
}
void InventoryWindow::toggleMaximized()
{
const WindowSettingValues settings = getModeSettings(mGuiMode);
const WindowRectSettingValues& rect = settings.mIsMaximized ? settings.mRegular : settings.mMaximized;
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
const float x = rect.mX * viewSize.width;
const float y = rect.mY * viewSize.height;
const float w = rect.mW * viewSize.width;
const float h = rect.mH * viewSize.height;
MyGUI::Window* window = mMainWidget->castType<MyGUI::Window>();
window->setCoord(x, y, w, h);
settings.mIsMaximized.set(!settings.mIsMaximized);
adjustPanes();
updatePreviewSize();
}
2013-07-31 21:40:29 +02:00
void InventoryWindow::setGuiMode(GuiMode mode)
{
mGuiMode = mode;
const WindowSettingValues settings = getModeSettings(mGuiMode);
setPinButtonVisible(mode == GM_Inventory);
const WindowRectSettingValues& rect = settings.mIsMaximized ? settings.mMaximized : settings.mRegular;
2013-12-03 18:42:35 +01:00
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::IntPoint pos(static_cast<int>(rect.mX * viewSize.width), static_cast<int>(rect.mY * viewSize.height));
MyGUI::IntSize size(static_cast<int>(rect.mW * viewSize.width), static_cast<int>(rect.mH * viewSize.height));
bool needUpdate = (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight());
mMainWidget->setPosition(pos);
mMainWidget->setSize(size);
2013-12-03 18:42:35 +01:00
adjustPanes();
if (needUpdate)
updatePreviewSize();
2013-07-31 21:40:29 +02:00
}
SortFilterItemModel* InventoryWindow::getSortFilterModel()
{
return mSortModel;
}
2013-05-11 18:38:27 +02:00
TradeItemModel* InventoryWindow::getTradeModel()
{
return mTradeModel;
}
ItemModel* InventoryWindow::getModel()
{
return mTradeModel;
}
void InventoryWindow::onBackgroundSelected()
{
if (mDragAndDrop->mIsOnDragAndDrop)
mDragAndDrop->drop(mTradeModel, mItemView);
}
void InventoryWindow::onItemSelected(int index)
{
2017-10-19 17:17:14 +02:00
onItemSelectedFromSourceModel(mSortModel->mapToSource(index));
2013-05-11 18:38:27 +02:00
}
2017-10-19 17:17:14 +02:00
void InventoryWindow::onItemSelectedFromSourceModel(int index)
2013-05-11 18:38:27 +02:00
{
if (mDragAndDrop->mIsOnDragAndDrop)
{
mDragAndDrop->drop(mTradeModel, mItemView);
return;
}
const ItemStack& item = mTradeModel->getItem(index);
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID Slowly going through all the changes to make, still hundreds of errors a lot of functions/structures use std::string or stringview to designate an ID. So it takes time Continues slowly replacing ids. There are technically more and more compilation errors I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type Continue moving forward, changes to the stores slowly moving along Starting to see the fruit of those changes. still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type. More replacements. Things are starting to get easier I can see more and more often the issue is that the function is awaiting a RefId, but is given a string there is less need to go down functions and to fix a long list of them. Still moving forward, and for the first time error count is going down! Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably Cells are back to using string for the name, haven't fixed everything yet. Many other changes Under the bar of 400 compilation errors. more good progress <100 compile errors! More progress Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string some more progress on other fronts Mostly game settings clean one error opened a lot of other errors. Down to 18, but more will prbably appear only link errors left?? Fixed link errors OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
const ESM::RefId& sound = item.mBase.getClass().getDownSoundId(item.mBase);
2013-05-11 18:38:27 +02:00
MWWorld::Ptr object = item.mBase;
int count = item.mCount;
bool shift = MyGUI::InputManager::getInstance().isShiftPressed();
2013-05-11 18:38:27 +02:00
if (MyGUI::InputManager::getInstance().isControlPressed())
count = 1;
if (mTrading)
{
// Can't give conjured items to a merchant
if (item.mFlags & ItemStack::Flag_Bound)
{
MWBase::Environment::get().getWindowManager()->playSound(sound);
MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog9}");
return;
}
2013-05-11 18:38:27 +02:00
// check if merchant accepts item
int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices();
if (!object.getClass().canSell(object, services))
2013-05-11 18:38:27 +02:00
{
MWBase::Environment::get().getWindowManager()->playSound(sound);
2013-05-11 18:38:27 +02:00
MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog4}");
return;
}
}
// If we unequip weapon during attack, it can lead to unexpected behaviour
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPtr))
{
bool isWeapon = item.mBase.getType() == ESM::Weapon::sRecordId;
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
if (isWeapon && invStore.isEquipped(item.mBase))
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sCantEquipWeapWarning}");
return;
}
}
2013-05-11 18:38:27 +02:00
if (count > 1 && !shift)
{
CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();
std::string message = mTrading ? "#{sQuanityMenuMessage01}" : "#{sTake}";
2022-08-16 21:15:03 +02:00
std::string name{ object.getClass().getName(object) };
name += MWGui::ToolTips::getSoulString(object.getCellRef());
dialog->openCountDialog(name, message, count);
2013-05-11 18:38:27 +02:00
dialog->eventOkClicked.clear();
if (mTrading)
dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::sellItem);
else
dialog->eventOkClicked += MyGUI::newDelegate(this, &InventoryWindow::dragItem);
mSelectedItem = index;
}
else
{
mSelectedItem = index;
if (mTrading)
2018-10-09 10:21:12 +04:00
sellItem(nullptr, count);
2013-05-11 18:38:27 +02:00
else
2018-10-09 10:21:12 +04:00
dragItem(nullptr, count);
2013-05-11 18:38:27 +02:00
}
}
void InventoryWindow::ensureSelectedItemUnequipped(int count)
{
const ItemStack& item = mTradeModel->getItem(mSelectedItem);
if (item.mType == ItemStack::Type_Equipped)
{
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::Ptr newStack = *invStore.unequipItemQuantity(item.mBase, count);
// The unequipped item was re-stacked. We have to update the index
// since the item pointed does not exist anymore.
if (item.mBase != newStack)
{
updateItemView(); // Unequipping can produce a new stack, not yet in the window...
// newIndex will store the index of the ItemStack the item was stacked on
int newIndex = -1;
for (size_t i = 0; i < mTradeModel->getItemCount(); ++i)
{
if (mTradeModel->getItem(i).mBase == newStack)
{
newIndex = i;
break;
}
}
if (newIndex == -1)
throw std::runtime_error("Can't find restacked item");
mSelectedItem = newIndex;
}
}
}
2013-05-11 18:38:27 +02:00
void InventoryWindow::dragItem(MyGUI::Widget* sender, int count)
{
ensureSelectedItemUnequipped(count);
2013-05-11 18:38:27 +02:00
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count);
notifyContentChanged();
2013-05-11 18:38:27 +02:00
}
void InventoryWindow::sellItem(MyGUI::Widget* sender, int count)
{
ensureSelectedItemUnequipped(count);
2013-05-11 18:38:27 +02:00
const ItemStack& item = mTradeModel->getItem(mSelectedItem);
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID Slowly going through all the changes to make, still hundreds of errors a lot of functions/structures use std::string or stringview to designate an ID. So it takes time Continues slowly replacing ids. There are technically more and more compilation errors I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type Continue moving forward, changes to the stores slowly moving along Starting to see the fruit of those changes. still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type. More replacements. Things are starting to get easier I can see more and more often the issue is that the function is awaiting a RefId, but is given a string there is less need to go down functions and to fix a long list of them. Still moving forward, and for the first time error count is going down! Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably Cells are back to using string for the name, haven't fixed everything yet. Many other changes Under the bar of 400 compilation errors. more good progress <100 compile errors! More progress Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string some more progress on other fronts Mostly game settings clean one error opened a lot of other errors. Down to 18, but more will prbably appear only link errors left?? Fixed link errors OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
const ESM::RefId& sound = item.mBase.getClass().getUpSoundId(item.mBase);
MWBase::Environment::get().getWindowManager()->playSound(sound);
2013-05-11 18:38:27 +02:00
if (item.mType == ItemStack::Type_Barter)
{
// this was an item borrowed to us by the merchant
mTradeModel->returnItemBorrowedToUs(mSelectedItem, count);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->returnItem(mSelectedItem, count);
2013-05-11 18:38:27 +02:00
}
else
{
// borrow item to the merchant
mTradeModel->borrowItemFromUs(mSelectedItem, count);
MWBase::Environment::get().getWindowManager()->getTradeWindow()->borrowItem(mSelectedItem, count);
2013-05-11 18:38:27 +02:00
}
mItemView->update();
notifyContentChanged();
2013-05-11 18:38:27 +02:00
}
void InventoryWindow::updateItemView()
{
2015-03-11 20:04:25 +01:00
MWBase::Environment::get().getWindowManager()->updateSpellWindow();
2013-05-11 18:38:27 +02:00
mItemView->update();
2015-05-20 02:18:20 +02:00
dirtyPreview();
}
2012-05-12 21:28:04 +02:00
void InventoryWindow::onOpen()
{
// Reset the filter focus when opening the window
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (focus == mFilterEdit)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr);
if (!mPtr.isEmpty())
{
updateEncumbranceBar();
mItemView->update();
notifyContentChanged();
}
2013-12-03 18:42:35 +01:00
adjustPanes();
}
void InventoryWindow::onWindowResize(MyGUI::Window* _sender)
{
adjustPanes();
const WindowSettingValues settings = getModeSettings(mGuiMode);
2013-12-03 18:42:35 +01:00
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
settings.mRegular.mX.set(_sender->getPosition().left / static_cast<float>(viewSize.width));
settings.mRegular.mY.set(_sender->getPosition().top / static_cast<float>(viewSize.height));
settings.mRegular.mW.set(_sender->getSize().width / static_cast<float>(viewSize.width));
settings.mRegular.mH.set(_sender->getSize().height / static_cast<float>(viewSize.height));
settings.mIsMaximized.set(false);
2013-12-03 18:42:35 +01:00
2012-09-14 17:10:10 +02:00
if (mMainWidget->getSize().width != mLastXSize || mMainWidget->getSize().height != mLastYSize)
{
mLastXSize = mMainWidget->getSize().width;
mLastYSize = mMainWidget->getSize().height;
2015-05-20 02:18:20 +02:00
updatePreviewSize();
updateArmorRating();
2012-09-14 17:10:10 +02:00
}
}
2015-05-20 02:18:20 +02:00
void InventoryWindow::updateArmorRating()
{
mArmorRating->setCaptionWithReplacing(
"#{sArmor}: " + MyGUI::utility::toString(static_cast<int>(mPtr.getClass().getArmorRating(mPtr))));
if (mArmorRating->getTextSize().width > mArmorRating->getSize().width)
mArmorRating->setCaptionWithReplacing(
MyGUI::utility::toString(static_cast<int>(mPtr.getClass().getArmorRating(mPtr))));
}
void InventoryWindow::updatePreviewSize()
{
const MyGUI::IntSize viewport = getPreviewViewportSize();
mPreview->setViewport(viewport.width, viewport.height);
mAvatarImage->getSubWidgetMain()->_setUVSet(
MyGUI::FloatRect(0.f, 0.f, viewport.width / float(mPreview->getTextureWidth()),
viewport.height / float(mPreview->getTextureHeight())));
2015-05-20 02:18:20 +02:00
}
void InventoryWindow::onNameFilterChanged(MyGUI::EditBox* _sender)
{
mSortModel->setNameFilter(_sender->getCaption());
mItemView->update();
}
2012-05-12 21:44:33 +02:00
void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender)
{
if (_sender == mFilterAll)
2013-05-11 18:38:27 +02:00
mSortModel->setCategory(SortFilterItemModel::Category_All);
2012-05-12 21:44:33 +02:00
else if (_sender == mFilterWeapon)
2013-05-11 18:38:27 +02:00
mSortModel->setCategory(SortFilterItemModel::Category_Weapon);
2012-05-12 21:44:33 +02:00
else if (_sender == mFilterApparel)
2013-05-11 18:38:27 +02:00
mSortModel->setCategory(SortFilterItemModel::Category_Apparel);
2012-05-12 21:44:33 +02:00
else if (_sender == mFilterMagic)
2013-05-11 18:38:27 +02:00
mSortModel->setCategory(SortFilterItemModel::Category_Magic);
2012-05-12 21:44:33 +02:00
else if (_sender == mFilterMisc)
2013-05-11 18:38:27 +02:00
mSortModel->setCategory(SortFilterItemModel::Category_Misc);
mFilterAll->setStateSelected(false);
mFilterWeapon->setStateSelected(false);
mFilterApparel->setStateSelected(false);
mFilterMagic->setStateSelected(false);
mFilterMisc->setStateSelected(false);
2013-05-11 18:38:27 +02:00
mItemView->update();
2014-09-13 04:07:40 +02:00
_sender->castType<MyGUI::Button>()->setStateSelected(true);
2012-05-12 21:44:33 +02:00
}
2012-05-12 22:44:12 +02:00
void InventoryWindow::onPinToggled()
{
Settings::windows().mInventoryPin.set(mPinned);
MWBase::Environment::get().getWindowManager()->setWeaponVisibility(!mPinned);
2012-05-12 22:44:12 +02:00
}
void InventoryWindow::onTitleDoubleClicked()
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
toggleMaximized();
else if (!mPinned)
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Inventory);
}
void InventoryWindow::useItem(const MWWorld::Ptr& ptr, bool force)
{
Initial commit: In ESM structures, replace the string members that are RefIds to other records, to a new strong type The strong type is actually just a string underneath, but this will help in the future to have a distinction so it's easier to search and replace when we use an integer ID Slowly going through all the changes to make, still hundreds of errors a lot of functions/structures use std::string or stringview to designate an ID. So it takes time Continues slowly replacing ids. There are technically more and more compilation errors I have good hope that there is a point where the amount of errors will dramatically go down as all the main functions use the ESM::RefId type Continue moving forward, changes to the stores slowly moving along Starting to see the fruit of those changes. still many many error, but more and more Irun into a situation where a function is sandwiched between two functions that use the RefId type. More replacements. Things are starting to get easier I can see more and more often the issue is that the function is awaiting a RefId, but is given a string there is less need to go down functions and to fix a long list of them. Still moving forward, and for the first time error count is going down! Good pace, not sure about topics though, mId and mName are actually the same thing and are used interchangeably Cells are back to using string for the name, haven't fixed everything yet. Many other changes Under the bar of 400 compilation errors. more good progress <100 compile errors! More progress Game settings store can use string for find, it was a bit absurd how every use of it required to create refId from string some more progress on other fronts Mostly game settings clean one error opened a lot of other errors. Down to 18, but more will prbably appear only link errors left?? Fixed link errors OpenMW compiles, and launches, with some issues, but still!
2022-09-25 13:17:09 +02:00
const ESM::RefId& script = ptr.getClass().getScript(ptr);
if (!script.empty())
{
// Don't try to equip the item if PCSkipEquip is set to 1
if (ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)
{
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
return;
}
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 0);
}
2015-08-21 21:12:39 +12:00
MWWorld::Ptr player = MWMechanics::getPlayer();
// early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that
// case
if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())
{
if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
updateItemView();
return;
}
if (!force)
{
auto canEquip = ptr.getClass().canBeEquipped(ptr, player);
if (canEquip.first == 0)
{
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
updateItemView();
return;
}
}
}
// If the item has a script, set OnPCEquip or PCSkipEquip to 1
if (!script.empty())
{
// Ingredients, books and repair hammers must not have OnPCEquip set to 1 here
auto type = ptr.getType();
bool isBook = type == ESM::Book::sRecordId;
if (!isBook && type != ESM::Ingredient::sRecordId && type != ESM::Repair::sRecordId)
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
// Books must have PCSkipEquip set to 1 instead
else if (isBook)
ptr.getRefData().getLocals().setVarByInt(script, "pcskipequip", 1);
}
std::unique_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
action->execute(player);
if (isVisible())
{
mItemView->update();
notifyContentChanged();
}
// else: will be updated in open()
}
void InventoryWindow::onAvatarClicked(MyGUI::Widget* _sender)
{
if (mDragAndDrop->mIsOnDragAndDrop)
{
2013-05-11 18:38:27 +02:00
MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase;
2013-05-11 18:38:27 +02:00
mDragAndDrop->finish();
2013-05-11 18:38:27 +02:00
if (mDragAndDrop->mSourceModel != mTradeModel)
{
// Move item to the player's inventory
ptr = mDragAndDrop->mSourceModel->moveItem(
mDragAndDrop->mItem, mDragAndDrop->mDraggedCount, mTradeModel);
}
2017-10-19 17:17:14 +02:00
2023-06-28 09:32:15 +00:00
// Handles partial equipping
const std::pair<std::vector<int>, bool> slots = ptr.getClass().getEquipmentSlots(ptr);
if (!slots.first.empty() && slots.second)
{
int equippedStackableCount = 0;
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
MWWorld::ConstContainerStoreIterator slotIt = invStore.getSlot(slots.first.front());
// Get the count before useItem()
if (slotIt != invStore.end() && slotIt->getCellRef().getRefId() == ptr.getCellRef().getRefId())
equippedStackableCount = slotIt->getRefData().getCount();
useItem(ptr);
int unequipCount = ptr.getRefData().getCount() - mDragAndDrop->mDraggedCount - equippedStackableCount;
if (unequipCount > 0)
{
invStore.unequipItemQuantity(ptr, unequipCount);
updateItemView();
}
}
else
useItem(ptr);
2017-10-19 17:17:14 +02:00
// If item is ingredient or potion don't stop drag and drop to simplify action of taking more than one 1
// item
if ((ptr.getType() == ESM::Potion::sRecordId || ptr.getType() == ESM::Ingredient::sRecordId)
2017-10-19 17:17:14 +02:00
&& mDragAndDrop->mDraggedCount > 1)
{
// Item can be provided from other window for example container.
// But after DragAndDrop::startDrag item automaticly always gets to player inventory.
mSelectedItem = getModel()->getIndex(mDragAndDrop->mItem);
dragItem(nullptr, mDragAndDrop->mDraggedCount - 1);
}
}
2012-09-14 14:34:18 +02:00
else
{
MyGUI::IntPoint mousePos
= MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left);
MyGUI::IntPoint relPos = mousePos - mAvatarImage->getAbsolutePosition();
2015-05-20 03:35:52 +02:00
MWWorld::Ptr itemSelected = getAvatarSelectedItem(relPos.left, relPos.top);
2012-09-14 14:34:18 +02:00
if (itemSelected.isEmpty())
return;
2013-05-11 18:38:27 +02:00
for (size_t i = 0; i < mTradeModel->getItemCount(); ++i)
2012-09-14 14:34:18 +02:00
{
2013-05-11 18:38:27 +02:00
if (mTradeModel->getItem(i).mBase == itemSelected)
2012-09-14 14:34:18 +02:00
{
2017-10-19 17:17:14 +02:00
onItemSelectedFromSourceModel(i);
2012-09-14 14:34:18 +02:00
return;
}
}
2013-05-11 18:38:27 +02:00
throw std::runtime_error("Can't find clicked item");
2012-09-14 14:34:18 +02:00
}
}
MWWorld::Ptr InventoryWindow::getAvatarSelectedItem(int x, int y)
{
const osg::Vec2f viewport_coords = mapPreviewWindowToViewport(x, y);
int slot = mPreview->getSlotSelected(viewport_coords.x(), viewport_coords.y());
if (slot == -1)
return MWWorld::Ptr();
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
if (invStore.getSlot(slot) != invStore.end())
{
MWWorld::Ptr item = *invStore.getSlot(slot);
if (!item.getClass().showsInInventory(item))
return MWWorld::Ptr();
return item;
}
return MWWorld::Ptr();
}
2012-05-15 22:45:46 +02:00
void InventoryWindow::updateEncumbranceBar()
{
2015-08-21 21:12:39 +12:00
MWWorld::Ptr player = MWMechanics::getPlayer();
2012-05-15 22:45:46 +02:00
float capacity = player.getClass().getCapacity(player);
float encumbrance = player.getClass().getEncumbrance(player);
mTradeModel->adjustEncumbrance(encumbrance);
2018-02-24 15:13:14 +03:00
mEncumbranceBar->setValue(std::ceil(encumbrance), static_cast<int>(capacity));
2012-05-15 22:45:46 +02:00
}
void InventoryWindow::onFrame(float dt)
2012-05-15 22:45:46 +02:00
{
updateEncumbranceBar();
if (mPinned)
{
mUpdateTimer += dt;
if (0.1f < mUpdateTimer)
{
mUpdateTimer = 0;
// Update pinned inventory in-game
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
{
mItemView->update();
notifyContentChanged();
}
}
}
2012-05-15 22:45:46 +02:00
}
2013-05-11 18:38:27 +02:00
void InventoryWindow::setTrading(bool trading)
{
2013-05-11 18:38:27 +02:00
mTrading = trading;
}
2012-05-29 12:35:03 +02:00
2015-05-20 02:18:20 +02:00
void InventoryWindow::dirtyPreview()
{
2015-05-20 02:18:20 +02:00
mPreview->update();
2015-05-20 02:18:20 +02:00
updateArmorRating();
}
2012-05-29 12:35:03 +02:00
void InventoryWindow::notifyContentChanged()
{
// update the spell window just in case new enchanted items were added to inventory
2015-03-11 20:04:25 +01:00
MWBase::Environment::get().getWindowManager()->updateSpellWindow();
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(MWMechanics::getPlayer());
2015-05-20 02:18:20 +02:00
dirtyPreview();
}
void InventoryWindow::pickUpObject(MWWorld::Ptr object)
{
// If the inventory is not yet enabled, don't pick anything up
if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))
return;
// make sure the object is of a type that can be picked up
auto type = object.getType();
if ((type != ESM::Apparatus::sRecordId) && (type != ESM::Armor::sRecordId) && (type != ESM::Book::sRecordId)
&& (type != ESM::Clothing::sRecordId) && (type != ESM::Ingredient::sRecordId)
&& (type != ESM::Light::sRecordId) && (type != ESM::Miscellaneous::sRecordId)
&& (type != ESM::Lockpick::sRecordId) && (type != ESM::Probe::sRecordId) && (type != ESM::Repair::sRecordId)
&& (type != ESM::Weapon::sRecordId) && (type != ESM::Potion::sRecordId))
return;
// An object that can be picked up must have a tooltip.
if (!object.getClass().hasToolTip(object))
return;
int count = object.getRefData().getCount();
if (object.getClass().isGold(object))
count *= object.getClass().getValue(object);
2015-08-21 21:12:39 +12:00
MWWorld::Ptr player = MWMechanics::getPlayer();
MWBase::Environment::get().getWorld()->breakInvisibility(player);
2022-09-22 21:26:05 +03:00
2017-11-04 00:44:16 +00:00
if (!object.getRefData().activate())
return;
// Player must not be paralyzed, knocked down, or dead to pick up an item.
const MWMechanics::NpcStats& playerStats = player.getClass().getNpcStats(player);
bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();
if ((!godmode && playerStats.isParalyzed()) || playerStats.getKnockedDown() || playerStats.isDead())
return;
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, object, MWWorld::Ptr(), count);
// add to player inventory
// can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object
MWWorld::Ptr newObject
= *player.getClass().getContainerStore(player).add(object, object.getRefData().getCount());
// remove from world
MWBase::Environment::get().getWorld()->deleteObject(object);
2013-05-11 18:38:27 +02:00
// get ModelIndex to the item
mTradeModel->update();
size_t i = 0;
for (; i < mTradeModel->getItemCount(); ++i)
{
if (mTradeModel->getItem(i).mBase == newObject)
break;
}
if (i == mTradeModel->getItemCount())
throw std::runtime_error("Added item not found");
mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count);
2014-01-07 19:49:16 +01:00
2015-03-11 20:04:25 +01:00
MWBase::Environment::get().getWindowManager()->updateSpellWindow();
2012-05-29 12:35:03 +02:00
}
void InventoryWindow::cycle(bool next)
{
MWWorld::Ptr player = MWMechanics::getPlayer();
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
return;
const MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();
if ((!godmode && stats.isParalyzed()) || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
return;
ItemModel::ModelIndex selected = -1;
// not using mSortFilterModel as we only need sorting, not filtering
2022-08-31 18:49:50 +02:00
SortFilterItemModel model(std::make_unique<InventoryItemModel>(player));
model.setSortByType(false);
model.update();
if (model.getItemCount() == 0)
return;
for (ItemModel::ModelIndex i = 0; i < int(model.getItemCount()); ++i)
{
MWWorld::Ptr item = model.getItem(i).mBase;
if (model.getItem(i).mType & ItemStack::Type_Equipped && isRightHandWeapon(item))
selected = i;
}
int incr = next ? 1 : -1;
bool found = false;
ESM::RefId lastId;
if (selected != -1)
lastId = model.getItem(selected).mBase.getCellRef().getRefId();
ItemModel::ModelIndex cycled = selected;
2023-05-21 18:14:12 +02:00
for (size_t i = 0; i < model.getItemCount(); ++i)
{
cycled += incr;
cycled = (cycled + model.getItemCount()) % model.getItemCount();
MWWorld::Ptr item = model.getItem(cycled).mBase;
// skip different stacks of the same item, or we will get stuck as stacking/unstacking them may change their
// relative ordering
if (lastId == item.getCellRef().getRefId())
continue;
lastId = item.getCellRef().getRefId();
if (item.getClass().getType() == ESM::Weapon::sRecordId && isRightHandWeapon(item)
&& item.getClass().canBeEquipped(item, player).first)
2015-03-02 17:25:26 +01:00
{
found = true;
2015-03-02 17:25:26 +01:00
break;
}
}
2017-10-30 18:05:45 +01:00
if (!found || selected == cycled)
2015-03-02 17:25:26 +01:00
return;
useItem(model.getItem(cycled).mBase);
}
2015-01-10 02:50:43 +01:00
void InventoryWindow::rebuildAvatar()
{
mPreview->rebuild();
}
MyGUI::IntSize InventoryWindow::getPreviewViewportSize() const
{
const MyGUI::IntSize previewWindowSize = mAvatarImage->getSize();
const float scale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
return MyGUI::IntSize(std::min<int>(mPreview->getTextureWidth(), previewWindowSize.width * scale),
std::min<int>(mPreview->getTextureHeight(), previewWindowSize.height * scale));
}
osg::Vec2f InventoryWindow::mapPreviewWindowToViewport(int x, int y) const
{
const MyGUI::IntSize previewWindowSize = mAvatarImage->getSize();
const float normalisedX = x / std::max<float>(1.0f, previewWindowSize.width);
const float normalisedY = y / std::max<float>(1.0f, previewWindowSize.height);
const MyGUI::IntSize viewport = getPreviewViewportSize();
return osg::Vec2f(normalisedX * float(viewport.width - 1), (1.0 - normalisedY) * float(viewport.height - 1));
}
2012-04-22 21:06:08 +02:00
}