Post merge
commit
20288de685
@ -0,0 +1,512 @@
|
||||
#include "alchemywindow.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwsound/soundmanager.hpp"
|
||||
|
||||
#include "window_manager.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string getIconPath(MWWorld::Ptr ptr)
|
||||
{
|
||||
std::string path = std::string("icons\\");
|
||||
path += MWWorld::Class::get(ptr).getInventoryIcon(ptr);
|
||||
int pos = path.rfind(".");
|
||||
path.erase(pos);
|
||||
path.append(".dds");
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
AlchemyWindow::AlchemyWindow(WindowManager& parWindowManager)
|
||||
: WindowBase("openmw_alchemy_window_layout.xml", parWindowManager)
|
||||
, ContainerBase(0)
|
||||
{
|
||||
getWidget(mCreateButton, "CreateButton");
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
getWidget(mIngredient1, "Ingredient1");
|
||||
getWidget(mIngredient2, "Ingredient2");
|
||||
getWidget(mIngredient3, "Ingredient3");
|
||||
getWidget(mIngredient4, "Ingredient4");
|
||||
getWidget(mApparatus1, "Apparatus1");
|
||||
getWidget(mApparatus2, "Apparatus2");
|
||||
getWidget(mApparatus3, "Apparatus3");
|
||||
getWidget(mApparatus4, "Apparatus4");
|
||||
getWidget(mEffectsBox, "CreatedEffects");
|
||||
getWidget(mNameEdit, "NameEdit");
|
||||
|
||||
mIngredient1->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
|
||||
mIngredient2->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
|
||||
mIngredient3->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
|
||||
mIngredient4->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected);
|
||||
|
||||
MyGUI::Widget* buttonBox = mCancelButton->getParent();
|
||||
int cancelButtonWidth = mCancelButton->getTextSize().width + 24;
|
||||
mCancelButton->setCoord(buttonBox->getWidth() - cancelButtonWidth,
|
||||
mCancelButton->getTop(), cancelButtonWidth, mCancelButton->getHeight());
|
||||
int createButtonWidth = mCreateButton->getTextSize().width + 24;
|
||||
mCreateButton->setCoord(buttonBox->getWidth() - createButtonWidth - cancelButtonWidth - 4,
|
||||
mCreateButton->getTop(), createButtonWidth, mCreateButton->getHeight());
|
||||
|
||||
mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked);
|
||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);
|
||||
|
||||
MyGUI::ScrollView* itemView;
|
||||
MyGUI::Widget* containerWidget;
|
||||
getWidget(containerWidget, "Items");
|
||||
getWidget(itemView, "ItemView");
|
||||
setWidgets(containerWidget, itemView);
|
||||
|
||||
center();
|
||||
}
|
||||
|
||||
void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mWindowManager.popGuiMode();
|
||||
mWindowManager.popGuiMode();
|
||||
}
|
||||
|
||||
void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
// check if mortar & pestle is available (always needed)
|
||||
/// \todo check albemic, calcinator, retort (sometimes needed)
|
||||
if (!mApparatus1->isUserString("ToolTipType"))
|
||||
{
|
||||
mWindowManager.messageBox("#{sNotifyMessage45}", std::vector<std::string>());
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure 2 or more ingredients were selected
|
||||
int numIngreds = 0;
|
||||
if (mIngredient1->isUserString("ToolTipType"))
|
||||
++numIngreds;
|
||||
if (mIngredient2->isUserString("ToolTipType"))
|
||||
++numIngreds;
|
||||
if (mIngredient3->isUserString("ToolTipType"))
|
||||
++numIngreds;
|
||||
if (mIngredient4->isUserString("ToolTipType"))
|
||||
++numIngreds;
|
||||
if (numIngreds < 2)
|
||||
{
|
||||
mWindowManager.messageBox("#{sNotifyMessage6a}", std::vector<std::string>());
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure a name was entered
|
||||
std::string name = mNameEdit->getCaption();
|
||||
boost::algorithm::trim(name);
|
||||
if (name == "")
|
||||
{
|
||||
mWindowManager.messageBox("#{sNotifyMessage37}", std::vector<std::string>());
|
||||
return;
|
||||
}
|
||||
|
||||
// if there are no created effects, the potion will always fail (but the ingredients won't be destroyed)
|
||||
if (mEffects.empty())
|
||||
{
|
||||
mWindowManager.messageBox("#{sNotifyMessage8}", std::vector<std::string>());
|
||||
MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rand() % 2 == 0) /// \todo
|
||||
{
|
||||
ESM::Potion newPotion;
|
||||
newPotion.name = mNameEdit->getCaption();
|
||||
ESM::EffectList effects;
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
{
|
||||
if (mEffects.size() >= i+1)
|
||||
{
|
||||
ESM::ENAMstruct effect;
|
||||
effect.effectID = mEffects[i].mEffectID;
|
||||
effect.area = 0;
|
||||
effect.range = ESM::RT_Self;
|
||||
effect.skill = mEffects[i].mSkill;
|
||||
effect.attribute = mEffects[i].mAttribute;
|
||||
effect.magnMin = 1; /// \todo
|
||||
effect.magnMax = 10; /// \todo
|
||||
effect.duration = 60; /// \todo
|
||||
effects.list.push_back(effect);
|
||||
}
|
||||
}
|
||||
|
||||
// UESP Wiki / Morrowind:Alchemy
|
||||
// "The weight of a potion is an average of the weight of the ingredients, rounded down."
|
||||
// note by scrawl: not rounding down here, I can't imagine a created potion to
|
||||
// have 0 weight when using ingredients with 0.1 weight respectively
|
||||
float weight = 0;
|
||||
if (mIngredient1->isUserString("ToolTipType"))
|
||||
weight += mIngredient1->getUserData<MWWorld::Ptr>()->get<ESM::Ingredient>()->base->data.weight;
|
||||
if (mIngredient2->isUserString("ToolTipType"))
|
||||
weight += mIngredient2->getUserData<MWWorld::Ptr>()->get<ESM::Ingredient>()->base->data.weight;
|
||||
if (mIngredient3->isUserString("ToolTipType"))
|
||||
weight += mIngredient3->getUserData<MWWorld::Ptr>()->get<ESM::Ingredient>()->base->data.weight;
|
||||
if (mIngredient4->isUserString("ToolTipType"))
|
||||
weight += mIngredient4->getUserData<MWWorld::Ptr>()->get<ESM::Ingredient>()->base->data.weight;
|
||||
newPotion.data.weight = weight / float(numIngreds);
|
||||
|
||||
newPotion.data.value = 100; /// \todo
|
||||
newPotion.effects = effects;
|
||||
// pick a random mesh and icon
|
||||
std::vector<std::string> names;
|
||||
/// \todo is the mesh/icon dependent on alchemy skill?
|
||||
names.push_back("standard");
|
||||
names.push_back("bargain");
|
||||
names.push_back("cheap");
|
||||
names.push_back("fresh");
|
||||
names.push_back("exclusive");
|
||||
names.push_back("quality");
|
||||
int random = rand() % names.size();
|
||||
newPotion.model = "m\\misc_potion_" + names[random ] + "_01.nif";
|
||||
newPotion.icon = "m\\tx_potion_" + names[random ] + "_01.dds";
|
||||
|
||||
// check if a similiar potion record exists already
|
||||
bool found = false;
|
||||
std::string objectId;
|
||||
typedef std::map<std::string, ESM::Potion> PotionMap;
|
||||
PotionMap potions = MWBase::Environment::get().getWorld()->getStore().potions.list;
|
||||
for (PotionMap::const_iterator it = potions.begin(); it != potions.end(); ++it)
|
||||
{
|
||||
if (found) break;
|
||||
|
||||
if (it->second.data.value == newPotion.data.value
|
||||
&& it->second.data.weight == newPotion.data.weight
|
||||
&& it->second.name == newPotion.name
|
||||
&& it->second.effects.list.size() == newPotion.effects.list.size())
|
||||
{
|
||||
// check effects
|
||||
for (unsigned int i=0; i < it->second.effects.list.size(); ++i)
|
||||
{
|
||||
const ESM::ENAMstruct& a = it->second.effects.list[i];
|
||||
const ESM::ENAMstruct& b = newPotion.effects.list[i];
|
||||
if (a.effectID == b.effectID
|
||||
&& a.area == b.area
|
||||
&& a.range == b.range
|
||||
&& a.skill == b.skill
|
||||
&& a.attribute == b.attribute
|
||||
&& a.magnMin == b.magnMin
|
||||
&& a.magnMax == b.magnMax
|
||||
&& a.duration == b.duration)
|
||||
{
|
||||
found = true;
|
||||
objectId = it->first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
std::pair<std::string, const ESM::Potion*> result = MWBase::Environment::get().getWorld()->createRecord(newPotion);
|
||||
objectId = result.first;
|
||||
}
|
||||
|
||||
// create a reference and add it to player inventory
|
||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), objectId);
|
||||
MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr);
|
||||
ref.getPtr().getRefData().setCount(1);
|
||||
store.add(ref.getPtr());
|
||||
|
||||
mWindowManager.messageBox("#{sPotionSuccess}", std::vector<std::string>());
|
||||
MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// potion failed
|
||||
mWindowManager.messageBox("#{sNotifyMessage8}", std::vector<std::string>());
|
||||
MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f);
|
||||
}
|
||||
|
||||
// reduce count of the ingredients
|
||||
if (mIngredient1->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr ingred = *mIngredient1->getUserData<MWWorld::Ptr>();
|
||||
ingred.getRefData().setCount(ingred.getRefData().getCount()-1);
|
||||
if (ingred.getRefData().getCount() == 0)
|
||||
removeIngredient(mIngredient1);
|
||||
}
|
||||
if (mIngredient2->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr ingred = *mIngredient2->getUserData<MWWorld::Ptr>();
|
||||
ingred.getRefData().setCount(ingred.getRefData().getCount()-1);
|
||||
if (ingred.getRefData().getCount() == 0)
|
||||
removeIngredient(mIngredient2);
|
||||
}
|
||||
if (mIngredient3->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr ingred = *mIngredient3->getUserData<MWWorld::Ptr>();
|
||||
ingred.getRefData().setCount(ingred.getRefData().getCount()-1);
|
||||
if (ingred.getRefData().getCount() == 0)
|
||||
removeIngredient(mIngredient3);
|
||||
}
|
||||
if (mIngredient4->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr ingred = *mIngredient4->getUserData<MWWorld::Ptr>();
|
||||
ingred.getRefData().setCount(ingred.getRefData().getCount()-1);
|
||||
if (ingred.getRefData().getCount() == 0)
|
||||
removeIngredient(mIngredient4);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void AlchemyWindow::open()
|
||||
{
|
||||
openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
||||
setFilter(ContainerBase::Filter_Ingredients);
|
||||
|
||||
// pick the best available apparatus
|
||||
MWWorld::ContainerStore& store = MWWorld::Class::get(mPtr).getContainerStore(mPtr);
|
||||
|
||||
MWWorld::Ptr bestAlbemic;
|
||||
MWWorld::Ptr bestMortarPestle;
|
||||
MWWorld::Ptr bestCalcinator;
|
||||
MWWorld::Ptr bestRetort;
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it(store.begin(MWWorld::ContainerStore::Type_Apparatus));
|
||||
it != store.end(); ++it)
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Apparatus, MWWorld::RefData>* ref = it->get<ESM::Apparatus>();
|
||||
if (ref->base->data.type == ESM::Apparatus::Albemic
|
||||
&& (bestAlbemic.isEmpty() || ref->base->data.quality > bestAlbemic.get<ESM::Apparatus>()->base->data.quality))
|
||||
bestAlbemic = *it;
|
||||
else if (ref->base->data.type == ESM::Apparatus::MortarPestle
|
||||
&& (bestMortarPestle.isEmpty() || ref->base->data.quality > bestMortarPestle.get<ESM::Apparatus>()->base->data.quality))
|
||||
bestMortarPestle = *it;
|
||||
else if (ref->base->data.type == ESM::Apparatus::Calcinator
|
||||
&& (bestCalcinator.isEmpty() || ref->base->data.quality > bestCalcinator.get<ESM::Apparatus>()->base->data.quality))
|
||||
bestCalcinator = *it;
|
||||
else if (ref->base->data.type == ESM::Apparatus::Retort
|
||||
&& (bestRetort.isEmpty() || ref->base->data.quality > bestRetort.get<ESM::Apparatus>()->base->data.quality))
|
||||
bestRetort = *it;
|
||||
}
|
||||
|
||||
if (!bestMortarPestle.isEmpty())
|
||||
{
|
||||
mApparatus1->setUserString("ToolTipType", "ItemPtr");
|
||||
mApparatus1->setUserData(bestMortarPestle);
|
||||
mApparatus1->setImageTexture(getIconPath(bestMortarPestle));
|
||||
}
|
||||
if (!bestAlbemic.isEmpty())
|
||||
{
|
||||
mApparatus2->setUserString("ToolTipType", "ItemPtr");
|
||||
mApparatus2->setUserData(bestAlbemic);
|
||||
mApparatus2->setImageTexture(getIconPath(bestAlbemic));
|
||||
}
|
||||
if (!bestCalcinator.isEmpty())
|
||||
{
|
||||
mApparatus3->setUserString("ToolTipType", "ItemPtr");
|
||||
mApparatus3->setUserData(bestCalcinator);
|
||||
mApparatus3->setImageTexture(getIconPath(bestCalcinator));
|
||||
}
|
||||
if (!bestRetort.isEmpty())
|
||||
{
|
||||
mApparatus4->setUserString("ToolTipType", "ItemPtr");
|
||||
mApparatus4->setUserData(bestRetort);
|
||||
mApparatus4->setImageTexture(getIconPath(bestRetort));
|
||||
}
|
||||
}
|
||||
|
||||
void AlchemyWindow::onIngredientSelected(MyGUI::Widget* _sender)
|
||||
{
|
||||
removeIngredient(_sender);
|
||||
drawItems();
|
||||
update();
|
||||
}
|
||||
|
||||
void AlchemyWindow::onSelectedItemImpl(MWWorld::Ptr item)
|
||||
{
|
||||
MyGUI::ImageBox* add = NULL;
|
||||
|
||||
// don't allow to add an ingredient that is already added
|
||||
// (which could happen if two similiar ingredients don't stack because of script / owner)
|
||||
bool alreadyAdded = false;
|
||||
std::string name = MWWorld::Class::get(item).getName(item);
|
||||
if (mIngredient1->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr item2 = *mIngredient1->getUserData<MWWorld::Ptr>();
|
||||
std::string name2 = MWWorld::Class::get(item2).getName(item2);
|
||||
if (name == name2)
|
||||
alreadyAdded = true;
|
||||
}
|
||||
if (mIngredient2->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr item2 = *mIngredient2->getUserData<MWWorld::Ptr>();
|
||||
std::string name2 = MWWorld::Class::get(item2).getName(item2);
|
||||
if (name == name2)
|
||||
alreadyAdded = true;
|
||||
}
|
||||
if (mIngredient3->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr item2 = *mIngredient3->getUserData<MWWorld::Ptr>();
|
||||
std::string name2 = MWWorld::Class::get(item2).getName(item2);
|
||||
if (name == name2)
|
||||
alreadyAdded = true;
|
||||
}
|
||||
if (mIngredient4->isUserString("ToolTipType"))
|
||||
{
|
||||
MWWorld::Ptr item2 = *mIngredient4->getUserData<MWWorld::Ptr>();
|
||||
std::string name2 = MWWorld::Class::get(item2).getName(item2);
|
||||
if (name == name2)
|
||||
alreadyAdded = true;
|
||||
}
|
||||
if (alreadyAdded)
|
||||
return;
|
||||
|
||||
if (!mIngredient1->isUserString("ToolTipType"))
|
||||
add = mIngredient1;
|
||||
if (add == NULL && !mIngredient2->isUserString("ToolTipType"))
|
||||
add = mIngredient2;
|
||||
if (add == NULL && !mIngredient3->isUserString("ToolTipType"))
|
||||
add = mIngredient3;
|
||||
if (add == NULL && !mIngredient4->isUserString("ToolTipType"))
|
||||
add = mIngredient4;
|
||||
|
||||
if (add != NULL)
|
||||
{
|
||||
add->setUserString("ToolTipType", "ItemPtr");
|
||||
add->setUserData(item);
|
||||
add->setImageTexture(getIconPath(item));
|
||||
drawItems();
|
||||
update();
|
||||
|
||||
std::string sound = MWWorld::Class::get(item).getUpSoundId(item);
|
||||
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<MWWorld::Ptr> AlchemyWindow::itemsToIgnore()
|
||||
{
|
||||
std::vector<MWWorld::Ptr> ignore;
|
||||
// don't show ingredients that are currently selected in the "available ingredients" box.
|
||||
if (mIngredient1->isUserString("ToolTipType"))
|
||||
ignore.push_back(*mIngredient1->getUserData<MWWorld::Ptr>());
|
||||
if (mIngredient2->isUserString("ToolTipType"))
|
||||
ignore.push_back(*mIngredient2->getUserData<MWWorld::Ptr>());
|
||||
if (mIngredient3->isUserString("ToolTipType"))
|
||||
ignore.push_back(*mIngredient3->getUserData<MWWorld::Ptr>());
|
||||
if (mIngredient4->isUserString("ToolTipType"))
|
||||
ignore.push_back(*mIngredient4->getUserData<MWWorld::Ptr>());
|
||||
|
||||
return ignore;
|
||||
}
|
||||
|
||||
void AlchemyWindow::update()
|
||||
{
|
||||
Widgets::SpellEffectList effects;
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
MyGUI::ImageBox* ingredient;
|
||||
if (i==0)
|
||||
ingredient = mIngredient1;
|
||||
else if (i==1)
|
||||
ingredient = mIngredient2;
|
||||
else if (i==2)
|
||||
ingredient = mIngredient3;
|
||||
else if (i==3)
|
||||
ingredient = mIngredient4;
|
||||
|
||||
if (!ingredient->isUserString("ToolTipType"))
|
||||
continue;
|
||||
|
||||
// add the effects of this ingredient to list of effects
|
||||
ESMS::LiveCellRef<ESM::Ingredient, MWWorld::RefData>* ref = ingredient->getUserData<MWWorld::Ptr>()->get<ESM::Ingredient>();
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
if (ref->base->data.effectID[i] < 0)
|
||||
continue;
|
||||
MWGui::Widgets::SpellEffectParams params;
|
||||
params.mEffectID = ref->base->data.effectID[i];
|
||||
params.mAttribute = ref->base->data.attributes[i];
|
||||
params.mSkill = ref->base->data.skills[i];
|
||||
effects.push_back(params);
|
||||
}
|
||||
|
||||
// update ingredient count labels
|
||||
if (ingredient->getChildCount())
|
||||
MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0));
|
||||
|
||||
MyGUI::TextBox* text = ingredient->createWidget<MyGUI::TextBox>("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label"));
|
||||
text->setTextAlign(MyGUI::Align::Right);
|
||||
text->setNeedMouseFocus(false);
|
||||
text->setTextShadow(true);
|
||||
text->setTextShadowColour(MyGUI::Colour(0,0,0));
|
||||
text->setCaption(getCountString(ingredient->getUserData<MWWorld::Ptr>()->getRefData().getCount()));
|
||||
}
|
||||
|
||||
// now remove effects that are only present once
|
||||
Widgets::SpellEffectList::iterator it = effects.begin();
|
||||
while (it != effects.end())
|
||||
{
|
||||
Widgets::SpellEffectList::iterator next = it;
|
||||
++next;
|
||||
bool found = false;
|
||||
for (; next != effects.end(); ++next)
|
||||
{
|
||||
if (*next == *it)
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
it = effects.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// now remove duplicates, and don't allow more than 4 effects
|
||||
Widgets::SpellEffectList old = effects;
|
||||
effects.clear();
|
||||
int i=0;
|
||||
for (Widgets::SpellEffectList::iterator it = old.begin();
|
||||
it != old.end(); ++it)
|
||||
{
|
||||
bool found = false;
|
||||
for (Widgets::SpellEffectList::iterator it2 = effects.begin();
|
||||
it2 != effects.end(); ++it2)
|
||||
{
|
||||
// MW considers all "foritfy attribute" effects as the same effect. See the
|
||||
// "Can't create multi-state boost potions" discussion on http://www.uesp.net/wiki/Morrowind_talk:Alchemy
|
||||
// thus, we are only checking effectID here and not attribute or skill
|
||||
if (it2->mEffectID == it->mEffectID)
|
||||
found = true;
|
||||
}
|
||||
if (!found && i<4)
|
||||
{
|
||||
++i;
|
||||
effects.push_back(*it);
|
||||
}
|
||||
}
|
||||
mEffects = effects;
|
||||
|
||||
while (mEffectsBox->getChildCount())
|
||||
MyGUI::Gui::getInstance().destroyWidget(mEffectsBox->getChildAt(0));
|
||||
|
||||
MyGUI::IntCoord coord(0, 0, mEffectsBox->getWidth(), 24);
|
||||
Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget<Widgets::MWEffectList>
|
||||
("MW_StatName", coord, Align::Left | Align::Top);
|
||||
effectsWidget->setWindowManager(&mWindowManager);
|
||||
effectsWidget->setEffectList(effects);
|
||||
|
||||
std::vector<MyGUI::WidgetPtr> effectItems;
|
||||
effectsWidget->createEffectWidgets(effectItems, mEffectsBox, coord, false, 0);
|
||||
effectsWidget->setCoord(coord);
|
||||
}
|
||||
|
||||
void AlchemyWindow::removeIngredient(MyGUI::Widget* ingredient)
|
||||
{
|
||||
ingredient->clearUserStrings();
|
||||
static_cast<MyGUI::ImageBox*>(ingredient)->setImageTexture("");
|
||||
if (ingredient->getChildCount())
|
||||
MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0));
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
#ifndef MWGUI_ALCHEMY_H
|
||||
#define MWGUI_ALCHEMY_H
|
||||
|
||||
#include "window_base.hpp"
|
||||
#include "container.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class AlchemyWindow : public WindowBase, public ContainerBase
|
||||
{
|
||||
public:
|
||||
AlchemyWindow(WindowManager& parWindowManager);
|
||||
|
||||
virtual void open();
|
||||
|
||||
protected:
|
||||
MyGUI::Button* mCreateButton;
|
||||
MyGUI::Button* mCancelButton;
|
||||
|
||||
MyGUI::ImageBox* mIngredient1;
|
||||
MyGUI::ImageBox* mIngredient2;
|
||||
MyGUI::ImageBox* mIngredient3;
|
||||
MyGUI::ImageBox* mIngredient4;
|
||||
|
||||
MyGUI::ImageBox* mApparatus1;
|
||||
MyGUI::ImageBox* mApparatus2;
|
||||
MyGUI::ImageBox* mApparatus3;
|
||||
MyGUI::ImageBox* mApparatus4;
|
||||
|
||||
MyGUI::Widget* mEffectsBox;
|
||||
|
||||
MyGUI::EditBox* mNameEdit;
|
||||
|
||||
Widgets::SpellEffectList mEffects; // effects of created potion
|
||||
|
||||
void onCancelButtonClicked(MyGUI::Widget* _sender);
|
||||
void onCreateButtonClicked(MyGUI::Widget* _sender);
|
||||
void onIngredientSelected(MyGUI::Widget* _sender);
|
||||
|
||||
virtual void onSelectedItemImpl(MWWorld::Ptr item);
|
||||
virtual std::vector<MWWorld::Ptr> itemsToIgnore();
|
||||
|
||||
void removeIngredient(MyGUI::Widget* ingredient);
|
||||
|
||||
virtual void onReferenceUnavailable() { ; }
|
||||
|
||||
void update();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,70 @@
|
||||
#include "confirmationdialog.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
ConfirmationDialog::ConfirmationDialog(WindowManager& parWindowManager) :
|
||||
WindowBase("openmw_confirmation_dialog_layout.xml", parWindowManager)
|
||||
{
|
||||
getWidget(mMessage, "Message");
|
||||
getWidget(mOkButton, "OkButton");
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
|
||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onCancelButtonClicked);
|
||||
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ConfirmationDialog::onOkButtonClicked);
|
||||
}
|
||||
|
||||
void ConfirmationDialog::open(const std::string& message)
|
||||
{
|
||||
setVisible(true);
|
||||
|
||||
mMessage->setCaptionWithReplacing(message);
|
||||
|
||||
int height = mMessage->getTextSize().height + 72;
|
||||
|
||||
mMainWidget->setSize(mMainWidget->getWidth(), height);
|
||||
|
||||
mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height+24);
|
||||
|
||||
center();
|
||||
|
||||
// make other gui elements inaccessible while this dialog is open
|
||||
MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget);
|
||||
|
||||
int okButtonWidth = mOkButton->getTextSize().width + 24;
|
||||
mOkButton->setCoord(mMainWidget->getWidth() - 30 - okButtonWidth,
|
||||
mOkButton->getTop(),
|
||||
okButtonWidth,
|
||||
mOkButton->getHeight());
|
||||
|
||||
int cancelButtonWidth = mCancelButton->getTextSize().width + 24;
|
||||
mCancelButton->setCoord(mMainWidget->getWidth() - 30 - okButtonWidth - cancelButtonWidth - 8,
|
||||
mCancelButton->getTop(),
|
||||
cancelButtonWidth,
|
||||
mCancelButton->getHeight());
|
||||
}
|
||||
|
||||
void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventCancelClicked();
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventOkClicked();
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
void ConfirmationDialog::close()
|
||||
{
|
||||
setVisible(false);
|
||||
MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef MWGUI_CONFIRMATIONDIALOG_H
|
||||
#define MWGUI_CONFIRMATIONDIALOG_H
|
||||
|
||||
#include "window_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class ConfirmationDialog : public WindowBase
|
||||
{
|
||||
public:
|
||||
ConfirmationDialog(WindowManager& parWindowManager);
|
||||
void open(const std::string& message);
|
||||
|
||||
typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void;
|
||||
|
||||
/** Event : Ok button was clicked.\n
|
||||
signature : void method()\n
|
||||
*/
|
||||
EventHandle_Void eventOkClicked;
|
||||
EventHandle_Void eventCancelClicked;
|
||||
|
||||
private:
|
||||
MyGUI::EditBox* mMessage;
|
||||
MyGUI::Button* mOkButton;
|
||||
MyGUI::Button* mCancelButton;
|
||||
|
||||
void onCancelButtonClicked(MyGUI::Widget* _sender);
|
||||
void onOkButtonClicked(MyGUI::Widget* _sender);
|
||||
|
||||
void close();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,28 @@
|
||||
#include "referenceinterface.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
ReferenceInterface::ReferenceInterface()
|
||||
: mCurrentPlayerCell(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void ReferenceInterface::checkReferenceAvailable()
|
||||
{
|
||||
if (mPtr.isEmpty())
|
||||
return;
|
||||
|
||||
MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell();
|
||||
|
||||
// check if player has changed cell, or count of the reference has become 0
|
||||
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL)
|
||||
|| mPtr.getRefData().getCount() == 0)
|
||||
onReferenceUnavailable();
|
||||
|
||||
mCurrentPlayerCell = playerCell;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef MWGUI_REFERENCEINTERFACE_H
|
||||
#define MWGUI_REFERENCEINTERFACE_H
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
/// \brief this class is intended for GUI interfaces that access an MW-Reference
|
||||
/// for example dialogue window accesses an NPC, or Container window accesses a Container
|
||||
/// these classes have to be automatically closed if the reference becomes unavailable
|
||||
/// make sure that checkReferenceAvailable() is called every frame and that onReferenceUnavailable() has been overridden
|
||||
class ReferenceInterface
|
||||
{
|
||||
public:
|
||||
ReferenceInterface();
|
||||
|
||||
void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable
|
||||
|
||||
protected:
|
||||
virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable
|
||||
|
||||
MWWorld::Ptr mPtr;
|
||||
|
||||
private:
|
||||
MWWorld::Ptr::CellStore* mCurrentPlayerCell;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,342 @@
|
||||
#include "settingswindow.hpp"
|
||||
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreRenderSystem.h>
|
||||
#include <OgreString.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "../mwsound/soundmanager.hpp"
|
||||
#include "../mwinput/inputmanager.hpp"
|
||||
|
||||
#include "window_manager.hpp"
|
||||
#include "confirmationdialog.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string fpsLevelToStr(int level)
|
||||
{
|
||||
if (level == 0)
|
||||
return "#{sOff}";
|
||||
else if (level == 1)
|
||||
return "Basic";
|
||||
else
|
||||
return "Detailed";
|
||||
}
|
||||
|
||||
std::string textureFilteringToStr(const std::string& val)
|
||||
{
|
||||
if (val == "none")
|
||||
return "None";
|
||||
else if (val == "anisotropic")
|
||||
return "Anisotropic";
|
||||
else if (val == "bilinear")
|
||||
return "Bilinear";
|
||||
else
|
||||
return "Trilinear";
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
SettingsWindow::SettingsWindow(WindowManager& parWindowManager) :
|
||||
WindowBase("openmw_settings_window_layout.xml", parWindowManager)
|
||||
{
|
||||
getWidget(mOkButton, "OkButton");
|
||||
getWidget(mResolutionList, "ResolutionList");
|
||||
getWidget(mMenuTransparencySlider, "MenuTransparencySlider");
|
||||
getWidget(mToolTipDelaySlider, "ToolTipDelaySlider");
|
||||
getWidget(mViewDistanceSlider, "ViewDistanceSlider");
|
||||
getWidget(mFullscreenButton, "FullscreenButton");
|
||||
getWidget(mVSyncButton, "VSyncButton");
|
||||
getWidget(mFPSButton, "FPSButton");
|
||||
getWidget(mFOVSlider, "FOVSlider");
|
||||
getWidget(mMasterVolumeSlider, "MasterVolume");
|
||||
getWidget(mVoiceVolumeSlider, "VoiceVolume");
|
||||
getWidget(mEffectsVolumeSlider, "EffectsVolume");
|
||||
getWidget(mFootstepsVolumeSlider, "FootstepsVolume");
|
||||
getWidget(mMusicVolumeSlider, "MusicVolume");
|
||||
getWidget(mAnisotropySlider, "AnisotropySlider");
|
||||
getWidget(mTextureFilteringButton, "TextureFilteringButton");
|
||||
getWidget(mAnisotropyLabel, "AnisotropyLabel");
|
||||
getWidget(mAnisotropyBox, "AnisotropyBox");
|
||||
getWidget(mWaterShaderButton, "WaterShaderButton");
|
||||
getWidget(mReflectObjectsButton, "ReflectObjectsButton");
|
||||
getWidget(mReflectActorsButton, "ReflectActorsButton");
|
||||
getWidget(mReflectTerrainButton, "ReflectTerrainButton");
|
||||
|
||||
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked);
|
||||
mFullscreenButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mWaterShaderButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mReflectObjectsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mReflectTerrainButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mReflectActorsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mTextureFilteringButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onTextureFilteringToggled);
|
||||
mVSyncButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
mFPSButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onFpsToggled);
|
||||
mMenuTransparencySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mFOVSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mToolTipDelaySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mViewDistanceSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mResolutionList->eventListChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onResolutionSelected);
|
||||
mAnisotropySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
|
||||
mMasterVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mVoiceVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mEffectsVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mFootstepsVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
mMusicVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
|
||||
center();
|
||||
|
||||
int okSize = mOkButton->getTextSize().width + 24;
|
||||
mOkButton->setCoord(mMainWidget->getWidth()-16-okSize, mOkButton->getTop(),
|
||||
okSize, mOkButton->getHeight());
|
||||
|
||||
// fill resolution list
|
||||
Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem();
|
||||
const Ogre::StringVector& videoModes = rs->getConfigOptions()["Video Mode"].possibleValues;
|
||||
for (Ogre::StringVector::const_iterator it=videoModes.begin();
|
||||
it!=videoModes.end(); ++it)
|
||||
{
|
||||
mResolutionList->addItem(*it);
|
||||
}
|
||||
|
||||
// read settings
|
||||
int menu_transparency = (mMenuTransparencySlider->getScrollRange()-1) * Settings::Manager::getFloat("menu transparency", "GUI");
|
||||
mMenuTransparencySlider->setScrollPosition(menu_transparency);
|
||||
int tooltip_delay = (mToolTipDelaySlider->getScrollRange()-1) * Settings::Manager::getFloat("tooltip delay", "GUI");
|
||||
mToolTipDelaySlider->setScrollPosition(tooltip_delay);
|
||||
|
||||
float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin);
|
||||
mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1));
|
||||
MyGUI::TextBox* fovText;
|
||||
getWidget(fovText, "FovText");
|
||||
fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int(Settings::Manager::getFloat("field of view", "General"))) + ")");
|
||||
|
||||
float anisotropyVal = Settings::Manager::getInt("anisotropy", "General") / 16.0;
|
||||
mAnisotropySlider->setScrollPosition(anisotropyVal * (mAnisotropySlider->getScrollRange()-1));
|
||||
std::string tf = Settings::Manager::getString("texture filtering", "General");
|
||||
mTextureFilteringButton->setCaption(textureFilteringToStr(tf));
|
||||
mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast<std::string>(Settings::Manager::getInt("anisotropy", "General")) + ")");
|
||||
mAnisotropyBox->setVisible(tf == "anisotropic");
|
||||
|
||||
float val = (Settings::Manager::getFloat("max viewing distance", "Viewing distance")-sViewDistMin)/(sViewDistMax-sViewDistMin);
|
||||
int viewdist = (mViewDistanceSlider->getScrollRange()-1) * val;
|
||||
mViewDistanceSlider->setScrollPosition(viewdist);
|
||||
|
||||
mMasterVolumeSlider->setScrollPosition(Settings::Manager::getFloat("master volume", "Sound") * (mMasterVolumeSlider->getScrollRange()-1));
|
||||
mMusicVolumeSlider->setScrollPosition(Settings::Manager::getFloat("music volume", "Sound") * (mMusicVolumeSlider->getScrollRange()-1));
|
||||
mEffectsVolumeSlider->setScrollPosition(Settings::Manager::getFloat("sfx volume", "Sound") * (mEffectsVolumeSlider->getScrollRange()-1));
|
||||
mFootstepsVolumeSlider->setScrollPosition(Settings::Manager::getFloat("footsteps volume", "Sound") * (mFootstepsVolumeSlider->getScrollRange()-1));
|
||||
mVoiceVolumeSlider->setScrollPosition(Settings::Manager::getFloat("voice volume", "Sound") * (mVoiceVolumeSlider->getScrollRange()-1));
|
||||
|
||||
mWaterShaderButton->setCaptionWithReplacing(Settings::Manager::getBool("shader", "Water") ? "#{sOn}" : "#{sOff}");
|
||||
mReflectObjectsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect objects", "Water") ? "#{sOn}" : "#{sOff}");
|
||||
mReflectActorsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect actors", "Water") ? "#{sOn}" : "#{sOff}");
|
||||
mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}");
|
||||
|
||||
if (!MWRender::RenderingManager::waterShaderSupported())
|
||||
{
|
||||
mWaterShaderButton->setEnabled(false);
|
||||
mReflectObjectsButton->setEnabled(false);
|
||||
mReflectActorsButton->setEnabled(false);
|
||||
mReflectTerrainButton->setEnabled(false);
|
||||
}
|
||||
|
||||
mFullscreenButton->setCaptionWithReplacing(Settings::Manager::getBool("fullscreen", "Video") ? "#{sOn}" : "#{sOff}");
|
||||
mVSyncButton->setCaptionWithReplacing(Settings::Manager::getBool("vsync", "Video") ? "#{sOn}": "#{sOff}");
|
||||
mFPSButton->setCaptionWithReplacing(fpsLevelToStr(Settings::Manager::getInt("fps", "HUD")));
|
||||
}
|
||||
|
||||
void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mWindowManager.popGuiMode();
|
||||
}
|
||||
|
||||
void SettingsWindow::onResolutionSelected(MyGUI::ListBox* _sender, size_t index)
|
||||
{
|
||||
if (index == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
|
||||
ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog();
|
||||
dialog->open("#{sNotifyMessage67}");
|
||||
dialog->eventOkClicked.clear();
|
||||
dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept);
|
||||
dialog->eventCancelClicked.clear();
|
||||
dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept);
|
||||
}
|
||||
|
||||
void SettingsWindow::onResolutionAccept()
|
||||
{
|
||||
std::string resStr = mResolutionList->getItemNameAt(mResolutionList->getIndexSelected());
|
||||
size_t xPos = resStr.find("x");
|
||||
std::string resXStr = resStr.substr(0, xPos-1);
|
||||
Ogre::StringUtil::trim(resXStr);
|
||||
std::string resYStr = resStr.substr(xPos+2, resStr.size()-(xPos+2));
|
||||
Ogre::StringUtil::trim(resYStr);
|
||||
int resX = boost::lexical_cast<int>(resXStr);
|
||||
int resY = boost::lexical_cast<int>(resYStr);
|
||||
|
||||
Settings::Manager::setInt("resolution x", "Video", resX);
|
||||
Settings::Manager::setInt("resolution y", "Video", resY);
|
||||
|
||||
apply();
|
||||
mResolutionList->setIndexSelected(MyGUI::ITEM_NONE);
|
||||
}
|
||||
|
||||
void SettingsWindow::onResolutionCancel()
|
||||
{
|
||||
mResolutionList->setIndexSelected(MyGUI::ITEM_NONE);
|
||||
}
|
||||
|
||||
void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender)
|
||||
{
|
||||
std::string on = mWindowManager.getGameSettingString("sOn", "On");
|
||||
std::string off = mWindowManager.getGameSettingString("sOff", "On");
|
||||
bool newState;
|
||||
if (_sender->castType<MyGUI::Button>()->getCaption() == on)
|
||||
{
|
||||
_sender->castType<MyGUI::Button>()->setCaption(off);
|
||||
newState = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sender->castType<MyGUI::Button>()->setCaption(on);
|
||||
newState = true;
|
||||
}
|
||||
|
||||
if (_sender == mFullscreenButton)
|
||||
{
|
||||
// check if this resolution is supported in fullscreen
|
||||
bool supported = false;
|
||||
for (unsigned int i=0; i<mResolutionList->getItemCount(); ++i)
|
||||
{
|
||||
std::string resStr = mResolutionList->getItemNameAt(i);
|
||||
size_t xPos = resStr.find("x");
|
||||
std::string resXStr = resStr.substr(0, xPos-1);
|
||||
Ogre::StringUtil::trim(resXStr);
|
||||
std::string resYStr = resStr.substr(xPos+2, resStr.size()-(xPos+2));
|
||||
Ogre::StringUtil::trim(resYStr);
|
||||
int resX = boost::lexical_cast<int>(resXStr);
|
||||
int resY = boost::lexical_cast<int>(resYStr);
|
||||
|
||||
if (resX == Settings::Manager::getInt("resolution x", "Video")
|
||||
&& resY == Settings::Manager::getInt("resolution y", "Video"))
|
||||
supported = true;
|
||||
}
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
std::string msg = "This resolution is not supported in Fullscreen mode. Please select a resolution from the list.";
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
messageBox(msg, std::vector<std::string>());
|
||||
_sender->castType<MyGUI::Button>()->setCaption(off);
|
||||
}
|
||||
else
|
||||
{
|
||||
Settings::Manager::setBool("fullscreen", "Video", newState);
|
||||
apply();
|
||||
}
|
||||
}
|
||||
else if (_sender == mVSyncButton)
|
||||
{
|
||||
Settings::Manager::setBool("vsync", "Video", newState);
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
messageBox("VSync will be applied after a restart", std::vector<std::string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_sender == mWaterShaderButton)
|
||||
Settings::Manager::setBool("shader", "Water", newState);
|
||||
else if (_sender == mReflectObjectsButton)
|
||||
{
|
||||
Settings::Manager::setBool("reflect misc", "Water", newState);
|
||||
Settings::Manager::setBool("reflect statics", "Water", newState);
|
||||
Settings::Manager::setBool("reflect statics small", "Water", newState);
|
||||
}
|
||||
else if (_sender == mReflectActorsButton)
|
||||
Settings::Manager::setBool("reflect actors", "Water", newState);
|
||||
else if (_sender == mReflectTerrainButton)
|
||||
Settings::Manager::setBool("reflect terrain", "Water", newState);
|
||||
|
||||
apply();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWindow::onFpsToggled(MyGUI::Widget* _sender)
|
||||
{
|
||||
int newLevel = (Settings::Manager::getInt("fps", "HUD") + 1) % 3;
|
||||
Settings::Manager::setInt("fps", "HUD", newLevel);
|
||||
mFPSButton->setCaptionWithReplacing(fpsLevelToStr(newLevel));
|
||||
apply();
|
||||
}
|
||||
|
||||
void SettingsWindow::onTextureFilteringToggled(MyGUI::Widget* _sender)
|
||||
{
|
||||
std::string current = Settings::Manager::getString("texture filtering", "General");
|
||||
std::string next;
|
||||
if (current == "none")
|
||||
next = "bilinear";
|
||||
else if (current == "bilinear")
|
||||
next = "trilinear";
|
||||
else if (current == "trilinear")
|
||||
next = "anisotropic";
|
||||
else
|
||||
next = "none";
|
||||
|
||||
mTextureFilteringButton->setCaption(textureFilteringToStr(next));
|
||||
mAnisotropyBox->setVisible(next == "anisotropic");
|
||||
|
||||
Settings::Manager::setString("texture filtering", "General", next);
|
||||
apply();
|
||||
}
|
||||
|
||||
void SettingsWindow::onSliderChangePosition(MyGUI::ScrollBar* scroller, size_t pos)
|
||||
{
|
||||
float val = pos / float(scroller->getScrollRange()-1);
|
||||
if (scroller == mMenuTransparencySlider)
|
||||
Settings::Manager::setFloat("menu transparency", "GUI", val);
|
||||
else if (scroller == mToolTipDelaySlider)
|
||||
Settings::Manager::setFloat("tooltip delay", "GUI", val);
|
||||
else if (scroller == mViewDistanceSlider)
|
||||
Settings::Manager::setFloat("max viewing distance", "Viewing distance", (1-val) * sViewDistMin + val * sViewDistMax);
|
||||
else if (scroller == mFOVSlider)
|
||||
{
|
||||
MyGUI::TextBox* fovText;
|
||||
getWidget(fovText, "FovText");
|
||||
fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int((1-val) * sFovMin + val * sFovMax)) + ")");
|
||||
Settings::Manager::setFloat("field of view", "General", (1-val) * sFovMin + val * sFovMax);
|
||||
}
|
||||
else if (scroller == mAnisotropySlider)
|
||||
{
|
||||
mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast<std::string>(int(val*16)) + ")");
|
||||
Settings::Manager::setInt("anisotropy", "General", val * 16);
|
||||
}
|
||||
else if (scroller == mMasterVolumeSlider)
|
||||
Settings::Manager::setFloat("master volume", "Sound", val);
|
||||
else if (scroller == mVoiceVolumeSlider)
|
||||
Settings::Manager::setFloat("voice volume", "Sound", val);
|
||||
else if (scroller == mEffectsVolumeSlider)
|
||||
Settings::Manager::setFloat("sfx volume", "Sound", val);
|
||||
else if (scroller == mFootstepsVolumeSlider)
|
||||
Settings::Manager::setFloat("footsteps volume", "Sound", val);
|
||||
else if (scroller == mMusicVolumeSlider)
|
||||
Settings::Manager::setFloat("music volume", "Sound", val);
|
||||
|
||||
apply();
|
||||
}
|
||||
|
||||
void SettingsWindow::apply()
|
||||
{
|
||||
const Settings::CategorySettingVector changed = Settings::Manager::apply();
|
||||
MWBase::Environment::get().getWorld()->processChangedSettings(changed);
|
||||
MWBase::Environment::get().getSoundManager()->processChangedSettings(changed);
|
||||
MWBase::Environment::get().getWindowManager()->processChangedSettings(changed);
|
||||
MWBase::Environment::get().getInputManager()->processChangedSettings(changed);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
#ifndef MWGUI_SETTINGS_H
|
||||
#define MWGUI_SETTINGS_H
|
||||
|
||||
#include "window_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class WindowManager;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class SettingsWindow : public WindowBase
|
||||
{
|
||||
public:
|
||||
SettingsWindow(WindowManager& parWindowManager);
|
||||
|
||||
private:
|
||||
static int const sFovMin = 30;
|
||||
static int const sFovMax = 140;
|
||||
static int const sViewDistMin = 2000;
|
||||
static int const sViewDistMax = 5600;
|
||||
|
||||
protected:
|
||||
MyGUI::Button* mOkButton;
|
||||
|
||||
MyGUI::ScrollBar* mMenuTransparencySlider;
|
||||
MyGUI::ScrollBar* mToolTipDelaySlider;
|
||||
|
||||
// graphics
|
||||
MyGUI::ListBox* mResolutionList;
|
||||
MyGUI::Button* mFullscreenButton;
|
||||
MyGUI::Button* mVSyncButton;
|
||||
MyGUI::Button* mFPSButton;
|
||||
MyGUI::ScrollBar* mViewDistanceSlider;
|
||||
MyGUI::ScrollBar* mFOVSlider;
|
||||
MyGUI::ScrollBar* mAnisotropySlider;
|
||||
MyGUI::Button* mTextureFilteringButton;
|
||||
MyGUI::TextBox* mAnisotropyLabel;
|
||||
MyGUI::Widget* mAnisotropyBox;
|
||||
MyGUI::Button* mWaterShaderButton;
|
||||
MyGUI::Button* mReflectObjectsButton;
|
||||
MyGUI::Button* mReflectActorsButton;
|
||||
MyGUI::Button* mReflectTerrainButton;
|
||||
|
||||
// audio
|
||||
MyGUI::ScrollBar* mMasterVolumeSlider;
|
||||
MyGUI::ScrollBar* mVoiceVolumeSlider;
|
||||
MyGUI::ScrollBar* mEffectsVolumeSlider;
|
||||
MyGUI::ScrollBar* mFootstepsVolumeSlider;
|
||||
MyGUI::ScrollBar* mMusicVolumeSlider;
|
||||
|
||||
void onOkButtonClicked(MyGUI::Widget* _sender);
|
||||
void onFpsToggled(MyGUI::Widget* _sender);
|
||||
void onTextureFilteringToggled(MyGUI::Widget* _sender);
|
||||
void onSliderChangePosition(MyGUI::ScrollBar* scroller, size_t pos);
|
||||
void onButtonToggled(MyGUI::Widget* _sender);
|
||||
void onResolutionSelected(MyGUI::ListBox* _sender, size_t index);
|
||||
void onResolutionAccept();
|
||||
void onResolutionCancel();
|
||||
|
||||
void apply();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,459 @@
|
||||
#include "spellwindow.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwmechanics/spells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/spellsuccess.hpp"
|
||||
#include "../mwsound/soundmanager.hpp"
|
||||
|
||||
#include "window_manager.hpp"
|
||||
#include "inventorywindow.hpp"
|
||||
#include "confirmationdialog.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool sortSpells(const std::string& left, const std::string& right)
|
||||
{
|
||||
const ESM::Spell* a = MWBase::Environment::get().getWorld()->getStore().spells.find(left);
|
||||
const ESM::Spell* b = MWBase::Environment::get().getWorld()->getStore().spells.find(right);
|
||||
|
||||
int cmp = a->name.compare(b->name);
|
||||
return cmp < 0;
|
||||
}
|
||||
|
||||
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
|
||||
{
|
||||
int cmp = MWWorld::Class::get(left).getName(left).compare(
|
||||
MWWorld::Class::get(right).getName(right));
|
||||
return cmp < 0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
SpellWindow::SpellWindow(WindowManager& parWindowManager)
|
||||
: WindowPinnableBase("openmw_spell_window_layout.xml", parWindowManager)
|
||||
, mHeight(0)
|
||||
, mWidth(0)
|
||||
{
|
||||
getWidget(mSpellView, "SpellView");
|
||||
getWidget(mEffectBox, "EffectsBox");
|
||||
|
||||
setCoord(498, 300, 302, 300);
|
||||
|
||||
updateSpells();
|
||||
|
||||
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SpellWindow::onWindowResize);
|
||||
}
|
||||
|
||||
void SpellWindow::onPinToggled()
|
||||
{
|
||||
mWindowManager.setSpellVisibility(!mPinned);
|
||||
}
|
||||
|
||||
void SpellWindow::open()
|
||||
{
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
void SpellWindow::updateSpells()
|
||||
{
|
||||
const int spellHeight = 18;
|
||||
|
||||
mHeight = 0;
|
||||
while (mSpellView->getChildCount())
|
||||
MyGUI::Gui::getInstance().destroyWidget(mSpellView->getChildAt(0));
|
||||
|
||||
// retrieve all player spells, divide them into Powers and Spells and sort them
|
||||
std::vector<std::string> spellList;
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
MWMechanics::Spells& spells = stats.mSpells;
|
||||
|
||||
// the following code switches between selected enchanted item and selected spell (only one of these
|
||||
// can be active at a time)
|
||||
std::string selectedSpell = spells.getSelectedSpell();
|
||||
MWWorld::Ptr selectedItem;
|
||||
if (store.getSelectedEnchantItem() != store.end())
|
||||
{
|
||||
selectedSpell = "";
|
||||
selectedItem = *store.getSelectedEnchantItem();
|
||||
|
||||
bool allowSelectedItem = true;
|
||||
|
||||
// make sure that the item is still in the player inventory, otherwise it can't be selected
|
||||
bool found = false;
|
||||
for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it)
|
||||
{
|
||||
if (*it == selectedItem)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
allowSelectedItem = false;
|
||||
|
||||
// if the selected item can be equipped, make sure that it actually is equipped
|
||||
std::pair<std::vector<int>, bool> slots;
|
||||
slots = MWWorld::Class::get(selectedItem).getEquipmentSlots(selectedItem);
|
||||
if (!slots.first.empty())
|
||||
{
|
||||
bool equipped = false;
|
||||
for (int i=0; i < MWWorld::InventoryStore::Slots; ++i)
|
||||
{
|
||||
if (store.getSlot(i) != store.end() && *store.getSlot(i) == selectedItem)
|
||||
{
|
||||
equipped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!equipped)
|
||||
allowSelectedItem = false;
|
||||
}
|
||||
|
||||
if (!allowSelectedItem)
|
||||
{
|
||||
store.setSelectedEnchantItem(store.end());
|
||||
spells.setSelectedSpell("");
|
||||
mWindowManager.unsetSelectedSpell();
|
||||
selectedItem = MWWorld::Ptr();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
spellList.push_back(*it);
|
||||
}
|
||||
|
||||
std::vector<std::string> powers;
|
||||
std::vector<std::string>::iterator it = spellList.begin();
|
||||
while (it != spellList.end())
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it);
|
||||
if (spell->data.type == ESM::Spell::ST_Power)
|
||||
{
|
||||
powers.push_back(*it);
|
||||
it = spellList.erase(it);
|
||||
}
|
||||
else if (spell->data.type == ESM::Spell::ST_Ability)
|
||||
{
|
||||
// abilities are always active and don't show in the spell window.
|
||||
it = spellList.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
std::sort(powers.begin(), powers.end(), sortSpells);
|
||||
std::sort(spellList.begin(), spellList.end(), sortSpells);
|
||||
|
||||
// retrieve player's enchanted items
|
||||
std::vector<MWWorld::Ptr> items;
|
||||
for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it)
|
||||
{
|
||||
std::string enchantId = MWWorld::Class::get(*it).getEnchantment(*it);
|
||||
if (enchantId != "")
|
||||
{
|
||||
// only add items with "Cast once" or "Cast on use"
|
||||
const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().enchants.find(enchantId);
|
||||
int type = enchant->data.type;
|
||||
if (type != ESM::Enchantment::CastOnce
|
||||
&& type != ESM::Enchantment::WhenUsed)
|
||||
continue;
|
||||
|
||||
items.push_back(*it);
|
||||
}
|
||||
}
|
||||
std::sort(items.begin(), items.end(), sortItems);
|
||||
|
||||
|
||||
int height = estimateHeight(items.size() + powers.size() + spellList.size());
|
||||
bool scrollVisible = height > mSpellView->getHeight();
|
||||
mWidth = mSpellView->getWidth() - (scrollVisible ? 18 : 0);
|
||||
|
||||
// powers
|
||||
addGroup("#{sPowers}", "");
|
||||
|
||||
for (std::vector<std::string>::const_iterator it = powers.begin(); it != powers.end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it);
|
||||
MyGUI::Button* t = mSpellView->createWidget<MyGUI::Button>("SpellText",
|
||||
MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
t->setCaption(spell->name);
|
||||
t->setTextAlign(MyGUI::Align::Left);
|
||||
t->setUserString("ToolTipType", "Spell");
|
||||
t->setUserString("Spell", *it);
|
||||
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
|
||||
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected);
|
||||
|
||||
if (*it == selectedSpell)
|
||||
t->setStateSelected(true);
|
||||
|
||||
mHeight += spellHeight;
|
||||
}
|
||||
|
||||
// other spells
|
||||
addGroup("#{sSpells}", "#{sCostChance}");
|
||||
for (std::vector<std::string>::const_iterator it = spellList.begin(); it != spellList.end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it);
|
||||
MyGUI::Button* t = mSpellView->createWidget<MyGUI::Button>("SpellText",
|
||||
MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
t->setCaption(spell->name);
|
||||
t->setTextAlign(MyGUI::Align::Left);
|
||||
t->setUserString("ToolTipType", "Spell");
|
||||
t->setUserString("Spell", *it);
|
||||
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
|
||||
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected);
|
||||
t->setStateSelected(*it == selectedSpell);
|
||||
|
||||
// cost / success chance
|
||||
MyGUI::Button* costChance = mSpellView->createWidget<MyGUI::Button>("SpellText",
|
||||
MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
std::string cost = boost::lexical_cast<std::string>(spell->data.cost);
|
||||
std::string chance = boost::lexical_cast<std::string>(int(MWMechanics::getSpellSuccessChance(*it, player)));
|
||||
costChance->setCaption(cost + "/" + chance);
|
||||
costChance->setTextAlign(MyGUI::Align::Right);
|
||||
costChance->setNeedMouseFocus(false);
|
||||
costChance->setStateSelected(*it == selectedSpell);
|
||||
|
||||
|
||||
mHeight += spellHeight;
|
||||
}
|
||||
|
||||
|
||||
// enchanted items
|
||||
addGroup("#{sMagicItem}", "#{sCostCharge}");
|
||||
|
||||
for (std::vector<MWWorld::Ptr>::const_iterator it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
MWWorld::Ptr item = *it;
|
||||
|
||||
const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().enchants.find(MWWorld::Class::get(item).getEnchantment(item));
|
||||
|
||||
// check if the item is currently equipped (will display in a different color)
|
||||
bool equipped = false;
|
||||
for (int i=0; i < MWWorld::InventoryStore::Slots; ++i)
|
||||
{
|
||||
if (store.getSlot(i) != store.end() && *store.getSlot(i) == item)
|
||||
{
|
||||
equipped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MyGUI::Button* t = mSpellView->createWidget<MyGUI::Button>(equipped ? "SpellText" : "SpellTextUnequipped",
|
||||
MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
t->setCaption(MWWorld::Class::get(item).getName(item));
|
||||
t->setTextAlign(MyGUI::Align::Left);
|
||||
t->setUserData(item);
|
||||
t->setUserString("ToolTipType", "ItemPtr");
|
||||
t->setUserString("Equipped", equipped ? "true" : "false");
|
||||
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected);
|
||||
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
|
||||
t->setStateSelected(item == selectedItem);
|
||||
|
||||
// cost / charge
|
||||
MyGUI::Button* costCharge = mSpellView->createWidget<MyGUI::Button>(equipped ? "SpellText" : "SpellTextUnequipped",
|
||||
MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
|
||||
std::string cost = boost::lexical_cast<std::string>(enchant->data.cost);
|
||||
std::string charge = boost::lexical_cast<std::string>(enchant->data.charge); /// \todo track current charge
|
||||
if (enchant->data.type == ESM::Enchantment::CastOnce)
|
||||
{
|
||||
// this is Morrowind behaviour
|
||||
cost = "100";
|
||||
charge = "100";
|
||||
}
|
||||
|
||||
costCharge->setCaption(cost + "/" + charge);
|
||||
costCharge->setTextAlign(MyGUI::Align::Right);
|
||||
costCharge->setNeedMouseFocus(false);
|
||||
costCharge->setStateSelected(item == selectedItem);
|
||||
|
||||
mHeight += spellHeight;
|
||||
}
|
||||
|
||||
mSpellView->setCanvasSize(mSpellView->getWidth(), std::max(mSpellView->getHeight(), mHeight));
|
||||
}
|
||||
|
||||
void SpellWindow::addGroup(const std::string &label, const std::string& label2)
|
||||
{
|
||||
if (mSpellView->getChildCount() > 0)
|
||||
{
|
||||
MyGUI::ImageBox* separator = mSpellView->createWidget<MyGUI::ImageBox>("MW_HLine",
|
||||
MyGUI::IntCoord(4, mHeight, mWidth-8, 18),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
separator->setNeedMouseFocus(false);
|
||||
mHeight += 18;
|
||||
}
|
||||
|
||||
MyGUI::TextBox* groupWidget = mSpellView->createWidget<MyGUI::TextBox>("SandBrightText",
|
||||
MyGUI::IntCoord(0, mHeight, mWidth, 24),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);
|
||||
groupWidget->setCaptionWithReplacing(label);
|
||||
groupWidget->setTextAlign(MyGUI::Align::Left);
|
||||
groupWidget->setNeedMouseFocus(false);
|
||||
|
||||
if (label2 != "")
|
||||
{
|
||||
MyGUI::TextBox* groupWidget2 = mSpellView->createWidget<MyGUI::TextBox>("SandBrightText",
|
||||
MyGUI::IntCoord(0, mHeight, mWidth-4, 24),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
groupWidget2->setCaptionWithReplacing(label2);
|
||||
groupWidget2->setTextAlign(MyGUI::Align::Right);
|
||||
groupWidget2->setNeedMouseFocus(false);
|
||||
}
|
||||
|
||||
mHeight += 24;
|
||||
}
|
||||
|
||||
void SpellWindow::onWindowResize(MyGUI::Window* _sender)
|
||||
{
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender)
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
|
||||
MWMechanics::Spells& spells = stats.mSpells;
|
||||
MWWorld::Ptr item = *_sender->getUserData<MWWorld::Ptr>();
|
||||
|
||||
// retrieve ContainerStoreIterator to the item
|
||||
MWWorld::ContainerStoreIterator it = store.begin();
|
||||
for (; it != store.end(); ++it)
|
||||
{
|
||||
if (*it == item)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(it != store.end());
|
||||
|
||||
// equip, if it can be equipped and is not already equipped
|
||||
if (_sender->getUserString("Equipped") == "false"
|
||||
&& !MWWorld::Class::get(item).getEquipmentSlots(item).first.empty())
|
||||
{
|
||||
// sound
|
||||
MWBase::Environment::get().getSoundManager()->playSound(MWWorld::Class::get(item).getUpSoundId(item), 1.0, 1.0);
|
||||
|
||||
// Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping
|
||||
|
||||
/// \todo the following code is pretty much copy&paste from ActionEquip, put it in a function?
|
||||
// slots that this item can be equipped in
|
||||
std::pair<std::vector<int>, bool> slots = MWWorld::Class::get(item).getEquipmentSlots(item);
|
||||
|
||||
// equip the item in the first free slot
|
||||
for (std::vector<int>::const_iterator slot=slots.first.begin();
|
||||
slot!=slots.first.end(); ++slot)
|
||||
{
|
||||
// if all slots are occupied, replace the last slot
|
||||
if (slot == --slots.first.end())
|
||||
{
|
||||
store.equip(*slot, it);
|
||||
break;
|
||||
}
|
||||
|
||||
if (store.getSlot(*slot) == store.end())
|
||||
{
|
||||
// slot is not occupied
|
||||
store.equip(*slot, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// \todo scripts?
|
||||
|
||||
// since we changed equipping status, update the inventory window
|
||||
mWindowManager.getInventoryWindow()->drawItems();
|
||||
}
|
||||
|
||||
store.setSelectedEnchantItem(it);
|
||||
spells.setSelectedSpell("");
|
||||
mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge %
|
||||
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
void SpellWindow::onSpellSelected(MyGUI::Widget* _sender)
|
||||
{
|
||||
std::string spellId = _sender->getUserString("Spell");
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
|
||||
MWMechanics::Spells& spells = stats.mSpells;
|
||||
|
||||
if (MyGUI::InputManager::getInstance().isShiftPressed())
|
||||
{
|
||||
// delete spell, if allowed
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId);
|
||||
if (spell->data.flags & ESM::Spell::F_Always
|
||||
|| spell->data.type == ESM::Spell::ST_Power)
|
||||
{
|
||||
mWindowManager.messageBox("#{sDeleteSpellError}", std::vector<std::string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
// ask for confirmation
|
||||
mSpellToDelete = spellId;
|
||||
ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog();
|
||||
std::string question = mWindowManager.getGameSettingString("sQuestionDeleteSpell", "Delete %s?");
|
||||
question = boost::str(boost::format(question) % spell->name);
|
||||
dialog->open(question);
|
||||
dialog->eventOkClicked.clear();
|
||||
dialog->eventOkClicked += MyGUI::newDelegate(this, &SpellWindow::onDeleteSpellAccept);
|
||||
dialog->eventCancelClicked.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
spells.setSelectedSpell(spellId);
|
||||
store.setSelectedEnchantItem(store.end());
|
||||
mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
|
||||
}
|
||||
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
int SpellWindow::estimateHeight(int numSpells) const
|
||||
{
|
||||
int height = 0;
|
||||
height += 24 * 3 + 18 * 2; // group headings
|
||||
height += numSpells * 18;
|
||||
return height;
|
||||
}
|
||||
|
||||
void SpellWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
if (mSpellView->getViewOffset().top + _rel*0.3 > 0)
|
||||
mSpellView->setViewOffset(MyGUI::IntPoint(0, 0));
|
||||
else
|
||||
mSpellView->setViewOffset(MyGUI::IntPoint(0, mSpellView->getViewOffset().top + _rel*0.3));
|
||||
}
|
||||
|
||||
void SpellWindow::onDeleteSpellAccept()
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
MWMechanics::Spells& spells = stats.mSpells;
|
||||
|
||||
if (spells.getSelectedSpell() == mSpellToDelete)
|
||||
{
|
||||
spells.setSelectedSpell("");
|
||||
mWindowManager.unsetSelectedSpell();
|
||||
}
|
||||
|
||||
spells.remove(mSpellToDelete);
|
||||
|
||||
updateSpells();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
#ifndef MWGUI_SPELLWINDOW_H
|
||||
#define MWGUI_SPELLWINDOW_H
|
||||
|
||||
#include "window_pinnable_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class SpellWindow : public WindowPinnableBase
|
||||
{
|
||||
public:
|
||||
SpellWindow(WindowManager& parWindowManager);
|
||||
|
||||
void updateSpells();
|
||||
|
||||
protected:
|
||||
MyGUI::ScrollView* mSpellView;
|
||||
MyGUI::Widget* mEffectBox;
|
||||
|
||||
int mHeight;
|
||||
int mWidth;
|
||||
|
||||
std::string mSpellToDelete;
|
||||
|
||||
void addGroup(const std::string& label, const std::string& label2);
|
||||
|
||||
int estimateHeight(int numSpells) const;
|
||||
|
||||
void onWindowResize(MyGUI::Window* _sender);
|
||||
void onEnchantedItemSelected(MyGUI::Widget* _sender);
|
||||
void onSpellSelected(MyGUI::Widget* _sender);
|
||||
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||
void onDeleteSpellAccept();
|
||||
|
||||
virtual void onPinToggled();
|
||||
virtual void open();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,99 @@
|
||||
#ifndef MWMECHANICS_SPELLSUCCESS_H
|
||||
#define MWMECHANICS_SPELLSUCCESS_H
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "npcstats.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
inline int spellSchoolToSkill(int school)
|
||||
{
|
||||
std::map<int, int> schoolSkillMap; // maps spell school to skill id
|
||||
schoolSkillMap[0] = 11; // alteration
|
||||
schoolSkillMap[1] = 13; // conjuration
|
||||
schoolSkillMap[3] = 12; // illusion
|
||||
schoolSkillMap[2] = 10; // destruction
|
||||
schoolSkillMap[4] = 14; // mysticism
|
||||
schoolSkillMap[5] = 15; // restoration
|
||||
assert(schoolSkillMap.find(school) != schoolSkillMap.end());
|
||||
return schoolSkillMap[school];
|
||||
}
|
||||
|
||||
inline int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId);
|
||||
NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
|
||||
// determine the spell's school
|
||||
// this is always the school where the player's respective skill is the least advanced
|
||||
// out of all the magic effects' schools
|
||||
const std::vector<ESM::ENAMstruct>& effects = spell->effects.list;
|
||||
int school = -1;
|
||||
int skillLevel = -1;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.begin();
|
||||
it != effects.end(); ++it)
|
||||
{
|
||||
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().magicEffects.find(it->effectID);
|
||||
int _school = effect->data.school;
|
||||
int _skillLevel = stats.mSkill[spellSchoolToSkill(_school)].getModified();
|
||||
|
||||
if (school == -1)
|
||||
{
|
||||
school = _school;
|
||||
skillLevel = _skillLevel;
|
||||
}
|
||||
else if (_skillLevel < skillLevel)
|
||||
{
|
||||
school = _school;
|
||||
skillLevel = _skillLevel;
|
||||
}
|
||||
}
|
||||
|
||||
return school;
|
||||
}
|
||||
|
||||
|
||||
// UESP wiki / Morrowind/Spells:
|
||||
// Chance of success is (Spell's skill * 2 + Willpower / 5 + Luck / 10 - Spell cost - Sound magnitude) * (Current fatigue + Maximum Fatigue * 1.5) / Maximum fatigue * 2
|
||||
/**
|
||||
* @param spellId ID of spell
|
||||
* @param actor calculate spell success chance for this actor (depends on actor's skills)
|
||||
* @attention actor has to be an NPC and not a creature!
|
||||
* @return success chance from 0 to 100 (in percent)
|
||||
*/
|
||||
inline float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId);
|
||||
|
||||
if (spell->data.flags & ESM::Spell::F_Always // spells with this flag always succeed (usually birthsign spells)
|
||||
|| spell->data.type == ESM::Spell::ST_Power) // powers always succeed, but can be cast only once per day
|
||||
return 100.0;
|
||||
|
||||
NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
|
||||
|
||||
int skillLevel = stats.mSkill[getSpellSchool(spellId, actor)].getModified();
|
||||
|
||||
// Sound magic effect (reduces spell casting chance)
|
||||
int soundMagnitude = creatureStats.mMagicEffects.get (MWMechanics::EffectKey (48)).mMagnitude;
|
||||
|
||||
int willpower = creatureStats.mAttributes[ESM::Attribute::Willpower].getModified();
|
||||
int luck = creatureStats.mAttributes[ESM::Attribute::Luck].getModified();
|
||||
int currentFatigue = creatureStats.mDynamic[2].getCurrent();
|
||||
int maxFatigue = creatureStats.mDynamic[2].getModified();
|
||||
int spellCost = spell->data.cost;
|
||||
|
||||
// There we go, all needed variables are there, lets go
|
||||
float chance = (float(skillLevel * 2) + float(willpower)/5.0 + float(luck)/ 10.0 - spellCost - soundMagnitude) * (float(currentFatigue + maxFatigue * 1.5)) / float(maxFatigue * 2.0);
|
||||
|
||||
chance = std::max(0.0f, std::min(100.0f, chance)); // clamp to 0 .. 100
|
||||
|
||||
return chance;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue