mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-04 21:40:03 +00:00
Merge branch 'alchemyfilter' into 'master'
Filters for ingredients & effects in alchemy window See merge request OpenMW/openmw!106
This commit is contained in:
commit
6de1c0d0d3
@ -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…
x
Reference in New Issue
Block a user