The Battle for Wesnoth  1.15.2+dev
menu_events.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
3  wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Operations activated from menus/hotkeys while playing a game.
19  * E.g. Unitlist, status_table, save_game, save_map, chat, show_help, etc.
20  */
21 
22 #include "menu_events.hpp"
23 
24 #include "actions/attack.hpp"
25 #include "actions/create.hpp"
26 #include "actions/move.hpp"
27 #include "actions/undo.hpp"
28 #include "actions/vision.hpp"
29 #include "ai/manager.hpp"
30 #include "chat_command_handler.hpp"
31 #include "color.hpp"
32 #include "display_chat_manager.hpp"
33 #include "font/standard_colors.hpp"
34 #include "formula/string_utils.hpp"
35 #include "game_board.hpp"
36 #include "game_config_manager.hpp"
37 #include "game_end_exceptions.hpp"
38 #include "game_events/pump.hpp"
39 #include "preferences/game.hpp"
40 #include "game_state.hpp"
41 #include "gettext.hpp"
42 #include "gui/dialogs/chat_log.hpp"
49 #include "gui/dialogs/message.hpp"
60 #include "gui/widgets/settings.hpp"
61 #include "gui/widgets/retval.hpp"
62 #include "help/help.hpp"
63 #include "log.hpp"
64 #include "map/label.hpp"
65 #include "map/map.hpp"
66 #include "map_command_handler.hpp"
67 #include "mouse_events.hpp"
68 #include "play_controller.hpp"
71 #include "preferences/display.hpp"
72 #include "replay.hpp"
73 #include "replay_helper.hpp"
74 #include "resources.hpp"
75 #include "save_index.hpp"
76 #include "savegame.hpp"
79 #include "synced_context.hpp"
80 #include "terrain/builder.hpp"
81 #include "units/udisplay.hpp"
82 #include "units/unit.hpp"
83 #include "whiteboard/manager.hpp"
84 #include "sound.hpp"
85 
86 static lg::log_domain log_engine("engine");
87 #define ERR_NG LOG_STREAM(err, log_engine)
88 #define LOG_NG LOG_STREAM(info, log_engine)
89 
90 namespace events
91 {
93  : gui_(gui)
94  , pc_(pc)
95  , game_config_(game_config_manager::get()->game_config())
96  , textbox_info_()
97  , last_search_()
98  , last_search_hit_()
99 {
100 }
101 
103 {
104 }
105 
107 {
108  return pc_.gamestate();
109 }
110 
112 {
113  return gamestate().gamedata_;
114 }
115 
117 {
118  return gamestate().board_;
119 }
120 
122 {
123  return gamestate().board_.units_;
124 }
125 
126 std::vector<team>& menu_handler::teams() const
127 {
128  return gamestate().board_.teams_;
129 }
130 
132 {
133  return gamestate().board_.map();
134 }
135 
137 {
138  return textbox_info_;
139 }
140 
142 {
143  if(!gamestate().lua_kernel_) {
144  return;
145  }
146 
149 }
150 
152 {
153  gui2::dialogs::statistics_dialog::display(board().get_team(side_num));
154 }
155 
157 {
159 }
160 
162 {
163  int selected_side;
164 
165  if(gui2::dialogs::game_stats::execute(board(), gui_->viewing_team(), selected_side)) {
166  gui_->scroll_to_leader(selected_side);
167  }
168 }
169 
171 {
172  const std::string& input_name
174 
176 
177  dlg.set_title(_("Save Map As"))
178  .set_save_mode(true)
179  .set_path(input_name)
180  .set_extension(".map");
181 
182  if(!dlg.show()) {
183  return;
184  }
185 
186  try {
187  filesystem::write_file(dlg.path(), map().write());
188  gui2::show_transient_message("", _("Map saved."));
189  } catch(const filesystem::io_exception& e) {
190  utils::string_map symbols;
191  symbols["msg"] = e.what();
192  const std::string msg = VGETTEXT("Could not save the map: $msg", symbols);
194  }
195 }
196 
198 {
200  // Needed after changing fullscreen/windowed mode or display resolution
202 }
203 
205 {
206  config c;
207  c["name"] = "prototype of chat log";
208  gui2::dialogs::chat_log::display(vconfig(c), *resources::recorder);
209  // std::string text = resources::recorder->build_chat_log();
210  // gui::show_dialog(*gui_,nullptr,_("Chat Log"),"",gui::CLOSE_ONLY,nullptr,nullptr,"",&text);
211 }
212 
214 {
215  help::show_help();
216 }
217 
219 {
221  ? board().is_observer()
222  ? _("Send to observers only")
223  : _("Send to allies only")
225 }
226 
228 {
230  speak();
231 }
232 
234 {
236  speak();
237 }
238 
240 {
241  if(board().is_observer()) {
242  return !gui_->observers().empty();
243  }
244 
245  for(std::size_t n = 0; n != teams().size(); ++n) {
246  if(n != gui_->viewing_team() && teams()[gui_->viewing_team()].team_name() == teams()[n].team_name()
247  && teams()[n].is_network()) {
248  return true;
249  }
250  }
251 
252  return false;
253 }
254 
255 void menu_handler::recruit(int side_num, const map_location& last_hex)
256 {
257  std::map<const unit_type*, t_string> sample_units;
258 
259  std::set<std::string> recruits = actions::get_recruits(side_num, last_hex);
260 
261  for(const auto& recruit : recruits) {
263  if(!type) {
264  ERR_NG << "could not find unit '" << recruit << "'" << std::endl;
265  return;
266  }
267 
268  map_location ignored;
269  map_location recruit_hex = last_hex;
270  sample_units[type] = (can_recruit(type->id(), side_num, recruit_hex, ignored));
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 : 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, 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 < teams().size() && 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, 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, 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, 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 != 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 != units().end()) {
637  }
638 }
639 
641 {
642  const map_location& loc = mousehandler.get_last_hex();
643  if(map().on_board(loc) == false || gui_->shrouded(loc)) {
644  return;
645  }
646 
647  const terrain_type& type = map().get_terrain_info(loc);
648  // const terrain_type& info = board().map().get_terrain_info(terrain);
650 }
651 
653 {
654  const unit_map::iterator un = current_unit();
655  if(un == 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(N_("Rename Unit"));
665  const std::string label(N_("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(), teams()[gui_->viewing_team()], see_all);
680  if(res != units().end()) {
681  return res;
682  }
683 
684  return board().find_visible_unit(mousehandler.get_selected_hex(), 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::pair<const unit_type*, unit_race::GENDER> type_and_gender;
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_and_gender 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_and_gender(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_and_gender(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_and_gender(utp, gender);
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 {
743  synced_context::run_and_throw("debug_create_unit",
744  config {
745  "x", loc.wml_x(),
746  "y", loc.wml_y(),
747  "type", u_type.id(),
748  "gender", gender_string(gender),
749  }
750  );
751 }
752 
753 } // Anonymous namespace
754 
755 /**
756  * Creates a unit (in debug mode via hotkey or context menu).
757  */
759 {
760  // Save the current mouse location before popping up the choice menu (which
761  // gives time for the mouse to move, changing the location).
762  const map_location destination = mousehandler.get_last_hex();
763  assert(gui_ != nullptr);
764 
765  // Let the user select the kind of unit to create.
766  type_and_gender selection = choose_unit();
767  if(selection.first != nullptr) {
768  // Make it so.
769  create_and_place(*gui_, map(), units(), destination, *selection.first, selection.second);
770  }
771 }
772 
774 {
775  const map_location& loc = mousehandler.get_last_hex();
776  const unit_map::iterator i = units().find(loc);
777  if(i == units().end()) {
778  if(!map().is_village(loc)) {
779  return;
780  }
781 
782  // village_owner returns -1 for free village, so team 0 will get it
783  int team = board().village_owner(loc) + 1;
784  // team is 0-based so team=team::nteams() is not a team
785  // but this will make get_village free it
786  if(team > static_cast<int>(teams().size())) {
787  team = 0;
788  }
789  actions::get_village(loc, team + 1);
790  } else {
791  int side = i->side();
792  ++side;
793  if(side > static_cast<int>(teams().size())) {
794  side = 1;
795  }
796  i->set_side(side);
797 
798  if(map().is_village(loc)) {
799  actions::get_village(loc, side);
800  }
801  }
802 }
803 
805 {
806  const map_location loc = mousehandler.get_last_hex();
807  synced_context::run_and_throw("debug_kill", config {"x", loc.wml_x(), "y", loc.wml_y()});
808 }
809 
810 void menu_handler::label_terrain(mouse_handler& mousehandler, bool team_only)
811 {
812  const map_location& loc = mousehandler.get_last_hex();
813  if(map().on_board(loc) == false) {
814  return;
815  }
816 
817  const terrain_label* old_label = gui_->labels().get_label(loc);
818  std::string label = old_label ? old_label->text() : "";
819 
820  if(gui2::dialogs::edit_label::execute(label, team_only)) {
821  std::string team_name;
822  color_t color = font::LABEL_COLOR;
823 
824  if(team_only) {
825  team_name = gui_->labels().team_name();
826  } else {
828  }
829  const terrain_label* res = gui_->labels().set_label(loc, label, gui_->viewing_team(), team_name, color);
830  if(res) {
832  }
833  }
834 }
835 
837 {
838  if(gui_->team_valid() && !board().is_observer()) {
839  const int res = gui2::show_message(
840  _("Clear Labels"),
841  _("Are you sure you want to clear map labels?"),
843  );
844 
845  if(res == gui2::retval::OK) {
846  gui_->labels().clear(gui_->current_team_name(), false);
848  }
849  }
850 }
851 
853 {
854  if(gui2::dialogs::label_settings::execute(board())) {
856  }
857 }
858 
859 void menu_handler::continue_move(mouse_handler& mousehandler, int side_num)
860 {
862  if(i == units().end() || !i->move_interrupted()) {
863  i = units().find(mousehandler.get_selected_hex());
864  if(i == units().end() || !i->move_interrupted()) {
865  return;
866  }
867  }
868  move_unit_to_loc(i, i->get_interrupted_move(), true, side_num, mousehandler);
869 }
870 
872  const map_location& target,
873  bool continue_move,
874  int side_num,
875  mouse_handler& mousehandler)
876 {
877  assert(ui != units().end());
878 
879  pathfind::marked_route route = mousehandler.get_route(&*ui, target, board().get_team(side_num));
880 
881  if(route.steps.empty()) {
882  return;
883  }
884 
885  assert(route.steps.front() == ui->get_location());
886 
887  gui_->set_route(&route);
889 
890  {
891  LOG_NG << "move_unit_to_loc " << route.steps.front() << " to " << route.steps.back() << "\n";
893  }
894 
895  gui_->set_route(nullptr);
897 }
898 
899 void menu_handler::execute_gotos(mouse_handler& mousehandler, int side)
900 {
901  // we will loop on all gotos and try to fully move a maximum of them,
902  // but we want to avoid multiple blocking of the same unit,
903  // so, if possible, it's better to first wait that the blocker move
904 
905  bool wait_blocker_move = true;
906  std::set<map_location> fully_moved;
907 
908  bool change = false;
909  bool blocked_unit = false;
910  do {
911  change = false;
912  blocked_unit = false;
913  for(auto& unit : units()) {
914  if(unit.side() != side || unit.movement_left() == 0) {
915  continue;
916  }
917 
918  const map_location& current_loc = unit.get_location();
919  const map_location& goto_loc = unit.get_goto();
920 
921  if(goto_loc == current_loc) {
923  continue;
924  }
925 
926  if(!map().on_board(goto_loc)) {
927  continue;
928  }
929 
930  // avoid pathfinding calls for finished units
931  if(fully_moved.count(current_loc)) {
932  continue;
933  }
934 
935  pathfind::marked_route route = mousehandler.get_route(&unit, goto_loc, board().get_team(side));
936 
937  if(route.steps.size() <= 1) { // invalid path
938  fully_moved.insert(current_loc);
939  continue;
940  }
941 
942  // look where we will stop this turn (turn_1 waypoint or goto)
943  map_location next_stop = goto_loc;
944  pathfind::marked_route::mark_map::const_iterator w = route.marks.begin();
945  for(; w != route.marks.end(); ++w) {
946  if(w->second.turns == 1) {
947  next_stop = w->first;
948  break;
949  }
950  }
951 
952  if(next_stop == current_loc) {
953  fully_moved.insert(current_loc);
954  continue;
955  }
956 
957  // we delay each blocked move because some other change
958  // may open a another not blocked path
959  if(units().count(next_stop)) {
960  blocked_unit = true;
961  if(wait_blocker_move)
962  continue;
963  }
964 
965  gui_->set_route(&route);
966 
967  {
968  LOG_NG << "execute goto from " << route.steps.front() << " to " << route.steps.back() << "\n";
969  int moves = actions::move_unit_and_record(route.steps, &pc_.get_undo_stack());
970  change = moves > 0;
971  }
972 
973  if(change) {
974  // something changed, resume waiting blocker (maybe one can move now)
975  wait_blocker_move = true;
976  }
977  }
978 
979  if(!change && wait_blocker_move) {
980  // no change when waiting, stop waiting and retry
981  wait_blocker_move = false;
982  change = true;
983  }
984  } while(change && blocked_unit);
985 
986  // erase the footsteps after movement
987  gui_->set_route(nullptr);
989 }
990 
992 {
994  gui_->invalidate_all(); // TODO can fewer tiles be invalidated?
995 }
996 
998 {
1000  gui_->invalidate_all();
1001 }
1002 
1003 void menu_handler::unit_hold_position(mouse_handler& mousehandler, int side_num)
1004 {
1005  const unit_map::iterator un = units().find(mousehandler.get_selected_hex());
1006  if(un != units().end() && un->side() == side_num && un->movement_left() >= 0) {
1007  un->toggle_hold_position();
1008  gui_->invalidate(mousehandler.get_selected_hex());
1009 
1010  mousehandler.set_current_paths(pathfind::paths());
1011 
1012  if(un->hold_position()) {
1013  mousehandler.cycle_units(false);
1014  }
1015  }
1016 }
1017 
1018 void menu_handler::end_unit_turn(mouse_handler& mousehandler, int side_num)
1019 {
1020  const unit_map::iterator un = units().find(mousehandler.get_selected_hex());
1021  if(un != units().end() && un->side() == side_num && un->movement_left() >= 0) {
1022  un->toggle_user_end_turn();
1023  gui_->invalidate(mousehandler.get_selected_hex());
1024 
1025  mousehandler.set_current_paths(pathfind::paths());
1026 
1027  if(un->user_end_turn()) {
1028  mousehandler.cycle_units(false);
1029  }
1030 
1031  // If cycle_units hasn't found a new unit to cycle to then the original unit is still selected, but
1032  // in a state where left-clicking on it does nothing. Make it respond to mouse clicks again.
1033  if(un == units().find(mousehandler.get_selected_hex())) {
1034  mousehandler.deselect_hex();
1035  }
1036  }
1037 }
1038 
1040 {
1041  std::ostringstream msg;
1042  msg << _("Search");
1043  if(last_search_hit_.valid()) {
1044  msg << " [" << last_search_ << "]";
1045  }
1046  msg << ':';
1047  textbox_info_.show(gui::TEXTBOX_SEARCH, msg.str(), "", false, *gui_);
1048 }
1049 
1051 {
1052  // None of the two parameters really needs to be passed since the information belong to members of the class.
1053  // But since it makes the called method more generic, it is done anyway.
1055  textbox_info_.box()->text(), textbox_info_.check() != nullptr ? textbox_info_.check()->checked() : false);
1056 }
1057 
1058 void menu_handler::add_chat_message(const std::time_t& time,
1059  const std::string& speaker,
1060  int side,
1061  const std::string& message,
1063 {
1064  gui_->get_chat_manager().add_chat_message(time, speaker, side, message, type, false);
1065 
1067  config {
1068  "sender", preferences::login(),
1069  "message", message,
1070  "whisper", type == events::chat_handler::MESSAGE_PRIVATE,
1071  }
1072  );
1073 }
1074 
1075 // command handler for user :commands. Also understands all chat commands
1076 // via inheritance. This complicates some things a bit.
1077 class console_handler : public map_command_handler<console_handler>, private chat_command_handler
1078 {
1079 public:
1080  // convenience typedef
1083  : chmap()
1084  , chat_command_handler(menu_handler, true)
1085  , menu_handler_(menu_handler)
1086  , team_num_(menu_handler.pc_.current_side())
1087  {
1088  }
1089 
1090  using chmap::dispatch; // disambiguate
1091  using chmap::get_commands_list;
1092  using chmap::command_failed;
1093 
1094 protected:
1095  // chat_command_handler's init_map() and handlers will end up calling these.
1096  // this makes sure the commands end up in our map
1097  virtual void register_command(const std::string& cmd,
1099  const std::string& help = "",
1100  const std::string& usage = "",
1101  const std::string& flags = "")
1102  {
1103  chmap::register_command(cmd, h, help, usage, flags + "N"); // add chat commands as network_only
1104  }
1105 
1106  virtual void register_alias(const std::string& to_cmd, const std::string& cmd)
1107  {
1108  chmap::register_alias(to_cmd, cmd);
1109  }
1110 
1111  virtual std::string get_arg(unsigned i) const
1112  {
1113  return chmap::get_arg(i);
1114  }
1115 
1116  virtual std::string get_cmd() const
1117  {
1118  return chmap::get_cmd();
1119  }
1120 
1121  virtual std::string get_data(unsigned n = 1) const
1122  {
1123  return chmap::get_data(n);
1124  }
1125 
1126  // these are needed to avoid ambiguities introduced by inheriting from console_command_handler
1127  using chmap::register_command;
1128  using chmap::register_alias;
1129  using chmap::help;
1130  using chmap::is_enabled;
1131  using chmap::command_failed_need_arg;
1132 
1133  void do_refresh();
1134  void do_droid();
1135  void do_terrain();
1136  void do_idle();
1137  void do_theme();
1138  void do_control();
1139  void do_controller();
1140  void do_clear();
1141  void do_foreground();
1142  void do_layers();
1143  void do_fps();
1144  void do_benchmark();
1145  void do_save();
1146  void do_save_quit();
1147  void do_quit();
1148  void do_ignore_replay_errors();
1149  void do_nosaves();
1150  void do_next_level();
1151  void do_choose_level();
1152  void do_turn();
1153  void do_turn_limit();
1154  void do_debug();
1155  void do_nodebug();
1156  void do_lua();
1157  void do_unsafe_lua();
1158  void do_custom();
1159  void do_set_alias();
1160  void do_set_var();
1161  void do_show_var();
1162  void do_inspect();
1163  void do_control_dialog();
1164  void do_unit();
1165  // void do_buff();
1166  // void do_unbuff();
1167  void do_discover();
1168  void do_undiscover();
1169  void do_create();
1170  void do_fog();
1171  void do_shroud();
1172  void do_gold();
1173  void do_event();
1174  void do_toggle_draw_coordinates();
1175  void do_toggle_draw_terrain_codes();
1176  void do_toggle_draw_num_of_bitmaps();
1177  void do_toggle_whiteboard();
1178  void do_whiteboard_options();
1179 
1180  std::string get_flags_description() const
1181  {
1182  return _("(D) — debug only, (N) — network only, (A) — admin only");
1183  }
1184 
1185  using chat_command_handler::get_command_flags_description; // silence a warning
1186  std::string get_command_flags_description(const chmap::command& c) const
1187  {
1188  std::string space(" ");
1189  return (c.has_flag('D') ? space + _("(debug command)") : "")
1190  + (c.has_flag('N') ? space + _("(network only)") : "")
1191  + (c.has_flag('A') ? space + _("(admin only)") : "")
1192  + (c.has_flag('S') ? space + _("(not during other events)") : "");
1193  }
1194 
1195  using map::is_enabled;
1196  bool is_enabled(const chmap::command& c) const
1197  {
1198  return !((c.has_flag('D') && !game_config::debug)
1199  || (c.has_flag('N') && !menu_handler_.pc_.is_networked_mp())
1200  || (c.has_flag('A') && !preferences::is_authenticated())
1201  || (c.has_flag('S') && (synced_context::get_synced_state() != synced_context::UNSYNCED || !menu_handler_.pc_.current_team().is_local())));
1202  }
1203 
1204  void print(const std::string& title, const std::string& message)
1205  {
1206  menu_handler_.add_chat_message(std::time(nullptr), title, 0, message);
1207  }
1208 
1209  void init_map()
1210  {
1211  chat_command_handler::init_map(); // grab chat_ /command handlers
1212 
1213  chmap::get_command("log")->flags = ""; // clear network-only flag from log
1214  chmap::get_command("version")->flags = ""; // clear network-only flag
1215  chmap::get_command("ignore")->flags = ""; // clear network-only flag
1216  chmap::get_command("friend")->flags = ""; // clear network-only flag
1217  chmap::get_command("remove")->flags = ""; // clear network-only flag
1218 
1219  chmap::set_cmd_prefix(":");
1220 
1221  register_command("refresh", &console_handler::do_refresh, _("Refresh gui."));
1222  register_command("droid", &console_handler::do_droid, _("Switch a side to/from AI control."),
1223  // TRANSLATORS: These are the arguments accepted by the "droid" command,
1224  // which must be a side-number and then optionally one of "on", "off" or "full".
1225  // As with the command's name, "on", "off" and "full" are hardcoded, and shouldn't change in the translation.
1226  _("[<side> [on/off/full]]\n“on” = enable but retain vision, “full” = as if it’s controlled by another player"));
1227  register_command("terrain", &console_handler::do_terrain, _("Change terrain type of current hex"),
1228  // TRANSLATORS: [both|base|overlay] are hardcoded literal arguments and shouldn't be translated.
1229  _("<terrain type> [both|base|overlay]"), "DS");
1230  register_command("idle", &console_handler::do_idle, _("Switch a side to/from idle state."),
1231  // TRANSLATORS: These are the arguments accepted by the "idle" command,
1232  // which must be a side-number and then optionally "on" or "off".
1233  // As with the command's name, "on" and "off" are hardcoded, and shouldn't change in the translation.
1234  _("command_idle^[<side> [on/off]]"));
1235  register_command("theme", &console_handler::do_theme, _("Change the in-game theme."));
1236  register_command("control", &console_handler::do_control,
1237  _("Assign control of a side to a different player or observer."), _("<side> <nickname>"), "N");
1238  register_command("controller", &console_handler::do_controller, _("Query the controller status of a side."),
1239  _("<side>"));
1240  register_command("clear", &console_handler::do_clear, _("Clear chat history."));
1241  register_command("foreground", &console_handler::do_foreground, _("Debug foreground terrain."), "", "D");
1242  register_command(
1243  "layers", &console_handler::do_layers, _("Debug layers from terrain under the mouse."), "", "D");
1244  register_command("fps", &console_handler::do_fps, _("Show fps (Frames Per Second)."));
1245  register_command("benchmark", &console_handler::do_benchmark);
1246  register_command("save", &console_handler::do_save, _("Save game."));
1247  register_alias("save", "w");
1248  register_command("quit", &console_handler::do_quit, _("Quit game."));
1249  // Note the next value is used hardcoded in the init tests.
1250  register_alias("quit", "q!");
1251  register_command("save_quit", &console_handler::do_save_quit, _("Save and quit."));
1252  register_alias("save_quit", "wq");
1253  register_command("ignore_replay_errors", &console_handler::do_ignore_replay_errors, _("Ignore replay errors."));
1254  register_command("nosaves", &console_handler::do_nosaves, _("Disable autosaves."));
1255  register_command("next_level", &console_handler::do_next_level,
1256  _("Advance to the next scenario, or scenario identified by 'id'"), _("<id>"), "DS");
1257  register_alias("next_level", "n");
1258  register_command("choose_level", &console_handler::do_choose_level, _("Choose next scenario"), "", "DS");
1259  register_alias("choose_level", "cl");
1260  register_command("turn", &console_handler::do_turn,
1261  _("Change turn number (and time of day), or increase by one if no number is specified."), _("[turn]"),
1262  "DS");
1263  register_command("turn_limit", &console_handler::do_turn_limit,
1264  _("Change turn limit, or turn the turn limit off if no number is specified or it’s −1."), _("[limit]"),
1265  "DS");
1266  register_command("debug", &console_handler::do_debug, _("Turn debug mode on."));
1267  register_command("nodebug", &console_handler::do_nodebug, _("Turn debug mode off."), "", "D");
1268  register_command(
1269  "lua", &console_handler::do_lua, _("Execute a Lua statement."), _("<command>[;<command>...]"), "DS");
1270  register_command(
1271  "unsafe_lua", &console_handler::do_unsafe_lua, _("Grant higher privileges to Lua scripts."), "", "D");
1272  register_command("custom", &console_handler::do_custom, _("Set the command used by the custom command hotkey"),
1273  _("<command>[;<command>...]"));
1274  register_command("give_control", &console_handler::do_control_dialog,
1275  _("Invoke a dialog allowing changing control of MP sides."), "", "N");
1276  register_command("inspect", &console_handler::do_inspect, _("Launch the gamestate inspector"), "", "D");
1277  register_command(
1278  "alias", &console_handler::do_set_alias, _("Set or show alias to a command"), _("<name>[=<command>]"));
1279  register_command(
1280  "set_var", &console_handler::do_set_var, _("Set a scenario variable."), _("<var>=<value>"), "DS");
1281  register_command("show_var", &console_handler::do_show_var, _("Show a scenario variable."), _("<var>"), "D");
1282  register_command("unit", &console_handler::do_unit,
1283  // TRANSLATORS: Do not translate the word "advances"; it is a hardcoded literal argument.
1284  _("Modify a unit variable. (Only top level keys are supported, and advances=<number>.)"),
1285  _("<var>=<value>"), "DS");
1286 
1287  // register_command("buff", &console_handler::do_buff,
1288  // _("Add a trait to a unit."), "", "D");
1289  // register_command("unbuff", &console_handler::do_unbuff,
1290  // _("Remove a trait from a unit. (Does not work yet.)"), "", "D");
1291  register_command("discover", &console_handler::do_discover, _("Discover all units in help."), "");
1292  register_command("undiscover", &console_handler::do_undiscover, _("'Undiscover' all units in help."), "");
1293  register_command("create", &console_handler::do_create, _("Create a unit."), _("<unit type id>"), "DS");
1294  register_command("fog", &console_handler::do_fog, _("Toggle fog for the current player."), "", "DS");
1295  register_command("shroud", &console_handler::do_shroud, _("Toggle shroud for the current player."), "", "DS");
1296  register_command("gold", &console_handler::do_gold, _("Give gold to the current player."), _("<amount>"), "DS");
1297  register_command("throw", &console_handler::do_event, _("Fire a game event."), _("<event name>"), "DS");
1298  register_alias("throw", "fire");
1299  register_command("show_coordinates", &console_handler::do_toggle_draw_coordinates,
1300  _("Toggle overlaying of x,y coordinates on hexes."));
1301  register_alias("show_coordinates", "sc");
1302  register_command("show_terrain_codes", &console_handler::do_toggle_draw_terrain_codes,
1303  _("Toggle overlaying of terrain codes on hexes."));
1304  register_alias("show_terrain_codes", "tc");
1305  register_command("show_num_of_bitmaps", &console_handler::do_toggle_draw_num_of_bitmaps,
1306  _("Toggle overlaying of number of bitmaps on hexes."));
1307  register_alias("show_num_of_bitmaps", "bn");
1308  register_command("whiteboard", &console_handler::do_toggle_whiteboard, _("Toggle planning mode."));
1309  register_alias("whiteboard", "wb");
1310  register_command(
1311  "whiteboard_options", &console_handler::do_whiteboard_options, _("Access whiteboard options dialog."));
1312  register_alias("whiteboard_options", "wbo");
1313 
1314  if(const config& alias_list = preferences::get_alias()) {
1315  for(const config::attribute& a : alias_list.attribute_range()) {
1316  register_alias(a.second, a.first);
1317  }
1318  }
1319  }
1320 
1321 private:
1323  const unsigned int team_num_;
1324 };
1325 
1326 void menu_handler::send_chat_message(const std::string& message, bool allies_only)
1327 {
1328  config cfg;
1329  cfg["id"] = preferences::login();
1330  cfg["message"] = message;
1331  const std::time_t time = ::std::time(nullptr);
1332  std::stringstream ss;
1333  ss << time;
1334  cfg["time"] = ss.str();
1335 
1336  const int side = board().is_observer() ? 0 : gui_->viewing_side();
1337  if(!board().is_observer()) {
1338  cfg["side"] = side;
1339  }
1340 
1341  bool private_message = has_friends() && allies_only;
1342 
1343  if(private_message) {
1344  if(board().is_observer()) {
1345  cfg["to_sides"] = game_config::observer_team_name;
1346  } else {
1347  cfg["to_sides"] = teams()[gui_->viewing_team()].allied_human_teams();
1348  }
1349  }
1350 
1351  resources::recorder->speak(cfg);
1352 
1353  add_chat_message(time, cfg["id"], side, message,
1355 }
1356 
1357 void menu_handler::do_search(const std::string& new_search)
1358 {
1359  if(new_search.empty() == false && new_search != last_search_)
1360  last_search_ = new_search;
1361 
1362  if(last_search_.empty())
1363  return;
1364 
1365  bool found = false;
1367  // If this is a location search, just center on that location.
1368  std::vector<std::string> args = utils::split(last_search_, ',');
1369  if(args.size() == 2) {
1370  int x, y;
1371  x = lexical_cast_default<int>(args[0], 0) - 1;
1372  y = lexical_cast_default<int>(args[1], 0) - 1;
1373  if(x >= 0 && x < map().w() && y >= 0 && y < map().h()) {
1374  loc = map_location(x, y);
1375  found = true;
1376  }
1377  }
1378  // Start scanning the game map
1379  if(loc.valid() == false) {
1380  loc = map_location(map().w() - 1, map().h() - 1);
1381  }
1382 
1383  map_location start = loc;
1384  while(!found) {
1385  // Move to the next location
1386  loc.x = (loc.x + 1) % map().w();
1387  if(loc.x == 0)
1388  loc.y = (loc.y + 1) % map().h();
1389 
1390  // Search label
1391  if(!gui_->shrouded(loc)) {
1392  const terrain_label* label = gui_->labels().get_label(loc);
1393  if(label) {
1394  std::string label_text = label->text().str();
1395  if(std::search(label_text.begin(), label_text.end(), last_search_.begin(), last_search_.end(),
1397  != label_text.end()) {
1398  found = true;
1399  }
1400  }
1401  }
1402  // Search unit name
1403  if(!gui_->fogged(loc)) {
1404  unit_map::const_iterator ui = units().find(loc);
1405  if(ui != units().end()) {
1406  const std::string name = ui->name();
1407  if(std::search(
1408  name.begin(), name.end(), last_search_.begin(), last_search_.end(), chars_equal_insensitive)
1409  != name.end()) {
1410  if(!teams()[gui_->viewing_team()].is_enemy(ui->side())
1411  || !ui->invisible(ui->get_location())) {
1412  found = true;
1413  }
1414  }
1415  }
1416  }
1417 
1418  if(loc == start)
1419  break;
1420  }
1421 
1422  if(found) {
1423  last_search_hit_ = loc;
1425  gui_->highlight_hex(loc);
1426  } else {
1428  // Not found, inform the player
1429  utils::string_map symbols;
1430  symbols["search"] = last_search_;
1431  const std::string msg = VGETTEXT("Could not find label or unit "
1432  "containing the string ‘$search’.",
1433  symbols);
1435  }
1436 }
1437 
1438 void menu_handler::do_command(const std::string& str)
1439 {
1440  console_handler ch(*this);
1441  ch.dispatch(str);
1442 }
1443 
1444 std::vector<std::string> menu_handler::get_commands_list()
1445 {
1446  console_handler ch(*this);
1447  // HACK: we need to call dispatch() at least once to get the
1448  // command list populated *at all*. Terrible design.
1449  // An empty command is silently ignored and has negligible
1450  // overhead, so we use that for this purpose here.
1451  ch.dispatch("");
1452  return ch.get_commands_list();
1453 }
1454 
1456 {
1459 
1460  menu_handler_.gui_->create_buttons();
1461  menu_handler_.gui_->redraw_everything();
1462 }
1463 
1465 {
1466  // :droid [<side> [on/off/full]]
1467  const std::string side_s = get_arg(1);
1468  std::string action = get_arg(2);
1469  std::transform(action.begin(), action.end(), action.begin(), tolower);
1470  // default to the current side if empty
1471  const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1472 
1473  utils::string_map symbols;
1474  symbols["side"] = std::to_string(side);
1475 
1476  if(side < 1 || side > menu_handler_.teams().size()) {
1477  command_failed(VGETTEXT("Can't droid invalid side: '$side'.", symbols));
1478  return;
1479  } else if(menu_handler_.board().get_team(side).is_network()) {
1480  command_failed(VGETTEXT("Can't droid networked side: '$side'.", symbols));
1481  return;
1482  } else if(menu_handler_.board().get_team(side).is_local()) {
1483  bool changed = false;
1484 
1485  if(action == "on") {
1486  if(!menu_handler_.board().get_team(side).is_human() || !menu_handler_.board().get_team(side).is_droid()) {
1487  menu_handler_.board().get_team(side).make_human();
1488  menu_handler_.board().get_team(side).make_droid();
1489  changed = true;
1490  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
1491  } else {
1492  print(get_cmd(), VGETTEXT("Side '$side' is already droided.", symbols));
1493  }
1494  } else if(action == "off") {
1495  if(!menu_handler_.board().get_team(side).is_human() || !menu_handler_.board().get_team(side).is_proxy_human()) {
1496  menu_handler_.board().get_team(side).make_human();
1497  menu_handler_.board().get_team(side).make_proxy_human();
1498  changed = true;
1499  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
1500  } else {
1501  print(get_cmd(), VGETTEXT("Side '$side' is already not droided.", symbols));
1502  }
1503  } else if(action == "full") {
1504  if(!menu_handler_.board().get_team(side).is_ai() || !menu_handler_.board().get_team(side).is_droid()) {
1505  menu_handler_.board().get_team(side).make_ai();
1506  menu_handler_.board().get_team(side).make_droid();
1507  changed = true;
1508  print(get_cmd(), VGETTEXT("Side '$side' controller is now fully controlled by: AI.", symbols));
1509  } else {
1510  print(get_cmd(), VGETTEXT("Side '$side' is already fully AI controlled.", symbols));
1511  }
1512  } else if(action == "") {
1513  if(menu_handler_.board().get_team(side).is_ai() || menu_handler_.board().get_team(side).is_droid()) {
1514  menu_handler_.board().get_team(side).make_human();
1515  menu_handler_.board().get_team(side).make_proxy_human();
1516  changed = true;
1517  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
1518  } else {
1519  menu_handler_.board().get_team(side).make_human();
1520  menu_handler_.board().get_team(side).make_droid();
1521  changed = true;
1522  print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
1523  }
1524  } else {
1525  print(get_cmd(), VGETTEXT("Invalid action provided for side '$side'. Valid actions are: on, off, full.", symbols));
1526  }
1527 
1528  if(team_num_ == side && changed) {
1529  if(playsingle_controller* psc = dynamic_cast<playsingle_controller*>(&menu_handler_.pc_)) {
1530  psc->set_player_type_changed();
1531  }
1532  }
1533  }
1534  menu_handler_.textbox_info_.close(*menu_handler_.gui_);
1535 }
1536 
1538 {
1539  // :terrain [<terrain type> [both|base|overlay]]
1540  const std::string terrain_type = get_arg(1);
1541  const std::string mode_str = get_arg(2);
1542 
1543  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1544  const map_location& loc = mousehandler.get_last_hex();
1545 
1546  synced_context::run_and_throw("debug_terrain",
1547  config {
1548  "x", loc.wml_x(),
1549  "y", loc.wml_y(),
1550  "terrain_type", terrain_type,
1551  "mode_str", mode_str,
1552  }
1553  );
1554 }
1555 
1557 {
1558  // :idle [<side> [on/off]]
1559  const std::string side_s = get_arg(1);
1560  const std::string action = get_arg(2);
1561  // default to the current side if empty
1562  const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1563 
1564  if(side < 1 || side > menu_handler_.teams().size()) {
1565  utils::string_map symbols;
1566  symbols["side"] = side_s;
1567  command_failed(VGETTEXT("Can't idle invalid side: '$side'.", symbols));
1568  return;
1569  } else if(menu_handler_.board().get_team(side).is_network()) {
1570  utils::string_map symbols;
1571  symbols["side"] = std::to_string(side);
1572  command_failed(VGETTEXT("Can't idle networked side: '$side'.", symbols));
1573  return;
1574  } else if(menu_handler_.board().get_team(side).is_local_ai()) {
1575  utils::string_map symbols;
1576  symbols["side"] = std::to_string(side);
1577  command_failed(VGETTEXT("Can't idle local ai side: '$side'.", symbols));
1578  return;
1579  } else if(menu_handler_.board().get_team(side).is_local_human()) {
1580  if(menu_handler_.board().get_team(side).is_idle() ? action == " on" : action == " off") {
1581  return;
1582  }
1583  // toggle the proxy controller between idle / non idle
1584  menu_handler_.board().get_team(side).toggle_idle();
1585  if(team_num_ == side) {
1586  if(playsingle_controller* psc = dynamic_cast<playsingle_controller*>(&menu_handler_.pc_)) {
1587  psc->set_player_type_changed();
1588  }
1589  }
1590  }
1591  menu_handler_.textbox_info_.close(*menu_handler_.gui_);
1592 }
1593 
1595 {
1597 }
1598 
1600 {
1601  save_id_matches(const std::string& save_id)
1602  : save_id_(save_id)
1603  {
1604  }
1605 
1606  bool operator()(const team& t) const
1607  {
1608  return t.save_id() == save_id_;
1609  }
1610 
1611  std::string save_id_;
1612 };
1613 
1615 {
1616  // :control <side> <nick>
1617  if(!menu_handler_.pc_.is_networked_mp()) {
1618  return;
1619  }
1620 
1621  const std::string side = get_arg(1);
1622  const std::string player = get_arg(2);
1623  if(player.empty()) {
1624  command_failed_need_arg(2);
1625  return;
1626  }
1627 
1628  unsigned int side_num;
1629  try {
1630  side_num = lexical_cast<unsigned int>(side);
1631  } catch(const bad_lexical_cast&) {
1632  const auto it_t = std::find_if(
1634  if(it_t == resources::gameboard->teams().end()) {
1635  utils::string_map symbols;
1636  symbols["side"] = side;
1637  command_failed(VGETTEXT("Can't change control of invalid side: '$side'.", symbols));
1638  return;
1639  } else {
1640  side_num = it_t->side();
1641  }
1642  }
1643 
1644  if(side_num < 1 || side_num > menu_handler_.teams().size()) {
1645  utils::string_map symbols;
1646  symbols["side"] = side;
1647  command_failed(VGETTEXT("Can't change control of out-of-bounds side: '$side'.", symbols));
1648  return;
1649  }
1650 
1651  menu_handler_.request_control_change(side_num, player);
1652  menu_handler_.textbox_info_.close(*(menu_handler_.gui_));
1653 }
1654 
1656 {
1657  const std::string side = get_arg(1);
1658  unsigned int side_num;
1659  try {
1660  side_num = lexical_cast<unsigned int>(side);
1661  } catch(const bad_lexical_cast&) {
1662  utils::string_map symbols;
1663  symbols["side"] = side;
1664  command_failed(VGETTEXT("Can't query control of invalid side: '$side'.", symbols));
1665  return;
1666  }
1667 
1668  if(side_num < 1 || side_num > menu_handler_.teams().size()) {
1669  utils::string_map symbols;
1670  symbols["side"] = side;
1671  command_failed(VGETTEXT("Can't query control of out-of-bounds side: '$side'.", symbols));
1672  return;
1673  }
1674 
1675  std::string report = menu_handler_.board().get_team(side_num).controller().to_string();
1676  if(!menu_handler_.board().get_team(side_num).is_proxy_human()) {
1677  report += " (" + menu_handler_.board().get_team(side_num).proxy_controller().to_string() + ")";
1678  }
1679 
1680  if(menu_handler_.board().get_team(side_num).is_network()) {
1681  report += " (networked)";
1682  }
1683 
1684  print(get_cmd(), report);
1685 }
1686 
1688 {
1689  menu_handler_.gui_->get_chat_manager().clear_chat_messages();
1690 }
1691 
1693 {
1694  menu_handler_.gui_->toggle_debug_foreground();
1695 }
1696 
1698 {
1699  display& disp = *(menu_handler_.gui_);
1700 
1701  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1702  const map_location& loc = mousehandler.get_last_hex();
1703 
1704  //
1705  // It's possible to invoke this dialog even if loc isn't a valid hex. I'm not sure
1706  // exactly how that happens, but it does seem to occur when moving the mouse outside
1707  // the window to the menu bar. Not sure if there's a bug when the set-last-hex code
1708  // in that case, but this check at least ensures the dialog is only ever shown for a
1709  // valid, on-map location. Otherwise, an assertion gets thrown.
1710  //
1711  // -- vultraz, 2017-09-21
1712  //
1713  if(disp.get_map().on_board_with_border(loc)) {
1714  gui2::dialogs::terrain_layers::display(disp, loc);
1715  }
1716 }
1717 
1719 {
1721 }
1722 
1724 {
1725  menu_handler_.gui_->toggle_benchmark();
1726 }
1727 
1729 {
1730  menu_handler_.pc_.do_consolesave(get_data());
1731 }
1732 
1734 {
1735  do_save();
1736  do_quit();
1737 }
1738 
1740 {
1742 }
1743 
1745 {
1746  game_config::ignore_replay_errors = (get_data() != "off") ? true : false;
1747 }
1748 
1750 {
1751  game_config::disable_autosave = (get_data() != "off") ? true : false;
1752 }
1753 
1755 {
1756  synced_context::run_and_throw("debug_next_level", config {"next_level", get_data()});
1757 }
1758 
1760 {
1761  std::string tag = menu_handler_.pc_.get_classification().get_tagname();
1762  std::vector<std::string> options;
1763  std::string next;
1764  if(tag != "multiplayer") {
1765  for(const config& sc : menu_handler_.game_config_.child_range(tag)) {
1766  const std::string& id = sc["id"];
1767  options.push_back(id);
1768  if(id == menu_handler_.gamedata().next_scenario()) {
1769  next = id;
1770  }
1771  }
1772  } else {
1773  // find scenarios of multiplayer campaigns
1774  // (assumes that scenarios are ordered properly in the game_config)
1775  std::string scenario_id = menu_handler_.pc_.get_mp_settings().mp_scenario;
1776  if(const config& this_scenario = menu_handler_.game_config_.find_child(tag, "id", scenario_id)) {
1777  std::string addon_id = this_scenario["addon_id"].str();
1778  for(const config& sc : menu_handler_.game_config_.child_range(tag)) {
1779  if(sc["addon_id"] == addon_id) {
1780  std::string id = sc["id"];
1781  options.push_back(id);
1782  if(id == menu_handler_.gamedata().next_scenario()) {
1783  next = id;
1784  }
1785  }
1786  }
1787  }
1788  }
1789  std::sort(options.begin(), options.end());
1790  int choice = std::distance(options.begin(), std::lower_bound(options.begin(), options.end(), next));
1791  {
1792  gui2::dialogs::simple_item_selector dlg(_("Choose Scenario (Debug!)"), "", options);
1793  dlg.set_selected_index(choice);
1794  dlg.show();
1795  choice = dlg.selected_index();
1796  }
1797 
1798  if(choice == -1) {
1799  return;
1800  }
1801 
1802  if(std::size_t(choice) < options.size()) {
1803  synced_context::run_and_throw("debug_next_level", config {"next_level", options[choice]});
1804  }
1805 }
1806 
1808 {
1809  tod_manager& tod_man = menu_handler_.gamestate().tod_manager_;
1810 
1811  int turn = tod_man.turn() + 1;
1812  const std::string& data = get_data();
1813  if(!data.empty()) {
1814  turn = lexical_cast_default<int>(data, 1);
1815  }
1816  synced_context::run_and_throw("debug_turn", config {"turn", turn});
1817 }
1818 
1820 {
1821  int limit = get_data().empty() ? -1 : lexical_cast_default<int>(get_data(), 1);
1822  synced_context::run_and_throw("debug_turn_limit", config {"turn_limit", limit});
1823 }
1824 
1826 {
1827  if(!menu_handler_.pc_.is_networked_mp() || game_config::mp_debug) {
1828  print(get_cmd(), _("Debug mode activated!"));
1829  game_config::set_debug(true);
1830  } else {
1831  command_failed(_("Debug mode not available in network games"));
1832  }
1833 }
1834 
1836 {
1837  if(game_config::debug) {
1838  print(get_cmd(), _("Debug mode deactivated!"));
1839  game_config::set_debug(false);
1840  }
1841 }
1842 
1844 {
1845  if(!menu_handler_.gamestate().lua_kernel_) {
1846  return;
1847  }
1848 
1849  synced_context::run_and_throw("debug_lua", config {"code", get_data()});
1850 }
1851 
1853 {
1854  if(!menu_handler_.gamestate().lua_kernel_) {
1855  return;
1856  }
1857 
1858  const int retval = gui2::show_message(_("WARNING! Unsafe Lua Mode"),
1859  _("Executing Lua code in in this manner opens your computer to potential security breaches from any "
1860  "malicious add-ons or other programs you may have installed.\n\n"
1861  "Do not continue unless you really know what you are doing."), gui2::dialogs::message::ok_cancel_buttons);
1862 
1863  if(retval == gui2::retval::OK) {
1864  print(get_cmd(), _("Unsafe mode enabled!"));
1865  menu_handler_.gamestate().lua_kernel_->load_package();
1866  }
1867 }
1868 
1870 {
1871  preferences::set_custom_command(get_data());
1872 }
1873 
1875 {
1876  const std::string data = get_data();
1877  const std::string::const_iterator j = std::find(data.begin(), data.end(), '=');
1878  const std::string alias(data.begin(), j);
1879  if(j != data.end()) {
1880  const std::string command(j + 1, data.end());
1881  if(!command.empty()) {
1882  register_alias(command, alias);
1883  } else {
1884  // "alias something=" deactivate this alias. We just set it
1885  // equal to itself here. Later preferences will filter empty alias.
1886  register_alias(alias, alias);
1887  }
1888  preferences::add_alias(alias, command);
1889  // directly save it for the moment, but will slow commands sequence
1891  } else {
1892  // "alias something" display its value
1893  // if no alias, will be "'something' = 'something'"
1894  const std::string command = chmap::get_actual_cmd(alias);
1895  print(get_cmd(), "'" + alias + "'" + " = " + "'" + command + "'");
1896  }
1897 }
1898 
1900 {
1901  const std::string data = get_data();
1902  if(data.empty()) {
1903  command_failed_need_arg(1);
1904  return;
1905  }
1906 
1907  const std::string::const_iterator j = std::find(data.begin(), data.end(), '=');
1908  if(j != data.end()) {
1909  const std::string name(data.begin(), j);
1910  const std::string value(j + 1, data.end());
1911  synced_context::run_and_throw("debug_set_var", config {"name", name, "value", value});
1912  } else {
1913  command_failed(_("Variable not found"));
1914  }
1915 }
1916 
1918 {
1919  gui2::show_transient_message("", menu_handler_.gamedata().get_variable_const(get_data()));
1920 }
1921 
1923 {
1925  gui2::dialogs::gamestate_inspector::display(
1927 }
1928 
1930 {
1931  gui2::dialogs::mp_change_control::display(menu_handler_);
1932 }
1933 
1935 {
1936  // prevent SIGSEGV due to attempt to set HP during a fight
1937  if(events::commands_disabled > 0) {
1938  return;
1939  }
1940 
1941  unit_map::iterator i = menu_handler_.current_unit();
1942  if(i == menu_handler_.units().end()) {
1943  return;
1944  }
1945 
1946  const map_location loc = i->get_location();
1947  const std::string data = get_data(1);
1948  std::vector<std::string> parameters = utils::split(data, '=', utils::STRIP_SPACES);
1949  if(parameters.size() < 2) {
1950  return;
1951  }
1952 
1953  if(parameters[0] == "alignment") {
1954  unit_type::ALIGNMENT alignment;
1955  if(!alignment.parse(parameters[1])) {
1956  utils::string_map symbols;
1957  symbols["alignment"] = get_arg(1);
1958  command_failed(VGETTEXT(
1959  "Invalid alignment: '$alignment', needs to be one of lawful, neutral, chaotic, or liminal.",
1960  symbols));
1961  return;
1962  }
1963  }
1964 
1965  synced_context::run_and_throw("debug_unit",
1966  config {
1967  "x", loc.wml_x(),
1968  "y", loc.wml_y(),
1969  "name", parameters[0],
1970  "value", parameters[1],
1971  }
1972  );
1973 }
1974 
1976 {
1977  for(const unit_type_data::unit_type_map::value_type& i : unit_types.types()) {
1978  preferences::encountered_units().insert(i.second.id());
1979  }
1980 }
1981 
1983 {
1984  const int res = gui2::show_message("Undiscover",
1985  _("Do you wish to clear all of your discovered units from help?"), gui2::dialogs::message::yes_no_buttons);
1986  if(res != gui2::retval::CANCEL) {
1988  }
1989 }
1990 
1991 /** Implements the (debug mode) console command that creates a unit. */
1993 {
1994  const mouse_handler& mousehandler = menu_handler_.pc_.get_mouse_handler_base();
1995  const map_location& loc = mousehandler.get_last_hex();
1996  if(menu_handler_.map().on_board(loc)) {
1997  const unit_type* ut = unit_types.find(get_data());
1998  if(!ut) {
1999  command_failed(_("Invalid unit type"));
2000  return;
2001  }
2002 
2003  // Create the unit.
2004  create_and_place(*menu_handler_.gui_, menu_handler_.map(), menu_handler_.units(), loc, *ut);
2005  } else {
2006  command_failed(_("Invalid location"));
2007  }
2008 }
2009 
2011 {
2012  synced_context::run_and_throw("debug_fog", config());
2013 }
2014 
2016 {
2017  synced_context::run_and_throw("debug_shroud", config());
2018 }
2019 
2021 {
2022  synced_context::run_and_throw("debug_gold", config {"gold", lexical_cast_default<int>(get_data(), 1000)});
2023 }
2024 
2026 {
2027  synced_context::run_and_throw("debug_event", config {"eventname", get_data()});
2028 }
2029 
2031 {
2032  menu_handler_.gui_->set_draw_coordinates(!menu_handler_.gui_->get_draw_coordinates());
2033  menu_handler_.gui_->invalidate_all();
2034 }
2036 {
2037  menu_handler_.gui_->set_draw_terrain_codes(!menu_handler_.gui_->get_draw_terrain_codes());
2038  menu_handler_.gui_->invalidate_all();
2039 }
2040 
2042 {
2043  menu_handler_.gui_->set_draw_num_of_bitmaps(!menu_handler_.gui_->get_draw_num_of_bitmaps());
2044  menu_handler_.gui_->invalidate_all();
2045 }
2046 
2048 {
2049  if(const std::shared_ptr<wb::manager>& whiteb = menu_handler_.pc_.get_whiteboard()) {
2050  whiteb->set_active(!whiteb->is_active());
2051  if(whiteb->is_active()) {
2052  print(get_cmd(), _("Planning mode activated!"));
2053  whiteb->print_help_once();
2054  } else {
2055  print(get_cmd(), _("Planning mode deactivated!"));
2056  }
2057  }
2058 }
2059 
2061 {
2062  if(menu_handler_.pc_.get_whiteboard()) {
2063  menu_handler_.pc_.get_whiteboard()->options_dlg();
2064  }
2065 }
2066 
2067 void menu_handler::do_ai_formula(const std::string& str, int side_num, mouse_handler& /*mousehandler*/)
2068 {
2069  try {
2070  add_chat_message(std::time(nullptr), "wfl", 0, ai::manager::get_singleton().evaluate_command(side_num, str));
2071  } catch(const wfl::formula_error&) {
2072  } catch(...) {
2073  add_chat_message(std::time(nullptr), "wfl", 0, "UNKNOWN ERROR IN FORMULA");
2074  }
2075 }
2076 
2078 {
2079  textbox_info_.show(gui::TEXTBOX_COMMAND, translation::sgettext("prompt^Command:"), "", false, *gui_);
2080 }
2081 
2082 void menu_handler::request_control_change(int side_num, const std::string& player)
2083 {
2084  std::string side = std::to_string(side_num);
2085  if(board().get_team(side_num).is_local_human() && player == preferences::login()) {
2086  // this is already our side.
2087  return;
2088  } else {
2089  // The server will (or won't because we aren't allowed to change the controller)
2090  // send us a [change_controller] back, which we then handle in playturn.cpp
2091  pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", player}});
2092  }
2093 }
2094 
2096 {
2097  for(const std::string& command : utils::split(preferences::custom_command(), ';')) {
2098  do_command(command);
2099  }
2100 }
2101 
2103 {
2104  if(!pc_.is_networked_mp()) {
2105  textbox_info_.show(gui::TEXTBOX_AI, translation::sgettext("prompt^Command:"), "", false, *gui_);
2106  }
2107 }
2108 
2110 {
2111  gui_->get_chat_manager().clear_chat_messages(); // also clear debug-messages and WML-error-messages
2112 }
2113 
2115 {
2116  pc_.send_to_wesnothd(cfg);
2117 }
2118 
2119 } // end namespace events
bool show_theme_dialog()
Definition: display.cpp:116
void label_terrain(mouse_handler &mousehandler, bool team_only)
pathfind::marked_route get_route(const unit *un, map_location go_to, team &team) const
int dispatch(lua_State *L)
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
int village_owner(const map_location &loc) const
Given the location of a village, will return the 0-based index of the team that currently owns it...
Dialog was closed with the CANCEL button.
Definition: retval.hpp:37
menu_handler & menu_handler_
bool empty() const
Is it empty?
void do_search(const std::string &new_search)
void set_current_paths(const pathfind::paths &new_paths)
virtual replay_controller * get_replay_controller() const
Game board class.
Definition: game_board.hpp:50
game_data & gamedata()
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
void set_grid(bool ison)
Definition: display.cpp:70
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &message, events::chat_handler::MESSAGE_TYPE type=events::chat_handler::MESSAGE_PRIVATE) override
void goto_leader(int side_num)
int h() const
Effective map height, in hexes.
Definition: map.hpp:128
unit_iterator end()
Definition: map.hpp:415
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:116
bool show_fps()
Definition: general.cpp:835
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:1270
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:291
bool message_private()
Definition: game.cpp:861
std::map< std::string, t_string > string_map
void write_preferences()
Definition: general.cpp:154
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:2998
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:99
void show_terrain_description(const terrain_type &t)
Definition: help.cpp:63
Various functions implementing vision (through fog of war and shroud).
void send_chat_message(const std::string &message, bool allies_only=false) override
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
bool is_authenticated()
Definition: game.cpp:181
void speak(const config &cfg)
Definition: replay.cpp:347
static manager & get_singleton()
Definition: manager.hpp:150
Various functions that implement attacks and attack calculations.
config & find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:836
map_command_handler< console_handler > chmap
unit_iterator find_leader(int side)
Definition: map.cpp:329
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
std::vector< std::string > get_commands_list() const
#define a
unit_race::GENDER gender()
Gender choice from the user.
Definition: unit_create.hpp:53
const map_location hovered_hex() const
Uses SDL and game_display::hex_clicked_on to fetch the hex the mouse is hovering, if applicable...
Shows an ok and cancel button.
Definition: message.hpp:75
std::string private_message
const t_string & text() const
Definition: label.hpp:136
menu_handler(game_display *gui, play_controller &pc)
Definition: menu_events.cpp:92
child_itors child_range(config_key_type key)
Definition: config.cpp:362
void show_unit_list(display &gui)
Definition: unit_list.cpp:199
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
void do_command(const std::string &str)
const std::string & choice() const
Unit type choice from the user.
Definition: unit_create.hpp:41
attribute_map::value_type attribute
Definition: config.hpp:226
void redraw_everything()
Invalidates entire screen, including all tiles and sidebar.
Definition: display.cpp:2398
gui::floating_textbox & get_textbox()
General purpose widgets.
void request_control_change(int side_num, const std::string &player)
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
bool show_everything() const
Definition: display.hpp:90
virtual std::string get_cmd() const
virtual const gamemap & map() const override
Definition: game_board.hpp:109
int wml_x() const
Definition: location.hpp:157
const unit_type * get_selected_unit_type() const
static config get_auto_shroud(bool turned_on)
Records that the player has toggled automatic shroud updates.
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:97
int viewing_side() const
Definition: display.hpp:103
unit_type_data unit_types
Definition: types.cpp:1528
void print(const std::string &title, const std::string &message)
std::size_t move_unit_and_record(const std::vector< map_location > &steps, undo_list *undo_stack, bool continued_move, bool show_move, bool *interrupted, move_unit_spectator *move_spectator)
Moves a unit across the board.
Definition: move.cpp:1246
const std::string & save_id() const
Definition: team.hpp:231
const color_t LABEL_COLOR
file_dialog & set_save_mode(bool value)
Sets the dialog&#39;s behavior on non-existent file name inputs.
void update_shroud_now(int side_num)
#define VNGETTEXT(msgid, msgid_plural, count,...)
Replay control code.
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
bool do_recruit(const std::string &name, int side_num, map_location &target_hex)
bool chars_equal_insensitive(char a, char b)
Definition: general.hpp:21
Contains the exception interfaces used to signal completion of a scenario, campaign or turn...
bool unit_can_move(const unit &u) const
Will return true iff the unit u has any possible moves it can do (including attacking etc)...
To lexical_cast(From value)
Lexical cast converts one type to another.
bool show(const unsigned auto_close_time=0)
Shows the window.
void create_unit(mouse_handler &mousehandler)
Creates a unit (in debug mode via hotkey or context menu).
const unit_type_map & types() const
Definition: types.hpp:363
save_id_matches(const std::string &save_id)
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
bool yellow_confirm()
Definition: game.cpp:975
bool ellipses()
Definition: general.cpp:495
static synced_state get_synced_state()
std::vector< unit_const_ptr > get_recalls(int side, const map_location &recall_loc)
Gets the recallable units for a side, restricted by that side&#39;s leaders&#39; personal abilities to recall...
Definition: create.cpp:158
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:277
void show_statistics(int side_num)
A single unit type that the player may recruit.
Definition: types.hpp:42
game_data * gamedata
Definition: resources.cpp:22
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
void flush_cache()
Definition: picture.cpp:231
bool get_disallow_recall() const
bool team_valid() const
Definition: display.cpp:729
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:78
int gold() const
Definition: team.hpp:189
void highlight_another_reach(const pathfind::paths &paths_list, const map_location &goal=map_location::null_location())
Add more paths to highlight.
std::string path() const
Gets the current file selection.
map_location get_selected_hex() const
game_board & board() const
int action_bonus_count() const
Definition: team.hpp:213
bool confirm_no_moves()
Definition: game.cpp:980
const map_location & get_last_hex() const
std::vector< team > & teams() const
unit_map units_
Definition: game_board.hpp:58
const config & options()
Definition: game.cpp:579
std::string write() const
Definition: map.cpp:205
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
bool end_turn(int side_num)
game_display * gui_
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
console_handler(menu_handler &menu_handler)
virtual void register_alias(const std::string &to_cmd, const std::string &cmd)
void toggle_shroud_updates(int side_num)
team & get_team(int i)
Definition: game_board.hpp:104
void set_custom_command(const std::string &command)
Definition: game.cpp:952
virtual std::string get_arg(unsigned i) const
t_string can_recruit(const std::string &name, int side_num, map_location &target_hex, map_location &recruited_from)
void throw_quit_game_exception()
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
const std::vector< unit_race::GENDER > & genders() const
The returned vector will not be empty, provided this has been built to the HELP_INDEXED status...
Definition: types.hpp:240
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:344
void scroll_to_leader(int side, SCROLL_TYPE scroll_type=ONSCREEN, bool force=true)
Scrolls to the leader of a certain side.
const t_string & cannot_end_turn_reason()
Definition: game_data.hpp:79
bool hex_hosts_unit(const map_location &hex) const
Unit exists on the hex, no matter if friend or foe.
int cost() const
Definition: types.hpp:159
void disable_units_highlight()
Use this to disable hovering an unit from highlighting its movement range.
std::vector< team > teams_
Definition: game_board.hpp:53
bool is_enabled(const chmap::command &c) const
const std::set< std::string > get_recruits(int side, const map_location &recruit_loc)
Gets the recruitable units from a side&#39;s leaders&#39; personal recruit lists who can recruit on or from a...
Definition: create.cpp:58
This file contains the settings handling of the widget library.
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition: label.cpp:146
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:382
void do_create()
Implements the (debug mode) console command that creates a unit.
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:135
Applies the planned unit map for the duration of the struct&#39;s life.
Definition: manager.hpp:251
static bool execute(game_board &board, const int viewing_team, int &selected_side_number)
Definition: game_stats.hpp:40
std::string get_user_data_dir()
Definition: filesystem.cpp:780
void clear(const std::string &, bool force)
Definition: label.cpp:210
int wml_y() const
Definition: location.hpp:158
bool disable_autosave
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
Definition: filesystem.cpp:986
static void ignore_error_function(const std::string &message, bool heavy)
a function to be passed to run_in_synced_context to ignore the error.
bool valid() const
Definition: location.hpp:93
const std::unique_ptr< gui::button > & check() const
bool ignore_replay_errors
void recall(int side_num, const map_location &last_hex)
game_board * gameboard
Definition: resources.cpp:20
REMOVE_EMPTY: remove empty elements.
const config & game_config_
void end_unit_turn(mouse_handler &mousehandler, int side_num)
void set_show_fps(bool value)
Definition: general.cpp:840
Encapsulates the map of the game.
Definition: map.hpp:36
const t_string & name() const
Gets this unit&#39;s translatable display name.
Definition: unit.hpp:367
virtual const std::set< std::string > & observers() const override
void recruit(int side_num, const map_location &last_hex)
const std::string & last_recruit() const
Definition: team.hpp:228
bool has_friends() const
void do_speak(const std::string &message, bool allies_only=false)
bool is_enemy(int n) const
Definition: team.hpp:243
const map_location & get_goto() const
The map location to which this unit is moving over multiple turns, if any.
Definition: unit.hpp:1297
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1867
std::string path
Definition: game_config.cpp:39
void add_rename(const std::string &name, const map_location &loc)
Definition: replay.cpp:290
Shows a yes and no button.
Definition: message.hpp:79
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:138
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
replay * recorder
Definition: resources.cpp:28
Structure which holds a single route and marks for special events.
Definition: pathfind.hpp:140
bool no_choice() const
Whether the user actually chose a unit type or not.
Definition: unit_create.hpp:47
std::shared_ptr< wb::manager > get_whiteboard() const
const terrain_label * get_label(const map_location &loc, const std::string &team_name) const
Definition: label.hpp:47
const char * what() const noexcept
Definition: exceptions.hpp:37
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:2991
void set_message_private(bool value)
Definition: game.cpp:866
game_events::manager * game_events
Definition: resources.cpp:24
void show_unit_description(const unit &u)
Definition: help.cpp:58
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:739
const std::string & gender_string(unit_race::GENDER gender)
Definition: race.cpp:131
int recall_cost() const
Definition: team.hpp:193
void flush_cache()
Definition: sound.cpp:194
Encapsulates the map of the game.
Definition: location.hpp:42
bool auto_shroud_updates() const
Definition: team.hpp:338
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:734
Various functions related to moving units.
std::string login()
bool user_end_turn() const
Check whether the user ended their turn.
Definition: unit.hpp:751
unit_iterator find(std::size_t id)
Definition: map.cpp:311
game_state & gamestate() const
Various functions related to the creation of units (recruits, recalls, and placed units)...
actions::undo_list & get_undo_stack()
bool has_moved() const
Checks if this unit has moved.
Definition: unit.hpp:1221
std::string current_team_name() const
void recalculate_labels()
Definition: label.cpp:244
int w() const
Effective map width, in hexes.
Definition: map.hpp:125
static void display(const config &game_cfg, const preferences::PREFERENCE_VIEW initial_view=preferences::VIEW_DEFAULT)
The display function – see modal_dialog for more information.
const gamemap & map() const
virtual bool is_networked_mp() const
void continue_move(mouse_handler &mousehandler, int side_num)
static void print(std::stringstream &sstr, const std::string &queue, const std::string &id)
Definition: fire_event.cpp:29
void add_alias(const std::string &alias, const std::string &command)
Definition: general.cpp:881
std::size_t i
Definition: function.cpp:933
logger & err()
Definition: log.cpp:78
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:2182
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:49
std::string get_command_flags_description(const chmap::command &c) const
An exception object used when an IO error occurs.
Definition: filesystem.hpp:48
void terrain_description(mouse_handler &mousehandler)
Define the game&#39;s event mechanism.
std::string find_recall_location(const int side, map_location &recall_location, map_location &recall_from, const unit &unit_recall)
Finds a location on which to recall unit_recall.
Definition: create.cpp:328
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:213
const display_context & get_disp_context() const
Definition: display.hpp:168
void unit_hold_position(mouse_handler &mousehandler, int side_num)
const std::string & team_name() const
Definition: label.cpp:128
void change_side(mouse_handler &mousehandler)
game_data gamedata_
Definition: game_state.hpp:45
int w
std::set< std::string > & encountered_units()
Definition: game.cpp:940
const bool & debug
#define N_(String)
Definition: gettext.hpp:99
Definitions for the terrain builder.
void show(gui::TEXTBOX_MODE mode, const std::string &label, const std::string &check_label, bool checked, game_display &gui)
static int sort(lua_State *L)
Definition: ltablib.cpp:411
void set_goto(const map_location &new_goto)
Sets this unit&#39;s long term destination.
Definition: unit.hpp:1303
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
void set_ellipses(bool ison)
Definition: general.cpp:500
void cycle_units(const bool browse, const bool reverse=false)
void kill_unit(mouse_handler &mousehandler)
play_controller & pc_
Handling of system events.
Definition: manager.hpp:41
bool grid()
Definition: general.cpp:505
bool is_replay() const
#define next(ls)
Definition: llex.cpp:32
bool operator()(const team &t) const
display_chat_manager & get_chat_manager()
static bool run_and_throw(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
virtual std::string get_data(unsigned n=1) const
void show_transient_error_message(const std::string &message, const std::string &image, const bool message_use_markup)
Shows a transient error message to the user.
bool green_confirm()
Definition: game.cpp:966
bool empty() const
Definition: tstring.hpp:182
game_events::pump_result_t get_village(const map_location &loc, int side, bool *action_timebonus, bool fire_event)
Makes it so the village at the given location is owned by the given side.
Definition: move.cpp:139
const gamemap & get_map() const
Definition: display.hpp:92
To store label data Class implements logic for rendering.
Definition: label.hpp:107
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
const std::unique_ptr< gui::textbox > & box() const
unit_map::iterator current_unit()
game_state & gamestate()
double t
Definition: astarsearch.cpp:64
bool unhighlight_reach()
Reset highlighting of paths.
bool find(E event, F functor)
Tests whether an event handler is available.
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:1266
static color_t get_side_color(int side)
Definition: team.cpp:948
static UNUSEDNOWARN std::string sgettext(const char *str)
Definition: gettext.hpp:68
void set_selected_index(int index)
Sets the initially selected item index (-1 by default).
game_board board_
Definition: game_state.hpp:46
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:102
#define DBG_WB
Definition: typedefs.hpp:27
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
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:464
void set_action_bonus_count(const int count)
Definition: team.hpp:214
Standard logging facilities (interface).
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:70
recall_list_manager & recall_list()
Definition: team.hpp:215
static const map_location & null_location()
Definition: location.hpp:85
Container associating units to locations.
Definition: map.hpp:99
std::string get_command_flags_description(const map_command_handler< chat_command_handler >::command &c) const
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:28
std::string get_flags_description() const
static vconfig empty_vconfig()
Definition: variable.cpp:105
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
map_labels & labels()
Definition: display.cpp:2517
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:304
Definition: help.cpp:56
bool player_acted() const
Returns true if the player has performed any actions this turn.
Definition: undo.hpp:84
Dialog was closed with the OK button.
Definition: retval.hpp:34
gui::floating_textbox textbox_info_
void send_to_server(const config &cfg) override
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
void notify_event(const std::string &name, const config &data)
Definition: manager.cpp:144
mock_char c
const std::string & str() const
Definition: tstring.hpp:186
const config & get_alias()
Definition: general.cpp:888
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:948
std::vector< std::string > get_commands_list()
static map_location::DIRECTION n
void repeat_recruit(int side_num, const map_location &last_hex)
void move_unit_to_loc(const unit_map::iterator &ui, const map_location &target, bool continue_move, int side_num, mouse_handler &mousehandler)
Thrown when a lexical_cast fails.
void clear_labels(const std::string &, bool)
Definition: replay.cpp:280
const unsigned int team_num_
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1191
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:177
virtual void send_to_wesnothd(const config &, const std::string &="unknown") const
Display units performing various actions: moving, attacking, and dying.
virtual void register_command(const std::string &cmd, chat_command_handler::command_handler h, const std::string &help="", const std::string &usage="", const std::string &flags="")
play_controller & pc_
std::vector< map_location > & steps
Definition: pathfind.hpp:186
void set_debug(bool new_debug)
static plugins_manager * get()
Definition: manager.cpp:58
void show_objectives() const
void add_label(const terrain_label *)
Definition: replay.cpp:269
const std::string observer_team_name
observer team name used for observer team chat
Definition: game_config.cpp:90
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
Definition: file_dialog.hpp:56