Mass potion creation (feature #4642)

pull/541/head
Andrei Kortunov 6 years ago
parent 07be9ae8ac
commit 276b7830a9

@ -155,6 +155,7 @@
Feature #4626: Weapon priority: account for weapon speed Feature #4626: Weapon priority: account for weapon speed
Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating
Feature #4636: Use sTo GMST in spellmaking menu Feature #4636: Use sTo GMST in spellmaking menu
Feature #4642: Batching potion creation
Task #2490: Don't open command prompt window on Release-mode builds automatically Task #2490: Don't open command prompt window on Release-mode builds automatically
Task #4545: Enable is_pod string test Task #4545: Enable is_pod string test
Task #4605: Optimize skinning Task #4605: Optimize skinning

@ -1,5 +1,7 @@
#include "alchemywindow.hpp" #include "alchemywindow.hpp"
#include <climits>
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <MyGUI_Button.h> #include <MyGUI_Button.h>
#include <MyGUI_EditBox.h> #include <MyGUI_EditBox.h>
@ -25,6 +27,9 @@
namespace MWGui namespace MWGui
{ {
const float AlchemyWindow::sCountChangeInitialPause = 0.5f;
const float AlchemyWindow::sCountChangeInterval = 0.1f;
AlchemyWindow::AlchemyWindow() AlchemyWindow::AlchemyWindow()
: WindowBase("openmw_alchemy_window.layout") : WindowBase("openmw_alchemy_window.layout")
, mSortModel(NULL) , mSortModel(NULL)
@ -43,9 +48,21 @@ namespace MWGui
getWidget(mApparatus[2], "Apparatus3"); getWidget(mApparatus[2], "Apparatus3");
getWidget(mApparatus[3], "Apparatus4"); getWidget(mApparatus[3], "Apparatus4");
getWidget(mEffectsBox, "CreatedEffects"); getWidget(mEffectsBox, "CreatedEffects");
getWidget(mBrewCountEdit, "BrewCount");
getWidget(mIncreaseButton, "IncreaseButton");
getWidget(mDecreaseButton, "DecreaseButton");
getWidget(mNameEdit, "NameEdit"); getWidget(mNameEdit, "NameEdit");
getWidget(mItemView, "ItemView"); getWidget(mItemView, "ItemView");
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); mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem);
@ -77,7 +94,15 @@ namespace MWGui
void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender)
{ {
MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ()); 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(); MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
switch (result) switch (result)
@ -92,8 +117,11 @@ namespace MWGui
winMgr->messageBox("#{sNotifyMessage6a}"); winMgr->messageBox("#{sNotifyMessage6a}");
break; break;
case MWMechanics::Alchemy::Result_Success: case MWMechanics::Alchemy::Result_Success:
winMgr->messageBox("#{sPotionSuccess}");
winMgr->playSound("potion success"); winMgr->playSound("potion success");
if (count == 1)
winMgr->messageBox("#{sPotionSuccess}");
else
winMgr->messageBox("#{sPotionSuccess} "+mNameEdit->getCaption()+" ("+std::to_string(count)+")");
break; break;
case MWMechanics::Alchemy::Result_NoEffects: case MWMechanics::Alchemy::Result_NoEffects:
case MWMechanics::Alchemy::Result_RandomFailure: case MWMechanics::Alchemy::Result_RandomFailure:
@ -126,6 +154,7 @@ namespace MWGui
mItemView->resetScrollBars(); mItemView->resetScrollBars();
mNameEdit->setCaption(""); mNameEdit->setCaption("");
mBrewCountEdit->setValue(1);
int index = 0; int index = 0;
for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools()); for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools());
@ -250,4 +279,61 @@ namespace MWGui
update(); update();
} }
void AlchemyWindow::addRepeatController(MyGUI::Widget *widget)
{
MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerRepeatEvent::getClassTypeName());
Controllers::ControllerRepeatEvent* controller = item->castType<Controllers::ControllerRepeatEvent>();
controller->eventRepeatClick += MyGUI::newDelegate(this, &AlchemyWindow::onRepeatClick);
controller->setRepeat(sCountChangeInitialPause, sCountChangeInterval);
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, and prevent entering INT_MIN since abs(INT_MIN) is undefined
if (currentCount == INT_MAX || currentCount == INT_MIN+1)
return;
mBrewCountEdit->setValue(currentCount+1);
}
void AlchemyWindow::onDecreaseButtonTriggered()
{
int currentCount = mBrewCountEdit->getValue();
if (currentCount > 1)
mBrewCountEdit->setValue(currentCount-1);
}
} }

@ -3,8 +3,13 @@
#include <vector> #include <vector>
#include <MyGUI_ControllerManager.h>
#include "../mwmechanics/alchemy.hpp" #include "../mwmechanics/alchemy.hpp"
#include <components/widgets/numericeditbox.hpp>
#include "controllers.hpp"
#include "windowbase.hpp" #include "windowbase.hpp"
namespace MWMechanics namespace MWMechanics
@ -28,6 +33,10 @@ namespace MWGui
void onResChange(int, int) { center(); } void onResChange(int, int) { center(); }
private: private:
static const float sCountChangeInitialPause; // in seconds
static const float sCountChangeInterval; // in seconds
std::string mSuggestedPotionName; std::string mSuggestedPotionName;
ItemView* mItemView; ItemView* mItemView;
@ -38,17 +47,32 @@ namespace MWGui
MyGUI::Widget* mEffectsBox; MyGUI::Widget* mEffectsBox;
MyGUI::Button* mIncreaseButton;
MyGUI::Button* mDecreaseButton;
MyGUI::EditBox* mNameEdit; MyGUI::EditBox* mNameEdit;
Gui::NumericEditBox* mBrewCountEdit;
void onCancelButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender);
void onCreateButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender);
void onIngredientSelected(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender);
void onAccept(MyGUI::EditBox*); 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);
void onCountButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onCountValueChanged(int value);
void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller);
void addRepeatController(MyGUI::Widget* widget);
void onIncreaseButtonTriggered();
void onDecreaseButtonTriggered();
void onSelectedItem(int index); void onSelectedItem(int index);
void removeIngredient(MyGUI::Widget* ingredient); void removeIngredient(MyGUI::Widget* ingredient);
void createPotions(int count);
void update(); void update();
std::unique_ptr<MWMechanics::Alchemy> mAlchemy; std::unique_ptr<MWMechanics::Alchemy> mAlchemy;

@ -30,6 +30,7 @@
MWMechanics::Alchemy::Alchemy() MWMechanics::Alchemy::Alchemy()
: mValue(0) : mValue(0)
, mPotionName("")
{ {
} }
@ -336,6 +337,25 @@ int MWMechanics::Alchemy::countIngredients() const
return ingredients; return ingredients;
} }
int MWMechanics::Alchemy::countPotionsToBrew() const
{
Result readyStatus = getReadyStatus();
if (readyStatus != Result_Success)
return 0;
int toBrew = -1;
for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)
if (!iter->isEmpty())
{
int count = iter->getRefData().getCount();
if ((count > 0 && count < toBrew) || toBrew < 0)
toBrew = count;
}
return toBrew;
}
void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc)
{ {
mAlchemist = npc; mAlchemist = npc;
@ -396,6 +416,12 @@ void MWMechanics::Alchemy::clear()
mTools.clear(); mTools.clear();
mIngredients.clear(); mIngredients.clear();
mEffects.clear(); mEffects.clear();
setPotionName("");
}
void MWMechanics::Alchemy::setPotionName(const std::string& name)
{
mPotionName = name;
} }
int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)
@ -456,7 +482,7 @@ bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWW
|| (potionEffectIndex <= 7 && alchemySkill >= fWortChanceValue*4); || (potionEffectIndex <= 7 && alchemySkill >= fWortChanceValue*4);
} }
MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) MWMechanics::Alchemy::Result MWMechanics::Alchemy::getReadyStatus() const
{ {
if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) if (mTools[ESM::Apparatus::MortarPestle].isEmpty())
return Result_NoMortarAndPestle; return Result_NoMortarAndPestle;
@ -464,15 +490,43 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na
if (countIngredients()<2) if (countIngredients()<2)
return Result_LessThanTwoIngredients; return Result_LessThanTwoIngredients;
if (name.empty()) if (mPotionName.empty())
return Result_NoName; return Result_NoName;
if (listEffects().empty()) if (listEffects().empty())
{
removeIngredients();
return Result_NoEffects; return Result_NoEffects;
return Result_Success;
}
MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name, int& count)
{
setPotionName(name);
Result readyStatus = getReadyStatus();
if (readyStatus == Result_NoEffects)
removeIngredients();
if (readyStatus != Result_Success)
return readyStatus;
Result result = Result_RandomFailure;
int brewedCount = 0;
for (int i = 0; i < count; ++i)
{
if (createSingle() == Result_Success)
{
result = Result_Success;
brewedCount++;
}
} }
count = brewedCount;
return result;
}
MWMechanics::Alchemy::Result MWMechanics::Alchemy::createSingle ()
{
if (beginEffects() == endEffects()) if (beginEffects() == endEffects())
{ {
// all effects were nullified due to insufficient skill // all effects were nullified due to insufficient skill
@ -486,7 +540,7 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na
return Result_RandomFailure; return Result_RandomFailure;
} }
addPotion (name); addPotion(mPotionName);
removeIngredients(); removeIngredients();

@ -51,11 +51,14 @@ namespace MWMechanics
TIngredientsContainer mIngredients; TIngredientsContainer mIngredients;
TEffectsContainer mEffects; TEffectsContainer mEffects;
int mValue; int mValue;
std::string mPotionName;
void applyTools (int flags, float& value) const; void applyTools (int flags, float& value) const;
void updateEffects(); void updateEffects();
Result getReadyStatus() const;
const ESM::Potion *getRecord(const ESM::Potion& toFind) const; const ESM::Potion *getRecord(const ESM::Potion& toFind) const;
///< Try to find a potion record similar to \a toFind in the record store, or return 0 if not found ///< Try to find a potion record similar to \a toFind in the record store, or return 0 if not found
/// \note Does not account for record ID, model or icon /// \note Does not account for record ID, model or icon
@ -70,6 +73,10 @@ namespace MWMechanics
void increaseSkill(); void increaseSkill();
///< Increase alchemist's skill. ///< Increase alchemist's skill.
Result createSingle ();
///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and
/// adjust the skills of the alchemist accordingly.
float getAlchemyFactor() const; float getAlchemyFactor() const;
int countIngredients() const; int countIngredients() const;
@ -79,6 +86,8 @@ namespace MWMechanics
TEffectsIterator endEffects() const; TEffectsIterator endEffects() const;
public: public:
int countPotionsToBrew() const;
///< calculates maximum amount of potions, which you can make from selected ingredients
static bool knownEffect (unsigned int potionEffectIndex, const MWWorld::Ptr& npc); static bool knownEffect (unsigned int potionEffectIndex, const MWWorld::Ptr& npc);
///< Does npc have sufficient alchemy skill to know about this potion effect? ///< Does npc have sufficient alchemy skill to know about this potion effect?
@ -100,6 +109,9 @@ namespace MWMechanics
void clear(); void clear();
///< Remove alchemist, tools and ingredients. ///< Remove alchemist, tools and ingredients.
void setPotionName(const std::string& name);
///< Set name of potion to create
std::set<EffectKey> listEffects() const; std::set<EffectKey> listEffects() const;
///< List all effects shared by at least two ingredients. ///< List all effects shared by at least two ingredients.
@ -115,8 +127,8 @@ namespace MWMechanics
std::string suggestPotionName (); std::string suggestPotionName ();
///< Suggest a name for the potion, based on the current effects ///< Suggest a name for the potion, based on the current effects
Result create (const std::string& name); Result create (const std::string& name, int& count);
///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and ///< Try to create potions from the ingredients, place them in the inventory of the alchemist and
/// adjust the skills of the alchemist accordingly. /// adjust the skills of the alchemist accordingly.
/// \param name must not be an empty string, or Result_NoName is returned /// \param name must not be an empty string, or Result_NoName is returned
}; };

@ -59,6 +59,11 @@ namespace Gui
} }
} }
int NumericEditBox::getValue()
{
return mValue;
}
void NumericEditBox::setMinValue(int minValue) void NumericEditBox::setMinValue(int minValue)
{ {
mMinValue = minValue; mMinValue = minValue;

@ -30,6 +30,7 @@ namespace Gui
/// @note Does not trigger eventValueChanged /// @note Does not trigger eventValueChanged
void setValue (int value); void setValue (int value);
int getValue();
void setMinValue(int minValue); void setMinValue(int minValue);
void setMaxValue(int maxValue); void setMaxValue(int maxValue);

@ -74,7 +74,20 @@
<!-- Buttons --> <!-- Buttons -->
<Widget type="HBox" skin="" position="110 374 452 28" align="Bottom Right"> <Widget type="HBox" skin="" position="10 374 552 28" align="Bottom HStretch">
<Widget type="AutoSizedButton" skin="MW_Button" name="DecreaseButton">
<Property key="Caption" value=" - "/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="NumericEditBox" skin="MW_TextEdit" position="0 0 96 25" name="BrewCount">
<Property key="TextAlign" value="Center"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" name="IncreaseButton">
<Property key="Caption" value=" + "/>
<Property key="NeedKey" value="false"/>
</Widget>
<Widget type="Spacer"/> <Widget type="Spacer"/>

Loading…
Cancel
Save