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