Mass potion creation (feature #4642)

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

@ -155,6 +155,7 @@
Feature #4626: Weapon priority: account for weapon speed
Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating
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 #4545: Enable is_pod string test
Task #4605: Optimize skinning

@ -1,5 +1,7 @@
#include "alchemywindow.hpp"
#include <climits>
#include <MyGUI_Gui.h>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
@ -25,6 +27,9 @@
namespace MWGui
{
const float AlchemyWindow::sCountChangeInitialPause = 0.5f;
const float AlchemyWindow::sCountChangeInterval = 0.1f;
AlchemyWindow::AlchemyWindow()
: WindowBase("openmw_alchemy_window.layout")
, mSortModel(NULL)
@ -43,9 +48,21 @@ namespace MWGui
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");
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);
@ -77,7 +94,15 @@ namespace MWGui
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();
switch (result)
@ -92,8 +117,11 @@ namespace MWGui
winMgr->messageBox("#{sNotifyMessage6a}");
break;
case MWMechanics::Alchemy::Result_Success:
winMgr->messageBox("#{sPotionSuccess}");
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:
@ -126,6 +154,7 @@ namespace MWGui
mItemView->resetScrollBars();
mNameEdit->setCaption("");
mBrewCountEdit->setValue(1);
int index = 0;
for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools());
@ -250,4 +279,61 @@ namespace MWGui
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 <MyGUI_ControllerManager.h>
#include "../mwmechanics/alchemy.hpp"
#include <components/widgets/numericeditbox.hpp>
#include "controllers.hpp"
#include "windowbase.hpp"
namespace MWMechanics
@ -28,6 +33,10 @@ namespace MWGui
void onResChange(int, int) { center(); }
private:
static const float sCountChangeInitialPause; // in seconds
static const float sCountChangeInterval; // in seconds
std::string mSuggestedPotionName;
ItemView* mItemView;
@ -38,17 +47,32 @@ namespace MWGui
MyGUI::Widget* mEffectsBox;
MyGUI::Button* mIncreaseButton;
MyGUI::Button* mDecreaseButton;
MyGUI::EditBox* mNameEdit;
Gui::NumericEditBox* mBrewCountEdit;
void onCancelButtonClicked(MyGUI::Widget* _sender);
void onCreateButtonClicked(MyGUI::Widget* _sender);
void onIngredientSelected(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);
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 removeIngredient(MyGUI::Widget* ingredient);
void createPotions(int count);
void update();
std::unique_ptr<MWMechanics::Alchemy> mAlchemy;

@ -30,6 +30,7 @@
MWMechanics::Alchemy::Alchemy()
: mValue(0)
, mPotionName("")
{
}
@ -336,6 +337,25 @@ int MWMechanics::Alchemy::countIngredients() const
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)
{
mAlchemist = npc;
@ -396,6 +416,12 @@ void MWMechanics::Alchemy::clear()
mTools.clear();
mIngredients.clear();
mEffects.clear();
setPotionName("");
}
void MWMechanics::Alchemy::setPotionName(const std::string& name)
{
mPotionName = name;
}
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);
}
MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name)
MWMechanics::Alchemy::Result MWMechanics::Alchemy::getReadyStatus() const
{
if (mTools[ESM::Apparatus::MortarPestle].isEmpty())
return Result_NoMortarAndPestle;
@ -464,15 +490,43 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na
if (countIngredients()<2)
return Result_LessThanTwoIngredients;
if (name.empty())
if (mPotionName.empty())
return Result_NoName;
if (listEffects().empty())
{
removeIngredients();
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())
{
// 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;
}
addPotion (name);
addPotion(mPotionName);
removeIngredients();

@ -51,11 +51,14 @@ namespace MWMechanics
TIngredientsContainer mIngredients;
TEffectsContainer mEffects;
int mValue;
std::string mPotionName;
void applyTools (int flags, float& value) const;
void updateEffects();
Result getReadyStatus() 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
/// \note Does not account for record ID, model or icon
@ -70,6 +73,10 @@ namespace MWMechanics
void increaseSkill();
///< 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;
int countIngredients() const;
@ -79,6 +86,8 @@ namespace MWMechanics
TEffectsIterator endEffects() const;
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);
///< Does npc have sufficient alchemy skill to know about this potion effect?
@ -100,6 +109,9 @@ namespace MWMechanics
void clear();
///< Remove alchemist, tools and ingredients.
void setPotionName(const std::string& name);
///< Set name of potion to create
std::set<EffectKey> listEffects() const;
///< List all effects shared by at least two ingredients.
@ -115,8 +127,8 @@ namespace MWMechanics
std::string suggestPotionName ();
///< Suggest a name for the potion, based on the current effects
Result create (const std::string& name);
///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and
Result create (const std::string& name, int& count);
///< 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
};

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

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

@ -74,7 +74,20 @@
<!-- 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"/>

Loading…
Cancel
Save