2012-05-29 07:02:22 +00:00
|
|
|
#include "spellwindow.hpp"
|
|
|
|
|
2018-05-24 06:48:46 +00:00
|
|
|
#include <MyGUI_EditBox.h>
|
2015-01-10 01:50:43 +00:00
|
|
|
#include <MyGUI_InputManager.h>
|
|
|
|
|
2019-02-22 21:14:07 +00:00
|
|
|
#include <components/misc/stringops.hpp>
|
2017-04-10 06:18:00 +00:00
|
|
|
#include <components/settings/settings.hpp>
|
|
|
|
|
2017-04-24 16:46:12 +00:00
|
|
|
/*
|
|
|
|
Start of tes3mp addition
|
|
|
|
|
|
|
|
Include additional headers for multiplayer purposes
|
|
|
|
*/
|
|
|
|
#include"../mwmp/Main.hpp"
|
|
|
|
#include"../mwmp/LocalPlayer.hpp"
|
|
|
|
/*
|
|
|
|
End of tes3mp addition
|
|
|
|
*/
|
|
|
|
|
2012-08-12 16:11:09 +00:00
|
|
|
#include "../mwbase/windowmanager.hpp"
|
2014-01-18 09:52:16 +00:00
|
|
|
#include "../mwbase/environment.hpp"
|
|
|
|
#include "../mwbase/world.hpp"
|
2017-08-18 15:24:34 +00:00
|
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
2012-07-03 10:30:50 +00:00
|
|
|
|
2012-05-29 10:35:03 +00:00
|
|
|
#include "../mwworld/inventorystore.hpp"
|
2014-01-18 09:52:16 +00:00
|
|
|
#include "../mwworld/class.hpp"
|
2014-12-19 10:26:54 +00:00
|
|
|
#include "../mwworld/esmstore.hpp"
|
2016-11-20 14:54:30 +00:00
|
|
|
#include "../mwworld/player.hpp"
|
2012-07-03 10:30:50 +00:00
|
|
|
|
2020-04-26 17:46:51 +00:00
|
|
|
#include "../mwmechanics/spellutil.hpp"
|
2014-01-18 09:52:16 +00:00
|
|
|
#include "../mwmechanics/spells.hpp"
|
|
|
|
#include "../mwmechanics/creaturestats.hpp"
|
2015-08-21 09:12:39 +00:00
|
|
|
#include "../mwmechanics/actorutil.hpp"
|
2012-07-03 10:30:50 +00:00
|
|
|
|
2013-03-03 11:01:19 +00:00
|
|
|
#include "spellicons.hpp"
|
2012-05-29 13:13:44 +00:00
|
|
|
#include "confirmationdialog.hpp"
|
2014-12-15 12:13:25 +00:00
|
|
|
#include "spellview.hpp"
|
2012-05-29 10:35:03 +00:00
|
|
|
|
2012-05-29 07:02:22 +00:00
|
|
|
namespace MWGui
|
|
|
|
{
|
2014-03-17 12:21:28 +00:00
|
|
|
|
2014-01-26 13:47:01 +00:00
|
|
|
SpellWindow::SpellWindow(DragAndDrop* drag)
|
2013-04-10 18:46:21 +00:00
|
|
|
: WindowPinnableBase("openmw_spell_window.layout")
|
2014-01-26 13:47:01 +00:00
|
|
|
, NoDrop(drag, mMainWidget)
|
2018-10-09 06:21:12 +00:00
|
|
|
, mSpellView(nullptr)
|
2015-04-03 04:59:13 +00:00
|
|
|
, mUpdateTimer(0.0f)
|
2012-05-29 07:02:22 +00:00
|
|
|
{
|
2013-03-03 11:01:19 +00:00
|
|
|
mSpellIcons = new SpellIcons();
|
|
|
|
|
2019-04-25 13:10:43 +00:00
|
|
|
MyGUI::Widget* deleteButton;
|
|
|
|
getWidget(deleteButton, "DeleteSpellButton");
|
|
|
|
|
2012-05-29 07:02:22 +00:00
|
|
|
getWidget(mSpellView, "SpellView");
|
|
|
|
getWidget(mEffectBox, "EffectsBox");
|
2018-05-24 06:48:46 +00:00
|
|
|
getWidget(mFilterEdit, "FilterEdit");
|
|
|
|
|
2014-12-15 12:13:25 +00:00
|
|
|
mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected);
|
2018-05-24 06:48:46 +00:00
|
|
|
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged);
|
2019-04-25 13:10:43 +00:00
|
|
|
deleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onDeleteClicked);
|
2012-05-29 16:59:11 +00:00
|
|
|
|
2014-12-15 12:13:25 +00:00
|
|
|
setCoord(498, 300, 302, 300);
|
2019-04-25 12:51:57 +00:00
|
|
|
|
2019-04-25 13:10:43 +00:00
|
|
|
// Adjust the spell filtering widget size because of MyGUI limitations.
|
|
|
|
int filterWidth = mSpellView->getSize().width - deleteButton->getSize().width - 3;
|
|
|
|
mFilterEdit->setSize(filterWidth, mFilterEdit->getSize().height);
|
2012-05-29 07:02:22 +00:00
|
|
|
}
|
|
|
|
|
2013-03-03 11:01:19 +00:00
|
|
|
SpellWindow::~SpellWindow()
|
|
|
|
{
|
|
|
|
delete mSpellIcons;
|
|
|
|
}
|
|
|
|
|
2012-05-29 07:02:22 +00:00
|
|
|
void SpellWindow::onPinToggled()
|
|
|
|
{
|
2017-04-10 06:18:00 +00:00
|
|
|
Settings::Manager::setBool("spells pin", "Windows", mPinned);
|
|
|
|
|
2013-04-10 04:32:05 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->setSpellVisibility(!mPinned);
|
2012-05-29 07:02:22 +00:00
|
|
|
}
|
2012-05-29 10:35:03 +00:00
|
|
|
|
2014-08-04 15:03:47 +00:00
|
|
|
void SpellWindow::onTitleDoubleClicked()
|
|
|
|
{
|
2019-04-26 07:37:57 +00:00
|
|
|
if (MyGUI::InputManager::getInstance().isShiftPressed())
|
|
|
|
MWBase::Environment::get().getWindowManager()->toggleMaximized(this);
|
|
|
|
else if (!mPinned)
|
2014-08-04 15:03:47 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->toggleVisible(GW_Magic);
|
|
|
|
}
|
|
|
|
|
2017-09-22 15:10:53 +00:00
|
|
|
void SpellWindow::onOpen()
|
2012-05-29 10:35:03 +00:00
|
|
|
{
|
2018-05-24 06:48:46 +00:00
|
|
|
// Reset the filter focus when opening the window
|
|
|
|
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
|
|
|
|
if (focus == mFilterEdit)
|
2019-06-08 23:08:09 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr);
|
2018-05-24 06:48:46 +00:00
|
|
|
|
2012-05-29 10:35:03 +00:00
|
|
|
updateSpells();
|
|
|
|
}
|
|
|
|
|
2015-04-03 04:59:13 +00:00
|
|
|
void SpellWindow::onFrame(float dt)
|
2017-09-23 20:00:15 +00:00
|
|
|
{
|
|
|
|
NoDrop::onFrame(dt);
|
|
|
|
mUpdateTimer += dt;
|
|
|
|
if (0.5f < mUpdateTimer)
|
2015-04-03 04:59:13 +00:00
|
|
|
{
|
2017-09-23 20:00:15 +00:00
|
|
|
mUpdateTimer = 0;
|
|
|
|
mSpellView->incrementalUpdate();
|
2015-04-03 04:59:13 +00:00
|
|
|
}
|
2018-08-27 05:37:08 +00:00
|
|
|
|
|
|
|
// Update effects in-game too if the window is pinned
|
|
|
|
if (mPinned && !MWBase::Environment::get().getWindowManager()->isGuiMode())
|
|
|
|
mSpellIcons->updateWidgets(mEffectBox, false);
|
2015-04-03 04:59:13 +00:00
|
|
|
}
|
|
|
|
|
2012-05-29 10:35:03 +00:00
|
|
|
void SpellWindow::updateSpells()
|
|
|
|
{
|
2013-03-03 11:01:19 +00:00
|
|
|
mSpellIcons->updateWidgets(mEffectBox, false);
|
|
|
|
|
2018-05-24 06:48:46 +00:00
|
|
|
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), mFilterEdit->getCaption()));
|
2012-05-29 10:35:03 +00:00
|
|
|
}
|
|
|
|
|
2014-12-15 12:13:25 +00:00
|
|
|
void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped)
|
2012-05-29 10:35:03 +00:00
|
|
|
{
|
2015-08-21 09:12:39 +00:00
|
|
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
2014-05-22 18:37:22 +00:00
|
|
|
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
2012-05-29 10:35:03 +00:00
|
|
|
|
|
|
|
// retrieve ContainerStoreIterator to the item
|
|
|
|
MWWorld::ContainerStoreIterator it = store.begin();
|
|
|
|
for (; it != store.end(); ++it)
|
|
|
|
{
|
|
|
|
if (*it == item)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-12-15 19:20:17 +00:00
|
|
|
if (it == store.end())
|
|
|
|
throw std::runtime_error("can't find selected item");
|
2012-05-29 10:35:03 +00:00
|
|
|
|
2012-05-29 13:13:44 +00:00
|
|
|
// equip, if it can be equipped and is not already equipped
|
2014-12-15 12:13:25 +00:00
|
|
|
if (!alreadyEquipped
|
2014-05-22 18:37:22 +00:00
|
|
|
&& !item.getClass().getEquipmentSlots(item).first.empty())
|
2012-05-29 10:35:03 +00:00
|
|
|
{
|
2015-09-07 19:32:28 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->useItem(item);
|
2014-12-15 12:47:34 +00:00
|
|
|
// make sure that item was successfully equipped
|
|
|
|
if (!store.isEquipped(item))
|
|
|
|
return;
|
2012-05-29 10:35:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
store.setSelectedEnchantItem(it);
|
2015-02-08 00:08:07 +00:00
|
|
|
// to reset WindowManager::mSelectedSpell immediately
|
|
|
|
MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(*it);
|
2012-05-29 10:35:03 +00:00
|
|
|
|
|
|
|
updateSpells();
|
|
|
|
}
|
|
|
|
|
2015-02-18 18:06:36 +00:00
|
|
|
void SpellWindow::askDeleteSpell(const std::string &spellId)
|
2012-05-29 10:35:03 +00:00
|
|
|
{
|
2015-02-18 18:06:36 +00:00
|
|
|
// delete spell, if allowed
|
|
|
|
const ESM::Spell* spell =
|
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
|
|
|
|
2016-11-20 14:54:30 +00:00
|
|
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
|
|
|
std::string raceId = player.get<ESM::NPC>()->mBase->mRace;
|
|
|
|
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceId);
|
2019-05-04 18:51:36 +00:00
|
|
|
// can't delete racial spells, birthsign spells or powers
|
|
|
|
bool isInherent = race->mPowers.exists(spell->mId) || spell->mData.mType == ESM::Spell::ST_Power;
|
|
|
|
const std::string& signId = MWBase::Environment::get().getWorld()->getPlayer().getBirthSign();
|
|
|
|
if (!isInherent && !signId.empty())
|
|
|
|
{
|
|
|
|
const ESM::BirthSign* sign = MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().find(signId);
|
|
|
|
isInherent = sign->mPowers.exists(spell->mId);
|
|
|
|
}
|
2016-11-20 14:54:30 +00:00
|
|
|
|
2019-05-04 18:51:36 +00:00
|
|
|
if (isInherent)
|
2014-12-15 12:13:25 +00:00
|
|
|
{
|
2015-02-18 18:06:36 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}");
|
2014-12-15 12:13:25 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-02-18 18:06:36 +00:00
|
|
|
// ask for confirmation
|
|
|
|
mSpellToDelete = spellId;
|
|
|
|
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
|
|
|
|
std::string question = MWBase::Environment::get().getWindowManager()->getGameSettingString("sQuestionDeleteSpell", "Delete %s?");
|
2019-05-19 08:33:57 +00:00
|
|
|
question = Misc::StringUtils::format(question, spell->mName);
|
2015-06-04 18:31:28 +00:00
|
|
|
dialog->askForConfirmation(question);
|
2015-02-18 18:06:36 +00:00
|
|
|
dialog->eventOkClicked.clear();
|
|
|
|
dialog->eventOkClicked += MyGUI::newDelegate(this, &SpellWindow::onDeleteSpellAccept);
|
|
|
|
dialog->eventCancelClicked.clear();
|
2014-12-15 12:13:25 +00:00
|
|
|
}
|
|
|
|
}
|
2012-05-29 10:35:03 +00:00
|
|
|
|
2015-02-18 18:06:36 +00:00
|
|
|
void SpellWindow::onModelIndexSelected(SpellModel::ModelIndex index)
|
2014-12-15 12:13:25 +00:00
|
|
|
{
|
2015-02-18 18:06:36 +00:00
|
|
|
const Spell& spell = mSpellView->getModel()->getItem(index);
|
|
|
|
if (spell.mType == Spell::Type_EnchantedItem)
|
2012-05-29 13:13:44 +00:00
|
|
|
{
|
2015-02-18 18:06:36 +00:00
|
|
|
onEnchantedItemSelected(spell.mItem, spell.mActive);
|
2012-05-29 13:13:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-02-18 18:06:36 +00:00
|
|
|
if (MyGUI::InputManager::getInstance().isShiftPressed())
|
|
|
|
askDeleteSpell(spell.mId);
|
|
|
|
else
|
|
|
|
onSpellSelected(spell.mId);
|
2012-05-29 13:13:44 +00:00
|
|
|
}
|
2015-02-18 18:06:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-24 06:48:46 +00:00
|
|
|
void SpellWindow::onFilterChanged(MyGUI::EditBox *sender)
|
|
|
|
{
|
|
|
|
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), sender->getCaption()));
|
|
|
|
}
|
|
|
|
|
2019-04-24 23:22:47 +00:00
|
|
|
void SpellWindow::onDeleteClicked(MyGUI::Widget *widget)
|
|
|
|
{
|
|
|
|
SpellModel::ModelIndex selected = mSpellView->getModel()->getSelectedIndex();
|
|
|
|
if (selected < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const Spell& spell = mSpellView->getModel()->getItem(selected);
|
|
|
|
if (spell.mType != Spell::Type_EnchantedItem)
|
|
|
|
askDeleteSpell(spell.mId);
|
|
|
|
}
|
|
|
|
|
2015-02-18 18:06:36 +00:00
|
|
|
void SpellWindow::onSpellSelected(const std::string& spellId)
|
|
|
|
{
|
2015-08-21 09:12:39 +00:00
|
|
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
2015-02-18 18:06:36 +00:00
|
|
|
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
|
|
|
store.setSelectedEnchantItem(store.end());
|
|
|
|
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
|
2012-05-29 10:35:03 +00:00
|
|
|
|
|
|
|
updateSpells();
|
2018-02-06 04:36:46 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Start of tes3mp addition
|
|
|
|
|
|
|
|
Send a PlayerMiscellaneous packet with the player's new selected spell
|
|
|
|
*/
|
|
|
|
mwmp::Main::get().getLocalPlayer()->sendSelectedSpell(spellId);
|
|
|
|
/*
|
|
|
|
End of tes3mp addition
|
|
|
|
*/
|
2012-05-29 10:35:03 +00:00
|
|
|
}
|
|
|
|
|
2012-05-29 13:13:44 +00:00
|
|
|
void SpellWindow::onDeleteSpellAccept()
|
|
|
|
{
|
2015-08-21 09:12:39 +00:00
|
|
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
2014-05-22 18:37:22 +00:00
|
|
|
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
2012-07-22 14:29:54 +00:00
|
|
|
MWMechanics::Spells& spells = stats.getSpells();
|
2012-05-29 13:13:44 +00:00
|
|
|
|
2014-01-05 18:40:05 +00:00
|
|
|
if (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == mSpellToDelete)
|
2013-04-10 04:32:05 +00:00
|
|
|
MWBase::Environment::get().getWindowManager()->unsetSelectedSpell();
|
2012-05-29 16:33:01 +00:00
|
|
|
|
2012-05-29 13:13:44 +00:00
|
|
|
spells.remove(mSpellToDelete);
|
|
|
|
|
2017-02-26 14:59:53 +00:00
|
|
|
/*
|
|
|
|
Start of tes3mp addition
|
|
|
|
|
|
|
|
Send an ID_PLAYER_SPELLBOOK packet every time a player deletes one of their spells
|
|
|
|
*/
|
2018-08-20 22:20:30 +00:00
|
|
|
mwmp::Main::get().getLocalPlayer()->sendSpellChange(mSpellToDelete, mwmp::SpellbookChanges::REMOVE);
|
2017-02-26 14:59:53 +00:00
|
|
|
/*
|
|
|
|
End of tes3mp addition
|
|
|
|
*/
|
2016-12-29 13:19:26 +00:00
|
|
|
|
2012-05-29 13:13:44 +00:00
|
|
|
updateSpells();
|
|
|
|
}
|
2014-12-15 14:23:03 +00:00
|
|
|
|
|
|
|
void SpellWindow::cycle(bool next)
|
|
|
|
{
|
2017-08-18 15:24:34 +00:00
|
|
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
|
|
|
|
|
|
|
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
|
|
|
|
return;
|
|
|
|
|
2020-10-09 17:34:36 +00:00
|
|
|
bool godmode = MWBase::Environment::get().getWorld()->getGodModeState();
|
2017-08-18 15:24:34 +00:00
|
|
|
const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);
|
2020-10-09 17:34:36 +00:00
|
|
|
if ((!godmode && stats.isParalyzed()) || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
|
2017-08-18 15:24:34 +00:00
|
|
|
return;
|
|
|
|
|
2018-05-24 08:39:00 +00:00
|
|
|
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), ""));
|
2014-12-15 14:23:03 +00:00
|
|
|
|
2019-04-24 23:22:47 +00:00
|
|
|
SpellModel::ModelIndex selected = mSpellView->getModel()->getSelectedIndex();
|
|
|
|
if (selected < 0)
|
|
|
|
selected = 0;
|
2014-12-15 14:23:03 +00:00
|
|
|
|
|
|
|
selected += next ? 1 : -1;
|
|
|
|
int itemcount = mSpellView->getModel()->getItemCount();
|
|
|
|
if (itemcount == 0)
|
|
|
|
return;
|
|
|
|
selected = (selected + itemcount) % itemcount;
|
|
|
|
|
2015-02-18 18:06:36 +00:00
|
|
|
const Spell& spell = mSpellView->getModel()->getItem(selected);
|
|
|
|
if (spell.mType == Spell::Type_EnchantedItem)
|
|
|
|
onEnchantedItemSelected(spell.mItem, spell.mActive);
|
|
|
|
else
|
|
|
|
onSpellSelected(spell.mId);
|
2014-12-15 14:23:03 +00:00
|
|
|
}
|
2012-05-29 07:02:22 +00:00
|
|
|
}
|