commit
37b0b8dd69
10 changed files with 159 additions and 5 deletions
|
@ -78,6 +78,12 @@ Version 1.11.9+dev:
|
|||
to work correctly in subsequent scenarios of an mp campaign.
|
||||
* Partial fix for bug #21405: The abort option presented when a player
|
||||
disconnects from a networked game is now a "save and abort" option.
|
||||
* Partial fix for bug #21405: Sides may now be set in an "idle" state when
|
||||
a player disconnects from a network game. This does not give any player
|
||||
control or vision. To proceed with the game, the host must reassign the
|
||||
controller using :control, :droid, or :give_control as usual.
|
||||
Related to this, there are new commands :controller which query the
|
||||
controller status, and :idle which toggles the idle status.
|
||||
|
||||
Version 1.11.9:
|
||||
* Add-ons client:
|
||||
|
|
|
@ -177,7 +177,7 @@ public:
|
|||
for(std::vector<team>::const_iterator it = resources::teams->begin();
|
||||
it != resources::teams->end();
|
||||
++it) {
|
||||
if(!it->is_ai() && !it->is_empty() && !it->current_player().empty())
|
||||
if(!it->is_ai() && !it->is_idle() && !it->is_empty() && !it->current_player().empty())
|
||||
nicks.insert(it->current_player());
|
||||
}
|
||||
|
||||
|
|
|
@ -1962,8 +1962,10 @@ class console_handler : public map_command_handler<console_handler>, private cha
|
|||
|
||||
void do_refresh();
|
||||
void do_droid();
|
||||
void do_idle();
|
||||
void do_theme();
|
||||
void do_control();
|
||||
void do_controller();
|
||||
void do_clear();
|
||||
void do_sunset();
|
||||
void do_foreground();
|
||||
|
@ -2041,9 +2043,13 @@ class console_handler : public map_command_handler<console_handler>, private cha
|
|||
_("Refresh gui."));
|
||||
register_command("droid", &console_handler::do_droid,
|
||||
_("Switch a side to/from AI control."), _("do not translate the on/off^[<side> [on/off]]"));
|
||||
register_command("idle", &console_handler::do_idle,
|
||||
_("Switch a side to/from idle state."), _("do not translate the on/off^[<side> [on/off]]"));
|
||||
register_command("theme", &console_handler::do_theme);
|
||||
register_command("control", &console_handler::do_control,
|
||||
_("Assign control of a side to a different player or observer."), _("<side> <nickname>"), "N");
|
||||
register_command("controller", &console_handler::do_controller,
|
||||
_("Query the controller status of a side."), _("<side>"));
|
||||
register_command("clear", &console_handler::do_clear,
|
||||
_("Clear chat history."));
|
||||
register_command("sunset", &console_handler::do_sunset,
|
||||
|
@ -2659,7 +2665,7 @@ void console_handler::do_droid() {
|
|||
symbols["side"] = lexical_cast<std::string>(side);
|
||||
command_failed(vgettext("Can't droid networked side: '$side'.", symbols));
|
||||
return;
|
||||
} else if (menu_handler_.teams_[side - 1].is_human() && action != " off") {
|
||||
} else if ((menu_handler_.teams_[side - 1].is_human() || menu_handler_.teams_[side - 1].is_idle()) && action != " off") {
|
||||
//this is our side, so give it to AI
|
||||
menu_handler_.teams_[side - 1].make_human_ai();
|
||||
menu_handler_.change_controller(lexical_cast<std::string>(side),"human_ai");
|
||||
|
@ -2675,6 +2681,49 @@ void console_handler::do_droid() {
|
|||
menu_handler_.textbox_info_.close(*menu_handler_.gui_);
|
||||
}
|
||||
|
||||
void console_handler::do_idle() {
|
||||
// :idle [<side> [on/off]]
|
||||
const std::string side_s = get_arg(1);
|
||||
const std::string action = get_arg(2);
|
||||
// default to the current side if empty
|
||||
const unsigned int side = side_s.empty() ?
|
||||
team_num_ : lexical_cast_default<unsigned int>(side_s);
|
||||
|
||||
if (side < 1 || side > menu_handler_.teams_.size()) {
|
||||
utils::string_map symbols;
|
||||
symbols["side"] = side_s;
|
||||
command_failed(vgettext("Can't idle invalid side: '$side'.", symbols));
|
||||
return;
|
||||
} else if (menu_handler_.teams_[side - 1].is_network()) {
|
||||
utils::string_map symbols;
|
||||
symbols["side"] = lexical_cast<std::string>(side);
|
||||
command_failed(vgettext("Can't droid networked side: '$side'.", symbols));
|
||||
return;
|
||||
} else if (menu_handler_.teams_[side - 1].is_human() && action != " off") {
|
||||
//this is our side, so give it to idle
|
||||
menu_handler_.teams_[side - 1].make_idle();
|
||||
menu_handler_.change_controller(lexical_cast<std::string>(side),"idle");
|
||||
if(team_num_ == side) {
|
||||
//if it is our turn at the moment, we have to indicate to the
|
||||
//play_controller, that we are no longer in control
|
||||
throw end_turn_exception(side);
|
||||
}
|
||||
} else if (menu_handler_.teams_[side - 1].is_ai() && action != " off") {
|
||||
//this is our side, so give it to idle
|
||||
menu_handler_.teams_[side - 1].make_human_ai();
|
||||
menu_handler_.change_controller(lexical_cast<std::string>(side),"human_ai");
|
||||
} else if (menu_handler_.teams_[side - 1].is_idle() && action != " on") {
|
||||
menu_handler_.teams_[side - 1].make_human();
|
||||
menu_handler_.change_controller(lexical_cast<std::string>(side),"human");
|
||||
if(team_num_ == side) {
|
||||
//if it is our turn at the moment, we have to indicate to the
|
||||
//play_controller, that idle should no longer be in control
|
||||
throw end_turn_exception(side);
|
||||
}
|
||||
}
|
||||
menu_handler_.textbox_info_.close(*menu_handler_.gui_);
|
||||
}
|
||||
|
||||
void console_handler::do_theme() {
|
||||
preferences::show_theme_dialog(*menu_handler_.gui_);
|
||||
}
|
||||
|
@ -2707,6 +2756,27 @@ void console_handler::do_control() {
|
|||
menu_handler_.request_control_change(side_num,player);
|
||||
menu_handler_.textbox_info_.close(*(menu_handler_.gui_));
|
||||
}
|
||||
void console_handler::do_controller()
|
||||
{
|
||||
const std::string side = get_arg(1);
|
||||
unsigned int side_num;
|
||||
try {
|
||||
side_num = lexical_cast<unsigned int>(side);
|
||||
} catch(bad_lexical_cast&) {
|
||||
utils::string_map symbols;
|
||||
symbols["side"] = side;
|
||||
command_failed(vgettext("Can't query control of invalid side: '$side'.", symbols));
|
||||
return;
|
||||
}
|
||||
if (side_num < 1 || side_num > menu_handler_.teams_.size()) {
|
||||
utils::string_map symbols;
|
||||
symbols["side"] = side;
|
||||
command_failed(vgettext("Can't query control of out-of-bounds side: '$side'.", symbols));
|
||||
return;
|
||||
}
|
||||
print(get_cmd(), menu_handler_.teams_[side_num - 1].controller_string());
|
||||
}
|
||||
|
||||
void console_handler::do_clear() {
|
||||
menu_handler_.gui_->clear_chat_messages();
|
||||
}
|
||||
|
@ -3202,6 +3272,18 @@ void menu_handler::request_control_change ( int side_num, const std::string& pla
|
|||
if (player == preferences::login())
|
||||
return;
|
||||
change_side_controller(side,player);
|
||||
} else if (teams_[side_num - 1].is_idle()) { //if this is our side and it is idle, make human and throw an end turn exception
|
||||
LOG_NG << " *** Got an idle side with requested control change " << std::endl;
|
||||
teams_[side_num - 1].make_human();
|
||||
change_controller(lexical_cast<std::string>(side_num),"human");
|
||||
|
||||
if (player == preferences::login()) {
|
||||
LOG_NG << " *** It's us, throwing end turn exception " << std::endl;
|
||||
} else {
|
||||
LOG_NG << " *** It's not us, changing sides now as usual, then throwing end_turn " << std::endl;
|
||||
change_side_controller(side,player);
|
||||
}
|
||||
throw end_turn_exception(side_num);
|
||||
} else {
|
||||
//it is not our side, the server will decide if we can change the
|
||||
//controller (that is if we are host of the game)
|
||||
|
|
|
@ -549,3 +549,10 @@ bool playmp_controller::can_execute_command(const hotkey::hotkey_command& cmd, i
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void playmp_controller::do_idle_notification()
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), "Wesnoth", 0,
|
||||
"This side is in an idle state. To proceed with the game, the host must assign it to another controller.",
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ protected:
|
|||
virtual void after_human_turn();
|
||||
virtual void finish_side_turn();
|
||||
virtual void play_network_turn();
|
||||
virtual void do_idle_notification();
|
||||
void init_turn_data();
|
||||
|
||||
turn_info* turn_data_;
|
||||
|
|
|
@ -629,6 +629,13 @@ void playsingle_controller::play_turn(bool save)
|
|||
check_time_over();
|
||||
}
|
||||
|
||||
void playsingle_controller::play_idle_loop()
|
||||
{
|
||||
play_slice();
|
||||
gui_->draw();
|
||||
SDL_Delay(10);
|
||||
}
|
||||
|
||||
void playsingle_controller::play_side(const unsigned int side_number, bool save)
|
||||
{
|
||||
//check for team-specific items in the scenario
|
||||
|
@ -681,6 +688,29 @@ void playsingle_controller::play_side(const unsigned int side_number, bool save)
|
|||
|
||||
} else if(current_team().is_network()) {
|
||||
play_network_turn();
|
||||
} else if(current_team().is_idle()) {
|
||||
try{
|
||||
end_turn_enable(false);
|
||||
do_idle_notification();
|
||||
before_human_turn(save);
|
||||
while(1) {
|
||||
play_idle_loop();
|
||||
}
|
||||
} catch(end_turn_exception& end_turn) {
|
||||
LOG_NG << "Escaped from idle state with exception!" << std::endl;
|
||||
if (end_turn.redo == side_number) {
|
||||
player_type_changed_ = true;
|
||||
// If new controller is not human,
|
||||
// reset gui to prev human one
|
||||
if (!teams_[side_number-1].is_human()) {
|
||||
browse_ = true;
|
||||
int s = find_human_team_before(side_number);
|
||||
if (s <= 0)
|
||||
s = gui_->playing_side();
|
||||
update_gui_to_player(s-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Else current_team().is_empty(), so do nothing.
|
||||
|
@ -884,6 +914,16 @@ void playsingle_controller::play_ai_turn(){
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Will handle sending a networked notification in descendent classes.
|
||||
*/
|
||||
void playsingle_controller::do_idle_notification()
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), "Wesnoth", 0,
|
||||
"This side is in an idle state. To proceed with the game, the host must assign it to another controller.",
|
||||
events::chat_handler::MESSAGE_PUBLIC, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will handle networked turns in descendent classes.
|
||||
*/
|
||||
|
|
|
@ -82,6 +82,8 @@ protected:
|
|||
void end_turn_enable(bool enable);
|
||||
virtual hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command, int index) const;
|
||||
void play_ai_turn();
|
||||
void play_idle_loop();
|
||||
virtual void do_idle_notification();
|
||||
virtual void play_network_turn();
|
||||
virtual void init_gui();
|
||||
void check_time_over();
|
||||
|
|
|
@ -243,6 +243,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
}
|
||||
|
||||
int action = 0;
|
||||
int first_observer_option_idx = 0;
|
||||
|
||||
std::vector<std::string> observers;
|
||||
std::vector<team*> allies;
|
||||
|
@ -253,8 +254,11 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
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"));
|
||||
|
||||
first_observer_option_idx = options.size();
|
||||
|
||||
//get all observers in as options to transfer control
|
||||
BOOST_FOREACH(const std::string &ob, resources::screen->observers())
|
||||
{
|
||||
|
@ -309,12 +313,19 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
|
||||
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
|
||||
case 2:
|
||||
tm.make_idle();
|
||||
tm.set_current_player("idle" + side_drop);
|
||||
if (have_leader) leader->rename("idle" + side_drop);
|
||||
|
||||
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 end_level_exception(QUIT);
|
||||
default:
|
||||
if (action > 2) {
|
||||
if (action > 3) {
|
||||
|
||||
{
|
||||
// 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.
|
||||
|
@ -323,7 +334,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
if (have_leader) leader->rename("human"+side_drop);
|
||||
}
|
||||
|
||||
const size_t index = static_cast<size_t>(action - 3);
|
||||
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) {
|
||||
|
|
|
@ -227,6 +227,7 @@ char const *team::team_info::controller_string() const
|
|||
case HUMAN_AI: return "human_ai";
|
||||
case NETWORK: return "network";
|
||||
case NETWORK_AI: return "network_ai";
|
||||
case IDLE: return "idle";
|
||||
case EMPTY: return "null";
|
||||
default: assert(false); return NULL;
|
||||
}
|
||||
|
@ -498,6 +499,8 @@ void team::change_controller(const std::string& controller)
|
|||
cid = team::NETWORK_AI;
|
||||
else if (controller == "null")
|
||||
cid = team::EMPTY;
|
||||
else if (controller == "idle")
|
||||
cid = team::IDLE;
|
||||
else
|
||||
cid = team::AI;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace wb {
|
|||
class team : public savegame::savegame_config
|
||||
{
|
||||
public:
|
||||
enum CONTROLLER { HUMAN, HUMAN_AI, AI, NETWORK, NETWORK_AI, EMPTY };
|
||||
enum CONTROLLER { HUMAN, HUMAN_AI, AI, NETWORK, NETWORK_AI, IDLE, EMPTY };
|
||||
|
||||
private:
|
||||
class shroud_map {
|
||||
|
@ -210,6 +210,7 @@ public:
|
|||
bool is_network_human() const { return info_.controller == NETWORK; }
|
||||
bool is_network_ai() const { return info_.controller == NETWORK_AI; }
|
||||
bool is_ai() const { return info_.controller == AI || is_human_ai(); }
|
||||
bool is_idle() const { return info_.controller == IDLE; }
|
||||
bool is_empty() const { return info_.controller == EMPTY; }
|
||||
|
||||
bool is_local() const { return is_human() || is_ai(); }
|
||||
|
@ -220,6 +221,7 @@ public:
|
|||
void make_network() { info_.controller = NETWORK; }
|
||||
void make_network_ai() { info_.controller = NETWORK_AI; }
|
||||
void make_ai() { info_.controller = AI; }
|
||||
void make_idle() { info_.controller = IDLE; }
|
||||
void change_controller(const std::string& controller);
|
||||
void change_controller(CONTROLLER controller) { info_.controller = controller; }
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue