1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-24 01:23:51 +00:00
openmw-tes3mp/apps/openmw/mwgui/alchemywindow.cpp
fredzio e1b5dd97b8 Add a filter in the alchemy window.
A button allow to switch between ingredient name and magic effect.
Switching reset the filter.
The default filter can be set in the layout file.

The player can show only ingredients whose either name or effect
match the filter
Only effect that are known to the player (via alchemy skill) are
taken into account
2020-03-20 06:35:57 +01:00

439 lines
16 KiB
C++

#include "alchemywindow.hpp"
#include <MyGUI_Gui.h>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include <MyGUI_ComboBox.h>
#include <MyGUI_ControllerManager.h>
#include <MyGUI_ControllerRepeatClick.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/magiceffects.hpp"
#include "../mwmechanics/alchemy.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include <MyGUI_Macros.h>
#include <components/esm/records.hpp>
#include "inventoryitemmodel.hpp"
#include "sortfilteritemmodel.hpp"
#include "itemview.hpp"
#include "itemwidget.hpp"
#include "widgets.hpp"
namespace MWGui
{
AlchemyWindow::AlchemyWindow()
: WindowBase("openmw_alchemy_window.layout")
, mModel(nullptr)
, mSortModel(nullptr)
, mAlchemy(new MWMechanics::Alchemy())
, mApparatus (4)
, mIngredients (4)
{
getWidget(mCreateButton, "CreateButton");
getWidget(mCancelButton, "CancelButton");
getWidget(mIngredients[0], "Ingredient1");
getWidget(mIngredients[1], "Ingredient2");
getWidget(mIngredients[2], "Ingredient3");
getWidget(mIngredients[3], "Ingredient4");
getWidget(mApparatus[0], "Apparatus1");
getWidget(mApparatus[1], "Apparatus2");
getWidget(mApparatus[2], "Apparatus3");
getWidget(mApparatus[3], "Apparatus4");
getWidget(mEffectsBox, "CreatedEffects");
getWidget(mBrewCountEdit, "BrewCount");
getWidget(mIncreaseButton, "IncreaseButton");
getWidget(mDecreaseButton, "DecreaseButton");
getWidget(mNameEdit, "NameEdit");
getWidget(mItemView, "ItemView");
getWidget(mFilterValue, "FilterValue");
getWidget(mFilterType, "FilterType");
mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged);
mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
mBrewCountEdit->setMinValue(1);
mBrewCountEdit->setValue(1);
mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onIncreaseButtonPressed);
mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased);
mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onDecreaseButtonPressed);
mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased);
mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem);
mIngredients[0]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
mIngredients[1]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
mIngredients[2]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
mIngredients[3]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);
mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
mFilterValue->eventComboChangePosition += MyGUI::newDelegate(this, &AlchemyWindow::onFilterChanged);
mFilterValue->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited);
mFilterType->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::switchFilterType);
center();
}
void AlchemyWindow::onAccept(MyGUI::EditBox* sender)
{
onCreateButtonClicked(sender);
// To do not spam onAccept() again and again
MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None);
}
void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Alchemy);
}
void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender)
{
mAlchemy->setPotionName(mNameEdit->getCaption());
int count = mAlchemy->countPotionsToBrew();
count = std::min(count, mBrewCountEdit->getValue());
createPotions(count);
}
void AlchemyWindow::createPotions(int count)
{
MWMechanics::Alchemy::Result result = mAlchemy->create(mNameEdit->getCaption(), count);
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
switch (result)
{
case MWMechanics::Alchemy::Result_NoName:
winMgr->messageBox("#{sNotifyMessage37}");
break;
case MWMechanics::Alchemy::Result_NoMortarAndPestle:
winMgr->messageBox("#{sNotifyMessage45}");
break;
case MWMechanics::Alchemy::Result_LessThanTwoIngredients:
winMgr->messageBox("#{sNotifyMessage6a}");
break;
case MWMechanics::Alchemy::Result_Success:
winMgr->playSound("potion success");
if (count == 1)
winMgr->messageBox("#{sPotionSuccess}");
else
winMgr->messageBox("#{sPotionSuccess} "+mNameEdit->getCaption()+" ("+std::to_string(count)+")");
break;
case MWMechanics::Alchemy::Result_NoEffects:
case MWMechanics::Alchemy::Result_RandomFailure:
winMgr->messageBox("#{sNotifyMessage8}");
winMgr->playSound("potion fail");
break;
}
// remove ingredient slots that have been fully used up
for (int i=0; i<4; ++i)
if (mIngredients[i]->isUserString("ToolTipType"))
{
MWWorld::Ptr ingred = *mIngredients[i]->getUserData<MWWorld::Ptr>();
if (ingred.getRefData().getCount() == 0)
removeIngredient(mIngredients[i]);
}
updateFilters();
update();
}
void AlchemyWindow::initFilter()
{
auto const& wm = MWBase::Environment::get().getWindowManager();
auto const ingredient = wm->getGameSettingString("sIngredients", "Ingredients");
auto const effect = wm->getGameSettingString("sMagicEffects", "Magic Effects");
if (mFilterType->getCaption() == ingredient)
mCurrentFilter = FilterType::ByName;
else
mCurrentFilter = FilterType::ByEffect;
updateFilters();
mFilterValue->clearIndexSelected();
updateFilters();
}
void AlchemyWindow::switchFilterType(MyGUI::Widget* _sender)
{
auto const& wm = MWBase::Environment::get().getWindowManager();
auto const ingredient = wm->getGameSettingString("sIngredients", "Ingredients");
auto const effect = wm->getGameSettingString("sMagicEffects", "Magic Effects");
auto *button = _sender->castType<MyGUI::Button>();
if (button->getCaption() == ingredient)
{
button->setCaption(effect);
mCurrentFilter = FilterType::ByEffect;
}
else
{
button->setCaption(ingredient);
mCurrentFilter = FilterType::ByName;
}
mSortModel->setNameFilter({});
mSortModel->setEffectFilter({});
mFilterValue->clearIndexSelected();
updateFilters();
mItemView->update();
}
void AlchemyWindow::updateFilters()
{
std::set<std::string> itemNames, itemEffects;
for (size_t i = 0; i < mModel->getItemCount(); ++i)
{
auto const& base = mModel->getItem(i).mBase;
if (base.getTypeName() != typeid(ESM::Ingredient).name())
continue;
itemNames.insert(base.getClass().getName(base));
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
auto const alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
auto const effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill);
itemEffects.insert(effects.begin(), effects.end());
}
mFilterValue->removeAllItems();
auto const addItems = [&](auto const& container)
{
for (auto const& item : container)
mFilterValue->addItem(item);
};
switch (mCurrentFilter)
{
case FilterType::ByName: addItems(itemNames); break;
case FilterType::ByEffect: addItems(itemEffects); break;
}
}
void AlchemyWindow::applyFilter(const std::string& filter)
{
switch (mCurrentFilter)
{
case FilterType::ByName: mSortModel->setNameFilter(filter); break;
case FilterType::ByEffect: mSortModel->setEffectFilter(filter); break;
}
mItemView->update();
}
void AlchemyWindow::onFilterChanged(MyGUI::ComboBox* _sender, size_t _index)
{
// ignore spurious event fired when one edit the content after selection.
// onFilterEdited will handle it.
if (_index != MyGUI::ITEM_NONE)
applyFilter(_sender->getItemNameAt(_index));
}
void AlchemyWindow::onFilterEdited(MyGUI::EditBox* _sender)
{
applyFilter(_sender->getCaption());
}
void AlchemyWindow::onOpen()
{
mAlchemy->clear();
mAlchemy->setAlchemist (MWMechanics::getPlayer());
mModel = new InventoryItemModel(MWMechanics::getPlayer());
mSortModel = new SortFilterItemModel(mModel);
mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients);
mItemView->setModel (mSortModel);
mItemView->resetScrollBars();
mNameEdit->setCaption("");
mBrewCountEdit->setValue(1);
int index = 0;
for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools());
iter!=mAlchemy->endTools() && index<static_cast<int> (mApparatus.size()); ++iter, ++index)
{
mApparatus.at (index)->setItem(*iter);
mApparatus.at (index)->clearUserStrings();
if (!iter->isEmpty())
{
mApparatus.at (index)->setUserString ("ToolTipType", "ItemPtr");
mApparatus.at (index)->setUserData (MWWorld::Ptr(*iter));
}
}
update();
initFilter();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
}
void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender)
{
removeIngredient(_sender);
update();
}
void AlchemyWindow::onSelectedItem(int index)
{
MWWorld::Ptr item = mSortModel->getItem(index).mBase;
int res = mAlchemy->addIngredient(item);
if (res != -1)
{
update();
std::string sound = item.getClass().getUpSoundId(item);
MWBase::Environment::get().getWindowManager()->playSound(sound);
}
}
void AlchemyWindow::update()
{
std::string suggestedName = mAlchemy->suggestPotionName();
if (suggestedName != mSuggestedPotionName)
mNameEdit->setCaptionWithReplacing(suggestedName);
mSuggestedPotionName = suggestedName;
mSortModel->clearDragItems();
MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy->beginIngredients ();
for (int i=0; i<4; ++i)
{
ItemWidget* ingredient = mIngredients[i];
MWWorld::Ptr item;
if (it != mAlchemy->endIngredients ())
{
item = *it;
++it;
}
if (!item.isEmpty())
mSortModel->addDragItem(item, item.getRefData().getCount());
if (ingredient->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0));
ingredient->clearUserStrings ();
ingredient->setItem(item);
if (item.isEmpty ())
continue;
ingredient->setUserString("ToolTipType", "ItemPtr");
ingredient->setUserData(MWWorld::Ptr(item));
ingredient->setCount(item.getRefData().getCount());
}
mItemView->update();
std::set<MWMechanics::EffectKey> effectIds = mAlchemy->listEffects();
Widgets::SpellEffectList list;
unsigned int effectIndex=0;
for (const MWMechanics::EffectKey& effectKey : effectIds)
{
Widgets::SpellEffectParams params;
params.mEffectID = effectKey.mId;
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectKey.mId);
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
params.mSkill = effectKey.mArg;
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
params.mAttribute = effectKey.mArg;
params.mIsConstant = true;
params.mNoTarget = true;
params.mNoMagnitude = true;
params.mKnown = mAlchemy->knownEffect(effectIndex, MWBase::Environment::get().getWorld()->getPlayerPtr());
list.push_back(params);
++effectIndex;
}
while (mEffectsBox->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mEffectsBox->getChildAt(0));
MyGUI::IntCoord coord(0, 0, mEffectsBox->getWidth(), 24);
Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget<Widgets::MWEffectList>
("MW_StatName", coord, MyGUI::Align::Left | MyGUI::Align::Top);
effectsWidget->setEffectList(list);
std::vector<MyGUI::Widget*> effectItems;
effectsWidget->createEffectWidgets(effectItems, mEffectsBox, coord, false, 0);
effectsWidget->setCoord(coord);
}
void AlchemyWindow::removeIngredient(MyGUI::Widget* ingredient)
{
for (int i=0; i<4; ++i)
if (mIngredients[i] == ingredient)
mAlchemy->removeIngredient (i);
update();
}
void AlchemyWindow::addRepeatController(MyGUI::Widget *widget)
{
MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(MyGUI::ControllerRepeatClick::getClassTypeName());
MyGUI::ControllerRepeatClick* controller = static_cast<MyGUI::ControllerRepeatClick*>(item);
controller->eventRepeatClick += newDelegate(this, &AlchemyWindow::onRepeatClick);
MyGUI::ControllerManager::getInstance().addItem(widget, controller);
}
void AlchemyWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
{
addRepeatController(_sender);
onIncreaseButtonTriggered();
}
void AlchemyWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
{
addRepeatController(_sender);
onDecreaseButtonTriggered();
}
void AlchemyWindow::onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller)
{
if (widget == mIncreaseButton)
onIncreaseButtonTriggered();
else if (widget == mDecreaseButton)
onDecreaseButtonTriggered();
}
void AlchemyWindow::onCountButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id)
{
MyGUI::ControllerManager::getInstance().removeItem(_sender);
}
void AlchemyWindow::onCountValueChanged(int value)
{
mBrewCountEdit->setValue(std::abs(value));
}
void AlchemyWindow::onIncreaseButtonTriggered()
{
int currentCount = mBrewCountEdit->getValue();
// prevent overflows
if (currentCount == std::numeric_limits<int>::max())
return;
mBrewCountEdit->setValue(currentCount+1);
}
void AlchemyWindow::onDecreaseButtonTriggered()
{
int currentCount = mBrewCountEdit->getValue();
if (currentCount > 1)
mBrewCountEdit->setValue(currentCount-1);
}
}