1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-10-17 07:16:35 +00:00
openmw/apps/openmw/mwgui/container.cpp
Alexei Kotov 12b2887e58 Fix build
2025-08-27 04:22:17 +03:00

422 lines
15 KiB
C++

#include "container.hpp"
#include <MyGUI_Button.h>
#include <MyGUI_InputManager.h>
#include <components/settings/values.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/aipackage.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/summoning.hpp"
#include "../mwscript/interpretercontext.hpp"
#include "containeritemmodel.hpp"
#include "countdialog.hpp"
#include "draganddrop.hpp"
#include "inventoryitemmodel.hpp"
#include "inventorywindow.hpp"
#include "itemtransfer.hpp"
#include "itemview.hpp"
#include "pickpocketitemmodel.hpp"
#include "sortfilteritemmodel.hpp"
#include "tooltips.hpp"
namespace MWGui
{
ContainerWindow::ContainerWindow(DragAndDrop& dragAndDrop, ItemTransfer& itemTransfer)
: WindowBase("openmw_container_window.layout")
, mDragAndDrop(&dragAndDrop)
, mItemTransfer(&itemTransfer)
, mSortModel(nullptr)
, mModel(nullptr)
, mSelectedItem(-1)
, mUpdateNextFrame(false)
, mTreatNextOpenAsLoot(false)
{
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
getWidget(mTakeButton, "TakeButton");
getWidget(mCloseButton, "CloseButton");
getWidget(mItemView, "ItemView");
mItemView->eventBackgroundClicked += MyGUI::newDelegate(this, &ContainerWindow::onBackgroundSelected);
mItemView->eventItemClicked += MyGUI::newDelegate(this, &ContainerWindow::onItemSelected);
mDisposeCorpseButton->eventMouseButtonClick
+= MyGUI::newDelegate(this, &ContainerWindow::onDisposeCorpseButtonClicked);
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked);
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked);
setCoord(200, 0, 600, 300);
mControllerButtons.mA = "#{Interface:Take}";
mControllerButtons.mB = "#{Interface:Close}";
mControllerButtons.mX = "#{Interface:TakeAll}";
mControllerButtons.mR3 = "#{Interface:Info}";
mControllerButtons.mL2 = "#{Interface:Inventory}";
}
void ContainerWindow::onItemSelected(int index)
{
if (mDragAndDrop->mIsOnDragAndDrop)
{
dropItem();
return;
}
const ItemStack& item = mSortModel->getItem(index);
// We can't take a conjured item from a container (some NPC we're pickpocketing, a box, etc)
if (item.mFlags & ItemStack::Flag_Bound)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage1}");
return;
}
MWWorld::Ptr object = item.mBase;
int count = item.mCount;
bool shift = MyGUI::InputManager::getInstance().isShiftPressed();
if (MyGUI::InputManager::getInstance().isControlPressed())
count = 1;
mSelectedItem = mSortModel->mapToSource(index);
if (count > 1 && !shift)
{
CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog();
std::string name{ object.getClass().getName(object) };
name += MWGui::ToolTips::getSoulString(object.getCellRef());
dialog->openCountDialog(name, "#{sTake}", count);
dialog->eventOkClicked.clear();
if (Settings::gui().mControllerMenus || MyGUI::InputManager::getInstance().isAltPressed())
dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::transferItem);
else
dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem);
}
else if (Settings::gui().mControllerMenus || MyGUI::InputManager::getInstance().isAltPressed())
transferItem(nullptr, count);
else
dragItem(nullptr, count);
}
void ContainerWindow::dragItem(MyGUI::Widget* /*sender*/, std::size_t count)
{
if (mModel == nullptr)
return;
const ItemStack item = mModel->getItem(mSelectedItem);
if (!mModel->onTakeItem(item.mBase, count))
return;
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
}
void ContainerWindow::transferItem(MyGUI::Widget* /*sender*/, std::size_t count)
{
if (mModel == nullptr)
return;
const ItemStack item = mModel->getItem(mSelectedItem);
if (!mModel->onTakeItem(item.mBase, count))
return;
mItemTransfer->apply(item, count, *mItemView);
}
void ContainerWindow::dropItem()
{
if (mModel == nullptr)
return;
bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount);
if (success)
mDragAndDrop->drop(mModel, mItemView);
}
void ContainerWindow::onBackgroundSelected()
{
if (mDragAndDrop->mIsOnDragAndDrop)
dropItem();
}
void ContainerWindow::setPtr(const MWWorld::Ptr& container)
{
if (container.isEmpty() || (container.getType() != ESM::REC_CONT && !container.getClass().isActor()))
throw std::runtime_error("Invalid argument in ContainerWindow::setPtr");
bool lootAnyway = mTreatNextOpenAsLoot;
mTreatNextOpenAsLoot = false;
mPtr = container;
bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead();
std::unique_ptr<ItemModel> model;
if (mPtr.getClass().hasInventoryStore(mPtr))
{
if (mPtr.getClass().isNpc() && !loot && !lootAnyway)
{
// we are stealing stuff
model = std::make_unique<PickpocketItemModel>(mPtr, std::make_unique<InventoryItemModel>(container),
!mPtr.getClass().getCreatureStats(mPtr).getKnockedDown());
}
else
model = std::make_unique<InventoryItemModel>(container);
}
else
{
model = std::make_unique<ContainerItemModel>(container);
}
mDisposeCorpseButton->setVisible(loot);
mModel = model.get();
auto sortModel = std::make_unique<SortFilterItemModel>(std::move(model));
mSortModel = sortModel.get();
mItemView->setModel(std::move(sortModel));
mItemView->resetScrollBars();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
setTitle(container.getClass().getName(container));
mPtr.getClass().getContainerStore(mPtr).setContListener(this);
}
void ContainerWindow::resetReference()
{
ReferenceInterface::resetReference();
mItemView->setModel(nullptr);
mModel = nullptr;
mSortModel = nullptr;
}
void ContainerWindow::onOpen()
{
mItemTransfer->addTarget(*mItemView);
}
void ContainerWindow::onClose()
{
// Make sure the window was actually closed and not temporarily hidden.
if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Container))
return;
if (mModel)
mModel->onClose();
if (!mPtr.isEmpty())
MWBase::Environment::get().getMechanicsManager()->onClose(mPtr);
resetReference();
mItemTransfer->removeTarget(*mItemView);
}
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
}
void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender)
{
if (!mModel)
return;
if (mDragAndDrop != nullptr && mDragAndDrop->mIsOnDragAndDrop)
return;
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
// transfer everything into the player's inventory
ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel();
assert(mModel);
mModel->update();
// unequip all items to avoid unequipping/reequipping
if (mPtr.getClass().hasInventoryStore(mPtr))
{
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr);
for (size_t i = 0; i < mModel->getItemCount(); ++i)
{
const ItemStack& item = mModel->getItem(i);
if (invStore.isEquipped(item.mBase) == false)
continue;
invStore.unequipItem(item.mBase);
}
}
mModel->update();
for (size_t i = 0; i < mModel->getItemCount(); ++i)
{
if (i == 0)
{
// play the sound of the first object
MWWorld::Ptr item = mModel->getItem(i).mBase;
const ESM::RefId& sound = item.getClass().getUpSoundId(item);
MWBase::Environment::get().getWindowManager()->playSound(sound);
}
const ItemStack item = mModel->getItem(i);
if (!mModel->onTakeItem(item.mBase, item.mCount))
break;
mModel->moveItem(item, item.mCount, playerModel);
}
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
}
void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget* sender)
{
if (mDragAndDrop == nullptr || !mDragAndDrop->mIsOnDragAndDrop)
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
// Copy mPtr because onTakeAllButtonClicked closes the window which resets the reference
MWWorld::Ptr ptr = mPtr;
onTakeAllButtonClicked(mTakeButton);
if (ptr.getClass().isPersistent(ptr))
MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}");
else
{
MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
// If we dispose corpse before end of death animation, we should update death counter counter manually.
// Also we should run actor's script - it may react on actor's death.
if (creatureStats.isDead() && !creatureStats.isDeathAnimationFinished())
{
creatureStats.setDeathAnimationFinished(true);
MWBase::Environment::get().getMechanicsManager()->notifyDied(ptr);
const ESM::RefId& script = ptr.getClass().getScript(ptr);
if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled())
{
MWScript::InterpreterContext interpreterContext(&ptr.getRefData().getLocals(), ptr);
MWBase::Environment::get().getScriptManager()->run(script, interpreterContext);
}
// Clean up summoned creatures as well
auto& creatureMap = creatureStats.getSummonedCreatureMap();
for (const auto& creature : creatureMap)
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(ptr, creature.second);
creatureMap.clear();
// Check if we are a summon and inform our master we've bit the dust
for (const auto& package : creatureStats.getAiSequence())
{
if (package->followTargetThroughDoors() && !package->getTarget().isEmpty())
{
const auto& summoner = package->getTarget();
auto& summons = summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap();
auto it = std::find_if(summons.begin(), summons.end(),
[&](const auto& entry) { return entry.second == creatureStats.getActorId(); });
if (it != summons.end())
{
auto summon = *it;
summons.erase(it);
MWMechanics::purgeSummonEffect(summoner, summon);
break;
}
}
}
}
MWBase::Environment::get().getWorld()->deleteObject(ptr);
}
mPtr = MWWorld::Ptr();
}
}
void ContainerWindow::onReferenceUnavailable()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
}
void ContainerWindow::onDeleteCustomData(const MWWorld::Ptr& ptr)
{
if (mModel && mModel->usesContainer(ptr))
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
}
ControllerButtons* ContainerWindow::getControllerButtons()
{
if (mDisposeCorpseButton->getVisible())
mControllerButtons.mR1 = "#{Interface:DisposeOfCorpse}";
else
mControllerButtons.mR1.clear();
return &mControllerButtons;
}
bool ContainerWindow::onControllerButtonEvent(const SDL_ControllerButtonEvent& arg)
{
if (arg.button == SDL_CONTROLLER_BUTTON_A)
{
int index = mItemView->getControllerFocus();
if (index >= 0 && index < mItemView->getItemCount())
onItemSelected(index);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_B)
{
onCloseButtonClicked(mCloseButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_X)
{
onTakeAllButtonClicked(mTakeButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
{
if (mDisposeCorpseButton->getVisible())
onDisposeCorpseButtonClicked(mDisposeCorpseButton);
}
else if (arg.button == SDL_CONTROLLER_BUTTON_RIGHTSTICK || arg.button == SDL_CONTROLLER_BUTTON_DPAD_UP
|| arg.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN || arg.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT
|| arg.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
{
mItemView->onControllerButton(arg.button);
}
return true;
}
void ContainerWindow::setActiveControllerWindow(bool active)
{
mItemView->setActiveControllerWindow(active);
WindowBase::setActiveControllerWindow(active);
}
void ContainerWindow::onFrame(float dt)
{
checkReferenceAvailable();
if (mUpdateNextFrame)
{
mItemView->update();
mUpdateNextFrame = false;
}
}
void ContainerWindow::itemAdded(const MWWorld::ConstPtr& item, int count)
{
mUpdateNextFrame = true;
}
void ContainerWindow::itemRemoved(const MWWorld::ConstPtr& item, int count)
{
mUpdateNextFrame = true;
}
}