Skip to content

Commit

Permalink
Merge branch 'makealchemygreatagain' into 'master'
Browse files Browse the repository at this point in the history
Allow choosing different apparatus in alchemy window

Closes #7665

See merge request OpenMW/openmw!3580
  • Loading branch information
Zackhasacat committed Nov 20, 2023
2 parents 6fb6c7a + a96c038 commit abd2c53
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
Bug #7647: NPC walk cycle bugs after greeting player
Bug #7654: Tooltips for enchantments with invalid effects cause crashes
Bug #7660: Some inconsistencies regarding Invisibility breaking
Bug #7665: Alchemy menu is missing the ability to deselect and choose different qualities of an apparatus
Bug #7675: Successful lock spell doesn't produce a sound
Bug #7679: Scene luminance value flashes when toggling shaders
Feature #3537: Shader-based water ripples
Expand Down
99 changes: 87 additions & 12 deletions apps/openmw/mwgui/alchemywindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <MyGUI_EditBox.h>
#include <MyGUI_Gui.h>

#include <components/esm3/loadappa.hpp>
#include <components/esm3/loadingr.hpp>
#include <components/esm3/loadmgef.hpp>

Expand Down Expand Up @@ -77,6 +78,11 @@ namespace MWGui
mIngredients[2]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
mIngredients[3]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);

mApparatus[0]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onApparatusSelected);
mApparatus[1]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onApparatusSelected);
mApparatus[2]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onApparatusSelected);
mApparatus[3]->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onApparatusSelected);

mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);

Expand Down Expand Up @@ -141,12 +147,12 @@ namespace MWGui
}

// remove ingredient slots that have been fully used up
for (int i = 0; i < 4; ++i)
for (size_t i = 0; i < mIngredients.size(); ++i)
if (mIngredients[i]->isUserString("ToolTipType"))
{
MWWorld::Ptr ingred = *mIngredients[i]->getUserData<MWWorld::Ptr>();
if (ingred.getRefData().getCount() == 0)
removeIngredient(mIngredients[i]);
mAlchemy->removeIngredient(i);
}

updateFilters();
Expand Down Expand Up @@ -289,7 +295,85 @@ namespace MWGui

void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender)
{
removeIngredient(_sender);
size_t i = std::distance(mIngredients.begin(), std::find(mIngredients.begin(), mIngredients.end(), _sender));
mAlchemy->removeIngredient(i);
update();
}

void AlchemyWindow::onItemSelected(MWWorld::Ptr item)
{
mItemSelectionDialog->setVisible(false);

int32_t index = item.get<ESM::Apparatus>()->mBase->mData.mType;
const auto& widget = mApparatus[index];

widget->setItem(item);

if (item.isEmpty())
{
widget->clearUserStrings();
return;
}

mAlchemy->addApparatus(item);

widget->setUserString("ToolTipType", "ItemPtr");
widget->setUserData(MWWorld::Ptr(item));

MWBase::Environment::get().getWindowManager()->playSound(item.getClass().getDownSoundId(item));
update();
}

void AlchemyWindow::onItemCancel()
{
mItemSelectionDialog->setVisible(false);
}

void AlchemyWindow::onApparatusSelected(MyGUI::Widget* _sender)
{
size_t i = std::distance(mApparatus.begin(), std::find(mApparatus.begin(), mApparatus.end(), _sender));
if (_sender->getUserData<MWWorld::Ptr>()->isEmpty()) // if this apparatus slot is empty
{
std::string title;
switch (i)
{
case ESM::Apparatus::AppaType::MortarPestle:
title = "#{sMortar}";
break;
case ESM::Apparatus::AppaType::Alembic:
title = "#{sAlembic}";
break;
case ESM::Apparatus::AppaType::Calcinator:
title = "#{sCalcinator}";
break;
case ESM::Apparatus::AppaType::Retort:
title = "#{sRetort}";
break;
default:
title = "#{sApparatus}";
}

mItemSelectionDialog = std::make_unique<ItemSelectionDialog>(title);
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &AlchemyWindow::onItemSelected);
mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &AlchemyWindow::onItemCancel);
mItemSelectionDialog->setVisible(true);
mItemSelectionDialog->openContainer(MWMechanics::getPlayer());
mItemSelectionDialog->getSortModel()->setApparatusTypeFilter(i);
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyAlchemyTools);
}
else
{
const auto& widget = mApparatus[i];
mAlchemy->removeApparatus(i);

if (widget->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(widget->getChildAt(0));

widget->clearUserStrings();
widget->setItem(MWWorld::Ptr());
widget->setUserData(MWWorld::Ptr());
}

update();
}

Expand Down Expand Up @@ -386,15 +470,6 @@ namespace MWGui
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
Expand Down
7 changes: 6 additions & 1 deletion apps/openmw/mwgui/alchemywindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <components/widgets/box.hpp>
#include <components/widgets/numericeditbox.hpp>

#include "itemselection.hpp"
#include "windowbase.hpp"

#include "../mwmechanics/alchemy.hpp"
Expand Down Expand Up @@ -44,6 +45,8 @@ namespace MWGui
};
FilterType mCurrentFilter;

std::unique_ptr<ItemSelectionDialog> mItemSelectionDialog;

ItemView* mItemView;
InventoryItemModel* mModel;
SortFilterItemModel* mSortModel;
Expand All @@ -63,6 +66,7 @@ namespace MWGui
void onCancelButtonClicked(MyGUI::Widget* _sender);
void onCreateButtonClicked(MyGUI::Widget* _sender);
void onIngredientSelected(MyGUI::Widget* _sender);
void onApparatusSelected(MyGUI::Widget* _sender);
void onAccept(MyGUI::EditBox*);
void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
Expand All @@ -84,7 +88,8 @@ namespace MWGui

void onSelectedItem(int index);

void removeIngredient(MyGUI::Widget* ingredient);
void onItemSelected(MWWorld::Ptr item);
void onItemCancel();

void createPotions(int count);

Expand Down
2 changes: 2 additions & 0 deletions apps/openmw/mwgui/itemselection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace MWGui
void setCategory(int category);
void setFilter(int filter);

SortFilterItemModel* getSortModel() { return mSortModel; }

private:
ItemView* mItemView;
SortFilterItemModel* mSortModel;
Expand Down
16 changes: 16 additions & 0 deletions apps/openmw/mwgui/sortfilteritemmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ namespace MWGui
: mCategory(Category_All)
, mFilter(0)
, mSortByType(true)
, mApparatusTypeFilter(-1)
{
mSourceModel = std::move(sourceModel);
}
Expand Down Expand Up @@ -311,6 +312,16 @@ namespace MWGui
return false;
}

if ((mFilter & Filter_OnlyAlchemyTools))
{
if (base.getType() != ESM::Apparatus::sRecordId)
return false;

int32_t apparatusType = base.get<ESM::Apparatus>()->mBase->mData.mType;
if (mApparatusTypeFilter >= 0 && apparatusType != mApparatusTypeFilter)
return false;
}

std::string compare = Utf8Stream::lowerCaseUtf8(item.mBase.getClass().getName(item.mBase));
if (compare.find(mNameFilter) == std::string::npos)
return false;
Expand Down Expand Up @@ -352,6 +363,11 @@ namespace MWGui
mEffectFilter = Utf8Stream::lowerCaseUtf8(filter);
}

void SortFilterItemModel::setApparatusTypeFilter(const int32_t type)
{
mApparatusTypeFilter = type;
}

void SortFilterItemModel::update()
{
mSourceModel->update();
Expand Down
3 changes: 3 additions & 0 deletions apps/openmw/mwgui/sortfilteritemmodel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace MWGui
void setFilter(int filter);
void setNameFilter(const std::string& filter);
void setEffectFilter(const std::string& filter);
void setApparatusTypeFilter(const int32_t type);

/// Use ItemStack::Type for sorting?
void setSortByType(bool sort) { mSortByType = sort; }
Expand All @@ -49,6 +50,7 @@ namespace MWGui
static constexpr int Filter_OnlyRepairable = (1 << 5);
static constexpr int Filter_OnlyRechargable = (1 << 6);
static constexpr int Filter_OnlyRepairTools = (1 << 7);
static constexpr int Filter_OnlyAlchemyTools = (1 << 8);

private:
std::vector<ItemStack> mItems;
Expand All @@ -59,6 +61,7 @@ namespace MWGui
int mFilter;
bool mSortByType;

int32_t mApparatusTypeFilter; // filter by apparatus type
std::string mNameFilter; // filter by item name
std::string mEffectFilter; // filter by magic effect
};
Expand Down
31 changes: 28 additions & 3 deletions apps/openmw/mwmechanics/alchemy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ void MWMechanics::Alchemy::setAlchemist(const MWWorld::Ptr& npc)

mTools.resize(4);

std::vector<MWWorld::Ptr> prevTools(mTools);

std::fill(mTools.begin(), mTools.end(), MWWorld::Ptr());

mEffects.clear();
Expand All @@ -384,6 +386,12 @@ void MWMechanics::Alchemy::setAlchemist(const MWWorld::Ptr& npc)
if (type < 0 || type >= static_cast<int>(mTools.size()))
throw std::runtime_error("invalid apparatus type");

if (prevTools[type] == *iter)
mTools[type] = *iter; // prefer the previous tool if still in the container

if (!mTools[type].isEmpty() && !prevTools[type].isEmpty() && mTools[type] == prevTools[type])
continue;

if (!mTools[type].isEmpty())
if (ref->mBase->mData.mQuality <= mTools[type].get<ESM::Apparatus>()->mBase->mData.mQuality)
continue;
Expand Down Expand Up @@ -415,7 +423,6 @@ MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::endIngredients(
void MWMechanics::Alchemy::clear()
{
mAlchemist = MWWorld::Ptr();
mTools.clear();
mIngredients.clear();
mEffects.clear();
setPotionName("");
Expand Down Expand Up @@ -452,15 +459,33 @@ int MWMechanics::Alchemy::addIngredient(const MWWorld::Ptr& ingredient)
return slot;
}

void MWMechanics::Alchemy::removeIngredient(int index)
void MWMechanics::Alchemy::removeIngredient(size_t index)
{
if (index >= 0 && index < static_cast<int>(mIngredients.size()))
if (index < mIngredients.size())
{
mIngredients[index] = MWWorld::Ptr();
updateEffects();
}
}

void MWMechanics::Alchemy::addApparatus(const MWWorld::Ptr& apparatus)
{
int32_t slot = apparatus.get<ESM::Apparatus>()->mBase->mData.mType;

mTools[slot] = apparatus;

updateEffects();
}

void MWMechanics::Alchemy::removeApparatus(size_t index)
{
if (index < mTools.size())
{
mTools[index] = MWWorld::Ptr();
updateEffects();
}
}

MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::beginEffects() const
{
return mEffects.begin();
Expand Down
8 changes: 7 additions & 1 deletion apps/openmw/mwmechanics/alchemy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,15 @@ namespace MWMechanics
/// \return Slot index or -1, if adding failed because of no free slot or the ingredient type being
/// listed already.

void removeIngredient(int index);
void addApparatus(const MWWorld::Ptr& apparatus);
///< Add apparatus into the appropriate slot.

void removeIngredient(size_t index);
///< Remove ingredient from slot (calling this function on an empty slot is a no-op).

void removeApparatus(size_t index);
///< Remove apparatus from slot.

std::string suggestPotionName();
///< Suggest a name for the potion, based on the current effects

Expand Down

0 comments on commit abd2c53

Please sign in to comment.