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