diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 33ee8bf49..bf6b4add0 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -347,7 +347,6 @@ void ContainerBase::setFilter(ContainerBase::Filter filter) void ContainerBase::openContainer(MWWorld::Ptr container) { mPtr = container; - drawItems(); } void ContainerBase::drawItems() @@ -638,6 +637,7 @@ void ContainerWindow::open(MWWorld::Ptr container) { openContainer(container); setTitle(MWWorld::Class::get(container).getName(container)); + drawItems(); } void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 6b0cdbd5c..95017579b 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -90,6 +90,7 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) getWidget(compass, "Compass"); getWidget(mCellNameBox, "CellName"); + getWidget(mWeaponSpellBox, "WeaponSpellName"); getWidget(crosshair, "Crosshair"); @@ -98,14 +99,11 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) getWidget(trianglecounter, "TriangleCounter"); getWidget(batchcounter, "BatchCounter"); - // These are just demo values, you should replace these with - // real calls from outside the class later. - setWeapIcon("icons\\w\\tx_knife_iron.dds"); - setWeapStatus(90, 100); - setSpellIcon("icons\\s\\b_tx_s_rstor_health.dds"); - setSpellStatus(65, 100); setEffect("icons\\s\\tx_s_chameleon.dds"); + unsetSelectedSpell(); + unsetSelectedWeapon(); + LocalMapBase::init(minimap, compass, this); mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); @@ -153,28 +151,6 @@ void HUD::setBatchCount(size_t count) batchcounter->setCaption(boost::lexical_cast(count)); } -void HUD::setWeapIcon(const char *str) -{ - weapImage->setImageTexture(str); -} - -void HUD::setSpellIcon(const char *str) -{ - spellImage->setImageTexture(str); -} - -void HUD::setWeapStatus(int s, int smax) -{ - weapStatus->setProgressRange(smax); - weapStatus->setProgressPosition(s); -} - -void HUD::setSpellStatus(int s, int smax) -{ - spellStatus->setProgressRange(smax); - spellStatus->setProgressPosition(s); -} - void HUD::setEffect(const char *img) { effect1->setImageTexture(img); @@ -354,11 +330,150 @@ void HUD::setCellName(const std::string& cellName) void HUD::onFrame(float dt) { mCellNameTimer -= dt; + mWeaponSpellTimer -= dt; if (mCellNameTimer < 0) mCellNameBox->setVisible(false); + if (mWeaponSpellTimer < 0) + mWeaponSpellBox->setVisible(false); } void HUD::onResChange(int width, int height) { setCoord(0, 0, width, height); } + +void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) +{ + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); + std::string spellName = spell->name; + if (spellName != mSpellName) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + spellStatus->setProgressRange(100); + spellStatus->setProgressPosition(successChancePercent); + + if (spellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(spellImage->getChildAt(0)); + + spellBox->setUserString("ToolTipType", "Spell"); + spellBox->setUserString("Spell", spellId); + + // use the icon of the first effect + const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().magicEffects.find(spell->effects.list.front().effectID); + std::string icon = effect->icon; + int slashPos = icon.find("\\"); + icon.insert(slashPos+1, "b_"); + icon = std::string("icons\\") + icon; + Widgets::fixTexturePath(icon); + spellImage->setImageTexture(icon); +} + +void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) +{ + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mSpellName) + { + mWeaponSpellTimer = 5.0f; + mSpellName = itemName; + mWeaponSpellBox->setCaption(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + spellStatus->setProgressRange(100); + spellStatus->setProgressPosition(chargePercent); + + if (spellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(spellImage->getChildAt(0)); + + spellBox->setUserString("ToolTipType", "ItemPtr"); + spellBox->setUserData(item); + + spellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = spellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + , MyGUI::Align::Stretch); + + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); + itemBox->setImageTexture(path); + itemBox->setNeedMouseFocus(false); +} + +void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) +{ + std::string itemName = MWWorld::Class::get(item).getName(item); + if (itemName != mWeaponName) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaption(mWeaponName); + mWeaponSpellBox->setVisible(true); + } + + weapBox->setUserString("ToolTipType", "ItemPtr"); + weapBox->setUserData(item); + + weapStatus->setProgressRange(100); + weapStatus->setProgressPosition(durabilityPercent); + + if (weapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(weapImage->getChildAt(0)); + + std::string path = std::string("icons\\"); + path+=MWWorld::Class::get(item).getInventoryIcon(item); + Widgets::fixTexturePath(path); + + if (MWWorld::Class::get(item).getEnchantment(item) != "") + { + weapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = weapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + , MyGUI::Align::Stretch); + itemBox->setImageTexture(path); + itemBox->setNeedMouseFocus(false); + } + else + weapImage->setImageTexture(path); +} + +void HUD::unsetSelectedSpell() +{ + std::string spellName = "#{sNone}"; + if (spellName != mSpellName) + { + mWeaponSpellTimer = 5.0f; + mSpellName = spellName; + mWeaponSpellBox->setCaptionWithReplacing(mSpellName); + mWeaponSpellBox->setVisible(true); + } + + if (spellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(spellImage->getChildAt(0)); + spellStatus->setProgressRange(100); + spellStatus->setProgressPosition(0); + spellImage->setImageTexture(""); + spellBox->clearUserStrings(); +} + +void HUD::unsetSelectedWeapon() +{ + std::string itemName = "#{sSkillHandtohand}"; + if (itemName != mWeaponName) + { + mWeaponSpellTimer = 5.0f; + mWeaponName = itemName; + mWeaponSpellBox->setCaptionWithReplacing(mWeaponName); + mWeaponSpellBox->setVisible(true); + } + + if (weapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(weapImage->getChildAt(0)); + weapStatus->setProgressRange(100); + weapStatus->setProgressPosition(0); + weapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); + weapBox->clearUserStrings(); +} diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 6d4bf0559..f51110637 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -3,6 +3,7 @@ #include #include "../mwmechanics/stat.hpp" +#include "../mwworld/ptr.hpp" namespace MWGui { @@ -12,10 +13,6 @@ namespace MWGui { public: HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop); - void setWeapIcon(const char *str); - void setSpellIcon(const char *str); - void setWeapStatus(int s, int smax); - void setSpellStatus(int s, int smax); void setEffect(const char *img); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setFPS(float fps); @@ -25,6 +22,12 @@ namespace MWGui void setBottomRightVisibility(bool effectBoxVisible, bool minimapVisible); void setFpsLevel(const int level); + void setSelectedSpell(const std::string& spellId, int successChancePercent); + void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); + void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); + void unsetSelectedSpell(); + void unsetSelectedWeapon(); + void onFrame(float dt); void onResChange(int width, int height); @@ -41,6 +44,7 @@ namespace MWGui MyGUI::ImageBox* compass; MyGUI::ImageBox* crosshair; MyGUI::TextBox* mCellNameBox; + MyGUI::TextBox* mWeaponSpellBox; MyGUI::WidgetPtr fpsbox; MyGUI::TextBox* fpscounter; @@ -58,6 +62,10 @@ namespace MWGui std::string mCellName; float mCellNameTimer; + std::string mWeaponName; + std::string mSpellName; + float mWeaponSpellTimer; + bool mMapVisible; void onWorldClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index e1d4cc998..103f5ebf3 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -188,6 +188,15 @@ namespace MWGui mWindowManager.setDragDrop(false); drawItems(); + + // update selected weapon icon + MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weaponSlot = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weaponSlot == invStore.end()) + mWindowManager.unsetSelectedWeapon(); + else + mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability + } } @@ -266,5 +275,14 @@ namespace MWGui // update the spell window just in case new enchanted items were added to inventory if (mWindowManager.getSpellWindow()) mWindowManager.getSpellWindow()->updateSpells(); + + // update selected weapon icon + MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weaponSlot = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weaponSlot == invStore.end()) + mWindowManager.unsetSelectedWeapon(); + else + mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability + } } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 168578831..aae3d878d 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -110,6 +110,8 @@ namespace MWGui if (!allowSelectedItem) { store.setSelectedEnchantItem(store.end()); + spells.setSelectedSpell(""); + mWindowManager.unsetSelectedSpell(); selectedItem = MWWorld::Ptr(); } } @@ -366,12 +368,14 @@ namespace MWGui 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); @@ -380,7 +384,7 @@ namespace MWGui if (MyGUI::InputManager::getInstance().isShiftPressed()) { // delete spell, if allowed - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(_sender->getUserString("Spell")); + 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) { @@ -389,7 +393,7 @@ namespace MWGui else { // ask for confirmation - mSpellToDelete = _sender->getUserString("Spell"); + mSpellToDelete = spellId; ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); std::string question = mWindowManager.getGameSettingString("sQuestionDeleteSpell", "Delete %s?"); question = boost::str(boost::format(question) % spell->name); @@ -401,8 +405,9 @@ namespace MWGui } else { - spells.setSelectedSpell(_sender->getUserString("Spell")); + spells.setSelectedSpell(spellId); store.setSelectedEnchantItem(store.end()); + mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } updateSpells(); @@ -430,6 +435,12 @@ namespace MWGui 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(); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index dc34ee86c..0f6892190 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -402,7 +402,6 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) TextBox* chargeText = enchantArea->createWidget("SandText", IntCoord(0, 0, 10, 18), Align::Default, "ToolTipEnchantChargeText"); chargeText->setCaption(store.gameSettings.search("sCharges")->str); - chargeText->setProperty("Static", "true"); const int chargeTextWidth = chargeText->getTextSize().width + 5; const int chargeAndTextWidth = chargeWidth + chargeTextWidth; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0a12a82a0..2089ed4af 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -104,6 +104,8 @@ namespace MWGui ContainerBase::openContainer(actor); updateLabels(); + + drawItems(); } void TradeWindow::onFilterChanged(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 4eaaf72a4..bd1ad46b6 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -674,3 +674,28 @@ void WindowManager::removeGuiMode(GuiMode mode) updateVisible(); } + +void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) +{ + hud->setSelectedSpell(spellId, successChancePercent); +} + +void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) +{ + hud->setSelectedEnchantItem(item, chargePercent); +} + +void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) +{ + hud->setSelectedWeapon(item, durabilityPercent); +} + +void WindowManager::unsetSelectedSpell() +{ + hud->unsetSelectedSpell(); +} + +void WindowManager::unsetSelectedWeapon() +{ + hud->unsetSelectedWeapon(); +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 2a7794f70..037022b42 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -204,6 +204,12 @@ namespace MWGui void setWeaponVisibility(bool visible); void setSpellVisibility(bool visible); + void setSelectedSpell(const std::string& spellId, int successChancePercent); + void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); + void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); + void unsetSelectedSpell(); + void unsetSelectedWeapon(); + template void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. diff --git a/apps/openmw/mwmechanics/spellsuccess.hpp b/apps/openmw/mwmechanics/spellsuccess.hpp index 532f9eb4e..11ac7cda7 100644 --- a/apps/openmw/mwmechanics/spellsuccess.hpp +++ b/apps/openmw/mwmechanics/spellsuccess.hpp @@ -4,30 +4,14 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwmechanics/creaturestats.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) + inline int spellSchoolToSkill(int school) { - 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 schoolSkillMap; // maps spell school to skill id schoolSkillMap[0] = 11; // alteration schoolSkillMap[1] = 13; // conjuration @@ -35,33 +19,65 @@ namespace MWMechanics 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& effects = spell->effects.list; - int skill = -1; + int school = -1; int skillLevel = -1; for (std::vector::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(); + int _school = effect->data.school; + int _skillLevel = stats.mSkill[spellSchoolToSkill(_school)].getModified(); - if (skill == -1) + if (school == -1) { - skill = schoolSkillMap[school]; + school = _school; skillLevel = _skillLevel; } else if (_skillLevel < skillLevel) { - skill = schoolSkillMap[school]; + 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; diff --git a/files/mygui/openmw_hud_layout.xml b/files/mygui/openmw_hud_layout.xml index d0026bf25..cf353a205 100644 --- a/files/mygui/openmw_hud_layout.xml +++ b/files/mygui/openmw_hud_layout.xml @@ -28,6 +28,14 @@ + + + + + + + +