#include "race.hpp" #include "window_manager.hpp" #include "widgets.hpp" #include "components/esm_store/store.hpp" #include #include #include #include #include using namespace MWGui; using namespace Widgets; RaceDialog::RaceDialog(WindowManager& parWindowManager) : WindowBase("openmw_chargen_race_layout.xml", parWindowManager) , genderIndex(0) , faceIndex(0) , hairIndex(0) , faceCount(10) , hairCount(14) { // Centre dialog center(); // These are just demo values, you should replace these with // real calls from outside the class later. setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); getWidget(appearanceBox, "AppearanceBox"); getWidget(headRotate, "HeadRotate"); headRotate->setScrollRange(50); headRotate->setScrollPosition(20); headRotate->setScrollViewPage(10); headRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); // Set up next/previous buttons MyGUI::ButtonPtr prevButton, nextButton; setText("GenderChoiceT", mWindowManager.getGameSettingString("sRaceMenu2", "Change Sex")); getWidget(prevButton, "PrevGenderButton"); getWidget(nextButton, "NextGenderButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender); setText("FaceChoiceT", mWindowManager.getGameSettingString("sRaceMenu3", "Change Face")); getWidget(prevButton, "PrevFaceButton"); getWidget(nextButton, "NextFaceButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace); setText("HairChoiceT", mWindowManager.getGameSettingString("sRaceMenu3", "Change Hair")); getWidget(prevButton, "PrevHairButton"); getWidget(nextButton, "NextHairButton"); prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu4", "Race")); getWidget(raceList, "RaceList"); raceList->setScrollVisible(true); raceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); raceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); raceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); setText("SkillsT", mWindowManager.getGameSettingString("sBonusSkillTitle", "Skill Bonus")); getWidget(skillList, "SkillList"); setText("SpellPowerT", mWindowManager.getGameSettingString("sRaceMenu7", "Specials")); getWidget(spellPowerList, "SpellPowerList"); // TODO: These buttons should be managed by a Dialog class MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked); MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked); updateRaces(); updateSkills(); updateSpellPowers(); } void RaceDialog::setNextButtonShow(bool shown) { MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); // TODO: All hardcoded coords for buttons are temporary, will be replaced with a dynamic system. if (shown) { okButton->setCaption("Next"); // Adjust back button when next is shown backButton->setCoord(MyGUI::IntCoord(471 - 18, 397, 53, 23)); okButton->setCoord(MyGUI::IntCoord(532 - 18, 397, 42 + 18, 23)); } else { okButton->setCaption("OK"); backButton->setCoord(MyGUI::IntCoord(471, 397, 53, 23)); okButton->setCoord(MyGUI::IntCoord(532, 397, 42, 23)); } } void RaceDialog::open() { updateRaces(); updateSkills(); updateSpellPowers(); setVisible(true); } void RaceDialog::setRaceId(const std::string &raceId) { currentRaceId = raceId; raceList->setIndexSelected(MyGUI::ITEM_NONE); size_t count = raceList->getItemCount(); for (size_t i = 0; i < count; ++i) { if (boost::iequals(*raceList->getItemDataAt(i), raceId)) { raceList->setIndexSelected(i); break; } } updateSkills(); updateSpellPowers(); } int wrap(int index, int max) { if (index < 0) return max - 1; else if (index >= max) return 0; else return index; } // widget controls void RaceDialog::onOkClicked(MyGUI::Widget* _sender) { eventDone(this); } void RaceDialog::onBackClicked(MyGUI::Widget* _sender) { eventBack(); } void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) { // TODO: Rotate head } void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) { genderIndex = wrap(genderIndex - 1, 2); } void RaceDialog::onSelectNextGender(MyGUI::Widget*) { genderIndex = wrap(genderIndex + 1, 2); } void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) { faceIndex = wrap(faceIndex - 1, faceCount); } void RaceDialog::onSelectNextFace(MyGUI::Widget*) { faceIndex = wrap(faceIndex + 1, faceCount); } void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) { hairIndex = wrap(hairIndex - 1, hairCount); } void RaceDialog::onSelectNextHair(MyGUI::Widget*) { hairIndex = wrap(hairIndex - 1, hairCount); } void RaceDialog::onSelectRace(MyGUI::List* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) return; const std::string *raceId = raceList->getItemDataAt(_index); if (boost::iequals(currentRaceId, *raceId)) return; currentRaceId = *raceId; updateSkills(); updateSpellPowers(); } // update widget content void RaceDialog::updateRaces() { raceList->removeAllItems(); const ESMS::ESMStore &store = mWindowManager.getStore(); ESMS::RecListT::MapType::const_iterator it = store.races.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.races.list.end(); int index = 0; for (; it != end; ++it) { const ESM::Race &race = it->second; bool playable = race.data.flags & ESM::Race::Playable; if (!playable) // Only display playable races continue; raceList->addItem(race.name, it->first); if (boost::iequals(it->first, currentRaceId)) raceList->setIndexSelected(index); ++index; } } void RaceDialog::updateSkills() { for (std::vector::iterator it = skillItems.begin(); it != skillItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } skillItems.clear(); if (currentRaceId.empty()) return; MWSkillPtr skillWidget; const int lineHeight = 18; MyGUI::IntCoord coord1(0, 0, skillList->getWidth(), 18); const ESMS::ESMStore &store = mWindowManager.getStore(); const ESM::Race *race = store.races.find(currentRaceId); int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? for (int i = 0; i < count; ++i) { int skillId = race->data.bonus[i].skill; if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes continue; skillWidget = skillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, std::string("Skill") + boost::lexical_cast(i)); skillWidget->setWindowManager(&mWindowManager); skillWidget->setSkillNumber(skillId); skillWidget->setSkillValue(MWSkill::SkillValue(race->data.bonus[i].bonus)); skillItems.push_back(skillWidget); coord1.top += lineHeight; } } void RaceDialog::updateSpellPowers() { for (std::vector::iterator it = spellPowerItems.begin(); it != spellPowerItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } spellPowerItems.clear(); if (currentRaceId.empty()) return; MWSpellPtr spellPowerWidget; const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, spellPowerList->getWidth(), 18); const ESMS::ESMStore &store = mWindowManager.getStore(); const ESM::Race *race = store.races.find(currentRaceId); std::vector::const_iterator it = race->powers.list.begin(); std::vector::const_iterator end = race->powers.list.end(); for (int i = 0; it != end; ++it) { const std::string &spellpower = *it; spellPowerWidget = spellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); spellPowerWidget->setWindowManager(&mWindowManager); spellPowerWidget->setSpellId(spellpower); spellPowerItems.push_back(spellPowerWidget); coord.top += lineHeight; ++i; } }