From 970a7a3498340fedf549c21d518e1f4924b177d1 Mon Sep 17 00:00:00 2001 From: Jan Borsodi Date: Thu, 21 Oct 2010 09:28:09 +0200 Subject: [PATCH] Removed hardcoded values from review dialog and added functionality for setting all fields in the dialog. Window manager keeps track of player values set from the outside and passes these to the review dialog. --- apps/openmw/mwgui/review.cpp | 277 +++++++++++++++++++++++++-- apps/openmw/mwgui/review.hpp | 55 ++++++ apps/openmw/mwgui/window_manager.cpp | 120 ++++++++++++ apps/openmw/mwgui/window_manager.hpp | 24 ++- 4 files changed, 461 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 6bc5daf01..6315ce485 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -11,9 +11,12 @@ using namespace MWGui; using namespace Widgets; +const int ReviewDialog::lineHeight = 18; + ReviewDialog::ReviewDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) : Layout("openmw_chargen_review_layout.xml") , environment(environment) + , lastPos(0) { // Centre dialog MyGUI::IntCoord coord = mMainWidget->getCoord(); @@ -24,30 +27,24 @@ ReviewDialog::ReviewDialog(MWWorld::Environment& environment, MyGUI::IntSize gam WindowManager *wm = environment.mWindowManager; // Setup static stats - StaticTextPtr name, race, klass, sign; ButtonPtr button; - getWidget(name, "NameText"); - name->setCaption("Drizt"); + getWidget(nameWidget, "NameText"); getWidget(button, "NameButton"); button->setCaption(wm->getGameSettingString("sName", "")); - getWidget(race, "RaceText"); - race->setCaption("Dark Elf"); + getWidget(raceWidget, "RaceText"); getWidget(button, "RaceButton"); button->setCaption(wm->getGameSettingString("sRace", "")); - getWidget(klass, "ClassText"); - klass->setCaption("Adventurer"); + getWidget(classWidget, "ClassText"); getWidget(button, "ClassButton"); button->setCaption(wm->getGameSettingString("sClass", "")); - getWidget(sign, "SignText"); - sign->setCaption("The Angel"); + getWidget(birthSignWidget, "SignText"); getWidget(button, "SignButton"); button->setCaption(wm->getGameSettingString("sBirthSign", "")); // Setup dynamic stats - MWDynamicStatPtr health, magicka, fatigue; getWidget(health, "Health"); health->setTitle(wm->getGameSettingString("sHealth", "")); health->setValue(45, 45); @@ -61,15 +58,33 @@ ReviewDialog::ReviewDialog(MWWorld::Environment& environment, MyGUI::IntSize gam fatigue->setValue(160, 160); // Setup attributes + MWAttributePtr attribute; for (int idx = 0; idx < ESM::Attribute::Length; ++idx) { getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); + attributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::attributeIds[idx]), attribute)); attribute->setWindowManager(wm); attribute->setAttributeId(ESM::Attribute::attributeIds[idx]); - attribute->setAttributeValue(MWAttribute::AttributeValue(40, 50)); + attribute->setAttributeValue(MWAttribute::AttributeValue(0, 0)); } + // Setup skills + getWidget(skillAreaWidget, "Skills"); + getWidget(skillClientWidget, "SkillClient"); + getWidget(skillScrollerWidget, "SkillScroller"); + + skillScrollerWidget->eventScrollChangePosition = MyGUI::newDelegate(this, &ReviewDialog::onScrollChangePosition); + updateScroller(); + + for (int i = 0; i < ESM::Skill::Length; ++i) + { + skillValues.insert(std::pair >(i, MWMechanics::Stat())); + skillWidgetMap.insert(std::pair(i, nullptr)); + } + + static_cast(mMainWidget)->eventWindowChangeCoord = MyGUI::newDelegate(this, &ReviewDialog::onWindowResize); + // TODO: These buttons should be managed by a Dialog class MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); @@ -80,6 +95,246 @@ ReviewDialog::ReviewDialog(MWWorld::Environment& environment, MyGUI::IntSize gam okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); } +void ReviewDialog::onScrollChangePosition(MyGUI::VScrollPtr scroller, size_t pos) +{ + int diff = lastPos - pos; + // Adjust position of all widget according to difference + if (diff == 0) + return; + lastPos = pos; + + std::vector::const_iterator end = skillWidgets.end(); + for (std::vector::const_iterator it = skillWidgets.begin(); it != end; ++it) + { + (*it)->setCoord((*it)->getCoord() + MyGUI::IntPoint(0, diff)); + } +} + +void ReviewDialog::onWindowResize(MyGUI::WidgetPtr window) +{ + updateScroller(); +} + +void ReviewDialog::setPlayerName(const std::string &name) +{ + nameWidget->setCaption(name); +} + +void ReviewDialog::setRace(const std::string &raceId_) +{ + raceId = raceId_; + const ESM::Race *race = environment.mWorld->getStore().races.search(raceId); + if (race) + raceWidget->setCaption(race->name); +} + +void ReviewDialog::setClass(const ESM::Class& class_) +{ + klass = class_; + classWidget->setCaption(klass.name); +} + +void ReviewDialog::setBirthSign(const std::string& signId) +{ + birthSignId = signId; + const ESM::BirthSign *sign = environment.mWorld->getStore().birthSigns.search(birthSignId); + if (sign) + birthSignWidget->setCaption(sign->name); +} + +void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) +{ + health->setValue(value.getCurrent(), value.getModified()); +} + +void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) +{ + magicka->setValue(value.getCurrent(), value.getModified()); +} + +void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) +{ + fatigue->setValue(value.getCurrent(), value.getModified()); +} + +void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) +{ + std::map::iterator attr = attributeWidgets.find(static_cast(attributeId)); + if (attr == attributeWidgets.end()) + return; + + attr->second->setAttributeValue(value); +} + +void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) +{ + skillValues[skillId] = value; + MyGUI::WidgetPtr widget = skillWidgetMap[skillId]; + if (widget) + { + float modified = value.getModified(), base = value.getBase(); + std::string text = boost::lexical_cast(std::floor(modified)); + ColorStyle style = CS_Normal; + if (modified > base) + style = CS_Super; + else if (modified < base) + style = CS_Sub; + + setStyledText(widget, style, text); + } +} + +void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) +{ + majorSkills = major; + minorSkills = minor; + + // Update misc skills with the remaining skills not in major or minor + std::set skillSet; + std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); + std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); + boost::array::const_iterator end = ESM::Skill::skillIds.end(); + miscSkills.clear(); + for (boost::array::const_iterator it = ESM::Skill::skillIds.begin(); it != end; ++it) + { + int skill = *it; + if (skillSet.find(skill) == skillSet.end()) + miscSkills.push_back(skill); + } +} + +void ReviewDialog::setStyledText(MyGUI::WidgetPtr widget, ColorStyle style, const std::string &value) +{ + widget->setCaption(value); + if (style == CS_Super) + widget->setTextColour(MyGUI::Colour(0, 1, 0)); + else if (style == CS_Sub) + widget->setTextColour(MyGUI::Colour(1, 0, 0)); + else + widget->setTextColour(MyGUI::Colour(1, 1, 1)); +} + +void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) +{ + MyGUI::StaticImagePtr separator = skillClientWidget->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); + skillWidgets.push_back(separator); + + coord1.top += separator->getHeight(); + coord2.top += separator->getHeight(); +} + +void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) +{ + MyGUI::StaticTextPtr groupWidget = skillClientWidget->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); + groupWidget->setCaption(label); + skillWidgets.push_back(groupWidget); + + coord1.top += lineHeight; + coord2.top += lineHeight; +} + +MyGUI::WidgetPtr ReviewDialog::addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) +{ + MyGUI::StaticTextPtr skillNameWidget, skillValueWidget; + + skillNameWidget = skillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget->setCaption(text); + + skillValueWidget = skillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Default); + setStyledText(skillValueWidget, style, value); + + skillWidgets.push_back(skillNameWidget); + skillWidgets.push_back(skillValueWidget); + + coord1.top += lineHeight; + coord2.top += lineHeight; + + return skillValueWidget; +} + +void ReviewDialog::addItem(const std::string text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) +{ + MyGUI::StaticTextPtr skillNameWidget; + + skillNameWidget = skillClientWidget->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget->setCaption(text); + + skillWidgets.push_back(skillNameWidget); + + coord1.top += lineHeight; + coord2.top += lineHeight; +} + +void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) +{ + WindowManager *wm = environment.mWindowManager; + MWMechanics::MechanicsManager *mm = environment.mMechanicsManager; + ESMS::ESMStore &store = environment.mWorld->getStore(); + + // Add a line separator if there are items above + if (!skillWidgets.empty()) + { + addSeparator(coord1, coord2); + } + + addGroup(wm->getGameSettingString(titleId, titleDefault), coord1, coord2); + + SkillList::const_iterator end = skills.end(); + for (SkillList::const_iterator it = skills.begin(); it != end; ++it) + { + int skillId = *it; + if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes + continue; + assert(skillId >= 0 && skillId < ESM::Skill::Length); + const std::string &skillNameId = ESMS::Skill::sSkillNameIds[skillId]; + const MWMechanics::Stat &stat = skillValues.find(skillId)->second; + float base = stat.getBase(); + float modified = stat.getModified(); + + ColorStyle style = CS_Normal; + if (modified > base) + style = CS_Super; + else if (modified < base) + style = CS_Sub; + MyGUI::WidgetPtr widget = addValueItem(wm->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast(static_cast(modified)), style, coord1, coord2); + skillWidgetMap[skillId] = widget; + } +} + +void ReviewDialog::updateSkillArea() +{ + for (std::vector::iterator it = skillWidgets.begin(); it != skillWidgets.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + skillWidgets.clear(); + + const int valueSize = 40; + MyGUI::IntCoord coord1(10, 0, skillClientWidget->getWidth() - (10 + valueSize), 18); + MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); + + if (!majorSkills.empty()) + addSkills(majorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + + if (!minorSkills.empty()) + addSkills(minorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + + if (!miscSkills.empty()) + addSkills(miscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + + WindowManager *wm = environment.mWindowManager; + ESMS::ESMStore &store = environment.mWorld->getStore(); + + clientHeight = coord1.top; + updateScroller(); +} + +void ReviewDialog::updateScroller() +{ + skillScrollerWidget->setScrollRange(std::max(clientHeight - skillClientWidget->getHeight(), 0)); + skillScrollerWidget->setScrollPage(std::max(skillClientWidget->getHeight() - lineHeight, 0)); +} + // widget controls void ReviewDialog::onOkClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index d18693872..fc17ecf65 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -1,7 +1,10 @@ #ifndef MWGUI_REVIEW_H #define MWGUI_REVIEW_H +#include #include +#include "../mwmechanics/stat.hpp" +#include "widgets.hpp" namespace MWWorld { @@ -20,8 +23,26 @@ namespace MWGui class ReviewDialog : public OEngine::GUI::Layout { public: + typedef std::vector SkillList; + ReviewDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + void setPlayerName(const std::string &name); + void setRace(const std::string &raceId); + void setClass(const ESM::Class& class_); + void setBirthSign (const std::string &signId); + + void setHealth(const MWMechanics::DynamicStat& value); + void setMagicka(const MWMechanics::DynamicStat& value); + void setFatigue(const MWMechanics::DynamicStat& value); + + void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value); + + void configureSkills(const SkillList& major, const SkillList& minor); + void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value); + + void updateSkillArea(); + // Events typedef delegates::CDelegate0 EventHandle_Void; @@ -40,7 +61,41 @@ namespace MWGui void onBackClicked(MyGUI::Widget* _sender); private: + enum ColorStyle + { + CS_Sub, + CS_Normal, + CS_Super + }; + void setStyledText(MyGUI::WidgetPtr widget, ColorStyle style, const std::string &value); + void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); + void addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); + void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); + MyGUI::WidgetPtr addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); + void addItem(const std::string text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); + void updateScroller(); + + void onScrollChangePosition(MyGUI::VScrollPtr scroller, size_t pos); + void onWindowResize(MyGUI::WidgetPtr window); + + static const int lineHeight; + MWWorld::Environment& environment; + MyGUI::StaticTextPtr nameWidget, raceWidget, classWidget, birthSignWidget; + MyGUI::WidgetPtr skillAreaWidget, skillClientWidget; + MyGUI::VScrollPtr skillScrollerWidget; + int lastPos, clientHeight; + + Widgets::MWDynamicStatPtr health, magicka, fatigue; + + std::map attributeWidgets; + + SkillList majorSkills, minorSkills, miscSkills; + std::map > skillValues; + std::map skillWidgetMap; + std::string name, raceId, birthSignId; + ESM::Class klass; + std::vector skillWidgets; //< Skills and other information }; } #endif diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index a4274b378..bd6e13e2d 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -56,6 +56,17 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment // The HUD is always on hud->setVisible(true); + // Setup player stats + for (int i = 0; i < ESM::Attribute::Length; ++i) + { + playerAttributes.insert(std::make_pair(ESM::Attribute::attributeIds[i], MWMechanics::Stat())); + } + + for (int i = 0; i < ESM::Skill::Length; ++i) + { + playerSkillValues.insert(std::make_pair(ESM::Skill::skillIds[i], MWMechanics::Stat())); + } + // Set up visibility updateVisible(); } @@ -193,6 +204,33 @@ void WindowManager::updateVisible() reviewNext = false; if (!reviewDialog) reviewDialog = new ReviewDialog(environment, gui->getViewSize()); + + reviewDialog->setPlayerName(playerName); + reviewDialog->setRace(playerRaceId); + reviewDialog->setClass(playerClass); + reviewDialog->setBirthSign(playerBirthSignId); + + reviewDialog->setHealth(playerHealth); + reviewDialog->setMagicka(playerMagicka); + reviewDialog->setFatigue(playerFatigue); + + { + std::map >::iterator end = playerAttributes.end(); + for (std::map >::iterator it = playerAttributes.begin(); it != end; ++it) + { + reviewDialog->setAttribute(it->first, it->second); + } + } + + { + std::map >::iterator end = playerSkillValues.end(); + for (std::map >::iterator it = playerSkillValues.begin(); it != end; ++it) + { + reviewDialog->setSkillValue(it->first, it->second); + } + reviewDialog->configureSkills(playerMajorSkills, playerMinorSkills); + } + reviewDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onReviewDialogDone); reviewDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onReviewDialogBack); reviewDialog->setVisible(true); @@ -225,22 +263,96 @@ void WindowManager::updateVisible() void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) { stats->setValue (id, value); + + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8" + }; + static ESM::Attribute::AttributeID attributes[] = + { + ESM::Attribute::Strength, + ESM::Attribute::Intelligence, + ESM::Attribute::Willpower, + ESM::Attribute::Agility, + ESM::Attribute::Speed, + ESM::Attribute::Endurance, + ESM::Attribute::Personality, + ESM::Attribute::Luck + }; + for (int i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i) + { + if (id != ids[i]) + continue; + playerAttributes[attributes[i]] = value; + break; + } } void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) { stats->setValue (id, value); + + static struct {const char *id; ESM::Skill::SkillEnum skillId; } skillMap[] = + { + {"SkillBlock", ESM::Skill::Block}, + {"SkillArmorer", ESM::Skill::Armorer}, + {"SkillMediumArmor", ESM::Skill::MediumArmor}, + {"SkillHeavyArmor", ESM::Skill::HeavyArmor}, + {"SkillBluntWeapon", ESM::Skill::BluntWeapon}, + {"SkillLongBlade", ESM::Skill::LongBlade}, + {"SkillAxe", ESM::Skill::Axe}, + {"SkillSpear", ESM::Skill::Spear}, + {"SkillAthletics", ESM::Skill::Athletics}, + {"SkillEnchant", ESM::Skill::Armorer}, + {"SkillDestruction", ESM::Skill::Destruction}, + {"SkillAlteration", ESM::Skill::Alteration}, + {"SkillIllusion", ESM::Skill::Illusion}, + {"SkillConjuration", ESM::Skill::Conjuration}, + {"SkillMysticism", ESM::Skill::Mysticism}, + {"SkillRestoration", ESM::Skill::Restoration}, + {"SkillAlchemy", ESM::Skill::Alchemy}, + {"SkillUnarmored", ESM::Skill::Unarmored}, + {"SkillSecurity", ESM::Skill::Security}, + {"SkillSneak", ESM::Skill::Sneak}, + {"SkillAcrobatics", ESM::Skill::Acrobatics}, + {"SkillLightArmor", ESM::Skill::LightArmor}, + {"SkillShortBlade", ESM::Skill::ShortBlade}, + {"SkillMarksman", ESM::Skill::Marksman}, + {"SkillMercantile", ESM::Skill::Mercantile}, + {"SkillSpeechcraft", ESM::Skill::Speechcraft}, + {"SkillHandToHand", ESM::Skill::HandToHand}, + }; + for (int i = 0; i < sizeof(skillMap)/sizeof(skillMap[0]); ++i) + { + if (skillMap[i].id == id) + { + ESM::Skill::SkillEnum skillId = skillMap[i].skillId; + playerSkillValues[skillId] = value; + break; + } + } } void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { stats->setValue (id, value); hud->setValue (id, value); + if (id == "HBar") + playerHealth = value; + else if (id == "MBar") + playerMagicka = value; + else if (id == "FBar") + playerFatigue = value; } void WindowManager::setValue (const std::string& id, const std::string& value) { stats->setValue (id, value); + if (id=="name") + playerName = value; + else if (id=="race") + playerRaceId = value; } void WindowManager::setValue (const std::string& id, int value) @@ -248,9 +360,17 @@ void WindowManager::setValue (const std::string& id, int value) stats->setValue (id, value); } +void WindowManager::setPlayerClass (const ESM::Class &class_) +{ + playerClass = class_; + stats->setValue("class", playerClass.name); +} + void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) { stats->configureSkills (major, minor); + playerMajorSkills = major; + playerMinorSkills = minor; } void WindowManager::setFactions (const FactionList& factions) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 046890f87..ee4326b2e 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -14,6 +14,7 @@ #include #include +#include #include "../mwmechanics/stat.hpp" #include "mode.hpp" @@ -54,6 +55,12 @@ namespace MWGui class WindowManager { + public: + typedef std::pair Faction; + typedef std::vector FactionList; + typedef std::vector SkillList; + + private: MWWorld::Environment& environment; HUD *hud; MapWindow *map; @@ -87,6 +94,16 @@ namespace MWGui unsigned generateClassStep; std::string generateClass; + // Various stats about player as needed by window manager + std::string playerName; + ESM::Class playerClass; + std::string playerRaceId, playerBirthSignId; + std::map > playerAttributes; + SkillList playerMajorSkills, playerMinorSkills; + std::map > playerSkillValues; + MWMechanics::DynamicStat playerHealth, playerMagicka, playerFatigue; + + // Gui MyGUI::Gui *gui; // Current gui mode @@ -145,10 +162,6 @@ namespace MWGui MyGUI::Gui* getGui() const { return gui; } - typedef std::pair Faction; - typedef std::vector FactionList; - typedef std::vector SkillList; - void setValue (const std::string& id, const MWMechanics::Stat& value); ///< Set value for the given ID. @@ -164,6 +177,9 @@ namespace MWGui void setValue (const std::string& id, int value); ///< set value for the given ID. + void setPlayerClass (const ESM::Class &class_); + ///< set current class of player + void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group.