The Battle for Wesnoth  1.15.3+dev
menu_events.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
3  wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Operations activated from menus/hotkeys while playing a game.
19  * E.g. Unitlist, status_table, save_game, save_map, chat, show_help, etc.
20  */
21 
22 #include "menu_events.hpp"
23 
24 #include "actions/attack.hpp"
25 #include "actions/create.hpp"
26 #include "actions/move.hpp"
27 #include "actions/undo.hpp"
28 #include "actions/vision.hpp"
29 #include "ai/manager.hpp"
30 #include "chat_command_handler.hpp"
31 #include "color.hpp"
32 #include "display_chat_manager.hpp"
33 #include "font/standard_colors.hpp"
34 #include "formula/string_utils.hpp"
35 #include "game_board.hpp"
36 #include "game_config_manager.hpp"
37 #include "game_end_exceptions.hpp"
38 #include "game_events/pump.hpp"
39 #include "preferences/game.hpp"
40 #include "game_state.hpp"
41 #include "gettext.hpp"
42 #include "gui/dialogs/chat_log.hpp"
49 #include "gui/dialogs/message.hpp"
60 #include "gui/widgets/settings.hpp"
61 #include "gui/widgets/retval.hpp"
62 #include "help/help.hpp"
63 #include "log.hpp"
64 #include "map/label.hpp"
65 #include "map/map.hpp"
66 #include "map_command_handler.hpp"
67 #include "mouse_events.hpp"
68 #include "play_controller.hpp"
71 #include "preferences/display.hpp"
72 #include "replay.hpp"
73 #include "replay_helper.hpp"
74 #include "resources.hpp"
75 #include "save_index.hpp"
76 #include "savegame.hpp"
79 #include "synced_context.hpp"
80 #include "terrain/builder.hpp"
81 #include "units/udisplay.hpp"
82 #include "units/unit.hpp"
83 #include "units/types.hpp"
84 #include "whiteboard/manager.hpp"
85 #include "sound.hpp"
86 
87 static lg::log_domain log_engine("engine");
88 #define ERR_NG LOG_STREAM(err, log_engine)
89 #define LOG_NG LOG_STREAM(info, log_engine)
90 
91 namespace events
92 {
94  : gui_(gui)
95  , pc_(pc)
96  , game_config_(game_config_manager::get()->game_config())
97  , textbox_info_()
98  , last_search_()
99  , last_search_hit_()
100 {
101 }
102 
104 {
105 }
106 
108 {
109  return pc_.gamestate();
110 }
111 
113 {
114  return gamestate().gamedata_;
115 }
116 
118 {
119  return gamestate().board_;
120 }
121 
123 {
124  return gamestate().board_.units_;
125 }
126 
127 std::vector<team>& menu_handler::teams() const
128 {
129  return gamestate().board_.teams_;
130 }
131 
133 {
134  return gamestate().board_.map();
135 }
136 
138 {
139  return textbox_info_;
140 }
141 
143 {
144  if(!gamestate().lua_kernel_) {
145  return;
146  }
147 
150 }
151 
153 {
154  gui2::dialogs::statistics_dialog::display(board().get_team(side_num));
155 }
156 
158 {
160 }
161 
163 {
164  int selected_side;
165 
166  if(gui2::dialogs::game_stats::execute(board(), gui_->viewing_team(), selected_side)) {
167  gui_->scroll_to_leader(selected_side);
168  }
169 }
170 
172 {
173  const std::string& input_name
175 
177 
178  dlg.set_title(_("Save Map As"))
179  .set_save_mode(true)
180  .set_path(input_name)
181  .set_extension(".map");
182 
183  if(!dlg.show()) {
184  return;
185  }
186 
187  try {
188  filesystem::write_file(dlg.path(), map().write());
189  gui2::show_transient_message("", _("Map saved."));
190  } catch(const filesystem::io_exception& e) {
191  utils::string_map symbols;
192  symbols["msg"] = e.what();
193  const std::string msg = VGETTEXT("Could not save the map: $msg", symbols);
195  }
196 }
197 
199 {
201  // Needed after changing fullscreen/windowed mode or display resolution
203 }
204 
206 {
207  config c;
208  c["name"] = "prototype of chat log";
209  gui2::dialogs::chat_log::display(vconfig(c), *resources::recorder);
210  // std::string text = resources::recorder->build_chat_log();
211  // gui::show_dialog(*gui_,nullptr,_("Chat Log"),"",gui::CLOSE_ONLY,nullptr,nullptr,"",&text);
212 }
213 
215 {
216  help::show_help();
217 }
218 
220 {
222  ? board().is_observer()
223  ? _("Send to observers only")
224  : _("Send to allies only")
226 }
227 
229 {
231  speak();
232 }
233 
235 {
237  speak();
238 }
239 
241 {
242  if(board().is_observer()) {
243  return !gui_->observers().empty();
244  }
245 
246  for(std::size_t n = 0; n != teams().size(); ++n) {
247  if(n != gui_->viewing_team() && teams()[gui_->viewing_team()].team_name() == teams()[n].team_name()
248  && teams()[n].is_network()) {
249  return true;
250  }
251  }
252 
253  return false;
254 }
255 
256 void menu_handler::recruit(int side_num, const map_location& last_hex)
257 {
258  std::map<const unit_type*, t_string> sample_units;
259 
260  std::set<std::string> recruits = actions::get_recruits(side_num, last_hex);
261 
262  for(const auto& recruit : recruits) {
264  if(!type) {
265  ERR_NG << "could not find unit '" << recruit << "'" << std::endl;
266  return;
267  }
268 
269  map_location ignored;
270  map_location recruit_hex = last_hex;
271  sample_units[type] = (can_recruit(type->id(), side_num, recruit_hex, ignored));
272  }
273 
274  if(sample_units.empty()) {
275  gui2::show_transient_message("", _("You have no units available to recruit."));
276  return;
277  }
278 
279  gui2::dialogs::unit_recruit dlg(sample_units, board().get_team(side_num));
280 
281  if(dlg.show()) {
282  map_location recruit_hex = last_hex;
283  const unit_type *type = dlg.get_selected_unit_type();
284  if (!type) {
285  gui2::show_transient_message("", _("No unit recruited."));
286  return;
287  }
288  do_recruit(type->id(), side_num, recruit_hex);
289  }
290 }
291 
292 void menu_handler::repeat_recruit(int side_num, const map_location& last_hex)
293 {
294  const std::string& last_recruit = board().get_team(side_num).last_recruit();
295  if(last_recruit.empty() == false) {
296  map_location recruit_hex = last_hex;
297  do_recruit(last_recruit, side_num, recruit_hex);
298  }
299 }
300 
301 // TODO: Return multiple strings here, in case more than one error applies? For
302 // example, if you start AOI S5 with 0GP and recruit a Mage, two reasons apply,
303 // leader not on keep (extrarecruit=Mage) and not enough gold.
304 t_string menu_handler::can_recruit(const std::string& name, int side_num, map_location& loc, map_location& recruited_from)
305 {
306  team& current_team = board().get_team(side_num);
307 
308  const unit_type* u_type = unit_types.find(name);
309  if(u_type == nullptr) {
310  return _("Internal error. Please report this as a bug! Details:\n")
311  + "menu_handler::can_recruit: u_type == nullptr for " + name;
312  }
313 
314  // search for the unit to be recruited in recruits
315  if(!utils::contains(actions::get_recruits(side_num, loc), name)) {
316  return VGETTEXT("You cannot recruit a $unit_type_name at this time.",
317  utils::string_map { { "unit_type_name", u_type->type_name() }});
318  }
319 
320  // TODO take a wb::future_map RAII as unit_recruit::pre_show does
321  int wb_gold = 0;
322  {
323  wb::future_map future;
324  wb_gold = (pc_.get_whiteboard() ? pc_.get_whiteboard()->get_spent_gold_for(side_num) : 0);
325  }
326  if(u_type->cost() > current_team.gold() - wb_gold)
327  {
328  if(wb_gold > 0)
329  // TRANSLATORS: "plan" refers to Planning Mode
330  return _("At this point in your plan, you will not have enough gold to recruit this unit.");
331  else
332  return _("You do not have enough gold to recruit this unit.");
333  }
334 
335  current_team.last_recruit(name);
336  const events::command_disabler disable_commands;
337 
338  {
339  wb::future_map_if_active future; /* start planned unit map scope if in planning mode */
340  std::string msg = actions::find_recruit_location(side_num, loc, recruited_from, name);
341  if(!msg.empty()) {
342  return msg;
343  }
344  } // end planned unit map scope
345 
346  return "";
347 }
348 
349 bool menu_handler::do_recruit(const std::string& name, int side_num, map_location& loc)
350 {
351  map_location recruited_from = map_location::null_location();
352  const std::string res = can_recruit(name, side_num, loc, recruited_from);
353  team& current_team = board().get_team(side_num);
354 
355  if(res.empty() && (!pc_.get_whiteboard() || !pc_.get_whiteboard()->save_recruit(name, side_num, loc))) {
356  // MP_COUNTDOWN grant time bonus for recruiting
357  current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
358 
359  // Do the recruiting.
360 
361  synced_context::run_and_throw("recruit", replay_helper::get_recruit(name, loc, recruited_from));
362  return true;
363  } else if(res.empty()) {
364  return false;
365  } else {
367  return false;
368  }
369 
370 }
371 
372 void menu_handler::recall(int side_num, const map_location& last_hex)
373 {
374  if(pc_.get_disallow_recall()) {
375  gui2::show_transient_message("", _("You are separated from your soldiers and may not recall them."));
376  return;
377  }
378 
379  team& current_team = board().get_team(side_num);
380 
381  std::vector<unit_const_ptr> recall_list_team;
382  bool empty;
383  {
384  wb::future_map future; // ensures recall list has planned recalls removed
385  recall_list_team = actions::get_recalls(side_num, last_hex);
386  empty = current_team.recall_list().empty();
387  }
388 
389  DBG_WB << "menu_handler::recall: Contents of wb-modified recall list:\n";
390  for(const unit_const_ptr& unit : recall_list_team) {
391  DBG_WB << unit->name() << " [" << unit->id() << "]\n";
392  }
393 
394  if(empty) {
396  _("There are no troops available to recall.\n(You must have veteran survivors from a previous scenario.)"));
397  return;
398  }
399  if(recall_list_team.empty()) {
400  gui2::show_transient_message("", _("You currently can't recall at the highlighted location."));
401  return;
402  }
403 
404  gui2::dialogs::unit_recall dlg(recall_list_team, current_team);
405 
406  if(!dlg.show()) {
407  return;
408  }
409 
410  int res = dlg.get_selected_index();
411  if (res < 0) {
412  gui2::show_transient_message("", _("No unit recalled."));
413  return;
414  }
415  int unit_cost = current_team.recall_cost();
416 
417  // we need to check if unit has a specific recall cost
418  // if it does we use it elsewise we use the team.recall_cost()
419  // the magic number -1 is what it gets set to if the unit doesn't
420  // have a special recall_cost of its own.
421  if(recall_list_team[res]->recall_cost() > -1) {
422  unit_cost = recall_list_team[res]->recall_cost();
423  }
424 
425  int wb_gold = pc_.get_whiteboard() ? pc_.get_whiteboard()->get_spent_gold_for(side_num) : 0;
426  if(current_team.gold() - wb_gold < unit_cost) {
427  utils::string_map i18n_symbols;
428  i18n_symbols["cost"] = std::to_string(unit_cost);
429  std::string msg = VNGETTEXT("You must have at least 1 gold piece to recall a unit.",
430  "You must have at least $cost gold pieces to recall this unit.", unit_cost, i18n_symbols);
432  return;
433  }
434 
435  LOG_NG << "recall index: " << res << "\n";
436  const events::command_disabler disable_commands;
437 
438  map_location recall_location = last_hex;
440  std::string err;
441  {
443  future; // future unit map removes invisible units from map, don't do this outside of planning mode
444  err = actions::find_recall_location(side_num, recall_location, recall_from, *recall_list_team[res].get());
445  } // end planned unit map scope
446 
447  if(!err.empty()) {
449  return;
450  }
451 
452  if(!pc_.get_whiteboard()
453  || !pc_.get_whiteboard()->save_recall(*recall_list_team[res].get(), side_num, recall_location)) {
454  bool success = synced_context::run_and_throw("recall",
455  replay_helper::get_recall(recall_list_team[res]->id(), recall_location, recall_from), true, true,
457 
458  if(!success) {
459  ERR_NG << "menu_handler::recall(): Unit does not exist in the recall list." << std::endl;
460  }
461  }
462 }
463 
464 // Highlights squares that an enemy could move to on their turn, showing how many can reach each square.
465 void menu_handler::show_enemy_moves(bool ignore_units, int side_num)
466 {
467  wb::future_map future; // use unit positions as if all planned actions were executed
468 
470  const map_location& hex_under_mouse = mh.hovered_hex();
471 
473 
474  // Compute enemy movement positions
475  for(auto& u : units()) {
476  bool invisible = u.invisible(u.get_location());
477 
478  if(board().get_team(side_num).is_enemy(u.side()) && !gui_->fogged(u.get_location()) && !u.incapacitated()
479  && !invisible) {
480  const unit_movement_resetter move_reset(u);
481  const pathfind::paths& path
482  = pathfind::paths(u, false, true, teams()[gui_->viewing_team()], 0, false, ignore_units);
483 
484  gui_->highlight_another_reach(path, hex_under_mouse);
485  }
486 
487  // Need to recompute ellipses for highlighted enemy units
488  gui_->invalidate(u.get_location());
489  }
490 
491  // Find possible unit (no matter whether friend or foe) under the
492  // mouse cursor.
493  const bool selected_hex_has_unit = mh.hex_hosts_unit(hex_under_mouse);
494 
495  if(selected_hex_has_unit) {
496  // At this point, a single pixel move would remove the enemy
497  // [best possible] movements hex tiles highlights, so some
498  // prevention on normal unit mouseover movement highlight
499  // has to be toggled temporarily.
501  }
502 }
503 
505 {
506  team& current_team = board().get_team(side_num);
507  bool auto_shroud = current_team.auto_shroud_updates();
508  // If we're turning automatic shroud updates on, then commit all moves
509  if(!auto_shroud) {
510  update_shroud_now(side_num);
511  }
512 
513  // Toggle the setting and record this.
515 }
516 
517 void menu_handler::update_shroud_now(int /* side_num */)
518 {
520 }
521 
522 // Helpers for menu_handler::end_turn()
523 namespace
524 {
525 /** Returns true if @a side_num has at least one living unit. */
526 bool units_alive(int side_num, const unit_map& units)
527 {
528  for(auto& unit : units) {
529  if(unit.side() == side_num) {
530  return true;
531  }
532  }
533  return false;
534 }
535 
536 /** Returns true if @a side_num has at least one unit that can still move. */
537 bool partmoved_units(
538  int side_num, const unit_map& units, const game_board& board, const std::shared_ptr<wb::manager>& whiteb)
539 {
540  for(auto& unit : units) {
541  if(unit.side() == side_num) {
542  // @todo whiteboard should take into consideration units that have
543  // a planned move but can still plan more movement in the same turn
544  if(board.unit_can_move(unit) && !unit.user_end_turn() && (!whiteb || !whiteb->unit_has_actions(&unit)))
545  return true;
546  }
547  }
548  return false;
549 }
550 
551 /**
552  * Returns true if @a side_num has at least one unit that (can but) has not moved.
553  */
554 bool unmoved_units(
555  int side_num, const unit_map& units, const game_board& board, const std::shared_ptr<wb::manager>& whiteb)
556 {
557  for(auto& unit : units) {
558  if(unit.side() == side_num) {
559  if(board.unit_can_move(unit) && !unit.has_moved() && !unit.user_end_turn()
560  && (!whiteb || !whiteb->unit_has_actions(&unit))) {
561  return true;
562  }
563  }
564  }
565  return false;
566 }
567 
568 } // end anon namespace
569 
570 bool menu_handler::end_turn(int side_num)
571 {
572  if(!gamedata().allow_end_turn()) {
574  if(reason.empty()) {
575  reason = _("You cannot end your turn yet!");
576  }
577  gui2::show_transient_message("", reason);
578  return false;
579  }
580 
581  std::size_t team_num = static_cast<std::size_t>(side_num - 1);
582  if(team_num < teams().size() && teams()[team_num].no_turn_confirmation()) {
583  // Skip the confirmations that follow.
584  }
585  // Ask for confirmation if the player hasn't made any moves.
587  && (!pc_.get_whiteboard() || !pc_.get_whiteboard()->current_side_has_actions())
588  && units_alive(side_num, units())) {
589  const int res = gui2::show_message("",
590  _("You have not started your turn yet. Do you really want to end your turn?"),
592  if(res == gui2::retval::CANCEL) {
593  return false;
594  }
595  }
596  // Ask for confirmation if units still have some movement left.
597  else if(preferences::yellow_confirm() && partmoved_units(side_num, units(), board(), pc_.get_whiteboard())) {
598  const int res = gui2::show_message("",
599  _("Some units have movement left. Do you really want to end your turn?"),
601  if(res == gui2::retval::CANCEL) {
602  return false;
603  }
604  }
605  // Ask for confirmation if units still have all movement left.
606  else if(preferences::green_confirm() && unmoved_units(side_num, units(), board(), pc_.get_whiteboard())) {
607  const int res = gui2::show_message("",
608  _("Some units have not moved. Do you really want to end your turn?"),
610  if(res == gui2::retval::CANCEL) {
611  return false;
612  }
613  }
614 
615  // Auto-execute remaining whiteboard planned actions
616  // Only finish turn if they all execute successfully, i.e. no ambush, etc.
617  if(pc_.get_whiteboard() && !pc_.get_whiteboard()->allow_end_turn()) {
618  return false;
619  }
620 
621  return true;
622 }
623 
624 void menu_handler::goto_leader(int side_num)
625 {
627  const display_context& dc = gui_->get_disp_context();
628  if(i != units().end() && i->is_visible_to_team(dc.get_team(gui_->viewing_side()), false)) {
629  gui_->scroll_to_tile(i->get_location(), game_display::WARP);
630  }
631 }
632 
634 {
636  if(un != units().end()) {
638  }
639 }
640 
642 {
643  const map_location& loc = mousehandler.get_last_hex();
644  if(map().on_board(loc) == false || gui_->shrouded(loc)) {
645  return;
646  }
647 
648  const terrain_type& type = map().get_terrain_info(loc);
649  // const terrain_type& info = board().map().get_terrain_info(terrain);
651 }
652 
654 {
655  const unit_map::iterator un = current_unit();
656  if(un == units().end() || gui_->viewing_side() != un->side()) {
657  return;
658  }
659 
660  if(un->unrenamable()) {
661  return;
662  }
663 
664  std::string name = un->name();
665  const std::string title(_("Rename Unit"));
666  const std::string label(_("Name:"));
667 
668  if(gui2::dialogs::edit_text::execute(title, label, name)) {
669  resources::recorder->add_rename(name, un->get_location());
670  un->rename(name);
672  }
673 }
674 
676 {
677  const mouse_handler& mousehandler = pc_.get_mouse_handler_base();
678  const bool see_all = gui_->show_everything() || (pc_.is_replay() && pc_.get_replay_controller()->see_all());
679 
680  unit_map::iterator res = board().find_visible_unit(mousehandler.get_last_hex(), teams()[gui_->viewing_team()], see_all);
681  if(res != units().end()) {
682  return res;
683  }
684 
685  return board().find_visible_unit(mousehandler.get_selected_hex(), teams()[gui_->viewing_team()], see_all);
686 }
687 
688 // Helpers for create_unit()
689 namespace
690 {
691 /// Allows a function to return both a type and a gender.
692 typedef std::tuple<const unit_type*, unit_race::GENDER, std::string> type_gender_variation;
693 
694 /**
695  * Allows the user to select a type of unit, using GUI2.
696  * (Intended for use when a unit is created in debug mode via hotkey or
697  * context menu.)
698  * @returns the selected type and gender. If this is canceled, the
699  * returned type is nullptr.
700  */
701 type_gender_variation choose_unit()
702 {
703  //
704  // The unit creation dialog makes sure unit types
705  // are properly cached.
706  //
707  gui2::dialogs::unit_create create_dlg;
708  create_dlg.show();
709 
710  if(create_dlg.no_choice()) {
711  return type_gender_variation(nullptr, unit_race::NUM_GENDERS, "");
712  }
713 
714  const std::string& ut_id = create_dlg.choice();
715  const unit_type* utp = unit_types.find(ut_id);
716  if(!utp) {
717  ERR_NG << "Create unit dialog returned nonexistent or unusable unit_type id '" << ut_id << "'." << std::endl;
718  return type_gender_variation(static_cast<const unit_type*>(nullptr), unit_race::NUM_GENDERS, "");
719  }
720  const unit_type& ut = *utp;
721 
722  unit_race::GENDER gender = create_dlg.gender();
723  // Do not try to set bad genders, may mess up l10n
724  /// @todo Is this actually necessary?
725  /// (Maybe create_dlg can enforce proper gender selection?)
726  if(ut.genders().end() == std::find(ut.genders().begin(), ut.genders().end(), gender)) {
727  gender = ut.genders().front();
728  }
729 
730  return type_gender_variation(utp, gender, create_dlg.variation());
731 }
732 
733 /**
734  * Creates a unit and places it on the board.
735  * (Intended for use with any units created via debug mode.)
736  */
737 void create_and_place(game_display&,
738  const gamemap&,
739  unit_map&,
740  const map_location& loc,
741  const unit_type& u_type,
743  const std::string& variation = "")
744 {
745  synced_context::run_and_throw("debug_create_unit",
746  config {
747  "x", loc.wml_x(),
748  "y", loc.wml_y(),
749  "type", u_type.id(),
750  "gender", gender_string(gender),
751  "variation", variation,
752  }
753  );
754 }
755 
756 } // Anonymous namespace
757 
758 /**
759  * Creates a unit (in debug mode via hotkey or context menu).
760  */
762 {
763  // Save the current mouse location before popping up the choice menu (which
764  // gives time for the mouse to move, changing the location).
765  const map_location destination = mousehandler.get_last_hex();
766  assert(gui_ != nullptr);
767 
768  // Let the user select the kind of unit to create.
769  type_gender_variation selection = choose_unit();
770  if(std::get<0>(selection) != nullptr) {
771  // Make it so.
772  create_and_place(*gui_, map(), units(), destination, *std::get<0>(selection), std::get<1>(selection), std::get<2>(selection));
773  }
774 }
775 
777 {
778  const map_location& loc = mousehandler.get_last_hex();
779  const unit_map::iterator i = units().find(loc);
780  if(i == units().end()) {
781  if(!map().is_village(loc)) {
782  return;
783  }
784 
785  // village_owner returns -1 for free village, so team 0 will get it
786  int team = board().village_owner(loc) + 1;
787  // team is 0-based so team=team::nteams() is not a team
788  // but this will make get_village free it
789  if(team > static_cast<int>(teams().size())) {
790  team = 0;
791  }
792  actions::get_village(loc, team + 1);
793  } else {
794  int side = i->side();
795  ++side;
796  if(side > static_cast<int>(teams().size())) {
797  side = 1;
798  }
799  i->set_side(side);
800 
801  if(map().is_village(loc)) {
802  actions::get_village(loc, side);
803  }
804  }
805 }
806 
808 {
809  const map_location loc = mousehandler.get_last_hex();
810  synced_context::run_and_throw("debug_kill", config {"x", loc.wml_x(), "y", loc.wml_y()});
811 }
812 
813 void menu_handler::label_terrain(mouse_handler& mousehandler, bool team_only)
814 {
815  const map_location& loc = mousehandler.get_last_hex();
816  if(map().on_board(loc) == false) {
817  return;
818  }
819 
820  const terrain_label* old_label = gui_->labels().get_label(loc);
821  std::string label = old_label ? old_label->text() : "";
822 
823  if(gui2::dialogs::edit_label::execute(label, team_only)) {
824  std::string team_name;
825  color_t color = font::LABEL_COLOR;
826 
827  if(team_only) {
828  team_name = gui_->labels().team_name();
829  } else {
831  }
832  const terrain_label* res = gui_->labels().set_label(loc, label, gui_->viewing_team(), team_name, color);
833  if(res) {
835  }
836  }
837 }
838 
840 {
841  if(gui_->team_valid() && !board().is_observer()) {
842  const int res = gui2::show_message(
843  _("Clear Labels"),
844  _("Are you sure you want to clear map labels?"),
846  );
847 
848  if(res == gui2::retval::OK) {
849  gui_->labels().clear(gui_->current_team_name(), false);
851  }
852  }
853 }
854 
856 {
857  if(gui2::dialogs::label_settings::execute(board())) {
859  }
860 }
861 
862 void menu_handler::continue_move(mouse_handler& mousehandler, int side_num)
863 {
865  if(i == units().end() || !i->move_interrupted()) {
866  i = units().find(mousehandler.get_selected_hex());
867  if(i == units().end() || !i->move_interrupted()) {
868  return;
869  }
870  }
871  move_unit_to_loc(i, i->get_interrupted_move(), true, side_num, mousehandler);
872 }
873 
875  const map_location& target,
876  bool continue_move,
877  int side_num,
878  mouse_handler& mousehandler)
879 {
880  assert(ui != units().end());
881 
882  pathfind::marked_route route = mousehandler.get_route(&*ui, target, board().get_team(side_num));
883 
884  if(route.steps.empty()) {
885  return;
886  }
887 
888  assert(route.steps.front() == ui->get_location());
889 
890  gui_->set_route(&route);
892 
893  {
894  LOG_NG << "move_unit_to_loc " << route.steps.front() << " to " << route.steps.back() << "\n";
896  }
897 
898  gui_->set_route(nullptr);
900 }
901 
902 void menu_handler::execute_gotos(mouse_handler& mousehandler, int side)
903 {
904  // we will loop on all gotos and try to fully move a maximum of them,
905  // but we want to avoid multiple blocking of the same unit,
906  // so, if possible, it's better to first wait that the blocker move
907 
908  bool wait_blocker_move = true;
909  std::set<map_location> fully_moved;
910 
911  bool change = false;
912  bool blocked_unit = false;
913  do {
914  change = false;
915  blocked_unit = false;
916  for(auto& unit : units()) {
917  if(unit.side() != side || unit.movement_left() == 0) {
918  continue;
919  }
920 
921  const map_location& current_loc = unit.get_location();
922  const map_location& goto_loc = unit.get_goto();
923 
924  if(goto_loc == current_loc) {
926  continue;
927  }
928 
929  if(!map().on_board(goto_loc)) {
930  continue;
931  }
932 
933  // avoid pathfinding calls for finished units
934  if(fully_moved.count(current_loc)) {
935  continue;
936  }
937 
938  pathfind::marked_route route = mousehandler.get_route(&unit, goto_loc, board().get_team(side));
939 
940  if(route.steps.size() <= 1) { // invalid path
941  fully_moved.insert(current_loc);
942  continue;
943  }
944 
945  // look where we will stop this turn (turn_1 waypoint or goto)
946  map_location next_stop = goto_loc;
947  pathfind::marked_route::mark_map::const_iterator w = route.marks.begin();
948  for(; w != route.marks.end(); ++w) {
949  if(w->second.turns == 1) {
950  next_stop = w->first;
951  break;
952  }
953  }
954 
955  if(next_stop == current_loc) {
956  fully_moved.insert(current_loc);
957  continue;
958  }
959 
960  // we delay each blocked move because some other change
961  // may open a another not blocked path
962  if(units().count(next_stop)) {
963  blocked_unit = true;
964  if(wait_blocker_move)
965  continue;
966  }
967 
968  gui_->set_route(&route);
969 
970  {
971  LOG_NG << "execute goto from " << route.steps.front() << " to " << route.steps.back() << "\n";
972  int moves = actions::move_unit_and_record(route.steps, &pc_.get_undo_stack());
973  change = moves > 0;
974  }
975 
976  if(change) {
977  // something changed, resume waiting blocker (maybe one can move now)
978  wait_blocker_move = true;
979  }
980  }
981 
982  if(!change && wait_blocker_move) {
983  // no change when waiting, stop waiting and retry
984  wait_blocker_move = false;
985  change = true;
986  }
987  } while(change && blocked_unit);
988 
989  // erase the footsteps after movement
990  gui_->set_route(nullptr);
992 }
993 
995 {
997  gui_->invalidate_all(); // TODO can fewer tiles be invalidated?
998 }
999 
1001 {
1003  gui_->invalidate_all();
1004 }
1005 
1006 void menu_handler::unit_hold_position(mouse_handler& mousehandler, int side_num)
1007 {
1008  const unit_map::iterator un = units().find(mousehandler.get_selected_hex());
1009  if(un != units().end() && un->side() == side_num && un->movement_left() >= 0) {
1010  un->toggle_hold_position();
1011  gui_->invalidate(mousehandler.get_selected_hex());
1012 
1013  mousehandler.set_current_paths(pathfind::paths());
1014 
1015  if(un->hold_position()) {
1016  mousehandler.cycle_units(false);
1017  }
1018  }
1019 }
1020 
1021 void menu_handler::end_unit_turn(mouse_handler& mousehandler, int side_num)
1022 {
1023  const unit_map::iterator un = units().find(mousehandler.get_selected_hex());
1024  if(un != units().end() && un->side() == side_num && un->movement_left() >= 0) {
1025  un->toggle_user_end_turn();
1026  gui_->invalidate(mousehandler.get_selected_hex());
1027 
1028  mousehandler.set_current_paths(pathfind::paths());
1029 
1030  if(un->user_end_turn()) {
1031  mousehandler.cycle_units(false);
1032  }
1033 
1034  // If cycle_units hasn't found a new unit to cycle to then the original unit is still selected, but
1035  // in a state where left-clicking on it does nothing. Make it respond to mouse clicks again.
1036  if(un == units().find(mousehandler.get_selected_hex())) {
1037  mousehandler.deselect_hex();
1038  }
1039  }
1040 }
1041 
1043 {
1044  std::ostringstream msg;
1045  msg << _("Search");
1046  if(last_search_hit_.valid()) {
1047  msg << " [" << last_search_ << "]";
1048  }
1049  msg << ':';
1050  textbox_info_.show(gui::TEXTBOX_SEARCH, msg.str(), "", false, *gui_);
1051 }
1052 
1054 {
1055  // None of the two parameters really needs to be passed since the information belong to members of the class.
1056  // But since it makes the called method more generic, it is done anyway.
1058  textbox_info_.box()->text(), textbox_info_.check() != nullptr ? textbox_info_.check()->checked() : false);
1059 }
1060 
1061 void menu_handler::add_chat_message(const std::time_t& time,
1062  const std::string& speaker,
1063  int side,
1064  const std::string& message,
1066 {
1067  gui_->get_chat_manager().add_chat_message(time, speaker, side, message, type, false);
1068 
1070  config {
1071  "sender", preferences::login(),
1072  "message", message,
1073  "whisper", type == events::chat_handler::MESSAGE_PRIVATE,
1074  }
1075  );
1076 }
1077 
1078 // command handler for user :commands. Also understands all chat commands
1079 // via inheritance. This complicates some things a bit.
1080 class console_handler : public map_command_handler<console_handler>, private chat_command_handler
1081 {
1082 public:
1083  // convenience typedef
1086  : chmap()
1087  , chat_command_handler(menu_handler, true)
1088  , menu_handler_(menu_handler)
1089  , team_num_(menu_handler.pc_.current_side())
1090  {
1091  }
1092 
1093  using chmap::dispatch; // disambiguate
1094  using chmap::get_commands_list;
1095  using chmap::command_failed;
1096 
1097 protected:
1098  // chat_command_handler's init_map() and handlers will end up calling these.
1099  // this makes sure the commands end up in our map
1100  virtual void register_command(const std::string& cmd,
1102  const std::string& help = "",
1103  const std::string& usage = "",
1104  const std::string& flags = "")
1105  {
1106  chmap::register_command(cmd, h, help, usage, flags + "N"); // add chat commands as network_only
1107  }
1108 
1109  virtual void register_alias(const std::string& to_cmd, const std::string& cmd)
1110  {
1111  chmap::register_alias(to_cmd, cmd);
1112  }
1113 
1114  virtual std::string get_arg(unsigned i) const
1115  {
1116  return chmap::get_arg(i);
1117  }
1118 
1119  virtual std::string get_cmd() const
1120  {
1121  return chmap::get_cmd();
1122  }
1123 
1124  virtual std::string get_data(unsigned n = 1) const
1125  {
1126  return chmap::get_data(n);
1127  }
1128 
1129  // these are needed to avoid ambiguities introduced by inheriting from console_command_handler
1130  using chmap::register_command;
1131  using chmap::register_alias;
1132  using chmap::help;
1133  using chmap::is_enabled;
1134  using chmap::command_failed_need_arg;
1135 
1136  void do_refresh();
1137  void do_droid();
1138  void do_terrain();
1139  void do_idle();
1140  void do_theme();
1141  void do_control();
1142  void do_controller();
1143  void do_clear();
1144  void do_foreground();
1145  void do_layers();
1146  void do_fps();
1147  void do_benchmark();
1148  void do_save();
1149  void do_save_quit();
1150  void do_quit();
1151  void do_ignore_replay_errors();
1152  void do_nosaves();
1153  void do_next_level();
1154  void do_choose_level();
1155  void do_turn();
1156  void do_turn_limit();
1157  void do_debug();
1158  void do_nodebug();
1159  void do_lua();
1160  void do_unsafe_lua();
1161  void do_custom();
1162  void do_set_alias();
1163  void do_set_var();
1164  void do_show_var();
1165  void do_inspect();
1166  void do_control_dialog();
1167  void do_unit();
1168  // void do_buff();
1169  // void do_unbuff();
1170  void do_discover();
1171  void do_undiscover();
1172  void do_create();
1173  void do_fog();
1174  void do_shroud();
1175  void do_gold();
1176  void do_event();
1177  void do_toggle_draw_coordinates();
1178  void do_toggle_draw_terrain_codes();
1179  void do_toggle_draw_num_of_bitmaps();
1180  void do_toggle_whiteboard();
1181  void do_whiteboard_options();
1182 
1183  std::string get_flags_description() const
1184  {
1185  return _("(D) — debug only, (N) — network only, (A) — admin only");
1186  }
1187 
1188  using chat_command_handler::get_command_flags_description; // silence a warning
1189  std::string get_command_flags_description(const chmap::command& c) const
1190  {
1191  std::string space(" ");
1192  return (c.has_flag('D') ? space + _("(debug command)") : "")
1193  + (c.has_flag('N') ? space + _("(network only)") : "")
1194  + (c.has_flag('A') ? space + _("(admin only)") : "")
1195  + (c.has_flag('S') ? space + _("(not during other events)") : "");
1196  }
1197 
1198  using map::is_enabled;
1199  bool is_enabled(const chmap::command& c) const
1200  {
1201  return !((c.has_flag('D') && !game_config::debug)
1202  || (c.has_flag('N') && !menu_handler_.pc_.is_networked_mp())
1203  || (c.has_flag('A') && !preferences::is_authenticated())
1204  || (c.has_flag('S') && (synced_context::get_synced_state() != synced_context::UNSYNCED || !menu_handler_.pc_.current_team().is_local())));
1205  }
1206 
1207  void print(const std::string& title, const std::string& message)
1208  {
1209  menu_handler_.add_chat_message(std::time(nullptr), title, 0, message);
1210  }
1211 
1212  void init_map()
1213  {
1214  chat_command_handler::init_map(); // grab chat_ /command handlers
1215 
1216  chmap::get_command("log")->flags = ""; // clear network-only flag from log
1217  chmap::get_command("version")->flags = ""; // clear network-only flag
1218  chmap::get_command("ignore")->flags = ""; // clear network-only flag
1219  chmap::get_command("friend")->flags = ""; // clear network-only flag
1220  chmap::get_command("remove")->flags = ""; // clear network-only flag
1221 
1222  chmap::set_cmd_prefix(":");
1223 
1224  register_command("refresh", &console_handler::do_refresh, _("Refresh gui."));
1225  register_command("droid", &console_handler::do_droid, _("Switch a side to/from AI control."),
1226  // TRANSLATORS: These are the arguments accepted by the "droid" command,
1227  // which must be a side-number and then optionally one of "on", "off" or "full".
1228  // As with the command's name, "on", "off" and "full" are hardcoded, and shouldn't change in the translation.
1229  _("[<side> [on/off/full]]\n“on” = enable but retain vision, “full” = as if it’s controlled by another player"));
1230  register_command("terrain", &console_handler::do_terrain, _("Change terrain type of current hex"),
1231  // TRANSLATORS: [both|base|overlay] are hardcoded literal arguments and shouldn't be translated.
1232  _("<terrain type> [both|base|overlay]"), "DS");
1233  register_command("idle", &console_handler::do_idle, _("Switch a side to/from idle state."),
1234  // TRANSLATORS: These are the arguments accepted by the "idle" command,
1235  // which must be a side-number and then optionally "on" or "off".
1236  // As with the command's name, "on" and "off" are hardcoded, and shouldn't change in the translation.
1237  _("command_idle^[<side> [on/off]]"));
1238  register_command("theme", &console_handler::do_theme, _("Change the in-game theme."));
1239  register_command("control", &console_handler::do_control,
1240  _("Assign control of a side to a different player or observer."), _("<side> <nickname>"), "N");
1241  register_command("controller", &console_handler::do_controller, _("Query the controller status of a side."),
1242  _("<side>"));
1243  register_command("clear", &console_handler::do_clear, _("Clear chat history."));
1244  register_command("foreground", &console_handler::do_foreground, _("Debug foreground terrain."), "", "D");
1245  register_command(
1246  "layers", &console_handler::do_layers, _("Debug layers from terrain under the mouse."), "", "D");
1247  register_command("fps", &console_handler::do_fps, _("Show fps (Frames Per Second)."));
1248  register_command("benchmark", &console_handler::do_benchmark);
1249  register_command("save", &console_handler::do_save, _("Save game."));
1250  register_alias("save", "w");
1251  register_command("quit", &console_handler::do_quit, _("Quit game."));
1252  // Note the next value is used hardcoded in the init tests.
1253  register_alias("quit", "q!");
1254  register_command("save_quit", &console_handler::do_save_quit, _("Save and quit."));
1255  register_alias("save_quit", "wq");
1256  register_command("ignore_replay_errors", &console_handler::do_ignore_replay_errors, _("Ignore replay errors."));
1257  register_command("nosaves", &console_handler::do_nosaves, _("Disable autosaves."));
1258  register_command("next_level", &console_handler::do_next_level,
1259  _("Advance to the next scenario, or scenario identified by 'id'"), _("<id>"), "DS");
1260  register_alias("next_level", "n");
1261  register_command("choose_level", &console_handler::do_choose_level, _("Choose next scenario"), "", "DS");
1262  register_alias("choose_level", "cl");
1263  register_command("turn", &console_handler::do_turn,
1264  _("Change turn number (and time of day), or increase by one if no number is specified."), _("[turn]"),
1265  "DS");
1266  register_command("turn_limit", &console_handler::do_turn_limit,
1267  _("Change turn limit, or turn the turn limit off if no number is specified or it’s −1."), _("[limit]"),
1268  "DS");
1269  register_command("debug", &console_handler::do_debug, _("Turn debug mode on."));
1270  register_command("nodebug", &console_handler::do_nodebug, _("Turn debug mode off."), "", "D");
1271  register_command(
1272  "lua", &console_handler::do_lua, _("Execute a Lua statement."), _("<command>[;<command>...]"), "DS");
1273  register_command(
1274  "unsafe_lua", &console_handler::do_unsafe_lua, _("Grant higher privileges to Lua scripts."), "", "D");
1275  register_command("custom", &console_handler::do_custom, _("Set the command used by the custom command hotkey"),
1276  _("<command>[;<command>...]"));
1277  register_command("give_control", &console_handler::do_control_dialog,
1278  _("Invoke a dialog allowing changing control of MP sides."), "", "N");
1279  register_command("inspect", &console_handler::do_inspect, _("Launch the gamestate inspector"), "", "D");
1280  register_command(
1281  "alias", &console_handler::do_set_alias, _("Set or show alias to a command"), _("<name>[=<command>]"));
1282  register_command(
1283  "set_var", &console_handler::do_set_var, _("Set a scenario variable."), _("<var>=<value>"), "DS");
1284  register_command("show_var", &console_handler::do_show_var, _("Show a scenario variable."), _("<var>"), "D");
1285  register_command("unit", &console_handler::do_unit,
1286  // TRANSLATORS: Do not translate the word "advances"; it is a hardcoded literal argument.
1287  _("Modify a unit variable. (Only top level keys are supported, and advances=<number>.)"),
1288  _("<var>=<value>"), "DS");
1289 
1290  // register_command("buff", &console_handler::do_buff,
1291  // _("Add a trait to a unit."), "", "D");
1292  // register_command("unbuff", &console_handler::do_unbuff,
1293  // _("Remove a trait from a unit. (Does not work yet.)"), "", "D");
1294  register_command("discover", &console_handler::do_discover, _("Discover all units in help."), "");
1295  register_command("undiscover", &console_handler::do_undiscover, _("'Undiscover' all units in help."), "");
1296  register_command("create", &console_handler::do_create, _("Create a unit."), _("<unit type id>"), "DS");
1297  register_command("fog", &console_handler::do_fog, _("Toggle fog for the current player."), "", "DS");
1298  register_command("shroud", &console_handler::do_shroud, _("Toggle shroud for the current player."), "", "DS");
1299  register_command("gold", &console_handler::do_gold, _("Give gold to the current player."), _("<amount>"), "DS");
1300  register_command("throw", &console_handler::do_event, _("Fire a game event."), _("<event name>"), "DS");
1301  register_alias("throw", "fire");
1302  register_command("show_coordinates", &console_handler::do_toggle_draw_coordinates,
1303  _("Toggle overlaying of x,y coordinates on hexes."));
1304  register_alias("show_coordinates", "sc");
1305  register_command("show_terrain_codes", &console_handler::do_toggle_draw_terrain_codes,
1306  _("Toggle overlaying of terrain codes on hexes."));
1307  register_alias("show_terrain_codes", "tc");
1308  register_command("show_num_of_bitmaps", &console_handler::do_toggle_draw_num_of_bitmaps,
1309  _("Toggle overlaying of number of bitmaps on hexes."));
1310  register_alias("show_num_of_bitmaps", "bn");
1311  register_command("whiteboard", &console_handler::do_toggle_whiteboard, _("Toggle planning mode."));
1312  register_alias("whiteboard", "wb");
1313  register_command(
1314  "whiteboard_options", &console_handler::do_whiteboard_options, _("Access whiteboard options dialog."));
1315  register_alias("whiteboard_options", "wbo");
1316 
1317  if(const config& alias_list = preferences::get_alias()) {
1318  for(const config::attribute& a : alias_list.attribute_range()) {
1319  register_alias(a.second, a.first);
1320  }
1321  }
1322  }
1323 
1324 private:
1326  const unsigned int team_num_;
1327 };
1328 
1329 void menu_handler::send_chat_message(const std::string& message, bool allies_only)
1330 {
1331  config cfg;
1332  cfg["id"] = preferences::login();
1333  cfg["message"] = message;
1334  const std::time_t time = ::std::time(nullptr);
1335  std::stringstream ss;
1336  ss << time;
1337  cfg["time"] = ss.str();
1338 
1339  const int side = board().is_observer() ? 0 : gui_->viewing_side();
1340  if(!board().is_observer()) {
1341  cfg["side"] = side;
1342  }
1343 
1344  bool private_message = has_friends() && allies_only;
1345 
1346  if(private_message) {
1347  if(board().is_observer()) {
1348  cfg["to_sides"] = game_config::observer_team_name;
1349  } else {
1350  cfg["to_sides"] = teams()[gui_->viewing_team()].allied_human_teams();
1351  }
1352  }
1353 
1354  resources::recorder->speak(cfg);
1355 
1356  add_chat_message(time, cfg["id"], side, message,
1358 }
1359 
1360 void menu_handler::do_search(const std::string& new_search)
1361 {
1362  if(new_search.empty() == false && new_search != last_search_)
1363  last_search_ = new_search;
1364 
1365  if(last_search_.empty())
1366  return;
1367 
1368  bool found = false;
1370  // If this is a location search, just center on that location.
1371  std::vector<std::string> args = utils::split(last_search_, ',');
1372  if(args.size() == 2) {
1373  int x, y;
1374  x = lexical_cast_default<int>(args[0], 0) - 1;
1375  y = lexical_cast_default<int>(args[1], 0) - 1;
1376  if(x >= 0 && x < map().w() && y >= 0 && y < map().h()) {
1377  loc = map_location(x, y);
1378  found = true;
1379  }
1380  }
1381  // Start scanning the game map
1382  if(loc.valid() == false) {
1383  loc = map_location(map().w() - 1, map().h() - 1);
1384  }
1385 
1386  map_location start = loc;
1387  while(!found) {
1388  // Move to the next location
1389  loc.x = (loc.x + 1) % map().w();
1390  if(loc.x == 0)
1391  loc.y = (loc.y + 1) % map().h();
1392 
1393  // Search label
1394  if(!gui_->shrouded(loc)) {
1395  const terrain_label* label = gui_->labels().get_label(loc);
1396  if(label) {
1397  std::string label_text = label->text().str();
1398  if(std::search(label_text.begin(), label_text.end(), last_search_.begin(), last_search_.end(),
1400  != label_text.end()) {
1401  found = true;
1402  }
1403  }
1404  }
1405  // Search unit name
1406  if(!gui_->fogged(loc)) {
1407  unit_map::const_iterator ui = units().find(loc);
1408  if(ui != units().end()) {
1409  const std::string name = ui->name();
1410  if(std::search(
1411  name.begin(), name.end(), last_search_.begin(), last_search_.end(), chars_equal_insensitive)
1412  != name.end()) {
1413  if(!teams()[gui_->viewing_team()].is_enemy(ui->side())
1414  || !ui->invisible(ui->get_location())) {
1415  found = true;
1416  }
1417  }
1418  }
1419  }
1420 
1421  if(loc == start)
1422  break;
1423  }
1424 
1425  if(found) {
1426  last_search_hit_ = loc;
1428  gui_->highlight_hex(loc);
1429  } else {
1431  // Not found, inform the player
1432  utils::string_map symbols;
1433  symbols["search"] = last_search_;
1434  const std::string msg = VGETTEXT("Could not find label or unit "
1435  "containing the string ‘$search’.",
1436  symbols);
1438  }
1439 }
1440 
1441 void menu_handler::do_command(const std::string& str)
1442 {
1443  console_handler ch(*this);
1444  ch.dispatch(str);
1445 }
1446 
1447 std::vector<std::string> menu_handler::get_commands_list()
1448 {
1449  console_handler ch(*this);
1450  // HACK: we need to call dispatch() at least once to get the
1451  // command list populated *at all*. Terrible design.
1452  // An empty command is silently ignored and has negligible
1453  // overhead, so we use that for this purpose here.
1454  ch.dispatch("");
1455  return ch.get_commands_list();
1456 }
1457 
1459 {
1462 
1463  menu_handler_.gui_->create_buttons();
1464  menu_handler_.gui_->redraw_everything();
1465 }
1466 
1468 {
1469  // :droid [<side> [on/off/full]]
1470  const std::string side_s = get_arg(1);
1471  std::string action = get_arg(2);
1472  std::transform(action.begin(), action.end(), action.begin(), tolower);
1473  // default to the current side if empty
1474  const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1475 
1476  utils::string_map symbols;
1477  symbols["side"] = std::to_string(side);
1478 
1479  if(side < 1 || side > menu_handler_.teams().size()) {
1480  command_failed(VGETTEXT("Can't droid invalid side: '$side'.", symbols));
1481  return;
1482  } else if(menu_handler_.board().get_team(side).is_network()) {
1483  command_failed(VGETTEXT("Can't droid networked side: '$side'.", symbols));
1484  return;
1485  } else if(menu_handler_.board().get_team(side).is_local()) {
1486  bool changed = false;
1487 
1488  if(action == "on") {
1489  if(!menu_handler_.board().get_team(side).is_human() || !menu_handler_.board().get_team(side).is_droid()) {
1490  menu_handler_.board().get_team(side).make_human();
1491  menu_handler_.board().get_team(side).make_droid();
1492  changed = true;
1493  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
1494  } else {
1495  print(get_cmd(), VGETTEXT("Side '$side' is already droided.", symbols));
1496  }
1497  } else if(action == "off") {
1498  if(!menu_handler_.board().get_team(side).is_human() || !menu_handler_.board().get_team(side).is_proxy_human()) {
1499  menu_handler_.board().get_team(side).make_human();
1500  menu_handler_.board().get_team(side).make_proxy_human();
1501  changed = true;
1502  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
1503  } else {
1504  print(get_cmd(), VGETTEXT("Side '$side' is already not droided.", symbols));
1505  }
1506  } else if(action == "full") {
1507  if(!menu_handler_.board().get_team(side).is_ai() || !menu_handler_.board().get_team(side).is_droid()) {
1508  menu_handler_.board().get_team(side).make_ai();
1509  menu_handler_.board().get_team(side).make_droid();
1510  changed = true;
1511  print(get_cmd(), VGETTEXT("Side '$side' controller is now fully controlled by: AI.", symbols));
1512  } else {
1513  print(get_cmd(), VGETTEXT("Side '$side' is already fully AI controlled.", symbols));
1514  }
1515  } else if(action == "") {
1516  if(menu_handler_.board().get_team(side).is_ai() || menu_handler_.board().get_team(side).is_droid()) {
1517  menu_handler_.board().get_team(side).make_human();
1518  menu_handler_.board().get_team(side).make_proxy_human();
1519  changed = true;
1520  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
1521  } else {
1522  menu_handler_.board().get_team(side).make_human();
1523  menu_handler_.board().get_team(side).make_droid();
1524  changed = true;
1525  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
1526  }
1527  } else {
1528  print(get_cmd(), VGETTEXT("Invalid action provided for side '$side'. Valid actions are: on, off, full.", symbols));
1529  }
1530 
1531  if(team_num_ == side && changed) {
1532  if(playsingle_controller* psc = dynamic_cast<playsingle_controller*>(&menu_handler_.pc_)) {
1533  psc->set_player_type_changed();
1534  }
1535  }
1536  }
1537  menu_handler_.textbox_info_.close(*menu_handler_.gui_);
1538 }
1539 
1541 {
1542  // :terrain [<terrain type> [both|base|overlay]]
1543  const std::string terrain_type = get_arg(1);
1544  const std::string mode_str = get_arg(2);
1545 
1546  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1547  const map_location& loc = mousehandler.get_last_hex();
1548 
1549  synced_context::run_and_throw("debug_terrain",
1550  config {
1551  "x", loc.wml_x(),
1552  "y", loc.wml_y(),
1553  "terrain_type", terrain_type,
1554  "mode_str", mode_str,
1555  }
1556  );
1557 }
1558 
1560 {
1561  // :idle [<side> [on/off]]
1562  const std::string side_s = get_arg(1);
1563  const std::string action = get_arg(2);
1564  // default to the current side if empty
1565  const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1566 
1567  if(side < 1 || side > menu_handler_.teams().size()) {
1568  utils::string_map symbols;
1569  symbols["side"] = side_s;
1570  command_failed(VGETTEXT("Can't idle invalid side: '$side'.", symbols));
1571  return;
1572  } else if(menu_handler_.board().get_team(side).is_network()) {
1573  utils::string_map symbols;
1574  symbols["side"] = std::to_string(side);
1575  command_failed(VGETTEXT("Can't idle networked side: '$side'.", symbols));
1576  return;
1577  } else if(menu_handler_.board().get_team(side).is_local_ai()) {
1578  utils::string_map symbols;
1579  symbols["side"] = std::to_string(side);
1580  command_failed(VGETTEXT("Can't idle local ai side: '$side'.", symbols));
1581  return;
1582  } else if(menu_handler_.board().get_team(side).is_local_human()) {
1583  if(menu_handler_.board().get_team(side).is_idle() ? action == " on" : action == " off") {
1584  return;
1585  }
1586  // toggle the proxy controller between idle / non idle
1587  menu_handler_.board().get_team(side).toggle_idle();
1588  if(team_num_ == side) {
1589  if(playsingle_controller* psc = dynamic_cast<playsingle_controller*>(&menu_handler_.pc_)) {
1590  psc->set_player_type_changed();
1591  }
1592  }
1593  }
1594  menu_handler_.textbox_info_.close(*menu_handler_.gui_);
1595 }
1596 
1598 {
1600 }
1601 
1603 {
1604  save_id_matches(const std::string& save_id)
1605  : save_id_(save_id)
1606  {
1607  }
1608 
1609  bool operator()(const team& t) const
1610  {
1611  return t.save_id() == save_id_;
1612  }
1613 
1614  std::string save_id_;
1615 };
1616 
1618 {
1619  // :control <side> <nick>
1620  if(!menu_handler_.pc_.is_networked_mp()) {
1621  return;
1622  }
1623 
1624  const std::string side = get_arg(1);
1625  const std::string player = get_arg(2);
1626  if(player.empty()) {
1627  command_failed_need_arg(2);
1628  return;
1629  }
1630 
1631  unsigned int side_num;
1632  try {
1633  side_num = lexical_cast<unsigned int>(side);
1634  } catch(const bad_lexical_cast&) {
1635  const auto it_t = std::find_if(
1637  if(it_t == resources::gameboard->teams().end()) {
1638  utils::string_map symbols;
1639  symbols["side"] = side;
1640  command_failed(VGETTEXT("Can't change control of invalid side: '$side'.", symbols));
1641  return;
1642  } else {
1643  side_num = it_t->side();
1644  }
1645  }
1646 
1647  if(side_num < 1 || side_num > menu_handler_.teams().size()) {
1648  utils::string_map symbols;
1649  symbols["side"] = side;
1650  command_failed(VGETTEXT("Can't change control of out-of-bounds side: '$side'.", symbols));
1651  return;
1652  }
1653 
1654  menu_handler_.request_control_change(side_num, player);
1655  menu_handler_.textbox_info_.close(*(menu_handler_.gui_));
1656 }
1657 
1659 {
1660  const std::string side = get_arg(1);
1661  unsigned int side_num;
1662  try {
1663  side_num = lexical_cast<unsigned int>(side);
1664  } catch(const bad_lexical_cast&) {
1665  utils::string_map symbols;
1666  symbols["side"] = side;
1667  command_failed(VGETTEXT("Can't query control of invalid side: '$side'.", symbols));
1668  return;
1669  }
1670 
1671  if(side_num < 1 || side_num > menu_handler_.teams().size()) {
1672  utils::string_map symbols;
1673  symbols["side"] = side;
1674  command_failed(VGETTEXT("Can't query control of out-of-bounds side: '$side'.", symbols));
1675  return;
1676  }
1677 
1678  std::string report = menu_handler_.board().get_team(side_num).controller().to_string();
1679  if(!menu_handler_.board().get_team(side_num).is_proxy_human()) {
1680  report += " (" + menu_handler_.board().get_team(side_num).proxy_controller().to_string() + ")";
1681  }
1682 
1683  if(menu_handler_.board().get_team(side_num).is_network()) {
1684  report += " (networked)";
1685  }
1686 
1687  print(get_cmd(), report);
1688 }
1689 
1691 {
1692  menu_handler_.gui_->get_chat_manager().clear_chat_messages();
1693 }
1694 
1696 {
1697  menu_handler_.gui_->toggle_debug_foreground();
1698 }
1699 
1701 {
1702  display& disp = *(menu_handler_.gui_);
1703 
1704  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1705  const map_location& loc = mousehandler.get_last_hex();
1706 
1707  //
1708  // It's possible to invoke this dialog even if loc isn't a valid hex. I'm not sure
1709  // exactly how that happens, but it does seem to occur when moving the mouse outside
1710  // the window to the menu bar. Not sure if there's a bug when the set-last-hex code
1711  // in that case, but this check at least ensures the dialog is only ever shown for a
1712  // valid, on-map location. Otherwise, an assertion gets thrown.
1713  //
1714  // -- vultraz, 2017-09-21
1715  //
1716  if(disp.get_map().on_board_with_border(loc)) {
1717  gui2::dialogs::terrain_layers::display(disp, loc);
1718  }
1719 }
1720 
1722 {
1724 }
1725 
1727 {
1728  menu_handler_.gui_->toggle_benchmark();
1729 }
1730 
1732 {
1733  menu_handler_.pc_.do_consolesave(get_data());
1734 }
1735 
1737 {
1738  do_save();
1739  do_quit();
1740 }
1741 
1743 {
1745 }
1746 
1748 {
1749  game_config::ignore_replay_errors = (get_data() != "off") ? true : false;
1750 }
1751 
1753 {
1754  game_config::disable_autosave = (get_data() != "off") ? true : false;
1755 }
1756 
1758 {
1759  synced_context::run_and_throw("debug_next_level", config {"next_level", get_data()});
1760 }
1761 
1763 {
1764  std::string tag = menu_handler_.pc_.get_classification().get_tagname();
1765  std::vector<std::string> options;
1766  std::string next;
1767  if(tag != "multiplayer") {
1768  for(const config& sc : menu_handler_.game_config_.child_range(tag)) {
1769  const std::string& id = sc["id"];
1770  options.push_back(id);
1771  if(id == menu_handler_.gamedata().next_scenario()) {
1772  next = id;
1773  }
1774  }
1775  } else {
1776  // find scenarios of multiplayer campaigns
1777  // (assumes that scenarios are ordered properly in the game_config)
1778  std::string scenario_id = menu_handler_.pc_.get_mp_settings().mp_scenario;
1779  if(const config& this_scenario = menu_handler_.game_config_.find_child(tag, "id", scenario_id)) {
1780  std::string addon_id = this_scenario["addon_id"].str();
1781  for(const config& sc : menu_handler_.game_config_.child_range(tag)) {
1782  if(sc["addon_id"] == addon_id) {
1783  std::string id = sc["id"];
1784  options.push_back(id);
1785  if(id == menu_handler_.gamedata().next_scenario()) {
1786  next = id;
1787  }
1788  }
1789  }
1790  }
1791  }
1792  std::sort(options.begin(), options.end());
1793  int choice = std::distance(options.begin(), std::lower_bound(options.begin(), options.end(), next));
1794  {
1795  gui2::dialogs::simple_item_selector dlg(_("Choose Scenario (Debug!)"), "", options);
1796  dlg.set_selected_index(choice);
1797  dlg.show();
1798  choice = dlg.selected_index();
1799  }
1800 
1801  if(choice == -1) {
1802  return;
1803  }
1804 
1805  if(std::size_t(choice) < options.size()) {
1806  synced_context::run_and_throw("debug_next_level", config {"next_level", options[choice]});
1807  }
1808 }
1809 
1811 {
1812  tod_manager& tod_man = menu_handler_.gamestate().tod_manager_;
1813 
1814  int turn = tod_man.turn() + 1;
1815  const std::string& data = get_data();
1816  if(!data.empty()) {
1817  turn = lexical_cast_default<int>(data, 1);
1818  }
1819  synced_context::run_and_throw("debug_turn", config {"turn", turn});
1820 }
1821 
1823 {
1824  int limit = get_data().empty() ? -1 : lexical_cast_default<int>(get_data(), 1);
1825  synced_context::run_and_throw("debug_turn_limit", config {"turn_limit", limit});
1826 }
1827 
1829 {
1830  if(!menu_handler_.pc_.is_networked_mp() || game_config::mp_debug) {
1831  print(get_cmd(), _("Debug mode activated!"));
1832  game_config::set_debug(true);
1833  } else {
1834  command_failed(_("Debug mode not available in network games"));
1835  }
1836 }
1837 
1839 {
1840  if(game_config::debug) {
1841  print(get_cmd(), _("Debug mode deactivated!"));
1842  game_config::set_debug(false);
1843  }
1844 }
1845 
1847 {
1848  if(!menu_handler_.gamestate().lua_kernel_) {
1849  return;
1850  }
1851 
1852  synced_context::run_and_throw("debug_lua", config {"code", get_data()});
1853 }
1854 
1856 {
1857  if(!menu_handler_.gamestate().lua_kernel_) {
1858  return;
1859  }
1860 
1861  const int retval = gui2::show_message(_("WARNING! Unsafe Lua Mode"),
1862  _("Executing Lua code in in this manner opens your computer to potential security breaches from any "
1863  "malicious add-ons or other programs you may have installed.\n\n"
1864  "Do not continue unless you really know what you are doing."), gui2::dialogs::message::ok_cancel_buttons);
1865 
1866  if(retval == gui2::retval::OK) {
1867  print(get_cmd(), _("Unsafe mode enabled!"));
1868  menu_handler_.gamestate().lua_kernel_->load_package();
1869  }
1870 }
1871 
1873 {
1874  preferences::set_custom_command(get_data());
1875 }
1876 
1878 {
1879  const std::string data = get_data();
1880  const std::string::const_iterator j = std::find(data.begin(), data.end(), '=');
1881  const std::string alias(data.begin(), j);
1882  if(j != data.end()) {
1883  const std::string command(j + 1, data.end());
1884  if(!command.empty()) {
1885  register_alias(command, alias);
1886  } else {
1887  // "alias something=" deactivate this alias. We just set it
1888  // equal to itself here. Later preferences will filter empty alias.
1889  register_alias(alias, alias);
1890  }
1891  preferences::add_alias(alias, command);
1892  // directly save it for the moment, but will slow commands sequence
1894  } else {
1895  // "alias something" display its value
1896  // if no alias, will be "'something' = 'something'"
1897  const std::string command = chmap::get_actual_cmd(alias);
1898  print(get_cmd(), "'" + alias + "'" + " = " + "'" + command + "'");
1899  }
1900 }
1901 
1903 {
1904  const std::string data = get_data();
1905  if(data.empty()) {
1906  command_failed_need_arg(1);
1907  return;
1908  }
1909 
1910  const std::string::const_iterator j = std::find(data.begin(), data.end(), '=');
1911  if(j != data.end()) {
1912  const std::string name(data.begin(), j);
1913  const std::string value(j + 1, data.end());
1914  synced_context::run_and_throw("debug_set_var", config {"name", name, "value", value});
1915  } else {
1916  command_failed(_("Variable not found"));
1917  }
1918 }
1919 
1921 {
1922  gui2::show_transient_message("", menu_handler_.gamedata().get_variable_const(get_data()));
1923 }
1924 
1926 {
1928  gui2::dialogs::gamestate_inspector::display(
1930 }
1931 
1933 {
1934  gui2::dialogs::mp_change_control::display(menu_handler_);
1935 }
1936 
1938 {
1939  // prevent SIGSEGV due to attempt to set HP during a fight
1940  if(events::commands_disabled > 0) {
1941  return;
1942  }
1943 
1944  unit_map::iterator i = menu_handler_.current_unit();
1945  if(i == menu_handler_.units().end()) {
1946  return;
1947  }
1948 
1949  const map_location loc = i->get_location();
1950  const std::string data = get_data(1);
1951  std::vector<std::string> parameters = utils::split(data, '=', utils::STRIP_SPACES);
1952  if(parameters.size() < 2) {
1953  return;
1954  }
1955 
1956  if(parameters[0] == "alignment") {
1957  unit_type::ALIGNMENT alignment;
1958  if(!alignment.parse(parameters[1])) {
1959  utils::string_map symbols;
1960  symbols["alignment"] = get_arg(1);
1961  command_failed(VGETTEXT(
1962  "Invalid alignment: '$alignment', needs to be one of lawful, neutral, chaotic, or liminal.",
1963  symbols));
1964  return;
1965  }
1966  }
1967 
1968  synced_context::run_and_throw("debug_unit",
1969  config {
1970  "x", loc.wml_x(),
1971  "y", loc.wml_y(),
1972  "name", parameters[0],
1973  "value", parameters[1],
1974  }
1975  );
1976 }
1977 
1979 {
1980  for(const unit_type_data::unit_type_map::value_type& i : unit_types.types()) {
1981  preferences::encountered_units().insert(i.second.id());
1982  }
1983 }
1984 
1986 {
1987  const int res = gui2::show_message("Undiscover",
1988  _("Do you wish to clear all of your discovered units from help?"), gui2::dialogs::message::yes_no_buttons);
1989  if(res != gui2::retval::CANCEL) {
1991  }
1992 }
1993 
1994 /** Implements the (debug mode) console command that creates a unit. */
1996 {
1997  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1998  const map_location& loc = mousehandler.get_last_hex();
1999  if(menu_handler_.map().on_board(loc)) {
2000  const unit_type* ut = unit_types.find(get_data());
2001  if(!ut) {
2002  command_failed(_("Invalid unit type"));
2003  return;
2004  }
2005 
2006  // Create the unit.
2007  create_and_place(*menu_handler_.gui_, menu_handler_.map(), menu_handler_.units(), loc, *ut);
2008  } else {
2009  command_failed(_("Invalid location"));
2010  }
2011 }
2012 
2014 {
2015  synced_context::run_and_throw("debug_fog", config());
2016 }
2017 
2019 {
2020  synced_context::run_and_throw("debug_shroud", config());
2021 }
2022 
2024 {
2025  synced_context::run_and_throw("debug_gold", config {"gold", lexical_cast_default<int>(get_data(), 1000)});
2026 }
2027 
2029 {
2030  synced_context::run_and_throw("debug_event", config {"eventname", get_data()});
2031 }
2032 
2034 {
2035  menu_handler_.gui_->set_draw_coordinates(!menu_handler_.gui_->get_draw_coordinates());
2036  menu_handler_.gui_->invalidate_all();
2037 }
2039 {
2040  menu_handler_.gui_->set_draw_terrain_codes(!menu_handler_.gui_->get_draw_terrain_codes());
2041  menu_handler_.gui_->invalidate_all();
2042 }
2043 
2045 {
2046  menu_handler_.gui_->set_draw_num_of_bitmaps(!menu_handler_.gui_->get_draw_num_of_bitmaps());
2047  menu_handler_.gui_->invalidate_all();
2048 }
2049 
2051 {
2052  if(const std::shared_ptr<wb::manager>& whiteb = menu_handler_.pc_.get_whiteboard()) {
2053  whiteb->set_active(!whiteb->is_active());
2054  if(whiteb->is_active()) {
2055  print(get_cmd(), _("Planning mode activated!"));
2056  whiteb->print_help_once();
2057  } else {
2058  print(get_cmd(), _("Planning mode deactivated!"));
2059  }
2060  }
2061 }
2062 
2064 {
2065  if(menu_handler_.pc_.get_whiteboard()) {
2066  menu_handler_.pc_.get_whiteboard()->options_dlg();
2067  }
2068 }
2069 
2070 void menu_handler::do_ai_formula(const std::string& str, int side_num, mouse_handler& /*mousehandler*/)
2071 {
2072  try {
2073  add_chat_message(std::time(nullptr), "wfl", 0, ai::manager::get_singleton().evaluate_command(side_num, str));
2074  } catch(const wfl::formula_error&) {
2075  } catch(...) {
2076  add_chat_message(std::time(nullptr), "wfl", 0, "UNKNOWN ERROR IN FORMULA");
2077  }
2078 }
2079 
2081 {
2082  textbox_info_.show(gui::TEXTBOX_COMMAND, translation::sgettext("prompt^Command:"), "", false, *gui_);
2083 }
2084 
2085 void menu_handler::request_control_change(int side_num, const std::string& player)
2086 {
2087  std::string side = std::to_string(side_num);
2088  if(board().get_team(side_num).is_local_human() && player == preferences::login()) {
2089  // this is already our side.
2090  return;
2091  } else {
2092  // The server will (or won't because we aren't allowed to change the controller)
2093  // send us a [change_controller] back, which we then handle in playturn.cpp
2094  pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", player}});
2095  }
2096 }
2097 
2099 {
2100  for(const std::string& command : utils::split(preferences::custom_command(), ';')) {
2101  do_command(command);
2102  }
2103 }
2104 
2106 {
2107  if(!pc_.is_networked_mp()) {
2108  textbox_info_.show(gui::TEXTBOX_AI, translation::sgettext("prompt^Command:"), "", false, *gui_);
2109  }
2110 }
2111 
2113 {
2114  gui_->get_chat_manager().clear_chat_messages(); // also clear debug-messages and WML-error-messages
2115 }
2116 
2118 {
2119  pc_.send_to_wesnothd(cfg);
2120 }
2121 
2122 } // end namespace events
bool show_theme_dialog()
Definition: display.cpp:116
void label_terrain(mouse_handler &mousehandler, bool team_only)
pathfind::marked_route get_route(const unit *un, map_location go_to, team &team) const
int dispatch(lua_State *L)
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
int village_owner(const map_location &loc) const
Given the location of a village, will return the 0-based index of the team that currently owns it...
Dialog was closed with the CANCEL button.
Definition: retval.hpp:37
menu_handler & menu_handler_
bool empty() const
Is it empty?
void do_search(const std::string &new_search)
void set_current_paths(const pathfind::paths &new_paths)
virtual replay_controller * get_replay_controller() const
Game board class.
Definition: game_board.hpp:50
game_data & gamedata()
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
void set_grid(bool ison)
Definition: display.cpp:70
UNIT_ALIGNMENT ALIGNMENT
Definition: types.hpp:182
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &message, events::chat_handler::MESSAGE_TYPE type=events::chat_handler::MESSAGE_PRIVATE) override
void goto_leader(int side_num)
int h() const
Effective map height, in hexes.
Definition: map.hpp:128
unit_iterator end()
Definition: map.hpp:429
void show_help(const std::string &show_topic, int xloc, int yloc)
Open the help browser, show topic with id show_topic.
Definition: help.cpp:114
bool show_fps()
Definition: general.cpp:836
const team & get_team(int side) const
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1157
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:291
bool message_private()
Definition: game.cpp:875
std::map< std::string, t_string > string_map
void write_preferences()
Definition: general.cpp:155
void refresh_objectives() const
Reevaluate [show_if] conditions and build a new objectives string.
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3025
map_location last_search_hit_
file_dialog & set_extension(const std::string &value)
Sets the default file extension for file names in save mode.
This class represents a single unit of a specific type.
Definition: unit.hpp:129
void show_terrain_description(const terrain_type &t)
Definition: help.cpp:62
Various functions implementing vision (through fog of war and shroud).
void send_chat_message(const std::string &message, bool allies_only=false) override
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
bool is_authenticated()
Definition: game.cpp:181
void speak(const config &cfg)
Definition: replay.cpp:347
static manager & get_singleton()
Definition: manager.hpp:151
Various functions that implement attacks and attack calculations.
config & find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:836
map_command_handler< console_handler > chmap
unit_iterator find_leader(int side)
Definition: map.cpp:329
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
std::vector< std::string > get_commands_list() const
#define a
unit_race::GENDER gender()
Gender choice from the user.
Definition: unit_create.hpp:54
const map_location hovered_hex() const
Uses SDL and game_display::hex_clicked_on to fetch the hex the mouse is hovering, if applicable...
Shows an ok and cancel button.
Definition: message.hpp:75
std::string private_message
const game_config_view & game_config_
const t_string & text() const
Definition: label.hpp:136
menu_handler(game_display *gui, play_controller &pc)
Definition: menu_events.cpp:93
child_itors child_range(config_key_type key)
Definition: config.cpp:362
void show_unit_list(display &gui)
Definition: unit_list.cpp:199
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
void do_command(const std::string &str)
const std::string & choice() const
Unit type choice from the user.
Definition: unit_create.hpp:42
attribute_map::value_type attribute
Definition: config.hpp:226
void redraw_everything()
Invalidates entire screen, including all tiles and sidebar.
Definition: display.cpp:2421
gui::floating_textbox & get_textbox()
General purpose widgets.
void request_control_change(int side_num, const std::string &player)
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
bool show_everything() const
Definition: display.hpp:90
virtual std::string get_cmd() const
virtual const gamemap & map() const override
Definition: game_board.hpp:109
int wml_x() const
Definition: location.hpp:157
const unit_type * get_selected_unit_type() const
static config get_auto_shroud(bool turned_on)
Records that the player has toggled automatic shroud updates.
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:97
int viewing_side() const
Definition: display.hpp:103
unit_type_data unit_types
Definition: types.cpp:1376
void print(const std::string &title, const std::string &message)
std::size_t move_unit_and_record(const std::vector< map_location > &steps, undo_list *undo_stack, bool continued_move, bool show_move, bool *interrupted, move_unit_spectator *move_spectator)
Moves a unit across the board.
Definition: move.cpp:1246
const std::string & save_id() const
Definition: team.hpp:231
const color_t LABEL_COLOR
file_dialog & set_save_mode(bool value)
Sets the dialog&#39;s behavior on non-existent file name inputs.
void update_shroud_now(int side_num)
#define VNGETTEXT(msgid, msgid_plural, count,...)
Replay control code.
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
bool do_recruit(const std::string &name, int side_num, map_location &target_hex)
bool chars_equal_insensitive(char a, char b)
Definition: general.hpp:21
Contains the exception interfaces used to signal completion of a scenario, campaign or turn...
bool unit_can_move(const unit &u) const
Will return true iff the unit u has any possible moves it can do (including attacking etc)...
To lexical_cast(From value)
Lexical cast converts one type to another.
bool show(const unsigned auto_close_time=0)
Shows the window.
void create_unit(mouse_handler &mousehandler)
Creates a unit (in debug mode via hotkey or context menu).
const unit_type_map & types() const
Definition: types.hpp:376
save_id_matches(const std::string &save_id)
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
bool yellow_confirm()
Definition: game.cpp:989
bool ellipses()
Definition: general.cpp:496
static synced_state get_synced_state()
std::vector< unit_const_ptr > get_recalls(int side, const map_location &recall_loc)
Gets the recallable units for a side, restricted by that side&#39;s leaders&#39; personal abilities to recall...
Definition: create.cpp:159
static config get_update_shroud()
Records that the player has manually updated fog/shroud.
static bfs::path get_dir(const bfs::path &dirpath)
Definition: filesystem.cpp:278
void show_statistics(int side_num)
A single unit type that the player may recruit.
Definition: types.hpp:44
game_data * gamedata
Definition: resources.cpp:22
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
void flush_cache()
Definition: picture.cpp:225
bool get_disallow_recall() const
bool team_valid() const
Definition: display.cpp:729
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:78
int gold() const
Definition: team.hpp:189
void highlight_another_reach(const pathfind::paths &paths_list, const map_location &goal=map_location::null_location())
Add more paths to highlight.
std::string path() const
Gets the current file selection.
map_location get_selected_hex() const
game_board & board() const
int action_bonus_count() const
Definition: team.hpp:213
bool confirm_no_moves()
Definition: game.cpp:994
const map_location & get_last_hex() const
std::vector< team > & teams() const
unit_map units_
Definition: game_board.hpp:58
const config & options()
Definition: game.cpp:593
std::string write() const
Definition: map.cpp:205
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
bool end_turn(int side_num)
game_display * gui_
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
console_handler(menu_handler &menu_handler)
virtual void register_alias(const std::string &to_cmd, const std::string &cmd)
void toggle_shroud_updates(int side_num)
team & get_team(int i)
Definition: game_board.hpp:104
void set_custom_command(const std::string &command)
Definition: game.cpp:966
virtual std::string get_arg(unsigned i) const
t_string can_recruit(const std::string &name, int side_num, map_location &target_hex, map_location &recruited_from)
void throw_quit_game_exception()
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
const std::vector< unit_race::GENDER > & genders() const
The returned vector will not be empty, provided this has been built to the HELP_INDEXED status...
Definition: types.hpp:238
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:371
void scroll_to_leader(int side, SCROLL_TYPE scroll_type=ONSCREEN, bool force=true)
Scrolls to the leader of a certain side.
const t_string & cannot_end_turn_reason()
Definition: game_data.hpp:79
bool hex_hosts_unit(const map_location &hex) const
Unit exists on the hex, no matter if friend or foe.
int cost() const
Definition: types.hpp:162
void disable_units_highlight()
Use this to disable hovering an unit from highlighting its movement range.
std::vector< team > teams_
Definition: game_board.hpp:53
bool is_enabled(const chmap::command &c) const
const std::set< std::string > get_recruits(int side, const map_location &recruit_loc)
Gets the recruitable units from a side&#39;s leaders&#39; personal recruit lists who can recruit on or from a...
Definition: create.cpp:59
This file contains the settings handling of the widget library.
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition: label.cpp:146
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:382
void do_create()
Implements the (debug mode) console command that creates a unit.
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:136
Applies the planned unit map for the duration of the struct&#39;s life.
Definition: manager.hpp:251
static bool execute(game_board &board, const int viewing_team, int &selected_side_number)
Definition: game_stats.hpp:40
std::string get_user_data_dir()
Definition: filesystem.cpp:795
void clear(const std::string &, bool force)
Definition: label.cpp:210
int wml_y() const
Definition: location.hpp:158
bool disable_autosave
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
static void ignore_error_function(const std::string &message, bool heavy)
a function to be passed to run_in_synced_context to ignore the error.
bool valid() const
Definition: location.hpp:93
const std::unique_ptr< gui::button > & check() const
bool ignore_replay_errors
void recall(int side_num, const map_location &last_hex)
game_board * gameboard
Definition: resources.cpp:20
REMOVE_EMPTY: remove empty elements.
void end_unit_turn(mouse_handler &mousehandler, int side_num)
void set_show_fps(bool value)
Definition: general.cpp:841
Encapsulates the map of the game.
Definition: map.hpp:36
const t_string & name() const
Gets this unit&#39;s translatable display name.
Definition: unit.hpp:394
virtual const std::set< std::string > & observers() const override
void recruit(int side_num, const map_location &last_hex)
const std::string & last_recruit() const
Definition: team.hpp:228
bool has_friends() const
void do_speak(const std::string &message, bool allies_only=false)
bool is_enemy(int n) const
Definition: team.hpp:243
const map_location & get_goto() const
The map location to which this unit is moving over multiple turns, if any.
Definition: unit.hpp:1367
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1945
std::string path
Definition: game_config.cpp:39
void add_rename(const std::string &name, const map_location &loc)
Definition: replay.cpp:290
Shows a yes and no button.
Definition: message.hpp:79
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:139
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
replay * recorder
Definition: resources.cpp:28
Structure which holds a single route and marks for special events.
Definition: pathfind.hpp:140
bool no_choice() const
Whether the user actually chose a unit type or not.
Definition: unit_create.hpp:48
std::shared_ptr< wb::manager > get_whiteboard() const
const terrain_label * get_label(const map_location &loc, const std::string &team_name) const
Definition: label.hpp:47
const char * what() const noexcept
Definition: exceptions.hpp:37
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3018
void set_message_private(bool value)
Definition: game.cpp:880
game_events::manager * game_events
Definition: resources.cpp:24
void show_unit_description(const unit &u)
Definition: help.cpp:57
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:739
const std::string & gender_string(unit_race::GENDER gender)
Definition: race.cpp:131
int recall_cost() const
Definition: team.hpp:193
void flush_cache()
Definition: sound.cpp:194
Encapsulates the map of the game.
Definition: location.hpp:42
bool auto_shroud_updates() const
Definition: team.hpp:338
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:734
Various functions related to moving units.
std::string login()
bool user_end_turn() const
Check whether the user ended their turn.
Definition: unit.hpp:786
unit_iterator find(std::size_t id)
Definition: map.cpp:311
game_state & gamestate() const
Various functions related to the creation of units (recruits, recalls, and placed units)...
actions::undo_list & get_undo_stack()
bool has_moved() const
Checks if this unit has moved.
Definition: unit.hpp:1291
std::string current_team_name() const
void recalculate_labels()
Definition: label.cpp:244
int w() const
Effective map width, in hexes.
Definition: map.hpp:125
const gamemap & map() const
virtual bool is_networked_mp() const
void continue_move(mouse_handler &mousehandler, int side_num)
static void print(std::stringstream &sstr, const std::string &queue, const std::string &id)
Definition: fire_event.cpp:29
void add_alias(const std::string &alias, const std::string &command)
Definition: general.cpp:882
std::size_t i
Definition: function.cpp:933
logger & err()
Definition: log.cpp:78
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:2205
bool is_observer() const
Check if we are an observer in this game.
int selected_index() const
Returns the selected item index after displaying.
void execute_gotos(mouse_handler &mousehandler, int side_num)
virtual void highlight_hex(map_location hex) override
Function to highlight a location.
std::string last_search_
Game configuration data as global variables.
Definition: build_info.cpp:55
std::string get_command_flags_description(const chmap::command &c) const
An exception object used when an IO error occurs.
Definition: filesystem.hpp:48
void terrain_description(mouse_handler &mousehandler)
Define the game&#39;s event mechanism.
std::string find_recall_location(const int side, map_location &recall_location, map_location &recall_from, const unit &unit_recall)
Finds a location on which to recall unit_recall.
Definition: create.cpp:329
ONLY IF whiteboard is currently active, applies the planned unit map for the duration of the struct&#39;s...
Definition: manager.hpp:272
CURSOR_TYPE get()
Definition: cursor.cpp:215
const display_context & get_disp_context() const
Definition: display.hpp:168
void unit_hold_position(mouse_handler &mousehandler, int side_num)
const std::string & team_name() const
Definition: label.cpp:128
void change_side(mouse_handler &mousehandler)
game_data gamedata_
Definition: game_state.hpp:45
int w
std::set< std::string > & encountered_units()
Definition: game.cpp:954
const bool & debug
Definitions for the terrain builder.
void show(gui::TEXTBOX_MODE mode, const std::string &label, const std::string &check_label, bool checked, game_display &gui)
static int sort(lua_State *L)
Definition: ltablib.cpp:411
void set_goto(const map_location &new_goto)
Sets this unit&#39;s long term destination.
Definition: unit.hpp:1373
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
void set_ellipses(bool ison)
Definition: general.cpp:501
void cycle_units(const bool browse, const bool reverse=false)
void kill_unit(mouse_handler &mousehandler)
play_controller & pc_
Handling of system events.
Definition: manager.hpp:42
bool grid()
Definition: general.cpp:506
bool is_replay() const
#define next(ls)
Definition: llex.cpp:32
bool operator()(const team &t) const
display_chat_manager & get_chat_manager()
static bool run_and_throw(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
virtual std::string get_data(unsigned n=1) const
void show_transient_error_message(const std::string &message, const std::string &image, const bool message_use_markup)
Shows a transient error message to the user.
bool green_confirm()
Definition: game.cpp:980
bool empty() const
Definition: tstring.hpp:182
game_events::pump_result_t get_village(const map_location &loc, int side, bool *action_timebonus, bool fire_event)
Makes it so the village at the given location is owned by the given side.
Definition: move.cpp:139
const gamemap & get_map() const
Definition: display.hpp:92
To store label data Class implements logic for rendering.
Definition: label.hpp:107
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
const std::unique_ptr< gui::textbox > & box() const
unit_map::iterator current_unit()
game_state & gamestate()
double t
Definition: astarsearch.cpp:64
bool unhighlight_reach()
Reset highlighting of paths.
bool find(E event, F functor)
Tests whether an event handler is available.
static void display(const game_config_view &game_cfg, const preferences::PREFERENCE_VIEW initial_view=preferences::VIEW_DEFAULT)
The display function – see modal_dialog for more information.
void show_enemy_moves(bool ignore_units, int side_num)
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1336
static color_t get_side_color(int side)
Definition: team.cpp:949
static UNUSEDNOWARN std::string sgettext(const char *str)
Definition: gettext.hpp:68
void set_selected_index(int index)
Sets the initially selected item index (-1 by default).
game_board board_
Definition: game_state.hpp:46
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:102
#define DBG_WB
Definition: typedefs.hpp:27
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Various functions that implement the undoing (and redoing) of in-game commands.
std::string find_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Finds a location on which to place a unit.
Definition: create.cpp:465
void set_action_bonus_count(const int count)
Definition: team.hpp:214
Standard logging facilities (interface).
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:70
recall_list_manager & recall_list()
Definition: team.hpp:215
static const map_location & null_location()
Definition: location.hpp:85
Container associating units to locations.
Definition: map.hpp:99
std::string get_command_flags_description(const map_command_handler< chat_command_handler >::command &c) const
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:28
std::string get_flags_description() const
static vconfig empty_vconfig()
Definition: variable.cpp:141
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
map_labels & labels()
Definition: display.cpp:2540
int turn() const
#define e
retval
Default window/dialog return values.
Definition: retval.hpp:28
int side() const
The side this unit belongs to.
Definition: unit.hpp:334
Definition: help.cpp:55
bool player_acted() const
Returns true if the player has performed any actions this turn.
Definition: undo.hpp:84
Dialog was closed with the OK button.
Definition: retval.hpp:34
gui::floating_textbox textbox_info_
void send_to_server(const config &cfg) override
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
void notify_event(const std::string &name, const config &data)
Definition: manager.cpp:144
mock_char c
const std::string & str() const
Definition: tstring.hpp:186
const config & get_alias()
Definition: general.cpp:889
void set_route(const pathfind::marked_route *route)
Sets the route along which footsteps are drawn to show movement of a unit.
Enables auto close.
Definition: message.hpp:69
std::string custom_command()
Definition: game.cpp:962
std::vector< std::string > get_commands_list()
static map_location::DIRECTION n
void repeat_recruit(int side_num, const map_location &last_hex)
void move_unit_to_loc(const unit_map::iterator &ui, const map_location &target, bool continue_move, int side_num, mouse_handler &mousehandler)
Thrown when a lexical_cast fails.
void clear_labels(const std::string &, bool)
Definition: replay.cpp:280
const unsigned int team_num_
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1261
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:177
virtual void send_to_wesnothd(const config &, const std::string &="unknown") const
Display units performing various actions: moving, attacking, and dying.
std::string variation() const
Variation choice from the user.
Definition: unit_create.hpp:60
virtual void register_command(const std::string &cmd, chat_command_handler::command_handler h, const std::string &help="", const std::string &usage="", const std::string &flags="")
play_controller & pc_
std::vector< map_location > & steps
Definition: pathfind.hpp:186
void set_debug(bool new_debug)
static plugins_manager * get()
Definition: manager.cpp:58
void show_objectives() const
void add_label(const terrain_label *)
Definition: replay.cpp:269
const std::string observer_team_name
observer team name used for observer team chat
Definition: game_config.cpp:90
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
Definition: file_dialog.hpp:56