1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-06 09:39:49 +00:00
OpenMW/apps/openmw/mwgui/trainingwindow.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

235 lines
8.5 KiB
C++
Raw Normal View History

2012-10-17 18:03:02 +02:00
#include "trainingwindow.hpp"
#include <MyGUI_Button.h>
2015-01-10 02:50:43 +01:00
#include <MyGUI_Gui.h>
#include <MyGUI_TextIterator.h>
2015-01-10 02:50:43 +01:00
2012-10-17 18:03:02 +02:00
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
2012-10-17 18:03:02 +02:00
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
2013-05-11 18:38:27 +02:00
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp"
2012-10-17 18:03:02 +02:00
2015-08-21 21:12:39 +12:00
#include "../mwmechanics/actorutil.hpp"
2012-10-17 18:03:02 +02:00
#include "../mwmechanics/npcstats.hpp"
#include <components/esm3/loadclas.hpp>
2023-06-27 23:41:06 +02:00
#include <components/settings/values.hpp>
2012-10-17 18:03:02 +02:00
#include "tooltips.hpp"
namespace MWGui
{
TrainingWindow::TrainingWindow()
: WindowBase("openmw_trainingwindow.layout")
, mTimeAdvancer(0.05f)
2012-10-17 18:03:02 +02:00
{
getWidget(mTrainingOptions, "TrainingOptions");
getWidget(mCancelButton, "CancelButton");
getWidget(mPlayerGold, "PlayerGold");
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked);
mTimeAdvancer.eventProgressChanged += MyGUI::newDelegate(this, &TrainingWindow::onTrainingProgressChanged);
mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &TrainingWindow::onTrainingFinished);
2012-10-17 18:03:02 +02:00
}
void TrainingWindow::onOpen()
2012-10-17 18:03:02 +02:00
{
if (mTimeAdvancer.isRunning())
{
mProgressBar.setVisible(true);
setVisible(false);
}
else
mProgressBar.setVisible(false);
2012-10-17 18:03:02 +02:00
center();
}
void TrainingWindow::setPtr(const MWWorld::Ptr& actor)
2012-10-17 18:03:02 +02:00
{
2023-08-20 15:27:00 +02:00
if (actor.isEmpty() || !actor.getClass().isActor())
throw std::runtime_error("Invalid argument in TrainingWindow::setPtr");
2012-10-17 18:03:02 +02:00
mPtr = actor;
2015-08-21 21:12:39 +12:00
MWWorld::Ptr player = MWMechanics::getPlayer();
int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId);
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
2012-10-17 18:03:02 +02:00
2023-06-05 16:45:45 +02:00
const auto& store = MWBase::Environment::get().getESMStore();
const MWWorld::Store<ESM::GameSetting>& gmst = store->get<ESM::GameSetting>();
const MWWorld::Store<ESM::Skill>& skillStore = store->get<ESM::Skill>();
2023-08-15 14:55:13 +00:00
// NPC can train you in their best 3 skills
constexpr size_t maxSkills = 3;
2023-06-05 16:45:45 +02:00
std::vector<std::pair<const ESM::Skill*, float>> skills;
skills.reserve(maxSkills);
2023-08-13 20:26:59 +02:00
const auto sortByValue
= [](const std::pair<const ESM::Skill*, float>& lhs, const std::pair<const ESM::Skill*, float>& rhs) {
return lhs.second > rhs.second;
};
// Maintain a sorted vector of max maxSkills elements, ordering skills by value and content file order
const MWMechanics::NpcStats& actorStats = actor.getClass().getNpcStats(actor);
2023-06-05 16:45:45 +02:00
for (const ESM::Skill& skill : skillStore)
2012-10-17 18:03:02 +02:00
{
2023-06-05 21:21:30 +02:00
float value = getSkillForTraining(actorStats, skill.mId);
if (skills.size() < maxSkills)
{
skills.emplace_back(&skill, value);
2023-08-13 20:26:59 +02:00
std::stable_sort(skills.begin(), skills.end(), sortByValue);
}
else
{
auto& lowest = skills[maxSkills - 1];
if (lowest.second < value)
{
lowest.first = &skill;
lowest.second = value;
2023-08-13 20:26:59 +02:00
std::stable_sort(skills.begin(), skills.end(), sortByValue);
}
}
2012-10-17 18:03:02 +02:00
}
MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator();
MyGUI::Gui::getInstance().destroyWidgets(widgets);
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
2012-10-17 18:03:02 +02:00
2023-07-16 20:46:54 +02:00
const int lineHeight = Settings::gui().mFontSize + 2;
2022-08-30 07:54:20 +00:00
for (size_t i = 0; i < skills.size(); ++i)
2012-10-17 18:03:02 +02:00
{
2023-06-05 16:45:45 +02:00
const ESM::Skill* skill = skills[i].first;
int price = static_cast<int>(
2023-06-05 21:21:30 +02:00
pcStats.getSkill(skill->mId).getBase() * gmst.find("iTrainingMod")->mValue.getInteger());
price = std::max(1, price);
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);
2012-10-17 18:03:02 +02:00
MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>(price <= playerGold
? "SandTextButton"
: "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip
2022-08-30 07:54:20 +00:00
MyGUI::IntCoord(5, 5 + i * lineHeight, mTrainingOptions->getWidth() - 10, lineHeight),
MyGUI::Align::Default);
2012-10-17 18:03:02 +02:00
button->setUserData(skills[i].first);
2012-10-17 18:03:02 +02:00
button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected);
button->setCaptionWithReplacing(
MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price));
2012-10-17 18:03:02 +02:00
button->setSize(button->getTextSize().width + 12, button->getSize().height);
2023-06-06 17:24:22 +02:00
ToolTips::createSkillToolTip(button, skill->mId);
2012-10-17 18:03:02 +02:00
}
center();
}
void TrainingWindow::onReferenceUnavailable()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training);
2012-10-17 18:03:02 +02:00
}
void TrainingWindow::onCancelButtonClicked(MyGUI::Widget* sender)
{
2017-09-23 12:18:39 +02:00
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training);
2012-10-17 18:03:02 +02:00
}
void TrainingWindow::onTrainingSelected(MyGUI::Widget* sender)
{
2023-06-05 16:45:45 +02:00
const ESM::Skill* skill = *sender->getUserData<const ESM::Skill*>();
2012-10-17 18:03:02 +02:00
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
2012-10-17 18:03:02 +02:00
2023-04-20 21:07:53 +02:00
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
2023-06-05 21:21:30 +02:00
int price = pcStats.getSkill(skill->mId).getBase()
2018-08-29 18:38:12 +03:00
* store.get<ESM::GameSetting>().find("iTrainingMod")->mValue.getInteger();
2012-11-09 14:42:09 +01:00
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true);
2012-10-17 18:03:02 +02:00
if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId))
return;
2023-06-05 21:21:30 +02:00
if (getSkillForTraining(mPtr.getClass().getNpcStats(mPtr), skill->mId)
<= pcStats.getSkill(skill->mId).getBase())
2012-10-17 18:48:29 +02:00
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sServiceTrainingWords}");
2012-10-17 18:48:29 +02:00
return;
}
// You can not train a skill above its governing attribute
2023-06-19 20:41:54 +02:00
if (pcStats.getSkill(skill->mId).getBase()
2023-08-03 20:21:44 +02:00
>= pcStats.getAttribute(ESM::Attribute::indexToRefId(skill->mData.mAttribute)).getBase())
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}");
return;
}
2012-10-17 18:03:02 +02:00
// increase skill
MWWorld::LiveCellRef<ESM::NPC>* playerRef = player.get<ESM::NPC>();
const ESM::Class* class_ = store.get<ESM::Class>().find(playerRef->mBase->mClass);
pcStats.increaseSkill(skill->mId, *class_, true);
2012-10-17 18:03:02 +02:00
// remove gold
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price);
2012-10-17 18:03:02 +02:00
// add gold to NPC trading gold pool
2018-10-08 17:06:30 +03:00
MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats(mPtr);
npcStats.setGoldPool(npcStats.getGoldPool() + price);
setVisible(false);
mProgressBar.setVisible(true);
mProgressBar.setProgress(0, 2);
mTimeAdvancer.run(2);
MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2);
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.2, false, 0.2);
2012-10-17 18:03:02 +02:00
}
void TrainingWindow::onTrainingProgressChanged(int cur, int total)
{
mProgressBar.setProgress(cur, total);
}
void TrainingWindow::onTrainingFinished()
{
mProgressBar.setVisible(false);
// advance time
MWBase::Environment::get().getMechanicsManager()->rest(2, false);
MWBase::Environment::get().getWorld()->advanceTime(2);
// go back to game mode
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Training);
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
}
2023-06-05 21:21:30 +02:00
float TrainingWindow::getSkillForTraining(const MWMechanics::NpcStats& stats, ESM::RefId id) const
{
2023-06-27 23:41:06 +02:00
if (Settings::game().mTrainersTrainingSkillsBasedOnBaseSkill)
2023-06-05 21:21:30 +02:00
return stats.getSkill(id).getBase();
return stats.getSkill(id).getModified();
}
2012-10-17 18:03:02 +02:00
void TrainingWindow::onFrame(float dt)
{
checkReferenceAvailable();
mTimeAdvancer.onFrame(dt);
2012-10-17 18:03:02 +02:00
}
2017-10-08 20:38:20 +00:00
bool TrainingWindow::exit()
{
return !mTimeAdvancer.isRunning();
}
2012-10-17 18:03:02 +02:00
}