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, &registry, 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, &registry, 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, &registry, 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, &registry, 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, &registry, 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, &registry, 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, &registry, 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