mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
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
This commit is contained in:
parent
d8e1a6b286
commit
e1b5dd97b8
9 changed files with 218 additions and 6 deletions
|
@ -74,6 +74,7 @@ Programmers
|
|||
Fil Krynicki (filkry)
|
||||
Finbar Crago (finbar-crago)
|
||||
Florian Weber (Florianjw)
|
||||
Frédéric Chardon (fr3dz10)
|
||||
Gaëtan Dezeiraud (Brouilles)
|
||||
Gašper Sedej
|
||||
Gijsbert ter Horst (Ghostbird)
|
||||
|
|
|
@ -264,6 +264,7 @@
|
|||
Feature #5219: Impelement TestCells console command
|
||||
Feature #5224: Handle NiKeyframeController for NiTriShape
|
||||
Feature #5304: Morrowind-style bump-mapping
|
||||
Feature #5314: Ingredient filter in the alchemy window
|
||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||
Task #4695: Optimize Distant Terrain memory consumption
|
||||
Task #4789: Optimize cell transitions
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <MyGUI_Gui.h>
|
||||
#include <MyGUI_Button.h>
|
||||
#include <MyGUI_EditBox.h>
|
||||
#include <MyGUI_ComboBox.h>
|
||||
#include <MyGUI_ControllerManager.h>
|
||||
#include <MyGUI_ControllerRepeatClick.h>
|
||||
|
||||
|
@ -17,6 +18,7 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include <MyGUI_Macros.h>
|
||||
#include <components/esm/records.hpp>
|
||||
|
||||
#include "inventoryitemmodel.hpp"
|
||||
|
@ -29,6 +31,7 @@ namespace MWGui
|
|||
{
|
||||
AlchemyWindow::AlchemyWindow()
|
||||
: WindowBase("openmw_alchemy_window.layout")
|
||||
, mModel(nullptr)
|
||||
, mSortModel(nullptr)
|
||||
, mAlchemy(new MWMechanics::Alchemy())
|
||||
, mApparatus (4)
|
||||
|
@ -50,6 +53,8 @@ namespace MWGui
|
|||
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);
|
||||
|
@ -72,6 +77,9 @@ namespace MWGui
|
|||
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();
|
||||
}
|
||||
|
@ -136,16 +144,110 @@ namespace MWGui
|
|||
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());
|
||||
|
||||
InventoryItemModel* model = new InventoryItemModel(MWMechanics::getPlayer());
|
||||
mSortModel = new SortFilterItemModel(model);
|
||||
mModel = new InventoryItemModel(MWMechanics::getPlayer());
|
||||
mSortModel = new SortFilterItemModel(mModel);
|
||||
mSortModel->setFilter(SortFilterItemModel::Filter_OnlyIngredients);
|
||||
mItemView->setModel (mSortModel);
|
||||
mItemView->resetScrollBars();
|
||||
|
@ -167,6 +269,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
update();
|
||||
initFilter();
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mNameEdit);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#include <vector>
|
||||
|
||||
#include <MyGUI_ControllerItem.h>
|
||||
#include <MyGUI_ComboBox.h>
|
||||
|
||||
#include <components/widgets/box.hpp>
|
||||
#include <components/widgets/numericeditbox.hpp>
|
||||
|
||||
#include "windowbase.hpp"
|
||||
|
@ -19,6 +21,7 @@ namespace MWGui
|
|||
{
|
||||
class ItemView;
|
||||
class ItemWidget;
|
||||
class InventoryItemModel;
|
||||
class SortFilterItemModel;
|
||||
|
||||
class AlchemyWindow : public WindowBase
|
||||
|
@ -36,8 +39,11 @@ namespace MWGui
|
|||
static const float sCountChangeInterval; // in seconds
|
||||
|
||||
std::string mSuggestedPotionName;
|
||||
enum class FilterType { ByName, ByEffect };
|
||||
FilterType mCurrentFilter;
|
||||
|
||||
ItemView* mItemView;
|
||||
InventoryItemModel* mModel;
|
||||
SortFilterItemModel* mSortModel;
|
||||
|
||||
MyGUI::Button* mCreateButton;
|
||||
|
@ -47,6 +53,8 @@ namespace MWGui
|
|||
|
||||
MyGUI::Button* mIncreaseButton;
|
||||
MyGUI::Button* mDecreaseButton;
|
||||
Gui::AutoSizedButton* mFilterType;
|
||||
MyGUI::ComboBox* mFilterValue;
|
||||
MyGUI::EditBox* mNameEdit;
|
||||
Gui::NumericEditBox* mBrewCountEdit;
|
||||
|
||||
|
@ -60,6 +68,13 @@ namespace MWGui
|
|||
void onCountValueChanged(int value);
|
||||
void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller);
|
||||
|
||||
void applyFilter(const std::string& filter);
|
||||
void initFilter();
|
||||
void onFilterChanged(MyGUI::ComboBox* _sender, size_t _index);
|
||||
void onFilterEdited(MyGUI::EditBox* _sender);
|
||||
void switchFilterType(MyGUI::Widget* _sender);
|
||||
void updateFilters();
|
||||
|
||||
void addRepeatController(MyGUI::Widget* widget);
|
||||
|
||||
void onIncreaseButtonTriggered();
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "../mwworld/nullaction.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwmechanics/alchemy.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool compareType(const std::string& type1, const std::string& type2)
|
||||
|
@ -151,6 +153,8 @@ namespace MWGui
|
|||
: mCategory(Category_All)
|
||||
, mFilter(0)
|
||||
, mSortByType(true)
|
||||
, mNameFilter("")
|
||||
, mEffectFilter("")
|
||||
{
|
||||
mSourceModel = sourceModel;
|
||||
}
|
||||
|
@ -199,8 +203,39 @@ namespace MWGui
|
|||
if (!(category & mCategory))
|
||||
return false;
|
||||
|
||||
if ((mFilter & Filter_OnlyIngredients) && base.getTypeName() != typeid(ESM::Ingredient).name())
|
||||
return false;
|
||||
if (mFilter & Filter_OnlyIngredients)
|
||||
{
|
||||
if (base.getTypeName() != typeid(ESM::Ingredient).name())
|
||||
return false;
|
||||
|
||||
if (!mNameFilter.empty() && !mEffectFilter.empty())
|
||||
throw std::logic_error("name and magic effect filter are mutually exclusive");
|
||||
|
||||
if (!mNameFilter.empty())
|
||||
{
|
||||
const auto itemName = Misc::StringUtils::lowerCase(base.getClass().getName(base));
|
||||
return itemName.find(mNameFilter) != std::string::npos;
|
||||
}
|
||||
|
||||
if (!mEffectFilter.empty())
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
||||
const auto alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
|
||||
|
||||
const auto effects = MWMechanics::Alchemy::effectsDescription(base, alchemySkill);
|
||||
|
||||
for (const auto& effect : effects)
|
||||
{
|
||||
const auto ciEffect = Misc::StringUtils::lowerCase(effect);
|
||||
|
||||
if (ciEffect.find(mEffectFilter) != std::string::npos)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((mFilter & Filter_OnlyEnchanted) && !(item.mFlags & ItemStack::Flag_Enchanted))
|
||||
return false;
|
||||
if ((mFilter & Filter_OnlyChargedSoulstones) && (base.getTypeName() != typeid(ESM::Miscellaneous).name()
|
||||
|
@ -286,6 +321,11 @@ namespace MWGui
|
|||
mNameFilter = Misc::StringUtils::lowerCase(filter);
|
||||
}
|
||||
|
||||
void SortFilterItemModel::setEffectFilter (const std::string& filter)
|
||||
{
|
||||
mEffectFilter = Misc::StringUtils::lowerCase(filter);
|
||||
}
|
||||
|
||||
void SortFilterItemModel::update()
|
||||
{
|
||||
mSourceModel->update();
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace MWGui
|
|||
void setCategory (int category);
|
||||
void setFilter (int filter);
|
||||
void setNameFilter (const std::string& filter);
|
||||
void setEffectFilter (const std::string& filter);
|
||||
|
||||
/// Use ItemStack::Type for sorting?
|
||||
void setSortByType(bool sort) { mSortByType = sort; }
|
||||
|
@ -60,6 +61,7 @@ namespace MWGui
|
|||
bool mSortByType;
|
||||
|
||||
std::string mNameFilter; // filter by item name
|
||||
std::string mEffectFilter; // filter by magic effect
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -554,3 +554,37 @@ std::string MWMechanics::Alchemy::suggestPotionName()
|
|||
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
ESM::MagicEffect::effectIdToString(effectId))->mValue.getString();
|
||||
}
|
||||
|
||||
std::vector<std::string> MWMechanics::Alchemy::effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySkill)
|
||||
{
|
||||
std::vector<std::string> effects;
|
||||
|
||||
const auto& item = ptr.get<ESM::Ingredient>()->mBase;
|
||||
const auto& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
const static auto fWortChanceValue = gmst.find("fWortChanceValue")->mValue.getFloat();
|
||||
const auto& data = item->mData;
|
||||
|
||||
for (auto i = 0; i < 4; ++i)
|
||||
{
|
||||
const auto effectID = data.mEffectID[i];
|
||||
const auto skillID = data.mSkills[i];
|
||||
const auto attributeID = data.mAttributes[i];
|
||||
|
||||
if (alchemySkill < fWortChanceValue * (i + 1))
|
||||
break;
|
||||
|
||||
if (effectID != -1)
|
||||
{
|
||||
std::string effect = gmst.find(ESM::MagicEffect::effectIdToString(effectID))->mValue.getString();
|
||||
|
||||
if (skillID != -1)
|
||||
effect += " " + gmst.find(ESM::Skill::sSkillNameIds[skillID])->mValue.getString();
|
||||
else if (attributeID != -1)
|
||||
effect += " " + gmst.find(ESM::Attribute::sGmstAttributeIds[attributeID])->mValue.getString();
|
||||
|
||||
effects.push_back(effect);
|
||||
|
||||
}
|
||||
}
|
||||
return effects;
|
||||
}
|
||||
|
|
|
@ -131,6 +131,8 @@ namespace MWMechanics
|
|||
///< Try to create potions from the ingredients, place them in the inventory of the alchemist and
|
||||
/// adjust the skills of the alchemist accordingly.
|
||||
/// \param name must not be an empty string, or Result_NoName is returned
|
||||
|
||||
static std::vector<std::string> effectsDescription (const MWWorld::ConstPtr &ptr, const int alchemySKill);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,7 @@
|
|||
|
||||
<!-- Available Ingredients -->
|
||||
|
||||
<Widget type="ItemView" skin="MW_ItemView" position="10 206 552 158" name="ItemView" align="Left Top Stretch"/>
|
||||
|
||||
<Widget type="ItemView" skin="MW_ItemView" position="10 206 552 132" name="ItemView" align="Left Top Stretch"/>
|
||||
|
||||
<!-- Created Effects -->
|
||||
|
||||
|
@ -71,6 +70,21 @@
|
|||
<Widget type="Widget" skin="" position="4 4 316 122" name="CreatedEffects" align="HStretch"/>
|
||||
</Widget>
|
||||
|
||||
<!-- Filters -->
|
||||
|
||||
<Widget type="HBox" skin="MW_Box" position="10 333 552 39" align="Bottom HStretch">
|
||||
<Property key="Padding" value="5"/>
|
||||
<Property key="Spacing" value="10"/>
|
||||
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="10 2 1 1" name="FilterType">
|
||||
<Property key="Caption" value="#{sIngredients}"/> <!-- default value, can be either sIngredients of sMagicEffects -->
|
||||
</Widget>
|
||||
<Widget type="ComboBox" skin="MW_ComboBox" position="0 2 0 24" name="FilterValue">
|
||||
<UserString key="HStretch" value="true"/>
|
||||
<Property key="ModeDrop" value="false"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
|
||||
<!-- Buttons -->
|
||||
|
||||
|
|
Loading…
Reference in a new issue