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_spectator.h"
21 
22 #include "base/i18n.h"
23 #include "base/macros.h"
24 #include "chat/chat.h"
25 #include "graphic/game_renderer.h"
26 #include "graphic/mouse_cursor.h"
27 #include "logic/game_controller.h"
28 #include "logic/player.h"
29 #include "ui_basic/textarea.h"
30 #include "ui_basic/unique_window.h"
31 #include "wui/fieldaction.h"
32 #include "wui/game_main_menu_save_game.h"
33 #include "wui/general_statistics_menu.h"
34 
35 /**
36  * Setup the replay UI for the given game.
37  */
InteractiveSpectator(Widelands::Game & g,Section & global_s,bool const multiplayer,ChatProvider * chat_provider)38 InteractiveSpectator::InteractiveSpectator(Widelands::Game& g,
39                                            Section& global_s,
40                                            bool const multiplayer,
41                                            ChatProvider* chat_provider)
42    : InteractiveGameBase(g, global_s, OBSERVER, multiplayer, chat_provider) {
43 	add_main_menu();
44 
45 	add_toolbar_button("wui/menus/statistics_general", "general_stats", _("Statistics"),
46 	                   &menu_windows_.stats_general, true);
47 	menu_windows_.stats_general.open_window = [this] {
48 		new GeneralStatisticsMenu(*this, menu_windows_.stats_general);
49 	};
50 
51 	toolbar()->add_space(15);
52 
53 	add_mapview_menu(MiniMapType::kStaticViewWindow);
54 	add_showhide_menu();
55 	add_gamespeed_menu();
56 
57 	toolbar()->add_space(15);
58 
59 	if (is_multiplayer()) {
60 		add_chat_ui();
61 	}
62 
63 	finalize_toolbar();
64 
65 	// Setup all screen elements
66 	map_view()->field_clicked.connect([this](const Widelands::NodeAndTriangle<>& node_and_triangle) {
67 		node_action(node_and_triangle);
68 	});
69 }
70 
draw(RenderTarget & dst)71 void InteractiveSpectator::draw(RenderTarget& dst) {
72 	// This fixes a crash with displaying an error dialog during loading.
73 	if (!game().is_loaded())
74 		return;
75 
76 	draw_map_view(map_view(), &dst);
77 }
78 
draw_map_view(MapView * given_map_view,RenderTarget * dst)79 void InteractiveSpectator::draw_map_view(MapView* given_map_view, RenderTarget* dst) {
80 	// In-game, selection can never be on triangles or have a radius.
81 	assert(get_sel_radius() == 0);
82 	assert(!get_sel_triangles());
83 
84 	const Widelands::Game& the_game = game();
85 	const Widelands::Map& map = the_game.map();
86 	auto* fields_to_draw =
87 	   given_map_view->draw_terrain(the_game, get_workarea_overlays(map), false, dst);
88 	const float scale = 1.f / given_map_view->view().zoom;
89 	const uint32_t gametime = the_game.get_gametime();
90 
91 	const auto info_to_draw = get_info_to_draw(!given_map_view->is_animating());
92 	for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
93 		const FieldsToDraw::Field& field = fields_to_draw->at(idx);
94 
95 		draw_bridges(dst, &field, gametime, scale);
96 		draw_border_markers(field, scale, *fields_to_draw, dst);
97 
98 		Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
99 		if (imm != nullptr && imm->get_positions(the_game).front() == field.fcoords) {
100 			imm->draw(gametime, info_to_draw, field.rendertarget_pixel, field.fcoords, scale, dst);
101 		}
102 
103 		for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
104 		     bob = bob->get_next_bob()) {
105 			bob->draw(the_game, info_to_draw, field.rendertarget_pixel, field.fcoords, scale, dst);
106 		}
107 
108 		// Draw build help.
109 		if (buildhelp()) {
110 			auto caps = Widelands::NodeCaps::CAPS_NONE;
111 			const Widelands::PlayerNumber nr_players = map.get_nrplayers();
112 			iterate_players_existing(p, nr_players, the_game, player) {
113 				const Widelands::NodeCaps nc = player->get_buildcaps(field.fcoords);
114 				if (nc > Widelands::NodeCaps::CAPS_NONE) {
115 					caps = nc;
116 					break;
117 				}
118 			}
119 			const auto* overlay = get_buildhelp_overlay(caps);
120 			if (overlay != nullptr) {
121 				blit_field_overlay(dst, field, overlay->pic, overlay->hotspot, scale);
122 			}
123 		}
124 
125 		// Blit the selection marker.
126 		if (g_mouse_cursor->is_visible() && field.fcoords == get_sel_pos().node) {
127 			const Image* pic = get_sel_picture();
128 			blit_field_overlay(dst, field, pic, Vector2i(pic->width() / 2, pic->height() / 2), scale);
129 		}
130 	}
131 }
132 
133 /**
134  * \return "our" player.
135  *
136  * \note We might want to implement a feature to watch a specific player,
137  * including their vision. Then this should be changed.
138  */
get_player() const139 Widelands::Player* InteractiveSpectator::get_player() const {
140 	return nullptr;
141 }
142 
player_hears_field(const Widelands::Coords &) const143 bool InteractiveSpectator::player_hears_field(const Widelands::Coords&) const {
144 	return true;
145 }
146 
147 // Toolbar button callback functions.
exit_btn()148 void InteractiveSpectator::exit_btn() {
149 	if (is_multiplayer()) {
150 		return;
151 	}
152 	end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
153 }
154 
can_see(Widelands::PlayerNumber) const155 bool InteractiveSpectator::can_see(Widelands::PlayerNumber) const {
156 	return true;
157 }
can_act(Widelands::PlayerNumber) const158 bool InteractiveSpectator::can_act(Widelands::PlayerNumber) const {
159 	return false;
160 }
player_number() const161 Widelands::PlayerNumber InteractiveSpectator::player_number() const {
162 	return 0;
163 }
164 
165 /**
166  * Observer has clicked on the given node; bring up the context menu.
167  */
node_action(const Widelands::NodeAndTriangle<> & node_and_triangle)168 void InteractiveSpectator::node_action(const Widelands::NodeAndTriangle<>& node_and_triangle) {
169 	// Special case for buildings
170 	if (is_a(Widelands::Building, egbase().map().get_immovable(node_and_triangle.node))) {
171 		show_building_window(node_and_triangle.node, false, false);
172 		return;
173 	}
174 
175 	if (try_show_ship_window()) {
176 		return;
177 	}
178 
179 	//  everything else can bring up the temporary dialog
180 	show_field_action(this, nullptr, &fieldaction_);
181 }
182 
183 /**
184  * Global in-game keypresses:
185  */
handle_key(bool const down,SDL_Keysym const code)186 bool InteractiveSpectator::handle_key(bool const down, SDL_Keysym const code) {
187 	return InteractiveGameBase::handle_key(down, code);
188 }
189