mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-14 10:18:03 +00:00
814 lines
25 KiB
C++
814 lines
25 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 "cursor.hpp"
|
|
#include "events.hpp"
|
|
#include "filesystem.hpp"
|
|
#include "game_events.hpp"
|
|
#include "halo.hpp"
|
|
#include "help.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 "statistics.hpp"
|
|
#include "tooltips.hpp"
|
|
#include "unit_display.hpp"
|
|
#include "util.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[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";
|
|
}
|
|
}
|
|
}
|
|
|
|
bool is_observer(const std::vector<team>& teams)
|
|
{
|
|
for(std::vector<team>::const_iterator i = teams.begin(); i != teams.end(); ++i) {
|
|
if(i->is_human()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
|
|
config* level, CVideo& video,
|
|
game_state& state_of_game,
|
|
const std::vector<config*>& story)
|
|
{
|
|
const int ticks = SDL_GetTicks();
|
|
std::cerr << "in play_level()...\n";
|
|
|
|
//if the entire scenario should be randomly generated
|
|
if((*level)["scenario_generation"] != "") {
|
|
std::cerr << "randomly generating scenario...\n";
|
|
const cursor::setter cursor_setter(cursor::WAIT);
|
|
|
|
static config scenario;
|
|
scenario = random_generate_scenario((*level)["scenario_generation"],level->child("generator"));
|
|
level = &scenario;
|
|
|
|
state_of_game.starting_pos = scenario;
|
|
}
|
|
|
|
std::string map_data = (*level)["map_data"];
|
|
if(map_data == "" && (*level)["map"] != "") {
|
|
map_data = read_map((*level)["map"]);
|
|
}
|
|
|
|
//if the map should be randomly generated
|
|
if(map_data == "" && (*level)["map_generation"] != "") {
|
|
const cursor::setter cursor_setter(cursor::WAIT);
|
|
map_data = random_generate_map((*level)["map_generation"],level->child("generator"));
|
|
|
|
//since we've had to generate the map, make sure that when we save the game,
|
|
//it will not ask for the map to be generated again on reload
|
|
static config new_level;
|
|
new_level = *level;
|
|
new_level.values["map_data"] = map_data;
|
|
level = &new_level;
|
|
|
|
state_of_game.starting_pos = new_level;
|
|
}
|
|
|
|
const config& lvl = *level;
|
|
|
|
std::cerr << "generated map " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
const statistics::scenario_context statistics_context(lvl["name"]);
|
|
|
|
const int num_turns = atoi(lvl["turns"].c_str());
|
|
gamestatus status(*level,num_turns);
|
|
|
|
gamemap map(game_config,map_data);
|
|
|
|
std::cerr << "created objects... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
CKey key;
|
|
unit_map units;
|
|
|
|
const verification_manager verify_manager(units);
|
|
|
|
const int xp_modifier = atoi(lvl["experience_modifier"].c_str());
|
|
const unit_type::experience_accelerator xp_mod(xp_modifier > 0 ? xp_modifier : 100);
|
|
|
|
std::vector<team> teams;
|
|
|
|
const teams_manager team_manager(teams);
|
|
|
|
int first_human_team = -1;
|
|
|
|
const config::child_list& unit_cfg = level->get_children("side");
|
|
|
|
if(lvl["modify_placing"] == "true") {
|
|
std::cerr << "modifying placing...\n";
|
|
place_sides_in_preferred_locations(map,unit_cfg);
|
|
}
|
|
|
|
std::cerr << "initializing teams..." << unit_cfg.size() << "\n";;
|
|
std::cerr << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
|
|
std::string save_id = (**ui)["save_id"];
|
|
|
|
if(save_id.empty()) {
|
|
save_id=(**ui)["description"];
|
|
}
|
|
|
|
player_info *player = NULL;
|
|
|
|
if((**ui)["controller"] == "human" ||
|
|
(**ui)["controller"] == "network" ||
|
|
(**ui)["persistent"] == "1") {
|
|
player = state_of_game.get_player(save_id);
|
|
|
|
if(!player && !save_id.empty()) {
|
|
player=&state_of_game.players[save_id];
|
|
}
|
|
}
|
|
|
|
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 = lexical_cast_default<int>(gold);
|
|
if(player && player->gold >= ngold) {
|
|
ngold = player->gold;
|
|
}
|
|
|
|
std::cerr << "set gold to '" << ngold << "'\n";
|
|
|
|
teams.push_back(team(**ui,ngold));
|
|
|
|
//if this side tag describes the leader of the side
|
|
if((**ui)["no_leader"] != "yes" && (**ui)["controller"] != "null") {
|
|
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(player) {
|
|
for(std::vector<unit>::iterator it = player->available_units.begin(); it != player->available_units.end(); ++it) {
|
|
if(it->can_recruit()) {
|
|
new_unit = *it;
|
|
player->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(map.empty()) {
|
|
throw gamestatus::load_game_failed("Map not found");
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
//if the game state specifies units that can be recruited for the player
|
|
//then add them
|
|
if(player && player->can_recruit.empty() == false) {
|
|
std::copy(player->can_recruit.begin(),player->can_recruit.end(),
|
|
std::inserter(teams.back().recruits(),teams.back().recruits().end()));
|
|
}
|
|
|
|
if(player) {
|
|
player->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);
|
|
|
|
new_unit.set_side(lexical_cast_default<int>((**ui)["side"],1));
|
|
|
|
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)) {
|
|
if(player) {
|
|
player->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";
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Add all recruitable units as encountered so that information
|
|
// about them are displayed to the user in the help system.
|
|
for (std::vector<team>::const_iterator help_team_it = teams.begin();
|
|
help_team_it != teams.end(); help_team_it++) {
|
|
std::cout << "Adding help units for team '" << help_team_it->name()
|
|
<< "'" << std::endl;
|
|
const std::set<std::string> &recruitable = help_team_it->recruits();
|
|
std::set<std::string> &enc_units = preferences::encountered_units();
|
|
std::cout << "Adding recruitable units: " << std::endl;
|
|
for (std::set<std::string>::const_iterator it = recruitable.begin();
|
|
it != recruitable.end(); it++) {
|
|
std::cout << *it << std::endl;
|
|
}
|
|
std::cout << "Added all recruitable units" << std::endl;
|
|
std::copy(recruitable.begin(), recruitable.end(),
|
|
std::inserter(enc_units, enc_units.begin()));
|
|
}
|
|
|
|
// Add all units that exist at the start to the encountered units so
|
|
// that information about them are displayed to the user in the help
|
|
// system.
|
|
for (unit_map::const_iterator help_unit_it = units.begin();
|
|
help_unit_it != units.end(); help_unit_it++) {
|
|
const std::string name = help_unit_it->second.type().name();
|
|
preferences::encountered_units().insert(name);
|
|
}
|
|
|
|
// Add all units that are recallable as encountred units.
|
|
for(std::map<std::string, player_info>::iterator pi = state_of_game.players.begin(); pi!=state_of_game.players.end(); ++pi) {
|
|
for(std::vector<unit>::iterator help_recall_it = pi->second.available_units.begin(); help_recall_it != pi->second.available_units.end(); help_recall_it++) {
|
|
preferences::encountered_units().insert(help_recall_it->type().name());
|
|
}
|
|
}
|
|
|
|
// Add all terrains on the map as encountered terrains.
|
|
for (int map_x = 0; map_x < map.x(); map_x++) {
|
|
for (int map_y = 0; map_y < map.y(); map_y++) {
|
|
const gamemap::TERRAIN t = map.get_terrain(gamemap::location(map_x, map_y));
|
|
std::string s;
|
|
s += t;
|
|
preferences::encountered_terrains().insert(s);
|
|
}
|
|
}
|
|
std::cerr << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
const config* theme_cfg = NULL;
|
|
if(lvl["theme"] != "") {
|
|
theme_cfg = game_config.find_child("theme","name",lvl["theme"]);
|
|
}
|
|
|
|
if(theme_cfg == NULL) {
|
|
theme_cfg = game_config.find_child("theme","name",preferences::theme());
|
|
}
|
|
|
|
std::cerr << "initializing display... " << (SDL_GetTicks() - ticks) << "\n";
|
|
const config dummy_cfg;
|
|
display gui(units,video,map,status,teams,theme_cfg != NULL ? *theme_cfg : dummy_cfg, game_config, *level);
|
|
|
|
std::cerr << "done initializing display... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
std::cerr << "a... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
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);
|
|
|
|
std::cerr << "b... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
//this *needs* to be created before the show_intro and show_map_scene
|
|
//as that functions use the manager state_of_game
|
|
game_events::manager events_manager(*level,gui,map,units,teams,
|
|
state_of_game,status,gameinfo);
|
|
|
|
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, *level);
|
|
}
|
|
}
|
|
|
|
//object that will make sure that labels are removed at the end of the scenario
|
|
const font::floating_label_context labels_manager;
|
|
const halo::manager halo_manager(gui);
|
|
gui.labels().read(*level);
|
|
std::cerr << "c... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
const std::string& music = lvl["music"];
|
|
if(music != "") {
|
|
sound::play_music(music);
|
|
}
|
|
|
|
std::cerr << "d... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
victory_conditions::set_victory_when_enemies_defeated(
|
|
lvl["victory_when_enemies_defeated"] != "no");
|
|
|
|
std::cerr << "initializing events manager... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
help::help_manager help_manager(&game_config, &gameinfo, &map);
|
|
|
|
//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"]);
|
|
}
|
|
|
|
int turn = 1, player_number = 0;
|
|
|
|
turn_info::floating_textbox textbox_info;
|
|
|
|
std::cerr << "entering try... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
replay_network_sender replay_sender(recorder);
|
|
|
|
try {
|
|
//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 = lvl["playing_team"].empty() == false;
|
|
|
|
gui.begin_game();
|
|
gui.adjust_colours(0,0,0);
|
|
|
|
if(!loading_game) {
|
|
game_events::fire("prestart");
|
|
}
|
|
|
|
std::cerr << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
|
|
if(first_human_team != -1) {
|
|
clear_shroud(gui,status,map,gameinfo,units,teams,first_human_team);
|
|
std::cerr << "b " << (SDL_GetTicks() - ticks) << "\n";
|
|
gui.scroll_to_tile(map.starting_position(first_human_team+1).x,map.starting_position(first_human_team+1).y,display::WARP);
|
|
std::cerr << "c " << (SDL_GetTicks() - ticks) << "\n";
|
|
}
|
|
|
|
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,display::WARP);
|
|
std::cerr << "done scrolling... " << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
bool replaying = (recorder.at_end() == false);
|
|
|
|
int first_player = atoi(lvl["playing_team"].c_str());
|
|
if(first_player < 0 || first_player >= int(teams.size())) {
|
|
first_player = 0;
|
|
}
|
|
|
|
std::cerr << "starting main loop\n";
|
|
std::cerr << (SDL_GetTicks() - ticks) << "\n";
|
|
|
|
std::deque<config> data_backlog;
|
|
|
|
for(bool first_time = true; true; first_time = false, first_player = 0) {
|
|
player_number = 0;
|
|
|
|
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());
|
|
events::raise_draw_event();
|
|
if(!loading_game) {
|
|
game_events::fire("start");
|
|
}
|
|
|
|
gui.draw();
|
|
|
|
for(std::vector<team>::iterator t = teams.begin(); t != teams.end(); ++t) {
|
|
clear_shroud(gui,status,map,gameinfo,units,teams,(t-teams.begin()));
|
|
}
|
|
|
|
gui.recalculate_minimap();
|
|
}
|
|
|
|
gui.new_turn();
|
|
gui.invalidate_game_status();
|
|
events::raise_draw_event();
|
|
|
|
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_it->is_empty() || team_units(units,player_number) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if(is_observer(teams)) {
|
|
gui.set_team(size_t(player_number-1));
|
|
}
|
|
|
|
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->villages().size();
|
|
if(expense > 0) {
|
|
team_it->spend_gold(expense);
|
|
}
|
|
|
|
calculate_healing(gui,status,map,units,player_number,teams);
|
|
}
|
|
|
|
team_it->set_time_of_day(int(status.turn()),status.get_time_of_day());
|
|
|
|
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,"",_("The file you have tried to load is corrupt"),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()) {
|
|
if(lvl["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, textbox_info, replay_sender);
|
|
|
|
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 cursor::setter cursor_setter(cursor::WAIT);
|
|
|
|
turn_info turn_data(gameinfo,state_of_game,status,
|
|
game_config,level,key,gui,
|
|
map,teams,player_number,units,
|
|
turn_info::BROWSE_AI,textbox_info,replay_sender);
|
|
|
|
ai_interface::info ai_info(gui,map,gameinfo,units,teams,player_number,status,turn_data);
|
|
util::scoped_ptr<ai_interface> ai_obj(create_ai(team_it->ai_algorithm(),ai_info));
|
|
ai_obj->play_turn();
|
|
|
|
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";
|
|
|
|
turn_info turn_data(gameinfo,state_of_game,status,
|
|
game_config,level,key,gui,
|
|
map,teams,player_number,units,turn_info::BROWSE_NETWORKED,textbox_info,replay_sender);
|
|
|
|
for(;;) {
|
|
|
|
bool have_data = false;
|
|
config cfg;
|
|
|
|
network::connection from = network::null_connection;
|
|
|
|
if(data_backlog.empty() == false) {
|
|
have_data = true;
|
|
cfg = data_backlog.front();
|
|
data_backlog.pop_front();
|
|
} else {
|
|
from = network::receive_data(cfg);
|
|
have_data = from != network::null_connection;
|
|
}
|
|
|
|
if(have_data) {
|
|
const turn_info::PROCESS_DATA_RESULT result = turn_data.process_network_data(cfg,from,data_backlog);
|
|
if(result == turn_info::PROCESS_RESTART_TURN) {
|
|
goto redo_turn;
|
|
} else if(result == turn_info::PROCESS_END_TURN) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
turn_data.turn_slice();
|
|
turn_data.send_data();
|
|
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();
|
|
}
|
|
|
|
//This implements "delayed map sharing." It's meant as an alternative to shared vision.
|
|
if(team_it->copy_ally_shroud()) {
|
|
gui.recalculate_minimap();
|
|
gui.invalidate_all();
|
|
}
|
|
|
|
game_events::pump();
|
|
|
|
check_victory(units,teams);
|
|
}
|
|
|
|
//time has run out
|
|
if(!status.next_turn()) {
|
|
|
|
if(non_interactive()) {
|
|
std::cout << "time over (draw)\n";
|
|
}
|
|
|
|
std::cerr << "firing time over event...\n";
|
|
game_events::fire("time over");
|
|
std::cerr << "done firing time over event...\n";
|
|
|
|
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");
|
|
}
|
|
} //end for loop
|
|
|
|
} catch(end_level_exception& end_level) {
|
|
|
|
if((end_level.result == DEFEAT || end_level.result == VICTORY) && is_observer(teams)) {
|
|
gui::show_dialog(gui,NULL,_("Game Over"),
|
|
_("The game is over."), gui::OK_ONLY);
|
|
return end_level.result;
|
|
}
|
|
|
|
if(end_level.result == QUIT) {
|
|
return end_level.result;
|
|
} else if(end_level.result == DEFEAT) {
|
|
try {
|
|
game_events::fire("defeat");
|
|
} catch(end_level_exception&) {
|
|
}
|
|
|
|
gui::show_dialog(gui,NULL,
|
|
_("Defeat"),
|
|
_("You have been defeated!"),
|
|
gui::OK_ONLY);
|
|
return DEFEAT;
|
|
} else if(end_level.result == VICTORY || end_level.result == LEVEL_CONTINUE || end_level.result == LEVEL_CONTINUE_NO_SAVE) {
|
|
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) {
|
|
player_info *player=state_of_game.get_player(teams[un->second.side()-1].save_id());
|
|
|
|
if(player) {
|
|
un->second.new_turn();
|
|
un->second.new_level();
|
|
player->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 == LEVEL_CONTINUE || end_level.result == LEVEL_CONTINUE_NO_SAVE) {
|
|
for(std::vector<team>::iterator i=teams.begin(); i!=teams.end(); ++i) {
|
|
player_info *player=state_of_game.get_player(i->save_id());
|
|
if(player) {
|
|
player->gold = i->gold();
|
|
}
|
|
}
|
|
|
|
return end_level.result == LEVEL_CONTINUE_NO_SAVE ? LEVEL_CONTINUE_NO_SAVE : VICTORY;
|
|
}
|
|
|
|
|
|
std::stringstream report;
|
|
|
|
for(std::vector<team>::iterator i=teams.begin(); i!=teams.end(); ++i) {
|
|
player_info *player=state_of_game.get_player(i->save_id());
|
|
|
|
const int remaining_gold = i->gold();
|
|
const int finishing_bonus_per_turn = map.villages().size()*game_config::village_income + game_config::base_income;
|
|
const int turns_left = maximum<int>(0,status.number_of_turns() - status.turn());
|
|
const int finishing_bonus = end_level.gold_bonus ?
|
|
(finishing_bonus_per_turn * turns_left) : 0;
|
|
|
|
if(player) {
|
|
player->gold = ((remaining_gold+finishing_bonus)*80)/100;
|
|
|
|
if(state_of_game.players.size()>1) {
|
|
if(i!=teams.begin()) {
|
|
report << "\n";
|
|
}
|
|
|
|
report << i->save_id() << ": " << "\n";
|
|
}
|
|
|
|
report << _("Remaining gold") << ": "
|
|
<< remaining_gold << "\n";
|
|
if(end_level.gold_bonus) {
|
|
report << _("Early finish bonus") << ": "
|
|
<< finishing_bonus_per_turn
|
|
<< " " << _("per turn") << "\n"
|
|
<< _("Turns finished early") << ": "
|
|
<< turns_left << "\n"
|
|
<< _("Bonus") << ": "
|
|
<< finishing_bonus << "\n"
|
|
<< _("Gold") << ": "
|
|
<< (remaining_gold+finishing_bonus);
|
|
}
|
|
|
|
// xgettext:no-c-format
|
|
report << "\n" << _("80% of gold is retained for the next scenario") << "\n" << _("Retained Gold") << ": " << player->gold;
|
|
}
|
|
}
|
|
|
|
gui::show_dialog(gui,NULL,_("Victory"),
|
|
_("You have emerged victorious!"),gui::OK_ONLY);
|
|
|
|
if(state_of_game.players.size()>0) {
|
|
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,"",_("The file you have tried to load is corrupt"),
|
|
gui::OK_ONLY);
|
|
return QUIT;
|
|
}
|
|
catch(network::error& e) {
|
|
bool disconnect = false;
|
|
if(e.socket) {
|
|
e.disconnect();
|
|
disconnect = true;
|
|
}
|
|
|
|
turn_info turn_data(gameinfo,state_of_game,status,
|
|
game_config,level,key,gui,
|
|
map,teams,player_number,units,turn_info::BROWSE_NETWORKED,textbox_info,replay_sender);
|
|
|
|
turn_data.save_game(_("A network disconnection has occured, and the game cannot continue. Do you want to save the game?"),gui::YES_NO);
|
|
if(disconnect) {
|
|
throw network::error();
|
|
} else {
|
|
return QUIT;
|
|
}
|
|
}
|
|
|
|
return QUIT;
|
|
}
|