1 /*
2 * Copyright (C) 2002-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_player.h"
21
22 #include "base/i18n.h"
23 #include "base/macros.h"
24 #include "economy/flag.h"
25 #include "game_io/game_loader.h"
26 #include "graphic/game_renderer.h"
27 #include "graphic/mouse_cursor.h"
28 #include "logic/cmd_queue.h"
29 #include "logic/map_objects/checkstep.h"
30 #include "logic/map_objects/immovable.h"
31 #include "logic/map_objects/tribes/building.h"
32 #include "logic/map_objects/tribes/constructionsite.h"
33 #include "logic/map_objects/tribes/productionsite.h"
34 #include "logic/map_objects/tribes/soldier.h"
35 #include "logic/map_objects/tribes/tribe_descr.h"
36 #include "logic/message_queue.h"
37 #include "logic/player.h"
38 #include "ui_basic/unique_window.h"
39 #include "wui/building_statistics_menu.h"
40 #include "wui/debugconsole.h"
41 #include "wui/fieldaction.h"
42 #include "wui/game_message_menu.h"
43 #include "wui/game_objectives_menu.h"
44 #include "wui/general_statistics_menu.h"
45 #include "wui/seafaring_statistics_menu.h"
46 #include "wui/stock_menu.h"
47 #include "wui/tribal_encyclopedia.h"
48 #include "wui/ware_statistics_menu.h"
49
50 using Widelands::Building;
51 using Widelands::Map;
52
53 namespace {
54
55 // Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for
56 // 'pf'. See 'field_brightness' in fields_to_draw.cc for scale of values.
adjusted_field_brightness(const Widelands::FCoords & fcoords,const uint32_t gametime,const Widelands::Player::Field & pf)57 float adjusted_field_brightness(const Widelands::FCoords& fcoords,
58 const uint32_t gametime,
59 const Widelands::Player::Field& pf) {
60 if (pf.vision == 0) {
61 return 0.;
62 }
63
64 uint32_t brightness = 144 + fcoords.field->get_brightness();
65 brightness = std::min<uint32_t>(255, (brightness * 255) / 160);
66
67 if (pf.vision == 1) {
68 static const uint32_t kDecayTimeInMs = 20000;
69 const Widelands::Duration time_ago = gametime - pf.time_node_last_unseen;
70 if (time_ago < kDecayTimeInMs) {
71 brightness = (brightness * (2 * kDecayTimeInMs - time_ago)) / (2 * kDecayTimeInMs);
72 } else {
73 brightness = brightness / 2;
74 }
75 }
76 return brightness / 255.;
77 }
78 // Remove statistics from the text to draw if the player does not match the map object's owner
filter_info_to_draw(InfoToDraw info_to_draw,const Widelands::MapObject * object,const Widelands::Player & player)79 InfoToDraw filter_info_to_draw(InfoToDraw info_to_draw,
80 const Widelands::MapObject* object,
81 const Widelands::Player& player) {
82 InfoToDraw result = info_to_draw;
83 const Widelands::Player* owner = object->get_owner();
84 if (owner != nullptr && !player.see_all() && player.is_hostile(*owner)) {
85 result = static_cast<InfoToDraw>(result & ~InfoToDraw::kStatistics);
86 }
87 return result;
88 }
89
draw_immovables_for_visible_field(const Widelands::EditorGameBase & egbase,const FieldsToDraw::Field & field,const float scale,const InfoToDraw info_to_draw,const Widelands::Player & player,RenderTarget * dst)90 void draw_immovables_for_visible_field(const Widelands::EditorGameBase& egbase,
91 const FieldsToDraw::Field& field,
92 const float scale,
93 const InfoToDraw info_to_draw,
94 const Widelands::Player& player,
95 RenderTarget* dst) {
96 Widelands::BaseImmovable* const imm = field.fcoords.field->get_immovable();
97 if (imm != nullptr && imm->get_positions(egbase).front() == field.fcoords) {
98 imm->draw(egbase.get_gametime(), filter_info_to_draw(info_to_draw, imm, player),
99 field.rendertarget_pixel, field.fcoords, scale, dst);
100 }
101 }
102
draw_bobs_for_visible_field(const Widelands::EditorGameBase & egbase,const FieldsToDraw::Field & field,const float scale,const InfoToDraw info_to_draw,const Widelands::Player & player,RenderTarget * dst)103 void draw_bobs_for_visible_field(const Widelands::EditorGameBase& egbase,
104 const FieldsToDraw::Field& field,
105 const float scale,
106 const InfoToDraw info_to_draw,
107 const Widelands::Player& player,
108 RenderTarget* dst) {
109 for (Widelands::Bob* bob = field.fcoords.field->get_first_bob(); bob;
110 bob = bob->get_next_bob()) {
111 bob->draw(egbase, filter_info_to_draw(info_to_draw, bob, player), field.rendertarget_pixel,
112 field.fcoords, scale, dst);
113 }
114 }
115
draw_immovable_for_formerly_visible_field(const FieldsToDraw::Field & field,const Widelands::Player::Field & player_field,const float scale,RenderTarget * dst)116 void draw_immovable_for_formerly_visible_field(const FieldsToDraw::Field& field,
117 const Widelands::Player::Field& player_field,
118 const float scale,
119 RenderTarget* dst) {
120 if (player_field.map_object_descr == nullptr) {
121 return;
122 }
123
124 if (player_field.constructionsite.becomes) {
125 assert(field.owner != nullptr);
126 player_field.constructionsite.draw(
127 field.rendertarget_pixel, field.fcoords, scale, field.owner->get_playercolor(), dst);
128
129 } else if (upcast(const Widelands::BuildingDescr, building, player_field.map_object_descr)) {
130 assert(field.owner != nullptr);
131 // this is a building therefore we either draw unoccupied or idle animation
132 dst->blit_animation(field.rendertarget_pixel, field.fcoords, scale,
133 building->get_unoccupied_animation(), 0, &field.owner->get_playercolor());
134 } else if (player_field.map_object_descr->type() == Widelands::MapObjectType::FLAG) {
135 assert(field.owner != nullptr);
136 dst->blit_animation(field.rendertarget_pixel, field.fcoords, scale,
137 field.owner->tribe().flag_animation(), 0,
138 &field.owner->get_playercolor());
139 } else if (const uint32_t pic = player_field.map_object_descr->main_animation()) {
140 dst->blit_animation(field.rendertarget_pixel, field.fcoords, scale, pic, 0,
141 (field.owner == nullptr) ? nullptr : &field.owner->get_playercolor());
142 }
143 }
144
145 } // namespace
146
InteractivePlayer(Widelands::Game & g,Section & global_s,Widelands::PlayerNumber const plyn,bool const multiplayer,ChatProvider * chat_provider)147 InteractivePlayer::InteractivePlayer(Widelands::Game& g,
148 Section& global_s,
149 Widelands::PlayerNumber const plyn,
150 bool const multiplayer,
151 ChatProvider* chat_provider)
152 : InteractiveGameBase(g, global_s, NONE, multiplayer, chat_provider),
153 auto_roadbuild_mode_(global_s.get_bool("auto_roadbuild_mode", true)),
154 flag_to_connect_(Widelands::Coords::null()),
155 statisticsmenu_(toolbar(),
156 "dropdown_menu_statistics",
157 0,
158 0,
159 34U,
160 10,
161 34U,
162 /** TRANSLATORS: Title for the statistics menu button in the game */
163 _("Statistics"),
164 UI::DropdownType::kPictorialMenu,
165 UI::PanelStyle::kWui,
166 UI::ButtonStyle::kWuiPrimary),
167 grid_marker_pic_(g_gr->images().get("images/wui/overlays/grid_marker.png")) {
168 add_main_menu();
169
170 set_display_flag(InteractiveBase::dfShowWorkareaOverlap, true); // enable by default
171
172 toolbar()->add_space(15);
173
174 add_mapview_menu(MiniMapType::kStaticViewWindow);
175 add_showhide_menu();
176 add_gamespeed_menu();
177
178 toolbar()->add_space(15);
179 if (multiplayer) {
180 add_chat_ui();
181 toolbar()->add_space(15);
182 }
183
184 add_statistics_menu();
185
186 add_toolbar_button("wui/menus/objectives", "objectives", _("Objectives"), &objectives_, true);
187 objectives_.open_window = [this] { new GameObjectivesMenu(this, objectives_); };
188
189 toggle_message_menu_ =
190 add_toolbar_button("wui/menus/message_old", "messages", _("Messages"), &message_menu_, true);
191 message_menu_.open_window = [this] { new GameMessageMenu(*this, message_menu_); };
192
193 toolbar()->add_space(15);
194
195 add_toolbar_button("ui_basic/menu_help", "help", _("Help"), &encyclopedia_, true);
196 encyclopedia_.open_window = [this] {
197 new TribalEncyclopedia(*this, encyclopedia_, &game().lua());
198 };
199
200 set_player_number(plyn);
201 map_view()->field_clicked.connect([this](const Widelands::NodeAndTriangle<>& node_and_triangle) {
202 node_action(node_and_triangle);
203 });
204
205 finalize_toolbar();
206
207 #ifndef NDEBUG // only in debug builds
208 addCommand(
209 "switchplayer", [this](const std::vector<std::string>& str) { cmdSwitchPlayer(str); });
210 #endif
211
212 map_options_subscriber_ = Notifications::subscribe<NoteMapOptions>(
213 [this](const NoteMapOptions&) { rebuild_statistics_menu(); });
214 }
215
add_statistics_menu()216 void InteractivePlayer::add_statistics_menu() {
217 statisticsmenu_.set_image(g_gr->images().get("images/wui/menus/statistics.png"));
218 toolbar()->add(&statisticsmenu_);
219
220 menu_windows_.stats_seafaring.open_window = [this] {
221 new SeafaringStatisticsMenu(*this, menu_windows_.stats_seafaring);
222 };
223
224 menu_windows_.stats_stock.open_window = [this] {
225 new StockMenu(*this, menu_windows_.stats_stock);
226 };
227
228 menu_windows_.stats_buildings.open_window = [this] {
229 new BuildingStatisticsMenu(*this, menu_windows_.stats_buildings);
230 };
231
232 menu_windows_.stats_wares.open_window = [this] {
233 new WareStatisticsMenu(*this, menu_windows_.stats_wares);
234 };
235
236 menu_windows_.stats_general.open_window = [this] {
237 new GeneralStatisticsMenu(*this, menu_windows_.stats_general);
238 };
239
240 // NoteMapOptions takes care of the rebuilding
241
242 statisticsmenu_.selected.connect(
243 [this] { statistics_menu_selected(statisticsmenu_.get_selected()); });
244 }
245
rebuild_statistics_menu()246 void InteractivePlayer::rebuild_statistics_menu() {
247 statisticsmenu_.clear();
248
249 if (egbase().map().allows_seafaring()) {
250 /** TRANSLATORS: An entry in the game's statistics menu */
251 statisticsmenu_.add(_("Seafaring"), StatisticsMenuEntry::kSeafaring,
252 g_gr->images().get("images/wui/menus/statistics_seafaring.png"), false,
253 "", "E");
254 }
255
256 /** TRANSLATORS: An entry in the game's statistics menu */
257 statisticsmenu_.add(_("Stock"), StatisticsMenuEntry::kStock,
258 g_gr->images().get("images/wui/menus/statistics_stock.png"), false, "", "I");
259
260 /** TRANSLATORS: An entry in the game's statistics menu */
261 statisticsmenu_.add(_("Buildings"), StatisticsMenuEntry::kBuildings,
262 g_gr->images().get("images/wui/menus/statistics_buildings.png"), false, "",
263 "B");
264
265 /** TRANSLATORS: An entry in the game's statistics menu */
266 statisticsmenu_.add(_("Wares"), StatisticsMenuEntry::kWare,
267 g_gr->images().get("images/wui/menus/statistics_wares.png"), false, "", "P");
268
269 /** TRANSLATORS: An entry in the game's statistics menu */
270 statisticsmenu_.add(_("General"), StatisticsMenuEntry::kGeneral,
271 g_gr->images().get("images/wui/menus/statistics_general.png"), false, "",
272 "G");
273 }
274
statistics_menu_selected(StatisticsMenuEntry entry)275 void InteractivePlayer::statistics_menu_selected(StatisticsMenuEntry entry) {
276 switch (entry) {
277 case StatisticsMenuEntry::kGeneral: {
278 menu_windows_.stats_general.toggle();
279 } break;
280 case StatisticsMenuEntry::kWare: {
281 menu_windows_.stats_wares.toggle();
282 } break;
283 case StatisticsMenuEntry::kBuildings: {
284 menu_windows_.stats_buildings.toggle();
285 } break;
286 case StatisticsMenuEntry::kStock: {
287 menu_windows_.stats_stock.toggle();
288 } break;
289 case StatisticsMenuEntry::kSeafaring: {
290 if (egbase().map().allows_seafaring()) {
291 menu_windows_.stats_seafaring.toggle();
292 }
293 } break;
294 }
295 statisticsmenu_.toggle();
296 }
297
rebuild_showhide_menu()298 void InteractivePlayer::rebuild_showhide_menu() {
299 InteractiveGameBase::rebuild_showhide_menu();
300
301 showhidemenu_.add(
302 get_display_flag(dfShowWorkareaOverlap) ?
303 /** TRANSLATORS: An entry in the game's show/hide menu to toggle whether workarea overlaps
304 * are highlighted */
305 _("Hide Workarea Overlaps") :
306 /** TRANSLATORS: An entry in the game's show/hide menu to toggle whether workarea overlaps
307 * are highlighted */
308 _("Show Workarea Overlaps"),
309 ShowHideEntry::kWorkareaOverlap,
310 g_gr->images().get("images/wui/menus/show_workarea_overlap.png"), false,
311 _("Toggle whether overlapping workareas are indicated when placing a constructionsite"), "W");
312 }
313
think()314 void InteractivePlayer::think() {
315 InteractiveBase::think();
316
317 if (flag_to_connect_) {
318 Widelands::Field& field = egbase().map()[flag_to_connect_];
319 if (upcast(Widelands::Flag const, flag, field.get_immovable())) {
320 if (!flag->has_road() && !in_road_building_mode())
321 if (auto_roadbuild_mode_) {
322 // There might be a fieldaction window open, showing a button
323 // for roadbuilding. If that dialog remains open so that the
324 // button is clicked, we would enter roadbuilding mode while
325 // we are already in roadbuilding mode from the call below.
326 // That is not allowed. Therefore we must delete the
327 // fieldaction window before entering roadbuilding mode here.
328 fieldaction_.destroy();
329 map_view()->mouse_to_field(flag_to_connect_, MapView::Transition::Jump);
330 set_sel_pos(Widelands::NodeAndTriangle<>{
331 flag_to_connect_,
332 Widelands::TCoords<>(flag_to_connect_, Widelands::TriangleIndex::D)});
333 start_build_road(flag_to_connect_, field.get_owned_by(), RoadBuildingType::kRoad);
334 }
335 flag_to_connect_ = Widelands::Coords::null();
336 }
337 }
338 {
339 char const* msg_icon = "images/wui/menus/message_old.png";
340 std::string msg_tooltip = _("Messages");
341 if (uint32_t const nr_new_messages =
342 player().messages().nr_messages(Widelands::Message::Status::kNew)) {
343 msg_icon = "images/wui/menus/message_new.png";
344 msg_tooltip =
345 (boost::format(ngettext("%u new message", "%u new messages", nr_new_messages)) %
346 nr_new_messages)
347 .str();
348 }
349 toggle_message_menu_->set_pic(g_gr->images().get(msg_icon));
350 toggle_message_menu_->set_tooltip(msg_tooltip);
351 }
352 }
353
draw(RenderTarget & dst)354 void InteractivePlayer::draw(RenderTarget& dst) {
355 // Bail out if the game isn't actually loaded.
356 // This fixes a crash with displaying an error dialog during loading.
357 if (!game().is_loaded())
358 return;
359
360 draw_map_view(map_view(), &dst);
361 }
362
draw_map_view(MapView * given_map_view,RenderTarget * dst)363 void InteractivePlayer::draw_map_view(MapView* given_map_view, RenderTarget* dst) {
364 // In-game, selection can never be on triangles or have a radius.
365 assert(get_sel_radius() == 0);
366 assert(!get_sel_triangles());
367
368 const Widelands::Player& plr = player();
369 const auto& gbase = egbase();
370 const Widelands::Map& map = gbase.map();
371 const uint32_t gametime = gbase.get_gametime();
372
373 Workareas workareas = get_workarea_overlays(map);
374 auto* fields_to_draw = given_map_view->draw_terrain(gbase, workareas, false, dst);
375 const auto& road_building_s = road_building_steepness_overlays();
376
377 const float scale = 1.f / given_map_view->view().zoom;
378
379 for (size_t idx = 0; idx < fields_to_draw->size(); ++idx) {
380 auto* f = fields_to_draw->mutable_field(idx);
381
382 const Widelands::Player::Field& player_field =
383 plr.fields()[map.get_index(f->fcoords, map.get_width())];
384
385 // Adjust this field for visibility for this player.
386 if (!plr.see_all()) {
387 f->brightness = adjusted_field_brightness(f->fcoords, gametime, player_field);
388 f->road_e = player_field.r_e;
389 f->road_se = player_field.r_se;
390 f->road_sw = player_field.r_sw;
391 f->vision = player_field.vision;
392 if (player_field.vision == 1) {
393 f->owner = player_field.owner != 0 ? gbase.get_player(player_field.owner) : nullptr;
394 f->is_border = player_field.border;
395 }
396 }
397
398 // Add road building overlays if applicable.
399 if (f->vision > 0) {
400 draw_road_building(*f);
401
402 draw_bridges(dst, f, f->vision > 1 ? gametime : 0, scale);
403 draw_border_markers(*f, scale, *fields_to_draw, dst);
404
405 // Render stuff that belongs to the node.
406 if (f->vision > 1) {
407 const auto info_to_draw = get_info_to_draw(!given_map_view->is_animating());
408 draw_immovables_for_visible_field(gbase, *f, scale, info_to_draw, plr, dst);
409 draw_bobs_for_visible_field(gbase, *f, scale, info_to_draw, plr, dst);
410 } else if (f->vision == 1) {
411 // We never show census or statistics for objects in the fog.
412 draw_immovable_for_formerly_visible_field(*f, player_field, scale, dst);
413 }
414 }
415
416 // Draw work area markers.
417 if (has_workarea_preview(f->fcoords, &map)) {
418 blit_field_overlay(dst, *f, grid_marker_pic_,
419 Vector2i(grid_marker_pic_->width() / 2, grid_marker_pic_->height() / 2),
420 scale);
421 }
422
423 if (f->vision > 0) {
424 // Draw build help.
425 bool show_port_space = has_expedition_port_space(f->fcoords);
426 if (show_port_space || buildhelp()) {
427 const auto* overlay = get_buildhelp_overlay(
428 show_port_space ? f->fcoords.field->maxcaps() : plr.get_buildcaps(f->fcoords));
429 if (overlay != nullptr) {
430 blit_field_overlay(dst, *f, overlay->pic, overlay->hotspot, scale);
431 }
432 }
433
434 // Blit the selection marker.
435 if (g_mouse_cursor->is_visible() && f->fcoords == get_sel_pos().node) {
436 const Image* pic = get_sel_picture();
437 blit_field_overlay(dst, *f, pic, Vector2i(pic->width() / 2, pic->height() / 2), scale);
438 }
439
440 // Draw road building slopes.
441 {
442 const auto itb = road_building_s.find(f->fcoords);
443 if (itb != road_building_s.end()) {
444 blit_field_overlay(dst, *f, itb->second,
445 Vector2i(itb->second->width() / 2, itb->second->height() / 2),
446 scale);
447 }
448 }
449 }
450 }
451 }
452
popup_message(Widelands::MessageId const id,const Widelands::Message & message)453 void InteractivePlayer::popup_message(Widelands::MessageId const id,
454 const Widelands::Message& message) {
455 message_menu_.create();
456 dynamic_cast<GameMessageMenu&>(*message_menu_.window).show_new_message(id, message);
457 }
458
can_see(Widelands::PlayerNumber const p) const459 bool InteractivePlayer::can_see(Widelands::PlayerNumber const p) const {
460 return p == player_number() || player().see_all();
461 }
can_act(Widelands::PlayerNumber const p) const462 bool InteractivePlayer::can_act(Widelands::PlayerNumber const p) const {
463 return p == player_number();
464 }
player_number() const465 Widelands::PlayerNumber InteractivePlayer::player_number() const {
466 return player_number_;
467 }
468
469 /// Player has clicked on the given node; bring up the context menu.
node_action(const Widelands::NodeAndTriangle<> & node_and_triangle)470 void InteractivePlayer::node_action(const Widelands::NodeAndTriangle<>& node_and_triangle) {
471 const Map& map = egbase().map();
472 if (1 < player().vision(Map::get_index(node_and_triangle.node, map.get_width()))) {
473 // Special case for buildings
474 if (upcast(Building, building, map.get_immovable(node_and_triangle.node))) {
475 if (can_see(building->owner().player_number())) {
476 show_building_window(node_and_triangle.node, false, false);
477 return;
478 }
479 }
480
481 if (!in_road_building_mode()) {
482 if (try_show_ship_window()) {
483 return;
484 }
485 }
486
487 // everything else can bring up the temporary dialog
488 show_field_action(this, get_player(), &fieldaction_);
489 }
490 }
491
492 /**
493 * Global in-game keypresses:
494 * \li Space: toggles buildhelp
495 * \li i: show stock (inventory)
496 * \li m: show minimap
497 * \li o: show objectives window
498 * \li c: toggle census
499 * \li s: toggle building statistics
500 * \li Home: go to starting position
501 * \li PageUp/PageDown: change game speed
502 * \li Pause: pauses the game
503 * \li Return: write chat message
504 */
handle_key(bool const down,SDL_Keysym const code)505 bool InteractivePlayer::handle_key(bool const down, SDL_Keysym const code) {
506 if (down) {
507 switch (code.sym) {
508
509 case SDLK_i:
510 menu_windows_.stats_stock.toggle();
511 return true;
512
513 case SDLK_n:
514 message_menu_.toggle();
515 return true;
516
517 case SDLK_o:
518 objectives_.toggle();
519 return true;
520
521 case SDLK_p:
522 menu_windows_.stats_wares.toggle();
523 return true;
524
525 case SDLK_F1:
526 encyclopedia_.toggle();
527 return true;
528
529 case SDLK_b:
530 if (menu_windows_.stats_buildings.window == nullptr) {
531 new BuildingStatisticsMenu(*this, menu_windows_.stats_buildings);
532 } else {
533 menu_windows_.stats_buildings.toggle();
534 }
535 return true;
536
537 case SDLK_e:
538 if (game().map().allows_seafaring()) {
539 if (menu_windows_.stats_seafaring.window == nullptr) {
540 new SeafaringStatisticsMenu(*this, menu_windows_.stats_seafaring);
541 } else {
542 menu_windows_.stats_seafaring.toggle();
543 }
544 }
545 return true;
546
547 case SDLK_w:
548 set_display_flag(dfShowWorkareaOverlap, !get_display_flag(dfShowWorkareaOverlap));
549 return true;
550
551 case SDLK_KP_5:
552 if (code.mod & KMOD_NUM)
553 break;
554 FALLS_THROUGH;
555 case SDLK_HOME:
556 map_view()->scroll_to_field(
557 game().map().get_starting_pos(player_number_), MapView::Transition::Smooth);
558 return true;
559
560 default:
561 break;
562 }
563 }
564
565 return InteractiveGameBase::handle_key(down, code);
566 }
567
568 /**
569 * Set the player and the visibility to this
570 * player
571 */
set_player_number(uint32_t const n)572 void InteractivePlayer::set_player_number(uint32_t const n) {
573 player_number_ = n;
574 }
575
576 /**
577 * Cleanup any game-related data before loading a new game
578 * while a game is currently playing.
579 */
cleanup_for_load()580 void InteractivePlayer::cleanup_for_load() {
581 }
582
postload()583 void InteractivePlayer::postload() {
584 InteractiveGameBase::postload();
585
586 ToolbarImageset* imageset = player().tribe().toolbar_image_set();
587 if (imageset != nullptr) {
588 set_toolbar_imageset(*imageset);
589 }
590 }
591
player_hears_field(const Widelands::Coords & coords) const592 bool InteractivePlayer::player_hears_field(const Widelands::Coords& coords) const {
593 const Widelands::Player& plr = player();
594 if (plr.see_all()) {
595 return true;
596 }
597 const Widelands::Map& map = egbase().map();
598 const Widelands::Player::Field& player_field =
599 plr.fields()[map.get_index(coords, map.get_width())];
600 return (player_field.vision > 1);
601 }
602
cmdSwitchPlayer(const std::vector<std::string> & args)603 void InteractivePlayer::cmdSwitchPlayer(const std::vector<std::string>& args) {
604 if (args.size() != 2) {
605 DebugConsole::write("Usage: switchplayer <nr>");
606 return;
607 }
608
609 int const n = atoi(args[1].c_str());
610 if (n < 1 || n > kMaxPlayers || !game().get_player(n)) {
611 DebugConsole::write(str(boost::format("Player #%1% does not exist.") % n));
612 return;
613 }
614
615 DebugConsole::write(
616 str(boost::format("Switching from #%1% to #%2%.") % static_cast<int>(player_number_) % n));
617 player_number_ = n;
618
619 if (UI::UniqueWindow* const building_statistics_window = menu_windows_.stats_buildings.window) {
620 dynamic_cast<BuildingStatisticsMenu&>(*building_statistics_window).update();
621 }
622 }
623