added the success formula, and spell deleting (shift+click)

actorid
scrawl 13 years ago
parent 30461438f6
commit 48ba293e88

@ -2,6 +2,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include "../mwworld/world.hpp"
#include "../mwworld/player.hpp"
@ -9,10 +10,12 @@
#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
{
@ -175,6 +178,7 @@ namespace MWGui
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)
@ -194,18 +198,19 @@ namespace MWGui
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);
t->setStateSelected(*it == selectedSpell);
// cost / success chance
MyGUI::TextBox* costChance = mSpellView->createWidget<MyGUI::TextBox>("SpellText",
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);
costChance->setCaption(cost + "/" + "100"); /// \todo
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;
@ -219,6 +224,8 @@ namespace MWGui
{
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)
@ -238,18 +245,26 @@ namespace MWGui
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::TextBox* costCharge = mSpellView->createWidget<MyGUI::TextBox>(equipped ? "SpellText" : "SpellTextUnequipped",
MyGUI::Button* costCharge = mSpellView->createWidget<MyGUI::Button>(equipped ? "SpellText" : "SpellTextUnequipped",
MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top);
const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().enchants.find(MWWorld::Class::get(item).getEnchantment(item));
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;
}
@ -312,8 +327,9 @@ namespace MWGui
}
assert(it != store.end());
// equip, if it is not already equipped
if (_sender->getUserString("Equipped") == "false")
// 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);
@ -324,7 +340,6 @@ namespace MWGui
// 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)
@ -362,8 +377,33 @@ namespace MWGui
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
MWMechanics::Spells& spells = stats.mSpells;
spells.setSelectedSpell(_sender->getUserString("Spell"));
store.setSelectedEnchantItem(store.end());
if (MyGUI::InputManager::getInstance().isShiftPressed())
{
// delete spell, if allowed
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(_sender->getUserString("Spell"));
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 = _sender->getUserString("Spell");
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(_sender->getUserString("Spell"));
store.setSelectedEnchantItem(store.end());
}
updateSpells();
}
@ -375,4 +415,23 @@ namespace MWGui
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;
spells.remove(mSpellToDelete);
updateSpells();
}
}

@ -19,14 +19,19 @@ namespace MWGui
int mHeight;
int mWidth;
std::string mSpellToDelete;
void addGroup(const std::string& label, const std::string& label2);
int estimateHeight(int numSpells) const;
virtual void onPinToggled();
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();
};
}

@ -0,0 +1,83 @@
#ifndef MWMECHANICS_SPELLSUCCESS_H
#define MWMECHANICS_SPELLSUCCESS_H
#include "../mwworld/ptr.hpp"
#include "../mwworld/world.hpp"
#include "../mwbase/environment.hpp"
#include "npcstats.hpp"
namespace MWMechanics
{
// 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)
*/
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)
return 100.0;
NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor);
CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor);
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
// 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 skill = -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;
assert(schoolSkillMap.find(school) != schoolSkillMap.end());
int _skillLevel = stats.mSkill[schoolSkillMap[school]].getModified();
if (skill == -1)
{
skill = schoolSkillMap[school];
skillLevel = _skillLevel;
}
else if (_skillLevel < skillLevel)
{
skill = schoolSkillMap[school];
skillLevel = _skillLevel;
}
}
// 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
Loading…
Cancel
Save