diff --git a/CMakeLists.txt b/CMakeLists.txt index 742b56287..0ad1c5a9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,10 @@ option(USE_MPG123 "use mpg123 + libsndfile for sound" ON) set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files") +if (WIN32) + option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON) +endif() + # We probably support older versions than this. cmake_minimum_required(VERSION 2.6) @@ -102,6 +106,9 @@ source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER}) file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp) set(ESM ${COMP_DIR}/esm/load_impl.cpp + ${COMP_DIR}/esm/skill.cpp + ${COMP_DIR}/esm/attr.cpp + ${COMP_DIR}/esm/class.cpp ) source_group(components\\esm FILES ${ESM_HEADER} ${ESM}) @@ -225,6 +232,16 @@ link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR}) add_subdirectory( extern/caelum ) add_subdirectory( extern/mygui_3.0.1 ) +# Make sure that certain libraries are used as static libraries +# This is in effect turns off __declspec (dllexport) for windows +# Each library will also need to be configured to build as a static lib + +# MyGUI: extern/mygui_3.0.0/ +add_definitions(-DMYGUI_STATIC) + +# Caelum: extern/caelum/ +add_definitions(-DCAELUM_STATIC) + # Specify build paths set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") @@ -289,3 +306,24 @@ option(BUILD_ESMTOOL "build ESM inspector" ON) if (BUILD_ESMTOOL) add_subdirectory( apps/esmtool ) endif() + +if (WIN32) + if (USE_DEBUG_CONSOLE) + set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") + set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") + set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE") + else() + # Turn off debug console, debug output will be written to visual studio output instead + set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS") + set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS") + endif() + + # Release builds use the debug console + set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") + set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") + set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") + + # TODO: At some point release builds should not use the console but rather write to a log file + #set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") + #set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") +endif() diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 70979cca7..33f38c1ae 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -34,13 +34,24 @@ set(GAMEINPUT_HEADER source_group(apps\\openmw\\mwinput FILES ${GAMEINPUT} ${GAMEINPUT_HEADER}) set(GAMEGUI_HEADER - mwgui/mw_layouts.hpp + mwgui/layouts.hpp + mwgui/text_input.hpp + mwgui/widgets.hpp + mwgui/race.hpp + mwgui/class.hpp + mwgui/birth.hpp mwgui/window_manager.hpp mwgui/console.hpp ) set(GAMEGUI mwgui/window_manager.cpp + mwgui/layouts.cpp mwgui/console.cpp + mwgui/text_input.cpp + mwgui/widgets.cpp + mwgui/race.cpp + mwgui/birth.cpp + mwgui/class.cpp ) source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI}) @@ -171,11 +182,14 @@ set(GAMECLASS_HEADER source_group(apps\\openmw\\mwclass FILES ${GAMECLASS} ${GAMECLASS_HEADER}) set(GAMEMECHANICS - mwmechanics/mechanicsmanager.cpp) + mwmechanics/mechanicsmanager.cpp + mwmechanics/magiceffects.cpp + ) set(GAMEMECHANICS_HEADER mwmechanics/mechanicsmanager.hpp mwmechanics/stat.hpp mwmechanics/creaturestats.hpp + mwmechanics/magiceffects.hpp ) source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER}) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bcb2d7f59..6cd3ded1e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -34,6 +34,9 @@ #include +#include +#include "mwgui/class.hpp" + void OMW::Engine::executeLocalScripts() { for (MWWorld::World::ScriptList::const_iterator iter ( @@ -56,47 +59,54 @@ void OMW::Engine::executeLocalScripts() bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) { - mEnvironment.mFrameDuration = evt.timeSinceLastFrame; + try + { + mEnvironment.mFrameDuration = evt.timeSinceLastFrame; - // global scripts - mEnvironment.mGlobalScripts->run (mEnvironment); + // global scripts + mEnvironment.mGlobalScripts->run (mEnvironment); - bool changed = mEnvironment.mWorld->hasCellChanged(); + bool changed = mEnvironment.mWorld->hasCellChanged(); - // local scripts - executeLocalScripts(); // This does not handle the case where a global script causes a cell - // change, followed by a cell change in a local script during the same - // frame. + // local scripts + executeLocalScripts(); // This does not handle the case where a global script causes a cell + // change, followed by a cell change in a local script during the same + // frame. - // passing of time - if (mEnvironment.mWindowManager->getMode()==MWGui::GM_Game) - mEnvironment.mWorld->advanceTime ( - mEnvironment.mFrameDuration*mEnvironment.mWorld->getTimeScaleFactor()/3600); + // passing of time + if (mEnvironment.mWindowManager->getMode()==MWGui::GM_Game) + mEnvironment.mWorld->advanceTime ( + mEnvironment.mFrameDuration*mEnvironment.mWorld->getTimeScaleFactor()/3600); - if (changed) // keep change flag for another frame, if cell changed happend in local script - mEnvironment.mWorld->markCellAsUnchanged(); + if (changed) // keep change flag for another frame, if cell changed happend in local script + mEnvironment.mWorld->markCellAsUnchanged(); - // update actors - mEnvironment.mMechanicsManager->update(); + // update actors + mEnvironment.mMechanicsManager->update(); - if (focusFrameCounter++ == focusUpdateFrame) - { - std::string handle = mEnvironment.mWorld->getFacedHandle(); + if (focusFrameCounter++ == focusUpdateFrame) + { + std::string handle = mEnvironment.mWorld->getFacedHandle(); - std::string name; + std::string name; - if (!handle.empty()) - { - MWWorld::Ptr ptr = mEnvironment.mWorld->getPtrViaHandle (handle); + if (!handle.empty()) + { + MWWorld::Ptr ptr = mEnvironment.mWorld->getPtrViaHandle (handle); - if (!ptr.isEmpty()) - name = MWWorld::Class::get (ptr).getName (ptr); - } + if (!ptr.isEmpty()) + name = MWWorld::Class::get (ptr).getName (ptr); + } - if (!name.empty()) - std::cout << "Object: " << name << std::endl; + if (!name.empty()) + std::cout << "Object: " << name << std::endl; - focusFrameCounter = 0; + focusFrameCounter = 0; + } + } + catch (const std::exception& e) + { + std::cerr << "Error in framelistener: " << e.what() << std::endl; } return true; @@ -230,6 +240,10 @@ void OMW::Engine::go() // Set up the GUI system mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene()); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); @@ -256,8 +270,7 @@ void OMW::Engine::go() *mScriptManager); // Create game mechanics system - mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager ( - mEnvironment.mWorld->getStore(), *mEnvironment.mWindowManager); + mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (mEnvironment); // Create dialog system mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment); @@ -271,6 +284,7 @@ void OMW::Engine::go() // Sets up the input system MWInput::MWInputManager input(mOgre, mEnvironment.mWorld->getPlayerPos(), *mEnvironment.mWindowManager, mDebug, *this); + mEnvironment.mInputManager = &input; focusFrameCounter = 0; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index b622b1f27..76eea1107 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -8,6 +8,22 @@ #include #include "engine.hpp" +#if defined(_WIN32) && !defined(_CONSOLE) +#include +#include + +# if !defined(_DEBUG) +# include +# include +# endif + +// For OutputDebugString +#include +// makes __argc and __argv available on windows +#include + +#endif + using namespace std; /// Parse command line options and openmw.cfg file (if one exists). Results are directly @@ -80,6 +96,7 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine) int main(int argc, char**argv) { + try { OMW::Engine engine; @@ -97,3 +114,71 @@ int main(int argc, char**argv) return 0; } + +// Platform specific for Windows when there is no console built into the executable. +// Windows will call the WinMain function instead of main in this case, the normal +// main function is then called with the __argc and __argv parameters. +// In addition if it is a debug build it will redirect cout to the debug console in Visual Studio +#if defined(_WIN32) && !defined(_CONSOLE) + +#if defined(_DEBUG) +class DebugOutput : public boost::iostreams::sink +{ +public: + std::streamsize write(const char *str, std::streamsize size) + { + // Make a copy for null termination + std::string tmp (str, size); + // Write string to Visual Studio Debug output + OutputDebugString (tmp.c_str ()); + return size; + } +}; +#else +class Logger : public boost::iostreams::sink +{ +public: + Logger(std::ofstream &stream) + : out(stream) + { + } + + std::streamsize write(const char *str, std::streamsize size) + { + out.write (str, size); + out.flush(); + return size; + } + +private: + std::ofstream &out; +}; +#endif + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + std::streambuf* old_rdbuf = std::cout.rdbuf (); + + int ret = 0; +#if defined(_DEBUG) + // Redirect cout to VS debug output when running in debug mode + { + boost::iostreams::stream_buffer sb; + sb.open(DebugOutput()); +#else + // Redirect cout to openmw.log + std::ofstream logfile ("openmw.log"); + { + boost::iostreams::stream_buffer sb; + sb.open (Logger (logfile)); +#endif + std::cout.rdbuf (&sb); + + ret = main (__argc, __argv); + + std::cout.rdbuf(old_rdbuf); + } + return ret; +} + +#endif diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2e16ac6a4..7a50fcb2a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -78,6 +78,8 @@ namespace MWClass stats->mDynamic[1].set (ref->base->data.mana); stats->mDynamic[2].set (ref->base->data.fatigue); + stats->mLevel = ref->base->data.level; + ptr.getRefData().getCreatureStats() = stats; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 04ae468e4..f9f99e58d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -94,6 +94,8 @@ namespace MWClass stats->mDynamic[1].set (ref->base->npdt52.mana); stats->mDynamic[2].set (ref->base->npdt52.fatigue); + stats->mLevel = ref->base->npdt52.level; + ptr.getRefData().getCreatureStats() = stats; } @@ -117,6 +119,9 @@ namespace MWClass stats->mFactionRank[ref->base->faction] = 0; } + for (int i=0; i<27; ++i) + stats->mSkill[i].setBase (ref->base->npdt52.skills[i]); + ptr.getRefData().getNpcStats() = stats; } diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp new file mode 100644 index 000000000..5e4247725 --- /dev/null +++ b/apps/openmw/mwgui/birth.cpp @@ -0,0 +1,224 @@ +#include "birth.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" +#include "window_manager.hpp" +#include "widgets.hpp" +#include "components/esm_store/store.hpp" + +#include +#include + +using namespace MWGui; +using namespace Widgets; + +BirthDialog::BirthDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_birth_layout.xml") + , environment(environment) +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + WindowManager *wm = environment.mWindowManager; + + getWidget(spellArea, "SpellArea"); + + getWidget(birthImage, "BirthsignImage"); + + getWidget(birthList, "BirthsignList"); + birthList->setScrollVisible(true); + birthList->eventListSelectAccept = MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + birthList->eventListMouseItemActivate = MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + birthList->eventListChangePosition = MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &BirthDialog::onBackClicked); + + MyGUI::ButtonPtr okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &BirthDialog::onOkClicked); + + updateBirths(); + updateSpells(); +} + +void BirthDialog::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(375 - 18, 340, 53, 23)); + okButton->setCoord(MyGUI::IntCoord(431 - 18, 340, 42 + 18, 23)); + } + else + { + okButton->setCaption("OK"); + backButton->setCoord(MyGUI::IntCoord(375, 340, 53, 23)); + okButton->setCoord(MyGUI::IntCoord(431, 340, 42, 23)); + } +} + +void BirthDialog::open() +{ + updateBirths(); + updateSpells(); + setVisible(true); +} + + +void BirthDialog::setBirthId(const std::string &birthId) +{ + currentBirthId = birthId; + birthList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = birthList->getItemCount(); + for (size_t i = 0; i < count; ++i) + { + if (boost::iequals(*birthList->getItemDataAt(i), birthId)) + { + birthList->setIndexSelected(i); + break; + } + } + + updateSpells(); +} + +// widget controls + +void BirthDialog::onOkClicked(MyGUI::Widget* _sender) +{ + eventDone(); +} + +void BirthDialog::onBackClicked(MyGUI::Widget* _sender) +{ + eventBack(); +} + +void BirthDialog::onSelectBirth(MyGUI::List* _sender, size_t _index) +{ + if (_index == MyGUI::ITEM_NONE) + return; + + const std::string *birthId = birthList->getItemDataAt(_index); + if (boost::iequals(currentBirthId, *birthId)) + return; + + currentBirthId = *birthId; + updateSpells(); +} + +// update widget content + +void BirthDialog::updateBirths() +{ + birthList->removeAllItems(); + + ESMS::ESMStore &store = environment.mWorld->getStore(); + + ESMS::RecListT::MapType::const_iterator it = store.birthSigns.list.begin(); + ESMS::RecListT::MapType::const_iterator end = store.birthSigns.list.end(); + int index = 0; + for (; it != end; ++it) + { + const ESM::BirthSign &birth = it->second; + birthList->addItem(birth.name, it->first); + if (boost::iequals(it->first, currentBirthId)) + birthList->setIndexSelected(index); + ++index; + } +} + +void BirthDialog::updateSpells() +{ + for (std::vector::iterator it = spellItems.begin(); it != spellItems.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + spellItems.clear(); + + if (currentBirthId.empty()) + return; + + MWSpellPtr spellWidget; + const int lineHeight = 18; + MyGUI::IntCoord coord(0, 0, spellArea->getWidth(), 18); + + ESMS::ESMStore &store = environment.mWorld->getStore(); + const ESM::BirthSign *birth = store.birthSigns.find(currentBirthId); + + std::string texturePath = std::string("textures\\") + birth->texture; + fixTexturePath(texturePath); + birthImage->setImageTexture(texturePath); + + std::vector abilities, powers, spells; + + std::vector::const_iterator it = birth->powers.list.begin(); + std::vector::const_iterator end = birth->powers.list.end(); + for (; it != end; ++it) + { + const std::string &spellId = *it; + const ESM::Spell *spell = store.spells.search(spellId); + if (!spell) + continue; // Skip spells which cannot be found + ESM::Spell::SpellType type = static_cast(spell->data.type); + if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power) + continue; // We only want spell, ability and powers. + + if (type == ESM::Spell::ST_Ability) + abilities.push_back(spellId); + else if (type == ESM::Spell::ST_Power) + powers.push_back(spellId); + else if (type == ESM::Spell::ST_Spell) + spells.push_back(spellId); + } + + int i = 0; + struct{ const std::vector &spells; const char *label; } categories[3] = { + {abilities, "sBirthsignmenu1"}, + {powers, "sPowers"}, + {spells, "sBirthsignmenu2"} + }; + for (int category = 0; category < 3; ++category) + { + if (!categories[category].spells.empty()) + { + MyGUI::StaticTextPtr label = spellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); + label->setCaption(environment.mWindowManager->getGameSettingString(categories[category].label, "")); + spellItems.push_back(label); + coord.top += lineHeight; + + std::vector::const_iterator end = categories[category].spells.end(); + for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) + { + const std::string &spellId = *it; + spellWidget = spellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); + spellWidget->setEnvironment(&environment); + spellWidget->setSpellId(spellId); + + spellItems.push_back(spellWidget); + coord.top += lineHeight; + + MyGUI::IntCoord spellCoord = coord; + spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? + spellWidget->createEffectWidgets(spellItems, spellArea, spellCoord); + coord.top = spellCoord.top; + + ++i; + } + } + } +} diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp new file mode 100644 index 000000000..ae9067b1a --- /dev/null +++ b/apps/openmw/mwgui/birth.hpp @@ -0,0 +1,70 @@ +#ifndef MWGUI_BIRTH_H +#define MWGUI_BIRTH_H + +#include + +namespace MWWorld +{ + class Environment; +} + +/* + This file contains the dialog for choosing a birth sign. + Layout is defined by resources/mygui/openmw_chargen_race_layout.xml. + */ + +namespace MWGui +{ + using namespace MyGUI; + + class BirthDialog : public OEngine::GUI::Layout + { + public: + BirthDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + enum Gender + { + GM_Male, + GM_Female + }; + + const std::string &getBirthId() const { return currentBirthId; } + void setBirthId(const std::string &raceId); + + void setNextButtonShow(bool shown); + void open(); + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Back button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventBack; + + /** Event : Dialog finished, OK button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventDone; + + protected: + void onSelectBirth(MyGUI::List* _sender, size_t _index); + + void onOkClicked(MyGUI::Widget* _sender); + void onBackClicked(MyGUI::Widget* _sender); + + private: + void updateBirths(); + void updateSpells(); + + MWWorld::Environment& environment; + + MyGUI::ListPtr birthList; + MyGUI::WidgetPtr spellArea; + MyGUI::StaticImagePtr birthImage; + std::vector spellItems; + + std::string currentBirthId; + }; +} +#endif diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp new file mode 100644 index 000000000..f7e567031 --- /dev/null +++ b/apps/openmw/mwgui/class.cpp @@ -0,0 +1,989 @@ +#include "class.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" +#include "window_manager.hpp" +#include "components/esm_store/store.hpp" + +#include +#include + +#include +#include + +using namespace MWGui; + +/* GenerateClassResultDialog */ + +GenerateClassResultDialog::GenerateClassResultDialog(MWWorld::Environment& environment) + : Layout("openmw_chargen_generate_class_result_layout.xml") + , environment(environment) +{ + // Centre dialog + MyGUI::IntSize gameWindowSize = environment.mWindowManager->getGui()->getViewSize(); + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + WindowManager *wm = environment.mWindowManager; + setText("ReflectT", wm->getGameSettingString("sMessageQuestionAnswer1", "")); + + getWidget(classImage, "ClassImage"); + getWidget(className, "ClassName"); + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked); + + MyGUI::ButtonPtr okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); +} + +std::string GenerateClassResultDialog::getClassId() const +{ + return className->getCaption(); +} + +void GenerateClassResultDialog::setClassId(const std::string &classId) +{ + currentClassId = classId; + classImage->setImageTexture(std::string("textures\\levelup\\") + currentClassId + ".dds"); + ESMS::ESMStore &store = environment.mWorld->getStore(); + className->setCaption(store.classes.find(currentClassId)->name); +} + +// widget controls + +void GenerateClassResultDialog::onOkClicked(MyGUI::Widget* _sender) +{ + eventDone(); +} + +void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) +{ + eventBack(); +} + +/* PickClassDialog */ + +PickClassDialog::PickClassDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_class_layout.xml") + , environment(environment) +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + WindowManager *wm = environment.mWindowManager; + setText("SpecializationT", wm->getGameSettingString("sChooseClassMenu1", "Specialization")); + getWidget(specializationName, "SpecializationName"); + + setText("FavoriteAttributesT", wm->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); + getWidget(favoriteAttribute0, "FavoriteAttribute0"); + getWidget(favoriteAttribute1, "FavoriteAttribute1"); + favoriteAttribute0->setWindowManager(wm); + favoriteAttribute1->setWindowManager(wm); + + setText("MajorSkillT", wm->getGameSettingString("sChooseClassMenu3", "Major Skills:")); + getWidget(majorSkill0, "MajorSkill0"); + getWidget(majorSkill1, "MajorSkill1"); + getWidget(majorSkill2, "MajorSkill2"); + getWidget(majorSkill3, "MajorSkill3"); + getWidget(majorSkill4, "MajorSkill4"); + majorSkill0->setWindowManager(wm); + majorSkill1->setWindowManager(wm); + majorSkill2->setWindowManager(wm); + majorSkill3->setWindowManager(wm); + majorSkill4->setWindowManager(wm); + + setText("MinorSkillT", wm->getGameSettingString("sChooseClassMenu4", "Minor Skills:")); + getWidget(minorSkill0, "MinorSkill0"); + getWidget(minorSkill1, "MinorSkill1"); + getWidget(minorSkill2, "MinorSkill2"); + getWidget(minorSkill3, "MinorSkill3"); + getWidget(minorSkill4, "MinorSkill4"); + minorSkill0->setWindowManager(wm); + minorSkill1->setWindowManager(wm); + minorSkill2->setWindowManager(wm); + minorSkill3->setWindowManager(wm); + minorSkill4->setWindowManager(wm); + + getWidget(classList, "ClassList"); + classList->setScrollVisible(true); + classList->eventListSelectAccept = MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + classList->eventListMouseItemActivate = MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + classList->eventListChangePosition = MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + + getWidget(classImage, "ClassImage"); + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &PickClassDialog::onBackClicked); + + MyGUI::ButtonPtr okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &PickClassDialog::onOkClicked); + + updateClasses(); + updateStats(); +} + +void PickClassDialog::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(382 - 18, 265, 53, 23)); + okButton->setCoord(MyGUI::IntCoord(434 - 18, 265, 42 + 18, 23)); + } + else + { + okButton->setCaption("OK"); + backButton->setCoord(MyGUI::IntCoord(382, 265, 53, 23)); + okButton->setCoord(MyGUI::IntCoord(434, 265, 42, 23)); + } +} + +void PickClassDialog::open() +{ + updateClasses(); + updateStats(); + setVisible(true); +} + + +void PickClassDialog::setClassId(const std::string &classId) +{ + currentClassId = classId; + classList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = classList->getItemCount(); + for (size_t i = 0; i < count; ++i) + { + if (boost::iequals(*classList->getItemDataAt(i), classId)) + { + classList->setIndexSelected(i); + break; + } + } + + updateStats(); +} + +// widget controls + +void PickClassDialog::onOkClicked(MyGUI::Widget* _sender) +{ + eventDone(); +} + +void PickClassDialog::onBackClicked(MyGUI::Widget* _sender) +{ + eventBack(); +} + +void PickClassDialog::onSelectClass(MyGUI::List* _sender, size_t _index) +{ + if (_index == MyGUI::ITEM_NONE) + return; + + const std::string *classId = classList->getItemDataAt(_index); + if (boost::iequals(currentClassId, *classId)) + return; + + currentClassId = *classId; + updateStats(); +} + +// update widget content + +void PickClassDialog::updateClasses() +{ + classList->removeAllItems(); + + ESMS::ESMStore &store = environment.mWorld->getStore(); + + ESMS::RecListT::MapType::const_iterator it = store.classes.list.begin(); + ESMS::RecListT::MapType::const_iterator end = store.classes.list.end(); + int index = 0; + for (; it != end; ++it) + { + const ESM::Class &klass = it->second; + bool playable = (klass.data.isPlayable != 0); + if (!playable) // Only display playable classes + continue; + + const std::string &id = it->first; + classList->addItem(klass.name, id); + if (boost::iequals(id, currentClassId)) + classList->setIndexSelected(index); + ++index; + } +} + +void PickClassDialog::updateStats() +{ + if (currentClassId.empty()) + return; + WindowManager *wm = environment.mWindowManager; + ESMS::ESMStore &store = environment.mWorld->getStore(); + const ESM::Class *klass = store.classes.find(currentClassId); + + ESM::Class::Specialization specialization = static_cast(klass->data.specialization); + + static const char *specIds[3] = { + "sSpecializationCombat", + "sSpecializationMagic", + "sSpecializationStealth" + }; + specializationName->setCaption(wm->getGameSettingString(specIds[specialization], specIds[specialization])); + + favoriteAttribute0->setAttributeId(klass->data.attribute[0]); + favoriteAttribute1->setAttributeId(klass->data.attribute[1]); + + Widgets::MWSkillPtr majorSkills[5] = { + majorSkill0, + majorSkill1, + majorSkill2, + majorSkill3, + majorSkill4 + }; + Widgets::MWSkillPtr minorSkills[5] = { + minorSkill0, + minorSkill1, + minorSkill2, + minorSkill3, + minorSkill4 + }; + + for (int i = 0; i < 5; ++i) + { + majorSkills[i]->setSkillNumber(klass->data.skills[i][0]); + minorSkills[i]->setSkillNumber(klass->data.skills[i][1]); + } + + classImage->setImageTexture(std::string("textures\\levelup\\") + currentClassId + ".dds"); +} + +/* InfoBoxDialog */ + +void fitToText(MyGUI::StaticTextPtr widget) +{ + MyGUI::IntCoord inner = widget->getTextRegion(); + MyGUI::IntCoord outer = widget->getCoord(); + MyGUI::IntSize size = widget->getTextSize(); + size.width += outer.width - inner.width; + size.height += outer.height - inner.height; + widget->setSize(size); +} + +void layoutVertically(MyGUI::WidgetPtr widget, int margin) +{ + size_t count = widget->getChildCount(); + int pos = 0; + pos += margin; + int width = 0; + for (unsigned i = 0; i < count; ++i) + { + MyGUI::WidgetPtr child = widget->getChildAt(i); + if (!child->isVisible()) + continue; + + child->setPosition(child->getLeft(), pos); + width = std::max(width, child->getWidth()); + pos += child->getHeight() + margin; + } + width += margin*2; + widget->setSize(width, pos); +} + +InfoBoxDialog::InfoBoxDialog(MWWorld::Environment& environment) + : Layout("openmw_infobox_layout.xml") + , environment(environment) + , currentButton(-1) +{ + getWidget(textBox, "TextBox"); + getWidget(text, "Text"); + text->getSubWidgetText()->setWordWrap(true); + getWidget(buttonBar, "ButtonBar"); + + center(); +} + +void InfoBoxDialog::setText(const std::string &str) +{ + text->setCaption(str); + textBox->setVisible(!str.empty()); + fitToText(text); +} + +std::string InfoBoxDialog::getText() const +{ + return text->getCaption(); +} + +void InfoBoxDialog::setButtons(ButtonList &buttons) +{ + for (std::vector::iterator it = this->buttons.begin(); it != this->buttons.end(); ++it) + { + MyGUI::Gui::getInstance().destroyWidget(*it); + } + this->buttons.clear(); + currentButton = -1; + + // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget + MyGUI::ButtonPtr button; + MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, buttonBar->getWidth(), 10); + ButtonList::const_iterator end = buttons.end(); + for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) + { + const std::string &text = *it; + button = buttonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); + button->getSubWidgetText()->setWordWrap(true); + button->setCaption(text); + fitToText(button); + button->eventMouseButtonClick = MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); + coord.top += button->getHeight(); + this->buttons.push_back(button); + } +} + +void InfoBoxDialog::update() +{ + // Fix layout + layoutVertically(textBox, 4); + layoutVertically(buttonBar, 6); + layoutVertically(mMainWidget, 4 + 6); + + center(); +} + +int InfoBoxDialog::getChosenButton() const +{ + return currentButton; +} + +void InfoBoxDialog::onButtonClicked(MyGUI::WidgetPtr _sender) +{ + std::vector::const_iterator end = buttons.end(); + int i = 0; + for (std::vector::const_iterator it = buttons.begin(); it != end; ++it) + { + if (*it == _sender) + { + currentButton = i; + eventButtonSelected(_sender, i); + return; + } + ++i; + } +} + +void InfoBoxDialog::center() +{ + // Centre dialog + MyGUI::IntSize gameWindowSize = environment.mWindowManager->getGui()->getViewSize(); + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); +} + +/* ClassChoiceDialog */ + +ClassChoiceDialog::ClassChoiceDialog(MWWorld::Environment& environment) + : InfoBoxDialog(environment) +{ + WindowManager *mw = environment.mWindowManager; + setText(""); + ButtonList buttons; + buttons.push_back(mw->getGameSettingString("sClassChoiceMenu1", "")); + buttons.push_back(mw->getGameSettingString("sClassChoiceMenu2", "")); + buttons.push_back(mw->getGameSettingString("sClassChoiceMenu3", "")); + buttons.push_back(mw->getGameSettingString("sBack", "")); + setButtons(buttons); + + update(); +} + +/* CreateClassDialog */ + +CreateClassDialog::CreateClassDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_create_class_layout.xml") + , environment(environment) + , specDialog(nullptr) + , attribDialog(nullptr) + , skillDialog(nullptr) + , descDialog(nullptr) +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + WindowManager *wm = environment.mWindowManager; + setText("SpecializationT", wm->getGameSettingString("sChooseClassMenu1", "Specialization")); + getWidget(specializationName, "SpecializationName"); + specializationName->setCaption(wm->getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Combat], "")); + specializationName->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); + + setText("FavoriteAttributesT", wm->getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); + getWidget(favoriteAttribute0, "FavoriteAttribute0"); + getWidget(favoriteAttribute1, "FavoriteAttribute1"); + favoriteAttribute0->setWindowManager(wm); + favoriteAttribute1->setWindowManager(wm); + favoriteAttribute0->eventClicked = MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + favoriteAttribute1->eventClicked = MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + + setText("MajorSkillT", wm->getGameSettingString("sSkillClassMajor", "")); + getWidget(majorSkill0, "MajorSkill0"); + getWidget(majorSkill1, "MajorSkill1"); + getWidget(majorSkill2, "MajorSkill2"); + getWidget(majorSkill3, "MajorSkill3"); + getWidget(majorSkill4, "MajorSkill4"); + skills.push_back(majorSkill0); + skills.push_back(majorSkill1); + skills.push_back(majorSkill2); + skills.push_back(majorSkill3); + skills.push_back(majorSkill4); + + setText("MinorSkillT", wm->getGameSettingString("sSkillClassMinor", "")); + getWidget(minorSkill0, "MinorSkill0"); + getWidget(minorSkill1, "MinorSkill1"); + getWidget(minorSkill2, "MinorSkill2"); + getWidget(minorSkill3, "MinorSkill3"); + getWidget(minorSkill4, "MinorSkill4"); + skills.push_back(minorSkill0); + skills.push_back(minorSkill1); + skills.push_back(minorSkill2); + skills.push_back(minorSkill3); + skills.push_back(minorSkill4); + + std::vector::const_iterator end = skills.end(); + for (std::vector::const_iterator it = skills.begin(); it != end; ++it) + { + (*it)->setWindowManager(wm); + (*it)->eventClicked = MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); + } + + setText("LabelT", wm->getGameSettingString("sName", "")); + getWidget(editName, "EditName"); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(editName); + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr descriptionButton; + getWidget(descriptionButton, "DescriptionButton"); + descriptionButton->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); + + MyGUI::ButtonPtr backButton; + getWidget(backButton, "BackButton"); + backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked); + + MyGUI::ButtonPtr okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked); + + // Set default skills, attributes + + favoriteAttribute0->setAttributeId(ESM::Attribute::Strength); + favoriteAttribute1->setAttributeId(ESM::Attribute::Agility); + + majorSkill0->setSkillId(ESM::Skill::Block); + majorSkill1->setSkillId(ESM::Skill::Armorer); + majorSkill2->setSkillId(ESM::Skill::MediumArmor); + majorSkill3->setSkillId(ESM::Skill::HeavyArmor); + majorSkill4->setSkillId(ESM::Skill::BluntWeapon); + + minorSkill0->setSkillId(ESM::Skill::LongBlade); + minorSkill1->setSkillId(ESM::Skill::Axe); + minorSkill2->setSkillId(ESM::Skill::Spear); + minorSkill3->setSkillId(ESM::Skill::Athletics); + minorSkill4->setSkillId(ESM::Skill::Enchant); +} + +CreateClassDialog::~CreateClassDialog() +{ + delete specDialog; + delete attribDialog; + delete skillDialog; + delete descDialog; +} + +std::string CreateClassDialog::getName() const +{ + return editName->getOnlyText(); +} + +std::string CreateClassDialog::getDescription() const +{ + return description; +} + +ESM::Class::Specialization CreateClassDialog::getSpecializationId() const +{ + return specializationId; +} + +std::vector CreateClassDialog::getFavoriteAttributes() const +{ + std::vector v; + v.push_back(favoriteAttribute0->getAttributeId()); + v.push_back(favoriteAttribute1->getAttributeId()); + return v; +} + +std::vector CreateClassDialog::getMajorSkills() const +{ + std::vector v; + v.push_back(majorSkill0->getSkillId()); + v.push_back(majorSkill1->getSkillId()); + v.push_back(majorSkill2->getSkillId()); + v.push_back(majorSkill3->getSkillId()); + v.push_back(majorSkill4->getSkillId()); + return v; +} + +std::vector CreateClassDialog::getMinorSkills() const +{ + std::vector v; + v.push_back(majorSkill0->getSkillId()); + v.push_back(majorSkill1->getSkillId()); + v.push_back(majorSkill2->getSkillId()); + v.push_back(majorSkill3->getSkillId()); + v.push_back(majorSkill4->getSkillId()); + return v; +} + +void CreateClassDialog::setNextButtonShow(bool shown) +{ + MyGUI::ButtonPtr descriptionButton; + getWidget(descriptionButton, "DescriptionButton"); + + 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 + descriptionButton->setCoord(MyGUI::IntCoord(207 - 18, 158, 143, 23)); + backButton->setCoord(MyGUI::IntCoord(356 - 18, 158, 53, 23)); + okButton->setCoord(MyGUI::IntCoord(417 - 18, 158, 42 + 18, 23)); + } + else + { + okButton->setCaption("OK"); + descriptionButton->setCoord(MyGUI::IntCoord(207, 158, 143, 23)); + backButton->setCoord(MyGUI::IntCoord(356, 158, 53, 23)); + okButton->setCoord(MyGUI::IntCoord(417, 158, 42, 23)); + } +} + +void CreateClassDialog::open() +{ + setVisible(true); +} + +// widget controls + +void CreateClassDialog::onDialogCancel() +{ + if (specDialog) + specDialog->setVisible(false); + if (attribDialog) + attribDialog->setVisible(false); + if (skillDialog) + skillDialog->setVisible(false); + if (descDialog) + descDialog->setVisible(false); + // TODO: Delete dialogs here +} + +void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) +{ + if (specDialog) + delete specDialog; + specDialog = new SelectSpecializationDialog(environment, environment.mWindowManager->getGui()->getViewSize()); + specDialog->eventCancel = MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + specDialog->eventItemSelected = MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); + specDialog->setVisible(true); +} + +void CreateClassDialog::onSpecializationSelected() +{ + specializationId = specDialog->getSpecializationId(); + specializationName->setCaption(environment.mWindowManager->getGameSettingString(ESM::Class::gmstSpecializationIds[specializationId], "")); + specDialog->setVisible(false); +} + +void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) +{ + if (attribDialog) + delete attribDialog; + attribDialog = new SelectAttributeDialog(environment, environment.mWindowManager->getGui()->getViewSize()); + attribDialog->setAffectedWidget(_sender); + attribDialog->eventCancel = MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + attribDialog->eventItemSelected = MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); + attribDialog->setVisible(true); +} + +void CreateClassDialog::onAttributeSelected() +{ + ESM::Attribute::AttributeID id = attribDialog->getAttributeId(); + Widgets::MWAttributePtr attribute = attribDialog->getAffectedWidget(); + if (attribute == favoriteAttribute0) + { + if (favoriteAttribute1->getAttributeId() == id) + favoriteAttribute1->setAttributeId(favoriteAttribute0->getAttributeId()); + } + else if (attribute == favoriteAttribute1) + { + if (favoriteAttribute0->getAttributeId() == id) + favoriteAttribute0->setAttributeId(favoriteAttribute1->getAttributeId()); + } + attribute->setAttributeId(id); + attribDialog->setVisible(false); +} + +void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) +{ + if (skillDialog) + delete skillDialog; + skillDialog = new SelectSkillDialog(environment, environment.mWindowManager->getGui()->getViewSize()); + skillDialog->setAffectedWidget(_sender); + skillDialog->eventCancel = MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + skillDialog->eventItemSelected = MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); + skillDialog->setVisible(true); +} + +void CreateClassDialog::onSkillSelected() +{ + ESM::Skill::SkillEnum id = skillDialog->getSkillId(); + Widgets::MWSkillPtr skill = skillDialog->getAffectedWidget(); + + // Avoid duplicate skills by swapping any skill field that matches the selected one + std::vector::const_iterator end = skills.end(); + for (std::vector::const_iterator it = skills.begin(); it != end; ++it) + { + if (*it == skill) + continue; + if ((*it)->getSkillId() == id) + { + (*it)->setSkillId(skill->getSkillId()); + break; + } + } + + skill->setSkillId(skillDialog->getSkillId()); + skillDialog->setVisible(false); +} + +void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) +{ + if (descDialog) + delete descDialog; + descDialog = new DescriptionDialog(environment, environment.mWindowManager->getGui()->getViewSize()); + descDialog->setTextInput(description); + descDialog->eventDone = MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); + descDialog->setVisible(true); +} + +void CreateClassDialog::onDescriptionEntered() +{ + description = descDialog->getTextInput(); + descDialog->setVisible(false); +} + +void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) +{ + eventDone(); +} + +void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) +{ + eventBack(); +} + +/* SelectSpecializationDialog */ + +SelectSpecializationDialog::SelectSpecializationDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_select_specialization_layout.xml") +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + WindowManager *wm = environment.mWindowManager; + + setText("LabelT", wm->getGameSettingString("sSpecializationMenu1", "")); + + getWidget(specialization0, "Specialization0"); + getWidget(specialization1, "Specialization1"); + getWidget(specialization2, "Specialization2"); + specialization0->setCaption(wm->getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Combat], "")); + specialization0->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + specialization1->setCaption(wm->getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Magic], "")); + specialization1->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + specialization2->setCaption(wm->getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Stealth], "")); + specialization2->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + specializationId = ESM::Class::Combat; + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(wm->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); +} + +// widget controls + +void SelectSpecializationDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) +{ + if (_sender == specialization0) + specializationId = ESM::Class::Combat; + else if (_sender == specialization1) + specializationId = ESM::Class::Magic; + else if (_sender == specialization2) + specializationId = ESM::Class::Stealth; + else + return; + + eventItemSelected(); +} + +void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) +{ + eventCancel(); +} + +/* SelectAttributeDialog */ + +SelectAttributeDialog::SelectAttributeDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_select_attribute_layout.xml") +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + WindowManager *wm = environment.mWindowManager; + + setText("LabelT", wm->getGameSettingString("sAttributesMenu1", "")); + + getWidget(attribute0, "Attribute0"); + getWidget(attribute1, "Attribute1"); + getWidget(attribute2, "Attribute2"); + getWidget(attribute3, "Attribute3"); + getWidget(attribute4, "Attribute4"); + getWidget(attribute5, "Attribute5"); + getWidget(attribute6, "Attribute6"); + getWidget(attribute7, "Attribute7"); + + Widgets::MWAttributePtr attributes[8] = { + attribute0, + attribute1, + attribute2, + attribute3, + attribute4, + attribute5, + attribute6, + attribute7 + }; + + for (int i = 0; i < 8; ++i) + { + attributes[i]->setWindowManager(wm); + attributes[i]->setAttributeId(ESM::Attribute::attributeIds[i]); + attributes[i]->eventClicked = MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked); + } + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(wm->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); +} + +// widget controls + +void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) +{ + // TODO: Change MWAttribute to set and get AttributeID enum instead of int + attributeId = static_cast(_sender->getAttributeId()); + eventItemSelected(); +} + +void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) +{ + eventCancel(); +} + + +/* SelectSkillDialog */ + +SelectSkillDialog::SelectSkillDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_select_skill_layout.xml") +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + WindowManager *wm = environment.mWindowManager; + + setText("LabelT", wm->getGameSettingString("sSkillsMenu1", "")); + setText("CombatLabelT", wm->getGameSettingString("sSpecializationCombat", "")); + setText("MagicLabelT", wm->getGameSettingString("sSpecializationMagic", "")); + setText("StealthLabelT", wm->getGameSettingString("sSpecializationStealth", "")); + + getWidget(combatSkill0, "CombatSkill0"); + getWidget(combatSkill1, "CombatSkill1"); + getWidget(combatSkill2, "CombatSkill2"); + getWidget(combatSkill3, "CombatSkill3"); + getWidget(combatSkill4, "CombatSkill4"); + getWidget(combatSkill5, "CombatSkill5"); + getWidget(combatSkill6, "CombatSkill6"); + getWidget(combatSkill7, "CombatSkill7"); + getWidget(combatSkill8, "CombatSkill8"); + + getWidget(magicSkill0, "MagicSkill0"); + getWidget(magicSkill1, "MagicSkill1"); + getWidget(magicSkill2, "MagicSkill2"); + getWidget(magicSkill3, "MagicSkill3"); + getWidget(magicSkill4, "MagicSkill4"); + getWidget(magicSkill5, "MagicSkill5"); + getWidget(magicSkill6, "MagicSkill6"); + getWidget(magicSkill7, "MagicSkill7"); + getWidget(magicSkill8, "MagicSkill8"); + + getWidget(stealthSkill0, "StealthSkill0"); + getWidget(stealthSkill1, "StealthSkill1"); + getWidget(stealthSkill2, "StealthSkill2"); + getWidget(stealthSkill3, "StealthSkill3"); + getWidget(stealthSkill4, "StealthSkill4"); + getWidget(stealthSkill5, "StealthSkill5"); + getWidget(stealthSkill6, "StealthSkill6"); + getWidget(stealthSkill7, "StealthSkill7"); + getWidget(stealthSkill8, "StealthSkill8"); + + struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} skills[3][9] = { + { + {combatSkill0, ESM::Skill::Block}, + {combatSkill1, ESM::Skill::Armorer}, + {combatSkill2, ESM::Skill::MediumArmor}, + {combatSkill3, ESM::Skill::HeavyArmor}, + {combatSkill4, ESM::Skill::BluntWeapon}, + {combatSkill5, ESM::Skill::LongBlade}, + {combatSkill6, ESM::Skill::Axe}, + {combatSkill7, ESM::Skill::Spear}, + {combatSkill8, ESM::Skill::Athletics} + }, + { + {magicSkill0, ESM::Skill::Enchant}, + {magicSkill1, ESM::Skill::Destruction}, + {magicSkill2, ESM::Skill::Alteration}, + {magicSkill3, ESM::Skill::Illusion}, + {magicSkill4, ESM::Skill::Conjuration}, + {magicSkill5, ESM::Skill::Mysticism}, + {magicSkill6, ESM::Skill::Restoration}, + {magicSkill7, ESM::Skill::Alchemy}, + {magicSkill8, ESM::Skill::Unarmored} + }, + { + {stealthSkill0, ESM::Skill::Security}, + {stealthSkill1, ESM::Skill::Sneak}, + {stealthSkill2, ESM::Skill::Acrobatics}, + {stealthSkill3, ESM::Skill::LightArmor}, + {stealthSkill4, ESM::Skill::ShortBlade}, + {stealthSkill5 ,ESM::Skill::Marksman}, + {stealthSkill6 ,ESM::Skill::Mercantile}, + {stealthSkill7 ,ESM::Skill::Speechcraft}, + {stealthSkill8 ,ESM::Skill::HandToHand} + } + }; + + for (int spec = 0; spec < 3; ++spec) + { + for (int i = 0; i < 9; ++i) + { + skills[spec][i].widget->setWindowManager(wm); + skills[spec][i].widget->setSkillId(skills[spec][i].skillId); + skills[spec][i].widget->eventClicked = MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); + } + } + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->setCaption(wm->getGameSettingString("sCancel", "")); + cancelButton->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); +} + +// widget controls + +void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) +{ + skillId = _sender->getSkillId(); + eventItemSelected(); +} + +void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) +{ + eventCancel(); +} + +/* DescriptionDialog */ + +DescriptionDialog::DescriptionDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_class_description_layout.xml") + , environment(environment) +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + getWidget(textEdit, "TextEdit"); + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); + okButton->setCaption(environment.mWindowManager->getGameSettingString("sInputMenu1", "")); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(textEdit); +} + +// widget controls + +void DescriptionDialog::onOkClicked(MyGUI::Widget* _sender) +{ + eventDone(); +} diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp new file mode 100644 index 000000000..7f50a8b3e --- /dev/null +++ b/apps/openmw/mwgui/class.hpp @@ -0,0 +1,347 @@ +#ifndef MWGUI_CLASS_H +#define MWGUI_CLASS_H + +#include + +#include + +#include + +#include "widgets.hpp" + +namespace MWWorld +{ + class Environment; +} + +/* + This file contains the dialogs for choosing a class. + Layout is defined by resources/mygui/openmw_chargen_class_layout.xml. + */ + +namespace MWGui +{ + using namespace MyGUI; + + class InfoBoxDialog : public OEngine::GUI::Layout + { + public: + InfoBoxDialog(MWWorld::Environment& environment); + + typedef std::vector ButtonList; + + void setText(const std::string &str); + std::string getText() const; + void setButtons(ButtonList &buttons); + + void update(); + int getChosenButton() const; + + // Events + typedef delegates::CDelegate2 EventHandle_WidgetInt; + + /** Event : Button was clicked.\n + signature : void method(MyGUI::WidgetPtr widget, int index)\n + */ + EventHandle_WidgetInt eventButtonSelected; + + protected: + void onButtonClicked(MyGUI::WidgetPtr _sender); + + private: + void center(); + + MWWorld::Environment& environment; + + int currentButton; + MyGUI::WidgetPtr textBox; + MyGUI::StaticTextPtr text; + MyGUI::WidgetPtr buttonBar; + std::vector buttons; + }; + + // Lets the player choose between 3 ways of creating a class + class ClassChoiceDialog : public InfoBoxDialog + { + public: + // Corresponds to the buttons that can be clicked + enum ClassChoice + { + Class_Generate = 0, + Class_Pick = 1, + Class_Create = 2, + Class_Back = 3 + }; + ClassChoiceDialog(MWWorld::Environment& environment); + }; + + class GenerateClassResultDialog : public OEngine::GUI::Layout + { + public: + GenerateClassResultDialog(MWWorld::Environment& environment); + + std::string getClassId() const; + void setClassId(const std::string &classId); + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Back button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventBack; + + /** Event : Dialog finished, OK button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventDone; + + protected: + void onOkClicked(MyGUI::Widget* _sender); + void onBackClicked(MyGUI::Widget* _sender); + + private: + MWWorld::Environment& environment; + + MyGUI::StaticImagePtr classImage; + MyGUI::StaticTextPtr className; + + std::string currentClassId; + }; + + class PickClassDialog : public OEngine::GUI::Layout + { + public: + PickClassDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + const std::string &getClassId() const { return currentClassId; } + void setClassId(const std::string &classId); + + void setNextButtonShow(bool shown); + void open(); + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Back button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventBack; + + /** Event : Dialog finished, OK button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventDone; + + protected: + void onSelectClass(MyGUI::List* _sender, size_t _index); + + void onOkClicked(MyGUI::Widget* _sender); + void onBackClicked(MyGUI::Widget* _sender); + + private: + void updateClasses(); + void updateStats(); + + MWWorld::Environment& environment; + + MyGUI::StaticImagePtr classImage; + MyGUI::ListPtr classList; + MyGUI::StaticTextPtr specializationName; + Widgets::MWAttributePtr favoriteAttribute0, favoriteAttribute1; + Widgets::MWSkillPtr majorSkill0, majorSkill1, majorSkill2, majorSkill3, majorSkill4; + Widgets::MWSkillPtr minorSkill0, minorSkill1, minorSkill2, minorSkill3, minorSkill4; + + std::string currentClassId; + }; + + class SelectSpecializationDialog : public OEngine::GUI::Layout + { + public: + SelectSpecializationDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + ESM::Class::Specialization getSpecializationId() const { return specializationId; } + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Cancel button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventCancel; + + /** Event : Dialog finished, specialization selected.\n + signature : void method()\n + */ + EventHandle_Void eventItemSelected; + + protected: + void onSpecializationClicked(MyGUI::Widget* _sender); + void onCancelClicked(MyGUI::Widget* _sender); + + private: + MyGUI::WidgetPtr specialization0, specialization1, specialization2; + + ESM::Class::Specialization specializationId; + }; + + class SelectAttributeDialog : public OEngine::GUI::Layout + { + public: + SelectAttributeDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + ESM::Attribute::AttributeID getAttributeId() const { return attributeId; } + Widgets::MWAttributePtr getAffectedWidget() const { return affectedWidget; } + void setAffectedWidget(Widgets::MWAttributePtr widget) { affectedWidget = widget; } + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Cancel button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventCancel; + + /** Event : Dialog finished, attribute selected.\n + signature : void method()\n + */ + EventHandle_Void eventItemSelected; + + protected: + void onAttributeClicked(Widgets::MWAttributePtr _sender); + void onCancelClicked(MyGUI::Widget* _sender); + + private: + Widgets::MWAttributePtr attribute0, attribute1, attribute2, attribute3, + attribute4, attribute5, attribute6, attribute7; + Widgets::MWAttributePtr affectedWidget; + + ESM::Attribute::AttributeID attributeId; + }; + + class SelectSkillDialog : public OEngine::GUI::Layout + { + public: + SelectSkillDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + ESM::Skill::SkillEnum getSkillId() const { return skillId; } + Widgets::MWSkillPtr getAffectedWidget() const { return affectedWidget; } + void setAffectedWidget(Widgets::MWSkillPtr widget) { affectedWidget = widget; } + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Cancel button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventCancel; + + /** Event : Dialog finished, skill selected.\n + signature : void method()\n + */ + EventHandle_Void eventItemSelected; + + protected: + void onSkillClicked(Widgets::MWSkillPtr _sender); + void onCancelClicked(MyGUI::Widget* _sender); + + private: + Widgets::MWSkillPtr combatSkill0, combatSkill1, combatSkill2, combatSkill3, combatSkill4, + combatSkill5, combatSkill6, combatSkill7, combatSkill8; + Widgets::MWSkillPtr magicSkill0, magicSkill1, magicSkill2, magicSkill3, magicSkill4, + magicSkill5, magicSkill6, magicSkill7, magicSkill8; + Widgets::MWSkillPtr stealthSkill0, stealthSkill1, stealthSkill2, stealthSkill3, stealthSkill4, + stealthSkill5, stealthSkill6, stealthSkill7, stealthSkill8; + Widgets::MWSkillPtr affectedWidget; + + ESM::Skill::SkillEnum skillId; + }; + + class DescriptionDialog : public OEngine::GUI::Layout + { + public: + DescriptionDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + std::string getTextInput() const { return textEdit ? textEdit->getOnlyText() : ""; } + void setTextInput(const std::string &text) { if (textEdit) textEdit->setOnlyText(text); } + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Dialog finished, OK button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventDone; + + protected: + void onOkClicked(MyGUI::Widget* _sender); + + private: + MWWorld::Environment& environment; + + MyGUI::EditPtr textEdit; + }; + + class CreateClassDialog : public OEngine::GUI::Layout + { + public: + CreateClassDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + virtual ~CreateClassDialog(); + + std::string getName() const; + std::string getDescription() const; + ESM::Class::Specialization getSpecializationId() const; + std::vector getFavoriteAttributes() const; + std::vector getMajorSkills() const; + std::vector getMinorSkills() const; + + void setNextButtonShow(bool shown); + void open(); + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Back button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventBack; + + /** Event : Dialog finished, OK button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventDone; + + protected: + void onOkClicked(MyGUI::Widget* _sender); + void onBackClicked(MyGUI::Widget* _sender); + + void onSpecializationClicked(MyGUI::WidgetPtr _sender); + void onSpecializationSelected(); + void onAttributeClicked(Widgets::MWAttributePtr _sender); + void onAttributeSelected(); + void onSkillClicked(Widgets::MWSkillPtr _sender); + void onSkillSelected(); + void onDescriptionClicked(MyGUI::Widget* _sender); + void onDescriptionEntered(); + void onDialogCancel(); + + private: + MWWorld::Environment& environment; + + MyGUI::EditPtr editName; + MyGUI::WidgetPtr specializationName; + Widgets::MWAttributePtr favoriteAttribute0, favoriteAttribute1; + Widgets::MWSkillPtr majorSkill0, majorSkill1, majorSkill2, majorSkill3, majorSkill4; + Widgets::MWSkillPtr minorSkill0, minorSkill1, minorSkill2, minorSkill3, minorSkill4; + std::vector skills; + std::string description; + + SelectSpecializationDialog *specDialog; + SelectAttributeDialog *attribDialog; + SelectSkillDialog *skillDialog; + DescriptionDialog *descDialog; + + ESM::Class::Specialization specializationId; + }; +} +#endif diff --git a/apps/openmw/mwgui/layouts.cpp b/apps/openmw/mwgui/layouts.cpp new file mode 100644 index 000000000..b5af8454a --- /dev/null +++ b/apps/openmw/mwgui/layouts.cpp @@ -0,0 +1,412 @@ +#include "layouts.hpp" + +#include "../mwworld/class.hpp" +#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwgui/window_manager.hpp" + +#include +#include +#include + +using namespace MWGui; + +const int StatsWindow::lineHeight = 18; + +StatsWindow::StatsWindow (MWWorld::Environment& environment) + : Layout("openmw_stats_window_layout.xml") + , environment(environment) + , lastPos(0) +{ + setCoord(0,0,498, 342); + + const char *names[][2] = + { + { "Attrib1", "sAttributeStrength" }, + { "Attrib2", "sAttributeIntelligence" }, + { "Attrib3", "sAttributeWillpower" }, + { "Attrib4", "sAttributeAgility" }, + { "Attrib5", "sAttributeSpeed" }, + { "Attrib6", "sAttributeEndurance" }, + { "Attrib7", "sAttributePersonality" }, + { "Attrib8", "sAttributeLuck" }, + { "Health_str", "sHealth" }, + { "Magicka_str", "sMagic" }, + { "Fatigue_str", "sFatigue" }, + { "Level_str", "sLevel" }, + { "Race_str", "sRace" }, + { "Class_str", "sClass" }, + { 0, 0 } + }; + + const ESMS::ESMStore &store = environment.mWorld->getStore(); + for (int i=0; names[i][0]; ++i) + { + setText (names[i][0], store.gameSettings.find (names[i][1])->str); + } + + getWidget(skillAreaWidget, "Skills"); + getWidget(skillClientWidget, "SkillClient"); + getWidget(skillScrollerWidget, "SkillScroller"); + + skillScrollerWidget->eventScrollChangePosition = MyGUI::newDelegate(this, &StatsWindow::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, &StatsWindow::onWindowResize); +} + +void StatsWindow::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 StatsWindow::onWindowResize(MyGUI::WidgetPtr window) +{ + updateScroller(); +} + +void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) +{ + MyGUI::ProgressPtr pt; + getWidget(pt, name); + pt->setProgressRange(max); + pt->setProgressPosition(val); + + std::stringstream out; + out << val << "/" << max; + setText(tname, out.str().c_str()); +} + +void StatsWindow::setPlayerName(const std::string& playerName) +{ + mMainWidget->setCaption(playerName); +} + +void StatsWindow::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 StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& value) +{ + static const char *ids[] = + { + "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", + "AttribVal6", "AttribVal7", "AttribVal8", + 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + std::ostringstream valueString; + valueString << value.getModified(); + setText (id, valueString.str()); + + if (value.getModified()>value.getBase()) + setTextColor (id, 0, 1, 0); + else if (value.getModified()& value) +{ + static const char *ids[] = + { + "HBar", "MBar", "FBar", + 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + std::string id (ids[i]); + setBar (id, id + "T", value.getCurrent(), value.getModified()); + } +} + +void StatsWindow::setValue (const std::string& id, const std::string& value) +{ + if (id=="name") + setPlayerName (value); + else if (id=="race") + setText ("RaceText", value); + else if (id=="class") + setText ("ClassText", value); +} + +void StatsWindow::setValue (const std::string& id, int value) +{ + if (id=="level") + { + std::ostringstream text; + text << value; + setText("LevelText", text.str()); + } +} + +void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& 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) + { + int skillId = skillMap[i].skillId; + 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); + } + break; + } + } +} + +void StatsWindow::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 StatsWindow::setFactions (const std::vector& factions) +{ + this->factions = factions; +} + +void StatsWindow::setBirthSign (const std::string& signId) +{ + birthSignId = signId; +} + +void StatsWindow::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 StatsWindow::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 StatsWindow::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 StatsWindow::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 StatsWindow::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 StatsWindow::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(); + + if (!factions.empty()) + { + // Add a line separator if there are items above + if (!skillWidgets.empty()) + addSeparator(coord1, coord2); + + addGroup(wm->getGameSettingString("sFaction", "Faction"), coord1, coord2); + FactionList::const_iterator end = factions.end(); + for (FactionList::const_iterator it = factions.begin(); it != end; ++it) + { + const ESM::Faction *faction = store.factions.find(it->first); + addItem(faction->name, coord1, coord2); + // TODO: Faction rank should be placed in tooltip + } + } + + if (!birthSignId.empty()) + { + // Add a line separator if there are items above + if (!skillWidgets.empty()) + addSeparator(coord1, coord2); + + addGroup(wm->getGameSettingString("sSign", "Sign"), coord1, coord2); + const ESM::BirthSign *sign = store.birthSigns.find(birthSignId); + addItem(sign->name, coord1, coord2); + } + + // Add a line separator if there are items above + if (!skillWidgets.empty()) + addSeparator(coord1, coord2); + + addValueItem(wm->getGameSettingString("sReputation", "Reputation"), boost::lexical_cast(static_cast(reputation)), CS_Normal, coord1, coord2); + addValueItem(wm->getGameSettingString("sBounty", "Bounty"), boost::lexical_cast(static_cast(bounty)), CS_Normal, coord1, coord2); + + clientHeight = coord1.top; + updateScroller(); +} + +void StatsWindow::updateScroller() +{ + skillScrollerWidget->setScrollRange(std::max(clientHeight - skillClientWidget->getHeight(), 0)); + skillScrollerWidget->setScrollPage(std::max(skillClientWidget->getHeight() - lineHeight, 0)); +} diff --git a/apps/openmw/mwgui/layouts.hpp b/apps/openmw/mwgui/layouts.hpp new file mode 100644 index 000000000..2caf33a1d --- /dev/null +++ b/apps/openmw/mwgui/layouts.hpp @@ -0,0 +1,364 @@ +#ifndef MWGUI_LAYOUTS_H +#define MWGUI_LAYOUTS_H + +#include + +#include + +#include + +#include +#include +#include +#include + +#include "../mwmechanics/stat.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" + +/* + This file contains classes corresponding to all the window layouts + defined in resources/mygui/ *.xml. + + Each class inherites GUI::Layout and loads the XML file, and + provides some helper functions to manipulate the elements of the + window. + + The windows are never created or destroyed (except at startup and + shutdown), they are only hid. You can control visibility with + setVisible(). + */ + +namespace MWGui +{ + class HUD : public OEngine::GUI::Layout + { + public: + HUD(int width, int height) + : Layout("openmw_hud_layout.xml") + { + setCoord(0,0, width, height); + + // Energy bars + getWidget(health, "Health"); + getWidget(magicka, "Magicka"); + getWidget(stamina, "Stamina"); + + // Item and spell images and status bars + getWidget(weapImage, "WeapImage"); + getWidget(weapStatus, "WeapStatus"); + getWidget(spellImage, "SpellImage"); + getWidget(spellStatus, "SpellStatus"); + + getWidget(effectBox, "EffectBox"); + getWidget(effect1, "Effect1"); + + getWidget(minimap, "MiniMap"); + getWidget(compass, "Compass"); + + getWidget(crosshair, "Crosshair"); + + compass->setImageTexture("textures\\compass.dds"); + crosshair->setImageTexture("textures\\target.dds"); + + // These are just demo values, you should replace these with + // real calls from outside the class later. + setWeapIcon("icons\\w\\tx_knife_iron.dds"); + setWeapStatus(90, 100); + setSpellIcon("icons\\s\\b_tx_s_rstor_health.dds"); + setSpellStatus(65, 100); + setEffect("icons\\s\\tx_s_chameleon.dds"); + } + + void setStats(int h, int hmax, int m, int mmax, int s, int smax) + { + health->setProgressRange(hmax); + health->setProgressPosition(h); + magicka->setProgressRange(mmax); + magicka->setProgressPosition(m); + stamina->setProgressRange(smax); + stamina->setProgressPosition(s); + } + + void setWeapIcon(const char *str) + { weapImage->setImageTexture(str); } + void setSpellIcon(const char *str) + { spellImage->setImageTexture(str); } + + void setWeapStatus(int s, int smax) + { + weapStatus->setProgressRange(smax); + weapStatus->setProgressPosition(s); + } + void setSpellStatus(int s, int smax) + { + spellStatus->setProgressRange(smax); + spellStatus->setProgressPosition(s); + } + + void setEffect(const char *img) + { effect1->setImageTexture(img); } + + void setValue (const std::string& id, const MWMechanics::DynamicStat& value) + { + static const char *ids[] = + { + "HBar", "MBar", "FBar", + 0 + }; + + for (int i=0; ids[i]; ++i) + if (ids[i]==id) + { + switch (i) + { + case 0: + + health->setProgressRange (value.getModified()); + health->setProgressPosition (value.getCurrent()); + break; + + case 1: + + magicka->setProgressRange (value.getModified()); + magicka->setProgressPosition (value.getCurrent()); + break; + + case 2: + + stamina->setProgressRange (value.getModified()); + stamina->setProgressPosition (value.getCurrent()); + break; + } + } + } + + MyGUI::ProgressPtr health, magicka, stamina; + + MyGUI::StaticImagePtr weapImage, spellImage; + MyGUI::ProgressPtr weapStatus, spellStatus; + + MyGUI::WidgetPtr effectBox; + MyGUI::StaticImagePtr effect1; + + MyGUI::StaticImagePtr minimap; + MyGUI::StaticImagePtr compass; + + MyGUI::StaticImagePtr crosshair; + }; + + class MapWindow : public OEngine::GUI::Layout + { + public: + MapWindow() + : Layout("openmw_map_window_layout.xml") + { + setCoord(500,0,320,300); + setText("WorldButton", "World"); + setImage("Compass", "compass.dds"); + + // Obviously you should override this later on + setCellName("No Cell Loaded"); + } + + void setCellName(const std::string& cellName) + { + mMainWidget->setCaption(cellName); + } + }; + + class MainMenu : public OEngine::GUI::Layout + { + public: + MainMenu(int w, int h) + : Layout("openmw_mainmenu_layout.xml") + { + setCoord(0,0,w,h); + } + }; + + class StatsWindow : public OEngine::GUI::Layout + { + public: + typedef std::pair Faction; + typedef std::vector FactionList; + + typedef std::vector SkillList; + + StatsWindow (MWWorld::Environment& environment); + + void setBar(const std::string& name, const std::string& tname, int val, int max); + void setPlayerName(const std::string& playerName); + + /// Set value for the given ID. + void setValue (const std::string& id, const MWMechanics::Stat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const std::string& value); + void setValue (const std::string& id, int value); + + void setValue (const std::string& id, const MWMechanics::Stat& value); + + void configureSkills (const SkillList& major, const SkillList& minor); + void setFactions (const std::vector& factions); + void setBirthSign (const std::string &signId); + void setReputation (int reputation) { this->reputation = reputation; } + void setBounty (int bounty) { this->bounty = bounty; } + void updateSkillArea(); + + 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::WidgetPtr skillAreaWidget, skillClientWidget; + MyGUI::VScrollPtr skillScrollerWidget; + int lastPos, clientHeight; + + SkillList majorSkills, minorSkills, miscSkills; + std::map > skillValues; + std::map skillWidgetMap; + std::map factionWidgetMap; + FactionList factions; ///< Stores a list of factions and the current rank + std::string birthSignId; + int reputation, bounty; + std::vector skillWidgets; //< Skills and other information + }; + +#if 0 + class InventoryWindow : public OEngine::GUI::Layout + { + public: + enum CategoryMode + { + CM_All = 0, // All items + CM_Weapon = 1, // Only weapons + CM_Apparel = 2, // Apparel + CM_Magic = 3, // Magic + CM_Misc = 4 // Misc + }; + + InventoryWindow () + : Layout("openmw_inventory_window_layout.xml") + , categoryMode(CM_All) + + // color should be fetched from skin + , activeColor(0, 0, 1) + , inactiveColor(0.7, 0.7, 0.7) + { + setCoord(0, 200, 600, 400); + + // These are just demo values, you should replace these with + // real calls from outside the class later. + + mMainWidget->setCaption("Glass Frostsword"); + setText("EncumbranceBarT", "176/210"); + + MyGUI::ProgressPtr pt; + getWidget(pt, "EncumbranceBar"); + pt->setProgressRange(210); + pt->setProgressPosition(176); + + MyGUI::WidgetPtr avatar; + getWidget(avatar, "Avatar"); + + // Adjust armor rating text to bottom of avatar widget + MyGUI::StaticTextPtr armor_rating; + getWidget(armor_rating, "ArmorRating"); + armor_rating->setCaption("Armor: 11"); + MyGUI::IntCoord coord = armor_rating->getCoord(); + coord.top = avatar->getCoord().height - 4 - coord.height; + armor_rating->setCoord(coord); + + names[0] = "All"; + names[1] = "Weapon"; + names[2] = "Apparel"; + names[3] = "Magic"; + names[4] = "Misc"; + + boost::array categories = { { + CM_All, CM_Weapon, CM_Apparel, CM_Magic, CM_Misc + } }; + + // Initialize buttons with text and adjust sizes, also mark All as active button + int margin = 2; + int last_x = 0; + for (int i = 0; i < categories.size(); ++i) + { + CategoryMode mode = categories[i]; + std::string name = names[mode]; + name += "Button"; + setText(name, names[mode]); + getWidget(buttons[mode], name); + + MyGUI::ButtonPtr &button_pt = buttons[mode]; + if (mode == CM_All) + button_pt->setTextColour(activeColor); + else + button_pt->setTextColour(inactiveColor); + MyGUI::IntCoord coord = button_pt->getCoord(); + coord.left = last_x; + last_x += coord.width + margin; + button_pt->setCoord(coord); + + button_pt->eventMouseButtonClick = MyGUI::newDelegate(this, &InventoryWindow::onCategorySelected); + } + } + + void setCategory(CategoryMode mode) + { + MyGUI::ButtonPtr pt = getCategoryButton(categoryMode); + pt->setTextColour(inactiveColor); + + pt = getCategoryButton(mode); + pt->setTextColour(activeColor); + categoryMode = mode; + } + + MyGUI::ButtonPtr getCategoryButton(CategoryMode mode) + { + return buttons[mode]; + } + + void onCategorySelected(MyGUI::Widget *widget) + { + boost::array categories = { { + CM_All, CM_Weapon, CM_Apparel, CM_Magic, CM_Misc + } }; + + for (int i = 0; i < categories.size(); ++i) + { + CategoryMode mode = categories[i]; + if (widget == buttons[mode]) + { + setCategory(mode); + return; + } + } + } + + CategoryMode categoryMode; // Current category filter + MyGUI::ButtonPtr buttons[5]; // Button pointers + std::string names[5]; // Names of category buttons + + MyGUI::Colour activeColor; + MyGUI::Colour inactiveColor; + }; +#endif +} +#endif diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp new file mode 100644 index 000000000..313b097cf --- /dev/null +++ b/apps/openmw/mwgui/mode.hpp @@ -0,0 +1,46 @@ +#ifndef MWGUI_MODE_H +#define MWGUI_MODE_H + +namespace MWGui +{ + enum GuiMode + { + GM_Game, // Game mode, only HUD + GM_Inventory, // Inventory mode + GM_MainMenu, // Main menu mode + + GM_Console, // Console mode + + // None of the following are implemented yet + + GM_Dialogue, // NPC interaction + GM_Barter, + GM_Rest, + // .. more here .. + + // Startup character creation dialogs + GM_Name, + GM_Race, + GM_Birth, + GM_Class, + GM_ClassGenerate, + GM_ClassPick, + GM_ClassCreate, + GM_Review + }; + + // Windows shown in inventory mode + enum GuiWindow + { + GW_None = 0, + + GW_Map = 0x01, + GW_Inventory = 0x02, + GW_Magic = 0x04, + GW_Stats = 0x08, + + GW_ALL = 0xFF + }; +} + +#endif diff --git a/apps/openmw/mwgui/mw_layouts.hpp b/apps/openmw/mwgui/mw_layouts.hpp deleted file mode 100644 index 22e7b58fa..000000000 --- a/apps/openmw/mwgui/mw_layouts.hpp +++ /dev/null @@ -1,272 +0,0 @@ -#ifndef MWGUI_LAYOUTS_H -#define MWGUI_LAYOUTS_H - -#include - -#include - -/* - This file contains classes corresponding to all the window layouts - defined in resources/mygui/ *.xml. - - Each class inherites GUI::Layout and loads the XML file, and - provides some helper functions to manipulate the elements of the - window. - - The windows are never created or destroyed (except at startup and - shutdown), they are only hid. You can control visibility with - setVisible(). - */ - -namespace MWGui -{ - class HUD : public OEngine::GUI::Layout - { - public: - HUD(int width, int height) - : Layout("openmw_hud_layout.xml") - { - setCoord(0,0, width, height); - - // Energy bars - getWidget(health, "Health"); - getWidget(magicka, "Magicka"); - getWidget(stamina, "Stamina"); - - // Item and spell images and status bars - getWidget(weapImage, "WeapImage"); - getWidget(weapStatus, "WeapStatus"); - getWidget(spellImage, "SpellImage"); - getWidget(spellStatus, "SpellStatus"); - - getWidget(effectBox, "EffectBox"); - getWidget(effect1, "Effect1"); - - getWidget(minimap, "MiniMap"); - getWidget(compass, "Compass"); - - getWidget(crosshair, "Crosshair"); - - compass->setImageTexture("textures\\compass.dds"); - crosshair->setImageTexture("textures\\target.dds"); - - // These are just demo values, you should replace these with - // real calls from outside the class later. - setWeapIcon("icons\\w\\tx_knife_iron.dds"); - setWeapStatus(90, 100); - setSpellIcon("icons\\s\\b_tx_s_rstor_health.dds"); - setSpellStatus(65, 100); - setEffect("icons\\s\\tx_s_chameleon.dds"); - } - - void setStats(int h, int hmax, int m, int mmax, int s, int smax) - { - health->setProgressRange(hmax); - health->setProgressPosition(h); - magicka->setProgressRange(mmax); - magicka->setProgressPosition(m); - stamina->setProgressRange(smax); - stamina->setProgressPosition(s); - } - - void setWeapIcon(const char *str) - { weapImage->setImageTexture(str); } - void setSpellIcon(const char *str) - { spellImage->setImageTexture(str); } - - void setWeapStatus(int s, int smax) - { - weapStatus->setProgressRange(smax); - weapStatus->setProgressPosition(s); - } - void setSpellStatus(int s, int smax) - { - spellStatus->setProgressRange(smax); - spellStatus->setProgressPosition(s); - } - - void setEffect(const char *img) - { effect1->setImageTexture(img); } - - void setValue (const std::string& id, const MWMechanics::DynamicStat& value) - { - static const char *ids[] = - { - "HBar", "MBar", "FBar", - 0 - }; - - for (int i=0; ids[i]; ++i) - if (ids[i]==id) - { - switch (i) - { - case 0: - - health->setProgressRange (value.getModified()); - health->setProgressPosition (value.getCurrent()); - break; - - case 1: - - magicka->setProgressRange (value.getModified()); - magicka->setProgressPosition (value.getCurrent()); - break; - - case 2: - - stamina->setProgressRange (value.getModified()); - stamina->setProgressPosition (value.getCurrent()); - break; - } - } - } - - MyGUI::ProgressPtr health, magicka, stamina; - - MyGUI::StaticImagePtr weapImage, spellImage; - MyGUI::ProgressPtr weapStatus, spellStatus; - - MyGUI::WidgetPtr effectBox; - MyGUI::StaticImagePtr effect1; - - MyGUI::StaticImagePtr minimap; - MyGUI::StaticImagePtr compass; - - MyGUI::StaticImagePtr crosshair; - }; - - class MapWindow : public OEngine::GUI::Layout - { - public: - MapWindow() - : Layout("openmw_map_window_layout.xml") - { - setCoord(500,0,320,300); - setText("WorldButton", "World"); - setImage("Compass", "compass.dds"); - - // Obviously you should override this later on - setCellName("No Cell Loaded"); - } - - void setCellName(const std::string& cellName) - { - mMainWidget->setCaption(cellName); - } - }; - - class MainMenu : public OEngine::GUI::Layout - { - public: - MainMenu(int w, int h) - : Layout("openmw_mainmenu_layout.xml") - { - setCoord(0,0,w,h); - } - }; - - class StatsWindow : public OEngine::GUI::Layout - { - public: - void setBar(const std::string& name, const std::string& tname, int val, int max) - { - MyGUI::ProgressPtr pt; - getWidget(pt, name); - pt->setProgressRange(max); - pt->setProgressPosition(val); - - std::stringstream out; - out << val << "/" << max; - setText(tname, out.str().c_str()); - } - - StatsWindow (const ESMS::ESMStore& store) - : Layout("openmw_stats_window_layout.xml") - { - setCoord(0,0,498, 342); - - const char *names[][2] = - { - { "Attrib1", "sAttributeStrength" }, - { "Attrib2", "sAttributeIntelligence" }, - { "Attrib3", "sAttributeWillpower" }, - { "Attrib4", "sAttributeAgility" }, - { "Attrib5", "sAttributeSpeed" }, - { "Attrib6", "sAttributeEndurance" }, - { "Attrib7", "sAttributePersonality" }, - { "Attrib8", "sAttributeLuck" }, - { "Health_str", "sHealth" }, - { "Magicka_str", "sMagic" }, - { "Fatigue_str", "sFatigue" }, - { "Level_str", "sLevel" }, - { "Race_str", "sRace" }, - { "Class_str", "sClass" }, - { 0, 0 } - }; - - for (int i=0; names[i][0]; ++i) - { - setText (names[i][0], store.gameSettings.find (names[i][1])->str); - } - - // These are just demo values, you should replace these with - // real calls from outside the class later. - setPlayerName("ThePlayer"); - - setText("LevelText", "5"); - setText("RaceText", "Wood Elf"); - setText("ClassText", "Pilgrim"); - } - - void setPlayerName(const std::string& playerName) - { - mMainWidget->setCaption(playerName); - } - - /// Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::Stat& value) - { - static const char *ids[] = - { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8", - 0 - }; - - for (int i=0; ids[i]; ++i) - if (ids[i]==id) - { - std::ostringstream valueString; - valueString << value.getModified(); - setText (id, valueString.str()); - - if (value.getModified()>value.getBase()) - setTextColor (id, 0, 1, 0); - else if (value.getModified()& value) - { - static const char *ids[] = - { - "HBar", "MBar", "FBar", - 0 - }; - - for (int i=0; ids[i]; ++i) - if (ids[i]==id) - { - std::string id (ids[i]); - setBar (id, id + "T", value.getCurrent(), value.getModified()); - } - } - }; -} -#endif diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp new file mode 100644 index 000000000..72f400a34 --- /dev/null +++ b/apps/openmw/mwgui/race.cpp @@ -0,0 +1,310 @@ +#include "race.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.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(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_chargen_race_layout.xml") + , environment(environment) + , genderIndex(0) + , faceIndex(0) + , hairIndex(0) + , faceCount(10) + , hairCount(14) +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + // These are just demo values, you should replace these with + // real calls from outside the class later. + + WindowManager *wm = environment.mWindowManager; + setText("AppearanceT", wm->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", wm->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", wm->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", wm->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", wm->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", wm->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); + getWidget(skillList, "SkillList"); + setText("SpellPowerT", wm->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(); +} + +void RaceDialog::onBackClicked(MyGUI::Widget* _sender) +{ + eventBack(); +} + +void RaceDialog::onHeadRotate(MyGUI::VScroll*, 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(); + + ESMS::ESMStore &store = environment.mWorld->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); + + WindowManager *wm = environment.mWindowManager; + ESMS::ESMStore &store = environment.mWorld->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(wm); + 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); + + ESMS::ESMStore &store = environment.mWorld->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->setEnvironment(&environment); + spellPowerWidget->setSpellId(spellpower); + + spellPowerItems.push_back(spellPowerWidget); + + coord.top += lineHeight; + ++i; + } +} diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp new file mode 100644 index 000000000..fc147c6c7 --- /dev/null +++ b/apps/openmw/mwgui/race.hpp @@ -0,0 +1,101 @@ +#ifndef MWGUI_RACE_H +#define MWGUI_RACE_H + +#include + +#include + +#include + +namespace MWWorld +{ + class Environment; +} + +/* + This file contains the dialog for choosing a race. + Layout is defined by resources/mygui/openmw_chargen_race_layout.xml. + */ + +namespace MWGui +{ + using namespace MyGUI; + + class RaceDialog : public OEngine::GUI::Layout + { + public: + RaceDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + enum Gender + { + GM_Male, + GM_Female + }; + + const std::string &getRaceId() const { return currentRaceId; } + Gender getGender() const { return genderIndex == 0 ? GM_Male : GM_Female; } + // getFace() + // getHair() + + void setRaceId(const std::string &raceId); + void setGender(Gender gender) { genderIndex = gender == GM_Male ? 0 : 1; } + // setFace() + // setHair() + + void setNextButtonShow(bool shown); + void open(); + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Back button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventBack; + + /** Event : Dialog finished, OK button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventDone; + + protected: + void onHeadRotate(MyGUI::VScroll* _sender, size_t _position); + + void onSelectPreviousGender(MyGUI::Widget* _sender); + void onSelectNextGender(MyGUI::Widget* _sender); + + void onSelectPreviousFace(MyGUI::Widget* _sender); + void onSelectNextFace(MyGUI::Widget* _sender); + + void onSelectPreviousHair(MyGUI::Widget* _sender); + void onSelectNextHair(MyGUI::Widget* _sender); + + void onSelectRace(MyGUI::List* _sender, size_t _index); + + void onOkClicked(MyGUI::Widget* _sender); + void onBackClicked(MyGUI::Widget* _sender); + + private: + void updateRaces(); + void updateSkills(); + void updateSpellPowers(); + + MWWorld::Environment& environment; + + MyGUI::CanvasPtr appearanceBox; + MyGUI::ListPtr raceList; + MyGUI::HScrollPtr headRotate; + + MyGUI::WidgetPtr skillList; + std::vector skillItems; + + MyGUI::WidgetPtr spellPowerList; + std::vector spellPowerItems; + + int genderIndex, faceIndex, hairIndex; + int faceCount, hairCount; + + std::string currentRaceId; + }; +} +#endif diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp new file mode 100644 index 000000000..36fa68eba --- /dev/null +++ b/apps/openmw/mwgui/text_input.cpp @@ -0,0 +1,68 @@ +#include "text_input.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" + +using namespace MWGui; + +TextInputDialog::TextInputDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize) + : Layout("openmw_text_input_layout.xml") + , environment(environment) +{ + // Centre dialog + MyGUI::IntCoord coord = mMainWidget->getCoord(); + coord.left = (gameWindowSize.width - coord.width)/2; + coord.top = (gameWindowSize.height - coord.height)/2; + mMainWidget->setCoord(coord); + + getWidget(textEdit, "TextEdit"); + textEdit->eventEditSelectAccept = newDelegate(this, &TextInputDialog::onTextAccepted); + + // TODO: These buttons should be managed by a Dialog class + MyGUI::ButtonPtr okButton; + getWidget(okButton, "OKButton"); + okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); + + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(textEdit); +} + +void TextInputDialog::setNextButtonShow(bool shown) +{ + MyGUI::ButtonPtr okButton; + getWidget(okButton, "OKButton"); + if (shown) + { + okButton->setCaption("Next"); + okButton->setCoord(MyGUI::IntCoord(264 - 18, 60, 42 + 18, 23)); + } + else + { + okButton->setCaption("OK"); + okButton->setCoord(MyGUI::IntCoord(264, 60, 42, 23)); + } +} + +void TextInputDialog::setTextLabel(const std::string &label) +{ + setText("LabelT", label); +} + +void TextInputDialog::open() +{ + // Make sure the edit box has focus + MyGUI::InputManager::getInstance().setKeyFocusWidget(textEdit); + textEdit->setOnlyText(""); + setVisible(true); +} + +// widget controls + +void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) +{ + eventDone(); +} + +void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) +{ + eventDone(); +} diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp new file mode 100644 index 000000000..8a0f1c56e --- /dev/null +++ b/apps/openmw/mwgui/text_input.hpp @@ -0,0 +1,48 @@ +#ifndef MWGUI_TEXT_INPUT_H +#define MWGUI_TEXT_INPUT_H + +#include + +namespace MWWorld +{ + class Environment; +} + +/* + */ + +namespace MWGui +{ + using namespace MyGUI; + + class TextInputDialog : public OEngine::GUI::Layout + { + public: + TextInputDialog(MWWorld::Environment& environment, MyGUI::IntSize gameWindowSize); + + std::string getTextInput() const { return textEdit ? textEdit->getOnlyText() : ""; } + void setTextInput(const std::string &text) { if (textEdit) textEdit->setOnlyText(text); } + + void setNextButtonShow(bool shown); + void setTextLabel(const std::string &label); + void open(); + + // Events + typedef delegates::CDelegate0 EventHandle_Void; + + /** Event : Dialog finished, OK button clicked.\n + signature : void method()\n + */ + EventHandle_Void eventDone; + + protected: + void onOkClicked(MyGUI::Widget* _sender); + void onTextAccepted(MyGUI::Edit* _sender); + + private: + MWWorld::Environment& environment; + + MyGUI::EditPtr textEdit; + }; +} +#endif diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp new file mode 100644 index 000000000..f23a884a2 --- /dev/null +++ b/apps/openmw/mwgui/widgets.cpp @@ -0,0 +1,464 @@ +#include "widgets.hpp" +#include "window_manager.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" +#include "components/esm_store/store.hpp" + +#include + +using namespace MWGui; +using namespace MWGui::Widgets; + +/* Helper functions */ + +/* + * Fixes the filename of a texture path to use the correct .dds extension. + * This is needed on some ESM entries which point to a .tga file instead. + */ +void MWGui::Widgets::fixTexturePath(std::string &path) +{ + int offset = path.rfind("."); + if (offset < 0) + return; + path.replace(offset, path.length() - offset, ".dds"); +} + +/* MWSkill */ + +MWSkill::MWSkill() + : manager(nullptr) + , skillId(ESM::Skill::Length) + , skillNameWidget(nullptr) + , skillValueWidget(nullptr) +{ +} + +void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) +{ + skillId = skill; + updateWidgets(); +} + +void MWSkill::setSkillNumber(int skill) +{ + if (skill < 0) + setSkillId(ESM::Skill::Length); + else if (skill < ESM::Skill::Length) + setSkillId(static_cast(skill)); + else + throw new std::runtime_error("Skill number out of range"); +} + +void MWSkill::setSkillValue(const SkillValue& value_) +{ + value = value_; + updateWidgets(); +} + +void MWSkill::updateWidgets() +{ + if (skillNameWidget && manager) + { + if (skillId == ESM::Skill::Length) + { + skillNameWidget->setCaption(""); + } + else + { + const std::string &name = manager->getGameSettingString(ESM::Skill::sSkillNameIds[skillId], ""); + skillNameWidget->setCaption(name); + } + } + if (skillValueWidget) + { + SkillValue::Type modified = value.getModified(), base = value.getBase(); + skillValueWidget->setCaption(boost::lexical_cast(modified)); + if (modified > base) + skillValueWidget->setState("increased"); + else if (modified < base) + skillValueWidget->setState("decreased"); + else + skillValueWidget->setState("normal"); + } +} + +void MWSkill::onClicked(MyGUI::Widget* _sender) +{ + eventClicked(this); +} + +void MWSkill::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name) +{ + Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name); + + initialiseWidgetSkin(_info); +} + +MWSkill::~MWSkill() +{ + shutdownWidgetSkin(); +} + +void MWSkill::baseChangeWidgetSkin(ResourceSkin* _info) +{ + shutdownWidgetSkin(); + Base::baseChangeWidgetSkin(_info); + initialiseWidgetSkin(_info); +} + +void MWSkill::initialiseWidgetSkin(ResourceSkin* _info) +{ + for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter) + { + const std::string &name = *(*iter)->_getInternalData(); + if (name == "StatName") + { + MYGUI_DEBUG_ASSERT( ! skillNameWidget, "widget already assigned"); + skillNameWidget = (*iter)->castType(); + } + else if (name == "StatValue") + { + MYGUI_DEBUG_ASSERT( ! skillValueWidget, "widget already assigned"); + skillValueWidget = (*iter)->castType(); + } + else if (name == "StatNameButton") + { + MYGUI_DEBUG_ASSERT( ! skillNameWidget, "widget already assigned"); + MyGUI::ButtonPtr button = (*iter)->castType