diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index d2cc89520c..c203af8a48 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -1171,7 +1171,7 @@ namespace EsmTool template <> void Record<ESM::Race>::print() { - static const char* sAttributeNames[8] + static const char* sAttributeNames[ESM::Attribute::Length] = { "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", "Luck" }; std::cout << " Name: " << mData.mName << std::endl; diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 5dd2e16ae8..149b5d19ca 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -7,6 +7,7 @@ #include <apps/opencs/model/world/refiddata.hpp> #include <apps/opencs/model/world/universalid.hpp> +#include <components/esm/attr.hpp> #include <components/esm3/loadcont.hpp> #include <components/esm3/loadmgef.hpp> #include <components/esm3/loadskil.hpp> @@ -1007,8 +1008,7 @@ int CSMWorld::NpcAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn int CSMWorld::NpcAttributesRefIdAdapter::getNestedRowsCount( const RefIdColumn* column, const RefIdData& data, int index) const { - // There are 8 attributes - return 8; + return ESM::Attribute::Length; } void CSMWorld::NpcSkillsRefIdAdapter::addNestedRow( @@ -1385,8 +1385,7 @@ int CSMWorld::CreatureAttributesRefIdAdapter::getNestedColumnsCount( int CSMWorld::CreatureAttributesRefIdAdapter::getNestedRowsCount( const RefIdColumn* column, const RefIdData& data, int index) const { - // There are 8 attributes - return 8; + return ESM::Attribute::Length; } void CSMWorld::CreatureAttackRefIdAdapter::addNestedRow( diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index fec0b4260f..b92422c43a 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -123,7 +123,7 @@ namespace MWClass const MWWorld::LiveCellRef<ESM::Clothing>* ref = ptr.get<ESM::Clothing>(); static const ESM::RefId ringUp = ESM::RefId::stringRefId("Item Ring Up"); static const ESM::RefId clothsUp = ESM::RefId::stringRefId("Item Clothes Up"); - if (ref->mBase->mData.mType == 8) + if (ref->mBase->mData.mType == ESM::Clothing::Ring) { return ringUp; } @@ -135,7 +135,7 @@ namespace MWClass const MWWorld::LiveCellRef<ESM::Clothing>* ref = ptr.get<ESM::Clothing>(); static const ESM::RefId ringDown = ESM::RefId::stringRefId("Item Ring Down"); static const ESM::RefId clothsDown = ESM::RefId::stringRefId("Item Clothes Down"); - if (ref->mBase->mData.mType == 8) + if (ref->mBase->mData.mType == ESM::Clothing::Ring) { return ringDown; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5f475ec61d..6c37b72b5b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -88,11 +88,12 @@ namespace bool male = (npc->mFlags & ESM::NPC::Female) == 0; + const auto& attributes = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>(); int level = creatureStats.getLevel(); - for (int i = 0; i < ESM::Attribute::Length; ++i) + for (const ESM::Attribute& attribute : attributes) { - const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; - creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); + const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[attribute.mId]; + creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale); } // class bonus @@ -102,18 +103,19 @@ namespace { if (attribute >= 0 && attribute < ESM::Attribute::Length) { - creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); + auto id = static_cast<ESM::Attribute::AttributeID>(attribute); + creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10); } } // skill bonus - for (int attribute = 0; attribute < ESM::Attribute::Length; ++attribute) + for (const ESM::Attribute& attribute : attributes) { float modifierSum = 0; for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>()) { - if (skill.mData.mAttribute != attribute) + if (skill.mData.mAttribute != attribute.mId) continue; // is this a minor or major skill? @@ -127,9 +129,10 @@ namespace } modifierSum += add; } - creatureStats.setAttribute(attribute, + creatureStats.setAttribute(attribute.mId, std::min( - round_ieee_754(creatureStats.getAttribute(attribute).getBase() + (level - 1) * modifierSum), 100)); + round_ieee_754(creatureStats.getAttribute(attribute.mId).getBase() + (level - 1) * modifierSum), + 100)); } // initial health @@ -223,13 +226,10 @@ namespace 100)); // Must gracefully handle level 0 } - int attributes[ESM::Attribute::Length]; - for (int i = 0; i < ESM::Attribute::Length; ++i) - attributes[i] = npcStats.getAttribute(i).getBase(); - if (!spellsInitialised) { - std::vector<ESM::RefId> spells = MWMechanics::autoCalcNpcSpells(npcStats.getSkills(), attributes, race); + std::vector<ESM::RefId> spells + = MWMechanics::autoCalcNpcSpells(npcStats.getSkills(), npcStats.getAttributes(), race); npcStats.getSpells().addAllToInstance(spells); } } diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index b4841d6cbe..e05f1b37c2 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -382,9 +382,10 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons .getModified(false); case SelectWrapper::Function_PcAttribute: - - return player.getClass().getCreatureStats(player).getAttribute(select.getArgument()).getModified(); - + { + auto attribute = static_cast<ESM::Attribute::AttributeID>(select.getArgument()); + return player.getClass().getCreatureStats(player).getAttribute(attribute).getModified(); + } case SelectWrapper::Function_PcSkill: { ESM::RefId skill = ESM::Skill::indexToRefId(select.getArgument()); @@ -653,8 +654,10 @@ bool MWDialogue::Filter::hasFactionRankSkillRequirements( MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); - return stats.getAttribute(faction.mData.mAttribute[0]).getBase() >= faction.mData.mRankData[rank].mAttribute1 - && stats.getAttribute(faction.mData.mAttribute[1]).getBase() >= faction.mData.mRankData[rank].mAttribute2; + return stats.getAttribute(ESM::Attribute::AttributeID(faction.mData.mAttribute[0])).getBase() + >= faction.mData.mRankData[rank].mAttribute1 + && stats.getAttribute(ESM::Attribute::AttributeID(faction.mData.mAttribute[1])).getBase() + >= faction.mData.mRankData[rank].mAttribute2; } bool MWDialogue::Filter::hasFactionRankReputationRequirements( diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index dc1608b9c7..114238482d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -102,21 +102,11 @@ namespace MWGui mPlayerSkillValues.emplace(skill.mId, MWMechanics::SkillValue()); } - void CharacterCreation::setValue(std::string_view id, const MWMechanics::AttributeValue& value) + void CharacterCreation::setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) { - std::string_view prefix = "AttribVal"; - if (id.starts_with(prefix) && id.size() == prefix.size() + 1) - { - char index = id[prefix.size()]; - if (index >= '1' && index <= '8') - { - // Match [AttribVal1-AttribVal8] to the corresponding AttributeID values [0-7] - auto attribute = static_cast<ESM::Attribute::AttributeID>(index - '0' - 1); - mPlayerAttributes[attribute] = value; - if (mReviewDialog) - mReviewDialog->setAttribute(attribute, value); - } - } + mPlayerAttributes[id] = value; + if (mReviewDialog) + mReviewDialog->setAttribute(id, value); } void CharacterCreation::setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index f9e76d1fc6..d590c0cfd6 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -44,7 +44,7 @@ namespace MWGui // Show a dialog void spawnDialog(const char id); - void setValue(std::string_view id, const MWMechanics::AttributeValue& value) override; + void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) override; void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override; void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override; void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor) override; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index b26d750191..202f18ad35 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -260,8 +260,8 @@ namespace MWGui mSpecializationName->setCaption(specName); ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); - mFavoriteAttribute[0]->setAttributeId(klass->mData.mAttribute[0]); - mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); + mFavoriteAttribute[0]->setAttributeId(static_cast<ESM::Attribute::AttributeID>(klass->mData.mAttribute[0])); + mFavoriteAttribute[1]->setAttributeId(static_cast<ESM::Attribute::AttributeID>(klass->mData.mAttribute[1])); ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); @@ -749,30 +749,34 @@ namespace MWGui center(); const auto& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::Attribute>(); + MyGUI::ScrollView* attributes; + getWidget(attributes, "Attributes"); + MyGUI::IntCoord coord{ 0, 0, attributes->getWidth(), 18 }; for (const ESM::Attribute& attribute : store) { - Widgets::MWAttributePtr widget; - char theIndex = '0' + attribute.mId; - - getWidget(widget, std::string("Attribute").append(1, theIndex)); + auto* widget + = attributes->createWidget<Widgets::MWAttribute>("MW_StatNameButtonC", coord, MyGUI::Align::Default); + coord.top += coord.height; widget->setAttributeId(attribute.mId); widget->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); - ToolTips::createAttributeToolTip(widget, widget->getAttributeId()); + ToolTips::createAttributeToolTip(widget, attribute.mId); } + attributes->setVisibleVScroll(false); + attributes->setCanvasSize(MyGUI::IntSize(attributes->getWidth(), std::max(attributes->getHeight(), coord.top))); + attributes->setVisibleVScroll(true); + attributes->setViewOffset(MyGUI::IntPoint()); + MyGUI::Button* cancelButton; getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); } - SelectAttributeDialog::~SelectAttributeDialog() {} - // widget controls void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { - // TODO: Change MWAttribute to set and get AttributeID enum instead of int - mAttributeId = static_cast<ESM::Attribute::AttributeID>(_sender->getAttributeId()); + mAttributeId = _sender->getAttributeId(); eventItemSelected(); } diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 1052dab581..b231a64b1d 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -184,7 +184,7 @@ namespace MWGui { public: SelectAttributeDialog(); - ~SelectAttributeDialog(); + ~SelectAttributeDialog() override = default; bool exit() override; diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 45ba20c597..b13fdbeeb9 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -3,8 +3,11 @@ #include <MyGUI_Button.h> #include <MyGUI_EditBox.h> #include <MyGUI_ImageBox.h> +#include <MyGUI_ScrollView.h> +#include <MyGUI_TextBox.h> #include <components/fallback/fallback.hpp> +#include <components/widgets/box.hpp> #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -12,6 +15,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -20,9 +24,13 @@ #include "class.hpp" #include "ustring.hpp" +namespace +{ + constexpr unsigned int sMaxCoins = 3; + constexpr int sColumnOffsets[] = { 32, 218 }; +} namespace MWGui { - const unsigned int LevelupDialog::sMaxCoins = 3; LevelupDialog::LevelupDialog() : WindowBase("openmw_levelup_dialog.layout") , mCoinCount(sMaxCoins) @@ -36,23 +44,43 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onOkButtonClicked); - for (int i = 1; i < 9; ++i) { - MyGUI::TextBox* t; - getWidget(t, "AttribVal" + MyGUI::utility::toString(i)); - mAttributeValues.push_back(t); + const auto& store = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>(); + const size_t perCol + = static_cast<size_t>(std::ceil(store.getSize() / static_cast<float>(std::size(sColumnOffsets)))); + size_t i = 0; + for (const ESM::Attribute& attribute : store) + { + const int offset = sColumnOffsets[i / perCol]; + const int row = static_cast<int>(i % perCol); + Widgets widgets; + widgets.mMultiplier = mAssignWidget->createWidget<MyGUI::TextBox>( + "SandTextVCenter", { offset, 20 * row, 100, 20 }, MyGUI::Align::Default); + auto* hbox = mAssignWidget->createWidget<Gui::HBox>( + {}, { offset + 20, 20 * row, 200, 20 }, MyGUI::Align::Default); + widgets.mButton = hbox->createWidget<Gui::AutoSizedButton>("SandTextButton", {}, MyGUI::Align::Default); + widgets.mButton->setUserData(attribute.mId); + widgets.mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); + widgets.mButton->setUserString("TextPadding", "0 0"); + widgets.mButton->setUserString("ToolTipType", "Layout"); + widgets.mButton->setUserString("ToolTipLayout", "AttributeToolTip"); + widgets.mButton->setUserString("Caption_AttributeName", attribute.mName); + widgets.mButton->setUserString("Caption_AttributeDescription", attribute.mDescription); + widgets.mButton->setUserString("ImageTexture_AttributeImage", attribute.mIcon); + widgets.mButton->setCaption(attribute.mName); + widgets.mValue = hbox->createWidget<Gui::AutoSizedTextBox>("SandText", {}, MyGUI::Align::Default); + mAttributeWidgets.emplace(attribute.mId, widgets); + ++i; + } - MyGUI::Button* b; - getWidget(b, "Attrib" + MyGUI::utility::toString(i)); - b->setUserData(i - 1); - b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); - mAttributes.push_back(b); - - getWidget(t, "AttribMultiplier" + MyGUI::utility::toString(i)); - mAttributeMultipliers.push_back(t); + mAssignWidget->setVisibleVScroll(false); + mAssignWidget->setCanvasSize(MyGUI::IntSize( + mAssignWidget->getWidth(), std::max(mAssignWidget->getHeight(), static_cast<int>(20 * perCol)))); + mAssignWidget->setVisibleVScroll(true); + mAssignWidget->setViewOffset(MyGUI::IntPoint()); } - for (unsigned int i = 0; i < mCoinCount; ++i) + for (unsigned int i = 0; i < sMaxCoins; ++i) { MyGUI::ImageBox* image = mCoinBox->createWidget<MyGUI::ImageBox>( "ImageBox", MyGUI::IntCoord(0, 0, 16, 16), MyGUI::Align::Default); @@ -69,24 +97,24 @@ namespace MWGui MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player); - for (int i = 0; i < 8; ++i) + for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>()) { - int val = creatureStats.getAttribute(i).getBase(); - if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end()) + int val = creatureStats.getAttribute(attribute.mId).getBase(); + if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute.mId) != mSpentAttributes.end()) { - val += pcStats.getLevelupAttributeMultiplier(i); + val += pcStats.getLevelupAttributeMultiplier(attribute.mId); } if (val >= 100) val = 100; - mAttributeValues[i]->setCaption(MyGUI::utility::toString(val)); + mAttributeWidgets[attribute.mId].mValue->setCaption(MyGUI::utility::toString(val)); } } void LevelupDialog::resetCoins() { - const int coinSpacing = 33; + constexpr int coinSpacing = 33; int curX = mCoinBox->getWidth() / 2 - (coinSpacing * (mCoinCount - 1) + 16 * mCoinCount) / 2; for (unsigned int i = 0; i < sMaxCoins; ++i) { @@ -113,13 +141,15 @@ namespace MWGui image->detachFromWidget(); image->attachToWidget(mAssignWidget); - int attribute = mSpentAttributes[i]; + const auto& attribute = mSpentAttributes[i]; + const auto& widgets = mAttributeWidgets[attribute]; - int xdiff = mAttributeMultipliers[attribute]->getCaption().empty() ? 0 : 20; + const int xdiff = widgets.mMultiplier->getCaption().empty() ? 0 : 20; + const auto* hbox = widgets.mButton->getParent(); - MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mAssignWidget->getAbsolutePosition() - - MyGUI::IntPoint(22 + xdiff, 0); - pos.top += (mAttributes[attribute]->getHeight() - image->getHeight()) / 2; + MyGUI::IntPoint pos = hbox->getPosition(); + pos.left -= 22 + xdiff; + pos.top += (hbox->getHeight() - image->getHeight()) / 2; image->setPosition(pos); } @@ -130,8 +160,8 @@ namespace MWGui { MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); - MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player); - MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player); + const MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player); + const MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player); setClassImage(mClassImage, ESM::RefId::stringRefId(getLevelupClassImage(pcStats.getSkillIncreasesForSpecialization(0), @@ -149,28 +179,28 @@ namespace MWGui mLevelDescription->setCaption(toUString(levelupdescription)); unsigned int availableAttributes = 0; - for (int i = 0; i < 8; ++i) + for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>()) { - MyGUI::TextBox* text = mAttributeMultipliers[i]; - if (pcStats.getAttribute(i).getBase() < 100) + const auto& widgets = mAttributeWidgets[attribute.mId]; + if (pcStats.getAttribute(attribute.mId).getBase() < 100) { - mAttributes[i]->setEnabled(true); - mAttributeValues[i]->setEnabled(true); + widgets.mButton->setEnabled(true); + widgets.mValue->setEnabled(true); availableAttributes++; - float mult = pcStats.getLevelupAttributeMultiplier(i); - mult = std::min(mult, 100 - pcStats.getAttribute(i).getBase()); + float mult = pcStats.getLevelupAttributeMultiplier(attribute.mId); + mult = std::min(mult, 100 - pcStats.getAttribute(attribute.mId).getBase()); if (mult <= 1) - text->setCaption({}); + widgets.mMultiplier->setCaption({}); else - text->setCaption("x" + MyGUI::utility::toString(mult)); + widgets.mMultiplier->setCaption("x" + MyGUI::utility::toString(mult)); } else { - mAttributes[i]->setEnabled(false); - mAttributeValues[i]->setEnabled(false); + widgets.mButton->setEnabled(false); + widgets.mValue->setEnabled(false); - text->setCaption({}); + widgets.mMultiplier->setCaption({}); } } @@ -215,9 +245,9 @@ namespace MWGui void LevelupDialog::onAttributeClicked(MyGUI::Widget* sender) { - int attribute = *sender->getUserData<int>(); + auto attribute = *sender->getUserData<ESM::Attribute::AttributeID>(); - std::vector<int>::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute); + auto found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute); if (found != mSpentAttributes.end()) mSpentAttributes.erase(found); else diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index 790db888cb..f3a80ebb9d 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -1,6 +1,8 @@ #ifndef MWGUI_LEVELUPDIALOG_H #define MWGUI_LEVELUPDIALOG_H +#include <components/esm/attr.hpp> + #include "windowbase.hpp" namespace MWGui @@ -14,23 +16,26 @@ namespace MWGui void onOpen() override; private: + struct Widgets + { + MyGUI::Button* mButton; + MyGUI::TextBox* mValue; + MyGUI::TextBox* mMultiplier; + }; MyGUI::Button* mOkButton; MyGUI::ImageBox* mClassImage; MyGUI::TextBox* mLevelText; MyGUI::EditBox* mLevelDescription; MyGUI::Widget* mCoinBox; - MyGUI::Widget* mAssignWidget; + MyGUI::ScrollView* mAssignWidget; - std::vector<MyGUI::Button*> mAttributes; - std::vector<MyGUI::TextBox*> mAttributeValues; - std::vector<MyGUI::TextBox*> mAttributeMultipliers; + std::map<ESM::Attribute::AttributeID, Widgets> mAttributeWidgets; std::vector<MyGUI::ImageBox*> mCoins; - std::vector<int> mSpentAttributes; + std::vector<ESM::Attribute::AttributeID> mSpentAttributes; unsigned int mCoinCount; - static const unsigned int sMaxCoins; void onOkButtonClicked(MyGUI::Widget* sender); void onAttributeClicked(MyGUI::Widget* sender); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 8887b9eb4f..39ab84246d 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -76,14 +76,22 @@ namespace MWGui // Setup attributes - Widgets::MWAttributePtr widget; + MyGUI::Widget* attributes = getWidget("Attributes"); const auto& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::Attribute>(); + MyGUI::IntCoord coord{ 8, 4, 250, 18 }; for (const ESM::Attribute& attribute : store) { - getWidget(widget, std::string("Attribute").append(1, '0' + attribute.mId)); + auto* widget + = attributes->createWidget<Widgets::MWAttribute>("MW_StatNameValue", coord, MyGUI::Align::Default); mAttributeWidgets.emplace(attribute.mId, widget); + widget->setUserString("ToolTipType", "Layout"); + widget->setUserString("ToolTipLayout", "AttributeToolTip"); + widget->setUserString("Caption_AttributeName", attribute.mName); + widget->setUserString("Caption_AttributeDescription", attribute.mDescription); + widget->setUserString("ImageTexture_AttributeImage", attribute.mIcon); widget->setAttributeId(attribute.mId); widget->setAttributeValue(Widgets::MWAttribute::AttributeValue()); + coord.top += coord.height; } // Setup skills @@ -193,7 +201,7 @@ namespace MWGui void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value) { - std::map<int, Widgets::MWAttributePtr>::iterator attr = mAttributeWidgets.find(static_cast<int>(attributeId)); + auto attr = mAttributeWidgets.find(attributeId); if (attr == mAttributeWidgets.end()) return; @@ -394,9 +402,9 @@ namespace MWGui if (!mRaceId.empty()) race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(mRaceId); - int attributes[ESM::Attribute::Length]; - for (int i = 0; i < ESM::Attribute::Length; ++i) - attributes[i] = mAttributeWidgets[i]->getAttributeValue().getBase(); + std::map<ESM::Attribute::AttributeID, MWMechanics::AttributeValue> attributes; + for (const auto& [key, value] : mAttributeWidgets) + attributes[key] = value->getAttributeValue(); std::vector<ESM::RefId> selectedSpells = MWMechanics::autoCalcPlayerSpells(mSkillValues, attributes, race); for (ESM::RefId& spellId : selectedSpells) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index dcb63d4d05..fe2a509fa6 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -90,7 +90,7 @@ namespace MWGui Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; - std::map<int, Widgets::MWAttributePtr> mAttributeWidgets; + std::map<ESM::Attribute::AttributeID, Widgets::MWAttributePtr> mAttributeWidgets; std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills; std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues; diff --git a/apps/openmw/mwgui/statswatcher.cpp b/apps/openmw/mwgui/statswatcher.cpp index dab6b9bf82..b18073f7ad 100644 --- a/apps/openmw/mwgui/statswatcher.cpp +++ b/apps/openmw/mwgui/statswatcher.cpp @@ -35,14 +35,16 @@ namespace MWGui if (mWatched.isEmpty()) return; + const auto& store = MWBase::Environment::get().getESMStore(); MWBase::WindowManager* winMgr = MWBase::Environment::get().getWindowManager(); const MWMechanics::NpcStats& stats = mWatched.getClass().getNpcStats(mWatched); - for (int i = 0; i < ESM::Attribute::Length; ++i) + for (const ESM::Attribute& attribute : store->get<ESM::Attribute>()) { - if (stats.getAttribute(i) != mWatchedAttributes[i] || mWatchedStatsEmpty) + const auto& value = stats.getAttribute(attribute.mId); + if (value != mWatchedAttributes[attribute.mId] || mWatchedStatsEmpty) { - mWatchedAttributes[i] = stats.getAttribute(i); - setValue("AttribVal" + std::to_string(i + 1), stats.getAttribute(i)); + mWatchedAttributes[attribute.mId] = value; + setValue(attribute.mId, value); } } @@ -83,7 +85,7 @@ namespace MWGui } } - for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>()) + for (const ESM::Skill& skill : store->get<ESM::Skill>()) { const auto& value = stats.getSkill(skill.mId); if (value != mWatchedSkills[skill.mId] || mWatchedStatsEmpty) @@ -112,16 +114,14 @@ namespace MWGui if (watchedRecord->mRace != mWatchedRace || mWatchedStatsEmpty) { mWatchedRace = watchedRecord->mRace; - const ESM::Race* race - = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(watchedRecord->mRace); + const ESM::Race* race = store->get<ESM::Race>().find(watchedRecord->mRace); setValue("race", race->mName); } if (watchedRecord->mClass != mWatchedClass || mWatchedStatsEmpty) { mWatchedClass = watchedRecord->mClass; - const ESM::Class* cls - = MWBase::Environment::get().getESMStore()->get<ESM::Class>().find(watchedRecord->mClass); + const ESM::Class* cls = store->get<ESM::Class>().find(watchedRecord->mClass); setValue("class", cls->mName); size_t size = cls->mData.mSkills.size(); @@ -151,7 +151,7 @@ namespace MWGui mListeners.erase(listener); } - void StatsWatcher::setValue(std::string_view id, const MWMechanics::AttributeValue& value) + void StatsWatcher::setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) { for (StatsListener* listener : mListeners) listener->setValue(id, value); diff --git a/apps/openmw/mwgui/statswatcher.hpp b/apps/openmw/mwgui/statswatcher.hpp index e7339294da..d4a9e54243 100644 --- a/apps/openmw/mwgui/statswatcher.hpp +++ b/apps/openmw/mwgui/statswatcher.hpp @@ -19,7 +19,7 @@ namespace MWGui virtual ~StatsListener() = default; /// Set value for the given ID. - virtual void setValue(std::string_view id, const MWMechanics::AttributeValue& value) {} + virtual void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) {} virtual void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) {} virtual void setValue(std::string_view, const std::string& value) {} virtual void setValue(std::string_view, int value) {} @@ -31,7 +31,7 @@ namespace MWGui { MWWorld::Ptr mWatched; - MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length]; + std::map<ESM::Attribute::AttributeID, MWMechanics::AttributeValue> mWatchedAttributes; std::map<ESM::RefId, MWMechanics::SkillValue> mWatchedSkills; MWMechanics::DynamicStat<float> mWatchedHealth; @@ -50,7 +50,7 @@ namespace MWGui std::set<StatsListener*> mListeners; - void setValue(std::string_view id, const MWMechanics::AttributeValue& value); + void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value); void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value); void setValue(std::string_view id, const std::string& value); void setValue(std::string_view id, int value); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 4bd82faa70..76c1b065dd 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -1,5 +1,6 @@ #include "statswindow.hpp" +#include <MyGUI_Button.h> #include <MyGUI_Gui.h> #include <MyGUI_ImageBox.h> #include <MyGUI_InputManager.h> @@ -43,15 +44,26 @@ namespace MWGui , mMinFullWidth(mMainWidget->getSize().width) { - const char* names[][2] = { { "Attrib1", "sAttributeStrength" }, { "Attrib2", "sAttributeIntelligence" }, - { "Attrib3", "sAttributeWillpower" }, { "Attrib4", "sAttributeAgility" }, { "Attrib5", "sAttributeSpeed" }, - { "Attrib6", "sAttributeEndurance" }, { "Attrib7", "sAttributePersonality" }, - { "Attrib8", "sAttributeLuck" }, { 0, 0 } }; - const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); - for (int i = 0; names[i][0]; ++i) + MyGUI::Widget* attributeView = getWidget("AttributeView"); + MyGUI::IntCoord coord{ 0, 0, 204, 18 }; + const MyGUI::Align alignment = MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch; + for (const ESM::Attribute& attribute : store.get<ESM::Attribute>()) { - setText(names[i][0], store.get<ESM::GameSetting>().find(names[i][1])->mValue.getString()); + auto* box = attributeView->createWidget<MyGUI::Button>({}, coord, alignment); + box->setUserString("ToolTipType", "Layout"); + box->setUserString("ToolTipLayout", "AttributeToolTip"); + box->setUserString("Caption_AttributeName", attribute.mName); + box->setUserString("Caption_AttributeDescription", attribute.mDescription); + box->setUserString("ImageTexture_AttributeImage", attribute.mIcon); + coord.top += coord.height; + auto* name = box->createWidget<MyGUI::TextBox>("SandText", { 0, 0, 160, 18 }, alignment); + name->setNeedMouseFocus(false); + name->setCaption(attribute.mName); + auto* value = box->createWidget<MyGUI::TextBox>( + "SandTextRight", { 160, 0, 44, 18 }, MyGUI::Align::Right | MyGUI::Align::Top); + value->setNeedMouseFocus(false); + mAttributeWidgets.emplace(attribute.mId, value); } getWidget(mSkillView, "SkillView"); @@ -143,37 +155,20 @@ namespace MWGui mMainWidget->castType<MyGUI::Window>()->setCaption(playerName); } - void StatsWindow::setValue(std::string_view id, const MWMechanics::AttributeValue& value) + void StatsWindow::setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) { - static const char* ids[] = { - "AttribVal1", - "AttribVal2", - "AttribVal3", - "AttribVal4", - "AttribVal5", - "AttribVal6", - "AttribVal7", - "AttribVal8", - nullptr, - }; - - for (int i = 0; ids[i]; ++i) - if (ids[i] == id) - { - setText(id, std::to_string(static_cast<int>(value.getModified()))); - - MyGUI::TextBox* box; - getWidget(box, id); - - if (value.getModified() > value.getBase()) - box->_setWidgetState("increased"); - else if (value.getModified() < value.getBase()) - box->_setWidgetState("decreased"); - else - box->_setWidgetState("normal"); - - break; - } + auto it = mAttributeWidgets.find(id); + if (it != mAttributeWidgets.end()) + { + MyGUI::TextBox* box = it->second; + box->setCaption(std::to_string(static_cast<int>(value.getModified()))); + if (value.getModified() > value.getBase()) + box->_setWidgetState("increased"); + else if (value.getModified() < value.getBase()) + box->_setWidgetState("decreased"); + else + box->_setWidgetState("normal"); + } } void StatsWindow::setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index ca7a5a7e07..92926c1180 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -3,6 +3,7 @@ #include "statswatcher.hpp" #include "windowpinnablebase.hpp" +#include <components/esm/attr.hpp> #include <components/esm/refid.hpp> namespace MWGui @@ -21,7 +22,7 @@ namespace MWGui void setPlayerName(const std::string& playerName); /// Set value for the given ID. - void setValue(std::string_view id, const MWMechanics::AttributeValue& value) override; + void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) override; void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override; void setValue(std::string_view id, const std::string& value) override; void setValue(std::string_view id, int value) override; @@ -67,6 +68,7 @@ namespace MWGui std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills; std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues; + std::map<ESM::Attribute::AttributeID, MyGUI::TextBox*> mAttributeWidgets; std::map<ESM::RefId, std::pair<MyGUI::TextBox*, MyGUI::TextBox*>> mSkillWidgetMap; std::map<std::string, MyGUI::Widget*> mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 6867b9114d..7fa58589df 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -823,7 +823,7 @@ namespace MWGui widget->setUserString("ImageTexture_SkillNoProgressImage", skill->mIcon); } - void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) + void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, ESM::Attribute::AttributeID attributeId) { const ESM::Attribute* attribute = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>().search(attributeId); diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 8246b0ca3b..4fa0803eaa 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -94,7 +94,7 @@ namespace MWGui // these do not create an actual tooltip, but they fill in the data that is required so the tooltip // system knows what to show in case this widget is hovered static void createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId); - static void createAttributeToolTip(MyGUI::Widget* widget, int attributeId); + static void createAttributeToolTip(MyGUI::Widget* widget, ESM::Attribute::AttributeID attributeId); static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId); static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId); static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 785a6b48c2..90c092dd79 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -150,7 +150,8 @@ namespace MWGui } // You can not train a skill above its governing attribute - if (pcStats.getSkill(skill->mId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase()) + if (pcStats.getSkill(skill->mId).getBase() + >= pcStats.getAttribute(ESM::Attribute::AttributeID(skill->mData.mAttribute)).getBase()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}"); return; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 194fa972af..de4f954438 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -102,13 +102,13 @@ namespace MWGui::Widgets /* MWAttribute */ MWAttribute::MWAttribute() - : mId(-1) + : mId(ESM::Attribute::Length) , mAttributeNameWidget(nullptr) , mAttributeValueWidget(nullptr) { } - void MWAttribute::setAttributeId(int attributeId) + void MWAttribute::setAttributeId(ESM::Attribute::AttributeID attributeId) { mId = attributeId; updateWidgets(); @@ -129,17 +129,15 @@ namespace MWGui::Widgets { if (mAttributeNameWidget) { - if (mId < 0 || mId >= 8) + const ESM::Attribute* attribute + = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>().search(mId); + if (!attribute) { mAttributeNameWidget->setCaption({}); } else { - static const std::string_view attributes[8] - = { "sAttributeStrength", "sAttributeIntelligence", "sAttributeWillpower", "sAttributeAgility", - "sAttributeSpeed", "sAttributeEndurance", "sAttributePersonality", "sAttributeLuck" }; - MyGUI::UString name = toUString( - MWBase::Environment::get().getWindowManager()->getGameSettingString(attributes[mId], {})); + MyGUI::UString name = toUString(attribute->mName); mAttributeNameWidget->setCaption(name); } } @@ -156,8 +154,6 @@ namespace MWGui::Widgets } } - MWAttribute::~MWAttribute() {} - void MWAttribute::initialiseOverride() { Base::initialiseOverride(); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 90e9680a90..8e45b37f2d 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -7,6 +7,7 @@ #include <MyGUI_TextBox.h> #include <MyGUI_Widget.h> +#include <components/esm/attr.hpp> #include <components/esm/refid.hpp> #include <components/esm3/effectlist.hpp> #include <components/esm3/loadskil.hpp> @@ -32,8 +33,6 @@ namespace MWGui { class MWEffectList; - void fixTexturePath(std::string& path); - struct SpellEffectParams { SpellEffectParams() @@ -139,10 +138,10 @@ namespace MWGui typedef MWMechanics::AttributeValue AttributeValue; - void setAttributeId(int attributeId); + void setAttributeId(ESM::Attribute::AttributeID attributeId); void setAttributeValue(const AttributeValue& value); - int getAttributeId() const { return mId; } + ESM::Attribute::AttributeID getAttributeId() const { return mId; } const AttributeValue& getAttributeValue() const { return mValue; } // Events @@ -154,7 +153,7 @@ namespace MWGui EventHandle_AttributeVoid eventClicked; protected: - virtual ~MWAttribute(); + ~MWAttribute() override = default; void initialiseOverride() override; @@ -163,7 +162,7 @@ namespace MWGui private: void updateWidgets(); - int mId; + ESM::Attribute::AttributeID mId; AttributeValue mValue; MyGUI::TextBox* mAttributeNameWidget; MyGUI::TextBox* mAttributeValueWidget; diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 1687b202d7..da4d2a4614 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -182,9 +182,10 @@ namespace MWLua template <class G> sol::object get(const Context& context, std::string_view prop, G getter) const { + auto id = static_cast<ESM::Attribute::AttributeID>(mIndex); return getValue( - context, mObject, &AttributeStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) { - return (ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex).*getter)(); + context, mObject, &AttributeStat::setValue, mIndex, prop, [id, getter](const MWWorld::Ptr& ptr) { + return (ptr.getClass().getCreatureStats(ptr).getAttribute(id).*getter)(); }); } @@ -213,9 +214,9 @@ namespace MWLua static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) { - int index = std::get<int>(i); + auto id = static_cast<ESM::Attribute::AttributeID>(std::get<int>(i)); auto& stats = ptr.getClass().getCreatureStats(ptr); - auto stat = stats.getAttribute(index); + auto stat = stats.getAttribute(id); float floatValue = LuaUtil::cast<float>(value); if (prop == "base") stat.setBase(floatValue); @@ -226,7 +227,7 @@ namespace MWLua } else if (prop == "modifier") stat.setModifier(floatValue); - stats.setAttribute(index, stat); + stats.setAttribute(id, stat); } }; diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 66ed8c7bab..64cc4e8597 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -27,13 +27,13 @@ namespace MWMechanics ESM::RefId mWeakestSpell; }; - std::vector<ESM::RefId> autoCalcNpcSpells( - const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race) + std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race) { const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>(); static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat(); - float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; + float baseMagicka = fNPCbaseMagickaMult * actorAttributes.at(ESM::Attribute::Intelligence).getBase(); static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; @@ -148,15 +148,15 @@ namespace MWMechanics return selectedSpells; } - std::vector<ESM::RefId> autoCalcPlayerSpells( - const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race) + std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race) { const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat(); - float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; + float baseMagicka = fPCbaseMagickaMult * actorAttributes.at(ESM::Attribute::Intelligence).getBase(); bool reachedLimit = false; const ESM::Spell* weakestSpell = nullptr; int minCost = std::numeric_limits<int>::max(); @@ -227,8 +227,8 @@ namespace MWMechanics return selectedSpells; } - bool attrSkillCheck( - const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes) + bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes) { for (const auto& spellEffect : spell->mEffects.mList) { @@ -250,8 +250,8 @@ namespace MWMechanics if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)) { - assert(spellEffect.mAttribute >= 0 && spellEffect.mAttribute < ESM::Attribute::Length); - if (actorAttributes[spellEffect.mAttribute] < iAutoSpellAttSkillMin) + auto found = actorAttributes.find(ESM::Attribute::AttributeID(spellEffect.mAttribute)); + if (found == actorAttributes.end() || found->second.getBase() < iAutoSpellAttSkillMin) return false; } } @@ -313,7 +313,7 @@ namespace MWMechanics } float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, - const int* actorAttributes, int effectiveSchool) + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, int effectiveSchool) { if (spell->mData.mType != ESM::Spell::ST_Spell) return 100.f; @@ -334,7 +334,8 @@ namespace MWMechanics spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this float castChance = skillTerm - MWMechanics::calcSpellCost(*spell) - + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck]; + + 0.2f * actorAttributes.at(ESM::Attribute::Willpower).getBase() + + 0.1f * actorAttributes.at(ESM::Attribute::Luck).getBase(); return castChance; } } diff --git a/apps/openmw/mwmechanics/autocalcspell.hpp b/apps/openmw/mwmechanics/autocalcspell.hpp index 4c445b01ab..09efb34bdb 100644 --- a/apps/openmw/mwmechanics/autocalcspell.hpp +++ b/apps/openmw/mwmechanics/autocalcspell.hpp @@ -19,22 +19,22 @@ namespace MWMechanics /// Contains algorithm for calculating an NPC's spells based on stats /// @note We might want to move this code to a component later, so the editor can use it for preview purposes - std::vector<ESM::RefId> autoCalcNpcSpells( - const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race); + std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race); - std::vector<ESM::RefId> autoCalcPlayerSpells( - const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race); + std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race); // Helpers - bool attrSkillCheck( - const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes); + bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes); void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, int& effectiveSchool, float& skillTerm); float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, - const int* actorAttributes, int effectiveSchool); + const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, int effectiveSchool); } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 44e1f4d2e1..756ac1ef39 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -46,6 +46,10 @@ namespace MWMechanics , mLevel(0) , mAttackingOrSpell(false) { + for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>()) + { + mAttributes.emplace(attribute.mId, AttributeValue{}); + } } const AiSequence& CreatureStats::getAiSequence() const @@ -74,13 +78,9 @@ namespace MWMechanics return fFatigueBase - fFatigueMult * (1 - normalised); } - const AttributeValue& CreatureStats::getAttribute(int index) const + const AttributeValue& CreatureStats::getAttribute(ESM::Attribute::AttributeID id) const { - if (index < 0 || index > 7) - { - throw std::runtime_error("attribute index is out of range"); - } - return mAttributes[index]; + return mAttributes.at(id); } const DynamicStat<float>& CreatureStats::getHealth() const @@ -147,30 +147,25 @@ namespace MWMechanics return mMagicEffects; } - void CreatureStats::setAttribute(int index, float base) + void CreatureStats::setAttribute(ESM::Attribute::AttributeID id, float base) { - AttributeValue current = getAttribute(index); + AttributeValue current = getAttribute(id); current.setBase(base); - setAttribute(index, current); + setAttribute(id, current); } - void CreatureStats::setAttribute(int index, const AttributeValue& value) + void CreatureStats::setAttribute(ESM::Attribute::AttributeID id, const AttributeValue& value) { - if (index < 0 || index > 7) - { - throw std::runtime_error("attribute index is out of range"); - } - - const AttributeValue& currentValue = mAttributes[index]; + const AttributeValue& currentValue = mAttributes.at(id); if (value != currentValue) { - mAttributes[index] = value; + mAttributes[id] = value; - if (index == ESM::Attribute::Intelligence) + if (id == ESM::Attribute::Intelligence) recalculateMagicka(); - else if (index == ESM::Attribute::Strength || index == ESM::Attribute::Willpower - || index == ESM::Attribute::Agility || index == ESM::Attribute::Endurance) + else if (id == ESM::Attribute::Strength || id == ESM::Attribute::Willpower || id == ESM::Attribute::Agility + || id == ESM::Attribute::Endurance) { float strength = getAttribute(ESM::Attribute::Strength).getModified(); float willpower = getAttribute(ESM::Attribute::Willpower).getModified(); @@ -535,10 +530,10 @@ namespace MWMechanics void CreatureStats::writeState(ESM::CreatureStats& state) const { - for (int i = 0; i < ESM::Attribute::Length; ++i) - mAttributes[i].writeState(state.mAttributes[i]); + for (size_t i = 0; i < state.mAttributes.size(); ++i) + getAttribute(static_cast<ESM::Attribute::AttributeID>(i)).writeState(state.mAttributes[i]); - for (int i = 0; i < 3; ++i) + for (size_t i = 0; i < state.mDynamic.size(); ++i) mDynamic[i].writeState(state.mDynamic[i]); state.mTradeTime = mLastRestock.toEsm(); @@ -582,7 +577,7 @@ namespace MWMechanics state.mSummonGraveyard = mSummonGraveyard; state.mHasAiSettings = true; - for (int i = 0; i < 4; ++i) + for (size_t i = 0; i < state.mAiSettings.size(); ++i) mAiSettings[i].writeState(state.mAiSettings[i]); state.mMissingACDT = false; @@ -592,10 +587,10 @@ namespace MWMechanics { if (!state.mMissingACDT) { - for (int i = 0; i < ESM::Attribute::Length; ++i) - mAttributes[i].readState(state.mAttributes[i]); + for (size_t i = 0; i < state.mAttributes.size(); ++i) + mAttributes[static_cast<ESM::Attribute::AttributeID>(i)].readState(state.mAttributes[i]); - for (int i = 0; i < 3; ++i) + for (size_t i = 0; i < state.mDynamic.size(); ++i) mDynamic[i].readState(state.mDynamic[i]); mGoldPool = state.mGoldPool; @@ -636,7 +631,7 @@ namespace MWMechanics mSummonGraveyard = state.mSummonGraveyard; if (state.mHasAiSettings) - for (int i = 0; i < 4; ++i) + for (size_t i = 0; i < state.mAiSettings.size(); ++i) mAiSettings[i].readState(state.mAiSettings[i]); if (state.mRecalcDynamicStats) recalculateMagicka(); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 9d1830fe03..ffe6e40a91 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -40,7 +40,7 @@ namespace MWMechanics { static int sActorId; DrawState mDrawState; - AttributeValue mAttributes[ESM::Attribute::Length]; + std::map<ESM::Attribute::AttributeID, AttributeValue> mAttributes; DynamicStat<float> mDynamic[3]; // health, magicka, fatigue Spells mSpells; ActiveSpells mActiveSpells; @@ -113,7 +113,7 @@ namespace MWMechanics /// @return total fall height float land(bool isPlayer = false); - const AttributeValue& getAttribute(int index) const; + const AttributeValue& getAttribute(ESM::Attribute::AttributeID id) const; const DynamicStat<float>& getHealth() const; @@ -139,9 +139,9 @@ namespace MWMechanics MagicEffects& getMagicEffects(); - void setAttribute(int index, const AttributeValue& value); + void setAttribute(ESM::Attribute::AttributeID id, const AttributeValue& value); // Shortcut to set only the base - void setAttribute(int index, float base); + void setAttribute(ESM::Attribute::AttributeID id, float base); void setHealth(const DynamicStat<float>& value); @@ -293,6 +293,8 @@ namespace MWMechanics bool wasTeleported() const { return mTeleported; } void setTeleported(bool v) { mTeleported = v; } + + const std::map<ESM::Attribute::AttributeID, AttributeValue> getAttributes() const { return mAttributes; } }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 30154785ea..c2cca5d5c1 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -148,11 +148,11 @@ namespace MWMechanics bool male = (player->mFlags & ESM::NPC::Female) == 0; - for (int i = 0; i < ESM::Attribute::Length; ++i) + for (const ESM::Attribute& attribute : esmStore.get<ESM::Attribute>()) { - const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; + const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[attribute.mId]; - creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); + creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale); } for (const ESM::Skill& skill : esmStore.get<ESM::Skill>()) @@ -195,7 +195,8 @@ namespace MWMechanics { if (attribute >= 0 && attribute < ESM::Attribute::Length) { - creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); + auto id = static_cast<ESM::Attribute::AttributeID>(attribute); + creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10); } } @@ -223,12 +224,10 @@ namespace MWMechanics if (mRaceSelected) race = esmStore.get<ESM::Race>().find(player->mRace); - int attributes[ESM::Attribute::Length]; - for (int i = 0; i < ESM::Attribute::Length; ++i) - attributes[i] = npcStats.getAttribute(i).getBase(); npcStats.updateHealth(); - std::vector<ESM::RefId> selectedSpells = autoCalcPlayerSpells(npcStats.getSkills(), attributes, race); + std::vector<ESM::RefId> selectedSpells + = autoCalcPlayerSpells(npcStats.getSkills(), npcStats.getAttributes(), race); for (const ESM::RefId& spell : selectedSpells) creatureStats.getSpells().add(spell); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 337757ebfa..7729432414 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -28,7 +28,6 @@ MWMechanics::NpcStats::NpcStats() , mTimeToStartDrowning(-1.0) // set breath to special value, it will be replaced during actor update , mIsWerewolf(false) { - mSkillIncreases.resize(ESM::Attribute::Length, 0); mSpecIncreases.resize(3, 0); for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>()) mSkills.emplace(skill.mId, SkillValue{}); @@ -246,7 +245,7 @@ void MWMechanics::NpcStats::increaseSkill(ESM::RefId id, const ESM::Class& class } } - mSkillIncreases[skill->mData.mAttribute] += increase; + mSkillIncreases[ESM::Attribute::AttributeID(skill->mData.mAttribute)] += increase; mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger(); @@ -286,8 +285,7 @@ void MWMechanics::NpcStats::levelUp() mLevelProgress -= gmst.find("iLevelUpTotal")->mValue.getInteger(); mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console - for (int i = 0; i < ESM::Attribute::Length; ++i) - mSkillIncreases[i] = 0; + mSkillIncreases.clear(); const float endurance = getAttribute(ESM::Attribute::Endurance).getBase(); @@ -312,14 +310,12 @@ void MWMechanics::NpcStats::updateHealth() setHealth(floor(0.5f * (strength + endurance))); } -int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const +int MWMechanics::NpcStats::getLevelupAttributeMultiplier(ESM::Attribute::AttributeID attribute) const { - int num = mSkillIncreases[attribute]; - - if (num == 0) + auto it = mSkillIncreases.find(attribute); + if (it == mSkillIncreases.end() || it->second == 0) return 1; - - num = std::min(10, num); + int num = std::min(10, it->second); // iLevelUp01Mult - iLevelUp10Mult std::stringstream gmst; @@ -488,8 +484,9 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const state.mWerewolfKills = mWerewolfKills; state.mLevelProgress = mLevelProgress; - for (size_t i = 0; i < state.mSkillIncrease.size(); ++i) - state.mSkillIncrease[i] = mSkillIncreases[i]; + state.mSkillIncrease.fill(0); + for (const auto& [key, value] : mSkillIncreases) + state.mSkillIncrease[key] = value; for (size_t i = 0; i < state.mSpecIncreases.size(); ++i) state.mSpecIncreases[i] = mSpecIncreases[i]; @@ -538,7 +535,7 @@ void MWMechanics::NpcStats::readState(const ESM::NpcStats& state) mLevelProgress = state.mLevelProgress; for (size_t i = 0; i < state.mSkillIncrease.size(); ++i) - mSkillIncreases[i] = state.mSkillIncrease[i]; + mSkillIncreases[static_cast<ESM::Attribute::AttributeID>(i)] = state.mSkillIncrease[i]; for (size_t i = 0; i < state.mSpecIncreases.size(); ++i) mSpecIncreases[i] = state.mSpecIncreases[i]; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 9026553bb5..39db2dd30a 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -36,7 +36,8 @@ namespace MWMechanics std::set<ESM::RefId> mExpelled; std::map<ESM::RefId, int> mFactionReputation; int mLevelProgress; // 0-10 - std::vector<int> mSkillIncreases; // number of skill increases for each attribute (resets after leveling up) + std::map<ESM::Attribute::AttributeID, int> + mSkillIncreases; // number of skill increases for each attribute (resets after leveling up) std::vector<int> mSpecIncreases; // number of skill increases for each specialization (accumulates throughout // the entire game) std::set<ESM::RefId> mUsedIds; @@ -87,7 +88,7 @@ namespace MWMechanics int getLevelProgress() const; - int getLevelupAttributeMultiplier(int attribute) const; + int getLevelupAttributeMultiplier(ESM::Attribute::AttributeID attribute) const; int getSkillIncreasesForSpecialization(int spec) const; diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index b0120d91fb..2c410c842a 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -79,27 +79,30 @@ namespace void damageAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) { auto& creatureStats = target.getClass().getCreatureStats(target); - auto attr = creatureStats.getAttribute(effect.mArg); + auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); + auto attr = creatureStats.getAttribute(attribute); if (effect.mEffectId == ESM::MagicEffect::DamageAttribute) magnitude = std::min(attr.getModified(), magnitude); attr.damage(magnitude); - creatureStats.setAttribute(effect.mArg, attr); + creatureStats.setAttribute(attribute, attr); } void restoreAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) { auto& creatureStats = target.getClass().getCreatureStats(target); - auto attr = creatureStats.getAttribute(effect.mArg); + auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); + auto attr = creatureStats.getAttribute(attribute); attr.restore(magnitude); - creatureStats.setAttribute(effect.mArg, attr); + creatureStats.setAttribute(attribute, attr); } void fortifyAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) { auto& creatureStats = target.getClass().getCreatureStats(target); - auto attr = creatureStats.getAttribute(effect.mArg); + auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); + auto attr = creatureStats.getAttribute(attribute); attr.setModifier(attr.getModifier() + magnitude); - creatureStats.setAttribute(effect.mArg, attr); + creatureStats.setAttribute(attribute, attr); } void damageSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) @@ -739,9 +742,10 @@ namespace MWMechanics if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) { auto& creatureStats = target.getClass().getCreatureStats(target); - AttributeValue attr = creatureStats.getAttribute(effect.mArg); + auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); + AttributeValue attr = creatureStats.getAttribute(attribute); attr.setBase(attr.getBase() + effect.mMagnitude); - creatureStats.setAttribute(effect.mArg, attr); + creatureStats.setAttribute(attribute, attr); } else fortifyAttribute(target, effect, effect.mMagnitude); @@ -1202,9 +1206,10 @@ namespace MWMechanics if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) { auto& creatureStats = target.getClass().getCreatureStats(target); - AttributeValue attr = creatureStats.getAttribute(effect.mArg); + auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); + AttributeValue attr = creatureStats.getAttribute(attribute); attr.setBase(attr.getBase() - effect.mMagnitude); - creatureStats.setAttribute(effect.mArg, attr); + creatureStats.setAttribute(attribute, attr); } else fortifyAttribute(target, effect, -effect.mMagnitude); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index e2c0dad55f..9bd4495651 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -540,7 +540,11 @@ namespace MWMechanics case ESM::MagicEffect::DamageAttribute: case ESM::MagicEffect::DrainAttribute: if (!enemy.isEmpty() - && enemy.getClass().getCreatureStats(enemy).getAttribute(effect.mAttribute).getModified() <= 0) + && enemy.getClass() + .getCreatureStats(enemy) + .getAttribute(ESM::Attribute::AttributeID(effect.mAttribute)) + .getModified() + <= 0) return 0.f; { if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 9e2e1ce798..f533926604 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -247,23 +247,24 @@ namespace MWMechanics return; // Note: if target actor has the Restore attribute effects, stats will be restored. - for (std::vector<ESM::SpellState::PermanentSpellEffectInfo>::const_iterator effectIt = it->second.begin(); - effectIt != it->second.end(); ++effectIt) + for (const ESM::SpellState::PermanentSpellEffectInfo& info : it->second) { // Applied corprus effects are already in loaded stats modifiers - if (effectIt->mId == ESM::MagicEffect::FortifyAttribute) + if (info.mId == ESM::MagicEffect::FortifyAttribute) { - AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); - attr.setModifier(attr.getModifier() - effectIt->mMagnitude); - attr.damage(-effectIt->mMagnitude); - creatureStats->setAttribute(effectIt->mArg, attr); + auto id = static_cast<ESM::Attribute::AttributeID>(info.mArg); + AttributeValue attr = creatureStats->getAttribute(id); + attr.setModifier(attr.getModifier() - info.mMagnitude); + attr.damage(-info.mMagnitude); + creatureStats->setAttribute(id, attr); } - else if (effectIt->mId == ESM::MagicEffect::DrainAttribute) + else if (info.mId == ESM::MagicEffect::DrainAttribute) { - AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); - attr.setModifier(attr.getModifier() + effectIt->mMagnitude); - attr.damage(effectIt->mMagnitude); - creatureStats->setAttribute(effectIt->mArg, attr); + auto id = static_cast<ESM::Attribute::AttributeID>(info.mArg); + AttributeValue attr = creatureStats->getAttribute(id); + attr.setModifier(attr.getModifier() + info.mMagnitude); + attr.damage(info.mMagnitude); + creatureStats->setAttribute(id, attr); } } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 23c1083903..222820f3bf 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -109,10 +109,10 @@ namespace MWScript template <class R> class OpGetAttribute : public Interpreter::Opcode0 { - int mIndex; + ESM::Attribute::AttributeID mIndex; public: - OpGetAttribute(int index) + OpGetAttribute(ESM::Attribute::AttributeID index) : mIndex(index) { } @@ -130,10 +130,10 @@ namespace MWScript template <class R> class OpSetAttribute : public Interpreter::Opcode0 { - int mIndex; + ESM::Attribute::AttributeID mIndex; public: - OpSetAttribute(int index) + OpSetAttribute(ESM::Attribute::AttributeID index) : mIndex(index) { } @@ -154,10 +154,10 @@ namespace MWScript template <class R> class OpModAttribute : public Interpreter::Opcode0 { - int mIndex; + ESM::Attribute::AttributeID mIndex; public: - OpModAttribute(int index) + OpModAttribute(ESM::Attribute::AttributeID index) : mIndex(index) { } @@ -1322,17 +1322,18 @@ namespace MWScript { for (int i = 0; i < Compiler::Stats::numberOfAttributes; ++i) { - interpreter.installSegment5<OpGetAttribute<ImplicitRef>>(Compiler::Stats::opcodeGetAttribute + i, i); + auto id = static_cast<ESM::Attribute::AttributeID>(i); + interpreter.installSegment5<OpGetAttribute<ImplicitRef>>(Compiler::Stats::opcodeGetAttribute + i, id); interpreter.installSegment5<OpGetAttribute<ExplicitRef>>( - Compiler::Stats::opcodeGetAttributeExplicit + i, i); + Compiler::Stats::opcodeGetAttributeExplicit + i, id); - interpreter.installSegment5<OpSetAttribute<ImplicitRef>>(Compiler::Stats::opcodeSetAttribute + i, i); + interpreter.installSegment5<OpSetAttribute<ImplicitRef>>(Compiler::Stats::opcodeSetAttribute + i, id); interpreter.installSegment5<OpSetAttribute<ExplicitRef>>( - Compiler::Stats::opcodeSetAttributeExplicit + i, i); + Compiler::Stats::opcodeSetAttributeExplicit + i, id); - interpreter.installSegment5<OpModAttribute<ImplicitRef>>(Compiler::Stats::opcodeModAttribute + i, i); + interpreter.installSegment5<OpModAttribute<ImplicitRef>>(Compiler::Stats::opcodeModAttribute + i, id); interpreter.installSegment5<OpModAttribute<ExplicitRef>>( - Compiler::Stats::opcodeModAttributeExplicit + i, i); + Compiler::Stats::opcodeModAttributeExplicit + i, id); } for (int i = 0; i < Compiler::Stats::numberOfDynamics; ++i) diff --git a/apps/openmw/mwworld/magiceffects.cpp b/apps/openmw/mwworld/magiceffects.cpp index d3e6db10a7..51c902a8f6 100644 --- a/apps/openmw/mwworld/magiceffects.cpp +++ b/apps/openmw/mwworld/magiceffects.cpp @@ -43,8 +43,7 @@ namespace MWWorld ESM::CreatureStats::CorprusStats stats; stats.mNextWorsening = oldStats.mNextWorsening; - for (int i = 0; i < ESM::Attribute::Length; ++i) - stats.mWorsenings[i] = 0; + stats.mWorsenings.fill(0); for (auto& effect : spell->mEffects.mList) { @@ -179,8 +178,8 @@ namespace MWWorld { it->mNextWorsening = spell.second.mNextWorsening; int worsenings = 0; - for (int i = 0; i < ESM::Attribute::Length; ++i) - worsenings = std::max(spell.second.mWorsenings[i], worsenings); + for (const auto& worsening : spell.second.mWorsenings) + worsenings = std::max(worsening, worsenings); it->mWorsenings = worsenings; } } @@ -209,20 +208,19 @@ namespace MWWorld } } // Reset modifiers that were previously recalculated each frame - for (std::size_t i = 0; i < ESM::Attribute::Length; ++i) - creatureStats.mAttributes[i].mMod = 0.f; - for (std::size_t i = 0; i < 3; ++i) + for (auto& attribute : creatureStats.mAttributes) + attribute.mMod = 0.f; + for (auto& dynamic : creatureStats.mDynamic) { - auto& dynamic = creatureStats.mDynamic[i]; dynamic.mCurrent -= dynamic.mMod - dynamic.mBase; dynamic.mMod = 0.f; } - for (std::size_t i = 0; i < 4; ++i) - creatureStats.mAiSettings[i].mMod = 0.f; + for (auto& setting : creatureStats.mAiSettings) + setting.mMod = 0.f; if (npcStats) { - for (std::size_t i = 0; i < npcStats->mSkills.size(); ++i) - npcStats->mSkills[i].mMod = 0.f; + for (auto& skill : npcStats->mSkills) + skill.mMod = 0.f; } } @@ -230,9 +228,9 @@ namespace MWWorld // version or not void convertStats(ESM::CreatureStats& creatureStats) { - for (std::size_t i = 0; i < 3; ++i) - creatureStats.mDynamic[i].mMod = 0.f; - for (std::size_t i = 0; i < 4; ++i) - creatureStats.mAiSettings[i].mMod = 0.f; + for (auto& dynamic : creatureStats.mDynamic) + dynamic.mMod = 0.f; + for (auto& setting : creatureStats.mAiSettings) + setting.mMod = 0.f; } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index bb70c46aa0..ad0cc92ad8 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -61,8 +61,8 @@ namespace MWWorld for (size_t i = 0; i < mSaveSkills.size(); ++i) mSaveSkills[i] = stats.getSkill(ESM::Skill::indexToRefId(i)).getModified(); - for (int i = 0; i < ESM::Attribute::Length; ++i) - mSaveAttributes[i] = stats.getAttribute(i).getModified(); + for (size_t i = 0; i < mSaveAttributes.size(); ++i) + mSaveAttributes[i] = stats.getAttribute(static_cast<ESM::Attribute::AttributeID>(i)).getModified(); } void Player::restoreStats() @@ -79,12 +79,13 @@ namespace MWWorld skill.restore(skill.getDamage()); skill.setModifier(mSaveSkills[i] - skill.getBase()); } - for (int i = 0; i < ESM::Attribute::Length; ++i) + for (size_t i = 0; i < mSaveAttributes.size(); ++i) { - auto attribute = npcStats.getAttribute(i); + auto id = static_cast<ESM::Attribute::AttributeID>(i); + auto attribute = npcStats.getAttribute(id); attribute.restore(attribute.getDamage()); attribute.setModifier(mSaveAttributes[i] - attribute.getBase()); - npcStats.setAttribute(i, attribute); + npcStats.setAttribute(id, attribute); } } @@ -252,11 +253,7 @@ namespace MWWorld mLastKnownExteriorPosition = osg::Vec3f(0, 0, 0); mSaveSkills.fill(0.f); - - for (int i = 0; i < ESM::Attribute::Length; ++i) - { - mSaveAttributes[i] = 0.f; - } + mSaveAttributes.fill(0.f); mMarkedPosition.pos[0] = 0; mMarkedPosition.pos[1] = 0; @@ -291,7 +288,7 @@ namespace MWWorld else player.mHasMark = false; - for (int i = 0; i < ESM::Attribute::Length; ++i) + for (size_t i = 0; i < mSaveAttributes.size(); ++i) player.mSaveAttributes[i] = mSaveAttributes[i]; for (size_t i = 0; i < mSaveSkills.size(); ++i) player.mSaveSkills[i] = mSaveSkills[i]; @@ -329,7 +326,7 @@ namespace MWWorld mPlayer.load(player.mObject); - for (int i = 0; i < ESM::Attribute::Length; ++i) + for (size_t i = 0; i < mSaveAttributes.size(); ++i) mSaveAttributes[i] = player.mSaveAttributes[i]; for (size_t i = 0; i < mSaveSkills.size(); ++i) mSaveSkills[i] = player.mSaveSkills[i]; diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 12eadab10f..0d56833df9 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -52,7 +52,7 @@ namespace MWWorld // Saved stats prior to becoming a werewolf std::array<float, ESM::Skill::Length> mSaveSkills; - float mSaveAttributes[ESM::Attribute::Length]; + std::array<float, ESM::Attribute::Length> mSaveAttributes; bool mJumping; diff --git a/components/esm3/creaturestats.cpp b/components/esm3/creaturestats.cpp index 8a7625b9f0..9121bcb8f5 100644 --- a/components/esm3/creaturestats.cpp +++ b/components/esm3/creaturestats.cpp @@ -10,11 +10,11 @@ namespace ESM void CreatureStats::load(ESMReader& esm) { const bool intFallback = esm.getFormatVersion() <= MaxIntFallbackFormatVersion; - for (int i = 0; i < 8; ++i) - mAttributes[i].load(esm, intFallback); + for (auto& attribute : mAttributes) + attribute.load(esm, intFallback); - for (int i = 0; i < 3; ++i) - mDynamic[i].load(esm); + for (auto& dynamic : mDynamic) + dynamic.load(esm); mGoldPool = 0; esm.getHNOT(mGoldPool, "GOLD"); @@ -154,8 +154,8 @@ namespace ESM if (mHasAiSettings) { - for (int i = 0; i < 4; ++i) - mAiSettings[i].load(esm); + for (auto& setting : mAiSettings) + setting.load(esm); } while (esm.isNextSub("CORP")) @@ -179,11 +179,11 @@ namespace ESM void CreatureStats::save(ESMWriter& esm) const { - for (int i = 0; i < 8; ++i) - mAttributes[i].save(esm); + for (const auto& attribute : mAttributes) + attribute.save(esm); - for (int i = 0; i < 3; ++i) - mDynamic[i].save(esm); + for (const auto& dynamic : mDynamic) + dynamic.save(esm); if (mGoldPool) esm.writeHNT("GOLD", mGoldPool); @@ -268,8 +268,8 @@ namespace ESM esm.writeHNT("AISE", mHasAiSettings); if (mHasAiSettings) { - for (int i = 0; i < 4; ++i) - mAiSettings[i].save(esm); + for (const auto& setting : mAiSettings) + setting.save(esm); } if (mMissingACDT) esm.writeHNT("NOAC", mMissingACDT); diff --git a/components/esm3/creaturestats.hpp b/components/esm3/creaturestats.hpp index 1a9f087e39..d9df6200af 100644 --- a/components/esm3/creaturestats.hpp +++ b/components/esm3/creaturestats.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_ESM_CREATURESTATS_H #define OPENMW_ESM_CREATURESTATS_H +#include <array> #include <map> #include <string> #include <vector> @@ -26,19 +27,19 @@ namespace ESM { struct CorprusStats { - int mWorsenings[Attribute::Length]; + std::array<int, Attribute::Length> mWorsenings; TimeStamp mNextWorsening; }; - StatState<float> mAttributes[Attribute::Length]; - StatState<float> mDynamic[3]; + std::array<StatState<float>, Attribute::Length> mAttributes; + std::array<StatState<float>, 3> mDynamic; MagicEffects mMagicEffects; AiSequence::AiSequence mAiSequence; bool mHasAiSettings; - StatState<int> mAiSettings[4]; + std::array<StatState<int>, 4> mAiSettings; std::map<SummonKey, int> mSummonedCreatureMap; std::multimap<int, int> mSummonedCreatures; diff --git a/files/data/mygui/openmw_chargen_review.layout b/files/data/mygui/openmw_chargen_review.layout index 1f158dc064..5f6034bf43 100644 --- a/files/data/mygui/openmw_chargen_review.layout +++ b/files/data/mygui/openmw_chargen_review.layout @@ -46,64 +46,7 @@ </Widget> <!-- Player attributes --> - <Widget type="Widget" skin="MW_Box" position="8 224 265 156"> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 4 250 18" name="Attribute0"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeStrength}"/> - <UserString key="Caption_AttributeDescription" value="#{sStrDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_strength.dds"/> - </Widget> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 22 250 18" name="Attribute1"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeIntelligence}"/> - <UserString key="Caption_AttributeDescription" value="#{sIntDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_int.dds"/> - </Widget> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 40 250 18" name="Attribute2"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeWillpower}"/> - <UserString key="Caption_AttributeDescription" value="#{sWilDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_wilpower.dds"/> - </Widget> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 58 250 18" name="Attribute3"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeAgility}"/> - <UserString key="Caption_AttributeDescription" value="#{sAgiDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_agility.dds"/> - </Widget> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 76 250 18" name="Attribute4"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeSpeed}"/> - <UserString key="Caption_AttributeDescription" value="#{sSpdDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_speed.dds"/> - </Widget> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 94 250 18" name="Attribute5"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeEndurance}"/> - <UserString key="Caption_AttributeDescription" value="#{sEndDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_endurance.dds"/> - </Widget> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 112 250 18" name="Attribute6"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributePersonality}"/> - <UserString key="Caption_AttributeDescription" value="#{sPerDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_personality.dds"/> - </Widget> - <Widget type="MWAttribute" skin="MW_StatNameValue" position="8 130 250 18" name="Attribute7"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeLuck}"/> - <UserString key="Caption_AttributeDescription" value="#{sLucDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_luck.dds"/> - </Widget> - </Widget> + <Widget type="Widget" skin="MW_Box" position="8 224 265 156" name="Attributes" /> <!-- Player Skills --> <Widget type="Widget" skin="MW_Box" position="281 7 244 372" align="Left VStretch" name="Skills"> diff --git a/files/data/mygui/openmw_chargen_select_attribute.layout b/files/data/mygui/openmw_chargen_select_attribute.layout index 621ba90817..7e475291f9 100644 --- a/files/data/mygui/openmw_chargen_select_attribute.layout +++ b/files/data/mygui/openmw_chargen_select_attribute.layout @@ -10,14 +10,9 @@ </Widget> <!-- Attribute list --> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 28 216 18" name="Attribute0" align="Left Top"/> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 46 216 18" name="Attribute1" align="Left Top"/> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 64 216 18" name="Attribute2" align="Left Top"/> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 82 216 18" name="Attribute3" align="Left Top"/> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 100 216 18" name="Attribute4" align="Left Top"/> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 118 216 18" name="Attribute5" align="Left Top"/> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 136 216 18" name="Attribute6" align="Left Top"/> - <Widget type="MWAttribute" skin="MW_StatNameButtonC" position="0 154 216 18" name="Attribute7" align="Left Top"/> + <Widget type="ScrollView" skin="MW_ScrollView" position="0 28 216 144" name="Attributes" align="Left Top"> + <Property key="CanvasAlign" value="Left"/> + </Widget> <!-- Dialog buttons --> <Widget type="HBox" position="0 175 216 28"> diff --git a/files/data/mygui/openmw_levelup_dialog.layout b/files/data/mygui/openmw_levelup_dialog.layout index ef161d7f58..498fa97c80 100644 --- a/files/data/mygui/openmw_levelup_dialog.layout +++ b/files/data/mygui/openmw_levelup_dialog.layout @@ -27,131 +27,10 @@ <UserString key="VStretch" value="false"/> </Widget> - <Widget type="Widget" skin="" position="0 280 420 84" name="AssignWidget"> + <Widget type="ScrollView" skin="MW_ScrollView" position="0 280 420 84" name="AssignWidget"> <UserString key="HStretch" value="false"/> <UserString key="VStretch" value="false"/> - - <Widget type="TextBox" skin="SandTextVCenter" position="32 0 100 20" name="AttribMultiplier1"/> - <Widget type="TextBox" skin="SandTextVCenter" position="32 20 100 20" name="AttribMultiplier2"/> - <Widget type="TextBox" skin="SandTextVCenter" position="32 40 100 20" name="AttribMultiplier3"/> - <Widget type="TextBox" skin="SandTextVCenter" position="32 60 100 20" name="AttribMultiplier4"/> - <Widget type="TextBox" skin="SandTextVCenter" position="218 0 100 20" name="AttribMultiplier5"/> - <Widget type="TextBox" skin="SandTextVCenter" position="218 20 100 20" name="AttribMultiplier6"/> - <Widget type="TextBox" skin="SandTextVCenter" position="218 40 100 20" name="AttribMultiplier7"/> - <Widget type="TextBox" skin="SandTextVCenter" position="218 60 100 20" name="AttribMultiplier8"/> - - <Widget type="HBox" position="52 0 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib1"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeStrength}"/> - <UserString key="Caption_AttributeDescription" value="#{sStrDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_strength.dds"/> - <Property key="Caption" value="#{sAttributeStrength}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal1"> - </Widget> - </Widget> - - <Widget type="HBox" position="52 20 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib2"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeIntelligence}"/> - <UserString key="Caption_AttributeDescription" value="#{sIntDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_int.dds"/> - <Property key="Caption" value="#{sAttributeIntelligence}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal2"> - </Widget> - </Widget> - - <Widget type="HBox" position="52 40 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib3"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeWillpower}"/> - <UserString key="Caption_AttributeDescription" value="#{sWilDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_wilpower.dds"/> - <Property key="Caption" value="#{sAttributeWillpower}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal3"> - </Widget> - </Widget> - - <Widget type="HBox" position="52 60 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib4"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeAgility}"/> - <UserString key="Caption_AttributeDescription" value="#{sAgiDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_agility.dds"/> - <Property key="Caption" value="#{sAttributeAgility}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal4"> - </Widget> - </Widget> - - - <Widget type="HBox" position="238 0 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib5"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeSpeed}"/> - <UserString key="Caption_AttributeDescription" value="#{sSpdDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_speed.dds"/> - <Property key="Caption" value="#{sAttributeSpeed}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal5"> - </Widget> - </Widget> - - <Widget type="HBox" position="238 20 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib6"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeEndurance}"/> - <UserString key="Caption_AttributeDescription" value="#{sEndDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_endurance.dds"/> - <Property key="Caption" value="#{sAttributeEndurance}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal6"> - </Widget> - </Widget> - - <Widget type="HBox" position="238 40 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib7"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributePersonality}"/> - <UserString key="Caption_AttributeDescription" value="#{sPerDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_personality.dds"/> - <Property key="Caption" value="#{sAttributePersonality}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal7"> - </Widget> - </Widget> - - <Widget type="HBox" position="238 60 200 20"> - <Widget type="AutoSizedButton" skin="SandTextButton" name="Attrib8"> - <UserString key="TextPadding" value="0 0"/> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeLuck}"/> - <UserString key="Caption_AttributeDescription" value="#{sLucDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_luck.dds"/> - <Property key="Caption" value="#{sAttributeLuck}"/> - </Widget> - <Widget type="AutoSizedTextBox" skin="SandText" name="AttribVal8"> - </Widget> - </Widget> + <Property key="CanvasAlign" value="Left"/> </Widget> <Widget type="HBox" skin="" position="0 0 330 24"> diff --git a/files/data/mygui/openmw_stats_window.layout b/files/data/mygui/openmw_stats_window.layout index 77de3ac753..93a648f0e0 100644 --- a/files/data/mygui/openmw_stats_window.layout +++ b/files/data/mygui/openmw_stats_window.layout @@ -113,120 +113,8 @@ </Widget> <Widget type="Widget" skin="MW_Box" position="8 148 212 152" align="Left Top Stretch"> - <!-- TODO: this should be a scroll view --> - <Widget type="Widget" skin="" position="4 4 204 144" align="Left Top Stretch"> - <Widget type="Button" skin="" position="0 0 204 18" name="Attrib1Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeStrength}"/> - <UserString key="Caption_AttributeDescription" value="#{sStrDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_strength.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib1" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal1" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - - <Widget type="Button" skin="" position="0 18 204 18" name="Attrib2Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeIntelligence}"/> - <UserString key="Caption_AttributeDescription" value="#{sIntDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_int.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib2" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal2" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - - <Widget type="Button" skin="" position="0 36 204 18" name="Attrib3Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeWillpower}"/> - <UserString key="Caption_AttributeDescription" value="#{sWilDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_wilpower.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib3" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal3" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - - <Widget type="Button" skin="" position="0 54 204 18" name="Attrib4Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeAgility}"/> - <UserString key="Caption_AttributeDescription" value="#{sAgiDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_agility.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib4" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal4" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - - <Widget type="Button" skin="" position="0 72 204 18" name="Attrib5Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeSpeed}"/> - <UserString key="Caption_AttributeDescription" value="#{sSpdDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_speed.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib5" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal5" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - - <Widget type="Button" skin="" position="0 90 204 18" name="Attrib6Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeEndurance}"/> - <UserString key="Caption_AttributeDescription" value="#{sEndDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_endurance.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib6" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal6" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - - <Widget type="Button" skin="" position="0 108 204 18" name="Attrib7Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributePersonality}"/> - <UserString key="Caption_AttributeDescription" value="#{sPerDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_personality.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib7" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal7" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - - <Widget type="Button" skin="" position="0 126 204 18" name="Attrib8Box" align="Left Top HStretch"> - <UserString key="ToolTipType" value="Layout"/> - <UserString key="ToolTipLayout" value="AttributeToolTip"/> - <UserString key="Caption_AttributeName" value="#{sAttributeLuck}"/> - <UserString key="Caption_AttributeDescription" value="#{sLucDesc}"/> - <UserString key="ImageTexture_AttributeImage" value="icons\k\attribute_luck.dds"/> - <Widget type="TextBox" skin="SandText" position="0 0 160 18" name="Attrib8" align="Left Top HStretch"> - <Property key="NeedMouse" value="false"/> - </Widget> - <Widget type="TextBox" skin="SandTextRight" position="160 0 44 18" name="AttribVal8" align="Right Top"> - <Property key="NeedMouse" value="false"/> - </Widget> - </Widget> - </Widget> + <!-- TODO: this should be a scroll view --> + <Widget type="Widget" skin="" position="4 4 204 144" align="Left Top Stretch" name="AttributeView" /> </Widget> </Widget>