643 lines
20 KiB
C++
643 lines
20 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
|
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#include "events.hpp"
|
|
#include "game_events.hpp"
|
|
#include "hotkeys.hpp"
|
|
#include "intro.hpp"
|
|
#include "language.hpp"
|
|
#include "log.hpp"
|
|
#include "mapgen.hpp"
|
|
#include "network.hpp"
|
|
#include "playlevel.hpp"
|
|
#include "playturn.hpp"
|
|
#include "preferences.hpp"
|
|
#include "replay.hpp"
|
|
#include "scoped_resource.hpp"
|
|
#include "sound.hpp"
|
|
#include "tooltips.hpp"
|
|
|
|
#include <iostream>
|
|
#include <iterator>
|
|
|
|
namespace {
|
|
int placing_score(const config& side, const gamemap& map, const gamemap::location& pos)
|
|
{
|
|
int positions = 0, liked = 0;
|
|
const std::string& terrain_liked = side["terrain_liked"];
|
|
for(int i = pos.x-8; i != pos.x+8; ++i) {
|
|
for(int j = pos.y-8; j != pos.y+8; ++j) {
|
|
const gamemap::location pos(i,j);
|
|
if(map.on_board(pos)) {
|
|
++positions;
|
|
if(std::count(terrain_liked.begin(),terrain_liked.end(),map.underlying_terrain(map[i][j]))) {
|
|
++liked;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (100*liked)/positions;
|
|
}
|
|
|
|
struct placing_info {
|
|
int side, score;
|
|
gamemap::location pos;
|
|
};
|
|
|
|
bool operator<(const placing_info& a, const placing_info& b) { return a.score > b.score; }
|
|
bool operator==(const placing_info& a, const placing_info& b) { return a.score == b.score; }
|
|
|
|
void place_sides_in_preferred_locations(gamemap& map, const config::child_list& sides)
|
|
{
|
|
std::vector<placing_info> placings;
|
|
|
|
const int num_pos = map.num_valid_starting_positions();
|
|
|
|
for(config::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
|
|
const int side_num = s - sides.begin() + 1;
|
|
for(int p = 1; p <= num_pos; ++p) {
|
|
const gamemap::location& pos = map.starting_position(p);
|
|
const int score = placing_score(**s,map,pos);
|
|
placing_info obj;
|
|
obj.side = side_num;
|
|
obj.score = score;
|
|
obj.pos = pos;
|
|
placings.push_back(obj);
|
|
}
|
|
}
|
|
|
|
std::sort(placings.begin(),placings.end());
|
|
std::set<int> placed;
|
|
std::set<gamemap::location> positions_taken;
|
|
|
|
for(std::vector<placing_info>::const_iterator i = placings.begin();
|
|
i != placings.end() && placed.size() != sides.size(); ++i) {
|
|
if(placed.count(i->side) == 0 && positions_taken.count(i->pos) == 0) {
|
|
placed.insert(i->side);
|
|
positions_taken.insert(i->pos);
|
|
map.set_starting_position(i->side,i->pos);
|
|
std::cerr << "placing side " << i->side << " at " << i->pos.x << "," << i->pos.y << "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LEVEL_RESULT play_level(game_data& gameinfo, config& game_config,
|
|
config* level, CVideo& video,
|
|
game_state& state_of_game,
|
|
const std::vector<config*>& story)
|
|
{
|
|
const int num_turns = atoi(level->values["turns"].c_str());
|
|
gamestatus status(*level,num_turns);
|
|
|
|
std::string map_data = (*level)["map_data"];
|
|
if(map_data == "" && (*level)["map"] != "") {
|
|
map_data = read_file("data/maps/" + (*level)["map"]);
|
|
}
|
|
|
|
//if the map should be randomly generated
|
|
if(map_data == "" && (*level)["map_generation"] != "") {
|
|
map_data = random_generate_map((*level)["map_generation"]);
|
|
}
|
|
|
|
gamemap map(game_config,map_data);
|
|
|
|
CKey key;
|
|
unit_map units;
|
|
|
|
std::vector<team> teams;
|
|
|
|
int first_human_team = -1;
|
|
|
|
const config::child_list& unit_cfg = level->get_children("side");
|
|
|
|
if((*level)["modify_placing"] == "true") {
|
|
std::cerr << "modifying placing...\n";
|
|
place_sides_in_preferred_locations(map,unit_cfg);
|
|
}
|
|
|
|
std::cerr << "initializing teams..." << unit_cfg.size() << "\n";;
|
|
|
|
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
|
|
std::cerr << "initializing team...\n";
|
|
|
|
if(first_human_team == -1 && (**ui)["controller"] == "human") {
|
|
first_human_team = ui - unit_cfg.begin();
|
|
}
|
|
|
|
std::string gold = (**ui)["gold"];
|
|
if(gold.empty())
|
|
gold = "100";
|
|
|
|
std::cerr << "found gold: '" << gold << "'\n";
|
|
|
|
int ngold = ::atoi(gold.c_str());
|
|
if(ui == unit_cfg.begin() && state_of_game.gold >= ngold &&
|
|
(*level)["disallow_recall"] != "yes") {
|
|
ngold = state_of_game.gold;
|
|
}
|
|
|
|
std::cerr << "set gold to '" << ngold << "'\n";
|
|
|
|
//if this side tag describes the leader of the side
|
|
if((**ui)["no_leader"] != "yes") {
|
|
unit new_unit(gameinfo, **ui);
|
|
|
|
//search the recall list for leader units, and if there is
|
|
//one, use it in place of the config-described unit
|
|
if(ui == unit_cfg.begin()) {
|
|
for(std::vector<unit>::iterator it = state_of_game.available_units.begin();
|
|
it != state_of_game.available_units.end(); ++it) {
|
|
if(it->can_recruit()) {
|
|
new_unit = *it;
|
|
state_of_game.available_units.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//see if the side specifies its location. Otherwise start it at the map-given
|
|
//starting position
|
|
const std::string& has_loc = (**ui)["x"];
|
|
gamemap::location start_pos(**ui);
|
|
|
|
if(has_loc.empty()) {
|
|
start_pos = map.starting_position(new_unit.side());
|
|
std::cerr << "initializing side '" << (**ui)["side"] << "' at " << start_pos.x << "," << start_pos.y << "\n";
|
|
}
|
|
|
|
if(!start_pos.valid() && new_unit.side() == 1) {
|
|
throw gamestatus::load_game_failed("No starting position for side 1");
|
|
}
|
|
|
|
if(start_pos.valid()) {
|
|
units.insert(std::pair<gamemap::location,unit>(
|
|
map.starting_position(new_unit.side()), new_unit));
|
|
}
|
|
}
|
|
|
|
teams.push_back(team(**ui,ngold));
|
|
|
|
//if the game state specifies units that can be recruited for the player
|
|
//then add them
|
|
if(teams.size() == 1 && state_of_game.can_recruit.empty() == false) {
|
|
std::copy(state_of_game.can_recruit.begin(),state_of_game.can_recruit.end(),
|
|
std::inserter(teams.back().recruits(),teams.back().recruits().end()));
|
|
}
|
|
|
|
if(teams.size() == 1) {
|
|
state_of_game.can_recruit = teams.back().recruits();
|
|
}
|
|
|
|
//if there are additional starting units on this side
|
|
const config::child_list& starting_units = (*ui)->get_children("unit");
|
|
for(config::child_list::const_iterator su = starting_units.begin();
|
|
su != starting_units.end(); ++su) {
|
|
unit new_unit(gameinfo,**su);
|
|
const std::string& x = (**su)["x"];
|
|
const std::string& y = (**su)["y"];
|
|
|
|
const gamemap::location loc(**su);
|
|
if(x.empty() || y.empty() || !map.on_board(loc)) {
|
|
state_of_game.available_units.push_back(new_unit);
|
|
} else {
|
|
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
|
|
std::cerr << "inserting unit for side " << new_unit.side() << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
const teams_manager team_manager(teams);
|
|
|
|
const config* theme_cfg = NULL;
|
|
if((*level)["theme"] != "") {
|
|
theme_cfg = game_config.find_child("theme","name",(*level)["theme"]);
|
|
}
|
|
|
|
if(theme_cfg == NULL) {
|
|
theme_cfg = game_config.find_child("theme","name",preferences::theme());
|
|
}
|
|
|
|
const config dummy_cfg;
|
|
display gui(units,video,map,status,teams,theme_cfg != NULL ? *theme_cfg : dummy_cfg);
|
|
|
|
if(first_human_team != -1) {
|
|
gui.set_team(first_human_team);
|
|
}
|
|
|
|
const preferences::display_manager prefs_disp_manager(&gui);
|
|
const tooltips::manager tooltips_manager(gui);
|
|
|
|
if(recorder.skipping() == false) {
|
|
for(std::vector<config*>::const_iterator story_i = story.begin();
|
|
story_i != story.end(); ++story_i) {
|
|
show_intro(gui,**story_i, state_of_game);
|
|
}
|
|
|
|
show_map_scene(gui,*level);
|
|
}
|
|
|
|
const std::string& music = level->values["music"];
|
|
if(music != "") {
|
|
sound::play_music(music);
|
|
}
|
|
|
|
victory_conditions::set_victory_when_enemies_defeated(
|
|
(*level)["victory_when_enemies_defeated"] != "no");
|
|
|
|
game_events::manager events_manager(*level,gui,map,units,teams,
|
|
state_of_game,gameinfo);
|
|
|
|
//find a list of 'items' (i.e. overlays) on the level, and add them
|
|
const config::child_list& overlays = level->get_children("item");
|
|
for(config::child_list::const_iterator overlay = overlays.begin(); overlay != overlays.end(); ++overlay) {
|
|
gui.add_overlay(gamemap::location(**overlay),(**overlay)["image"]);
|
|
}
|
|
|
|
if(first_human_team != -1) {
|
|
clear_shroud(gui,status,map,gameinfo,units,teams,first_human_team);
|
|
gui.scroll_to_tile(map.starting_position(first_human_team+1).x,map.starting_position(first_human_team+1).y,display::WARP);
|
|
}
|
|
|
|
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,display::WARP);
|
|
|
|
bool replaying = (recorder.at_end() == false);
|
|
|
|
//if a team is specified whose turn it is, it means we're loading a game
|
|
//instead of starting a fresh one
|
|
const bool loading_game = (*level)["playing_team"].empty() == false;
|
|
int first_player = atoi((*level)["playing_team"].c_str());
|
|
if(first_player < 0 || first_player >= int(teams.size())) {
|
|
first_player = 0;
|
|
}
|
|
|
|
int turn = 1;
|
|
std::cerr << "starting main loop\n";
|
|
for(bool first_time = true; true; first_time = false, first_player = 0) {
|
|
int player_number = 0;
|
|
|
|
try {
|
|
|
|
if(first_time) {
|
|
const hotkey::basic_handler key_events_handler(gui);
|
|
|
|
std::cerr << "first_time..." << (recorder.skipping() ? "skipping" : "no skip") << "\n";
|
|
update_locker lock_display(gui,recorder.skipping());
|
|
game_events::fire("start");
|
|
gui.draw();
|
|
}
|
|
|
|
gui.new_turn();
|
|
gui.invalidate_game_status();
|
|
|
|
std::cerr << "turn: " << turn++ << "\n";
|
|
|
|
for(std::vector<team>::iterator team_it = teams.begin()+first_player;
|
|
team_it != teams.end(); ++team_it) {
|
|
log_scope("player turn");
|
|
player_number = (team_it - teams.begin()) + 1;
|
|
|
|
//if a side is dead, don't do their turn
|
|
if(team_units(units,player_number) == 0)
|
|
continue;
|
|
|
|
std::stringstream player_number_str;
|
|
player_number_str << player_number;
|
|
game_events::set_variable("side_number",player_number_str.str());
|
|
game_events::fire("side turn");
|
|
|
|
//we want to work out if units for this player should get healed, and the
|
|
//player should get income now. healing/income happen if it's not the first
|
|
//turn of processing, or if we are loading a game, and this is not the
|
|
//player it started with.
|
|
const bool turn_refresh = !first_time || loading_game && team_it != teams.begin()+first_player;
|
|
|
|
if(turn_refresh) {
|
|
for(unit_map::iterator i = units.begin(); i != units.end(); ++i) {
|
|
if(i->second.side() == player_number) {
|
|
i->second.new_turn();
|
|
}
|
|
}
|
|
|
|
team_it->new_turn();
|
|
|
|
//if the expense is less than the number of villages owned,
|
|
//then we don't have to pay anything at all
|
|
const int expense = team_upkeep(units,player_number) -
|
|
team_it->towers().size();
|
|
if(expense > 0) {
|
|
team_it->spend_gold(expense);
|
|
}
|
|
|
|
calculate_healing(gui,map,units,player_number,teams);
|
|
}
|
|
|
|
gui.set_playing_team(size_t(player_number-1));
|
|
|
|
clear_shroud(gui,status,map,gameinfo,units,teams,player_number-1);
|
|
|
|
//scroll the map to the leader
|
|
const unit_map::iterator leader = find_leader(units,player_number);
|
|
|
|
if(leader != units.end() && !recorder.skipping()) {
|
|
const hotkey::basic_handler key_events_handler(gui);
|
|
gui.scroll_to_tile(leader->first.x,leader->first.y);
|
|
}
|
|
|
|
if(replaying) {
|
|
const hotkey::basic_handler key_events_handler(gui);
|
|
std::cerr << "doing replay " << player_number << "\n";
|
|
try {
|
|
replaying = do_replay(gui,map,gameinfo,units,teams,
|
|
player_number,status,state_of_game);
|
|
} catch(replay::error& e) {
|
|
std::cerr << "caught replay::error\n";
|
|
gui::show_dialog(gui,NULL,"",string_table["bad_save_message"],gui::OK_ONLY);
|
|
|
|
replaying = false;
|
|
}
|
|
std::cerr << "result of replay: " << (replaying?"true":"false") << "\n";
|
|
}
|
|
|
|
if(!replaying && team_it->music().empty() == false) {
|
|
std::cerr << "playing music: '" << team_it->music() << "'\n";
|
|
sound::play_music(team_it->music());
|
|
}
|
|
|
|
//goto this label if the type of a team (human/ai/networked) has changed mid-turn
|
|
redo_turn:
|
|
|
|
if(!replaying && team_it->is_human()) {
|
|
std::cerr << "is human...\n";
|
|
|
|
if(first_time && team_it == teams.begin() &&
|
|
level->values["objectives"].empty() == false) {
|
|
dialogs::show_objectives(gui,*level);
|
|
}
|
|
|
|
play_turn(gameinfo,state_of_game,status,game_config,
|
|
level, video, key, gui, events_manager, map,
|
|
teams, player_number, units);
|
|
|
|
if(game_config::debug)
|
|
display::clear_debug_highlights();
|
|
|
|
std::cerr << "human finished turn...\n";
|
|
|
|
} else if(!replaying && team_it->is_ai()) {
|
|
std::cerr << "is ai...\n";
|
|
gui.recalculate_minimap();
|
|
|
|
const hotkey::basic_handler key_events_handler(gui);
|
|
|
|
const int start_command = recorder.ncommands();
|
|
|
|
update_locker lock(gui,!preferences::show_ai_moves());
|
|
|
|
ai_interface::info ai_info(gui,map,gameinfo,units,teams,player_number,status);
|
|
util::scoped_ptr<ai_interface> ai_obj(create_ai(team_it->ai_algorithm(),ai_info));
|
|
ai_obj->play_turn();
|
|
|
|
if(network::nconnections() > 0) {
|
|
config cfg;
|
|
cfg.add_child("turn",recorder.get_data_range(start_command,recorder.ncommands()));
|
|
network::send_data(cfg);
|
|
}
|
|
|
|
gui.invalidate_unit();
|
|
gui.invalidate_game_status();
|
|
gui.invalidate_all();
|
|
gui.draw();
|
|
SDL_Delay(500);
|
|
} else if(!replaying && team_it->is_network()) {
|
|
std::cerr << "is networked...\n";
|
|
|
|
bool turn_end = false;
|
|
|
|
while(!turn_end) {
|
|
|
|
config cfg;
|
|
|
|
turn_info turn_data(gameinfo,state_of_game,status,
|
|
game_config,level,key,gui,
|
|
map,teams,player_number,units,true);
|
|
|
|
for(;;) {
|
|
network::connection res = network::receive_data(cfg);
|
|
if(res && cfg.child("leave_game") != NULL) {
|
|
throw network::error("");
|
|
}
|
|
|
|
//if a side has dropped out of the game.
|
|
if(res && cfg["side_drop"] != "") {
|
|
const size_t side = atoi(cfg["side_drop"].c_str())-1;
|
|
if(side >= teams.size()) {
|
|
std::cerr << "unknown side " << side << " is dropping game\n";
|
|
throw network::error("");
|
|
}
|
|
|
|
int action = 0;
|
|
|
|
//see if the side still has a leader alive. If they have
|
|
//no leader, we assume they just want to be replaced by
|
|
//the AI.
|
|
const unit_map::const_iterator leader = find_leader(units,side+1);
|
|
if(leader != units.end()) {
|
|
std::vector<std::string> options;
|
|
options.push_back(string_table["replace_ai_message"]);
|
|
options.push_back(string_table["replace_local_message"]);
|
|
options.push_back(string_table["abort_game_message"]);
|
|
|
|
const std::string msg = leader->second.description() + " " + string_table["player_leave_message"];
|
|
action = gui::show_dialog(gui,NULL,"",msg,gui::OK_ONLY,&options);
|
|
}
|
|
|
|
//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.
|
|
if(action == 0) {
|
|
teams[side].make_ai();
|
|
goto redo_turn;
|
|
} else if(action == 1) {
|
|
teams[side].make_human();
|
|
goto redo_turn;
|
|
} else {
|
|
throw network::error("");
|
|
}
|
|
}
|
|
|
|
if(res && cfg.child("turn") != NULL) {
|
|
//forward the data to other peers
|
|
network::send_data_all_except(cfg,res);
|
|
break;
|
|
}
|
|
|
|
const int ncommand = recorder.ncommands();
|
|
turn_data.turn_slice();
|
|
turn_data.send_data(ncommand);
|
|
gui.draw();
|
|
}
|
|
|
|
replay replay_obj(*cfg.child("turn"));
|
|
replay_obj.start_replay();
|
|
|
|
try {
|
|
turn_end = do_replay(gui,map,gameinfo,units,teams,
|
|
player_number,status,state_of_game,&replay_obj);
|
|
} catch(replay::error&) {
|
|
turn_data.save_game(string_table["network_sync_error"]);
|
|
|
|
return QUIT;
|
|
}
|
|
|
|
recorder.add_config(*cfg.child("turn"));
|
|
|
|
gui.invalidate_all();
|
|
gui.draw();
|
|
}
|
|
|
|
std::cerr << "finished networked...\n";
|
|
}
|
|
|
|
for(unit_map::iterator uit = units.begin(); uit != units.end(); ++uit) {
|
|
if(uit->second.side() == player_number)
|
|
uit->second.end_turn();
|
|
}
|
|
|
|
team_it->get_shared_maps();
|
|
|
|
game_events::pump();
|
|
|
|
check_victory(units,teams);
|
|
|
|
}
|
|
|
|
//time has run out
|
|
if(!status.next_turn()) {
|
|
if(non_interactive()) {
|
|
std::cout << "time over (draw)\n";
|
|
}
|
|
|
|
game_events::fire("time over");
|
|
throw end_level_exception(DEFEAT);
|
|
}
|
|
|
|
std::stringstream event_stream;
|
|
event_stream << status.turn();
|
|
|
|
{
|
|
std::cerr << "turn event..." << (recorder.skipping() ? "skipping" : "no skip") << "\n";
|
|
update_locker lock_display(gui,recorder.skipping());
|
|
const std::string turn_num = event_stream.str();
|
|
game_events::set_variable("turn_number",turn_num);
|
|
game_events::fire("turn " + turn_num);
|
|
game_events::fire("new turn");
|
|
}
|
|
|
|
} catch(end_level_exception& end_level) {
|
|
|
|
if(end_level.result == QUIT || end_level.result == REPLAY) {
|
|
return end_level.result;
|
|
} else if(end_level.result == DEFEAT) {
|
|
try {
|
|
game_events::fire("defeat");
|
|
} catch(end_level_exception&) {
|
|
}
|
|
|
|
gui::show_dialog(gui,NULL,
|
|
string_table["defeat_heading"],
|
|
string_table["defeat_message"],
|
|
gui::OK_ONLY);
|
|
return DEFEAT;
|
|
} else if(end_level.result == VICTORY || end_level.result == CONTINUE) {
|
|
try {
|
|
game_events::fire("victory");
|
|
} catch(end_level_exception&) {
|
|
}
|
|
|
|
//add all the units that survived the scenario
|
|
for(std::map<gamemap::location,unit>::iterator un = units.begin(); un != units.end(); ++un) {
|
|
if(un->second.side() == 1) {
|
|
un->second.new_turn();
|
|
un->second.new_level();
|
|
state_of_game.available_units.push_back(un->second);
|
|
}
|
|
}
|
|
|
|
//'continue' is like a victory, except it doesn't announce victory,
|
|
//and the player returns 100% of gold.
|
|
if(end_level.result == CONTINUE) {
|
|
state_of_game.gold = teams[0].gold();
|
|
return VICTORY;
|
|
}
|
|
|
|
if((*level)["disallow_recall"] == "yes") {
|
|
return VICTORY;
|
|
}
|
|
|
|
const int remaining_gold = teams[0].gold();
|
|
const int finishing_bonus_per_turn = map.towers().size()*game_config::tower_income + game_config::base_income;
|
|
const int turns_left = status.number_of_turns() - status.turn();
|
|
const int finishing_bonus = end_level.gold_bonus ?
|
|
(finishing_bonus_per_turn * turns_left) : 0;
|
|
state_of_game.gold = ((remaining_gold+finishing_bonus)*80)/100;
|
|
|
|
gui::show_dialog(gui,NULL,string_table["victory_heading"],
|
|
string_table["victory_message"],gui::OK_ONLY);
|
|
std::stringstream report;
|
|
report << string_table["remaining_gold"] << ": "
|
|
<< remaining_gold << "\n";
|
|
if(end_level.gold_bonus) {
|
|
report << string_table["early_finish_bonus"] << ": "
|
|
<< finishing_bonus_per_turn
|
|
<< " " << string_table["per_turn"] << "\n"
|
|
<< string_table["turns_finished_early"] << ": "
|
|
<< turns_left << "\n"
|
|
<< string_table["bonus"] << ": "
|
|
<< finishing_bonus << "\n"
|
|
<< string_table["gold"] << ": "
|
|
<< (remaining_gold+finishing_bonus);
|
|
}
|
|
|
|
report << "\n" << string_table["fifty_percent"] << "\n"
|
|
<< string_table["retained_gold"] << ": "
|
|
<< state_of_game.gold;
|
|
|
|
gui::show_dialog(gui,NULL,"",report.str(),gui::OK_ONLY);
|
|
return VICTORY;
|
|
}
|
|
} //end catch
|
|
catch(replay::error& e) {
|
|
std::cerr << "caught replay::error\n";
|
|
gui::show_dialog(gui,NULL,"",string_table["bad_save_message"],
|
|
gui::OK_ONLY);
|
|
return QUIT;
|
|
}
|
|
catch(network::error& e) {
|
|
if(e.socket) {
|
|
e.disconnect();
|
|
}
|
|
|
|
turn_info turn_data(gameinfo,state_of_game,status,
|
|
game_config,level,key,gui,
|
|
map,teams,player_number,units,true);
|
|
|
|
turn_data.save_game(string_table["save_game_error"]);
|
|
throw network::error();
|
|
}
|
|
|
|
} //end for(;;)
|
|
|
|
return QUIT;
|
|
}
|