From b364d47b0b4f73a9074545ba57a5aaeb5398ac5c Mon Sep 17 00:00:00 2001 From: Jan Borsodi Date: Sun, 10 Oct 2010 00:42:56 +0200 Subject: [PATCH] Implemented the Class Choice dialog which allows the player to choose between three ways to define a class. Added a generic infobox dialog which displays some text and a set of buttons (vertical only for now), this is used for the Class Choice dialogs and the dialogs which shows various questions the player must choose from. The questions are currently hardcoded. Added more gui states to handle the extra class dialogs (Generate, Pick or Create). --- apps/openmw/mwgui/class.cpp | 140 ++++++++++++++++++ apps/openmw/mwgui/class.hpp | 52 +++++++ apps/openmw/mwgui/mode.hpp | 3 + apps/openmw/mwgui/window_manager.cpp | 111 +++++++++++++- apps/openmw/mwgui/window_manager.hpp | 15 ++ extern/mygui_3.0.1/CMakeLists.txt | 1 + .../openmw_infobox_layout.xml | 16 ++ 7 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 extern/mygui_3.0.1/openmw_resources/openmw_infobox_layout.xml diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 3f5bc73de..ec792daa4 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -224,6 +224,146 @@ void PickClassDialog::updateStats() 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 */ diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 15547e594..38457b7e3 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -23,6 +23,58 @@ 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 PickClassDialog : public OEngine::GUI::Layout { public: diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index b18dd9f95..313b097cf 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -23,6 +23,9 @@ namespace MWGui GM_Race, GM_Birth, GM_Class, + GM_ClassGenerate, + GM_ClassPick, + GM_ClassCreate, GM_Review }; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 3073dcc08..7c4e16bf7 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -21,6 +21,8 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment : environment(environment) , nameDialog(nullptr) , raceDialog(nullptr) + , classChoiceDialog(nullptr) + , generateClassQuestionDialog(nullptr) , pickClassDialog(nullptr) , birthSignDialog(nullptr) , nameChosen(false) @@ -67,6 +69,8 @@ WindowManager::~WindowManager() delete nameDialog; delete raceDialog; + delete classChoiceDialog; + delete generateClassQuestionDialog; delete pickClassDialog; delete birthSignDialog; } @@ -129,6 +133,22 @@ void WindowManager::updateVisible() } if (mode == GM_Class) + { + if (classChoiceDialog) + delete classChoiceDialog; + classChoiceDialog = new ClassChoiceDialog(environment); + classChoiceDialog->eventButtonSelected = MyGUI::newDelegate(this, &WindowManager::onClassChoice); + return; + } + + if (mode == GM_ClassGenerate) + { + generateClassStep = 0; + showClassQuestionDialog(); + return; + } + + if (mode == GM_ClassPick) { if (!pickClassDialog) pickClassDialog = new PickClassDialog(environment, gui->getViewSize()); @@ -139,6 +159,13 @@ void WindowManager::updateVisible() return; } + if (mode == GM_ClassCreate) + { + CreateClassDialog *ccd = new CreateClassDialog(environment, gui->getViewSize()); + ccd->open(); + return; + } + if (mode == GM_Birth) { if (!birthSignDialog) @@ -317,6 +344,88 @@ void WindowManager::onRaceDialogBack() environment.mInputManager->setGuiMode(GM_Name); } +void WindowManager::onClassChoice(MyGUI::WidgetPtr, int _index) +{ + classChoiceDialog->setVisible(false); +// classChoiceDialog = nullptr; + + if (_index == ClassChoiceDialog::Class_Generate) + { + environment.mInputManager->setGuiMode(GM_ClassGenerate); + } + else if (_index == ClassChoiceDialog::Class_Pick) + { + environment.mInputManager->setGuiMode(GM_ClassPick); + } + else if (_index == ClassChoiceDialog::Class_Create) + { + environment.mInputManager->setGuiMode(GM_ClassCreate); + } + else if (_index == ClassChoiceDialog::Class_Back) + { + environment.mInputManager->setGuiMode(GM_Race); + } +} + +void WindowManager::showClassQuestionDialog() +{ + if (!generateClassQuestionDialog) + generateClassQuestionDialog = new InfoBoxDialog(environment); + + struct Step + { + const char* text; + const char* buttons[3]; + }; + static boost::array steps = { { + {"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.", + {"Use herbs from your pack to put it to sleep?", + "Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before?", + "Draw your dagger, mercifully endings its life with a single thrust?"} + }, + {"Your mother sends you to the market with a list of goods to buy. After you finish you find that by mistake a shopkeeper has given you too much money back in exchange for one of the items.", + {"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?", + "Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?", + "Decide to put the extra money to good use and purchase items that would help your family?"} + }, + } }; + + if (generateClassStep > steps.size()) + { + environment.mInputManager->setGuiMode(GM_Class); + return; + } + if (generateClassStep == steps.size()) + { + // TODO: Show selected class + environment.mInputManager->setGuiMode(GM_Review); + return; + } + + InfoBoxDialog::ButtonList buttons; + generateClassQuestionDialog->setText(steps[generateClassStep].text); + buttons.push_back(steps[generateClassStep].buttons[0]); + buttons.push_back(steps[generateClassStep].buttons[1]); + buttons.push_back(steps[generateClassStep].buttons[2]); + generateClassQuestionDialog->setButtons(buttons); + generateClassQuestionDialog->update(); + generateClassQuestionDialog->eventButtonSelected = MyGUI::newDelegate(this, &WindowManager::onClassQuestionChosen); + generateClassQuestionDialog->setVisible(true); +} + +void WindowManager::onClassQuestionChosen(MyGUI::Widget* _sender, int _index) +{ + generateClassQuestionDialog->setVisible(false); + if (_index < 0 || _index >= 3) + { + environment.mInputManager->setGuiMode(GM_Class); + return; + } + + ++generateClassStep; + showClassQuestionDialog(); +} + void WindowManager::onPickClassDialogDone() { pickClassDialog->eventDone = MWGui::PickClassDialog::EventHandle_Void(); @@ -349,7 +458,7 @@ void WindowManager::onPickClassDialogBack() updateCharacterGeneration(); - environment.mInputManager->setGuiMode(GM_Race); + environment.mInputManager->setGuiMode(GM_Class); } void WindowManager::onBirthSignDialogDone() diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 80c70f608..a0810e091 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -20,6 +20,7 @@ namespace MyGUI { class Gui; + class Widget; } namespace Compiler @@ -42,7 +43,9 @@ namespace MWGui class Console; class TextInputDialog; + class InfoBoxDialog; class RaceDialog; + class ClassChoiceDialog; class PickClassDialog; class BirthDialog; @@ -61,6 +64,8 @@ namespace MWGui // Character creation TextInputDialog *nameDialog; RaceDialog *raceDialog; + ClassChoiceDialog *classChoiceDialog; + InfoBoxDialog *generateClassQuestionDialog; PickClassDialog *pickClassDialog; BirthDialog *birthSignDialog; @@ -72,6 +77,9 @@ namespace MWGui bool reviewNext; ///< If true then any click on Next will cause the summary to be shown + // Keeps track of current step in Generate Class dialogs + unsigned generateClassStep; + MyGUI::Gui *gui; // Current gui mode @@ -189,6 +197,13 @@ namespace MWGui void onRaceDialogDone(); void onRaceDialogBack(); + // Character generation: Choose class process + void onClassChoice(MyGUI::Widget* _sender, int _index); + + // Character generation: Generate Class + void showClassQuestionDialog(); + void onClassQuestionChosen(MyGUI::Widget* _sender, int _index); + // Character generation: Pick Class dialog void onPickClassDialogDone(); void onPickClassDialogBack(); diff --git a/extern/mygui_3.0.1/CMakeLists.txt b/extern/mygui_3.0.1/CMakeLists.txt index e982df3ff..a02b02f1b 100644 --- a/extern/mygui_3.0.1/CMakeLists.txt +++ b/extern/mygui_3.0.1/CMakeLists.txt @@ -42,6 +42,7 @@ configure_file("${SDIR}/openmw_hud_box.skin.xml" "${DDIR}/openmw_hud_box.skin.xm configure_file("${SDIR}/openmw_hud_energybar.skin.xml" "${DDIR}/openmw_hud_energybar.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_hud_layout.xml" "${DDIR}/openmw_hud_layout.xml" COPYONLY) configure_file("${SDIR}/openmw_text_input_layout.xml" "${DDIR}/openmw_text_input_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_infobox_layout.xml" "${DDIR}/openmw_infobox_layout.xml" COPYONLY) configure_file("${SDIR}/openmw_chargen_race_layout.xml" "${DDIR}/openmw_chargen_race_layout.xml" COPYONLY) configure_file("${SDIR}/openmw_chargen_class_layout.xml" "${DDIR}/openmw_chargen_class_layout.xml" COPYONLY) configure_file("${SDIR}/openmw_chargen_create_class_layout.xml" "${DDIR}/openmw_chargen_create_class_layout.xml" COPYONLY) diff --git a/extern/mygui_3.0.1/openmw_resources/openmw_infobox_layout.xml b/extern/mygui_3.0.1/openmw_resources/openmw_infobox_layout.xml new file mode 100644 index 000000000..63725a5c3 --- /dev/null +++ b/extern/mygui_3.0.1/openmw_resources/openmw_infobox_layout.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + +