84 #define ERR_NG LOG_STREAM(err, log_engine)
85 #define LOG_NG LOG_STREAM(info, log_engine)
92 std::string last_created_unit =
"";
93 std::string last_recruit =
"";
94 std::string last_variation =
"";
161 const auto& unit_dlg = units_dialog::build_unit_list_dialog(
unit_list);
163 if (unit_dlg->show() && unit_dlg->is_selected()) {
199 symbols[
"msg"] =
e.what();
200 const std::string
msg =
VGETTEXT(
"Could not save the map: $msg", symbols);
208 gui2::dialogs::preferences_dialog::display();
216 c[
"name"] =
"prototype of chat log";
228 ?
board().is_observer()
229 ?
_(
"Send to observers only")
230 :
_(
"Send to allies only")
248 if(
board().is_observer()) {
267 std::map<const unit_type*, t_string> err_msgs_map;
268 std::vector<const unit_type*> recruit_list;
269 std::vector<t_string> unknown_units;
275 int selected_index = -1,
i = 0;
280 recruit_list.push_back(
type);
283 unknown_units.emplace_back(
recruit);
291 if(
type->id() == last_recruit) {
298 if(!unknown_units.empty()) {
303 "Error: there’s an unknown unit type on your recruit list: $unknown_ids",
304 "Error: there are several unknown unit types on your recruit list: $unknown_ids",
305 unknown_units.size(),
306 {{
"unknown_ids", utils::format_conjunct_list(
"", unknown_units) }}
310 if(recruit_list.empty()) {
315 auto dlg = units_dialog::build_recruit_dialog(recruit_list, err_msgs_map, current_team);
316 dlg->set_selected_index(selected_index);
317 if (!dlg->show() && !dlg->is_selected()) {
321 const unit_type*
type = recruit_list[dlg->get_selected_index()];
322 last_recruit =
type->id();
325 if(err_msgs_map[
type].empty()) {
326 do_recruit(
type->id(), side_num, last_hex);
333 void menu_handler::repeat_recruit(
int side_num,
const map_location& last_hex)
335 team& current_team = board().get_team(side_num);
336 const std::string& last_recruit = current_team.
last_recruit();
337 if(last_recruit.empty())
return;
339 const auto validation = std::array
350 for(
const std::string&
err : validation) {
357 do_recruit(last_recruit, side_num, last_hex);
360 bool menu_handler::do_recruit(
const std::string&
type,
int side_num,
const map_location& target_hex)
373 if(
auto wb = pc_.get_whiteboard()) {
374 if(
wb->save_recruit(
type, side_num,
dst)) {
380 team& current_team = board().get_team(side_num);
391 if(pc_.get_disallow_recall()) {
396 team& current_team = board().get_team(side_num);
398 std::vector<unit_const_ptr> recall_list_team;
404 DBG_WB <<
"menu_handler::recall: Contents of wb-modified recall list:";
411 _(
"There are no troops available to recall.\n(You must have veteran survivors from a previous scenario.)"));
415 if(recall_list_team.empty()) {
420 const auto& dlg = units_dialog::build_recall_dialog(recall_list_team, current_team);
422 if(!dlg->show() || !dlg->is_selected()) {
426 int res = dlg->get_selected_index();
435 if(sel_unit->recall_cost() > -1) {
436 unit_cost = sel_unit->recall_cost();
439 int wb_gold = pc_.get_whiteboard() ? pc_.get_whiteboard()->get_spent_gold_for(side_num) : 0;
441 if(current_team.
gold() - wb_gold < unit_cost) {
443 i18n_symbols[
"cost"] = std::to_string(unit_cost);
444 std::string
msg =
VNGETTEXT(
"You must have at least 1 gold piece to recall a unit.",
445 "You must have at least $cost gold pieces to recall this unit.", unit_cost, i18n_symbols);
450 LOG_NG <<
"recall index: " << res;
467 if(!pc_.get_whiteboard()
468 || !pc_.get_whiteboard()->save_recall(*sel_unit, side_num, recall_location)) {
473 ERR_NG <<
"menu_handler::recall(): Unit does not exist in the recall list.";
479 void menu_handler::show_enemy_moves(
bool ignore_units,
int side_num)
486 gui_->unhighlight_reach();
489 for(
auto& u : pc_.get_units()) {
490 bool invisible = u.invisible(u.get_location());
492 if(board().get_team(side_num).is_enemy(u.side()) && !gui_->fogged(u.get_location()) && !u.incapacitated()
496 =
pathfind::paths(u,
false,
true, gui_->viewing_team(), 0,
false, ignore_units);
498 gui_->highlight_another_reach(
path, hex_under_mouse);
502 gui_->invalidate(u.get_location());
507 const bool selected_hex_has_unit = mh.
hex_hosts_unit(hex_under_mouse);
509 if(selected_hex_has_unit) {
518 void menu_handler::toggle_shroud_updates(
int side_num)
520 team& current_team = board().get_team(side_num);
524 update_shroud_now(side_num);
531 void menu_handler::update_shroud_now(
int )
540 bool units_alive(
int side_num,
const unit_map& units)
542 for(
auto&
unit : units) {
551 bool partmoved_units(
552 int side_num,
const unit_map& units,
const game_board& board,
const std::shared_ptr<wb::manager>& whiteb)
554 for(
auto&
unit : units) {
569 int side_num,
const unit_map& units,
const game_board& board,
const std::shared_ptr<wb::manager>& whiteb)
571 for(
auto&
unit : units) {
574 && (!whiteb || !whiteb->unit_has_actions(&
unit))) {
584 bool menu_handler::end_turn(
int side_num)
589 reason =
_(
"You cannot end your turn yet!");
595 std::size_t team_num =
static_cast<std::size_t
>(side_num - 1);
596 if(team_num < pc_.get_teams().size() && pc_.get_teams()[team_num].no_turn_confirmation()) {
601 && (!pc_.get_whiteboard() || !pc_.get_whiteboard()->current_side_has_actions())
602 && units_alive(side_num, pc_.get_units())) {
604 _(
"You have not started your turn yet. Do you really want to end your turn?"),
605 message::yes_no_buttons);
611 else if(
prefs::get().yellow_confirm() && partmoved_units(side_num, pc_.get_units(), board(), pc_.get_whiteboard())) {
613 _(
"Some units have movement left. Do you really want to end your turn?"),
614 message::yes_no_buttons);
620 else if(
prefs::get().green_confirm() && unmoved_units(side_num, pc_.get_units(), board(), pc_.get_whiteboard())) {
622 _(
"Some units have not moved. Do you really want to end your turn?"),
623 message::yes_no_buttons);
631 if(pc_.get_whiteboard() && !pc_.get_whiteboard()->allow_end_turn()) {
638 void menu_handler::goto_leader(
int side_num)
641 if(
i != pc_.get_units().end() &&
i->is_visible_to_team(gui_->viewing_team(),
false)) {
646 void menu_handler::unit_description()
649 if(un != pc_.get_units().end()) {
657 if(pc_.get_map().on_board(
loc) ==
false || gui_->shrouded(
loc)) {
666 void menu_handler::rename_unit()
669 if(un == pc_.get_units().end() || gui_->viewing_team().side() != un->side()) {
673 if(un->unrenamable()) {
677 std::string name = un->name();
678 const std::string title(
_(
"Rename Unit"));
679 const std::string
label(
_(
"Name:"));
681 if(edit_text::execute(title,
label, name)) {
684 gui_->invalidate_unit();
690 const mouse_handler& mousehandler = pc_.get_mouse_handler_base();
691 const bool see_all = gui_->show_everything() || (pc_.is_replay() && pc_.get_replay_controller()->see_all());
694 if(res != pc_.get_units().end()) {
705 typedef std::tuple<const unit_type*, unit_race::GENDER, std::string> type_gender_variation;
714 type_gender_variation choose_unit()
718 const auto& create_dlg = units_dialog::build_create_dialog(types_list);
720 for (std::size_t
i = 0;
i < types_list.size();
i++) {
721 if (types_list[
i]->
id() == last_created_unit) {
722 create_dlg->set_selected_index(
i);
723 create_dlg->set_gender(last_gender);
724 create_dlg->set_variation(last_variation);
730 if (!create_dlg->show() && !create_dlg->is_selected()) {
734 const unit_type* ut = types_list[create_dlg->get_selected_index()];
735 last_created_unit = ut->
id();
736 last_gender = create_dlg->gender();
737 last_variation = create_dlg->variation();
740 if (create_dlg->is_selected()) {
741 info = type_gender_variation(ut, last_gender, last_variation);
743 ERR_NG <<
"Create unit dialog returned nonexistent or unusable unit_type id.";
754 void create_and_place(
758 const std::string& variation =
"")
766 "variation", variation,
783 if(
const auto& [
type, gender, variation] = choose_unit();
type !=
nullptr) {
784 create_and_place(destination, *
type, gender, variation);
792 if(
i == pc_.get_units().end()) {
793 if(!pc_.get_map().is_village(
loc)) {
801 if(
team >
static_cast<int>(pc_.get_teams().size())) {
806 int side =
i->side();
808 if(side >
static_cast<int>(pc_.get_teams().size())) {
813 if(pc_.get_map().is_village(
loc)) {
825 void menu_handler::label_terrain(
mouse_handler& mousehandler,
bool team_only)
828 if(pc_.get_map().on_board(
loc) ==
false) {
833 std::string
label = old_label ? old_label->
text() :
"";
835 if(edit_label::execute(
label, team_only)) {
836 std::string team_name;
840 team_name = gui_->labels().team_name();
844 const terrain_label* res = gui_->labels().set_label(
loc,
label, gui_->viewing_team_index(), team_name, color);
851 void menu_handler::clear_labels()
853 if(!board().is_observer()) {
856 _(
"Are you sure you want to clear map labels?"),
857 message::yes_no_buttons
861 std::string viewing_team = gui_->viewing_team().team_name();
862 gui_->labels().clear(viewing_team,
false);
868 void menu_handler::label_settings()
870 if(label_settings::execute(board())) {
871 gui_->labels().recalculate_labels();
878 if(
i == pc_.get_units().end() || !
i->move_interrupted()) {
880 if(
i == pc_.get_units().end() || !
i->move_interrupted()) {
884 move_unit_to_loc(
i,
i->get_interrupted_move(),
true, side_num, mousehandler);
887 void menu_handler::move_unit_to_loc(
894 assert(ui != pc_.get_units().end());
898 if(route.
steps.empty()) {
902 assert(route.
steps.front() == ui->get_location());
904 gui_->set_route(&route);
905 gui_->unhighlight_reach();
908 LOG_NG <<
"move_unit_to_loc " << route.
steps.front() <<
" to " << route.
steps.back();
913 gui_->set_route(
nullptr);
914 gui_->invalidate_game_status();
923 bool wait_blocker_move =
true;
924 std::set<map_location> fully_moved;
927 bool blocked_unit =
false;
930 blocked_unit =
false;
931 for(
auto&
unit : pc_.get_units()) {
939 if(goto_loc == current_loc) {
944 if(!pc_.get_map().on_board(goto_loc)) {
949 if(fully_moved.count(current_loc)) {
955 if(route.
steps.size() <= 1) {
956 fully_moved.insert(current_loc);
962 pathfind::marked_route::mark_map::const_iterator
w = route.
marks.begin();
963 for(;
w != route.
marks.end(); ++
w) {
964 if(
w->second.turns == 1) {
965 next_stop =
w->first;
970 if(next_stop == current_loc) {
971 fully_moved.insert(current_loc);
977 if(pc_.get_units().count(next_stop)) {
979 if(wait_blocker_move)
983 gui_->set_route(&route);
986 LOG_NG <<
"execute goto from " << route.
steps.front() <<
" to " << route.
steps.back();
993 wait_blocker_move =
true;
997 if(!change && wait_blocker_move) {
999 wait_blocker_move =
false;
1002 }
while(change && blocked_unit);
1005 gui_->set_route(
nullptr);
1006 gui_->invalidate_game_status();
1009 void menu_handler::toggle_ellipses()
1012 gui_->invalidate_all();
1015 void menu_handler::toggle_grid()
1018 gui_->invalidate_all();
1021 void menu_handler::unit_hold_position(
mouse_handler& mousehandler,
int side_num)
1024 if(un != pc_.get_units().end() && un->side() == side_num && un->movement_left() >= 0) {
1025 un->toggle_hold_position();
1030 if(un->hold_position()) {
1039 if(un != pc_.get_units().end() && un->side() == side_num && un->movement_left() >= 0) {
1040 un->toggle_user_end_turn();
1045 if(un->user_end_turn()) {
1057 void menu_handler::search()
1059 std::ostringstream
msg;
1061 if(last_search_hit_.valid()) {
1062 msg <<
" [" << last_search_ <<
"]";
1068 bool menu_handler::do_speak()
1072 return chat_handler::do_speak(
1073 textbox_info_.box()->text(), textbox_info_.check() !=
nullptr ? textbox_info_.check()->checked() :
false);
1076 void menu_handler::add_chat_message(
const std::chrono::system_clock::time_point& time,
1077 const std::string& speaker,
1082 gui_->get_chat_manager().add_chat_message(time, speaker, side,
message,
type,
false);
1109 using chmap::get_commands_list;
1110 using chmap::command_failed;
1116 chat_command_handler::command_handler
h,
1117 const std::string&
help =
"",
1118 const std::string& usage =
"",
1119 const std::string& flags =
"")
1121 chmap::register_command(cmd,
h,
help, usage, flags +
"N");
1126 chmap::register_alias(to_cmd, cmd);
1131 return chmap::get_arg(
i);
1136 return chmap::get_cmd();
1141 return chmap::get_data(
n);
1145 using chmap::register_command;
1146 using chmap::register_alias;
1148 using chmap::is_enabled;
1149 using chmap::command_failed_need_arg;
1157 void do_controller();
1159 void do_foreground();
1162 void do_benchmark();
1164 void do_save_quit();
1166 void do_ignore_replay_errors();
1168 void do_next_level();
1169 void do_choose_level();
1171 void do_turn_limit();
1175 void do_unsafe_lua();
1177 void do_set_alias();
1181 void do_control_dialog();
1186 void do_undiscover();
1192 void do_toggle_draw_coordinates();
1193 void do_toggle_draw_terrain_codes();
1194 void do_toggle_draw_num_of_bitmaps();
1195 void do_toggle_whiteboard();
1196 void do_whiteboard_options();
1200 return _(
"(D) — debug only, (N) — network only, (A) — admin only");
1203 using chat_command_handler::get_command_flags_description;
1206 std::string space(
" ");
1207 return (
c.has_flag(
'D') ? space +
_(
"(debug command)") :
"")
1208 + (
c.has_flag(
'N') ? space +
_(
"(network only)") :
"")
1209 + (
c.has_flag(
'A') ? space +
_(
"(admin only)") :
"")
1210 + (
c.has_flag(
'S') ? space +
_(
"(not during other events)") :
"");
1213 using map::is_enabled;
1217 || (
c.has_flag(
'N') && !menu_handler_.pc_.is_networked_mp())
1224 menu_handler_.add_chat_message(std::chrono::system_clock::now(), title, 0,
message);
1229 chat_command_handler::init_map();
1231 chmap::get_command(
"log")->flags =
"";
1232 chmap::get_command(
"version")->flags =
"";
1233 chmap::get_command(
"ignore")->flags =
"";
1234 chmap::get_command(
"friend")->flags =
"";
1235 chmap::get_command(
"remove")->flags =
"";
1237 chmap::set_cmd_prefix(
":");
1238 chmap::set_cmd_flag(
true);
1240 register_command(
"refresh", &console_handler::do_refresh,
_(
"Refresh gui."));
1241 register_command(
"droid", &console_handler::do_droid,
_(
"Switch a side to/from AI control."),
1245 _(
"[<side> [on/off/full]]\n“on” = enable but retain vision, “full” = as if it’s controlled by another player"));
1246 register_command(
"terrain", &console_handler::do_terrain,
_(
"Change terrain type of current hex"),
1248 _(
"<terrain type> [both|base|overlay]"),
"DS");
1249 register_command(
"idle", &console_handler::do_idle,
_(
"Switch a side to/from idle state."),
1253 _(
"command_idle^[<side> [on/off]]"));
1254 register_command(
"theme", &console_handler::do_theme,
_(
"Change the in-game theme."));
1255 register_command(
"control", &console_handler::do_control,
1256 _(
"Assign control of a side to a different player or observer."),
_(
"<side> <nickname>"),
"N");
1257 register_command(
"controller", &console_handler::do_controller,
_(
"Query the controller status of a side."),
1259 register_command(
"clear", &console_handler::do_clear,
_(
"Clear chat history."));
1260 register_command(
"foreground", &console_handler::do_foreground,
_(
"Debug foreground terrain."),
"",
"D");
1262 "layers", &console_handler::do_layers,
_(
"Debug layers from terrain under the mouse."),
"",
"D");
1263 register_command(
"fps", &console_handler::do_fps,
_(
"Display and log fps (Frames Per Second)."));
1264 register_command(
"benchmark", &console_handler::do_benchmark,
_(
"Similar to the ‘fps’ command, but also forces everything to redraw instead of only things that have changed."));
1265 register_command(
"save", &console_handler::do_save,
_(
"Save game."));
1266 register_alias(
"save",
"w");
1267 register_command(
"quit", &console_handler::do_quit,
_(
"Quit game."));
1269 register_alias(
"quit",
"q!");
1270 register_command(
"save_quit", &console_handler::do_save_quit,
_(
"Save and quit."));
1271 register_alias(
"save_quit",
"wq");
1272 register_command(
"ignore_replay_errors", &console_handler::do_ignore_replay_errors,
_(
"Ignore replay errors."));
1273 register_command(
"nosaves", &console_handler::do_nosaves,
_(
"Disable autosaves."));
1274 register_command(
"next_level", &console_handler::do_next_level,
1275 _(
"Advance to the next scenario, or scenario identified by ‘id’"),
_(
"<id>"),
"DS");
1276 register_alias(
"next_level",
"n");
1277 register_command(
"choose_level", &console_handler::do_choose_level,
_(
"Choose next scenario"),
"",
"DS");
1278 register_alias(
"choose_level",
"cl");
1279 register_command(
"turn", &console_handler::do_turn,
1280 _(
"Change turn number (and time of day), or increase by one if no number is specified."),
_(
"[turn]"),
1282 register_command(
"turn_limit", &console_handler::do_turn_limit,
1283 _(
"Change turn limit, or turn the turn limit off if no number is specified or it’s −1."),
_(
"[limit]"),
1285 register_command(
"debug", &console_handler::do_debug,
_(
"Turn debug mode on."));
1286 register_command(
"nodebug", &console_handler::do_nodebug,
_(
"Turn debug mode off."),
"",
"D");
1288 "lua", &console_handler::do_lua,
_(
"Execute a Lua statement."),
_(
"<command>[;<command>...]"),
"DS");
1290 "unsafe_lua", &console_handler::do_unsafe_lua,
_(
"Grant higher privileges to Lua scripts."),
"",
"D");
1291 register_command(
"custom", &console_handler::do_custom,
_(
"Set the command used by the custom command hotkey"),
1292 _(
"<command>[;<command>...]"));
1293 register_command(
"give_control", &console_handler::do_control_dialog,
1294 _(
"Invoke a dialog allowing changing control of MP sides."),
"",
"N");
1295 register_command(
"inspect", &console_handler::do_inspect,
_(
"Launch the gamestate inspector"),
"",
"D");
1297 "alias", &console_handler::do_set_alias,
_(
"Set or show alias to a command"),
_(
"<name>[=<command>]"));
1299 "set_var", &console_handler::do_set_var,
_(
"Set a scenario variable."),
_(
"<var>=<value>"),
"DS");
1300 register_command(
"show_var", &console_handler::do_show_var,
_(
"Show a scenario variable."),
_(
"<var>"),
"D");
1301 register_command(
"unit", &console_handler::do_unit,
1303 _(
"Modify a unit variable. (Only top level keys are supported, and advances=<number>.)"),
1304 _(
"<var>=<value>"),
"DS");
1310 register_command(
"discover", &console_handler::do_discover,
_(
"Discover all units in help."),
"");
1311 register_command(
"undiscover", &console_handler::do_undiscover,
_(
"‘Undiscover’ all units in help."),
"");
1312 register_command(
"create", &console_handler::do_create,
_(
"Create a unit."),
_(
"<unit type id>"),
"DS");
1313 register_command(
"fog", &console_handler::do_fog,
_(
"Toggle fog for the current player."),
"",
"DS");
1314 register_command(
"shroud", &console_handler::do_shroud,
_(
"Toggle shroud for the current player."),
"",
"DS");
1315 register_command(
"gold", &console_handler::do_gold,
_(
"Give gold to the current player."),
_(
"<amount>"),
"DS");
1316 register_command(
"throw", &console_handler::do_event,
_(
"Fire a game event."),
_(
"<event name>"),
"DS");
1317 register_alias(
"throw",
"fire");
1318 register_command(
"show_coordinates", &console_handler::do_toggle_draw_coordinates,
1319 _(
"Toggle overlaying of x,y coordinates on hexes."));
1320 register_alias(
"show_coordinates",
"sc");
1321 register_command(
"show_terrain_codes", &console_handler::do_toggle_draw_terrain_codes,
1322 _(
"Toggle overlaying of terrain codes on hexes."));
1323 register_alias(
"show_terrain_codes",
"tc");
1324 register_command(
"show_num_of_bitmaps", &console_handler::do_toggle_draw_num_of_bitmaps,
1325 _(
"Toggle overlaying of number of bitmaps on hexes."));
1326 register_alias(
"show_num_of_bitmaps",
"bn");
1327 register_command(
"whiteboard", &console_handler::do_toggle_whiteboard,
_(
"Toggle planning mode."));
1328 register_alias(
"whiteboard",
"wb");
1330 "whiteboard_options", &console_handler::do_whiteboard_options,
_(
"Access whiteboard options dialog."));
1331 register_alias(
"whiteboard_options",
"wbo");
1333 if(
auto alias_list =
prefs::get().get_alias()) {
1334 for(
const auto& [key, value] : alias_list->attribute_range()) {
1335 register_alias(value, key);
1345 void menu_handler::send_chat_message(
const std::string&
message,
bool allies_only)
1350 auto now = std::chrono::system_clock::now();
1353 const int side = board().
is_observer() ? 0 : gui_->viewing_team().side();
1354 if(!board().is_observer()) {
1361 if(board().is_observer()) {
1364 cfg[
"to_sides"] = gui_->viewing_team().allied_human_teams();
1370 add_chat_message(now,
cfg[
"id"], side,
message,
1374 void menu_handler::do_search(
const std::string& new_search)
1376 if(new_search.empty() ==
false && new_search != last_search_)
1377 last_search_ = new_search;
1379 if(last_search_.empty())
1385 std::vector<std::string> args =
utils::split(last_search_,
',');
1386 if(args.size() == 2) {
1388 x = lexical_cast_default<int>(args[0], 0) - 1;
1389 y = lexical_cast_default<int>(args[1], 0) - 1;
1390 if(x >= 0 && x < pc_.get_map().w() && y >= 0 && y < pc_.get_map().h()) {
1403 loc.
x = (
loc.
x + 1) % pc_.get_map().w();
1405 loc.
y = (
loc.
y + 1) % pc_.get_map().h();
1408 if(!gui_->shrouded(
loc)) {
1411 const std::string& label_text =
label->text().str();
1417 if(!gui_->fogged(
loc)) {
1419 if(ui != pc_.get_units().end()) {
1420 const std::string&
unit_name = ui->name();
1422 if(!gui_->viewing_team().is_enemy(ui->side())
1423 || !ui->invisible(ui->get_location())) {
1435 last_search_hit_ =
loc;
1437 gui_->highlight_hex(
loc);
1442 symbols[
"search"] = last_search_;
1443 const std::string
msg =
VGETTEXT(
"Could not find label or unit "
1444 "containing the string ‘$search’.",
1450 void menu_handler::do_command(
const std::string& str)
1456 std::vector<std::string> menu_handler::get_commands_list()
1467 void console_handler::do_refresh()
1472 menu_handler_.gui_->create_buttons();
1473 menu_handler_.gui_->queue_rerender();
1476 void console_handler::do_droid()
1479 const std::string side_s = get_arg(1);
1480 std::string action = get_arg(2);
1481 std::transform(action.begin(), action.end(), action.begin(), tolower);
1483 const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1484 const bool is_your_turn = menu_handler_.pc_.current_side() ==
static_cast<int>(menu_handler_.gui_->viewing_team().side());
1485 team&
team = menu_handler_.board().get_team(side);
1488 symbols[
"side"] = std::to_string(side);
1490 if(side < 1 || side > menu_handler_.pc_.get_teams().size()) {
1491 command_failed(
VGETTEXT(
"Can’t droid invalid side: ‘$side’.", symbols));
1494 command_failed(
VGETTEXT(
"Can’t droid networked side: ‘$side’.", symbols));
1497 bool changed =
false;
1504 if(action ==
"on") {
1505 if(is_ai && !is_your_turn) {
1506 command_failed(
_(
"It is not allowed to change a side from AI to human control when it’s not your turn."));
1509 if(!is_human || !is_droid) {
1514 menu_handler_.pc_.send_to_wesnothd(
config {
"change_controller",
config {
"side", side,
"player",
prefs::get().
login(),
"to", side_controller::human}});
1516 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ controller is now controlled by: AI.", symbols));
1518 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ is already droided.", symbols));
1520 }
else if(action ==
"off") {
1521 if(is_ai && !is_your_turn) {
1522 command_failed(
_(
"It is not allowed to change a side from AI to human control when it’s not your turn."));
1525 if(!is_human || !is_proxy_human) {
1530 menu_handler_.pc_.send_to_wesnothd(
config {
"change_controller",
config {
"side", side,
"player",
prefs::get().
login(),
"to", side_controller::human}});
1532 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ controller is now controlled by: human.", symbols));
1534 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ is already not droided.", symbols));
1536 }
else if(action ==
"full") {
1538 command_failed(
_(
"It is not allowed to change a side from human to AI control when it’s not your turn."));
1541 if(!is_ai || !is_droid) {
1545 if(is_human || is_proxy_human) {
1546 menu_handler_.pc_.send_to_wesnothd(
config {
"change_controller",
config {
"side", side,
"player",
prefs::get().
login(),
"to", side_controller::ai}});
1548 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ controller is now fully controlled by: AI.", symbols));
1550 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ is already fully AI controlled.", symbols));
1552 }
else if(action ==
"") {
1553 if(is_ai && !is_your_turn) {
1554 command_failed(
_(
"It is not allowed to change a side from AI to human control when it’s not your turn."));
1557 if(is_ai || is_droid) {
1562 menu_handler_.pc_.send_to_wesnothd(
config {
"change_controller",
config {
"side", side,
"player",
prefs::get().
login(),
"to", side_controller::human}});
1564 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ controller is now controlled by: human.", symbols));
1570 menu_handler_.pc_.send_to_wesnothd(
config {
"change_controller",
config {
"side", side,
"player",
prefs::get().
login(),
"to", side_controller::human}});
1572 print(get_cmd(),
VGETTEXT(
"Side ‘$side’ controller is now controlled by: AI.", symbols));
1575 print(get_cmd(),
VGETTEXT(
"Invalid action provided for side ‘$side’. Valid actions are: on, off, full.", symbols));
1578 if(team_num_ == side && changed) {
1580 psc->set_player_type_changed();
1584 command_failed(
VGETTEXT(
"Side ‘$side’ is not a human or AI player.", symbols));
1587 menu_handler_.textbox_info_.close();
1590 void console_handler::do_terrain()
1594 const std::string mode_str = get_arg(2);
1604 "mode_str", mode_str,
1609 void console_handler::do_idle()
1612 const std::string side_s = get_arg(1);
1613 const std::string action = get_arg(2);
1615 const unsigned int side = side_s.empty() ? team_num_ : lexical_cast_default<unsigned int>(side_s);
1616 team&
team = menu_handler_.board().get_team(side);
1618 if(side < 1 || side > menu_handler_.pc_.get_teams().size()) {
1620 symbols[
"side"] = side_s;
1621 command_failed(
VGETTEXT(
"Can’t idle invalid side: ‘$side’.", symbols));
1625 symbols[
"side"] = std::to_string(side);
1626 command_failed(
VGETTEXT(
"Can’t idle networked side: ‘$side’.", symbols));
1630 symbols[
"side"] = std::to_string(side);
1631 command_failed(
VGETTEXT(
"Can’t idle local ai side: ‘$side’.", symbols));
1634 if(
team.
is_idle() ? action ==
" on" : action ==
" off") {
1639 if(team_num_ == side) {
1641 psc->set_player_type_changed();
1645 menu_handler_.textbox_info_.close();
1648 void console_handler::do_theme()
1653 void console_handler::do_control()
1656 if(!menu_handler_.pc_.is_networked_mp()) {
1660 const std::string side = get_arg(1);
1661 const std::string player = get_arg(2);
1662 if(player.empty()) {
1663 command_failed_need_arg(2);
1667 unsigned int side_num;
1669 side_num = lexical_cast<unsigned int>(side);
1671 const auto& teams = menu_handler_.pc_.get_teams();
1674 if(it_t == teams.end()) {
1676 symbols[
"side"] = side;
1677 command_failed(
VGETTEXT(
"Can’t change control of invalid side: ‘$side’.", symbols));
1680 side_num = it_t->side();
1684 if(side_num < 1 || side_num > menu_handler_.pc_.get_teams().size()) {
1686 symbols[
"side"] = side;
1687 command_failed(
VGETTEXT(
"Can’t change control of out-of-bounds side: ‘$side’.", symbols));
1691 menu_handler_.request_control_change(side_num, player);
1692 menu_handler_.textbox_info_.close();
1695 void console_handler::do_controller()
1697 const std::string side = get_arg(1);
1698 unsigned int side_num;
1700 side_num = lexical_cast<unsigned int>(side);
1703 symbols[
"side"] = side;
1704 command_failed(
VGETTEXT(
"Can’t query control of invalid side: ‘$side’.", symbols));
1708 if(side_num < 1 || side_num > menu_handler_.pc_.get_teams().size()) {
1710 symbols[
"side"] = side;
1711 command_failed(
VGETTEXT(
"Can’t query control of out-of-bounds side: ‘$side’.", symbols));
1716 if(!menu_handler_.board().get_team(side_num).is_proxy_human()) {
1720 if(menu_handler_.board().get_team(side_num).is_network()) {
1721 report +=
" (networked)";
1724 print(get_cmd(), report);
1727 void console_handler::do_clear()
1729 menu_handler_.gui_->get_chat_manager().clear_chat_messages();
1732 void console_handler::do_foreground()
1735 menu_handler_.gui_->invalidate_all();
1738 void console_handler::do_layers()
1740 display& disp = *(menu_handler_.gui_);
1754 if(menu_handler_.pc_.get_map().on_board_with_border(
loc)) {
1755 terrain_layers::display(disp,
loc);
1759 void console_handler::do_fps()
1764 void console_handler::do_benchmark()
1769 void console_handler::do_save()
1771 menu_handler_.pc_.do_consolesave(get_data());
1774 void console_handler::do_save_quit()
1780 void console_handler::do_quit()
1785 void console_handler::do_ignore_replay_errors()
1790 void console_handler::do_nosaves()
1795 void console_handler::do_next_level()
1800 void console_handler::do_choose_level()
1802 std::string
tag = menu_handler_.pc_.get_classification().get_tagname();
1803 std::vector<std::string> options;
1805 if(
tag !=
"multiplayer") {
1807 const std::string&
id = sc[
"id"];
1808 options.push_back(
id);
1809 if(
id == menu_handler_.gamedata().next_scenario()) {
1816 std::string scenario_id = menu_handler_.pc_.get_mp_settings().mp_scenario;
1817 if(
auto this_scenario = menu_handler_.game_config_.find_child(
tag,
"id", scenario_id)) {
1818 std::string addon_id = this_scenario[
"addon_id"].str();
1820 if(sc[
"addon_id"] == addon_id) {
1821 std::string
id = sc[
"id"];
1822 options.push_back(
id);
1823 if(
id == menu_handler_.gamedata().next_scenario()) {
1830 std::sort(options.begin(), options.end());
1831 int choice = std::distance(options.begin(), std::lower_bound(options.begin(), options.end(), next));
1843 if(std::size_t(choice) < options.size()) {
1848 void console_handler::do_turn()
1850 tod_manager& tod_man = menu_handler_.gamestate().tod_manager_;
1852 int turn = tod_man.
turn() + 1;
1853 const std::string&
data = get_data();
1855 turn = lexical_cast_default<int>(
data, 1);
1860 void console_handler::do_turn_limit()
1862 int limit = get_data().empty() ? -1 : lexical_cast_default<int>(get_data(), 1);
1866 void console_handler::do_debug()
1869 print(get_cmd(),
_(
"Debug mode activated!"));
1872 command_failed(
_(
"Debug mode not available in network games"));
1876 void console_handler::do_nodebug()
1879 print(get_cmd(),
_(
"Debug mode deactivated!"));
1884 void console_handler::do_lua()
1886 if(!menu_handler_.gamestate().lua_kernel_) {
1893 void console_handler::do_unsafe_lua()
1895 if(!menu_handler_.gamestate().lua_kernel_) {
1900 _(
"Executing Lua code in in this manner opens your computer to potential security breaches from any "
1901 "malicious add-ons or other programs you may have installed.\n\n"
1902 "Do not continue unless you really know what you are doing."), message::ok_cancel_buttons);
1905 print(get_cmd(),
_(
"Unsafe mode enabled!"));
1906 menu_handler_.gamestate().lua_kernel_->load_package();
1910 void console_handler::do_custom()
1915 void console_handler::do_set_alias()
1917 const std::string
data = get_data();
1919 const std::string alias(
data.begin(), j);
1920 if(j !=
data.end()) {
1921 const std::string command(j + 1,
data.end());
1922 if(!command.empty()) {
1923 register_alias(command, alias);
1927 register_alias(alias, alias);
1935 const std::string command = chmap::get_actual_cmd(alias);
1936 print(get_cmd(),
"'" + alias +
"'" +
" = " +
"'" + command +
"'");
1940 void console_handler::do_set_var()
1942 const std::string
data = get_data();
1944 command_failed_need_arg(1);
1949 if(j !=
data.end()) {
1950 const std::string name(
data.begin(), j);
1951 const std::string value(j + 1,
data.end());
1954 command_failed(
_(
"Variable not found"));
1958 void console_handler::do_show_var()
1963 void console_handler::do_inspect()
1966 gamestate_inspector::display(
1970 void console_handler::do_control_dialog()
1972 mp_change_control::display(menu_handler_);
1975 void console_handler::do_unit()
1983 if(
i == menu_handler_.pc_.get_units().end()) {
1985 symbols[
"unit"] = get_arg(1);
1987 "Debug command ‘unit: $unit’ failed: no unit selected or hovered over.",
1993 const std::string
data = get_data(1);
1995 if(parameters.size() < 2) {
1999 if(parameters[0] ==
"alignment") {
2003 symbols[
"alignment"] = get_arg(1);
2005 "Invalid alignment: ‘$alignment’, needs to be one of lawful, neutral, chaotic, or liminal.",
2015 "name", parameters[0],
2016 "value", parameters[1],
2021 void console_handler::do_discover()
2023 for(
const unit_type_data::unit_type_map::value_type&
i :
unit_types.
types()) {
2028 void console_handler::do_undiscover()
2031 _(
"Do you wish to clear all of your discovered units from help?"), message::yes_no_buttons);
2038 void console_handler::do_create()
2042 if(menu_handler_.pc_.get_map().on_board(
loc)) {
2045 command_failed(
_(
"Invalid unit type"));
2050 create_and_place(
loc, *ut);
2052 command_failed(
_(
"Invalid location"));
2056 void console_handler::do_fog()
2061 void console_handler::do_shroud()
2066 void console_handler::do_gold()
2071 void console_handler::do_event()
2076 void console_handler::do_toggle_draw_coordinates()
2079 menu_handler_.gui_->invalidate_all();
2081 void console_handler::do_toggle_draw_terrain_codes()
2084 menu_handler_.gui_->invalidate_all();
2087 void console_handler::do_toggle_draw_num_of_bitmaps()
2090 menu_handler_.gui_->invalidate_all();
2093 void console_handler::do_toggle_whiteboard()
2095 if(
const std::shared_ptr<wb::manager>& whiteb = menu_handler_.pc_.get_whiteboard()) {
2096 whiteb->set_active(!whiteb->is_active());
2097 if(whiteb->is_active()) {
2098 print(get_cmd(),
_(
"Planning mode activated!"));
2099 whiteb->print_help_once();
2101 print(get_cmd(),
_(
"Planning mode deactivated!"));
2106 void console_handler::do_whiteboard_options()
2108 if(menu_handler_.pc_.get_whiteboard()) {
2109 menu_handler_.pc_.get_whiteboard()->options_dlg();
2118 add_chat_message(std::chrono::system_clock::now(),
"wfl", 0, result.
to_debug_string());
2125 void menu_handler::user_command()
2130 void menu_handler::request_control_change(
int side_num,
const std::string& player)
2132 std::string side = std::to_string(side_num);
2133 if(board().get_team(side_num).is_local_human() && player ==
prefs::get().login()) {
2139 pc_.send_to_wesnothd(
config {
"change_controller",
config {
"side", side,
"player", player}});
2143 void menu_handler::custom_command()
2146 do_command(command);
2150 void menu_handler::ai_formula()
2152 if(!pc_.is_networked_mp()) {
2157 void menu_handler::clear_messages()
2159 gui_->get_chat_manager().clear_chat_messages();
2164 pc_.send_to_wesnothd(
cfg);
Various functions related to moving units.
A config object defines a single node in a WML file, with access to child nodes.
child_itors child_range(config_key_type key)
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,...
bool is_observer() const
Check if we are an observer in this game.
can_move_result unit_can_move(const unit &u) const
Work out what u can do - this does not check which player's turn is currently active,...
virtual const unit_map & units() const =0
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
const team & viewing_team() const
@ DEBUG_COORDINATES
Overlays x,y coords on tiles.
@ DEBUG_BENCHMARK
Toggle to continuously redraw the whole map.
@ DEBUG_NUM_BITMAPS
Overlays number of bitmaps on tiles.
@ DEBUG_FOREGROUND
Separates background and foreground terrain layers.
@ DEBUG_TERRAIN_CODES
Overlays terrain codes on tiles.
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.
const display_context & context() const
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
virtual std::string get_arg(unsigned i) const
std::string get_flags_description() const
virtual std::string get_cmd() const
console_handler(menu_handler &menu_handler)
void print(const std::string &title, const std::string &message)
virtual void register_alias(const std::string &to_cmd, const std::string &cmd)
bool is_enabled(const chmap::command &c) const
virtual std::string get_data(unsigned n=1) const
menu_handler & menu_handler_
std::string get_command_flags_description(const chmap::command &c) const
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="")
map_command_handler< console_handler > chmap
const unsigned int team_num_
bool dispatch(std::string cmd)
std::vector< std::string > get_commands_list() const
game_board & board() const
gui::floating_textbox & get_textbox()
gui::floating_textbox textbox_info_
void show_statistics(int side_num)
void recruit(int side_num, const map_location &last_hex)
game_state & gamestate() const
void disable_units_highlight()
Use this to disable hovering an unit from highlighting its movement range.
void set_current_paths(const pathfind::paths &new_paths)
pathfind::marked_route get_route(const unit *un, map_location go_to, const team &team) const
map_location get_selected_hex() const
const map_location hovered_hex() const
Uses SDL and game_display::hex_clicked_on to fetch the hex the mouse is hovering, if applicable.
const map_location & get_last_hex() const
bool hex_hosts_unit(const map_location &hex) const
Unit exists on the hex, no matter if friend or foe.
void cycle_units(const bool browse, const bool reverse=false)
unit_map::iterator find_visible_unit(const map_location &loc, const team ¤t_team, bool see_all=false)
void scroll_to_leader(int side, SCROLL_TYPE scroll_type=ONSCREEN, bool force=true)
Scrolls to the leader of a certain side.
virtual const std::set< std::string > & observers() const override
virtual void select_hex(map_location hex) override
Function to display a location as selected.
std::string write() const
file_dialog & set_extension(const std::string &value)
Sets allowed file extensions for file names in save mode.
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
file_dialog & set_save_mode(bool value)
Sets the dialog's behavior on non-existent file name inputs.
std::string path() const
Gets the current file selection.
Main class to show messages to the user.
bool show(const unsigned auto_close_time=0)
Shows the window.
int selected_index() const
Returns the selected item index after displaying.
void set_selected_index(int index)
Sets the initially selected item index (-1 by default).
void show(gui::TEXTBOX_MODE mode, const std::string &label, const std::string &check_label, bool checked, game_display &gui)
std::vector< team > & get_teams()
void show_objectives() const
statistics_t & statistics()
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
const gamemap & get_map() const
void refresh_objectives() const
Reevaluate [show_if] conditions and build a new objectives string.
void notify_event(const std::string &name, const config &data)
static plugins_manager * get()
std::set< std::string > & encountered_units()
void add_alias(const std::string &alias, const std::string &command)
void set_message_private(bool value)
void set_show_fps(bool value)
bool empty() const
Is it empty?
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
static config get_auto_shroud(bool turned_on)
Records that the player has toggled automatic shroud updates.
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
static config get_update_shroud()
Records that the player has manually updated fog/shroud.
void add_rename(const std::string &name, const map_location &loc)
void add_label(const terrain_label *)
void speak(const config &cfg)
void clear_labels(const std::string &, bool)
static synced_state get_synced_state()
static bool run_and_throw(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
This class stores all the data for a single 'side' (in game nomenclature).
bool is_proxy_human() const
bool auto_shroud_updates() const
const std::string & team_name() const
bool is_local_human() const
void set_last_recruit(const std::string &u_type)
const std::string & save_id() const
static color_t get_side_color(int side)
void increment_action_bonus_count()
recall_list_manager & recall_list()
const std::string & last_recruit() const
To store label data Class implements logic for rendering.
const t_string & text() const
Container associating units to locations.
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.
const std::vector< const unit_type * > types_list() const
const unit_type_map & types() const
A single unit type that the player may recruit.
const std::string & id() const
The id for this unit_type.
This class represents a single unit of a specific type.
A variable-expanding proxy for the config class.
static vconfig empty_vconfig()
An object representing the state of the game, providing access to the map and basic information.
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Various functions related to the creation of units (recruits, recalls, and placed units).
static void print(std::stringstream &sstr, const std::string &queue, const std::string &id)
Contains the exception interfaces used to signal completion of a scenario, campaign or turn.
void throw_quit_game_exception()
int dispatch(lua_State *L)
static std::string _(const char *str)
bool user_end_turn() const
Check whether the user ended their turn.
const std::string & id() const
Gets this unit's id.
int side() const
The side this unit belongs to.
const t_string & name() const
Gets this unit's translatable display name.
const map_location & get_location() const
The current map location this unit is at.
bool has_moved() const
Checks if this unit has moved.
void set_goto(const map_location &new_goto)
Sets this unit's long term destination.
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
const map_location & get_goto() const
The map location to which this unit is moving over multiple turns, if any.
std::string label
What to show in the filter's drop-down list.
std::string id
Text to match against addon_info.tags()
Standard logging facilities (interface).
std::set< std::string > get_recruits(int side, const map_location &recruit_loc)
Gets the recruitable units from a side's leaders' personal recruit lists who can recruit on or from a...
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.
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's leaders' personal abilities to recall...
std::size_t move_unit_and_record(const std::vector< map_location > &steps, bool continued_move, bool *interrupted)
Wrapper around the other overload.
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.
auto serialize_timestamp(const std::chrono::system_clock::time_point &time)
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Handling of system events.
std::string get_legacy_editor_dir()
const std::string map_extension
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
const color_t LABEL_COLOR
std::string private_message
Game configuration data as global variables.
bool ignore_replay_errors
const std::string observer_team_name
observer team name used for observer team chat
void set_debug(bool new_debug)
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.
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)
Shows a transient message to the user.
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.
retval
Default window/dialog return values.
@ OK
Dialog was closed with the OK button.
@ CANCEL
Dialog was closed with the CANCEL button.
void show_help(const std::string &show_topic)
Open the help browser, show topic with id show_topic.
void show_terrain_description(const terrain_type &t)
void show_unit_description(const unit &u)
void flush_cache()
Purges all image caches.
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
void send_to_server(const config &data)
Attempts to send given data to server if a connection is open.
bool logged_in_as_moderator()
Gets whether the currently logged-in user is a moderator.
game_events::manager * game_events
bool ci_search(const std::string &s1, const std::string &s2)
Case-insensitive search.
static std::string sgettext(const char *str)
std::string check_recruit_list(const std::string &type, int side_number, const map_location &target_hex)
std::string check_recruit_purse(int unit_cost, int current_purse, int investments)
int planned_gold_spent(int side_number)
std::tuple< std::string, map_location, map_location > validate_recruit_target(const std::string &type, int side_number, const map_location &target_hex)
Verifies that target_hex is a valid recruit location for the given side.
auto find(Container &container, const Value &value, const Projection &projection={})
@ STRIP_SPACES
REMOVE_EMPTY: remove empty elements.
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
std::map< std::string, t_string > string_map
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
std::shared_ptr< const unit > unit_const_ptr
const std::string & gender_string(unit_race::GENDER gender)
static config unit_name(const unit *u)
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
Thrown when a lexical_cast fails.
The basic class for representing 8-bit RGB or RGBA colour values.
An exception object used when an IO error occurs.
Encapsulates the map of the game.
static const map_location & null_location()
Structure which holds a single route and marks for special events.
std::vector< map_location > & steps
Object which contains all the possible locations a unit can move to, with associated best routes to t...
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Object which temporarily resets a unit's movement.
ONLY IF whiteboard is currently active, applies the planned unit map for the duration of the struct's...
Applies the planned unit map for the duration of the struct's life.
const std::string & gamedata
static map_location::direction n
unit_type_data unit_types
Various functions that implement the undoing (and redoing) of in-game commands.