1 /*
2 * Copyright (C) 2007-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "wui/interactive_gamebase.h"
21
22 #include <memory>
23
24 #include "base/macros.h"
25 #include "graphic/font_handler.h"
26 #include "graphic/rendertarget.h"
27 #include "graphic/text_layout.h"
28 #include "logic/game.h"
29 #include "logic/game_controller.h"
30 #include "logic/map.h"
31 #include "logic/map_objects/findbob.h"
32 #include "logic/map_objects/tribes/ship.h"
33 #include "logic/player.h"
34 #include "network/gamehost.h"
35 #include "wui/constructionsitewindow.h"
36 #include "wui/dismantlesitewindow.h"
37 #include "wui/game_chat_menu.h"
38 #include "wui/game_client_disconnected.h"
39 #include "wui/game_exit_confirm_box.h"
40 #include "wui/game_main_menu_save_game.h"
41 #include "wui/game_options_sound_menu.h"
42 #include "wui/game_summary.h"
43 #include "wui/interactive_player.h"
44 #include "wui/militarysitewindow.h"
45 #include "wui/productionsitewindow.h"
46 #include "wui/shipwindow.h"
47 #include "wui/trainingsitewindow.h"
48 #include "wui/unique_window_handler.h"
49 #include "wui/warehousewindow.h"
50
51 namespace {
52
speed_string(int const speed)53 std::string speed_string(int const speed) {
54 if (speed) {
55 return (boost::format("%u.%ux") % (speed / 1000) % (speed / 100 % 10)).str();
56 }
57 return _("PAUSE");
58 }
59
60 constexpr uint8_t kSpeedSlow = 250;
61 constexpr uint16_t kSpeedDefault = 1000;
62 constexpr uint16_t kSpeedFast = 10000;
63 } // namespace
64
InteractiveGameBase(Widelands::Game & g,Section & global_s,PlayerType pt,bool const multiplayer,ChatProvider * chat_provider)65 InteractiveGameBase::InteractiveGameBase(Widelands::Game& g,
66 Section& global_s,
67 PlayerType pt,
68 bool const multiplayer,
69 ChatProvider* chat_provider)
70 : InteractiveBase(g, global_s),
71 chat_provider_(chat_provider),
72 multiplayer_(multiplayer),
73 playertype_(pt),
74 showhidemenu_(toolbar(),
75 "dropdown_menu_showhide",
76 0,
77 0,
78 34U,
79 10,
80 34U,
81 /** TRANSLATORS: Title for a menu button in the game. This menu will show/hide
82 building spaces, census, statistics */
83 _("Show / Hide"),
84 UI::DropdownType::kPictorialMenu,
85 UI::PanelStyle::kWui,
86 UI::ButtonStyle::kWuiPrimary),
87 mainmenu_(toolbar(),
88 "dropdown_menu_main",
89 0,
90 0,
91 34U,
92 10,
93 34U,
94 /** TRANSLATORS: Title for the main menu button in the game */
95 as_tooltip_text_with_hotkey(_("Main Menu"), pgettext("hotkey", "Esc")),
96 UI::DropdownType::kPictorialMenu,
97 UI::PanelStyle::kWui,
98 UI::ButtonStyle::kWuiPrimary),
99 gamespeedmenu_(toolbar(),
100 "dropdown_menu_gamespeed",
101 0,
102 0,
103 34U,
104 10,
105 34U,
106 /** TRANSLATORS: Title for a menu button in the game. This menu will show
107 options o increase/decrease the gamespeed, and to pause the game */
108 _("Game Speed"),
109 UI::DropdownType::kPictorialMenu,
110 UI::PanelStyle::kWui,
111 UI::ButtonStyle::kWuiPrimary) {
112 buildingnotes_subscriber_ = Notifications::subscribe<Widelands::NoteBuilding>(
113 [this](const Widelands::NoteBuilding& note) {
114 switch (note.action) {
115 case Widelands::NoteBuilding::Action::kFinishWarp: {
116 if (upcast(
117 Widelands::Building const, building, game().objects().get_object(note.serial))) {
118 const Widelands::Coords coords = building->get_position();
119 // Check whether the window is wanted
120 if (wanted_building_windows_.count(coords.hash()) == 1) {
121 const WantedBuildingWindow& wanted_building_window =
122 *wanted_building_windows_.at(coords.hash()).get();
123 UI::UniqueWindow* building_window =
124 show_building_window(coords, true, wanted_building_window.show_workarea);
125 building_window->set_pos(wanted_building_window.window_position);
126 if (wanted_building_window.minimize) {
127 building_window->minimize();
128 }
129 building_window->set_pinned(wanted_building_window.pin);
130 wanted_building_windows_.erase(coords.hash());
131 }
132 }
133 } break;
134 default:
135 break;
136 }
137 });
138
139 if (chat_provider_ != nullptr) {
140 chat_overlay()->set_chat_provider(*chat_provider_);
141 }
142 }
143
add_main_menu()144 void InteractiveGameBase::add_main_menu() {
145 mainmenu_.set_image(g_gr->images().get("images/wui/menus/main_menu.png"));
146 toolbar()->add(&mainmenu_);
147
148 #ifndef NDEBUG // only in debug builds
149 /** TRANSLATORS: An entry in the game's main menu */
150 mainmenu_.add(_("Script Console"), MainMenuEntry::kScriptConsole,
151 g_gr->images().get("images/wui/menus/lua.png"), false,
152 /** TRANSLATORS: Tooltip for Script Console in the game's main menu */
153 "", pgettext("hotkey", "F6"));
154 #endif
155
156 menu_windows_.sound_options.open_window = [this] {
157 new GameOptionsSoundMenu(*this, menu_windows_.sound_options);
158 };
159 /** TRANSLATORS: An entry in the game's main menu */
160 mainmenu_.add(_("Sound Options"), MainMenuEntry::kOptions,
161 g_gr->images().get("images/wui/menus/options.png"), false,
162 /** TRANSLATORS: Tooltip for Sound Options in the game's main menu */
163 _("Set sound effect and music options"));
164
165 menu_windows_.savegame.open_window = [this] {
166 new GameMainMenuSaveGame(*this, menu_windows_.savegame);
167 };
168 /** TRANSLATORS: An entry in the game's main menu */
169 mainmenu_.add(_("Save Game"), MainMenuEntry::kSaveMap,
170 g_gr->images().get("images/wui/menus/save_game.png"));
171
172 mainmenu_.add(
173 /** TRANSLATORS: An entry in the game's main menu */
174 _("Exit Game"), MainMenuEntry::kExitGame, g_gr->images().get("images/wui/menus/exit.png"));
175
176 mainmenu_.selected.connect([this] { main_menu_selected(mainmenu_.get_selected()); });
177 }
178
main_menu_selected(MainMenuEntry entry)179 void InteractiveGameBase::main_menu_selected(MainMenuEntry entry) {
180 switch (entry) {
181 #ifndef NDEBUG // only in debug builds
182 case MainMenuEntry::kScriptConsole: {
183 GameChatMenu::create_script_console(this, debugconsole_, *DebugConsole::get_chat_provider());
184 } break;
185 #endif
186 case MainMenuEntry::kOptions: {
187 menu_windows_.sound_options.toggle();
188 } break;
189 case MainMenuEntry::kSaveMap: {
190 menu_windows_.savegame.toggle();
191 } break;
192 case MainMenuEntry::kExitGame: {
193 if (SDL_GetModState() & KMOD_CTRL) {
194 end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
195 } else {
196 new GameExitConfirmBox(*this, *this);
197 }
198 } break;
199 }
200 }
201
add_showhide_menu()202 void InteractiveGameBase::add_showhide_menu() {
203 showhidemenu_.set_image(g_gr->images().get("images/wui/menus/showhide.png"));
204 toolbar()->add(&showhidemenu_);
205
206 rebuild_showhide_menu();
207 showhidemenu_.selected.connect([this] { showhide_menu_selected(showhidemenu_.get_selected()); });
208 }
209
rebuild_showhide_menu()210 void InteractiveGameBase::rebuild_showhide_menu() {
211 showhidemenu_.clear();
212
213 /** TRANSLATORS: An entry in the game's show/hide menu to toggle whether building spaces are
214 * shown */
215 showhidemenu_.add(buildhelp() ? _("Hide Building Spaces") : _("Show Building Spaces"),
216 ShowHideEntry::kBuildingSpaces,
217 g_gr->images().get("images/wui/menus/toggle_buildhelp.png"), false, "",
218 pgettext("hotkey", "Space"));
219
220 /** TRANSLATORS: An entry in the game's show/hide menu to toggle whether building names are shown
221 */
222 showhidemenu_.add(get_display_flag(dfShowCensus) ? _("Hide Census") : _("Show Census"),
223 ShowHideEntry::kCensus,
224 g_gr->images().get("images/wui/menus/toggle_census.png"), false, "", "C");
225
226 showhidemenu_.add(get_display_flag(dfShowStatistics) ?
227 /** TRANSLATORS: An entry in the game's show/hide menu to toggle whether
228 * building statistics are shown */
229 _("Hide Statistics") :
230 _("Show Statistics"),
231 ShowHideEntry::kStatistics,
232 g_gr->images().get("images/wui/menus/toggle_statistics.png"), false, "", "S");
233
234 showhidemenu_.add(get_display_flag(dfShowSoldierLevels) ?
235 /** TRANSLATORS: An entry in the game's show/hide menu to toggle whether
236 * level information is shown above soldiers' heads */
237 _("Hide Soldier Levels") :
238 _("Show Soldier Levels"),
239 ShowHideEntry::kSoldierLevels,
240 g_gr->images().get("images/wui/menus/toggle_soldier_levels.png"), false, "",
241 "L");
242 }
243
showhide_menu_selected(ShowHideEntry entry)244 void InteractiveGameBase::showhide_menu_selected(ShowHideEntry entry) {
245 switch (entry) {
246 case ShowHideEntry::kBuildingSpaces: {
247 toggle_buildhelp();
248 } break;
249 case ShowHideEntry::kCensus: {
250 set_display_flag(dfShowCensus, !get_display_flag(dfShowCensus));
251 } break;
252 case ShowHideEntry::kStatistics: {
253 set_display_flag(dfShowStatistics, !get_display_flag(dfShowStatistics));
254 } break;
255 case ShowHideEntry::kSoldierLevels: {
256 set_display_flag(dfShowSoldierLevels, !get_display_flag(dfShowSoldierLevels));
257 } break;
258 case ShowHideEntry::kWorkareaOverlap: {
259 set_display_flag(dfShowWorkareaOverlap, !get_display_flag(dfShowWorkareaOverlap));
260 } break;
261 }
262 rebuild_showhide_menu();
263 }
264
add_gamespeed_menu()265 void InteractiveGameBase::add_gamespeed_menu() {
266 gamespeedmenu_.set_image(g_gr->images().get("images/wui/menus/gamespeed.png"));
267 toolbar()->add(&gamespeedmenu_);
268 rebuild_gamespeed_menu();
269 gamespeedmenu_.selected.connect(
270 [this] { gamespeed_menu_selected(gamespeedmenu_.get_selected()); });
271 }
272
rebuild_gamespeed_menu()273 void InteractiveGameBase::rebuild_gamespeed_menu() {
274 gamespeedmenu_.clear();
275
276 gamespeedmenu_.add(_("Speed +"), GameSpeedEntry::kIncrease,
277 g_gr->images().get("images/wui/menus/gamespeed_increase.png"), false,
278 /** TRANSLATORS: Tooltip for Speed + in the game's game speed menu */
279 _("Increase the game speed"), pgettext("hotkey", "Page Up"));
280
281 gamespeedmenu_.add(_("Speed -"), GameSpeedEntry::kDecrease,
282 g_gr->images().get("images/wui/menus/gamespeed_decrease.png"), false,
283 /** TRANSLATORS: Tooltip for Speed - in the game's game speed menu */
284 _("Decrease the game speed"), pgettext("hotkey", "Page Down"));
285
286 if (!is_multiplayer()) {
287 if (get_game()->game_controller() && get_game()->game_controller()->is_paused()) {
288 gamespeedmenu_.add(_("Resume"), GameSpeedEntry::kPause,
289 g_gr->images().get("images/wui/menus/gamespeed_resume.png"), false,
290 /** TRANSLATORS: Tooltip for Pause in the game's game speed menu */
291 _("Resume the Game"), pgettext("hotkey", "Pause"));
292 } else {
293 gamespeedmenu_.add(_("Pause"), GameSpeedEntry::kPause,
294 g_gr->images().get("images/wui/menus/gamespeed_pause.png"), false,
295 /** TRANSLATORS: Tooltip for Pause in the game's game speed menu */
296 _("Pause the Game"), pgettext("hotkey", "Pause"));
297 }
298 }
299 }
300
gamespeed_menu_selected(GameSpeedEntry entry)301 void InteractiveGameBase::gamespeed_menu_selected(GameSpeedEntry entry) {
302 switch (entry) {
303 case GameSpeedEntry::kIncrease: {
304 increase_gamespeed(SDL_GetModState() & KMOD_SHIFT ?
305 kSpeedSlow :
306 SDL_GetModState() & KMOD_CTRL ? kSpeedFast : kSpeedDefault);
307 // Keep the window open so that the player can click this multiple times
308 gamespeedmenu_.toggle();
309 } break;
310 case GameSpeedEntry::kDecrease: {
311 decrease_gamespeed(SDL_GetModState() & KMOD_SHIFT ?
312 kSpeedSlow :
313 SDL_GetModState() & KMOD_CTRL ? kSpeedFast : kSpeedDefault);
314 // Keep the window open so that the player can click this multiple times
315 gamespeedmenu_.toggle();
316 } break;
317 case GameSpeedEntry::kPause: {
318 if (!is_multiplayer()) {
319 toggle_game_paused();
320 }
321 } break;
322 }
323 }
324
add_chat_ui()325 void InteractiveGameBase::add_chat_ui() {
326 add_toolbar_button("wui/menus/chat", "chat", _("Chat"), &chat_, true);
327 chat_.open_window = [this] {
328 if (chat_provider_) {
329 GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
330 }
331 };
332 }
333
increase_gamespeed(uint16_t speed)334 void InteractiveGameBase::increase_gamespeed(uint16_t speed) {
335 if (GameController* const ctrl = get_game()->game_controller()) {
336 uint32_t const current_speed = ctrl->desired_speed();
337 ctrl->set_desired_speed(current_speed + speed);
338 }
339 }
340
decrease_gamespeed(uint16_t speed)341 void InteractiveGameBase::decrease_gamespeed(uint16_t speed) {
342 if (GameController* const ctrl = get_game()->game_controller()) {
343 uint32_t const current_speed = ctrl->desired_speed();
344 ctrl->set_desired_speed(current_speed > speed ? current_speed - speed : 0);
345 }
346 }
347
reset_gamespeed()348 void InteractiveGameBase::reset_gamespeed() {
349 if (GameController* const ctrl = get_game()->game_controller()) {
350 ctrl->set_desired_speed(kSpeedDefault);
351 }
352 }
353
toggle_game_paused()354 void InteractiveGameBase::toggle_game_paused() {
355 if (GameController* const ctrl = get_game()->game_controller()) {
356 ctrl->toggle_paused();
357 // Toggle Pause / Resume in the menu
358 rebuild_gamespeed_menu();
359 }
360 }
361
handle_key(bool down,SDL_Keysym code)362 bool InteractiveGameBase::handle_key(bool down, SDL_Keysym code) {
363 if (InteractiveBase::handle_key(down, code)) {
364 return true;
365 }
366
367 if (down) {
368 switch (code.sym) {
369 case SDLK_PAGEUP:
370 increase_gamespeed(
371 code.mod & KMOD_SHIFT ? kSpeedSlow : code.mod & KMOD_CTRL ? kSpeedFast : kSpeedDefault);
372 return true;
373 case SDLK_PAUSE:
374 if (code.mod & KMOD_SHIFT) {
375 reset_gamespeed();
376 } else {
377 toggle_game_paused();
378 }
379 return true;
380 case SDLK_PAGEDOWN:
381 decrease_gamespeed(
382 code.mod & KMOD_SHIFT ? kSpeedSlow : code.mod & KMOD_CTRL ? kSpeedFast : kSpeedDefault);
383 return true;
384
385 case SDLK_c:
386 set_display_flag(
387 InteractiveBase::dfShowCensus, !get_display_flag(InteractiveBase::dfShowCensus));
388 return true;
389
390 case SDLK_g:
391 menu_windows_.stats_general.toggle();
392 return true;
393
394 case SDLK_l:
395 set_display_flag(dfShowSoldierLevels, !get_display_flag(dfShowSoldierLevels));
396 return true;
397
398 case SDLK_s:
399 if (code.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
400 new GameMainMenuSaveGame(*this, menu_windows_.savegame);
401 } else {
402 set_display_flag(dfShowStatistics, !get_display_flag(dfShowStatistics));
403 }
404 return true;
405
406 case SDLK_ESCAPE:
407 InteractiveGameBase::toggle_mainmenu();
408 return true;
409
410 case SDLK_KP_ENTER:
411 case SDLK_RETURN:
412 if (chat_provider_) {
413 if (!chat_.window) {
414 GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
415 }
416 return dynamic_cast<GameChatMenu*>(chat_.window)->enter_chat_message();
417 }
418 break;
419
420 default:
421 break;
422 }
423 }
424 return false;
425 }
426
427 /// \return a pointer to the running \ref Game instance.
get_game() const428 Widelands::Game* InteractiveGameBase::get_game() const {
429 return dynamic_cast<Widelands::Game*>(&egbase());
430 }
431
game() const432 Widelands::Game& InteractiveGameBase::game() const {
433 return dynamic_cast<Widelands::Game&>(egbase());
434 }
435
draw_overlay(RenderTarget & dst)436 void InteractiveGameBase::draw_overlay(RenderTarget& dst) {
437 InteractiveBase::draw_overlay(dst);
438
439 GameController* game_controller = game().game_controller();
440 // Display the gamespeed.
441 if (game_controller != nullptr) {
442 std::string game_speed;
443 uint32_t const real = game_controller->real_speed();
444 uint32_t const desired = game_controller->desired_speed();
445 if (real == desired) {
446 if (real != 1000) {
447 game_speed = speed_string(real);
448 }
449 } else {
450 game_speed = (boost::format
451 /** TRANSLATORS: actual_speed (desired_speed) */
452 (_("%1$s (%2$s)")) %
453 speed_string(real) % speed_string(desired))
454 .str();
455 }
456
457 if (!game_speed.empty()) {
458 std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh->render(
459 as_richtext_paragraph(game_speed, UI::FontStyle::kWuiGameSpeedAndCoordinates));
460 rendered_text->draw(dst, Vector2i(get_w() - 5, 5), UI::Align::kRight);
461 }
462 }
463 }
464
set_sel_pos(Widelands::NodeAndTriangle<> const center)465 void InteractiveGameBase::set_sel_pos(Widelands::NodeAndTriangle<> const center) {
466 InteractiveBase::set_sel_pos(center);
467
468 const Widelands::Map& map = egbase().map();
469
470 // If we have an immovable, we might want to show a tooltip
471 Widelands::BaseImmovable* imm = map[center.node].get_immovable();
472 if (imm == nullptr) {
473 return set_tooltip("");
474 }
475
476 // If we have a player, only show tooltips if he sees the field
477 const Widelands::Player* player = nullptr;
478 if (upcast(InteractivePlayer, iplayer, this)) {
479 player = iplayer->get_player();
480 if (player != nullptr && !player->see_all() &&
481 (1 >= player->vision(Widelands::Map::get_index(center.node, map.get_width())))) {
482 return set_tooltip("");
483 }
484 }
485
486 if (imm->descr().type() == Widelands::MapObjectType::IMMOVABLE) {
487 // Trees, Resource Indicators, fields ...
488 return set_tooltip(imm->descr().descname());
489 } else if (upcast(Widelands::ProductionSite, productionsite, imm)) {
490 // No productionsite tips for hostile players
491 if (player == nullptr || !player->is_hostile(*productionsite->get_owner())) {
492 return set_tooltip(
493 productionsite->info_string(Widelands::Building::InfoStringFormat::kTooltip));
494 }
495 }
496 set_tooltip("");
497 }
498
499 /**
500 * Called for every game after loading (from a savegame or just from a map
501 * during single/multiplayer/scenario).
502 */
postload()503 void InteractiveGameBase::postload() {
504 show_buildhelp(false);
505
506 // Recalc whole map for changed owner stuff
507 egbase().mutable_map()->recalc_whole_map(egbase());
508
509 // Close game-relevant UI windows (but keep main menu open)
510 fieldaction_.destroy();
511 hide_minimap();
512 }
513
start()514 void InteractiveGameBase::start() {
515 // Multiplayer games don't save the view position, so we go to the starting position instead
516 if (is_multiplayer()) {
517 Widelands::PlayerNumber pln = player_number();
518 const Widelands::PlayerNumber max = game().map().get_nrplayers();
519 if (pln == 0) {
520 // Spectator, use the view of the first viable player
521 for (pln = 1; pln <= max; ++pln) {
522 if (game().get_player(pln)) {
523 break;
524 }
525 }
526 }
527 // Adding a check, just in case there was no viable player found for spectator
528 if (game().get_player(pln)) {
529 map_view()->scroll_to_field(game().map().get_starting_pos(pln), MapView::Transition::Jump);
530 }
531 }
532 }
533
toggle_mainmenu()534 void InteractiveGameBase::toggle_mainmenu() {
535 mainmenu_.toggle();
536 }
537
add_wanted_building_window(const Widelands::Coords & coords,const Vector2i point,bool was_minimal,bool was_pinned)538 void InteractiveGameBase::add_wanted_building_window(const Widelands::Coords& coords,
539 const Vector2i point,
540 bool was_minimal,
541 bool was_pinned) {
542 wanted_building_windows_.insert(std::make_pair(
543 coords.hash(), std::unique_ptr<const WantedBuildingWindow>(new WantedBuildingWindow(
544 point, was_minimal, was_pinned, has_workarea_preview(coords)))));
545 }
546
show_building_window(const Widelands::Coords & coord,bool avoid_fastclick,bool workarea_preview_wanted)547 UI::UniqueWindow* InteractiveGameBase::show_building_window(const Widelands::Coords& coord,
548 bool avoid_fastclick,
549 bool workarea_preview_wanted) {
550 Widelands::BaseImmovable* immovable = game().map().get_immovable(coord);
551 upcast(Widelands::Building, building, immovable);
552 assert(building);
553 UI::UniqueWindow::Registry& registry =
554 unique_windows().get_registry((boost::format("building_%d") % building->serial()).str());
555
556 switch (building->descr().type()) {
557 case Widelands::MapObjectType::CONSTRUCTIONSITE:
558 registry.open_window = [this, ®istry, building, avoid_fastclick, workarea_preview_wanted] {
559 new ConstructionSiteWindow(*this, registry,
560 *dynamic_cast<Widelands::ConstructionSite*>(building),
561 avoid_fastclick, workarea_preview_wanted);
562 };
563 break;
564 case Widelands::MapObjectType::DISMANTLESITE:
565 registry.open_window = [this, ®istry, building, avoid_fastclick] {
566 new DismantleSiteWindow(
567 *this, registry, *dynamic_cast<Widelands::DismantleSite*>(building), avoid_fastclick);
568 };
569 break;
570 case Widelands::MapObjectType::MILITARYSITE:
571 registry.open_window = [this, ®istry, building, avoid_fastclick, workarea_preview_wanted] {
572 new MilitarySiteWindow(*this, registry, *dynamic_cast<Widelands::MilitarySite*>(building),
573 avoid_fastclick, workarea_preview_wanted);
574 };
575 break;
576 case Widelands::MapObjectType::PRODUCTIONSITE:
577 registry.open_window = [this, ®istry, building, avoid_fastclick, workarea_preview_wanted] {
578 new ProductionSiteWindow(*this, registry,
579 *dynamic_cast<Widelands::ProductionSite*>(building),
580 avoid_fastclick, workarea_preview_wanted);
581 };
582 break;
583 case Widelands::MapObjectType::TRAININGSITE:
584 registry.open_window = [this, ®istry, building, avoid_fastclick, workarea_preview_wanted] {
585 new TrainingSiteWindow(*this, registry, *dynamic_cast<Widelands::TrainingSite*>(building),
586 avoid_fastclick, workarea_preview_wanted);
587 };
588 break;
589 case Widelands::MapObjectType::WAREHOUSE:
590 registry.open_window = [this, ®istry, building, avoid_fastclick, workarea_preview_wanted] {
591 new WarehouseWindow(*this, registry, *dynamic_cast<Widelands::Warehouse*>(building),
592 avoid_fastclick, workarea_preview_wanted);
593 };
594 break;
595 // TODO(sirver,trading): Add UI for market.
596 default:
597 log("Unable to show window for building '%s', type '%s'.\n", building->descr().name().c_str(),
598 to_string(building->descr().type()).c_str());
599 NEVER_HERE();
600 }
601 registry.create();
602 return registry.window;
603 }
604
605 /**
606 * See if we can reasonably open a ship window at the current selection position.
607 * If so, do it and return true; otherwise, return false.
608 */
try_show_ship_window()609 bool InteractiveGameBase::try_show_ship_window() {
610 const Widelands::Map& map = game().map();
611 Widelands::Area<Widelands::FCoords> area(map.get_fcoords(get_sel_pos().node), 1);
612
613 if (!(area.field->nodecaps() & Widelands::MOVECAPS_SWIM)) {
614 return false;
615 }
616
617 std::vector<Widelands::Bob*> ships;
618 if (map.find_bobs(egbase(), area, &ships, Widelands::FindBobShip())) {
619 for (Widelands::Bob* ship : ships) {
620 if (can_see(ship->owner().player_number())) {
621 // FindBobShip should have returned only ships
622 assert(ship->descr().type() == Widelands::MapObjectType::SHIP);
623 show_ship_window(dynamic_cast<Widelands::Ship*>(ship));
624 return true;
625 }
626 }
627 }
628 return false;
629 }
630
show_ship_window(Widelands::Ship * ship)631 void InteractiveGameBase::show_ship_window(Widelands::Ship* ship) {
632 UI::UniqueWindow::Registry& registry =
633 unique_windows().get_registry((boost::format("ship_%d") % ship->serial()).str());
634 registry.open_window = [this, ®istry, ship] { new ShipWindow(*this, registry, ship); };
635 registry.create();
636 }
637
show_game_summary()638 void InteractiveGameBase::show_game_summary() {
639 if (game_summary_.window) {
640 game_summary_.window->set_visible(true);
641 game_summary_.window->think();
642 return;
643 }
644 new GameSummaryScreen(this, &game_summary_);
645 }
646
show_game_client_disconnected()647 bool InteractiveGameBase::show_game_client_disconnected() {
648 assert(is_a(GameHost, get_game()->game_controller()));
649 if (!client_disconnected_.window) {
650 if (upcast(GameHost, host, get_game()->game_controller())) {
651 new GameClientDisconnected(this, client_disconnected_, host);
652 return true;
653 }
654 }
655 return false;
656 }
657