1 #include "InGameMenu.h"
2 
3 #include "ClientUI.h"
4 #include "CUIControls.h"
5 #include "OptionsWnd.h"
6 #include "SaveFileDialog.h"
7 #include "TextBrowseWnd.h"
8 #include "../client/human/HumanClientApp.h"
9 #include "../network/Networking.h"
10 #include "../util/i18n.h"
11 #include "../util/GameRules.h"
12 #include "../util/OptionsDB.h"
13 #include "../util/Directories.h"
14 #include "../util/Logger.h"
15 
16 #include <GG/Button.h>
17 #include <GG/Clr.h>
18 #include <GG/dialogs/ThreeButtonDlg.h>
19 
20 #include <boost/filesystem/operations.hpp>
21 
22 namespace {
23     const GG::X IN_GAME_OPTIONS_WIDTH(150);
24     const GG::Y IN_GAME_OPTIONS_HEIGHT(310);
25 }
26 
InGameMenu()27 InGameMenu::InGameMenu():
28     CUIWnd(UserString("GAME_MENU_WINDOW_TITLE"),
29            GG::INTERACTIVE | GG::MODAL)
30 {}
31 
CompleteConstruction()32 void InGameMenu::CompleteConstruction() {
33     CUIWnd::CompleteConstruction();
34 
35     m_save_btn = Wnd::Create<CUIButton>(UserString("GAME_MENU_SAVE"));
36     if (HumanClientApp::GetApp()->SinglePlayerGame())
37         m_load_or_concede_btn = Wnd::Create<CUIButton>(UserString("GAME_MENU_LOAD"));
38     else
39         m_load_or_concede_btn = Wnd::Create<CUIButton>(UserString("GAME_MENU_CONCEDE"));
40     m_options_btn = Wnd::Create<CUIButton>(UserString("INTRO_BTN_OPTIONS"));
41     m_resign_btn = Wnd::Create<CUIButton>(UserString("GAME_MENU_RESIGN"));
42     m_done_btn = Wnd::Create<CUIButton>(UserString("DONE"));
43 
44     AttachChild(m_save_btn);
45     AttachChild(m_load_or_concede_btn);
46     AttachChild(m_options_btn);
47     AttachChild(m_resign_btn);
48     AttachChild(m_done_btn);
49 
50     m_save_btn->LeftClickedSignal.connect(boost::bind(&InGameMenu::Save, this));
51     if (HumanClientApp::GetApp()->SinglePlayerGame()) {
52         m_load_or_concede_btn->LeftClickedSignal.connect(boost::bind(&InGameMenu::Load, this));
53     } else {
54         m_load_or_concede_btn->LeftClickedSignal.connect(boost::bind(&InGameMenu::Concede, this));
55     }
56     m_options_btn->LeftClickedSignal.connect(boost::bind(&InGameMenu::Options, this));
57     m_resign_btn->LeftClickedSignal.connect(boost::bind(&InGameMenu::Resign, this));
58     m_done_btn->LeftClickedSignal.connect(boost::bind(&InGameMenu::Done, this));
59 
60     if (!HumanClientApp::GetApp()->CanSaveNow()) {
61         m_save_btn->Disable();
62         m_save_btn->SetBrowseModeTime(GetOptionsDB().Get<int>("ui.tooltip.delay"));
63         m_save_btn->SetBrowseInfoWnd(GG::Wnd::Create<TextBrowseWnd>(
64             UserString("BUTTON_DISABLED"),
65             UserString("SAVE_DISABLED_BROWSE_TEXT"),
66             GG::X(400)
67         ));
68     }
69 
70     if (!HumanClientApp::GetApp()->SinglePlayerGame() &&
71         !GetGameRules().Get<bool>("RULE_ALLOW_CONCEDE"))
72     {
73         m_load_or_concede_btn->Disable();
74         m_load_or_concede_btn->SetBrowseModeTime(GetOptionsDB().Get<int>("ui.tooltip.delay"));
75         m_load_or_concede_btn->SetBrowseInfoWnd(GG::Wnd::Create<TextBrowseWnd>(
76             UserString("BUTTON_DISABLED"),
77             UserString("ERROR_CONCEDE_DISABLED"),
78             GG::X(400)
79         ));
80     }
81 
82     ResetDefaultPosition();
83     DoLayout();
84 }
85 
~InGameMenu()86 InGameMenu::~InGameMenu()
87 {}
88 
CalculatePosition() const89 GG::Rect InGameMenu::CalculatePosition() const {
90     const GG::X H_MAINMENU_MARGIN(40);  //horizontal empty space
91     const GG::Y V_MAINMENU_MARGIN(40);  //vertical empty space
92 
93     // Calculate window width and height
94     GG::Pt new_size(ButtonWidth() + H_MAINMENU_MARGIN,
95                     5.75 * ButtonCellHeight() + V_MAINMENU_MARGIN); // 9 rows + 0.75 before exit button
96 
97     // This wnd determines its own position.
98     GG::Pt new_ul((HumanClientApp::GetApp()->AppWidth()  - new_size.x) / 2,
99                   (HumanClientApp::GetApp()->AppHeight() - new_size.y) / 2);
100 
101     return GG::Rect(new_ul, new_ul + new_size);
102 }
103 
ButtonWidth() const104 GG::X InGameMenu::ButtonWidth() const {
105     const GG::X MIN_BUTTON_WIDTH(160);
106     GG::X button_width(0);              //width of the buttons
107 
108     button_width = std::max(button_width, m_save_btn->MinUsableSize().x);
109     button_width = std::max(button_width, m_load_or_concede_btn->MinUsableSize().x);
110     button_width = std::max(button_width, m_options_btn->MinUsableSize().x);
111     button_width = std::max(button_width, m_resign_btn->MinUsableSize().x);
112     button_width = std::max(button_width, m_done_btn->MinUsableSize().x);
113     button_width = std::max(MIN_BUTTON_WIDTH, button_width);
114 
115     return button_width;
116 }
117 
ButtonCellHeight() const118 GG::Y InGameMenu::ButtonCellHeight() const {
119     const GG::Y MIN_BUTTON_HEIGHT(40);
120     return std::max(MIN_BUTTON_HEIGHT, m_done_btn->MinUsableSize().y);
121 }
122 
DoLayout()123 void InGameMenu::DoLayout() {
124     // place buttons
125     GG::Pt button_ul(GG::X(15), GG::Y(12));
126     GG::Pt button_lr(ButtonWidth(), m_done_btn->MinUsableSize().y);
127 
128     button_lr += button_ul;
129 
130     GG::Y button_cell_height = ButtonCellHeight();
131 
132     m_save_btn->SizeMove(button_ul, button_lr);
133     button_ul.y += GG::Y(button_cell_height);
134     button_lr.y += GG::Y(button_cell_height);
135     m_load_or_concede_btn->SizeMove(button_ul, button_lr);
136     button_ul.y += GG::Y(button_cell_height);
137     button_lr.y += GG::Y(button_cell_height);
138     m_resign_btn->SizeMove(button_ul, button_lr);
139     button_ul.y += GG::Y(button_cell_height);
140     button_lr.y += GG::Y(button_cell_height);
141     m_options_btn->SizeMove(button_ul, button_lr);
142     button_ul.y += GG::Y(button_cell_height) * 1.75;
143     button_lr.y += GG::Y(button_cell_height) * 1.75;
144     m_done_btn->SizeMove(button_ul, button_lr);
145 }
146 
KeyPress(GG::Key key,std::uint32_t key_code_point,GG::Flags<GG::ModKey> mod_keys)147 void InGameMenu::KeyPress(GG::Key key, std::uint32_t key_code_point,
148                           GG::Flags<GG::ModKey> mod_keys)
149 {
150     // Same behaviour as if "done" was pressed
151     if (key == GG::GGK_RETURN || key == GG::GGK_KP_ENTER || key == GG::GGK_ESCAPE )
152         Done();
153 }
154 
Save()155 void InGameMenu::Save() {
156     DebugLogger() << "InGameMenu::Save";
157 
158     HumanClientApp* app = HumanClientApp::GetApp();
159     if (!app)
160         return;
161     if (!app->CanSaveNow()) {
162         ErrorLogger() << "InGameMenu::Save aborting; Client app can't save now";
163         return;
164     }
165 
166     // send order changes could be made when player decide to save game
167     app->SendPartialOrders();
168 
169     try {
170         // When saving in multiplayer, you cannot see the old saves or
171         // browse directories, only give a save file name.
172         DebugLogger() << "... running save file dialog";
173         auto filename = ClientUI::GetClientUI()->GetFilenameWithSaveFileDialog(
174             SaveFileDialog::Purpose::Save,
175             app->SinglePlayerGame() ? SaveFileDialog::SaveType::SinglePlayer : SaveFileDialog::SaveType::MultiPlayer);
176 
177         if (!filename.empty()) {
178             if (!app->CanSaveNow()) {
179                 ErrorLogger() << "InGameMenu::Save aborting; Client app can't save now";
180                 throw std::runtime_error(UserString("UNABLE_TO_SAVE_NOW_TRY_AGAIN"));
181             }
182             DebugLogger() << "... initiating save to " << filename ;
183             app->SaveGame(filename);
184             CloseClicked();
185         }
186 
187     } catch (const std::exception& e) {
188         ErrorLogger() << "Exception thrown attempting save: " << e.what();
189         ClientUI::MessageBox(e.what(), true);
190     }
191 }
192 
Load()193 void InGameMenu::Load() {
194     Hide();
195     HumanClientApp::GetApp()->LoadSinglePlayerGame();
196     CloseClicked();
197 }
198 
Options()199 void InGameMenu::Options() {
200     auto options_wnd = GG::Wnd::Create<OptionsWnd>(true);
201     options_wnd->Run();
202 }
203 
Concede()204 void InGameMenu::Concede() {
205     // show confirmation dialog
206     std::shared_ptr<GG::Font> font = ClientUI::GetFont();
207     auto prompt = GG::GUI::GetGUI()->GetStyleFactory()->NewThreeButtonDlg(
208         GG::X(200), GG::Y(75), UserString("GAME_MENU_REALLY_CONCEDE"), font,
209         ClientUI::CtrlColor(), ClientUI::CtrlBorderColor(), ClientUI::CtrlColor(), ClientUI::TextColor(),
210         2, UserString("YES"), UserString("CANCEL"));
211     prompt->Run();
212     if (prompt->Result() == 0) {
213        // send ELIMINATE_SELF message
214        HumanClientApp::GetApp()->EliminateSelf();
215        CloseClicked();
216     }
217 }
218 
Resign()219 void InGameMenu::Resign() {
220     // send order changes could be made when player decides to disconnect
221     HumanClientApp::GetApp()->SendPartialOrders();
222 
223     HumanClientApp::GetApp()->ResetToIntro(false);
224     CloseClicked();
225 }
226 
Done()227 void InGameMenu::Done()
228 { m_done = true; }
229