Improve the control reassignment dialog when a player leaves (#654)

This moves the options for giving control to an ally/observer to the top of the list, labels them more descriptively, and prevents the dialog from appearing during linger mode if there is no next scenario. Also, dismissing the dialog by pressing esc no longer produces a network error, but instead simply defaults to setting the side to idle.
This commit is contained in:
Lari Nieminen 2016-05-22 23:58:04 +03:00
parent 6f29960e95
commit 6a9554239e
2 changed files with 80 additions and 69 deletions

View file

@ -20,6 +20,8 @@ Version 1.13.4+dev:
horizontal UI resolutions < 1024 (bug #24455).
* Fixed ToD schedule progress indicator appearing behind other top bar items
on vertical UI resolutions < 600.
* Improved the dialog for choosing what to do when a player leaves in
multiplayer.
* WML engine:
* Fix some issues with [foreach]
* Fix some issues with backstab-like weapon specials

View file

@ -240,45 +240,50 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
int action = 0;
int first_observer_option_idx = 0;
int control_change_options = 0;
bool has_next_scenario = !resources::gamedata->next_scenario().empty() && resources::gamedata->next_scenario() != "null";
std::vector<std::string> observers;
std::vector<const team *> allies;
std::vector<std::string> options;
const team &tm = resources::gameboard->teams()[index];
for (const team &t : resources::gameboard->teams()) {
if (!t.is_enemy(side_drop) && !t.is_local_human() && !t.is_local_ai() && !t.is_network_ai() && !t.is_empty()
&& t.current_player() != tm.current_player()) {
allies.push_back(&t);
}
}
// We want to give host chance to decide what to do for side
{
if (!resources::controller->is_linger_mode() || has_next_scenario) {
utils::string_map t_vars;
options.push_back(_("Replace with AI"));
options.push_back(_("Replace with local player"));
options.push_back(_("Set side to idle"));
options.push_back(_("Save and abort game"));
//get all allies in as options to transfer control
for (const team *t : allies) {
//if this is an ally of the dropping side and it is not us (choose local player
//if you want that) and not ai or empty and if it is not the dropping side itself,
//get this team in as well
t_vars["player"] = t->current_player();
options.push_back(vgettext("Give control to their ally $player", t_vars));
control_change_options++;
}
first_observer_option_idx = options.size();
//get all observers in as options to transfer control
for (const std::string &ob : resources::screen->observers())
{
for (const std::string &ob : resources::screen->observers()) {
t_vars["player"] = ob;
options.push_back(vgettext("Replace with $player", t_vars));
options.push_back(vgettext("Give control to observer $player", t_vars));
observers.push_back(ob);
control_change_options++;
}
const team &tm = resources::gameboard->teams()[index];
//get all allies in as options to transfer control
for (const team &t : resources::gameboard->teams())
{
if (!t.is_enemy(side_drop) && !t.is_local_human() && !t.is_local_ai() && !t.is_network_ai() && !t.is_empty()
&& t.current_player() != tm.current_player())
{
//if this is an ally of the dropping side and it is not us (choose local player
//if you want that) and not ai or empty and if it is not the dropping side itself,
//get this team in as well
t_vars["player"] = t.current_player();
options.push_back(vgettext("Replace with $player", t_vars));
allies.push_back(&t);
}
}
options.push_back(_("Replace with AI"));
options.push_back(_("Replace with local player"));
options.push_back(_("Set side to idle"));
options.push_back(_("Save and abort game"));
t_vars["player"] = tm.current_player();
const std::string msg = vgettext("$player has left the game. What do you want to do?", t_vars);
@ -286,59 +291,63 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
dlg.set_single_button(true);
dlg.show(resources::screen->video());
action = dlg.selected_index();
// If esc was pressed, default to setting side to idle
if (action == -1) {
action = control_change_options + 2;
}
} else {
// Always set leaving side to idle if in linger mode and there is no next scenario
action = 2;
}
//make the player an AI, and redo this turn, in case
//it was the current player's team who has just changed into
//an AI.
switch(action) {
case 0:
resources::controller->on_not_observer();
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_AI);
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
case 1:
resources::controller->on_not_observer();
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_HUMAN);
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
case 2:
if (action < control_change_options) {
// Grant control to selected ally
{
// Server thinks this side is ours now so in case of error transferring side we have to make local state to same as what server thinks it is.
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_IDLE);
}
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
if (action < first_observer_option_idx) {
change_side_controller(side_drop, allies[action]->current_player());
} else {
change_side_controller(side_drop, observers[action - first_observer_option_idx]);
}
case 3:
//The user pressed "end game". Don't throw a network error here or he will get
//thrown back to the title screen.
do_save();
throw_quit_game_exception();
default:
if (action > 3) {
return restart ? PROCESS_RESTART_TURN : PROCESS_CONTINUE;
} else {
action -= control_change_options;
{
// Server thinks this side is ours now so in case of error transferring side we have to make local state to same as what server thinks it is.
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_IDLE);
}
//make the player an AI, and redo this turn, in case
//it was the current player's team who has just changed into
//an AI.
switch(action) {
case 0:
resources::controller->on_not_observer();
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_AI);
const size_t index = static_cast<size_t>(action - first_observer_option_idx);
if (index < observers.size()) {
change_side_controller(side_drop, observers[index]);
} else if (index < options.size() - 1) {
size_t i = index - observers.size();
change_side_controller(side_drop, allies[i]->current_player());
} else {
//This shouldnt be possible.
assert(false);
}
return restart ? PROCESS_RESTART_TURN : PROCESS_CONTINUE;
}
break;
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
case 1:
resources::controller->on_not_observer();
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_HUMAN);
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
case 2:
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_IDLE);
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
case 3:
//The user pressed "end game". Don't throw a network error here or he will get
//thrown back to the title screen.
do_save();
throw_quit_game_exception();
default:
break;
}
}
//Lua code currently catches this exception if this function was called from lua code
// in that case network::error doesn't end the game.
// but at least he sees this error message.
throw network::error("Network Error: A player left and you pressed Escape.");
}
// The host has ended linger mode in a campaign -> enable the "End scenario" button