wesnoth/playlevel.cpp

294 lines
8.6 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 "game_events.hpp"
#include "intro.hpp"
#include "language.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include <iostream>
LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
config* level, CVideo& video,
game_state& state_of_game,
std::vector<config*>& story)
{
const int num_turns = atoi(level->values["turns"].c_str());
gamestatus status(num_turns);
gamemap map(terrain_config,read_file("data/maps/" + level->values["map"]));
CKey key;
typedef std::map<gamemap::location,unit> units_map;
units_map units;
std::vector<team> teams;
std::vector<config*>& unit_cfg = level->children["side"];
for(std::vector<config*>::iterator ui = unit_cfg.begin();
ui != unit_cfg.end(); ++ui) {
unit new_unit(gameinfo, **ui);
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;
}
}
}
std::string gold = (*ui)->values["gold"];
if(gold.empty())
gold = "100";
int ngold = ::atoi(gold.c_str());
if(ui == unit_cfg.begin() && state_of_game.gold >= 0)
ngold = state_of_game.gold;
units.insert(std::pair<gamemap::location,unit>(
map.starting_position(new_unit.side()), new_unit));
teams.push_back(team(**ui,ngold));
//if there are additional starting units on this side
std::vector<config*>& starting_units = (*ui)->children["unit"];
for(std::vector<config*>::iterator su = starting_units.begin();
su != starting_units.end(); ++su) {
unit new_unit(gameinfo,**su);
const std::string& x = (*su)->values["x"];
const std::string& y = (*su)->values["y"];
const gamemap::location loc(**su);
if(x.size() == 0 || y.size() == 0 || !map.on_board(loc)) {
state_of_game.available_units.push_back(new_unit);
} else {
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
}
}
}
display gui(units,video,map,status,teams);
const preferences::display_manager prefs_disp_manager(&gui);
if(recorder.skipping() == false) {
for(std::vector<config*>::iterator story_i = story.begin();
story_i != story.end(); ++story_i) {
show_intro(gui,**story_i);
}
show_map_scene(gui,*level);
}
const std::string& music = level->values["music"];
if(!music.empty()) {
sound::play_music(music);
}
game_events::manager events_manager(*level,gui,map,units,
state_of_game,gameinfo);
//find a list of 'items' (i.e. overlays) on the level, and add them
std::vector<config*>& overlays = level->children["item"];
for(std::vector<config*>::iterator overlay = overlays.begin();
overlay != overlays.end(); ++overlay) {
gui.add_overlay(gamemap::location(**overlay),
(*overlay)->values["image"]);
}
const double scroll_speed = 30.0;
const double zoom_amount = 5.0;
for(units_map::iterator i = units.begin(); i != units.end(); ++i) {
i->second.new_turn();
}
bool left_button = false, right_button = false;
gamemap::location selected_hex;
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,
display::WARP);
bool replaying = (recorder.empty() == false);
std::cout << "starting main loop\n";
for(bool first_time = true; true; first_time = false) {
try {
if(first_time) {
update_locker lock_display(gui,recorder.skipping());
game_events::fire("start");
}
gui.invalidate_game_status();
for(std::vector<team>::iterator team_it = teams.begin();
team_it != teams.end(); ++team_it) {
const int player_number = (team_it - teams.begin()) + 1;
calculate_healing(gui,map,units,player_number);
//scroll the map to the leader
const units_map::iterator leader =
find_leader(units,player_number);
if(leader != units.end() && !recorder.skipping()) {
gui.scroll_to_tile(leader->first.x,leader->first.y);
}
if(replaying) {
replaying = do_replay(gui,map,gameinfo,units,teams,
player_number,status,state_of_game);
}
if(!replaying && team_it->is_human()) {
play_turn(gameinfo,state_of_game,status,terrain_config,
level, video, key, gui, events_manager, map,
teams, player_number, units);
if(game_config::debug)
display::clear_debug_highlights();
} else if(!replaying) {
ai::do_move(gui,map,gameinfo,units,teams,
player_number,status);
gui.invalidate_unit();
gui.invalidate_game_status();
gui.invalidate_all();
gui.draw();
SDL_Delay(1000);
}
for(units_map::iterator uit = units.begin();
uit != units.end(); ++uit) {
if(uit->second.side() == player_number)
uit->second.end_turn();
}
game_events::pump();
const int victory = check_victory(units);
if(victory > 1) {
throw end_level_exception(DEFEAT);
} else if(victory == 1) {
throw end_level_exception(VICTORY);
}
}
//time has run out
if(!status.next_turn()) {
game_events::fire("time over");
throw end_level_exception(DEFEAT);
}
std::stringstream event_stream;
event_stream << "turn " << status.turn();
{
update_locker lock_display(gui,recorder.skipping());
game_events::fire(event_stream.str());
}
std::map<int,int> expenditure;
for(units_map::iterator i = units.begin();
i != units.end(); ++i) {
i->second.new_turn();
expenditure[i->second.side()]++;
}
int team_num = 1;
for(std::vector<team>::iterator it = teams.begin();
it != teams.end(); ++it, ++team_num) {
it->new_turn();
it->spend_gold(expenditure[team_num]);
}
} 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) {
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_level();
state_of_game.available_units.
push_back(un->second);
}
}
const int remaining_gold = teams[0].gold();
const int finishing_bonus_per_turn =
map.towers().size()*game_config::tower_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)/2;
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"] << ": "
<< 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) {
gui::show_dialog(gui,NULL,"","The file you loaded is corrupt "
"or from a different version of the game",gui::OK_ONLY);
return DEFEAT;
}
} //end for(;;)
return QUIT;
}