diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 33d958f6aec..c651bb76555 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -39,6 +39,10 @@ Version 1.11.7 (check this) from just before feature freeze introduced a "carryo Another point raised in internal discussion is that it would probably be more useful if this feature applied before random map generation occurred, which when it was first implemented wasn't how it worked. Since there are many changes planned to this scenario generation code path, for instance the merging of mp and sp, and this feature adds a fair bit of complication, we decided to shelf it and perhaps rethink it later. [/section] +[section="play single move button in replay mode"] +A button that allows playing one move at a time in replay mode has been added. +[/section] + [section="Example section 2"] Example contents 2. [/section] diff --git a/changelog b/changelog index 0ba87a18cb3..d4d78565736 100644 --- a/changelog +++ b/changelog @@ -76,6 +76,8 @@ Version 1.13.0-dev: * Fixed bug #22045: Only blit neutral surfaces. * [item] images are now subject to local ToD lighting effects instead of just the current map-wide ToD lighting (bug #22215). + * Replays: + * Added a button that allows playing a single move in replay mode. * Units: * Increased the experience requirement for the Rami from 32 to 39 * Increased the experience requirement for the Saree from 56 to 64 diff --git a/data/themes/macros.cfg b/data/themes/macros.cfg index cdedef4ee83..d2f0d9a2970 100644 --- a/data/themes/macros.cfg +++ b/data/themes/macros.cfg @@ -465,10 +465,25 @@ yanchor=fixed [/action] [/add] + [add] + [action] + id=button-nextmove + ref=button-nextside + type=image + image=button_square/button_square_30 + overlay=icons/action/play_move_30 + title= _ "Next Move" + tooltip= _ "play single move" + items=replaynextmove + rect="+4,=,+23,=" + xanchor=fixed + yanchor=fixed + [/action] + [/add] [add] [menu] id=show-what - ref=button-nextside + ref=button-nextmove image=button_normal/button_H22 title= _ "Point of view" items=replayshowteam1,replayshoweach,replayshoweverything @@ -591,10 +606,24 @@ yanchor=fixed [/action] [/add] + [add] + [action] + id=button-nextmove + ref=button-nextside + type=image + image=classic/play-move + title= _ "Next Move" + tooltip= _ "play single move" + items=replaynextmove + rect="+4,=,+23,=" + xanchor=fixed + yanchor=fixed + [/action] + [/add] [add] [menu] id=show-what - ref=button-nextside + ref=button-nextmove image=classic/lite title= _ "Point of view" items=replayshowteam1,replayshoweach,replayshoweverything diff --git a/images/buttons/classic/play_move-active.png b/images/buttons/classic/play_move-active.png new file mode 100644 index 00000000000..514f56f58b8 Binary files /dev/null and b/images/buttons/classic/play_move-active.png differ diff --git a/images/buttons/classic/play_move-pressed.png b/images/buttons/classic/play_move-pressed.png new file mode 100644 index 00000000000..c51d64930e0 Binary files /dev/null and b/images/buttons/classic/play_move-pressed.png differ diff --git a/images/buttons/classic/play_move.png b/images/buttons/classic/play_move.png new file mode 100644 index 00000000000..5dd843f4b78 Binary files /dev/null and b/images/buttons/classic/play_move.png differ diff --git a/images/icons/action/play_move_30-active.png b/images/icons/action/play_move_30-active.png new file mode 100644 index 00000000000..0d1f3473101 Binary files /dev/null and b/images/icons/action/play_move_30-active.png differ diff --git a/images/icons/action/play_move_30-pressed.png b/images/icons/action/play_move_30-pressed.png new file mode 100644 index 00000000000..08c18bf1959 Binary files /dev/null and b/images/icons/action/play_move_30-pressed.png differ diff --git a/images/icons/action/play_move_30.png b/images/icons/action/play_move_30.png new file mode 100644 index 00000000000..cb6040bd53a Binary files /dev/null and b/images/icons/action/play_move_30.png differ diff --git a/players_changelog b/players_changelog index 3428e3143ca..cfe3b9f46f6 100644 --- a/players_changelog +++ b/players_changelog @@ -32,6 +32,9 @@ Version 1.13.0-dev: * Updated translations: Czech, French, German, Greek, Hungarian, Italian, Lithuanian, Scottish Gaelic, Slovak, Vietnamese. + * Replays: + * Added a button that allows playing a single move in replay mode. + * Units: * Increased the experience requirement for the Rami from 32 to 39. * Increased the experience requirement for the Saree from 56 to 64. diff --git a/src/hotkey/command_executor.cpp b/src/hotkey/command_executor.cpp index 0346a37d6a5..4e8c874aa61 100644 --- a/src/hotkey/command_executor.cpp +++ b/src/hotkey/command_executor.cpp @@ -209,6 +209,9 @@ bool command_executor::execute_command(const hotkey_command& cmd, int /*index*/ case HOTKEY_REPLAY_NEXT_SIDE: replay_next_side(); break; + case HOTKEY_REPLAY_NEXT_MOVE: + replay_next_move(); + break; case HOTKEY_REPLAY_SHOW_EVERYTHING: replay_show_everything(); break; diff --git a/src/hotkey/command_executor.hpp b/src/hotkey/command_executor.hpp index c2eaf8735a1..6f6b0edc055 100644 --- a/src/hotkey/command_executor.hpp +++ b/src/hotkey/command_executor.hpp @@ -84,6 +84,7 @@ public: virtual void stop_replay() {} virtual possible_end_play_signal replay_next_turn() { return boost::none; } virtual possible_end_play_signal replay_next_side() { return boost::none; } + virtual possible_end_play_signal replay_next_move() { return boost::none; } virtual void replay_show_everything() {} virtual void replay_show_each() {} virtual void replay_show_team1() {} diff --git a/src/hotkey/hotkey_command.cpp b/src/hotkey/hotkey_command.cpp index b6022524aae..67b6119b483 100644 --- a/src/hotkey/hotkey_command.cpp +++ b/src/hotkey/hotkey_command.cpp @@ -100,6 +100,7 @@ hotkey::hotkey_command_temp hotkey_list_[] = { { hotkey::HOTKEY_REPLAY_STOP, "stopreplay", N_("Stop Replay"), false, scope_game, "" }, { hotkey::HOTKEY_REPLAY_NEXT_TURN, "replaynextturn", N_("Next Turn"), false, scope_game, "" }, { hotkey::HOTKEY_REPLAY_NEXT_SIDE, "replaynextside", N_("Next Side"), false, scope_game, "" }, + { hotkey::HOTKEY_REPLAY_NEXT_MOVE, "replaynextmove", N_("Next Move"), false, scope_game, "" }, { hotkey::HOTKEY_REPLAY_SHOW_EVERYTHING, "replayshoweverything", N_("Full Map"), false, scope_game, "" }, { hotkey::HOTKEY_REPLAY_SHOW_EACH, "replayshoweach", N_("Each Team"), false, scope_game, "" }, { hotkey::HOTKEY_REPLAY_SHOW_TEAM1, "replayshowteam1", N_("Team 1"), false, scope_game, "" }, diff --git a/src/hotkey/hotkey_command.hpp b/src/hotkey/hotkey_command.hpp index 9b9baf469a6..41ee51bdca8 100644 --- a/src/hotkey/hotkey_command.hpp +++ b/src/hotkey/hotkey_command.hpp @@ -56,7 +56,7 @@ enum HOTKEY_COMMAND { // Replay HOTKEY_REPLAY_PLAY, HOTKEY_REPLAY_RESET, HOTKEY_REPLAY_STOP, HOTKEY_REPLAY_NEXT_TURN, - HOTKEY_REPLAY_NEXT_SIDE, HOTKEY_REPLAY_SHOW_EVERYTHING, + HOTKEY_REPLAY_NEXT_SIDE, HOTKEY_REPLAY_NEXT_MOVE, HOTKEY_REPLAY_SHOW_EVERYTHING, HOTKEY_REPLAY_SHOW_EACH, HOTKEY_REPLAY_SHOW_TEAM1, HOTKEY_REPLAY_SKIP_ANIMATION, diff --git a/src/replay.cpp b/src/replay.cpp index 29a9c5201a4..fd991b84cf4 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -715,7 +715,7 @@ static void show_oos_error_error_function(const std::string& message, bool /*hea replay::process_error(message); } -REPLAY_RETURN do_replay() +REPLAY_RETURN do_replay(bool one_move) { log_scope("do replay"); @@ -724,10 +724,10 @@ REPLAY_RETURN do_replay() } update_locker lock_update(resources::screen->video(),get_replay_source().is_skipping()); - return do_replay_handle(); + return do_replay_handle(one_move); } -REPLAY_RETURN do_replay_handle() +REPLAY_RETURN do_replay_handle(bool one_move) { //team ¤t_team = (*resources::teams)[side_num - 1]; @@ -909,6 +909,9 @@ REPLAY_RETURN do_replay_handle() we need to use the undo stack during replays in order to make delayed shroud updated work. */ synced_context::run_in_synced_context(commandname, data, true, !get_replay_source().is_skipping(), false,show_oos_error_error_function); + if (one_move) { + return REPLAY_FOUND_END_MOVE; + } } } diff --git a/src/replay.hpp b/src/replay.hpp index 97fa210354e..f3f5f677fd9 100644 --- a/src/replay.hpp +++ b/src/replay.hpp @@ -165,13 +165,14 @@ enum REPLAY_RETURN { REPLAY_RETURN_AT_END, REPLAY_FOUND_DEPENDENT, - REPLAY_FOUND_END_TURN + REPLAY_FOUND_END_TURN, + REPLAY_FOUND_END_MOVE }; //replays up to one turn from the recorder object //returns true if it got to the end of the turn without data running out -REPLAY_RETURN do_replay(); +REPLAY_RETURN do_replay(bool one_move = false); -REPLAY_RETURN do_replay_handle(); +REPLAY_RETURN do_replay_handle(bool one_move = false); class replay_network_sender { diff --git a/src/replay_controller.cpp b/src/replay_controller.cpp index 43c8b1fda16..7fb943c28ad 100644 --- a/src/replay_controller.cpp +++ b/src/replay_controller.cpp @@ -140,7 +140,8 @@ replay_controller::~replay_controller() void replay_controller::init(){ DBG_REPLAY << "in replay_controller::init()...\n"; - + + last_replay_action = REPLAY_FOUND_END_MOVE; //guarantee the cursor goes back to 'normal' at the end of the level const cursor::setter cursor_setter(cursor::NORMAL); init_replay_display(); @@ -211,6 +212,11 @@ gui::button* replay_controller::play_side_button() return gui_->find_action_button("button-nextside"); } +gui::button* replay_controller::play_move_button() +{ + return gui_->find_action_button("button-nextmove"); +} + void replay_controller::update_replay_ui() { //check if we have all buttons - if someone messed with theme then some buttons may be missing @@ -218,7 +224,7 @@ void replay_controller::update_replay_ui() if(!replay_ui_has_all_buttons()) { gui::button *play_b = play_button(), *stop_b = stop_button(), *reset_b = reset_button(), *play_turn_b = play_turn_button(), - *play_side_b = play_side_button(); + *play_side_b = play_side_button(), *play_move_b = play_move_button(); if(play_b) { play_b->enable(false); @@ -239,6 +245,10 @@ void replay_controller::update_replay_ui() if(play_side_b) { play_side_b->enable(false); } + + if (play_move_b) { + play_move_b->enable(false); + } } } @@ -251,6 +261,7 @@ void replay_controller::replay_ui_playback_should_start() reset_button()->enable(false); play_turn_button()->enable(false); play_side_button()->enable(false); + play_move_button()->enable(false); } void replay_controller::replay_ui_playback_should_stop() @@ -263,10 +274,12 @@ void replay_controller::replay_ui_playback_should_stop() reset_button()->enable(true); play_turn_button()->enable(true); play_side_button()->enable(true); + play_move_button()->enable(true); play_button()->release(); play_turn_button()->release(); play_side_button()->release(); + play_move_button()->release(); } else { reset_button()->enable(true); stop_button()->enable(false); @@ -377,16 +390,16 @@ possible_end_play_signal replay_controller::replay_next_turn(){ return boost::none; } -possible_end_play_signal replay_controller::replay_next_side(){ +possible_end_play_signal replay_controller::replay_next_move_or_side(bool one_move){ is_playing_ = true; replay_ui_playback_should_start(); - - HANDLE_END_PLAY_SIGNAL( play_side() ); + + HANDLE_END_PLAY_SIGNAL( play_move_or_side(one_move) ); while (current_team().is_empty()) { - HANDLE_END_PLAY_SIGNAL( play_side() ); + HANDLE_END_PLAY_SIGNAL( play_move_or_side(one_move) ); } - if (!skip_replay_ || !is_playing_) { + if ( (!skip_replay_ || !is_playing_) && (last_replay_action == REPLAY_FOUND_END_TURN) ){ gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false); } @@ -394,6 +407,15 @@ possible_end_play_signal replay_controller::replay_next_side(){ return boost::none; } +possible_end_play_signal replay_controller::replay_next_side(){ + return replay_next_move_or_side(false); +} + +possible_end_play_signal replay_controller::replay_next_move(){ + return replay_next_move_or_side(true); +} + + void replay_controller::process_oos(const std::string& msg) const { if (game_config::ignore_replay_errors) { @@ -499,9 +521,18 @@ possible_end_play_signal replay_controller::play_turn(){ return boost::none; } -//make only one side move -possible_end_play_signal replay_controller::play_side() { +possible_end_play_signal replay_controller::play_side() { + return play_move_or_side(false); +} + +possible_end_play_signal replay_controller::play_move() { + return play_move_or_side(true); +} + +//make only one side move +possible_end_play_signal replay_controller::play_move_or_side(bool one_move) { + DBG_REPLAY << "Status turn number: " << turn() << "\n"; DBG_REPLAY << "Replay_Controller turn number: " << current_turn_ << "\n"; DBG_REPLAY << "Player number: " << player_number_ << "\n"; @@ -510,7 +541,10 @@ possible_end_play_signal replay_controller::play_side() { if (!current_team().is_empty()) { statistics::reset_turn_stats(current_team().save_id()); - possible_end_play_signal signal = play_controller::init_side(true); + possible_end_play_signal signal = NULL; + if (last_replay_action == REPLAY_FOUND_END_TURN) { + signal = play_controller::init_side(true); + } if (signal) { switch (boost::apply_visitor(get_signal_type(), *signal) ) { @@ -526,10 +560,12 @@ possible_end_play_signal replay_controller::play_side() { DBG_REPLAY << "doing replay " << player_number_ << "\n"; // if have reached the end we don't want to execute finish_side_turn and finish_turn // becasue we might not have enough data to execute them (like advancements during turn_end for example) - + try { - if(do_replay() != REPLAY_FOUND_END_TURN) { - // We reached the end of teh replay without finding and end turn tag. + last_replay_action = do_replay(one_move); + if(last_replay_action != REPLAY_FOUND_END_TURN) { + //We reached the end of the replay without finding an end turn tag. + //REPLAY_FOUND_DEPENDENT here might indicate an OOS error return boost::none; } } catch(end_level_exception& e){ @@ -640,6 +676,7 @@ bool replay_controller::can_execute_command(const hotkey::hotkey_command& cmd, i case hotkey::HOTKEY_REPLAY_PLAY: case hotkey::HOTKEY_REPLAY_NEXT_TURN: case hotkey::HOTKEY_REPLAY_NEXT_SIDE: + case hotkey::HOTKEY_REPLAY_NEXT_MOVE: //we have one events_disabler when starting the replay_controller and a second when entering the synced context. return (events::commands_disabled <= 1 ) && !recorder.at_end(); default: diff --git a/src/replay_controller.hpp b/src/replay_controller.hpp index a54b4d7e31c..0da99e2c184 100644 --- a/src/replay_controller.hpp +++ b/src/replay_controller.hpp @@ -39,8 +39,10 @@ public: possible_end_play_signal play_replay(); void reset_replay(); void stop_replay(); + possible_end_play_signal replay_next_move_or_side(bool one_move); possible_end_play_signal replay_next_turn(); possible_end_play_signal replay_next_side(); + possible_end_play_signal replay_next_move(); void process_oos(const std::string& msg) const; void replay_show_everything(); void replay_show_each(); @@ -60,7 +62,9 @@ protected: private: void init(); possible_end_play_signal play_turn(); + possible_end_play_signal play_move_or_side(bool one_move = false); possible_end_play_signal play_side(); + possible_end_play_signal play_move(); void update_teams(); void update_gui(); void init_replay_display(); @@ -80,6 +84,7 @@ private: gui::button* reset_button(); gui::button* play_turn_button(); gui::button* play_side_button(); + gui::button* play_move_button(); bool replay_ui_has_all_buttons() { return play_button() && stop_button() && reset_button() && @@ -89,6 +94,7 @@ private: saved_game saved_game_start_; game_board gameboard_start_; tod_manager tod_manager_start_; + unsigned int last_replay_action; unsigned int current_turn_; bool is_playing_;