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.