The Battle for Wesnoth  1.15.5+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  std::vector<t_string> unknown_units;
263  for(const auto& recruit : recruits) {
265  if(!type) {
266  ERR_NG << "could not find unit '" << recruit << "'" << std::endl;
267  unknown_units.emplace_back(recruit);
268  continue;
269  }
270 
271  map_location ignored;
272  map_location recruit_hex = last_hex;
273  sample_units[type] = (can_recruit(type->id(), side_num, recruit_hex, ignored));
274  }
275  if(!unknown_units.empty()) {
276  auto unknown_ids = utils::format_conjunct_list("", unknown_units);
277  // TRANSLATORS: An error that should never happen, might happen when loading an old savegame. If there are
278  // any units that the player can recruit then their standard recruitment dialog will be shown after this
279  // error message, otherwise they'll get the "You have no units available to recruit." error after this one.
280  const auto message = VNGETTEXT("Error: there’s an unknown unit type on your recruit list: $unknown_ids",
281  "Error: there are several unknown unit types on your recruit list: $unknown_ids",
282  unknown_units.size(),
283  utils::string_map { { "unknown_ids", unknown_ids }});
284  gui2::show_transient_message("", message);
285  }
286 
287  if(sample_units.empty()) {
288  gui2::show_transient_message("", _("You have no units available to recruit."));
289  return;
290  }
291 
292  gui2::dialogs::unit_recruit dlg(sample_units, board().get_team(side_num));
293 
294  if(dlg.show()) {
295  map_location recruit_hex = last_hex;
296  const unit_type *type = dlg.get_selected_unit_type();
297  if (!type) {
298  gui2::show_transient_message("", _("No unit recruited."));
299  return;
300  }
301  do_recruit(type->id(), side_num, recruit_hex);
302  }
303 }
304 
305 void menu_handler::repeat_recruit(int side_num, const map_location& last_hex)
306 {
307  const std::string& last_recruit = board().get_team(side_num).last_recruit();
308  if(last_recruit.empty() == false) {
309  map_location recruit_hex = last_hex;
310  do_recruit(last_recruit, side_num, recruit_hex);
311  }
312 }
313 
314 // TODO: Return multiple strings here, in case more than one error applies? For
315 // example, if you start AOI S5 with 0GP and recruit a Mage, two reasons apply,
316 // leader not on keep (extrarecruit=Mage) and not enough gold.
317 t_string menu_handler::can_recruit(const std::string& name, int side_num, map_location& loc, map_location& recruited_from)
318 {
319  team& current_team = board().get_team(side_num);
320 
321  const unit_type* u_type = unit_types.find(name);
322  if(u_type == nullptr) {
323  return _("Internal error. Please report this as a bug! Details:\n")
324  + "menu_handler::can_recruit: u_type == nullptr for " + name;
325  }
326 
327  // search for the unit to be recruited in recruits
328  if(!utils::contains(actions::get_recruits(side_num, loc), name)) {
329  return VGETTEXT("You cannot recruit a $unit_type_name at this time.",
330  utils::string_map { { "unit_type_name", u_type->type_name() }});
331  }
332 
333  // TODO take a wb::future_map RAII as unit_recruit::pre_show does
334  int wb_gold = 0;
335  {
336  wb::future_map future;
337  wb_gold = (pc_.get_whiteboard() ? pc_.get_whiteboard()->get_spent_gold_for(side_num) : 0);
338  }
339  if(u_type->cost() > current_team.gold() - wb_gold)
340  {
341  if(wb_gold > 0)
342  // TRANSLATORS: "plan" refers to Planning Mode
343  return _("At this point in your plan, you will not have enough gold to recruit this unit.");
344  else
345  return _("You do not have enough gold to recruit this unit.");
346  }
347 
348  current_team.last_recruit(name);
349  const events::command_disabler disable_commands;
350 
351  {
352  wb::future_map_if_active future; /* start planned unit map scope if in planning mode */
353  std::string msg = actions::find_recruit_location(side_num, loc, recruited_from, name);
354  if(!msg.empty()) {
355  return msg;
356  }
357  } // end planned unit map scope
358 
359  return "";
360 }
361 
362 bool menu_handler::do_recruit(const std::string& name, int side_num, map_location& loc)
363 {
364  map_location recruited_from = map_location::null_location();
365  const std::string res = can_recruit(name, side_num, loc, recruited_from);
366  team& current_team = board().get_team(side_num);
367 
368  if(res.empty() && (!pc_.get_whiteboard() || !pc_.get_whiteboard()->save_recruit(name, side_num, loc))) {
369  // MP_COUNTDOWN grant time bonus for recruiting
370  current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
371 
372  // Do the recruiting.
373 
374  synced_context::run_and_throw("recruit", replay_helper::get_recruit(name, loc, recruited_from));
375  return true;
376  } else if(res.empty()) {
377  return false;
378  } else {
380  return false;
381  }
382 
383 }
384 
385 void menu_handler::recall(int side_num, const map_location& last_hex)
386 {
387  if(pc_.get_disallow_recall()) {
388  gui2::show_transient_message("", _("You are separated from your soldiers and may not recall them."));
389  return;
390  }
391 
392  team& current_team = board().get_team(side_num);
393 
394  std::vector<unit_const_ptr> recall_list_team;
395  bool empty;
396  {
397  wb::future_map future; // ensures recall list has planned recalls removed
398  recall_list_team = actions::get_recalls(side_num, last_hex);
399  empty = current_team.recall_list().empty();
400  }
401 
402  DBG_WB << "menu_handler::recall: Contents of wb-modified recall list:\n";
403  for(const unit_const_ptr& unit : recall_list_team) {
404  DBG_WB << unit->name() << " [" << unit->id() << "]\n";
405  }
406 
407  if(empty) {
409  _("There are no troops available to recall.\n(You must have veteran survivors from a previous scenario.)"));
410  return;
411  }
412  if(recall_list_team.empty()) {
413  gui2::show_transient_message("", _("You currently can't recall at the highlighted location."));
414  return;
415  }
416 
417  gui2::dialogs::unit_recall dlg(recall_list_team, current_team);
418 
419  if(!dlg.show()) {
420  return;
421  }
422 
423  int res = dlg.get_selected_index();
424  if (res < 0) {
425  gui2::show_transient_message("", _("No unit recalled."));
426  return;
427  }
428  int unit_cost = current_team.recall_cost();
429 
430  // we need to check if unit has a specific recall cost
431  // if it does we use it elsewise we use the team.recall_cost()
432  // the magic number -1 is what it gets set to if the unit doesn't
433  // have a special recall_cost of its own.
434  if(recall_list_team[res]->recall_cost() > -1) {
435  unit_cost = recall_list_team[res]->recall_cost();
436  }
437 
438  int wb_gold = pc_.get_whiteboard() ? pc_.get_whiteboard()->get_spent_gold_for(side_num) : 0;
439  if(current_team.gold() - wb_gold < unit_cost) {
440  utils::string_map i18n_symbols;
441  i18n_symbols["cost"] = std::to_string(unit_cost);
442  std::string msg = VNGETTEXT("You must have at least 1 gold piece to recall a unit.",
443  "You must have at least $cost gold pieces to recall this unit.", unit_cost, i18n_symbols);
445  return;
446  }
447 
448  LOG_NG << "recall index: " << res << "\n";
449  const events::command_disabler disable_commands;
450 
451  map_location recall_location = last_hex;
454  {
456  future; // future unit map removes invisible units from map, don't do this outside of planning mode
457  err = actions::find_recall_location(side_num, recall_location, recall_from, *recall_list_team[res].get());
458  } // end planned unit map scope
459 
460  if(!err.empty()) {
462  return;
463  }
464 
465  if(!pc_.get_whiteboard()
466  || !pc_.get_whiteboard()->save_recall(*recall_list_team[res].get(), side_num, recall_location)) {
467  bool success = synced_context::run_and_throw("recall",
468  replay_helper::get_recall(recall_list_team[res]->id(), recall_location, recall_from), true, true,
470 
471  if(!success) {
472  ERR_NG << "menu_handler::recall(): Unit does not exist in the recall list." << std::endl;
473  }
474  }
475 }
476 
477 // Highlights squares that an enemy could move to on their turn, showing how many can reach each square.
478 void menu_handler::show_enemy_moves(bool ignore_units, int side_num)
479 {
480  wb::future_map future; // use unit positions as if all planned actions were executed
481 
483  const map_location& hex_under_mouse = mh.hovered_hex();
484 
486 
487  // Compute enemy movement positions
488  for(auto& u : units()) {
489  bool invisible = u.invisible(u.get_location());
490 
491  if(board().get_team(side_num).is_enemy(u.side()) && !gui_->fogged(u.get_location()) && !u.incapacitated()
492  && !invisible) {
493  const unit_movement_resetter move_reset(u);
494  const pathfind::paths& path
495  = pathfind::paths(u, false, true, teams()[gui_->viewing_team()], 0, false, ignore_units);
496 
497  gui_->highlight_another_reach(path, hex_under_mouse);
498  }
499 
500  // Need to recompute ellipses for highlighted enemy units
501  gui_->invalidate(u.get_location());
502  }
503 
504  // Find possible unit (no matter whether friend or foe) under the
505  // mouse cursor.
506  const bool selected_hex_has_unit = mh.hex_hosts_unit(hex_under_mouse);
507 
508  if(selected_hex_has_unit) {
509  // At this point, a single pixel move would remove the enemy
510  // [best possible] movements hex tiles highlights, so some
511  // prevention on normal unit mouseover movement highlight
512  // has to be toggled temporarily.
514  }
515 }
516 
518 {
519  team& current_team = board().get_team(side_num);
520  bool auto_shroud = current_team.auto_shroud_updates();
521  // If we're turning automatic shroud updates on, then commit all moves
522  if(!auto_shroud) {
523  update_shroud_now(side_num);
524  }
525 
526  // Toggle the setting and record this.
528 }
529 
530 void menu_handler::update_shroud_now(int /* side_num */)
531 {
533 }
534 
535 // Helpers for menu_handler::end_turn()
536 namespace
537 {
538 /** Returns true if @a side_num has at least one living unit. */
539 bool units_alive(int side_num, const unit_map& units)
540 {
541  for(auto& unit : units) {
542  if(unit.side() == side_num) {
543  return true;
544  }
545  }
546  return false;
547 }
548 
549 /** Returns true if @a side_num has at least one unit that can still move. */
550 bool partmoved_units(
551  int side_num, const unit_map& units, const game_board& board, const std::shared_ptr<wb::manager>& whiteb)
552 {
553  for(auto& unit : units) {
554  if(unit.side() == side_num) {
555  // @todo whiteboard should take into consideration units that have
556  // a planned move but can still plan more movement in the same turn
557  if(board.unit_can_move(unit) && !unit.user_end_turn() && (!whiteb || !whiteb->unit_has_actions(&unit)))
558  return true;
559  }
560  }
561  return false;
562 }
563 
564 /**
565  * Returns true if @a side_num has at least one unit that (can but) has not moved.
566  */
567 bool unmoved_units(
568  int side_num, const unit_map& units, const game_board& board, const std::shared_ptr<wb::manager>& whiteb)
569 {
570  for(auto& unit : units) {
571  if(unit.side() == side_num) {
572  if(board.unit_can_move(unit) && !unit.has_moved() && !unit.user_end_turn()
573  && (!whiteb || !whiteb->unit_has_actions(&unit))) {
574  return true;
575  }
576  }
577  }
578  return false;
579 }
580 
581 } // end anon namespace
582 
583 bool menu_handler::end_turn(int side_num)
584 {
585  if(!gamedata().allow_end_turn()) {
587  if(reason.empty()) {
588  reason = _("You cannot end your turn yet!");
589  }
590  gui2::show_transient_message("", reason);
591  return false;
592  }
593 
594  std::size_t team_num = static_cast<std::size_t>(side_num - 1);
595  if(team_num < teams().size() && teams()[team_num].no_turn_confirmation()) {
596  // Skip the confirmations that follow.
597  }
598  // Ask for confirmation if the player hasn't made any moves.
600  && (!pc_.get_whiteboard() || !pc_.get_whiteboard()->current_side_has_actions())
601  && units_alive(side_num, units())) {
602  const int res = gui2::show_message("",
603  _("You have not started your turn yet. Do you really want to end your turn?"),
605  if(res == gui2::retval::CANCEL) {
606  return false;
607  }
608  }
609  // Ask for confirmation if units still have some movement left.
610  else if(preferences::yellow_confirm() && partmoved_units(side_num, units(), board(), pc_.get_whiteboard())) {
611  const int res = gui2::show_message("",
612  _("Some units have movement left. Do you really want to end your turn?"),
614  if(res == gui2::retval::CANCEL) {
615  return false;
616  }
617  }
618  // Ask for confirmation if units still have all movement left.
619  else if(preferences::green_confirm() && unmoved_units(side_num, units(), board(), pc_.get_whiteboard())) {
620  const int res = gui2::show_message("",
621  _("Some units have not moved. Do you really want to end your turn?"),
623  if(res == gui2::retval::CANCEL) {
624  return false;
625  }
626  }
627 
628  // Auto-execute remaining whiteboard planned actions
629  // Only finish turn if they all execute successfully, i.e. no ambush, etc.
630  if(pc_.get_whiteboard() && !pc_.get_whiteboard()->allow_end_turn()) {
631  return false;
632  }
633 
634  return true;
635 }
636 
637 void menu_handler::goto_leader(int side_num)
638 {
640  const display_context& dc = gui_->get_disp_context();
641  if(i != units().end() && i->is_visible_to_team(dc.get_team(gui_->viewing_side()), false)) {
642  gui_->scroll_to_tile(i->get_location(), game_display::WARP);
643  }
644 }
645 
647 {
649  if(un != units().end()) {
651  }
652 }
653 
655 {
656  const map_location& loc = mousehandler.get_last_hex();
657  if(map().on_board(loc) == false || gui_->shrouded(loc)) {
658  return;
659  }
660 
661  const terrain_type& type = map().get_terrain_info(loc);
662  // const terrain_type& info = board().map().get_terrain_info(terrain);
664 }
665 
667 {
668  const unit_map::iterator un = current_unit();
669  if(un == units().end() || gui_->viewing_side() != un->side()) {
670  return;
671  }
672 
673  if(un->unrenamable()) {
674  return;
675  }
676 
677  std::string name = un->name();
678  const std::string title(_("Rename Unit"));
679  const std::string label(_("Name:"));
680 
681  if(gui2::dialogs::edit_text::execute(title, label, name)) {
682  resources::recorder->add_rename(name, un->get_location());
683  un->rename(name);
685  }
686 }
687 
689 {
690  const mouse_handler& mousehandler = pc_.get_mouse_handler_base();
691  const bool see_all = gui_->show_everything() || (pc_.is_replay() && pc_.get_replay_controller()->see_all());
692 
693  unit_map::iterator res = board().find_visible_unit(mousehandler.get_last_hex(), teams()[gui_->viewing_team()], see_all);
694  if(res != units().end()) {
695  return res;
696  }
697 
698  return board().find_visible_unit(mousehandler.get_selected_hex(), teams()[gui_->viewing_team()], see_all);
699 }
700 
701 // Helpers for create_unit()
702 namespace
703 {
704 /// Allows a function to return both a type and a gender.
705 typedef std::tuple<const unit_type*, unit_race::GENDER, std::string> type_gender_variation;
706 
707 /**
708  * Allows the user to select a type of unit, using GUI2.
709  * (Intended for use when a unit is created in debug mode via hotkey or
710  * context menu.)
711  * @returns the selected type and gender. If this is canceled, the
712  * returned type is nullptr.
713  */
714 type_gender_variation choose_unit()
715 {
716  //
717  // The unit creation dialog makes sure unit types
718  // are properly cached.
719  //
720  gui2::dialogs::unit_create create_dlg;
721  create_dlg.show();
722 
723  if(create_dlg.no_choice()) {
724  return type_gender_variation(nullptr, unit_race::NUM_GENDERS, "");
725  }
726 
727  const std::string& ut_id = create_dlg.choice();
728  const unit_type* utp = unit_types.find(ut_id);
729  if(!utp) {
730  ERR_NG << "Create unit dialog returned nonexistent or unusable unit_type id '" << ut_id << "'." << std::endl;
731  return type_gender_variation(static_cast<const unit_type*>(nullptr), unit_race::NUM_GENDERS, "");
732  }
733  const unit_type& ut = *utp;
734 
735  unit_race::GENDER gender = create_dlg.gender();
736  // Do not try to set bad genders, may mess up l10n
737  /// @todo Is this actually necessary?
738  /// (Maybe create_dlg can enforce proper gender selection?)
739  if(ut.genders().end() == std::find(ut.genders().begin(), ut.genders().end(), gender)) {
740  gender = ut.genders().front();
741  }
742 
743  return type_gender_variation(utp, gender, create_dlg.variation());
744 }
745 
746 /**
747  * Creates a unit and places it on the board.
748  * (Intended for use with any units created via debug mode.)
749  */
750 void create_and_place(game_display&,
751  const gamemap&,
752  unit_map&,
753  const map_location& loc,
754  const unit_type& u_type,
756  const std::string& variation = "")
757 {
758  synced_context::run_and_throw("debug_create_unit",
759  config {
760  "x", loc.wml_x(),
761  "y", loc.wml_y(),
762  "type", u_type.id(),
763  "gender", gender_string(gender),
764  "variation", variation,
765  }
766  );
767 }
768 
769 } // Anonymous namespace
770 
771 /**
772  * Creates a unit (in debug mode via hotkey or context menu).
773  */
775 {
776  // Save the current mouse location before popping up the choice menu (which
777  // gives time for the mouse to move, changing the location).
778  const map_location destination = mousehandler.get_last_hex();
779  assert(gui_ != nullptr);
780 
781  // Let the user select the kind of unit to create.
782  type_gender_variation selection = choose_unit();
783  if(std::get<0>(selection) != nullptr) {
784  // Make it so.
785  create_and_place(*gui_, map(), units(), destination, *std::get<0>(selection), std::get<1>(selection), std::get<2>(selection));
786  }
787 }
788 
790 {
791  const map_location& loc = mousehandler.get_last_hex();
792  const unit_map::iterator i = units().find(loc);
793  if(i == units().end()) {
794  if(!map().is_village(loc)) {
795  return;
796  }
797 
798  // village_owner returns 0 for free village, so team 0 will get it
799  int team = board().village_owner(loc);
800  // team is 0-based so team=team::nteams() is not a team
801  // but this will make get_village free it
802  if(team > static_cast<int>(teams().size())) {
803  team = 0;
804  }
805  actions::get_village(loc, team);
806  } else {
807  int side = i->side();
808  ++side;
809  if(side > static_cast<int>(teams().size())) {
810  side = 1;
811  }
812  i->set_side(side);
813 
814  if(map().is_village(loc)) {
815  actions::get_village(loc, side);
816  }
817  }
818 }
819 
821 {
822  const map_location loc = mousehandler.get_last_hex();
823  synced_context::run_and_throw("debug_kill", config {"x", loc.wml_x(), "y", loc.wml_y()});
824 }
825 
826 void menu_handler::label_terrain(mouse_handler& mousehandler, bool team_only)
827 {
828  const map_location& loc = mousehandler.get_last_hex();
829  if(map().on_board(loc) == false) {
830  return;
831  }
832 
833  const terrain_label* old_label = gui_->labels().get_label(loc);
834  std::string label = old_label ? old_label->text() : "";
835 
836  if(gui2::dialogs::edit_label::execute(label, team_only)) {
837  std::string team_name;
838  color_t color = font::LABEL_COLOR;
839 
840  if(team_only) {
841  team_name = gui_->labels().team_name();
842  } else {
844  }
845  const terrain_label* res = gui_->labels().set_label(loc, label, gui_->viewing_team(), team_name, color);
846  if(res) {
848  }
849  }
850 }
851 
853 {
854  if(gui_->team_valid() && !board().is_observer()) {
855  const int res = gui2::show_message(
856  _("Clear Labels"),
857  _("Are you sure you want to clear map labels?"),
859  );
860 
861  if(res == gui2::retval::OK) {
862  gui_->labels().clear(gui_->current_team_name(), false);
864  }
865  }
866 }
867 
869 {
872  }
873 }
874 
875 void menu_handler::continue_move(mouse_handler& mousehandler, int side_num)
876 {
878  if(i == units().end() || !i->move_interrupted()) {
879  i = units().find(mousehandler.get_selected_hex());
880  if(i == units().end() || !i->move_interrupted()) {
881  return;
882  }
883  }
884  move_unit_to_loc(i, i->get_interrupted_move(), true, side_num, mousehandler);
885 }
886 
888  const map_location& target,
889  bool continue_move,
890  int side_num,
891  mouse_handler& mousehandler)
892 {
893  assert(ui != units().end());
894 
895  pathfind::marked_route route = mousehandler.get_route(&*ui, target, board().get_team(side_num));
896 
897  if(route.steps.empty()) {
898  return;
899  }
900 
901  assert(route.steps.front() == ui->get_location());
902 
903  gui_->set_route(&route);
905 
906  {
907  LOG_NG << "move_unit_to_loc " << route.steps.front() << " to " << route.steps.back() << "\n";
909  }
910 
911  gui_->set_route(nullptr);
913 }
914 
915 void menu_handler::execute_gotos(mouse_handler& mousehandler, int side)
916 {
917  // we will loop on all gotos and try to fully move a maximum of them,
918  // but we want to avoid multiple blocking of the same unit,
919  // so, if possible, it's better to first wait that the blocker move
920 
921  bool wait_blocker_move = true;
922  std::set<map_location> fully_moved;
923 
924  bool change = false;
925  bool blocked_unit = false;
926  do {
927  change = false;
928  blocked_unit = false;
929  for(auto& unit : units()) {
930  if(unit.side() != side || unit.movement_left() == 0) {
931  continue;
932  }
933 
934  const map_location& current_loc = unit.get_location();
935  const map_location& goto_loc = unit.get_goto();
936 
937  if(goto_loc == current_loc) {
939  continue;
940  }
941 
942  if(!map().on_board(goto_loc)) {
943  continue;
944  }
945 
946  // avoid pathfinding calls for finished units
947  if(fully_moved.count(current_loc)) {
948  continue;
949  }
950 
951  pathfind::marked_route route = mousehandler.get_route(&unit, goto_loc, board().get_team(side));
952 
953  if(route.steps.size() <= 1) { // invalid path
954  fully_moved.insert(current_loc);
955  continue;
956  }
957 
958  // look where we will stop this turn (turn_1 waypoint or goto)
959  map_location next_stop = goto_loc;
960  pathfind::marked_route::mark_map::const_iterator w = route.marks.begin();
961  for(; w != route.marks.end(); ++w) {
962  if(w->second.turns == 1) {
963  next_stop = w->first;
964  break;
965  }
966  }
967 
968  if(next_stop == current_loc) {
969  fully_moved.insert(current_loc);
970  continue;
971  }
972 
973  // we delay each blocked move because some other change
974  // may open a another not blocked path
975  if(units().count(next_stop)) {
976  blocked_unit = true;
977  if(wait_blocker_move)
978  continue;
979  }
980 
981  gui_->set_route(&route);
982 
983  {
984  LOG_NG << "execute goto from " << route.steps.front() << " to " << route.steps.back() << "\n";
985  int moves = actions::move_unit_and_record(route.steps, &pc_.get_undo_stack());
986  change = moves > 0;
987  }
988 
989  if(change) {
990  // something changed, resume waiting blocker (maybe one can move now)
991  wait_blocker_move = true;
992  }
993  }
994 
995  if(!change && wait_blocker_move) {
996  // no change when waiting, stop waiting and retry
997  wait_blocker_move = false;
998  change = true;
999  }
1000  } while(change && blocked_unit);
1001 
1002  // erase the footsteps after movement
1003  gui_->set_route(nullptr);
1005 }
1006 
1008 {
1010  gui_->invalidate_all(); // TODO can fewer tiles be invalidated?
1011 }
1012 
1014 {
1016  gui_->invalidate_all();
1017 }
1018 
1019 void menu_handler::unit_hold_position(mouse_handler& mousehandler, int side_num)
1020 {
1021  const unit_map::iterator un = units().find(mousehandler.get_selected_hex());
1022  if(un != units().end() && un->side() == side_num && un->movement_left() >= 0) {
1023  un->toggle_hold_position();
1024  gui_->invalidate(mousehandler.get_selected_hex());
1025 
1026  mousehandler.set_current_paths(pathfind::paths());
1027 
1028  if(un->hold_position()) {
1029  mousehandler.cycle_units(false);
1030  }
1031  }
1032 }
1033 
1034 void menu_handler::end_unit_turn(mouse_handler& mousehandler, int side_num)
1035 {
1036  const unit_map::iterator un = units().find(mousehandler.get_selected_hex());
1037  if(un != units().end() && un->side() == side_num && un->movement_left() >= 0) {
1038  un->toggle_user_end_turn();
1039  gui_->invalidate(mousehandler.get_selected_hex());
1040 
1041  mousehandler.set_current_paths(pathfind::paths());
1042 
1043  if(un->user_end_turn()) {
1044  mousehandler.cycle_units(false);
1045  }
1046 
1047  // If cycle_units hasn't found a new unit to cycle to then the original unit is still selected, but
1048  // in a state where left-clicking on it does nothing. Make it respond to mouse clicks again.
1049  if(un == units().find(mousehandler.get_selected_hex())) {
1050  mousehandler.deselect_hex();
1051  }
1052  }
1053 }
1054 
1056 {
1057  std::ostringstream msg;
1058  msg << _("Search");
1059  if(last_search_hit_.valid()) {
1060  msg << " [" << last_search_ << "]";
1061  }
1062  msg << ':';
1063  textbox_info_.show(gui::TEXTBOX_SEARCH, msg.str(), "", false, *gui_);
1064 }
1065 
1067 {
1068  // None of the two parameters really needs to be passed since the information belong to members of the class.
1069  // But since it makes the called method more generic, it is done anyway.
1071  textbox_info_.box()->text(), textbox_info_.check() != nullptr ? textbox_info_.check()->checked() : false);
1072 }
1073 
1074 void menu_handler::add_chat_message(const std::time_t& time,
1075  const std::string& speaker,
1076  int side,
1077  const std::string& message,
1079 {
1080  gui_->get_chat_manager().add_chat_message(time, speaker, side, message, type, false);
1081 
1083  config {
1084  "sender", preferences::login(),
1085  "message", message,
1086  "whisper", type == events::chat_handler::MESSAGE_PRIVATE,
1087  }
1088  );
1089 }
1090 
1091 // command handler for user :commands. Also understands all chat commands
1092 // via inheritance. This complicates some things a bit.
1093 class console_handler : public map_command_handler<console_handler>, private chat_command_handler
1094 {
1095 public:
1096  // convenience typedef
1099  : chmap()
1100  , chat_command_handler(menu_handler, true)
1101  , menu_handler_(menu_handler)
1102  , team_num_(menu_handler.pc_.current_side())
1103  {
1104  }
1105 
1106  using chmap::dispatch; // disambiguate
1107  using chmap::get_commands_list;
1108  using chmap::command_failed;
1109 
1110 protected:
1111  // chat_command_handler's init_map() and handlers will end up calling these.
1112  // this makes sure the commands end up in our map
1113  virtual void register_command(const std::string& cmd,
1115  const std::string& help = "",
1116  const std::string& usage = "",
1117  const std::string& flags = "")
1118  {
1119  chmap::register_command(cmd, h, help, usage, flags + "N"); // add chat commands as network_only
1120  }
1121 
1122  virtual void register_alias(const std::string& to_cmd, const std::string& cmd)
1123  {
1124  chmap::register_alias(to_cmd, cmd);
1125  }
1126 
1127  virtual std::string get_arg(unsigned i) const
1128  {
1129  return chmap::get_arg(i);
1130  }
1131 
1132  virtual std::string get_cmd() const
1133  {
1134  return chmap::get_cmd();
1135  }
1136 
1137  virtual std::string get_data(unsigned n = 1) const
1138  {
1139  return chmap::get_data(n);
1140  }
1141 
1142  // these are needed to avoid ambiguities introduced by inheriting from console_command_handler
1143  using chmap::register_command;
1144  using chmap::register_alias;
1145  using chmap::help;
1146  using chmap::is_enabled;
1147  using chmap::command_failed_need_arg;
1148 
1149  void do_refresh();
1150  void do_droid();
1151  void do_terrain();
1152  void do_idle();
1153  void do_theme();
1154  void do_control();
1155  void do_controller();
1156  void do_clear();
1157  void do_foreground();
1158  void do_layers();
1159  void do_fps();
1160  void do_benchmark();
1161  void do_save();
1162  void do_save_quit();
1163  void do_quit();
1164  void do_ignore_replay_errors();
1165  void do_nosaves();
1166  void do_next_level();
1167  void do_choose_level();
1168  void do_turn();
1169  void do_turn_limit();
1170  void do_debug();
1171  void do_nodebug();
1172  void do_lua();
1173  void do_unsafe_lua();
1174  void do_custom();
1175  void do_set_alias();
1176  void do_set_var();
1177  void do_show_var();
1178  void do_inspect();
1179  void do_control_dialog();
1180  void do_unit();
1181  // void do_buff();
1182  // void do_unbuff();
1183  void do_discover();
1184  void do_undiscover();
1185  void do_create();
1186  void do_fog();
1187  void do_shroud();
1188  void do_gold();
1189  void do_event();
1190  void do_toggle_draw_coordinates();
1191  void do_toggle_draw_terrain_codes();
1192  void do_toggle_draw_num_of_bitmaps();
1193  void do_toggle_whiteboard();
1194  void do_whiteboard_options();
1195 
1197  {
1198  return _("(D) — debug only, (N) — network only, (A) — admin only");
1199  }
1200 
1201  using chat_command_handler::get_command_flags_description; // silence a warning
1202  std::string get_command_flags_description(const chmap::command& c) const
1203  {
1204  std::string space(" ");
1205  return (c.has_flag('D') ? space + _("(debug command)") : "")
1206  + (c.has_flag('N') ? space + _("(network only)") : "")
1207  + (c.has_flag('A') ? space + _("(admin only)") : "")
1208  + (c.has_flag('S') ? space + _("(not during other events)") : "");
1209  }
1210 
1211  using map::is_enabled;
1212  bool is_enabled(const chmap::command& c) const
1213  {
1214  return !((c.has_flag('D') && !game_config::debug)
1215  || (c.has_flag('N') && !menu_handler_.pc_.is_networked_mp())
1216  || (c.has_flag('A') && !preferences::is_authenticated())
1217  || (c.has_flag('S') && (synced_context::get_synced_state() != synced_context::UNSYNCED || !menu_handler_.pc_.current_team().is_local())));
1218  }
1219 
1220  void print(const std::string& title, const std::string& message)
1221  {
1222  menu_handler_.add_chat_message(std::time(nullptr), title, 0, message);
1223  }
1224 
1225  void init_map()
1226  {
1227  chat_command_handler::init_map(); // grab chat_ /command handlers
1228 
1229  chmap::get_command("log")->flags = ""; // clear network-only flag from log
1230  chmap::get_command("version")->flags = ""; // clear network-only flag
1231  chmap::get_command("ignore")->flags = ""; // clear network-only flag
1232  chmap::get_command("friend")->flags = ""; // clear network-only flag
1233  chmap::get_command("remove")->flags = ""; // clear network-only flag
1234 
1235  chmap::set_cmd_prefix(":");
1236 
1237  register_command("refresh", &console_handler::do_refresh, _("Refresh gui."));
1238  register_command("droid", &console_handler::do_droid, _("Switch a side to/from AI control."),
1239  // TRANSLATORS: These are the arguments accepted by the "droid" command,
1240  // which must be a side-number and then optionally one of "on", "off" or "full".
1241  // As with the command's name, "on", "off" and "full" are hardcoded, and shouldn't change in the translation.
1242  _("[<side> [on/off/full]]\n“on” = enable but retain vision, “full” = as if it’s controlled by another player"));
1243  register_command("terrain", &console_handler::do_terrain, _("Change terrain type of current hex"),
1244  // TRANSLATORS: [both|base|overlay] are hardcoded literal arguments and shouldn't be translated.
1245  _("<terrain type> [both|base|overlay]"), "DS");
1246  register_command("idle", &console_handler::do_idle, _("Switch a side to/from idle state."),
1247  // TRANSLATORS: These are the arguments accepted by the "idle" command,
1248  // which must be a side-number and then optionally "on" or "off".
1249  // As with the command's name, "on" and "off" are hardcoded, and shouldn't change in the translation.
1250  _("command_idle^[<side> [on/off]]"));
1251  register_command("theme", &console_handler::do_theme, _("Change the in-game theme."));
1252  register_command("control", &console_handler::do_control,
1253  _("Assign control of a side to a different player or observer."), _("<side> <nickname>"), "N");
1254  register_command("controller", &console_handler::do_controller, _("Query the controller status of a side."),
1255  _("<side>"));
1256  register_command("clear", &console_handler::do_clear, _("Clear chat history."));
1257  register_command("foreground", &console_handler::do_foreground, _("Debug foreground terrain."), "", "D");
1258  register_command(
1259  "layers", &console_handler::do_layers, _("Debug layers from terrain under the mouse."), "", "D");
1260  register_command("fps", &console_handler::do_fps, _("Show fps (Frames Per Second)."));
1261  register_command("benchmark", &console_handler::do_benchmark);
1262  register_command("save", &console_handler::do_save, _("Save game."));
1263  register_alias("save", "w");
1264  register_command("quit", &console_handler::do_quit, _("Quit game."));
1265  // Note the next value is used hardcoded in the init tests.
1266  register_alias("quit", "q!");
1267  register_command("save_quit", &console_handler::do_save_quit, _("Save and quit."));
1268  register_alias("save_quit", "wq");
1269  register_command("ignore_replay_errors", &console_handler::do_ignore_replay_errors, _("Ignore replay errors."));
1270  register_command("nosaves", &console_handler::do_nosaves, _("Disable autosaves."));
1271  register_command("next_level", &console_handler::do_next_level,
1272  _("Advance to the next scenario, or scenario identified by 'id'"), _("<id>"), "DS");
1273  register_alias("next_level", "n");
1274  register_command("choose_level", &console_handler::do_choose_level, _("Choose next scenario"), "", "DS");
1275  register_alias("choose_level", "cl");
1276  register_command("turn", &console_handler::do_turn,
1277  _("Change turn number (and time of day), or increase by one if no number is specified."), _("[turn]"),
1278  "DS");
1279  register_command("turn_limit", &console_handler::do_turn_limit,
1280  _("Change turn limit, or turn the turn limit off if no number is specified or it’s −1."), _("[limit]"),
1281  "DS");
1282  register_command("debug", &console_handler::do_debug, _("Turn debug mode on."));
1283  register_command("nodebug", &console_handler::do_nodebug, _("Turn debug mode off."), "", "D");
1284  register_command(
1285  "lua", &console_handler::do_lua, _("Execute a Lua statement."), _("<command>[;<command>...]"), "DS");
1286  register_command(
1287  "unsafe_lua", &console_handler::do_unsafe_lua, _("Grant higher privileges to Lua scripts."), "", "D");
1288  register_command("custom", &console_handler::do_custom, _("Set the command used by the custom command hotkey"),
1289  _("<command>[;<command>...]"));
1290  register_command("give_control", &console_handler::do_control_dialog,
1291  _("Invoke a dialog allowing changing control of MP sides."), "", "N");
1292  register_command("inspect", &console_handler::do_inspect, _("Launch the gamestate inspector"), "", "D");
1293  register_command(
1294  "alias", &console_handler::do_set_alias, _("Set or show alias to a command"), _("<name>[=<command>]"));
1295  register_command(
1296  "set_var", &console_handler::do_set_var, _("Set a scenario variable."), _("<var>=<value>"), "DS");
1297  register_command("show_var", &console_handler::do_show_var, _("Show a scenario variable."), _("<var>"), "D");
1298  register_command("unit", &console_handler::do_unit,
1299  // TRANSLATORS: Do not translate the word "advances"; it is a hardcoded literal argument.
1300  _("Modify a unit variable. (Only top level keys are supported, and advances=<number>.)"),
1301  _("<var>=<value>"), "DS");
1302 
1303  // register_command("buff", &console_handler::do_buff,
1304  // _("Add a trait to a unit."), "", "D");
1305  // register_command("unbuff", &console_handler::do_unbuff,
1306  // _("Remove a trait from a unit. (Does not work yet.)"), "", "D");
1307  register_command("discover", &console_handler::do_discover, _("Discover all units in help."), "");
1308  register_command("undiscover", &console_handler::do_undiscover, _("'Undiscover' all units in help."), "");
1309  register_command("create", &console_handler::do_create, _("Create a unit."), _("<unit type id>"), "DS");
1310  register_command("fog", &console_handler::do_fog, _("Toggle fog for the current player."), "", "DS");
1311  register_command("shroud", &console_handler::do_shroud, _("Toggle shroud for the current player."), "", "DS");
1312  register_command("gold", &console_handler::do_gold, _("Give gold to the current player."), _("<amount>"), "DS");
1313  register_command("throw", &console_handler::do_event, _("Fire a game event."), _("<event name>"), "DS");
1314  register_alias("throw", "fire");
1315  register_command("show_coordinates", &console_handler::do_toggle_draw_coordinates,
1316  _("Toggle overlaying of x,y coordinates on hexes."));
1317  register_alias("show_coordinates", "sc");
1318  register_command("show_terrain_codes", &console_handler::do_toggle_draw_terrain_codes,
1319  _("Toggle overlaying of terrain codes on hexes."));
1320  register_alias("show_terrain_codes", "tc");
1321  register_command("show_num_of_bitmaps", &console_handler::do_toggle_draw_num_of_bitmaps,
1322  _("Toggle overlaying of number of bitmaps on hexes."));
1323  register_alias("show_num_of_bitmaps", "bn");
1324  register_command("whiteboard", &console_handler::do_toggle_whiteboard, _("Toggle planning mode."));
1325  register_alias("whiteboard", "wb");
1326  register_command(
1327  "whiteboard_options", &console_handler::do_whiteboard_options, _("Access whiteboard options dialog."));
1328  register_alias("whiteboard_options", "wbo");
1329 
1330  if(const config& alias_list = preferences::get_alias()) {
1331  for(const config::attribute& a : alias_list.attribute_range()) {
1332  register_alias(a.second, a.first);
1333  }
1334  }
1335  }
1336 
1337 private:
1339  const unsigned int team_num_;
1340 };
1341 
1342 void menu_handler::send_chat_message(const std::string& message, bool allies_only)
1343 {
1344  config cfg;
1345  cfg["id"] = preferences::login();
1346  cfg["message"] = message;
1347  const std::time_t time = ::std::time(nullptr);
1348  std::stringstream ss;
1349  ss << time;
1350  cfg["time"] = ss.str();
1351 
1352  const int side = board().is_observer() ? 0 : gui_->viewing_side();
1353  if(!board().is_observer()) {
1354  cfg["side"] = side;
1355  }
1356 
1357  bool private_message = has_friends() && allies_only;
1358 
1359  if(private_message) {
1360  if(board().is_observer()) {
1361  cfg["to_sides"] = game_config::observer_team_name;
1362  } else {
1363  cfg["to_sides"] = teams()[gui_->viewing_team()].allied_human_teams();
1364  }
1365  }
1366 
1367  resources::recorder->speak(cfg);
1368 
1369  add_chat_message(time, cfg["id"], side, message,
1371 }
1372 
1373 void menu_handler::do_search(const std::string& new_search)
1374 {
1375  if(new_search.empty() == false && new_search != last_search_)
1376  last_search_ = new_search;
1377 
1378  if(last_search_.empty())
1379  return;
1380 
1381  bool found = false;
1383  // If this is a location search, just center on that location.
1384  std::vector<std::string> args = utils::split(last_search_, ',');
1385  if(args.size() == 2) {
1386  int x, y;
1387  x = lexical_cast_default<int>(args[0], 0) - 1;
1388  y = lexical_cast_default<int>(args[1], 0) - 1;
1389  if(x >= 0 && x < map().w() && y >= 0 && y < map().h()) {
1390  loc = map_location(x, y);
1391  found = true;
1392  }
1393  }
1394  // Start scanning the game map
1395  if(loc.valid() == false) {
1396  loc = map_location(map().w() - 1, map().h() - 1);
1397  }
1398 
1399  map_location start = loc;
1400  while(!found) {
1401  // Move to the next location
1402  loc.x = (loc.x + 1) % map().w();
1403  if(loc.x == 0)
1404  loc.y = (loc.y + 1) % map().h();
1405 
1406  // Search label
1407  if(!gui_->shrouded(loc)) {
1408  const terrain_label* label = gui_->labels().get_label(loc);
1409  if(label) {
1410  std::string label_text = label->text().str();
1411  if(std::search(label_text.begin(), label_text.end(), last_search_.begin(), last_search_.end(),
1413  != label_text.end()) {
1414  found = true;
1415  }
1416  }
1417  }
1418  // Search unit name
1419  if(!gui_->fogged(loc)) {
1420  unit_map::const_iterator ui = units().find(loc);
1421  if(ui != units().end()) {
1422  const std::string name = ui->name();
1423  if(std::search(
1424  name.begin(), name.end(), last_search_.begin(), last_search_.end(), chars_equal_insensitive)
1425  != name.end()) {
1426  if(!teams()[gui_->viewing_team()].is_enemy(ui->side())
1427  || !ui->invisible(ui->get_location())) {
1428  found = true;
1429  }
1430  }
1431  }
1432  }
1433 
1434  if(loc == start)
1435  break;
1436  }
1437 
1438  if(found) {
1439  last_search_hit_ = loc;
1441  gui_->highlight_hex(loc);
1442  } else {
1444  // Not found, inform the player
1445  utils::string_map symbols;
1446  symbols["search"] = last_search_;
1447  const std::string msg = VGETTEXT("Could not find label or unit "
1448  "containing the string ‘$search’.",
1449  symbols);
1451  }
1452 }
1453 
1455 {
1456  console_handler ch(*this);
1457  ch.dispatch(str);
1458 }
1459 
1460 std::vector<std::string> menu_handler::get_commands_list()
1461 {
1462  console_handler ch(*this);
1463  // HACK: we need to call dispatch() at least once to get the
1464  // command list populated *at all*. Terrible design.
1465  // An empty command is silently ignored and has negligible
1466  // overhead, so we use that for this purpose here.
1467  ch.dispatch("");
1468  return ch.get_commands_list();
1469 }
1470 
1472 {
1475 
1476  menu_handler_.gui_->create_buttons();
1477  menu_handler_.gui_->redraw_everything();
1478 }
1479 
1481 {
1482  // :droid [<side> [on/off/full]]
1483  const std::string side_s = get_arg(1);
1484  std::string action = get_arg(2);
1485  std::transform(action.begin(), action.end(), action.begin(), tolower);
1486  // default to the current side if empty
1487  const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1488 
1489  utils::string_map symbols;
1490  symbols["side"] = std::to_string(side);
1491 
1492  if(side < 1 || side > menu_handler_.teams().size()) {
1493  command_failed(VGETTEXT("Can't droid invalid side: '$side'.", symbols));
1494  return;
1495  } else if(menu_handler_.board().get_team(side).is_network()) {
1496  command_failed(VGETTEXT("Can't droid networked side: '$side'.", symbols));
1497  return;
1498  } else if(menu_handler_.board().get_team(side).is_local()) {
1499  bool changed = false;
1500 
1501  if(action == "on") {
1502  if(!menu_handler_.board().get_team(side).is_human() || !menu_handler_.board().get_team(side).is_droid()) {
1503  menu_handler_.board().get_team(side).make_human();
1504  menu_handler_.board().get_team(side).make_droid();
1505  changed = true;
1506  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
1507  } else {
1508  print(get_cmd(), VGETTEXT("Side '$side' is already droided.", symbols));
1509  }
1510  } else if(action == "off") {
1511  if(!menu_handler_.board().get_team(side).is_human() || !menu_handler_.board().get_team(side).is_proxy_human()) {
1512  menu_handler_.board().get_team(side).make_human();
1513  menu_handler_.board().get_team(side).make_proxy_human();
1514  changed = true;
1515  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
1516  } else {
1517  print(get_cmd(), VGETTEXT("Side '$side' is already not droided.", symbols));
1518  }
1519  } else if(action == "full") {
1520  if(!menu_handler_.board().get_team(side).is_ai() || !menu_handler_.board().get_team(side).is_droid()) {
1521  menu_handler_.board().get_team(side).make_ai();
1522  menu_handler_.board().get_team(side).make_droid();
1523  changed = true;
1524  print(get_cmd(), VGETTEXT("Side '$side' controller is now fully controlled by: AI.", symbols));
1525  } else {
1526  print(get_cmd(), VGETTEXT("Side '$side' is already fully AI controlled.", symbols));
1527  }
1528  } else if(action == "") {
1529  if(menu_handler_.board().get_team(side).is_ai() || menu_handler_.board().get_team(side).is_droid()) {
1530  menu_handler_.board().get_team(side).make_human();
1531  menu_handler_.board().get_team(side).make_proxy_human();
1532  changed = true;
1533  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
1534  } else {
1535  menu_handler_.board().get_team(side).make_human();
1536  menu_handler_.board().get_team(side).make_droid();
1537  changed = true;
1538  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
1539  }
1540  } else {
1541  print(get_cmd(), VGETTEXT("Invalid action provided for side '$side'. Valid actions are: on, off, full.", symbols));
1542  }
1543 
1544  if(team_num_ == side && changed) {
1545  if(playsingle_controller* psc = dynamic_cast<playsingle_controller*>(&menu_handler_.pc_)) {
1546  psc->set_player_type_changed();
1547  }
1548  }
1549  }
1550  menu_handler_.textbox_info_.close(*menu_handler_.gui_);
1551 }
1552 
1554 {
1555  // :terrain [<terrain type> [both|base|overlay]]
1556  const std::string terrain_type = get_arg(1);
1557  const std::string mode_str = get_arg(2);
1558 
1559  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1560  const map_location& loc = mousehandler.get_last_hex();
1561 
1562  synced_context::run_and_throw("debug_terrain",
1563  config {
1564  "x", loc.wml_x(),
1565  "y", loc.wml_y(),
1566  "terrain_type", terrain_type,
1567  "mode_str", mode_str,
1568  }
1569  );
1570 }
1571 
1573 {
1574  // :idle [<side> [on/off]]
1575  const std::string side_s = get_arg(1);
1576  const std::string action = get_arg(2);
1577  // default to the current side if empty
1578  const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1579 
1580  if(side < 1 || side > menu_handler_.teams().size()) {
1581  utils::string_map symbols;
1582  symbols["side"] = side_s;
1583  command_failed(VGETTEXT("Can't idle invalid side: '$side'.", symbols));
1584  return;
1585  } else if(menu_handler_.board().get_team(side).is_network()) {
1586  utils::string_map symbols;
1587  symbols["side"] = std::to_string(side);
1588  command_failed(VGETTEXT("Can't idle networked side: '$side'.", symbols));
1589  return;
1590  } else if(menu_handler_.board().get_team(side).is_local_ai()) {
1591  utils::string_map symbols;
1592  symbols["side"] = std::to_string(side);
1593  command_failed(VGETTEXT("Can't idle local ai side: '$side'.", symbols));
1594  return;
1595  } else if(menu_handler_.board().get_team(side).is_local_human()) {
1596  if(menu_handler_.board().get_team(side).is_idle() ? action == " on" : action == " off") {
1597  return;
1598  }
1599  // toggle the proxy controller between idle / non idle
1600  menu_handler_.board().get_team(side).toggle_idle();
1601  if(team_num_ == side) {
1602  if(playsingle_controller* psc = dynamic_cast<playsingle_controller*>(&menu_handler_.pc_)) {
1603  psc->set_player_type_changed();
1604  }
1605  }
1606  }
1607  menu_handler_.textbox_info_.close(*menu_handler_.gui_);
1608 }
1609 
1611 {
1613 }
1614 
1616 {
1618  : save_id_(save_id)
1619  {
1620  }
1621 
1622  bool operator()(const team& t) const
1623  {
1624  return t.save_id() == save_id_;
1625  }
1626 
1628 };
1629 
1631 {
1632  // :control <side> <nick>
1633  if(!menu_handler_.pc_.is_networked_mp()) {
1634  return;
1635  }
1636 
1637  const std::string side = get_arg(1);
1638  const std::string player = get_arg(2);
1639  if(player.empty()) {
1640  command_failed_need_arg(2);
1641  return;
1642  }
1643 
1644  unsigned int side_num;
1645  try {
1646  side_num = lexical_cast<unsigned int>(side);
1647  } catch(const bad_lexical_cast&) {
1648  const auto it_t = std::find_if(
1650  if(it_t == resources::gameboard->teams().end()) {
1651  utils::string_map symbols;
1652  symbols["side"] = side;
1653  command_failed(VGETTEXT("Can't change control of invalid side: '$side'.", symbols));
1654  return;
1655  } else {
1656  side_num = it_t->side();
1657  }
1658  }
1659 
1660  if(side_num < 1 || side_num > menu_handler_.teams().size()) {
1661  utils::string_map symbols;
1662  symbols["side"] = side;
1663  command_failed(VGETTEXT("Can't change control of out-of-bounds side: '$side'.", symbols));
1664  return;
1665  }
1666 
1667  menu_handler_.request_control_change(side_num, player);
1668  menu_handler_.textbox_info_.close(*(menu_handler_.gui_));
1669 }
1670 
1672 {
1673  const std::string side = get_arg(1);
1674  unsigned int side_num;
1675  try {
1676  side_num = lexical_cast<unsigned int>(side);
1677  } catch(const bad_lexical_cast&) {
1678  utils::string_map symbols;
1679  symbols["side"] = side;
1680  command_failed(VGETTEXT("Can't query control of invalid side: '$side'.", symbols));
1681  return;
1682  }
1683 
1684  if(side_num < 1 || side_num > menu_handler_.teams().size()) {
1685  utils::string_map symbols;
1686  symbols["side"] = side;
1687  command_failed(VGETTEXT("Can't query control of out-of-bounds side: '$side'.", symbols));
1688  return;
1689  }
1690 
1691  std::string report = menu_handler_.board().get_team(side_num).controller().to_string();
1692  if(!menu_handler_.board().get_team(side_num).is_proxy_human()) {
1693  report += " (" + menu_handler_.board().get_team(side_num).proxy_controller().to_string() + ")";
1694  }
1695 
1696  if(menu_handler_.board().get_team(side_num).is_network()) {
1697  report += " (networked)";
1698  }
1699 
1700  print(get_cmd(), report);
1701 }
1702 
1704 {
1705  menu_handler_.gui_->get_chat_manager().clear_chat_messages();
1706 }
1707 
1709 {
1710  menu_handler_.gui_->toggle_debug_foreground();
1711 }
1712 
1714 {
1715  display& disp = *(menu_handler_.gui_);
1716 
1717  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1718  const map_location& loc = mousehandler.get_last_hex();
1719 
1720  //
1721  // It's possible to invoke this dialog even if loc isn't a valid hex. I'm not sure
1722  // exactly how that happens, but it does seem to occur when moving the mouse outside
1723  // the window to the menu bar. Not sure if there's a bug when the set-last-hex code
1724  // in that case, but this check at least ensures the dialog is only ever shown for a
1725  // valid, on-map location. Otherwise, an assertion gets thrown.
1726  //
1727  // -- vultraz, 2017-09-21
1728  //
1729  if(disp.get_map().on_board_with_border(loc)) {
1730  gui2::dialogs::terrain_layers::display(disp, loc);
1731  }
1732 }
1733 
1735 {
1737 }
1738 
1740 {
1741  menu_handler_.gui_->toggle_benchmark();
1742 }
1743 
1745 {
1746  menu_handler_.pc_.do_consolesave(get_data());
1747 }
1748 
1750 {
1751  do_save();
1752  do_quit();
1753 }
1754 
1756 {
1758 }
1759 
1761 {
1762  game_config::ignore_replay_errors = (get_data() != "off") ? true : false;
1763 }
1764 
1766 {
1767  game_config::disable_autosave = (get_data() != "off") ? true : false;
1768 }
1769 
1771 {
1772  synced_context::run_and_throw("debug_next_level", config {"next_level", get_data()});
1773 }
1774 
1776 {
1777  std::string tag = menu_handler_.pc_.get_classification().get_tagname();
1778  std::vector<std::string> options;
1779  std::string next;
1780  if(tag != "multiplayer") {
1781  for(const config& sc : menu_handler_.game_config_.child_range(tag)) {
1782  const std::string& id = sc["id"];
1783  options.push_back(id);
1784  if(id == menu_handler_.gamedata().next_scenario()) {
1785  next = id;
1786  }
1787  }
1788  } else {
1789  // find scenarios of multiplayer campaigns
1790  // (assumes that scenarios are ordered properly in the game_config)
1791  std::string scenario_id = menu_handler_.pc_.get_mp_settings().mp_scenario;
1792  if(const config& this_scenario = menu_handler_.game_config_.find_child(tag, "id", scenario_id)) {
1793  std::string addon_id = this_scenario["addon_id"].str();
1794  for(const config& sc : menu_handler_.game_config_.child_range(tag)) {
1795  if(sc["addon_id"] == addon_id) {
1796  std::string id = sc["id"];
1797  options.push_back(id);
1798  if(id == menu_handler_.gamedata().next_scenario()) {
1799  next = id;
1800  }
1801  }
1802  }
1803  }
1804  }
1805  std::sort(options.begin(), options.end());
1806  int choice = std::distance(options.begin(), std::lower_bound(options.begin(), options.end(), next));
1807  {
1808  gui2::dialogs::simple_item_selector dlg(_("Choose Scenario (Debug!)"), "", options);
1809  dlg.set_selected_index(choice);
1810  dlg.show();
1811  choice = dlg.selected_index();
1812  }
1813 
1814  if(choice == -1) {
1815  return;
1816  }
1817 
1818  if(std::size_t(choice) < options.size()) {
1819  synced_context::run_and_throw("debug_next_level", config {"next_level", options[choice]});
1820  }
1821 }
1822 
1824 {
1825  tod_manager& tod_man = menu_handler_.gamestate().tod_manager_;
1826 
1827  int turn = tod_man.turn() + 1;
1828  const std::string& data = get_data();
1829  if(!data.empty()) {
1830  turn = lexical_cast_default<int>(data, 1);
1831  }
1832  synced_context::run_and_throw("debug_turn", config {"turn", turn});
1833 }
1834 
1836 {
1837  int limit = get_data().empty() ? -1 : lexical_cast_default<int>(get_data(), 1);
1838  synced_context::run_and_throw("debug_turn_limit", config {"turn_limit", limit});
1839 }
1840 
1842 {
1843  if(!menu_handler_.pc_.is_networked_mp() || game_config::mp_debug) {
1844  print(get_cmd(), _("Debug mode activated!"));
1845  game_config::set_debug(true);
1846  } else {
1847  command_failed(_("Debug mode not available in network games"));
1848  }
1849 }
1850 
1852 {
1853  if(game_config::debug) {
1854  print(get_cmd(), _("Debug mode deactivated!"));
1855  game_config::set_debug(false);
1856  }
1857 }
1858 
1860 {
1861  if(!menu_handler_.gamestate().lua_kernel_) {
1862  return;
1863  }
1864 
1865  synced_context::run_and_throw("debug_lua", config {"code", get_data()});
1866 }
1867 
1869 {
1870  if(!menu_handler_.gamestate().lua_kernel_) {
1871  return;
1872  }
1873 
1874  const int retval = gui2::show_message(_("WARNING! Unsafe Lua Mode"),
1875  _("Executing Lua code in in this manner opens your computer to potential security breaches from any "
1876  "malicious add-ons or other programs you may have installed.\n\n"
1877  "Do not continue unless you really know what you are doing."), gui2::dialogs::message::ok_cancel_buttons);
1878 
1879  if(retval == gui2::retval::OK) {
1880  print(get_cmd(), _("Unsafe mode enabled!"));
1881  menu_handler_.gamestate().lua_kernel_->load_package();
1882  }
1883 }
1884 
1886 {
1887  preferences::set_custom_command(get_data());
1888 }
1889 
1891 {
1892  const std::string data = get_data();
1893  const std::string::const_iterator j = std::find(data.begin(), data.end(), '=');
1894  const std::string alias(data.begin(), j);
1895  if(j != data.end()) {
1896  const std::string command(j + 1, data.end());
1897  if(!command.empty()) {
1898  register_alias(command, alias);
1899  } else {
1900  // "alias something=" deactivate this alias. We just set it
1901  // equal to itself here. Later preferences will filter empty alias.
1902  register_alias(alias, alias);
1903  }
1904  preferences::add_alias(alias, command);
1905  // directly save it for the moment, but will slow commands sequence
1907  } else {
1908  // "alias something" display its value
1909  // if no alias, will be "'something' = 'something'"
1910  const std::string command = chmap::get_actual_cmd(alias);
1911  print(get_cmd(), "'" + alias + "'" + " = " + "'" + command + "'");
1912  }
1913 }
1914 
1916 {
1917  const std::string data = get_data();
1918  if(data.empty()) {
1919  command_failed_need_arg(1);
1920  return;
1921  }
1922 
1923  const std::string::const_iterator j = std::find(data.begin(), data.end(), '=');
1924  if(j != data.end()) {
1925  const std::string name(data.begin(), j);
1926  const std::string value(j + 1, data.end());
1927  synced_context::run_and_throw("debug_set_var", config {"name", name, "value", value});
1928  } else {
1929  command_failed(_("Variable not found"));
1930  }
1931 }
1932 
1934 {
1935  gui2::show_transient_message("", menu_handler_.gamedata().get_variable_const(get_data()));
1936 }
1937 
1939 {
1941  gui2::dialogs::gamestate_inspector::display(
1943 }
1944 
1946 {
1947  gui2::dialogs::mp_change_control::display(menu_handler_);
1948 }
1949 
1951 {
1952  // prevent SIGSEGV due to attempt to set HP during a fight
1953  if(events::commands_disabled > 0) {
1954  return;
1955  }
1956 
1957  unit_map::iterator i = menu_handler_.current_unit();
1958  if(i == menu_handler_.units().end()) {
1959  return;
1960  }
1961 
1962  const map_location loc = i->get_location();
1963  const std::string data = get_data(1);
1964  std::vector<std::string> parameters = utils::split(data, '=', utils::STRIP_SPACES);
1965  if(parameters.size() < 2) {
1966  return;
1967  }
1968 
1969  if(parameters[0] == "alignment") {
1970  unit_type::ALIGNMENT alignment;
1971  if(!alignment.parse(parameters[1])) {
1972  utils::string_map symbols;
1973  symbols["alignment"] = get_arg(1);
1974  command_failed(VGETTEXT(
1975  "Invalid alignment: '$alignment', needs to be one of lawful, neutral, chaotic, or liminal.",
1976  symbols));
1977  return;
1978  }
1979  }
1980 
1981  synced_context::run_and_throw("debug_unit",
1982  config {
1983  "x", loc.wml_x(),
1984  "y", loc.wml_y(),
1985  "name", parameters[0],
1986  "value", parameters[1],
1987  }
1988  );
1989 }
1990 
1992 {
1993  for(const unit_type_data::unit_type_map::value_type& i : unit_types.types()) {
1994  preferences::encountered_units().insert(i.second.id());
1995  }
1996 }
1997 
1999 {
2000  const int res = gui2::show_message("Undiscover",
2001  _("Do you wish to clear all of your discovered units from help?"), gui2::dialogs::message::yes_no_buttons);
2002  if(res != gui2::retval::CANCEL) {
2004  }
2005 }
2006 
2007 /** Implements the (debug mode) console command that creates a unit. */
2009 {
2010  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
2011  const map_location& loc = mousehandler.get_last_hex();
2012  if(menu_handler_.map().on_board(loc)) {
2013  const unit_type* ut = unit_types.find(get_data());
2014  if(!ut) {
2015  command_failed(_("Invalid unit type"));
2016  return;
2017  }
2018 
2019  // Create the unit.
2020  create_and_place(*menu_handler_.gui_, menu_handler_.map(), menu_handler_.units(), loc, *ut);
2021  } else {
2022  command_failed(_("Invalid location"));
2023  }
2024 }
2025 
2027 {
2028  synced_context::run_and_throw("debug_fog", config());
2029 }
2030 
2032 {
2033  synced_context::run_and_throw("debug_shroud", config());
2034 }
2035 
2037 {
2038  synced_context::run_and_throw("debug_gold", config {"gold", lexical_cast_default<int>(get_data(), 1000)});
2039 }
2040 
2042 {
2043  synced_context::run_and_throw("debug_event", config {"eventname", get_data()});
2044 }
2045 
2047 {
2048  menu_handler_.gui_->set_draw_coordinates(!menu_handler_.gui_->get_draw_coordinates());
2049  menu_handler_.gui_->invalidate_all();
2050 }
2052 {
2053  menu_handler_.gui_->set_draw_terrain_codes(!menu_handler_.gui_->get_draw_terrain_codes());
2054  menu_handler_.gui_->invalidate_all();
2055 }
2056 
2058 {
2059  menu_handler_.gui_->set_draw_num_of_bitmaps(!menu_handler_.gui_->get_draw_num_of_bitmaps());
2060  menu_handler_.gui_->invalidate_all();
2061 }
2062 
2064 {
2065  if(const std::shared_ptr<wb::manager>& whiteb = menu_handler_.pc_.get_whiteboard()) {
2066  whiteb->set_active(!whiteb->is_active());
2067  if(whiteb->is_active()) {
2068  print(get_cmd(), _("Planning mode activated!"));
2069  whiteb->print_help_once();
2070  } else {
2071  print(get_cmd(), _("Planning mode deactivated!"));
2072  }
2073  }
2074 }
2075 
2077 {
2078  if(menu_handler_.pc_.get_whiteboard()) {
2079  menu_handler_.pc_.get_whiteboard()->options_dlg();
2080  }
2081 }
2082 
2083 void menu_handler::do_ai_formula(const std::string& str, int side_num, mouse_handler& /*mousehandler*/)
2084 {
2085  try {
2086  add_chat_message(std::time(nullptr), "wfl", 0, ai::manager::get_singleton().evaluate_command(side_num, str));
2087  } catch(const wfl::formula_error&) {
2088  } catch(...) {
2089  add_chat_message(std::time(nullptr), "wfl", 0, "UNKNOWN ERROR IN FORMULA");
2090  }
2091 }
2092 
2094 {
2095  textbox_info_.show(gui::TEXTBOX_COMMAND, translation::sgettext("prompt^Command:"), "", false, *gui_);
2096 }
2097 
2098 void menu_handler::request_control_change(int side_num, const std::string& player)
2099 {
2100  std::string side = std::to_string(side_num);
2101  if(board().get_team(side_num).is_local_human() && player == preferences::login()) {
2102  // this is already our side.
2103  return;
2104  } else {
2105  // The server will (or won't because we aren't allowed to change the controller)
2106  // send us a [change_controller] back, which we then handle in playturn.cpp
2107  pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", player}});
2108  }
2109 }
2110 
2112 {
2113  for(const std::string& command : utils::split(preferences::custom_command(), ';')) {
2114  do_command(command);
2115  }
2116 }
2117 
2119 {
2120  if(!pc_.is_networked_mp()) {
2121  textbox_info_.show(gui::TEXTBOX_AI, translation::sgettext("prompt^Command:"), "", false, *gui_);
2122  }
2123 }
2124 
2126 {
2127  gui_->get_chat_manager().clear_chat_messages(); // also clear debug-messages and WML-error-messages
2128 }
2129 
2131 {
2132  pc_.send_to_wesnothd(cfg);
2133 }
2134 
2135 } // 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)
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number 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
std::string format_conjunct_list(const t_string &empty, const std::vector< t_string > &elems)
Format a conjunctive list.
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:1159
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:291
bool message_private()
Definition: game.cpp:874
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:180
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
REMOVE_EMPTY: remove empty elements.
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:1378
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...
std::string str
Definition: statement.cpp:110
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)
bool yellow_confirm()
Definition: game.cpp:988
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.
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:29
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:993
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:592
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:100
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:965
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()
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:378
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
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:401
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:1374
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1942
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:879
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:133
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:793
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:1298
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_
u64 size
Definition: statement.cpp:80
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:953
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:1380
#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:979
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.
std::vector< std::string > split(const config_attribute_value &val)
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:1343
static color_t get_side_color(int side)
Definition: team.cpp:949
static UNUSEDNOWARN std::string sgettext(const char *str)
Definition: gettext.hpp:69
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:341
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:961
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:1268
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
auto * ss
Definition: result_set.cpp:281
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