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