merges the gameplay_refactoring branch

please report any problems to me
This commit is contained in:
Jörg Hinrichs 2006-04-06 21:31:55 +00:00
commit 45dd38ff87
38 changed files with 4325 additions and 5148 deletions

View file

@ -54,6 +54,7 @@ wesnoth_SOURCES = \
dialogs.cpp \
display.cpp \
events.cpp \
floating_textbox.cpp \
font.cpp \
game.cpp \
game_events.cpp \
@ -86,7 +87,9 @@ wesnoth_SOURCES = \
network_worker.cpp \
pathfind.cpp \
playcampaign.cpp \
playlevel.cpp \
play_controller.cpp \
playmp_controller.cpp \
playsingle_controller.cpp \
playturn.cpp \
preferences.cpp \
preferences_display.cpp \
@ -156,6 +159,7 @@ wesnoth_editor_SOURCES = \
display.cpp \
events.cpp \
filechooser.cpp \
floating_textbox.cpp \
font.cpp \
game_events.cpp \
gamestatus.cpp \
@ -171,11 +175,16 @@ wesnoth_editor_SOURCES = \
mapgen.cpp \
mapgen_dialog.cpp \
marked-up_text.cpp \
menu_events.cpp \
minimap.cpp \
mouse.cpp \
mouse_events.cpp \
network.cpp \
network_worker.cpp \
pathfind.cpp \
play_controller.cpp \
playmp_controller.cpp \
playsingle_controller.cpp \
playturn.cpp \
preferences.cpp \
preferences_display.cpp \
@ -341,7 +350,6 @@ noinst_HEADERS = \
loadscreen.hpp \
builder.hpp \
publish_campaign.hpp \
playlevel.hpp \
game_config.hpp \
terrain.hpp \
config.hpp \
@ -403,6 +411,10 @@ noinst_HEADERS = \
preferences_display.hpp \
replay_controller.hpp \
config_adapter.hpp \
floating_textbox.hpp \
play_controller.hpp \
playmp_controller.hpp \
playsingle_controller.hpp \
sdl_ttf/SDL_ttf.h \
wesconfig.h

View file

@ -17,13 +17,16 @@
#include "game_errors.hpp"
#include "game_events.hpp"
#include "gettext.hpp"
#include "halo.hpp"
#include "hotkeys.hpp"
#include "menu_events.hpp"
#include "mouse_events.hpp"
#include "pathfind.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "random.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "unit_abilities.hpp"
#include "unit_display.hpp"
#include "wassert.hpp"
@ -90,7 +93,7 @@ std::string recruit_unit(const gamemap& map, int side,
unit_map& units, unit new_unit,
gamemap::location& recruit_location, display* disp, bool need_castle, bool full_movement)
{
const command_disabler disable_commands;
const events::command_disabler disable_commands;
LOG_NG << "recruiting unit for side " << side << "\n";
typedef std::map<gamemap::location,unit> units_map;
@ -790,7 +793,7 @@ void attack(display& gui, const gamemap& map,
bool update_display)
{
//stop the user from issuing any commands while the units are fighting
const command_disabler disable_commands;
const events::command_disabler disable_commands;
units_map::iterator a = units.find(attacker);
units_map::iterator d = units.find(defender);
@ -1436,6 +1439,7 @@ unit_map::const_iterator find_leader(const unit_map& units, int side)
return units.end();
}
// Simple algorithm: no maximum number of patients per healer.
void reset_resting(std::map<gamemap::location,unit>& units, unsigned int side)
{
for (unit_map::iterator i = units.begin(); i != units.end(); ++i) {
@ -1443,8 +1447,7 @@ void reset_resting(std::map<gamemap::location,unit>& units, unsigned int side)
i->second.set_resting(true);
}
}
// Simple algorithm: no maximum number of patients per healer.
void calculate_healing(display& disp, const gamestatus& status, const gamemap& map,
units_map& units, unsigned int side,
const std::vector<team>& teams, bool update_display)
@ -1964,7 +1967,7 @@ size_t move_unit(display* disp, const game_data& gamedata,
wassert(route.empty() == false);
//stop the user from issuing any commands while the unit is moving
const command_disabler disable_commands;
const events::command_disabler disable_commands;
unit_map::iterator ui = units.find(route.front());

View file

@ -127,6 +127,10 @@ unit_map::iterator find_leader(unit_map& units, int side);
unit_map::const_iterator find_leader(const unit_map& units, int side);
// Resets resting for all units on this side: should be called after calculate_healing().
// FIXME: Try moving this to unit::new_turn, then move it above calculate_healing().
void reset_resting(std::map<gamemap::location,unit>& units, unsigned int side);
//calculates healing for all units for the given side. Should be called
//at the beginning of a side's turn.
void calculate_healing(display& disp, const gamestatus& status, const gamemap& map,

View file

@ -20,6 +20,7 @@
#include "dialogs.hpp"
#include "game_config.hpp"
#include "game_events.hpp"
#include "menu_events.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "replay.hpp"
@ -296,7 +297,7 @@ bool ai_interface::recruit(const std::string& unit_name, location loc)
//confirm the transaction - i.e. don't undo recruitment
replay_guard.confirm_transaction();
sync_network();
info_.turn_data_.sync_network();
const team_data data = calculate_team_data(current_team(),info_.team_num,info_.units);
LOG_AI <<
"recruit confirmed: team=" << (info_.team_num) <<
@ -356,24 +357,6 @@ const team& ai_interface::current_team() const
return info_.teams[info_.team_num-1];
}
void ai_interface::sync_network()
{
if(network::nconnections() > 0) {
//receive data first, and then send data. When we sent the end of
//the AI's turn, we don't want there to be any chance where we
//could get data back pertaining to the next turn.
config cfg;
while(network::connection res = network::receive_data(cfg)) {
std::deque<config> backlog;
info_.turn_data_.process_network_data(cfg,res,backlog,false);
cfg.clear();
}
info_.turn_data_.send_data();
}
}
gamemap::location ai_interface::move_unit(location from, location to, std::map<location,paths>& possible_moves)
{
const location loc = move_unit_partial(from,to,possible_moves);
@ -394,7 +377,7 @@ gamemap::location ai_interface::move_unit_partial(location from, location to, st
{
LOG_AI << "ai_interface::move_unit " << from << " -> " << to << '\n';
//stop the user from issuing any commands while the unit is moving
const command_disabler disable_commands;
const events::command_disabler disable_commands;
wassert(info_.units.find(to) == info_.units.end() || from == to);
@ -510,7 +493,7 @@ gamemap::location ai_interface::move_unit_partial(location from, location to, st
}
info_.disp.unhighlight_reach();
sync_network();
info_.turn_data_.sync_network();
return to;
}
@ -1002,7 +985,7 @@ bool ai::do_combat(std::map<gamemap::location,paths>& possible_moves, const move
void ai_interface::attack_enemy(const location& u, const location& target, int weapon)
{
//stop the user from issuing any commands while the unit is attacking
const command_disabler disable_commands;
const events::command_disabler disable_commands;
if(info_.units.count(u) && info_.units.count(target)) {
if(info_.units.find(target)->second.get_state("stoned")=="yes") {
@ -1028,7 +1011,7 @@ void ai_interface::attack_enemy(const location& u, const location& target, int w
}
}
sync_network();
info_.turn_data_.sync_network();
}
}

View file

@ -75,9 +75,6 @@ public:
team& current_team();
const team& current_team() const;
///function to update network players as to what the AI has done so far this turn
void sync_network();
///function to show a diagnostic message on the screen
void diagnostic(const std::string& msg);

View file

@ -12,7 +12,7 @@
Full algorithm by Yogin. Typing and optimization by Rusty.
This code has lots of debugging. It is there for a reason: this
This code has lots of debugging. It is there for a reason: this
code is kinda tricky. Do not remove it.
*/
#include <cstring> // For memset
@ -423,168 +423,169 @@ void prob_matrix::receive_blow_a(unsigned damage, double hit_chance,
}
}
combatant::combatant(unsigned hp, unsigned max_hp, bool slowed,
bool could_ever_drain)
: hp_dist(could_ever_drain ? max_hp+1: hp+1), hp_(hp), max_hp_(max_hp),
slowed_(slowed)
{
}
// Select a weapon.
void combatant::set_weapon(unsigned num_attacks, bool drains, bool berserk,
bool swarm, bool firststrike)
{
base_num_attacks_ = num_attacks;
drains_ = drains;
berserk_ = berserk;
swarm_ = swarm;
firststrike_ = firststrike;
}
unsigned combatant::num_attacks(unsigned int hp) const
{
if (swarm_)
return base_num_attacks_ - (base_num_attacks_*(max_hp_-hp)/max_hp_);
else
return base_num_attacks_;
}
// Set effect against this particular opponent.
void combatant::set_effectiveness(unsigned damage, double hit_chance,
bool slows)
{
slows_ = slows;
base_hit_chance_ = hit_chance;
if (slowed_)
damage_ = damage / 2;
else
damage_ = damage;
if (!swarm_ || summary[0].empty())
hit_chances_ = std::vector<double>(num_attacks(hp_), hit_chance);
else {
// Whether we get an attack depends on HP distribution from previous
// combat. So we roll this into our P(hitting), since no attack is
// equivalent to missing.
hit_chances_ = std::vector<double>(base_num_attacks_);
double alive_prob;
if (summary[1].empty())
alive_prob = 1 - summary[0][0];
else
alive_prob = 1 - summary[0][0] - summary[1][0];
for (unsigned int i = 1; i <= max_hp_; i++) {
double prob = summary[0][i];
if (!summary[1].empty())
prob += summary[1][i];
for (unsigned int j = 0; j < num_attacks(i); j++)
hit_chances_[j] += prob * hit_chance / alive_prob;
}
}
debug(("\nhit_chances_ (base %u%%):", (unsigned)(hit_chance * 100.0 + 0.5)));
for (unsigned int i = 0; i < base_num_attacks_; i++)
debug((" %.2f", hit_chances_[i]));
debug(("\n"));
}
void combatant::reset()
{
for (unsigned int i = 0; i < hp_dist.size(); i++)
hp_dist[i] = 0.0;
summary[0] = std::vector<double>();
summary[1] = std::vector<double>();
hit_chances_ = std::vector<double>(num_attacks(hp_), base_hit_chance_);
}
// Two man enter. One man leave!
// ... Or maybe two. But definitely not three. Of course, one could
// be a woman. Or both. And neither could be human, too.
// Um, ok, it was a stupid thing to say.
void combatant::fight(combatant &opp)
{
unsigned int i, rounds = berserk_ || opp.berserk_ ? 30 : 1;
// If defender has firststrike and we don't, reverse.
if (opp.firststrike_ && !firststrike_) {
opp.fight(*this);
return;
}
debug(("A: %u %u %u %2g%% ",
damage_, base_num_attacks_, hp_, base_hit_chance_*100.0));
if (drains_)
debug(("drains,"));
if (slows_ && !opp.slowed_)
debug(("slows,"));
if (berserk_)
debug(("berserk,"));
if (swarm_)
debug(("swarm,"));
debug(("maxhp=%u\n", hp_dist.size()-1));
debug(("B: %u %u %u %2g%% ", opp.damage_, opp.base_num_attacks_, opp.hp_,
opp.base_hit_chance_*100.0));
if (opp.drains_)
debug(("drains,"));
if (opp.slows_ && !slowed_)
debug(("slows,"));
if (opp.berserk_)
debug(("berserk,"));
if (opp.swarm_)
debug(("swarm,"));
debug(("maxhp=%u\n", opp.hp_dist.size()-1));
prob_matrix m(hp_dist.size()-1, opp.hp_dist.size()-1,
slows_ && !opp.slowed_, opp.slows_ && !slowed_, hp_, opp.hp_,
summary, opp.summary);
unsigned max_attacks = maximum(hit_chances_.size(),
opp.hit_chances_.size());
debug(("A gets %u attacks, B %u\n",
hit_chances_.size(),
opp.hit_chances_.size()));
do {
for (i = 0; i < max_attacks; i++) {
if (i < hit_chances_.size()) {
debug(("A strikes\n"));
m.receive_blow_b(damage_, hit_chances_[i],
slows_ && !opp.slowed_, drains_);
m.dump();
}
if (i < opp.hit_chances_.size()) {
debug(("B strikes\n"));
m.receive_blow_a(opp.damage_, opp.hit_chances_[i],
opp.slows_ && !slowed_, opp.drains_);
m.dump();
}
}
debug(("Combat ends:\n"));
m.dump();
} while (--rounds && m.dead_prob() < 0.99);
// We extract results separately, then combine.
m.extract_results(summary, opp.summary);
if (summary[1].empty())
hp_dist = summary[0];
else {
for (i = 0; i < hp_dist.size(); i++)
hp_dist[i] = summary[0][i] + summary[1][i];
}
if (opp.summary[1].empty())
opp.hp_dist = opp.summary[0];
else {
for (i = 0; i < opp.hp_dist.size(); i++)
opp.hp_dist[i] = opp.summary[0][i] + opp.summary[1][i];
}
// FIXME: This is approximate: we could drain, then get hit.
untouched = hp_dist[hp_];
opp.untouched = opp.hp_dist[opp.hp_];
}
};
combatant::combatant(unsigned hp, unsigned max_hp, bool slowed,
bool could_ever_drain)
: hp_dist(could_ever_drain ? max_hp+1: hp+1), hp_(hp), max_hp_(max_hp),
slowed_(slowed)
{
}
// Select a weapon.
void combatant::set_weapon(unsigned num_attacks, bool drains, bool berserk,
bool swarm, bool firststrike)
{
base_num_attacks_ = num_attacks;
drains_ = drains;
berserk_ = berserk;
swarm_ = swarm;
firststrike_ = firststrike;
}
unsigned combatant::num_attacks(unsigned int hp) const
{
if (swarm_)
return base_num_attacks_ - (base_num_attacks_*(max_hp_-hp)/max_hp_);
else
return base_num_attacks_;
}
// Set effect against this particular opponent.
void combatant::set_effectiveness(unsigned damage, double hit_chance,
bool slows)
{
slows_ = slows;
base_hit_chance_ = hit_chance;
if (slowed_)
damage_ = damage / 2;
else
damage_ = damage;
if (!swarm_ || summary[0].empty())
hit_chances_ = std::vector<double>(num_attacks(hp_), hit_chance);
else {
// Whether we get an attack depends on HP distribution from previous
// combat. So we roll this into our P(hitting), since no attack is
// equivalent to missing.
hit_chances_ = std::vector<double>(base_num_attacks_);
double alive_prob;
if (summary[1].empty())
alive_prob = 1 - summary[0][0];
else
alive_prob = 1 - summary[0][0] - summary[1][0];
for (unsigned int i = 1; i <= max_hp_; i++) {
double prob = summary[0][i];
if (!summary[1].empty())
prob += summary[1][i];
for (unsigned int j = 0; j < num_attacks(i); j++)
hit_chances_[j] += prob * hit_chance / alive_prob;
}
}
debug(("\nhit_chances_ (base %u%%):", (unsigned)(hit_chance * 100.0 + 0.5)));
for (unsigned int i = 0; i < base_num_attacks_; i++)
debug((" %.2f", hit_chances_[i]));
debug(("\n"));
}
void combatant::reset()
{
for (unsigned int i = 0; i < hp_dist.size(); i++)
hp_dist[i] = 0.0;
summary[0] = std::vector<double>();
summary[1] = std::vector<double>();
hit_chances_ = std::vector<double>(num_attacks(hp_), base_hit_chance_);
}
// Two man enter. One man leave!
// ... Or maybe two. But definitely not three. Of course, one could
// be a woman. Or both. And neither could be human, too.
// Um, ok, it was a stupid thing to say.
void combatant::fight(combatant &opp)
{
unsigned int i, rounds = berserk_ || opp.berserk_ ? 30 : 1;
// If defender has firststrike and we don't, reverse.
if (opp.firststrike_ && !firststrike_) {
opp.fight(*this);
return;
}
debug(("A: %u %u %u %2g%% ",
damage_, base_num_attacks_, hp_, base_hit_chance_*100.0));
if (drains_)
debug(("drains,"));
if (slows_ && !opp.slowed_)
debug(("slows,"));
if (berserk_)
debug(("berserk,"));
if (swarm_)
debug(("swarm,"));
debug(("maxhp=%u\n", hp_dist.size()-1));
debug(("B: %u %u %u %2g%% ", opp.damage_, opp.base_num_attacks_, opp.hp_,
opp.base_hit_chance_*100.0));
if (opp.drains_)
debug(("drains,"));
if (opp.slows_ && !slowed_)
debug(("slows,"));
if (opp.berserk_)
debug(("berserk,"));
if (opp.swarm_)
debug(("swarm,"));
debug(("maxhp=%u\n", opp.hp_dist.size()-1));
prob_matrix m(hp_dist.size()-1, opp.hp_dist.size()-1,
slows_ && !opp.slowed_, opp.slows_ && !slowed_, hp_, opp.hp_,
summary, opp.summary);
unsigned max_attacks = maximum(hit_chances_.size(),
opp.hit_chances_.size());
debug(("A gets %u attacks, B %u\n",
hit_chances_.size(),
opp.hit_chances_.size()));
do {
for (i = 0; i < max_attacks; i++) {
if (i < hit_chances_.size()) {
debug(("A strikes\n"));
m.receive_blow_b(damage_, hit_chances_[i],
slows_ && !opp.slowed_, drains_);
m.dump();
}
if (i < opp.hit_chances_.size()) {
debug(("B strikes\n"));
m.receive_blow_a(opp.damage_, opp.hit_chances_[i],
opp.slows_ && !slowed_, opp.drains_);
m.dump();
}
}
debug(("Combat ends:\n"));
m.dump();
} while (--rounds && m.dead_prob() < 0.99);
// We extract results separately, then combine.
m.extract_results(summary, opp.summary);
if (summary[1].empty())
hp_dist = summary[0];
else {
for (i = 0; i < hp_dist.size(); i++)
hp_dist[i] = summary[0][i] + summary[1][i];
}
if (opp.summary[1].empty())
opp.hp_dist = opp.summary[0];
else {
for (i = 0; i < opp.hp_dist.size(); i++)
opp.hp_dist[i] = opp.summary[0][i] + opp.summary[1][i];
}
// FIXME: This is approximate: we could drain, then get hit.
untouched = hp_dist[hp_];
opp.untouched = opp.hp_dist[opp.hp_];
}
#if defined(BENCHMARK) || defined(CHECK)
// We create a significant number of nasty-to-calculate units, and

View file

@ -24,8 +24,8 @@
#include "log.hpp"
#include "map.hpp"
#include "marked-up_text.hpp"
#include "menu_events.hpp"
#include "minimap.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include "show_dialog.hpp"
@ -115,7 +115,7 @@ void advance_unit(const game_data& info,
bool animate_unit_advancement(const game_data& info,unit_map& units, gamemap::location loc, display& gui, size_t choice)
{
const command_disabler cmd_disabler;
const events::command_disabler cmd_disabler;
units_map::iterator u = units.find(loc);
if(u == units.end() || u->second.advances() == false) {

View file

@ -643,7 +643,7 @@ std::string next_filename(const std::string &dirname)
try {
num = lexical_cast<int>(*i)+1;
break;
} catch (bad_lexical_cast &c) {
} catch (bad_lexical_cast &) {
}
}
}

180
src/floating_textbox.cpp Normal file
View file

@ -0,0 +1,180 @@
#include "floating_textbox.hpp"
#include "font.hpp"
#include "preferences.hpp"
namespace gui{
floating_textbox::floating_textbox() : box_(NULL), check_(NULL), mode_(TEXTBOX_NONE), label_(0)
{}
void floating_textbox::close(display& gui)
{
if(!active()) {
return;
}
if(check_ != NULL) {
if(mode_ == TEXTBOX_MESSAGE) {
preferences::set_message_private(check_->checked());
}
}
box_.assign(NULL);
check_.assign(NULL);
font::remove_floating_label(label_);
mode_ = TEXTBOX_NONE;
gui.invalidate_all();
}
void floating_textbox::update_location(display& gui)
{
if (box_ == NULL)
return;
const SDL_Rect& area = gui.map_area();
const int border_size = 10;
const int ypos = area.y+area.h-30 - (check_ != NULL ? check_->height() + border_size : 0);
if (label_ != 0)
font::remove_floating_label(label_);
label_ = font::add_floating_label(label_string_,font::SIZE_NORMAL,
font::YELLOW_COLOUR,area.x+border_size,ypos,0,0,-1, area,font::LEFT_ALIGN);
if (label_ == 0)
return;
const SDL_Rect& label_area = font::get_floating_label_rect(label_);
const int textbox_width = area.w - label_area.w - border_size*3;
if(textbox_width <= 0) {
font::remove_floating_label(label_);
return;
}
if(box_ != NULL) {
box_->set_volatile(true);
const SDL_Rect rect = {
area.x + label_area.w + border_size*2, ypos,
textbox_width, box_->height()
};
box_->set_location(rect);
}
if(check_ != NULL) {
check_->set_volatile(true);
check_->set_location(box_->location().x,box_->location().y + box_->location().h + border_size);
}
}
void floating_textbox::show(gui::TEXTBOX_MODE mode, const std::string& label,
const std::string& check_label, bool checked, display& gui)
{
close(gui);
label_string_ = label;
mode_ = mode;
if(check_label != "") {
check_.assign(new gui::button(gui.video(),check_label,gui::button::TYPE_CHECK));
check_->set_check(checked);
}
box_.assign(new gui::textbox(gui.video(),100,"",true,256,0.8,0.6));
update_location(gui);
}
void floating_textbox::tab(std::vector<team>& teams, const unit_map& units, display& gui)
{
if(active() == false) {
return;
}
switch(mode_) {
case gui::TEXTBOX_MESSAGE:
{
std::string text = box_->text();
std::string semiword;
bool beginning;
const size_t last_space = text.rfind(" ");
//if last character is a space return
if(last_space == text.size() -1) {
return;
}
if(last_space == std::string::npos) {
beginning = true;
semiword = text;
}else{
beginning = false;
semiword.assign(text,last_space+1,text.size());
}
std::vector<std::string> matches;
std::string best_match = semiword;
for(size_t n = 0; n != teams.size(); ++n) {
if(teams[n].is_empty()) {
continue;
}
const unit_map::const_iterator leader = team_leader(n+1,units);
if(leader != units.end()) {
const std::string& name = leader->second.description();
if( name.size() >= semiword.size() &&
std::equal(semiword.begin(),semiword.end(),name.begin(),chars_equal_insensitive)) {
if(matches.empty()) {
best_match = name;
} else {
int j= 0;;
while(best_match[j] == name[j]) j++;
best_match.erase(best_match.begin()+j,best_match.end());
}
matches.push_back(name);
}
}
}
if(matches.empty()) {
const std::set<std::string>& observers = gui.observers();
for(std::set<std::string>::const_iterator i = observers.begin(); i != observers.end(); ++i) {
if( i->size() >= semiword.size() &&
std::equal(semiword.begin(),semiword.end(),i->begin(),chars_equal_insensitive)) {
if(matches.empty()) {
best_match = *i;
} else {
int j;
while(toupper(best_match[j]) == toupper((*i)[j])) j++;
best_match.erase(best_match.begin()+j,best_match.end());
}
matches.push_back(*i);
}
}
}
if(!matches.empty()) {
std::string add = beginning ? ": " : " ";
text.replace(last_space+1, semiword.size(), best_match);
if(matches.size() == 1) {
text.append(add);
} else {
std::string completion_list;
std::vector<std::string>::iterator it;
for(it =matches.begin();it!=matches.end();it++) {
completion_list += " ";
completion_list += *it;
}
gui.add_chat_message("",0,completion_list,display::MESSAGE_PRIVATE);
}
box_->set_text(text);
}
break;
}
default:
LOG_STREAM(err, display) << "unknown textbox mode\n";
}
}
}

42
src/floating_textbox.hpp Normal file
View file

@ -0,0 +1,42 @@
#ifndef FLOATING_TEXTBOX_H_INCLUDED
#define FLOATING_TEXTBOX_H_INCLUDED
#include "global.hpp"
#include "display.hpp"
#include "scoped_resource.hpp"
#include "team.hpp"
#include "unit.hpp"
#include "widgets/textbox.hpp"
namespace gui{
enum TEXTBOX_MODE { TEXTBOX_NONE, TEXTBOX_SEARCH, TEXTBOX_MESSAGE,
TEXTBOX_COMMAND };
class floating_textbox{
public:
floating_textbox();
const TEXTBOX_MODE mode() const { return mode_; }
const util::scoped_ptr<gui::button> check() const { return check_; }
const util::scoped_ptr<gui::textbox> box() const { return box_; }
void close(display& gui);
void update_location(display& gui);
void show(gui::TEXTBOX_MODE mode, const std::string& label,
const std::string& check_label, bool checked, display& gui);
void tab(std::vector<team>& teams, const unit_map& units, display& gui);
bool active() const { return box_.get() != NULL; }
private:
util::scoped_ptr<gui::textbox> box_;
util::scoped_ptr<gui::button> check_;
TEXTBOX_MODE mode_;
std::string label_string_;
int label_;
};
}
#endif

View file

@ -14,14 +14,13 @@
#include "global.hpp"
#include "actions.hpp"
#include "ai.hpp"
#include "display.hpp"
#include "game_errors.hpp"
#include "game_events.hpp"
#include "image.hpp"
#include "language.hpp"
#include "log.hpp"
#include "playlevel.hpp"
#include "menu_events.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include "show_dialog.hpp"

View file

@ -19,8 +19,9 @@
#include "display.hpp"
#include "events.hpp"
#include "hotkeys.hpp"
#include "game_config.hpp"
#include "gettext.hpp"
#include "playlevel.hpp"
#include "menu_events.hpp"
#include "preferences_display.hpp"
#include "show_dialog.hpp"
#include "util.hpp"

View file

@ -165,8 +165,7 @@ bool show_intro_part(display &disp, const config& part,
//draw title if needed
if(show_title) {
const SDL_Rect area = {0,0,video.getx(),video.gety()};
const SDL_Rect scenario_size =
font::draw_text(NULL,area,font::SIZE_XLARGE,font::NORMAL_COLOUR,scenario,0,0);
font::draw_text(NULL,area,font::SIZE_XLARGE,font::NORMAL_COLOUR,scenario,0,0);
update_rect(font::draw_text(&video,area,font::SIZE_XLARGE,font::NORMAL_COLOUR,scenario,
dstrect.x + 20,dstrect.y + 20));
}

View file

@ -94,7 +94,7 @@ bool load_language_list()
try {
scoped_istream stream = preprocess_file("data/language.cfg");
read(cfg, *stream);
} catch(config::error &e) {
} catch(config::error &) {
return false;
}

File diff suppressed because it is too large Load diff

View file

@ -3,30 +3,109 @@
#include "global.hpp"
#include "dialogs.hpp"
#include "gamestatus.hpp"
#include "statistics.hpp"
#include "dialogs.hpp"
#include "display.hpp"
#include "floating_textbox.hpp"
#include "gamestatus.hpp"
#include "mouse_events.hpp"
#include "statistics.hpp"
#include "widgets/textbox.hpp"
enum LEVEL_RESULT { VICTORY, DEFEAT, QUIT, LEVEL_CONTINUE, LEVEL_CONTINUE_NO_SAVE };
struct end_level_exception {
end_level_exception(LEVEL_RESULT res, bool bonus=true)
: result(res), gold_bonus(bonus)
{}
LEVEL_RESULT result;
bool gold_bonus;
};
struct end_turn_exception {
end_turn_exception(unsigned int r = 0): redo(r) {}
unsigned int redo;
};
namespace events{
class menu_handler{
public:
void objectives(display& gui, const config& level, team& current_team);
void show_statistics(display& gui, const game_data& gameinfo);
void unit_list(const unit_map& units, display& gui, const gamemap& map);
void status_table(std::vector<team>& teams, display& gui, const unit_map& units);
void save_game(const std::string& message, gui::DIALOG_TYPE dialog_type,
const game_state& gamestate, const gamestatus& status, display& gui,
const config& level, std::vector<team>& teams, const unit_map& units, const gamemap& map);
void load_game(display& gui, const config& terrain_config, const game_data& gameinfo);
void preferences(display& gui, const config& terrain_config);
void show_chat_log(std::vector<team>& teams, display& gui);
void show_help(display& gui);
menu_handler(display* gui, unit_map& units, std::vector<team>& teams,
const config& level, const game_data& gameinfo, const gamemap& map,
const config& game_config, const gamestatus& status, game_state& gamestate,
undo_list& undo_stack, undo_list& redo_stack);
const undo_list& get_undo_list() const;
gui::floating_textbox& get_textbox();
void set_gui(display* gui) { gui_ = gui; }
void objectives(const int team_num);
void show_statistics();
void unit_list();
void status_table();
void save_game(const std::string& message, gui::DIALOG_TYPE dialog_type);
void load_game();
void preferences();
void show_chat_log();
void show_help();
void speak();
void recruit(const bool browse, const int team_num, const gamemap::location& last_hex);
void repeat_recruit(const int team_num, const gamemap::location& last_hex);
void recall(const int team_num, const gamemap::location& last_hex);
void undo(const int team_num, mouse_handler& mousehandler);
void redo(const int team_num, mouse_handler& mousehandler);
void show_enemy_moves(bool ignore_units, const int team_num);
void toggle_shroud_updates(const int team_num);
void update_shroud_now(const int team_num);
void end_turn(const int team_num);
void goto_leader(const int team_num);
void unit_description(mouse_handler& mousehandler);
void rename_unit(mouse_handler& mousehandler);
void create_unit(mouse_handler& mousehandler);
void change_unit_side(mouse_handler& mousehandler);
void label_terrain(mouse_handler& mousehandler);
void clear_labels();
void continue_move(mouse_handler& mousehandler, const int team_num);
void toggle_grid();
void unit_hold_position(mouse_handler& mousehandler, const int team_num);
void end_unit_turn(mouse_handler& mousehandler, const int team_num);
void search();
void user_command();
unit_map::iterator current_unit(mouse_handler& mousehandler);
unit_map::const_iterator current_unit(const mouse_handler& mousehandler) const;
void move_unit_to_loc(const unit_map::const_iterator& ui, const gamemap::location& target,
bool continue_move, const int team_num, mouse_handler& mousehandler);
void do_speak();
void do_search(const std::string& new_search);
void do_command(const std::string& str, const int team_num, mouse_handler& mousehandler);
void clear_undo_stack(const int team_num);
private:
std::vector<std::string> create_unit_table(const statistics::stats::str_int_map& m, const game_data& gameinfo);
void write_game_snapshot(config& start, const config& level, display& gui,
std::vector<team>& teams, const unit_map& units, const gamestatus& status,
const game_state& gamestate, const gamemap& map) const;
void do_speak(const std::string& message, bool allies_only);
void do_recruit(const std::string& name, const int team_num, const gamemap::location& last_hex);
std::vector<std::string> create_unit_table(const statistics::stats::str_int_map& m);
void write_game_snapshot(config& start) const;
bool has_friends() const;
bool clear_shroud(const int team_num);
void change_side_controller(const std::string& side, const std::string& player, bool orphan_side=false);
display* gui_;
unit_map& units_;
std::vector<team>& teams_;
const config& level_;
const game_data& gameinfo_;
const gamemap& map_;
const config& game_config_;
const gamestatus& status_;
game_state& gamestate_;
undo_list& undo_stack_;
undo_list& redo_stack_;
gui::floating_textbox textbox_info_;
std::string last_search_;
gamemap::location last_search_hit_;
std::string last_recruit_;
};
}

View file

@ -1,13 +1,31 @@
#include "mouse_events.hpp"
#include "cursor.hpp"
#include "dialogs.hpp"
#include "game_events.hpp"
#include "gettext.hpp"
#include "marked-up_text.hpp"
#include "menu_events.hpp"
#include "preferences_display.hpp"
#include "replay.hpp"
#include "show_dialog.hpp"
#include "wassert.hpp"
#include "wml_separators.hpp"
namespace events{
int commands_disabled = 0;
command_disabler::command_disabler()
{
++commands_disabled;
}
command_disabler::~command_disabler()
{
--commands_disabled;
}
bool command_active()
{
#ifdef __APPLE__
@ -17,29 +35,130 @@ bool command_active()
#endif
}
mouse_handler::mouse_handler(display* gui, std::vector<team>& teams, const unit_map& units, gamemap& map,
gamestatus& status, const game_data& gameinfo):
gui_(gui), teams_(teams), units_(units), map_(map), status_(status), gameinfo_(gameinfo)
namespace{
//which attack is the better one to select for the player by default
//(the player can change the selected weapon if desired)
class simple_attack_rating
{
public:
simple_attack_rating() {}
simple_attack_rating(const battle_stats& stats) : stats_(stats) {}
bool operator<(const simple_attack_rating& a) const
{
//if our weapon can kill the enemy in one blow, the enemy does not
//drain back and our weapon has more blows, prefer our weapon
if(stats_.damage_defender_takes >= stats_.defender_hp &&
stats_.amount_defender_drains == 0 &&
stats_.nattacks > a.stats_.nattacks)
{
return false;
}
int this_avg_damage_dealt = stats_.chance_to_hit_defender *
stats_.damage_defender_takes * stats_.nattacks;
int this_avg_damage_taken = stats_.chance_to_hit_attacker *
stats_.damage_attacker_takes * stats_.ndefends;
int other_avg_damage_dealt = a.stats_.chance_to_hit_defender *
a.stats_.damage_defender_takes * a.stats_.nattacks;
int other_avg_damage_taken = a.stats_.chance_to_hit_attacker *
a.stats_.damage_attacker_takes * a.stats_.ndefends;
//if our weapon does less damage, it's worse
if(this_avg_damage_dealt < other_avg_damage_dealt)
return true;
//if both weapons are the same but
//ours makes the enemy retaliate for more damage, it's worse
else if(this_avg_damage_dealt == other_avg_damage_dealt &&
this_avg_damage_taken > other_avg_damage_taken)
return true;
//otherwise, ours is at least as good a default weapon
return false;
}
private:
battle_stats stats_;
};
class attack_calculations_displayer : public gui::dialog_button_action
{
public:
typedef std::vector< battle_stats_strings > stats_vector;
attack_calculations_displayer(display &disp, stats_vector const &stats)
: disp_(disp), stats_(stats)
{}
RESULT button_pressed(int selection);
private:
display &disp_;
stats_vector const &stats_;
};
gui::dialog_button_action::RESULT attack_calculations_displayer::button_pressed(int selection)
{
const size_t index = size_t(selection);
if(index < stats_.size()) {
battle_stats_strings const &sts = stats_[index];
std::vector< std::string > sts_att = sts.attack_calculations,
sts_def = sts.defend_calculations,
calcs;
unsigned sts_att_sz = sts_att.size(),
sts_def_sz = sts_def.size(),
sts_sz = maximum< unsigned >(sts_att_sz, sts_def_sz);
std::stringstream str;
str << _("Attacker") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR;
if (sts_def_sz > 0)
str << _("Defender");
calcs.push_back(str.str());
for(unsigned i = 0; i < sts_sz; ++i) {
std::stringstream str;
if (i < sts_att_sz)
str << sts_att[i];
else
str << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR << ' ';
str << COLUMN_SEPARATOR;
if (i < sts_def_sz)
str << sts_def[i];
else
str << ' ' << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR << ' ';
calcs.push_back(str.str());
}
gui::show_dialog(disp_, NULL, "", _("Damage Calculations"), gui::OK_ONLY, &calcs);
}
return NO_EFFECT;
}
} //end anonymous namespace
mouse_handler::mouse_handler(display* gui, std::vector<team>& teams, unit_map& units, gamemap& map,
gamestatus& status, const game_data& gameinfo, undo_list& undo_stack, undo_list& redo_stack):
gui_(gui), teams_(teams), units_(units), map_(map), status_(status), gameinfo_(gameinfo),
undo_stack_(undo_stack), redo_stack_(redo_stack)
{
minimap_scrolling_ = false;
last_nearest_ = gamemap::location::NORTH;
last_second_nearest_ = gamemap::location::NORTH;
enemy_paths_ = false;
browse_ = false;
path_turns_ = 0;
undo_ = false;
show_menu_ = false;
}
bool mouse_handler::browse(){
return browse_;
}
void mouse_handler::mouse_motion(const SDL_MouseMotionEvent& event, const int player_number)
void mouse_handler::mouse_motion(const SDL_MouseMotionEvent& event, const int player_number, const bool browse)
{
team_num_ = player_number;
mouse_motion(event.x,event.y);
mouse_motion(event.x,event.y, browse);
}
void mouse_handler::mouse_motion(int x, int y)
void mouse_handler::mouse_motion(int x, int y, const bool browse)
{
if(minimap_scrolling_) {
//if the game is run in a window, we could miss a LMB/MMB up event
@ -89,7 +208,7 @@ void mouse_handler::mouse_motion(int x, int y)
attack_from.valid())) {
if(mouseover_unit == units_.end()) {
cursor::set(cursor::MOVE);
} else if(current_team().is_enemy(mouseover_unit->second.side()) && mouseover_unit->second.get_state("stoned")!="yes") {
} else if(viewing_team().is_enemy(mouseover_unit->second.side()) && mouseover_unit->second.get_state("stoned")!="yes") {
cursor::set(cursor::ATTACK);
} else {
cursor::set(cursor::NORMAL);
@ -132,7 +251,7 @@ void mouse_handler::mouse_motion(int x, int y)
current_route_.move_left = route_turns_to_complete(un->second,map_,current_route_);
if(!browse_) {
if(!browse) {
(*gui_).set_route(&current_route_);
}
}
@ -140,7 +259,7 @@ void mouse_handler::mouse_motion(int x, int y)
unit_map::const_iterator un = find_unit(new_hex);
if(un != units_.end() && /* un->second.side() != player_number && */
if(un != units_.end() && un->second.side() != team_num_ &&
current_paths_.routes.empty() && !(*gui_).fogged(un->first.x,un->first.y)) {
//Quick-Hack because of problems with passing a constant Reference
unit un2 = un->second;
@ -160,7 +279,22 @@ void mouse_handler::mouse_motion(int x, int y)
last_second_nearest_ = second_nearest_hex;
}
unit_map::const_iterator mouse_handler::find_unit(const gamemap::location& hex)
unit_map::iterator mouse_handler::selected_unit()
{
unit_map::iterator res = find_unit(selected_hex_);
if(res != units_.end()) {
return res;
} else {
return find_unit(last_hex_);
}
}
unit_map::iterator mouse_handler::find_unit(const gamemap::location& hex)
{
return find_visible_unit(units_,hex,map_,status_.get_time_of_day().lawful_bonus,teams_,viewing_team());
}
unit_map::const_iterator mouse_handler::find_unit(const gamemap::location& hex) const
{
return find_visible_unit(units_,hex,map_,status_.get_time_of_day().lawful_bonus,teams_,viewing_team());
}
@ -234,19 +368,23 @@ const unit_map& mouse_handler::visible_units()
return visible_units_;
}
void mouse_handler::mouse_press(const SDL_MouseButtonEvent& event, const int player_number)
void mouse_handler::mouse_press(const SDL_MouseButtonEvent& event, const int player_number, const bool browse)
{
show_menu_ = false;
team_num_ = player_number;
mouse_motion(event.x, event.y);
mouse_motion(event.x, event.y, browse);
if(is_left_click(event) && event.state == SDL_RELEASED) {
minimap_scrolling_ = false;
} else if(is_middle_click(event) && event.state == SDL_RELEASED) {
minimap_scrolling_ = false;
} else if(is_left_click(event) && event.state == SDL_PRESSED) {
left_click(event);
left_click(event, browse);
} else if(is_right_click(event) && event.state == SDL_PRESSED) {
if(!current_paths_.routes.empty()) {
// FIXME: when it's not our turn, movement gets highlighted
// merely by mousing over. This hack means we don't require a
// two clicks to access right menu.
if (gui_->viewing_team() == team_num_-1 && !current_paths_.routes.empty()) {
selected_hex_ = gamemap::location();
gui_->select_hex(gamemap::location());
gui_->unhighlight_reach();
@ -259,7 +397,7 @@ void mouse_handler::mouse_press(const SDL_MouseButtonEvent& event, const int pla
gui_->draw(); // redraw highlight (and maybe some more)
const theme::menu* const m = gui_->get_theme().context_menu();
if (m != NULL)
;//show_menu(m->items(),event.x,event.y,true);
show_menu_ = true;
else
LOG_STREAM(warn, display) << "no context menu found...\n";
}
@ -315,8 +453,9 @@ bool mouse_handler::is_right_click(const SDL_MouseButtonEvent& event)
return event.button == SDL_BUTTON_RIGHT || event.button == SDL_BUTTON_LEFT && command_active();
}
void mouse_handler::left_click(const SDL_MouseButtonEvent& event)
void mouse_handler::left_click(const SDL_MouseButtonEvent& event, const bool browse)
{
undo_ = false;
if(commands_disabled) {
return;
}
@ -334,11 +473,11 @@ void mouse_handler::left_click(const SDL_MouseButtonEvent& event)
gamemap::location::DIRECTION nearest_hex, second_nearest_hex;
gamemap::location hex = gui_->hex_clicked_on(event.x,event.y,&nearest_hex,&second_nearest_hex);
unit_map::const_iterator u = find_unit(selected_hex_);
unit_map::iterator u = find_unit(selected_hex_);
//if the unit is selected and then itself clicked on,
//any goto command is cancelled
if(u != units_.end() && !browse_ && selected_hex_ == hex && u->second.side() == team_num_) {
if(u != units_.end() && !browse && selected_hex_ == hex && u->second.side() == team_num_) {
((unit) u->second).set_goto(gamemap::location());
}
@ -347,12 +486,57 @@ void mouse_handler::left_click(const SDL_MouseButtonEvent& event)
route = enemy_paths_ ? current_paths_.routes.end() :
current_paths_.routes.find(hex);
unit_map::const_iterator enemy = find_unit(hex);
unit_map::iterator enemy = find_unit(hex);
const gamemap::location src = selected_hex_;
paths orig_paths = current_paths_;
{
//see if we're trying to do a move-and-attack
if(!browse && u != units_.end() && enemy != units_.end() && !current_route_.steps.empty()) {
const gamemap::location& attack_from = current_unit_attacks_from(hex, nearest_hex, second_nearest_hex);
if(attack_from.valid()) {
if(move_unit_along_current_route(false)) { //move the unit without updating shroud
u = find_unit(attack_from);
// enemy = find_unit(hex);
if(u != units_.end() && u->second.side() == team_num_ &&
enemy != units_.end() && current_team().is_enemy(enemy->second.side()) && enemy->second.get_state("stoned")!="yes") {
if(attack_enemy(u,enemy) == false) {
undo_ = true;
selected_hex_ = src;
gui_->select_hex(src);
current_paths_ = orig_paths;
gui_->highlight_reach(current_paths_);
return;
}
}
}
if(clear_shroud(*gui_, status_, map_, gameinfo_, units_, teams_, team_num_ - 1)) {
clear_undo_stack();
}
return;
}
}
//see if we're trying to attack an enemy
if(u != units_.end() && route != current_paths_.routes.end() && enemy != units_.end() &&
hex != selected_hex_ && !browse &&
enemy->second.side() != u->second.side() &&
current_team().is_enemy(enemy->second.side())) {
attack_enemy(u,enemy);
}
//otherwise we're trying to move to a hex
else if(!browse && selected_hex_.valid() && selected_hex_ != hex &&
units_.count(selected_hex_) && !enemy_paths_ &&
enemy == units_.end() && !current_route_.steps.empty() &&
current_route_.steps.front() == selected_hex_) {
move_unit_along_current_route();
if(clear_shroud(*gui_, status_, map_, gameinfo_, units_, teams_, team_num_ - 1)) {
clear_undo_stack();
}
} else {
gui_->unhighlight_reach();
current_paths_ = paths();
@ -361,7 +545,7 @@ void mouse_handler::left_click(const SDL_MouseButtonEvent& event)
current_route_.steps.clear();
gui_->set_route(NULL);
unit_map::const_iterator it = find_unit(hex);
const unit_map::iterator it = find_unit(hex);
if(it != units_.end() && it->second.side() == team_num_ && !gui_->fogged(it->first.x,it->first.y)) {
const bool ignore_zocs = it->second.get_ability_bool("skirmisher",it->first);
@ -396,10 +580,204 @@ void mouse_handler::left_click(const SDL_MouseButtonEvent& event)
route.move_left = route_turns_to_complete(it->second,map_,route);
gui_->set_route(&route);
}
game_events::fire("select",hex);
}
}
}
void mouse_handler::clear_undo_stack()
{
if(teams_[team_num_ - 1].auto_shroud_updates() == false)
apply_shroud_changes(undo_stack_,gui_,status_,map_,gameinfo_,units_,teams_,team_num_-1);
undo_stack_.clear();
}
bool mouse_handler::move_unit_along_current_route(bool check_shroud)
{
const std::vector<gamemap::location> steps = current_route_.steps;
if(steps.empty()) {
return false;
}
const size_t moves = ::move_unit(gui_,gameinfo_,status_,map_,units_,teams_,
steps,&recorder,&undo_stack_,&next_unit_,false,check_shroud);
cursor::set(cursor::NORMAL);
gui_->invalidate_game_status();
selected_hex_ = gamemap::location();
gui_->select_hex(gamemap::location());
gui_->set_route(NULL);
gui_->unhighlight_reach();
current_paths_ = paths();
if(moves == 0)
return false;
redo_stack_.clear();
wassert(moves <= steps.size());
const gamemap::location& dst = steps[moves-1];
const unit_map::const_iterator u = units_.find(dst);
//u may be equal to units_.end() in the case of e.g. a [teleport]
if(u != units_.end()) {
//Reselect the unit if the move was interrupted
if(dst != steps.back()) {
selected_hex_ = dst;
gui_->select_hex(dst);
}
current_route_.steps.clear();
show_attack_options(u);
if(current_paths_.routes.empty() == false) {
current_paths_.routes[dst] = paths::route();
selected_hex_ = dst;
gui_->select_hex(dst);
gui_->highlight_reach(current_paths_);
}
}
return moves == steps.size();
}
bool mouse_handler::attack_enemy(unit_map::iterator attacker, unit_map::iterator defender)
{
//we must get locations by value instead of by references, because the iterators
//may become invalidated later
const gamemap::location attacker_loc = attacker->first;
const gamemap::location defender_loc = defender->first;
const std::vector<attack_type>& attacks = attacker->second.attacks();
std::vector<std::string> items;
std::vector<int> weapons;
int best_weapon_index = -1;
simple_attack_rating best_weapon_rating;
attack_calculations_displayer::stats_vector stats;
for(size_t a = 0; a != attacks.size(); ++a) {
// skip weapons with attack_weight=0
if (attacks[a].attack_weight() > 0){
weapons.push_back(a);
battle_stats_strings sts;
battle_stats st = evaluate_battle_stats(map_, teams_, attacker_loc, defender_loc,
a, units_, status_, gameinfo_, 0, &sts);
stats.push_back(sts);
simple_attack_rating weapon_rating(st);
if (best_weapon_index < 0 || best_weapon_rating < weapon_rating) {
best_weapon_index = items.size();
best_weapon_rating = weapon_rating;
}
//if there is an attack special or defend special, we output a single space for the other unit, to make sure
//that the attacks line up nicely.
std::string special_pad = (sts.attack_special.empty() && sts.defend_special.empty()) ? "" : " ";
int damage_defender_takes;
damage_defender_takes = st.damage_defender_takes;
int damage_attacker_takes;
damage_attacker_takes = st.damage_attacker_takes;
std::stringstream att;
att << IMAGE_PREFIX << sts.attack_icon << COLUMN_SEPARATOR
<< font::BOLD_TEXT << sts.attack_name << "\n" << damage_defender_takes << "-"
<< st.nattacks << " " << sts.range << " (" << st.chance_to_hit_defender << "%)\n"
<< sts.attack_special << special_pad
<< COLUMN_SEPARATOR << _("vs") << COLUMN_SEPARATOR
<< font::BOLD_TEXT << sts.defend_name << "\n" << damage_attacker_takes << "-"
<< st.ndefends << " " << sts.range << " (" << st.chance_to_hit_attacker << "%)\n"
<< sts.defend_special << special_pad << COLUMN_SEPARATOR
<< IMAGE_PREFIX << sts.defend_icon;
items.push_back(att.str());
}
}
if (best_weapon_index >= 0) {
items[best_weapon_index] = DEFAULT_ITEM + items[best_weapon_index];
}
//make it so that when we attack an enemy, the attacking unit
//is again shown in the status bar, so that we can easily
//compare between the attacking and defending unit
gui_->highlight_hex(gamemap::location());
gui_->draw(true,true);
attack_calculations_displayer calc_displayer(*gui_,stats);
std::vector<gui::dialog_button> buttons;
buttons.push_back(gui::dialog_button(&calc_displayer,_("Damage Calculations")));
int res = 0;
{
const events::event_context dialog_events_context;
dialogs::unit_preview_pane attacker_preview(*gui_,&map_,attacker->second,dialogs::unit_preview_pane::SHOW_BASIC,true);
dialogs::unit_preview_pane defender_preview(*gui_,&map_,defender->second,dialogs::unit_preview_pane::SHOW_BASIC,false);
std::vector<gui::preview_pane*> preview_panes;
preview_panes.push_back(&attacker_preview);
preview_panes.push_back(&defender_preview);
res = gui::show_dialog(*gui_,NULL,_("Attack Enemy"),
_("Choose weapon:")+std::string("\n"),
gui::OK_CANCEL,&items,&preview_panes,"",NULL,-1,NULL,NULL,-1,-1,
NULL,&buttons);
}
cursor::set(cursor::NORMAL)
;
if(size_t(res) < weapons.size()) {
attacker->second.set_goto(gamemap::location());
clear_undo_stack();
redo_stack_.clear();
current_paths_ = paths();
gui_->unhighlight_reach();
gui_->invalidate_all();
gui_->draw();
const bool defender_human = teams_[defender->second.side()-1].is_human();
recorder.add_attack(attacker_loc,defender_loc,weapons[res]);
//MP_COUNTDOWN grant time bonus for attacking
current_team().set_action_bonus_count(1 + current_team().action_bonus_count());
try {
attack(*gui_,map_,teams_,attacker_loc,defender_loc,weapons[res],units_,status_,gameinfo_);
} catch(end_level_exception&) {
//if the level ends due to a unit being killed, still see if
//either the attacker or defender should advance
dialogs::advance_unit(gameinfo_,map_,units_,attacker_loc,*gui_);
dialogs::advance_unit(gameinfo_,map_,units_,defender_loc,*gui_,!defender_human);
throw;
}
dialogs::advance_unit(gameinfo_,map_,units_,attacker_loc,*gui_);
dialogs::advance_unit(gameinfo_,map_,units_,defender_loc,*gui_,!defender_human);
selected_hex_ = gamemap::location();
current_route_.steps.clear();
gui_->set_route(NULL);
check_victory(units_,teams_);
gui_->invalidate_all();
gui_->draw(); //clear the screen
return true;
} else {
return false;
}
}
void mouse_handler::show_attack_options(unit_map::const_iterator u)
{
team& current_team = teams_[team_num_-1];
@ -415,4 +793,109 @@ void mouse_handler::show_attack_options(unit_map::const_iterator u)
}
}
bool mouse_handler::unit_in_cycle(unit_map::const_iterator it)
{
if(it->second.side() == team_num_ && unit_can_move(it->first,units_,map_,teams_) && it->second.user_end_turn() == false && !gui_->fogged(it->first.x,it->first.y)) {
bool is_enemy = current_team().is_enemy(int(gui_->viewing_team()+1));
return is_enemy == false || it->second.invisible(map_.underlying_union_terrain(it->first),status_.get_time_of_day().lawful_bonus,it->first,units_,teams_) == false;
}
return false;
}
void mouse_handler::cycle_units()
{
unit_map::const_iterator it = units_.find(next_unit_);
if(it != units_.end()) {
for(++it; it != units_.end(); ++it) {
if(unit_in_cycle(it)) {
break;
}
}
}
if(it == units_.end()) {
for(it = units_.begin(); it != units_.end(); ++it) {
if(unit_in_cycle(it)) {
break;
}
}
}
if(it != units_.end() && !gui_->fogged(it->first.x,it->first.y)) {
const bool ignore_zocs = it->second.get_ability_bool("skirmisher",it->first);
const bool teleport = it->second.get_ability_bool("teleport",it->first);
current_paths_ = paths(map_,status_,gameinfo_,units_,it->first,teams_,ignore_zocs,teleport,viewing_team(),path_turns_);
gui_->highlight_reach(current_paths_);
gui_->scroll_to_tile(it->first.x,it->first.y,display::WARP);
}
if(it != units_.end()) {
next_unit_ = it->first;
selected_hex_ = next_unit_;
gui_->select_hex(selected_hex_);
gui_->highlight_hex(selected_hex_);
current_route_.steps.clear();
gui_->set_route(NULL);
last_hex_=gamemap::location(-1,-1);
int mousex, mousey;
SDL_GetMouseState(&mousex, &mousey);
mouse_motion(mousex, mousey, true);
show_attack_options(it);
} else {
next_unit_ = gamemap::location();
}
}
void mouse_handler::cycle_back_units()
{
unit_map::const_iterator it = units_.find(next_unit_);
if(it != units_.begin()) {
for(--it; it != units_.begin(); --it) {
if(unit_in_cycle(it)) {
break;
}
}
}
if(it == units_.begin()) {
for(it = units_.end(); it != units_.begin(); --it) {
if(unit_in_cycle(it)) {
break;
}
}
}
if(it != units_.begin() && !gui_->fogged(it->first.x,it->first.y)) {
const bool ignore_zocs = it->second.get_ability_bool("skirmisher",it->first);
const bool teleport = it->second.get_ability_bool("teleport",it->first);
current_paths_ = paths(map_,status_,gameinfo_,units_,it->first,teams_,ignore_zocs,teleport,viewing_team(),path_turns_);
gui_->highlight_reach(current_paths_);
gui_->scroll_to_tile(it->first.x,it->first.y,display::WARP);
}
if(it != units_.begin()) {
next_unit_ = it->first;
selected_hex_ = next_unit_;
gui_->select_hex(selected_hex_);
gui_->highlight_hex(selected_hex_);
current_route_.steps.clear();
gui_->set_route(NULL);
show_attack_options(it);
} else {
next_unit_ = gamemap::location();
}
}
void mouse_handler::set_current_paths(paths new_paths) {
gui_->unhighlight_reach();
current_paths_ = new_paths;
current_route_.steps.clear();
gui_->set_route(NULL);
}
}

View file

@ -12,34 +12,63 @@
#include "SDL.h"
namespace events{
struct command_disabler
{
command_disabler();
~command_disabler();
};
class mouse_handler{
public:
mouse_handler(display* gui, std::vector<team>& teams, const unit_map& units, gamemap& map, gamestatus& status, const game_data& gameinfo);
bool browse();
void mouse_motion(const SDL_MouseMotionEvent& event, const int player_number);
void mouse_press(const SDL_MouseButtonEvent& event, const int player_number);
void set_gui(display* gui) { gui_ = gui; }
mouse_handler(display* gui, std::vector<team>& teams, unit_map& units, gamemap& map,
gamestatus& status, const game_data& gameinfo, undo_list& undo_stack, undo_list& redo_stack);
void mouse_motion(const SDL_MouseMotionEvent& event, const int player_number, const bool browse);
void mouse_press(const SDL_MouseButtonEvent& event, const int player_number, const bool browse);
void mouse_handler::cycle_units();
void mouse_handler::cycle_back_units();
int get_path_turns() const { return path_turns_; }
paths get_current_paths() { return current_paths_; }
paths::route get_current_route() const { return current_route_; }
const gamemap::location& get_last_hex() const { return last_hex_; }
gamemap::location get_selected_hex() const { return selected_hex_; }
const bool get_undo() const { return undo_; }
const bool get_show_menu() const { return show_menu_; }
void set_path_turns(const int path_turns) { path_turns_ = path_turns; }
void set_current_paths(paths new_paths);
void set_selected_hex(gamemap::location hex) { selected_hex_ = hex; }
void set_gui(display* gui) { gui_ = gui; }
unit_map::iterator selected_unit();
const unit_map& visible_units();
private:
team& viewing_team() { return teams_[(*gui_).viewing_team()]; }
const team& viewing_team() const { return teams_[(*gui_).viewing_team()]; }
team& current_team() { return teams_[team_num_-1]; }
void mouse_motion(int x, int y);
void mouse_motion(int x, int y, const bool browse);
bool is_left_click(const SDL_MouseButtonEvent& event);
bool is_middle_click(const SDL_MouseButtonEvent& event);
bool is_right_click(const SDL_MouseButtonEvent& event);
void left_click(const SDL_MouseButtonEvent& event);
void left_click(const SDL_MouseButtonEvent& event, const bool browse);
void clear_undo_stack();
bool move_unit_along_current_route(bool check_shroud=true);
bool attack_enemy(unit_map::iterator attacker, unit_map::iterator defender);
void show_attack_options(unit_map::const_iterator u);
gamemap::location current_unit_attacks_from(const gamemap::location& loc, const gamemap::location::DIRECTION preferred, const gamemap::location::DIRECTION second_preferred);
unit_map::const_iterator find_unit(const gamemap::location& hex);
const unit_map& visible_units();
unit_map::const_iterator find_unit(const gamemap::location& hex) const;
unit_map::iterator find_unit(const gamemap::location& hex);
bool unit_in_cycle(unit_map::const_iterator it);
display* gui_;
std::vector<team>& teams_;
const unit_map& units_;
unit_map& units_;
gamemap& map_;
gamestatus& status_;
const game_data& gameinfo_;
undo_list& undo_stack_;
undo_list& redo_stack_;
bool minimap_scrolling_;
gamemap::location last_hex_;
@ -49,18 +78,18 @@ private:
paths::route current_route_;
paths current_paths_;
bool enemy_paths_;
bool browse_;
mutable unit_map visible_units_;
int path_turns_;
unsigned int team_num_;
undo_list undo_stack_;
undo_list redo_stack_;
//cached value indicating whether any enemy units are visible.
//computed with enemies_visible()
bool enemies_visible_;
bool enemies_visible_;
bool undo_;
bool show_menu_;
};
extern int commands_disabled;
}
#endif

650
src/play_controller.cpp Normal file
View file

@ -0,0 +1,650 @@
#include "play_controller.hpp"
#include "config_adapter.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "replay.hpp"
#include "sound.hpp"
#define LOG_NG LOG_STREAM(info, engine)
play_controller::play_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
int ticks, int num_turns, const config& game_config, CVideo& video,
bool skip_replay) :
level_(level), ticks_(ticks), first_human_team_(-1), gameinfo_(gameinfo),
gamestate_(state_of_game), status_(level, num_turns), statistics_context_(level["name"]),
game_config_(game_config), map_(game_config, level["map_data"]), verify_manager_(units_),
labels_manager_(), help_manager_(&game_config, &gameinfo, &map_),
team_manager_(teams_), xp_modifier_(atoi(level["experience_modifier"].c_str())),
loading_game_(level["playing_team"].empty() == false),
menu_handler_(gui_, units_, teams_, level, gameinfo, map_, game_config, status_, state_of_game, undo_stack_, redo_stack_),
mouse_handler_(gui_, teams_, units_, map_, status_, gameinfo, undo_stack_, redo_stack_)
{
player_number_ = 1;
current_turn_ = 1;
first_player_ = atoi(level_["playing_team"].c_str());
if(first_player_ < 0 || first_player_ >= int(teams_.size())) {
first_player_ = 0;
}
skip_replay_ = skip_replay;
browse_ = false;
init(video);
key_events_handler_ = new hotkey::basic_handler(gui_);
}
play_controller::~play_controller(){
delete key_events_handler_;
delete halo_manager_;
delete prefs_disp_manager_;
delete tooltips_manager_;
delete events_manager_;
delete gui_;
}
void play_controller::init(CVideo& video){
//if the recorder has no event, adds an "game start" event to the
//recorder, whose only goal is to initialize the RNG
if(recorder.empty()) {
recorder.add_start();
} else {
recorder.pre_replay();
}
recorder.set_skip(false);
const config::child_list& unit_cfg = level_.get_children("side");
if(level_["modify_placing"] == "true") {
LOG_NG << "modifying placing...\n";
place_sides_in_preferred_locations(map_,unit_cfg);
}
LOG_NG << "initializing teams..." << unit_cfg.size() << "\n";;
LOG_NG << (SDL_GetTicks() - ticks_) << "\n";
std::set<std::string> seen_save_ids;
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
std::string save_id = get_unique_saveid(**ui, seen_save_ids);
seen_save_ids.insert(save_id);
if (first_human_team_ == -1){
first_human_team_ = get_first_human_team(ui, unit_cfg);
}
get_player_info(**ui, gamestate_, save_id, teams_, level_, gameinfo_, map_, units_, status_);
}
preferences::encounter_recruitable_units(teams_);
preferences::encounter_start_units(units_);
preferences::encounter_recallable_units(gamestate_);
preferences::encounter_map_terrain(map_);
LOG_NG << "initialized teams... " << (SDL_GetTicks() - ticks_) << "\n";
LOG_NG << "initializing display... " << (SDL_GetTicks() - ticks_) << "\n";
const config* theme_cfg = get_theme(game_config_, level_["theme"]);
gui_ = new display(units_,video,map_,status_,teams_,*theme_cfg, game_config_, level_);
mouse_handler_.set_gui(gui_);
menu_handler_.set_gui(gui_);
theme::set_known_themes(&game_config_);
LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks_) << "\n";
if(first_human_team_ != -1) {
gui_->set_team(first_human_team_);
}
init_managers();
gui_->labels().read(level_);
LOG_NG << "start music... " << (SDL_GetTicks() - ticks_) << "\n";
const std::string& music = level_["music"];
if(music != "") {
sound::play_music_repeatedly(music);
}
//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"], (**overlay)["halo"]);
}
}
void play_controller::init_managers(){
LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks_) << "\n";
prefs_disp_manager_ = new preferences::display_manager(gui_);
tooltips_manager_ = new tooltips::manager(gui_->video());
//this *needs* to be created before the show_intro and show_map_scene
//as that functions use the manager state_of_game
events_manager_ = new game_events::manager(level_,*gui_,map_,units_,teams_,
gamestate_,status_,gameinfo_);
halo_manager_ = new halo::manager(*gui_);
LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks_) << "\n";
}
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 play_controller::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);
LOG_NG << "placing side " << i->side << " at " << i->pos << '\n';
}
}
}
void play_controller::objectives(){
menu_handler_.objectives(player_number_);
}
void play_controller::show_statistics(){
menu_handler_.show_statistics();
}
void play_controller::unit_list(){
menu_handler_.unit_list();
}
void play_controller::status_table(){
menu_handler_.status_table();
}
void play_controller::save_game(){
menu_handler_.save_game("",gui::OK_CANCEL);
}
void play_controller::load_game(){
menu_handler_.load_game();
}
void play_controller::preferences(){
menu_handler_.preferences();
}
void play_controller::cycle_units(){
mouse_handler_.cycle_units();
}
void play_controller::cycle_back_units(){
mouse_handler_.cycle_back_units();
}
void play_controller::show_chat_log(){
menu_handler_.show_chat_log();
}
void play_controller::show_help(){
menu_handler_.show_help();
}
void play_controller::undo(){
menu_handler_.undo(player_number_, mouse_handler_);
}
void play_controller::redo(){
menu_handler_.redo(player_number_, mouse_handler_);
}
void play_controller::show_enemy_moves(bool ignore_units){
menu_handler_.show_enemy_moves(ignore_units, player_number_);
}
void play_controller::goto_leader(){
menu_handler_.goto_leader(player_number_);
}
void play_controller::unit_description(){
menu_handler_.unit_description(mouse_handler_);
}
void play_controller::toggle_grid(){
menu_handler_.toggle_grid();
}
void play_controller::search(){
menu_handler_.search();
}
const int play_controller::get_xp_modifier(){
return xp_modifier_;
}
const int play_controller::get_ticks(){
return ticks_;
}
void play_controller::fire_prestart(bool execute){
//pre-start events must be executed before any GUI operation,
//as those may cause the display to be refreshed.
if (execute){
update_locker lock_display(gui_->video());
game_events::fire("prestart");
}
}
void play_controller::fire_start(bool execute){
if(execute) {
game_events::fire("start");
gamestate_.set_variable("turn_number", "1");
}
}
void play_controller::init_gui(){
gui_->begin_game();
gui_->adjust_colours(0,0,0);
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
::clear_shroud(*gui_,status_,map_,gameinfo_,units_,teams_,(t-teams_.begin()));
}
}
void play_controller::init_side(const int team_index){
log_scope("player turn");
team& current_team = teams_[team_index];
//if a side is dead, don't do their turn
if(!current_team.is_empty() || team_units(units_,player_number_) == 0) {
if(team_manager_.is_observer()) {
gui_->set_team(size_t(player_number_-1));
}
std::stringstream player_number_str;
player_number_str << player_number_;
gamestate_.set_variable("side_number",player_number_str.str());
//fire side turn event only if real side change occurs not counting changes from void to a side
if (team_index != first_player_ || current_turn_ > 1) {
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 = current_turn_ > 1 || loading_game_ && team_index != first_player_;
if(turn_refresh) {
for(unit_map::iterator i = units_.begin(); i != units_.end(); ++i) {
if(i->second.side() == (size_t)player_number_) {
i->second.new_turn(i->first);
}
}
current_team.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_) -
current_team.villages().size();
if(expense > 0) {
current_team.spend_gold(expense);
}
calculate_healing((*gui_),status_,map_,units_,player_number_,teams_, !recorder.is_skipping());
reset_resting(units_, player_number_);
}
current_team.set_time_of_day(int(status_.turn()),status_.get_time_of_day());
gui_->set_playing_team(size_t(player_number_-1));
if (!recorder.is_skipping()){
::clear_shroud(*gui_,status_,map_,gameinfo_,units_,teams_,player_number_-1);
}
if (!recorder.is_skipping()){
gui_->scroll_to_leader(units_, player_number_);
}
}
}
bool play_controller::do_replay(const bool replaying){
bool result = false;
if(replaying) {
const hotkey::basic_handler key_events_handler(gui_);
LOG_NG << "doing replay " << player_number_ << "\n";
try {
result = ::do_replay(*gui_,map_,gameinfo_,units_,teams_,
player_number_,status_,gamestate_);
} catch(replay::error&) {
gui::show_dialog(*gui_,NULL,"",_("The file you have tried to load is corrupt"),gui::OK_ONLY);
result = false;
}
LOG_NG << "result of replay: " << (result?"true":"false") << "\n";
}
return result;
}
void play_controller::finish_side_turn(){
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(current_team().copy_ally_shroud()) {
gui_->recalculate_minimap();
gui_->invalidate_all();
}
game_events::pump();
}
void play_controller::finish_turn(){
std::stringstream event_stream;
event_stream << status_.turn();
{
LOG_NG << "turn event..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui_->video(),recorder.is_skipping());
const std::string turn_num = event_stream.str();
gamestate_.set_variable("turn_number",turn_num);
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
}
}
bool play_controller::enemies_visible() const
{
// If we aren't using fog/shroud, this is easy :)
if(current_team().uses_fog() == false && current_team().uses_shroud() == false)
return true;
//See if any enemies are visible
for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u)
if(current_team().is_enemy(u->second.side()) && !gui_->fogged(u->first.x,u->first.y))
return true;
return false;
}
bool play_controller::can_execute_command(hotkey::HOTKEY_COMMAND command) const
{
switch(command) {
//commands we can always do
case hotkey::HOTKEY_LEADER:
case hotkey::HOTKEY_CYCLE_UNITS:
case hotkey::HOTKEY_CYCLE_BACK_UNITS:
case hotkey::HOTKEY_ZOOM_IN:
case hotkey::HOTKEY_ZOOM_OUT:
case hotkey::HOTKEY_ZOOM_DEFAULT:
case hotkey::HOTKEY_FULLSCREEN:
case hotkey::HOTKEY_SCREENSHOT:
case hotkey::HOTKEY_ACCELERATED:
case hotkey::HOTKEY_TOGGLE_GRID:
case hotkey::HOTKEY_STATUS_TABLE:
case hotkey::HOTKEY_MUTE:
case hotkey::HOTKEY_PREFERENCES:
case hotkey::HOTKEY_OBJECTIVES:
case hotkey::HOTKEY_UNIT_LIST:
case hotkey::HOTKEY_STATISTICS:
case hotkey::HOTKEY_QUIT_GAME:
case hotkey::HOTKEY_SEARCH:
case hotkey::HOTKEY_HELP:
case hotkey::HOTKEY_USER_CMD:
case hotkey::HOTKEY_SAVE_GAME:
return true;
case hotkey::HOTKEY_SHOW_ENEMY_MOVES:
case hotkey::HOTKEY_BEST_ENEMY_MOVES:
return enemies_visible();
case hotkey::HOTKEY_LOAD_GAME:
return network::nconnections() == 0; //can only load games if not in a network game
case hotkey::HOTKEY_CHAT_LOG:
return network::nconnections() > 0;
case hotkey::HOTKEY_REDO:
return !browse_ && !redo_stack_.empty() && !events::commands_disabled;
case hotkey::HOTKEY_UNDO:
return !browse_ && !undo_stack_.empty() && !events::commands_disabled;
case hotkey::HOTKEY_UNIT_DESCRIPTION:
return menu_handler_.current_unit(mouse_handler_) != units_.end();
case hotkey::HOTKEY_RENAME_UNIT:
return !events::commands_disabled &&
menu_handler_.current_unit(mouse_handler_) != units_.end() &&
!(menu_handler_.current_unit(mouse_handler_)->second.unrenamable()) &&
menu_handler_.current_unit(mouse_handler_)->second.side() == gui_->viewing_team()+1;
default:
return false;
}
}
void play_controller::enter_textbox()
{
if(menu_handler_.get_textbox().active() == false) {
return;
}
switch(menu_handler_.get_textbox().mode()) {
case gui::TEXTBOX_SEARCH:
menu_handler_.do_search(menu_handler_.get_textbox().box()->text());
break;
case gui::TEXTBOX_MESSAGE:
menu_handler_.do_speak();
break;
case gui::TEXTBOX_COMMAND:
menu_handler_.do_command(menu_handler_.get_textbox().box()->text(), player_number_, mouse_handler_);
break;
default:
LOG_STREAM(err, display) << "unknown textbox mode\n";
}
menu_handler_.get_textbox().close(*gui_);
}
void play_controller::handle_event(const SDL_Event& event)
{
if(gui::in_dialog()) {
return;
}
switch(event.type) {
case SDL_KEYDOWN:
//detect key press events, unless there is a textbox present on-screen
//in which case the key press events should go only to it.
if(menu_handler_.get_textbox().active() == false) {
hotkey::key_event(*gui_,event.key,this);
} else if(event.key.keysym.sym == SDLK_ESCAPE) {
menu_handler_.get_textbox().close(*gui_);
} else if(event.key.keysym.sym == SDLK_TAB) {
menu_handler_.get_textbox().tab(teams_, units_, *gui_);
} else if(event.key.keysym.sym == SDLK_RETURN) {
enter_textbox();
}
//intentionally fall-through
case SDL_KEYUP:
//if the user has pressed 1 through 9, we want to show how far
//the unit can move in that many turns
if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '7') {
const int new_path_turns = (event.type == SDL_KEYDOWN) ?
event.key.keysym.sym - '1' : 0;
if(new_path_turns != mouse_handler_.get_path_turns()) {
mouse_handler_.set_path_turns(new_path_turns);
const unit_map::iterator u = mouse_handler_.selected_unit();
if(u != units_.end() && u->second.side() == player_number_) {
const bool ignore_zocs = u->second.get_ability_bool("skirmisher",u->first);
const bool teleport = u->second.get_ability_bool("teleport",u->first);
mouse_handler_.set_current_paths(paths(map_,status_,gameinfo_,units_,u->first,
teams_,ignore_zocs,teleport, teams_[gui_->viewing_team()],
mouse_handler_.get_path_turns()));
gui_->highlight_reach(mouse_handler_.get_current_paths());
}
}
}
break;
case SDL_MOUSEMOTION:
// ignore old mouse motion events in the event queue
SDL_Event new_event;
if(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0) {
while(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0);
mouse_handler_.mouse_motion(new_event.motion, player_number_, browse_);
} else {
mouse_handler_.mouse_motion(event.motion, player_number_, browse_);
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
mouse_handler_.mouse_press(event.button, player_number_, browse_);
if (mouse_handler_.get_undo()){
menu_handler_.undo(player_number_, mouse_handler_);
}
if (mouse_handler_.get_show_menu()){
show_menu(gui_->get_theme().context_menu()->items(),event.button.x,event.button.y,true);
}
break;
default:
break;
}
}
void play_controller::play_slice()
{
CKey key;
events::pump();
events::raise_process_event();
events::raise_draw_event();
const theme::menu* const m = gui_->menu_pressed();
if(m != NULL) {
const SDL_Rect& menu_loc = m->location(gui_->screen_area());
show_menu(m->items(),menu_loc.x+1,menu_loc.y + menu_loc.h + 1,false);
return;
}
int mousex, mousey;
SDL_GetMouseState(&mousex,&mousey);
tooltips::process(mousex, mousey);
const int scroll_threshold = 5;
if(key[SDLK_UP] || mousey < scroll_threshold)
gui_->scroll(0,-preferences::scroll_speed());
if(key[SDLK_DOWN] || mousey > gui_->y()-scroll_threshold)
gui_->scroll(0,preferences::scroll_speed());
if(key[SDLK_LEFT] || mousex < scroll_threshold)
gui_->scroll(-preferences::scroll_speed(),0);
if(key[SDLK_RIGHT] || mousex > gui_->x()-scroll_threshold)
gui_->scroll(preferences::scroll_speed(),0);
gui_->draw();
if(!browse_ && current_team().objectives_changed()) {
dialogs::show_objectives(*gui_, level_, current_team().objectives());
current_team().reset_objectives_changed();
}
}
void play_controller::show_menu(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu)
{
std::vector<std::string> items = items_arg;
hotkey::HOTKEY_COMMAND command;
for(std::vector<std::string>::iterator i = items.begin(); i != items.end();){
command = hotkey::get_hotkey(*i).get_id();
if (!can_execute_command(command) || (context_menu && !in_context_menu(command))){
i = items.erase(i);
}
else{ i++; }
}
if(items.empty())
return;
command_executor::show_menu(items, xloc, yloc, context_menu, *gui_);
}
// Indicates whether the command should be in the context menu or not.
// Independant of whether or not we can actually execute the command.
bool play_controller::in_context_menu(hotkey::HOTKEY_COMMAND command) const
{
switch(command) {
//Only display these if the mouse is over a castle or keep tile
case hotkey::HOTKEY_RECRUIT:
case hotkey::HOTKEY_REPEAT_RECRUIT:
case hotkey::HOTKEY_RECALL: {
// last_hex_ is set by turn_info::mouse_motion
// Enable recruit/recall on castle/keep tiles
const unit_map::const_iterator leader = team_leader(player_number_,units_);
if (leader != units_.end()) {
return can_recruit_on(map_, leader->first, mouse_handler_.get_last_hex());
} else {
return false;
}
}
default:
return true;
}
}

137
src/play_controller.hpp Normal file
View file

@ -0,0 +1,137 @@
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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.
*/
#ifndef PLAY_CONTROLLER_H_INCLUDED
#define PLAY_CONTROLLER_H_INCLUDED
#include "global.hpp"
#include "display.hpp"
#include "game_config.hpp"
#include "game_events.hpp"
#include "gamestatus.hpp"
#include "halo.hpp"
#include "help.hpp"
#include "hotkeys.hpp"
#include "key.hpp"
#include "menu_events.hpp"
#include "mouse_events.hpp"
#include "preferences_display.hpp"
#include "replay.hpp"
#include "statistics.hpp"
#include "team.hpp"
#include "tooltips.hpp"
#include "unit_types.hpp"
#include <vector>
class play_controller : public hotkey::command_executor, public events::handler
{
public:
play_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
int ticks, int num_turns, const config& game_config, CVideo& video, bool skip_replay);
~play_controller();
virtual void play_slice();
//event handlers, overriden from command_executor
virtual void objectives();
virtual void show_statistics();
virtual void unit_list();
virtual void status_table();
virtual void save_game();
virtual void load_game();
virtual void preferences();
virtual void show_chat_log();
virtual void show_help();
virtual void cycle_units();
virtual void cycle_back_units();
virtual void undo();
virtual void redo();
virtual void show_enemy_moves(bool ignore_units);
virtual void goto_leader();
virtual void unit_description();
virtual void toggle_grid();
virtual void search();
virtual void play_side(const int team_num) = 0;
const int get_xp_modifier();
const int get_ticks();
protected:
virtual void handle_event(const SDL_Event& event);
virtual bool can_execute_command(hotkey::HOTKEY_COMMAND command) const;
void show_menu(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu);
bool in_context_menu(hotkey::HOTKEY_COMMAND command) const;
virtual void init(CVideo& video);
void init_managers();
void fire_prestart(bool execute);
void fire_start(bool execute);
virtual void init_gui();
virtual void init_side(const int team_index);
void place_sides_in_preferred_locations(gamemap& map, const config::child_list& sides);
bool do_replay(const bool replaying);
virtual void finish_side_turn();
void finish_turn();
bool clear_shroud();
bool enemies_visible() const;
void enter_textbox();
team& current_team() { return teams_[player_number_-1]; }
const team& current_team() const { return teams_[player_number_-1]; }
//managers
const verification_manager verify_manager_;
teams_manager team_manager_;
preferences::display_manager* prefs_disp_manager_;
tooltips::manager* tooltips_manager_;
game_events::manager* events_manager_;
halo::manager* halo_manager_;
font::floating_label_context labels_manager_;
help::help_manager help_manager_;
hotkey::basic_handler* key_events_handler_;
events::mouse_handler mouse_handler_;
events::menu_handler menu_handler_;
//other objects
display* gui_;
const statistics::scenario_context statistics_context_;
const game_data& gameinfo_;
const config& level_;
const config& game_config_;
std::vector<team> teams_;
game_state& gamestate_;
gamestatus status_;
gamemap map_;
unit_map units_;
undo_list undo_stack_;
undo_list redo_stack_;
const int ticks_;
const int xp_modifier_;
//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_;
CKey key_;
int first_human_team_;
int player_number_;
int first_player_;
int current_turn_;
bool skip_replay_;
bool browse_;
};
#endif

View file

@ -17,12 +17,12 @@
#include <map>
#include "playcampaign.hpp"
#include "playlevel.hpp"
#include "config.hpp"
#include "filesystem.hpp"
#include "gamestatus.hpp"
//30.12.2005 YogiHH
//please keep in for the moment, supports me in merging gameplay and replay functionality
//#include "play_controller.hpp"
#include "map_create.hpp"
#include "playmp_controller.hpp"
#include "playsingle_controller.hpp"
#include "replay.hpp"
#include "replay_controller.hpp"
#include "log.hpp"
@ -205,8 +205,52 @@ LEVEL_RESULT play_game(display& disp, game_state& state, const config& game_conf
if (state.label.empty())
state.label = (*scenario)["name"];
LEVEL_RESULT res = play_level(units_data,game_config,scenario,video,state,story,log, skip_replay);
//LEVEL_RESULT res = play_scenario(units_data,game_config,scenario,video,state,story);
//if the entire scenario should be randomly generated
if((*scenario)["scenario_generation"] != "") {
LOG_G << "randomly generating scenario...\n";
const cursor::setter cursor_setter(cursor::WAIT);
static config scenario2;
scenario2 = random_generate_scenario((*scenario)["scenario_generation"], scenario->child("generator"));
//level_ = scenario;
state.starting_pos = scenario2;
scenario = &scenario2;
}
std::string map_data = (*scenario)["map_data"];
if(map_data == "" && (*scenario)["map"] != "") {
map_data = read_map((*scenario)["map"]);
}
//if the map should be randomly generated
if(map_data == "" && (*scenario)["map_generation"] != "") {
const cursor::setter cursor_setter(cursor::WAIT);
map_data = random_generate_map((*scenario)["map_generation"],scenario->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 = *scenario;
new_level.values["map_data"] = map_data;
scenario = &new_level;
state.starting_pos = new_level;
LOG_G << "generated map\n";
}
//LEVEL_RESULT res = play_level(units_data,game_config,scenario,video,state,story,log, skip_replay);
LEVEL_RESULT res;
switch (io_type){
case IO_NONE:
res = playsingle_scenario(units_data,game_config,scenario,video,state,story,log, skip_replay);
break;
case IO_SERVER:
case IO_CLIENT:
res = playmp_scenario(units_data,game_config,scenario,video,state,story,log, skip_replay);
break;
}
state.snapshot = config();
if (res == DEFEAT) {

View file

@ -15,7 +15,7 @@
#ifndef PLAYCAMPAIGN_H_INCLUDED
#define PLAYCAMPAIGN_H_INCLUDED
#include "playlevel.hpp"
#include "menu_events.hpp"
class display;
struct game_state;

View file

@ -1,759 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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 "global.hpp"
#include "ai_interface.hpp"
#include "config_adapter.hpp"
#include "cursor.hpp"
#include "filesystem.hpp"
#include "gettext.hpp"
#include "game_events.hpp"
#include "halo.hpp"
#include "help.hpp"
#include "intro.hpp"
#include "log.hpp"
#include "map_create.hpp"
#include "marked-up_text.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "preferences_display.hpp"
#include "replay.hpp"
#include "scoped_resource.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "tooltips.hpp"
#include "upload_log.hpp"
#include "game_errors.hpp"
#include <iostream>
#include <iterator>
#define LOG_NG LOG_STREAM(info, engine)
namespace play{
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);
LOG_NG << "placing side " << i->side << " at " << i->pos << '\n';
}
}
}
}
LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
config const* level, CVideo& video,
game_state& state_of_game,
const std::vector<config*>& story,
upload_log &log,
bool skip_replay)
{
//if the recorder has no event, adds an "game start" event to the
//recorder, whose only goal is to initialize the RNG
if(recorder.empty()) {
recorder.add_start();
} else {
recorder.pre_replay();
}
const set_random_generator generator_setter(&recorder);
//guarantee the cursor goes back to 'normal' at the end of the level
const cursor::setter cursor_setter(cursor::NORMAL);
const int ticks = SDL_GetTicks();
LOG_NG << "in play_level()...\n";
//if the entire scenario should be randomly generated
if((*level)["scenario_generation"] != "") {
LOG_NG << "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;
LOG_NG << "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);
LOG_NG << "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;
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") {
LOG_NG << "modifying placing...\n";
play::place_sides_in_preferred_locations(map,unit_cfg);
}
LOG_NG << "initializing teams..." << unit_cfg.size() << "\n";;
LOG_NG << (SDL_GetTicks() - ticks) << "\n";
std::set<std::string> seen_save_ids;
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
std::string save_id = get_unique_saveid(**ui, seen_save_ids);
seen_save_ids.insert(save_id);
if (first_human_team == -1){
first_human_team = get_first_human_team(ui, unit_cfg);
}
get_player_info(**ui, state_of_game, save_id, teams, lvl, gameinfo, map, units, status);
}
preferences::encounter_recruitable_units(teams);
preferences::encounter_start_units(units);
preferences::encounter_recallable_units(state_of_game);
preferences::encounter_map_terrain(map);
LOG_NG << "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());
}
LOG_NG << "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);
theme::set_known_themes(&game_config);
LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks) << "\n";
LOG_NG << "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.video());
LOG_NG << "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.is_skipping()) {
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);
LOG_NG << "c... " << (SDL_GetTicks() - ticks) << "\n";
const config::child_list& m = lvl.get_children("music");
if (!m.empty()) {
config::const_child_iterator i;
for (i = m.begin(); i != m.end(); i++) {
sound::play_music_config(**i);
}
sound::commit_music_changes();
} else {
const std::string& music = lvl["music"];
if(music != "") {
sound::play_music_repeatedly(music);
}
}
LOG_NG << "d... " << (SDL_GetTicks() - ticks) << "\n";
victory_conditions::set_victory_when_enemies_defeated(
lvl["victory_when_enemies_defeated"] != "no");
LOG_NG << "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"], (**overlay)["halo"]);
}
unsigned int turn = 1, player_number = 0;
turn_info::floating_textbox textbox_info;
LOG_NG << "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;
// log before prestart events: they do weird things.
if (first_human_team != -1) {
log.start(state_of_game, teams[first_human_team],
first_human_team+1, units,
loading_game ? state_of_game.get_variable("turn_number") : "",
num_turns);
}
//pre-start events must be executed before any GUI operation,
//as those may cause the display to be refreshed.
if(!loading_game) {
update_locker lock_display(gui.video());
game_events::fire("prestart");
}
gui.begin_game();
gui.adjust_colours(0,0,0);
LOG_NG << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
LOG_NG << "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);
LOG_NG << "c " << (SDL_GetTicks() - ticks) << "\n";
}
gui.scroll_to_tile(map.starting_position(1).x,map.starting_position(1).y,display::WARP);
LOG_NG << "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;
}
for(std::vector<team>::iterator t = teams.begin(); t != teams.end(); ++t) {
clear_shroud(gui,status,map,gameinfo,units,teams,(t-teams.begin()));
}
std::deque<config> data_backlog;
LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks) << "\n";
for(bool first_time = true; true; first_time = false, first_player = 0) {
if(first_time) {
const hotkey::basic_handler key_events_handler(&gui);
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui.video(),recorder.is_skipping());
events::raise_draw_event();
gui.draw();
std::vector<team>::iterator t;
for(t = teams.begin(); t != teams.end(); ++t) {
clear_shroud(gui,status,map,gameinfo,units,teams,(t-teams.begin()));
}
if(!loading_game) {
game_events::fire("start");
state_of_game.set_variable("turn_number", "1");
}
// Initialize countdown clock.
for(t = teams.begin(); t != teams.end(); ++t) {
std::string countd_enabled = lvl["mp_countdown"].c_str();
if ( countd_enabled == "yes" && !loading_game ){
t->set_countdown_time(1000 * lexical_cast_default<int>(lvl["mp_countdown_init_time"],0));
}
}
gui.recalculate_minimap();
}
player_number = 0;
gui.new_turn();
gui.invalidate_game_status();
events::raise_draw_event();
LOG_NG << "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(team_manager.is_observer()) {
gui.set_team(size_t(player_number-1));
}
std::stringstream player_number_str;
player_number_str << player_number;
state_of_game.set_variable("side_number",player_number_str.str());
//fire side turn event only if real side change occurs not counting changes from void to a side
if (team_it != teams.begin()+first_player || !first_time) {
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(i->first);
}
}
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, !skip_replay);
reset_resting(units, player_number);
}
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);
if (!skip_replay){
gui.scroll_to_leader(units, player_number);
}
if(replaying) {
const hotkey::basic_handler key_events_handler(&gui);
LOG_NG << "doing replay " << player_number << "\n";
try {
replaying = do_replay(gui,map,gameinfo,units,teams,
player_number,status,state_of_game);
} catch(replay::error&) {
gui::show_dialog(gui,NULL,"",_("The file you have tried to load is corrupt"),gui::OK_ONLY);
replaying = false;
}
LOG_NG << "result of replay: " << (replaying?"true":"false") << "\n";
}
//goto this label if the type of a team (human/ai/networked) has changed mid-turn
redo_turn:
if(!replaying && team_it->is_human()) {
LOG_NG << "is human...\n";
try {
play_turn(gameinfo,state_of_game,status,game_config,
*level, key, gui, map, teams, player_number,
units, textbox_info, replay_sender, skip_replay);
} catch(end_turn_exception& end_turn) {
if (end_turn.redo == player_number)
goto redo_turn;
}
if(game_config::debug)
display::clear_debug_highlights();
LOG_NG << "human finished turn...\n";
} else if(!replaying && team_it->is_ai()) {
LOG_NG << "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();
recorder.end_turn();
ai_obj->sync_network();
gui.unhighlight_reach();
gui.recalculate_minimap();
clear_shroud(gui,status,map,gameinfo,units,teams,player_number-1);
gui.invalidate_unit();
gui.invalidate_game_status();
gui.invalidate_all();
gui.draw();
SDL_Delay(500);
} else if(!replaying && team_it->is_network()) {
LOG_NG << "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, skip_replay);
if(result == turn_info::PROCESS_RESTART_TURN) {
goto redo_turn;
} else if(result == turn_info::PROCESS_END_TURN) {
break;
}
}
else{
skip_replay = false;
}
turn_data.turn_slice();
turn_data.send_data();
gui.draw();
}
LOG_NG << "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";
}
LOG_NG << "firing time over event...\n";
game_events::fire("time over");
LOG_NG << "done firing time over event...\n";
throw end_level_exception(DEFEAT);
}
std::stringstream event_stream;
event_stream << status.turn();
{
LOG_NG << "turn event..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui.video(),recorder.is_skipping());
const std::string turn_num = event_stream.str();
state_of_game.set_variable("turn_number",turn_num);
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
}
} //end for loop
} catch(game::load_game_exception& e) {
// Loading a new game is effectively a quit.
log.quit(status.turn());
throw;
} catch(end_level_exception& end_level) {
bool obs = team_manager.is_observer();
if (end_level.result == DEFEAT || end_level.result == VICTORY) {
// if we're a player, and the result is victory/defeat, then send a message to notify
// the server of the reason for the game ending
if (!obs) {
config cfg;
config& info = cfg.add_child("info");
info["type"] = "termination";
info["condition"] = "game over";
network::send_data(cfg);
} else {
gui::show_dialog(gui, NULL, _("Game Over"),
_("The game is over."), gui::OK_ONLY);
return QUIT;
}
}
if(end_level.result == QUIT) {
log.quit(status.turn());
return end_level.result;
} else if(end_level.result == DEFEAT) {
log.defeat(status.turn());
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
}
if (!obs) {
sound::play_music_once(game_config::defeat_music);
return DEFEAT;
}
else
return QUIT;
} 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&) {
}
if (end_level.result == VICTORY && first_human_team != -1) {
log.victory(status.turn(), teams[first_human_team].gold());
}
if(state_of_game.scenario == (*level)["id"]) {
state_of_game.scenario = (*level)["next_scenario"];
}
const bool has_next_scenario = !state_of_game.scenario.empty() &&
state_of_game.scenario != "null";
//add all the units that survived the scenario
for(units_map::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->first);
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 retains 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) {
if (!i->is_persistent())
continue;
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 << font::BOLD_TEXT << 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;
}
}
if (!obs) {
sound::play_music_once(game_config::victory_music);
gui::show_dialog(gui, NULL, _("Victory"),
_("You have emerged victorious!"), gui::OK_ONLY);
}
if (state_of_game.players.size() > 0 && has_next_scenario ||
state_of_game.campaign_type == "test")
gui::show_dialog(gui, NULL, _("Scenario Report"), report.str(), gui::OK_ONLY);
return VICTORY;
}
} //end catch
catch(replay::error&) {
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 occurred, 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;
}

View file

@ -1,53 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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.
*/
#ifndef PLAY_LEVEL_HPP_INCLUDED
#define PLAY_LEVEL_HPP_INCLUDED
class config;
class CVideo;
struct game_state;
struct upload_log;
#include "game_config.hpp"
#include "unit_types.hpp"
#include <vector>
enum LEVEL_RESULT { VICTORY, DEFEAT, QUIT, LEVEL_CONTINUE, LEVEL_CONTINUE_NO_SAVE };
struct end_level_exception {
end_level_exception(LEVEL_RESULT res, bool bonus=true)
: result(res), gold_bonus(bonus)
{}
LEVEL_RESULT result;
bool gold_bonus;
};
struct end_turn_exception {
end_turn_exception(unsigned int r = 0): redo(r) {}
unsigned int redo;
};
LEVEL_RESULT play_level(const game_data& gameinfo, const config& terrain_config,
config const* level, CVideo& video,
game_state& state_of_game,
const std::vector<config*>& story,
upload_log &log,
bool skip_replay);
namespace play{
void place_sides_in_preferred_locations(gamemap& map, const config::child_list& sides);
}
#endif

210
src/playmp_controller.cpp Normal file
View file

@ -0,0 +1,210 @@
#include "playmp_controller.hpp"
#include "log.hpp"
#include "sound.hpp"
#define LOG_NG LOG_STREAM(info, engine)
LEVEL_RESULT playmp_scenario(const game_data& gameinfo, const config& game_config,
config const* level, CVideo& video, game_state& state_of_game,
const config::child_list& story, upload_log& log, bool skip_replay)
{
const int ticks = SDL_GetTicks();
const int num_turns = atoi((*level)["turns"].c_str());
playmp_controller playcontroller(*level, gameinfo, state_of_game, ticks, num_turns, game_config, video, skip_replay);
return playcontroller.play_scenario(story, log, skip_replay);
}
playmp_controller::playmp_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
const int ticks, const int num_turns, const config& game_config, CVideo& video,
bool skip_replay)
: playsingle_controller(level, gameinfo, state_of_game, ticks, num_turns, game_config, video, skip_replay)
{
beep_warning_time_ = 10000; //Starts beeping each second when time is less than this (millisec)
turn_data_ = NULL;
}
void playmp_controller::clear_labels(){
menu_handler_.clear_labels();
}
void playmp_controller::speak(){
menu_handler_.speak();
}
void playmp_controller::user_command(){
menu_handler_.user_command();
}
void playmp_controller::play_side(const int team_index){
//goto this label if the type of a team (human/ai/networked) has changed mid-turn
redo_turn:
bool player_type_changed_ = false;
end_turn_ = false;
if(current_team().is_human() || current_team().is_ai()) {
playsingle_controller::play_side(team_index);
} else if(current_team().is_network()) {
player_type_changed_ = play_network_turn();
}
if (player_type_changed_) { goto redo_turn; }
}
void playmp_controller::before_human_turn(){
playsingle_controller::before_human_turn();
turn_data_ = new turn_info(gameinfo_,gamestate_,status_,
*gui_,map_,teams_,player_number_,units_,turn_info::PLAY_TURN,replay_sender_);
}
void playmp_controller::play_human_turn(){
int cur_ticks = SDL_GetTicks();
while(!end_turn_) {
try {
config cfg;
const network::connection res = network::receive_data(cfg);
std::deque<config> backlog;
if(res != network::null_connection) {
turn_data_->process_network_data(cfg,res,backlog,skip_replay_);
}
play_slice();
} catch(end_level_exception& e) {
turn_data_->send_data();
throw e;
}
if (current_team().countdown_time() > 0 && ( level_["mp_countdown"] == "yes" ) ){
SDL_Delay(1);
const int ticks = SDL_GetTicks();
int new_time = current_team().countdown_time()-maximum<int>(1,(ticks - cur_ticks));
if (new_time > 0 ){
current_team().set_countdown_time(current_team().countdown_time()-maximum<int>(1,(ticks - cur_ticks)));
cur_ticks = ticks;
if ( current_team().countdown_time() <= beep_warning_time_){
beep_warning_time_ = beep_warning_time_ - 1000;
sound::play_sound("bell.wav");
}
} else {
// Clock time ended
// If no turn bonus or action bonus -> defeat
const int action_increment = lexical_cast_default<int>(level_["mp_countdown_action_bonus"],0);
if ( lexical_cast_default<int>(level_["mp_countdown_turn_bonus"],0) == 0
&& (action_increment == 0 || current_team().action_bonus_count() == 0)) {
// Not possible to end level in MP with throw end_level_exception(DEFEAT);
// because remote players only notice network disconnection
// Current solution end remaining turns automatically
current_team().set_countdown_time(10);
recorder.add_countdown_update(current_team().countdown_time(),player_number_);
recorder.end_turn();
turn_data_->send_data();
throw end_turn_exception();
} else {
const int maxtime = lexical_cast_default<int>(level_["mp_countdown_reservoir_time"],0);
int secs = lexical_cast_default<int>(level_["mp_countdown_turn_bonus"],0);
secs += action_increment * current_team().action_bonus_count();
current_team().set_action_bonus_count(0);
secs = (secs > maxtime) ? maxtime : secs;
current_team().set_countdown_time(1000 * secs);
recorder.add_countdown_update(current_team().countdown_time(),player_number_);
recorder.end_turn();
turn_data_->send_data();
throw end_turn_exception();
}
}
}
menu_handler_.clear_undo_stack(player_number_);
gui_->invalidate_animations();
gui_->draw();
turn_data_->send_data();
}
}
void playmp_controller::after_human_turn(){
if ( level_["mp_countdown"] == "yes" ){
current_team().set_countdown_time(current_team().countdown_time() + 1000 * lexical_cast_default<int>(level_["mp_countdown_turn_bonus"],0));
recorder.add_countdown_update(current_team().countdown_time(),player_number_);
}
//send one more time to make sure network is up-to-date.
turn_data_->send_data();
if (turn_data_ != NULL){
delete turn_data_;
turn_data_ = NULL;
}
playsingle_controller::after_human_turn();
}
void playmp_controller::finish_side_turn(){
play_controller::finish_side_turn();
//just in case due to an exception turn_data_ has not been deleted in after_human_turn
if (turn_data_ != NULL){
delete turn_data_;
turn_data_ = NULL;
}
}
bool playmp_controller::play_network_turn(){
LOG_NG << "is networked...\n";
browse_ = true;
turn_info turn_data(gameinfo_,gamestate_,status_,*gui_,
map_,teams_,player_number_,units_,
turn_info::BROWSE_NETWORKED,
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_,skip_replay_);
if(result == turn_info::PROCESS_RESTART_TURN) {
return true;
} else if(result == turn_info::PROCESS_END_TURN) {
break;
}
}
play_slice();
turn_data.send_data();
gui_->draw();
}
LOG_NG << "finished networked...\n";
return false;
}
bool playmp_controller::can_execute_command(hotkey::HOTKEY_COMMAND command) const
{
switch (command){
case hotkey::HOTKEY_SPEAK:
case hotkey::HOTKEY_SPEAK_ALLY:
case hotkey::HOTKEY_SPEAK_ALL:
case hotkey::HOTKEY_CLEAR_LABELS:
return network::nconnections() > 0;
}
return playsingle_controller::can_execute_command(command);
}

53
src/playmp_controller.hpp Normal file
View file

@ -0,0 +1,53 @@
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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.
*/
#ifndef PLAYMP_CONTROLLER_H_INCLUDED
#define PLAYMP_CONTROLLER_H_INCLUDED
#include "global.hpp"
#include "hotkeys.hpp"
#include "playsingle_controller.hpp"
#include <vector>
class playmp_controller : public playsingle_controller
{
public:
playmp_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
const int ticks, const int num_turns, const config& game_config, CVideo& video, bool skip_replay);
protected:
virtual void speak();
virtual void clear_labels();
virtual void user_command();
virtual bool playmp_controller::can_execute_command(hotkey::HOTKEY_COMMAND command) const;
virtual void play_side(const int team_index);
virtual void before_human_turn();
virtual void play_human_turn();
virtual void after_human_turn();
virtual void finish_side_turn();
bool play_network_turn();
turn_info* turn_data_;
int beep_warning_time_;
private:
};
LEVEL_RESULT playmp_scenario(const game_data& gameinfo, const config& terrain_config,
config const* level, CVideo& video, game_state& state_of_game,
const config::child_list& story, upload_log& log, bool skip_replay);
#endif

View file

@ -0,0 +1,498 @@
#include "playsingle_controller.hpp"
#include "ai_interface.hpp"
#include "gettext.hpp"
#include "intro.hpp"
#include "marked-up_text.hpp"
#include "sound.hpp"
#define LOG_NG LOG_STREAM(info, engine)
LEVEL_RESULT playsingle_scenario(const game_data& gameinfo, const config& game_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& log, bool skip_replay)
{
const int ticks = SDL_GetTicks();
const int num_turns = atoi((*level)["turns"].c_str());
LOG_NG << "creating objects... " << (SDL_GetTicks() - ticks) << "\n";
playsingle_controller playcontroller(*level, gameinfo, state_of_game, ticks, num_turns, game_config, video, skip_replay);
LOG_NG << "created objects... " << (SDL_GetTicks() - playcontroller.get_ticks()) << "\n";
const unit_type::experience_accelerator xp_mod(playcontroller.get_xp_modifier() > 0 ? playcontroller.get_xp_modifier() : 100);
return playcontroller.play_scenario(story, log, skip_replay);
}
playsingle_controller::playsingle_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
const int ticks, const int num_turns, const config& game_config, CVideo& video,
bool skip_replay)
: play_controller(level, gameinfo, state_of_game, ticks, num_turns, game_config, video, skip_replay),
generator_setter(&recorder), cursor_setter(cursor::NORMAL), replay_sender_(recorder)
{
end_turn_ = false;
replaying_ = false;
}
void playsingle_controller::init_gui(){
LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks_) << "\n";
play_controller::init_gui();
if(first_human_team_ != -1) {
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);
update_locker lock_display(gui_->video(),recorder.is_skipping());
events::raise_draw_event();
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()));
}
}
void playsingle_controller::recruit(){
if (!browse_)
menu_handler_.recruit(browse_, player_number_, mouse_handler_.get_last_hex());
}
void playsingle_controller::repeat_recruit(){
if (!browse_)
menu_handler_.repeat_recruit(player_number_, mouse_handler_.get_last_hex());
}
void playsingle_controller::recall(){
if (!browse_)
menu_handler_.recall(player_number_, mouse_handler_.get_last_hex());
}
void playsingle_controller::toggle_shroud_updates(){
menu_handler_.toggle_shroud_updates(player_number_);
}
void playsingle_controller::update_shroud_now(){
menu_handler_.update_shroud_now(player_number_);
}
void playsingle_controller::end_turn(){
if (!browse_){
menu_handler_.end_turn(player_number_);
end_turn_ = true;
}
}
void playsingle_controller::rename_unit(){
menu_handler_.rename_unit(mouse_handler_);
}
void playsingle_controller::create_unit(){
menu_handler_.create_unit(mouse_handler_);
}
void playsingle_controller::change_unit_side(){
menu_handler_.change_unit_side(mouse_handler_);
}
void playsingle_controller::label_terrain(){
menu_handler_.label_terrain(mouse_handler_);
}
void playsingle_controller::continue_move(){
menu_handler_.continue_move(mouse_handler_, player_number_);
}
void playsingle_controller::unit_hold_position(){
if (!browse_)
menu_handler_.unit_hold_position(mouse_handler_, player_number_);
}
void playsingle_controller::end_unit_turn(){
if (!browse_)
menu_handler_.end_unit_turn(mouse_handler_, player_number_);
}
LEVEL_RESULT playsingle_controller::play_scenario(const std::vector<config*>& story, upload_log& log,
bool skip_replay)
{
LOG_NG << "in playsingle_controller::play_scenario()...\n";
if(!skip_replay) {
for(std::vector<config*>::const_iterator story_i = story.begin(); story_i != story.end(); ++story_i) {
show_intro(*gui_,**story_i, level_);
}
}
victory_conditions::set_victory_when_enemies_defeated(
level_["victory_when_enemies_defeated"] != "no");
LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n";
try {
fire_prestart(!loading_game_);
init_gui();
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
fire_start(!loading_game_);
gui_->recalculate_minimap();
bool replaying_ = (recorder.at_end() == false);
LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";
for(; ; first_player_ = 0) {
play_turn();
} //end for loop
} catch(end_level_exception& end_level) {
bool obs = team_manager_.is_observer();
if (end_level.result == DEFEAT || end_level.result == VICTORY) {
// if we're a player, and the result is victory/defeat, then send a message to notify
// the server of the reason for the game ending
if (!obs) {
config cfg;
config& info = cfg.add_child("info");
info["type"] = "termination";
info["condition"] = "game over";
network::send_data(cfg);
} else {
gui::show_dialog(*gui_, NULL, _("Game Over"),
_("The game is over."), gui::OK_ONLY);
return QUIT;
}
}
if(end_level.result == QUIT) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
}
if (!obs)
return DEFEAT;
else
return QUIT;
} 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&) {
}
if(gamestate_.scenario == (level_)["id"]) {
gamestate_.scenario = (level_)["next_scenario"];
}
const bool has_next_scenario = !gamestate_.scenario.empty() &&
gamestate_.scenario != "null";
//add all the units that survived the scenario
for(unit_map::iterator un = units_.begin(); un != units_.end(); ++un) {
player_info *player=gamestate_.get_player(teams_[un->second.side()-1].save_id());
if(player) {
un->second.new_turn(un->first);
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 retains 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=gamestate_.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) {
if (!i->is_persistent())
continue;
player_info *player=gamestate_.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(gamestate_.players.size()>1) {
if(i!=teams_.begin()) {
report << "\n";
}
report << font::BOLD_TEXT << 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;
}
}
if (!obs)
gui::show_dialog(*gui_, NULL, _("Victory"),
_("You have emerged victorious!"), gui::OK_ONLY);
if (gamestate_.players.size() > 0 && has_next_scenario ||
gamestate_.campaign_type == "test")
gui::show_dialog(*gui_, NULL, _("Scenario Report"), report.str(), gui::OK_ONLY);
return VICTORY;
}
} //end catch
catch(replay::error&) {
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;
}
menu_handler_.save_game(_("A network disconnection has occurred, 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;
}
void playsingle_controller::play_turn(){
gui_->new_turn();
gui_->invalidate_game_status();
events::raise_draw_event();
LOG_NG << "turn: " << current_turn_ << "\n";
current_turn_++;
for(player_number_ = first_player_ + 1; player_number_ <= teams_.size(); player_number_++) {
init_side(player_number_ - 1);
if (replaying_){
const hotkey::basic_handler key_events_handler(gui_);
LOG_NG << "doing replay " << player_number_ << "\n";
try {
replaying_ = ::do_replay(*gui_,map_,gameinfo_,units_,teams_,
player_number_,status_,gamestate_);
} catch(replay::error&) {
gui::show_dialog(*gui_,NULL,"",_("The file you have tried to load is corrupt"),gui::OK_ONLY);
replaying_ = false;
}
LOG_NG << "result of replay: " << (replaying_?"true":"false") << "\n";
}
if (!replaying_){
play_side(player_number_);
}
finish_side_turn();
check_victory(units_,teams_);
}
//time has run out
check_time_over();
finish_turn();
}
void playsingle_controller::play_side(const int team_index){
//goto this label if the type of a team (human/ai/networked) has changed mid-turn
redo_turn:
//although this flag is used only in this method it has to be a class member
//since derived classes rely on it
player_type_changed_ = false;
end_turn_ = false;
if(current_team().is_human()) {
LOG_NG << "is human...\n";
try{
before_human_turn();
play_human_turn();
after_human_turn();
} catch(end_turn_exception& end_turn) {
if (end_turn.redo == player_number_)
player_type_changed_ = true;
}
if(game_config::debug)
display::clear_debug_highlights();
LOG_NG << "human finished turn...\n";
} else if(current_team().is_ai()) {
play_ai_turn();
}
if (player_type_changed_) { goto redo_turn; }
}
void playsingle_controller::before_human_turn(){
log_scope("player turn");
browse_ = false;
gui_->set_team(player_number_ - 1);
gui_->recalculate_minimap();
gui_->invalidate_all();
gui_->draw();
gui_->update_display();
if(preferences::turn_bell()) {
sound::play_sound(game_config::sounds::turn_bell);
}
if(preferences::turn_dialog()) {
gui::show_dialog(*gui_,NULL,"",_("It is now your turn"),gui::MESSAGE);
}
const std::string& turn_cmd = preferences::turn_cmd();
if(turn_cmd.empty() == false) {
system(turn_cmd.c_str());
}
//execute gotos - first collect gotos in a list
std::vector<gamemap::location> gotos;
for(unit_map::iterator ui = units_.begin(); ui != units_.end(); ++ui) {
if(ui->second.get_goto() == ui->first)
ui->second.set_goto(gamemap::location());
if(ui->second.side() == player_number_ && map_.on_board(ui->second.get_goto()))
gotos.push_back(ui->first);
}
for(std::vector<gamemap::location>::const_iterator g = gotos.begin(); g != gotos.end(); ++g) {
unit_map::const_iterator ui = units_.find(*g);
menu_handler_.move_unit_to_loc(ui,ui->second.get_goto(),false, player_number_, mouse_handler_);
}
}
void playsingle_controller::play_human_turn(){
while(!end_turn_) {
play_slice();
gui_->invalidate_animations();
gui_->draw();
}
}
void playsingle_controller::after_human_turn(){
menu_handler_.clear_undo_stack(player_number_);
gui_->unhighlight_reach();
}
void playsingle_controller::play_ai_turn(){
LOG_NG << "is ai...\n";
browse_ = true;
gui_->recalculate_minimap();
const cursor::setter cursor_setter(cursor::WAIT);
turn_info turn_data(gameinfo_,gamestate_,status_,*gui_,
map_,teams_,player_number_,units_,
turn_info::BROWSE_AI,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(current_team().ai_algorithm(),ai_info));
ai_obj->play_turn();
recorder.end_turn();
turn_data.sync_network();
gui_->recalculate_minimap();
::clear_shroud(*gui_,status_,map_,gameinfo_,units_,teams_,player_number_-1);
gui_->invalidate_unit();
gui_->invalidate_game_status();
gui_->invalidate_all();
gui_->draw();
SDL_Delay(500);
}
void playsingle_controller::check_time_over(){
if(!status_.next_turn()) {
if(non_interactive()) {
std::cout << "time over (draw)\n";
}
LOG_NG << "firing time over event...\n";
game_events::fire("time over");
LOG_NG << "done firing time over event...\n";
throw end_level_exception(DEFEAT);
}
}
bool playsingle_controller::can_execute_command(hotkey::HOTKEY_COMMAND command) const
{
switch (command){
case hotkey::HOTKEY_UNIT_HOLD_POSITION:
case hotkey::HOTKEY_END_UNIT_TURN:
case hotkey::HOTKEY_RECRUIT:
case hotkey::HOTKEY_REPEAT_RECRUIT:
case hotkey::HOTKEY_RECALL:
case hotkey::HOTKEY_ENDTURN:
return !browse_ && !events::commands_disabled;
case hotkey::HOTKEY_DELAY_SHROUD:
return !browse_ && (current_team().uses_fog() || current_team().uses_shroud());
case hotkey::HOTKEY_UPDATE_SHROUD:
return !browse_ && !events::commands_disabled && current_team().auto_shroud_updates() == false;
//commands we can only do if in debug mode
case hotkey::HOTKEY_CREATE_UNIT:
case hotkey::HOTKEY_CHANGE_UNIT_SIDE:
return !events::commands_disabled && game_config::debug && map_.on_board(mouse_handler_.get_last_hex());
case hotkey::HOTKEY_LABEL_TERRAIN:
return !events::commands_disabled && map_.on_board(mouse_handler_.get_last_hex())
&& !gui_->shrouded(mouse_handler_.get_last_hex().x, mouse_handler_.get_last_hex().y)
&& !is_observer();
case hotkey::HOTKEY_CONTINUE_MOVE: {
if(browse_ || events::commands_disabled)
return false;
if( (menu_handler_.current_unit(mouse_handler_) != units_.end())
&& (menu_handler_.current_unit(mouse_handler_)->second.move_interrupted()))
return true;
const unit_map::const_iterator i = units_.find(mouse_handler_.get_selected_hex());
if (i == units_.end()) return false;
return i->second.move_interrupted();
}
}
return play_controller::can_execute_command(command);
}

View file

@ -0,0 +1,80 @@
/*
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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.
*/
#ifndef PLAYSINGLE_CONTROLLER_H_INCLUDED
#define PLAYSINGLE_CONTROLLER_H_INCLUDED
#include "global.hpp"
#include "cursor.hpp"
#include "hotkeys.hpp"
#include "menu_events.hpp"
#include "play_controller.hpp"
#include "playturn.hpp"
#include "random.hpp"
#include "upload_log.hpp"
#include <vector>
class playsingle_controller : public play_controller
{
public:
playsingle_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
const int ticks, const int num_turns, const config& game_config, CVideo& video, bool skip_replay);
LEVEL_RESULT play_scenario(const std::vector<config*>& story, upload_log& log, bool skip_replay);
virtual void recruit();
virtual void repeat_recruit();
virtual void recall();
virtual bool can_execute_command(hotkey::HOTKEY_COMMAND command) const;
virtual void toggle_shroud_updates();
virtual void update_shroud_now();
virtual void end_turn();
virtual void rename_unit();
virtual void create_unit();
virtual void change_unit_side();
virtual void label_terrain();
virtual void continue_move();
virtual void unit_hold_position();
virtual void end_unit_turn();
void playsingle_slice();
protected:
virtual void play_turn();
virtual void play_side(const int team_index);
virtual void before_human_turn();
virtual void play_human_turn();
virtual void after_human_turn();
void play_ai_turn();
virtual void init_gui();
void check_time_over();
const set_random_generator generator_setter;
const cursor::setter cursor_setter;
std::deque<config> data_backlog_;
gui::floating_textbox textbox_info_;
replay_network_sender replay_sender_;
bool end_turn_;
bool player_type_changed_;
bool replaying_;
private:
};
LEVEL_RESULT playsingle_scenario(const game_data& gameinfo, const config& terrain_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story, upload_log& loo, bool skip_replay);
#endif

File diff suppressed because it is too large Load diff

View file

@ -15,25 +15,16 @@
class replay_network_sender;
#include "global.hpp"
#include "actions.hpp"
#include "config.hpp"
#include "display.hpp"
#include "events.hpp"
#include "gamestatus.hpp"
#include "hotkeys.hpp"
#include "key.hpp"
#include "scoped_resource.hpp"
#include "pathfind.hpp"
#include "show_dialog.hpp"
#include "statistics.hpp"
#include "network.hpp"
#include "team.hpp"
#include "unit_types.hpp"
#include "unit.hpp"
#include "widgets/button.hpp"
#include "widgets/textbox.hpp"
#include <deque>
#include <map>
#include <vector>
@ -43,54 +34,23 @@ struct command_disabler
~command_disabler();
};
class turn_info : public hotkey::command_executor, public events::handler
class turn_info
{
public:
//this keeps track of any textbox that might be used to do searching
//and send messages
struct floating_textbox
{
util::scoped_ptr<gui::textbox> box;
util::scoped_ptr<gui::button> check;
enum MODE { TEXTBOX_NONE, TEXTBOX_SEARCH, TEXTBOX_MESSAGE,
TEXTBOX_COMMAND };
MODE mode;
std::string label_string;
int label;
floating_textbox() : box(NULL), check(NULL), mode(TEXTBOX_NONE), label(0)
{}
bool active() const { return box.get() != NULL; }
};
enum TURN_MODE { PLAY_TURN, BROWSE_NETWORKED, BROWSE_AI };
turn_info(const game_data& gameinfo, game_state& state_of_game,
const gamestatus& status, const config& terrain_config,
const config& level, CKey& key, display& gui, gamemap& map,
const gamestatus& status, display& gui, gamemap& map,
std::vector<team>& teams, unsigned int team_num, unit_map& units,
TURN_MODE mode, floating_textbox& textbox,
replay_network_sender& network_sender);
TURN_MODE mode, replay_network_sender& network_sender);
~turn_info();
void turn_slice();
bool turn_over() const;
void sync_network();
void send_data();
undo_list& undos() { return undo_stack_; }
bool can_execute_command(hotkey::HOTKEY_COMMAND command) const;
bool in_context_menu(hotkey::HOTKEY_COMMAND command) const;
void move_unit_to_loc(const unit_map::const_iterator& ui, const gamemap::location& target, bool continue_move);
void start_interactive_turn();
void save_game(const std::string& message,gui::DIALOG_TYPE dialog_type);
enum PROCESS_DATA_RESULT { PROCESS_CONTINUE, PROCESS_RESTART_TURN, PROCESS_END_TURN };
//function which will process incoming network data, and act on it. If there is
@ -102,163 +62,21 @@ public:
//which case data will not be forwarded
PROCESS_DATA_RESULT process_network_data(const config& cfg,network::connection from,std::deque<config>& backlog, bool skip_replay);
private:
//convenience functions
team& current_team() { return teams_[team_num_-1]; }
const team& current_team() const { return teams_[team_num_-1]; }
team& viewing_team() { return teams_[gui_.viewing_team()]; }
const team& viewing_team() const { return teams_[gui_.viewing_team()]; }
unit_map::const_iterator find_unit(const gamemap::location& hex) const;
unit_map::iterator find_unit(const gamemap::location& hex);
unit_map::const_iterator selected_unit() const;
unit_map::iterator selected_unit();
unit_map::iterator current_unit();
unit_map::const_iterator current_unit() const;
void write_game_snapshot(config& cfg) const;
bool unit_in_cycle(unit_map::const_iterator it) const;
//overridden from command_executor
virtual void cycle_units();
virtual void cycle_back_units();
virtual void end_turn();
virtual void goto_leader();
virtual void unit_hold_position();
virtual void end_unit_turn();
virtual void undo();
virtual void redo();
virtual void unit_description();
virtual void rename_unit();
virtual void save_game();
virtual void load_game();
virtual void toggle_grid();
virtual void status_table();
virtual void recruit();
virtual void repeat_recruit();
virtual void recall();
virtual void speak();
virtual void create_unit();
virtual void change_unit_side();
virtual void preferences();
virtual void objectives();
virtual void unit_list();
virtual void show_statistics();
virtual void label_terrain();
virtual void clear_labels();
virtual void show_enemy_moves(bool ignore_units);
virtual void toggle_shroud_updates();
virtual void update_shroud_now();
virtual void continue_move();
virtual void search();
virtual void show_help();
virtual void show_chat_log();
virtual void user_command();
virtual hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command) const;
void do_search(const std::string& str);
void do_command(const std::string& str);
void do_speak(const std::string& str, bool allies_only);
bool has_friends() const;
void do_recruit(const std::string& name);
void handle_event(const SDL_Event& event);
void mouse_motion(const SDL_MouseMotionEvent& event);
void mouse_motion(int x, int y);
void mouse_press(const SDL_MouseButtonEvent& event);
void left_click(const SDL_MouseButtonEvent& event);
void show_menu(const std::vector<std::string>& items, int xloc, int yloc, bool context_menu);
void show_attack_options(unit_map::const_iterator u);
//function which, given the location of a potential enemy to attack, will return the location
//that the currently selected unit would move to and attack it from this turn. Returns an
//invalid location if not possible.
gamemap::location current_unit_attacks_from(const gamemap::location& loc, const gamemap::location::DIRECTION preferred, const gamemap::location::DIRECTION second_preferred) const;
bool attack_enemy(unit_map::iterator attacker, unit_map::iterator defender);
bool move_unit_along_current_route(bool check_shroud=true);
bool clear_shroud();
void clear_undo_stack();
std::vector<std::string> create_unit_table(const statistics::stats::str_int_map& m);
bool enemies_visible() const;
void change_side_controller(const std::string& side, const std::string& player, bool orphan_side=false);
const game_data& gameinfo_;
game_state& state_of_game_;
const gamestatus& status_;
const config& terrain_config_;
const config& level_;
CKey key_;
display& gui_;
gamemap& map_;
std::vector<team>& teams_;
unsigned int team_num_;
unit_map& units_;
const unit_map& visible_units() const;
mutable unit_map visible_units_;
bool browse_;
bool allow_network_commands_;
bool left_button_, right_button_, middle_button_;
bool minimap_scrolling_;
gamemap::location next_unit_;
paths current_paths_, all_paths_;
paths::route current_route_;
bool enemy_paths_;
gamemap::location last_hex_;
gamemap::location::DIRECTION last_nearest_, last_second_nearest_;
gamemap::location selected_hex_;
undo_list undo_stack_;
undo_list redo_stack_;
int path_turns_;
//cached value indicating whether any enemy units are visible.
//computed with enemies_visible()
bool enemies_visible_;
bool end_turn_;
int start_ncmd_;
std::string last_recruit_;
std::string last_search_;
gamemap::location last_search_hit_;
floating_textbox& textbox_;
void update_textbox_location();
void create_textbox(floating_textbox::MODE mode, const std::string& label, const std::string& check_label="", bool checked=false);
void close_textbox();
void enter_textbox();
void tab_textbox();
replay_network_sender& replay_sender_;
};
void play_turn(const game_data& gameinfo, game_state& state_of_game,
const gamestatus& status, const config& terrain_config,
const config& level,
CKey& key, display& gui, gamemap& map,
std::vector<team>& teams, unsigned int team_num,
units_map& units,
turn_info::floating_textbox& textbox,
replay_network_sender& network_sender,
bool skip_replay);
#endif

View file

@ -22,7 +22,6 @@
#include "game_events.hpp"
#include "log.hpp"
#include "pathfind.hpp"
#include "playlevel.hpp"
#include "preferences.hpp"
#include "replay.hpp"
#include "show_dialog.hpp"
@ -191,15 +190,7 @@ void replay::set_skip(bool skip)
bool replay::is_skipping() const
{
//YogiHH, 03.03.2006
//In multiplayer, skipping is implemented through the skip_ flag.
//Since at_end() is always true in mp replays (process_network_data
//creates a new replayer for every single command), we need a separate
//condition for it
//mp replay skipped or "skip_animation" in replay mode checked
if (skip_) { return true; }
//mp replay not skipped or "skip animation" in replay mode not checked
return at_end() == false && skip_;
return skip_;
}
void replay::save_game(const std::string& label, const config& snapshot,
@ -616,7 +607,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
//if there is nothing more in the records
if(cfg == NULL) {
replayer.set_skip(false);
//replayer.set_skip(false);
return false;
}

View file

@ -1,676 +1,302 @@
#include "global.hpp"
#include "ai_interface.hpp"
#include "config_adapter.hpp"
#include "cursor.hpp"
#include "filesystem.hpp"
#include "gettext.hpp"
#include "game_events.hpp"
#include "halo.hpp"
#include "help.hpp"
#include "intro.hpp"
#include "log.hpp"
#include "map_create.hpp"
#include "playlevel.hpp"
#include "playturn.hpp"
#include "preferences.hpp"
#include "preferences_display.hpp"
#include "replay.hpp"
#include "replay_controller.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "tooltips.hpp"
#include <iostream>
#include <iterator>
#define LOG_NG LOG_STREAM(info, engine)
LEVEL_RESULT play_replay_level(const game_data& gameinfo, const config& game_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story)
{
try{
const int ticks = SDL_GetTicks();
const int num_turns = atoi((*level)["turns"].c_str());
replay_controller replaycontroller(*level, gameinfo, state_of_game, ticks, num_turns, game_config, video, story);
//replay event-loop
for (;;){
replaycontroller.replay_slice();
}
}
catch(end_level_exception&){
LOG_NG << "play_replay_level: end_level_exception\n";
}
return LEVEL_CONTINUE;
}
replay_controller::replay_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
const int ticks, const int num_turns, const config& game_config,
CVideo& video, const std::vector<config*>& story) :
verify_manager_(units_), team_manager_(teams_), labels_manager_(), help_manager_(&game_config, &gameinfo, &map_),
level_(level), game_config_(game_config), gameinfo_(gameinfo), gamestate_(state_of_game),
gamestate_start_(state_of_game), status_(level, num_turns), status_start_(level, num_turns),
map_(game_config, level["map_data"]), mouse_handler_(gui_, teams_, units_, map_, status_, gameinfo),
ticks_(ticks), xp_modifier_(atoi(level["experience_modifier"].c_str()))
{
player_number_ = 1;
delay_ = 0;
is_playing_ = false;
current_turn_ = 1;
loading_game_ = level["playing_team"].empty() == false;
first_player_ = atoi(level_["playing_team"].c_str());
if(first_player_ < 0 || first_player_ >= int(teams_.size())) {
first_player_ = 0;
}
init(video, story);
}
replay_controller::~replay_controller(){
delete gui_;
delete halo_manager_;
delete prefs_disp_manager_;
delete tooltips_manager_;
delete events_manager_;
}
void replay_controller::init(CVideo& video, const std::vector<config*>& /*story*/){
//if the recorder has no event, adds an "game start" event to the
//recorder, whose only goal is to initialize the RNG
if(recorder.empty()) {
recorder.add_start();
} else {
recorder.pre_replay();
}
recorder.set_skip(false);
replay_network_sender replay_sender(recorder);
const set_random_generator generator_setter(&recorder);
//guarantee the cursor goes back to 'normal' at the end of the level
const cursor::setter cursor_setter(cursor::NORMAL);
const int ticks = SDL_GetTicks();
LOG_NG << "in replay_controller::init()...\n";
const statistics::scenario_context statistics_context(level_["name"]);
LOG_NG << "created objects... " << (SDL_GetTicks() - ticks) << "\n";
const unit_type::experience_accelerator xp_mod(xp_modifier_ > 0 ? xp_modifier_ : 100);
const config::child_list& unit_cfg = level_.get_children("side");
if(level_["modify_placing"] == "true") {
LOG_NG << "modifying placing...\n";
play::place_sides_in_preferred_locations(map_,unit_cfg);
}
LOG_NG << "initializing teams..." << unit_cfg.size() << "\n";;
LOG_NG << (SDL_GetTicks() - ticks) << "\n";
int first_human_team = -1;
std::set<std::string> seen_save_ids;
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
std::string save_id = get_unique_saveid(**ui, seen_save_ids);
seen_save_ids.insert(save_id);
if (first_human_team == -1){
first_human_team = get_first_human_team(ui, unit_cfg);
}
get_player_info(**ui, gamestate_, save_id, teams_, level_, gameinfo_, map_, units_, status_);
}
preferences::encounter_recruitable_units(teams_);
preferences::encounter_start_units(units_);
preferences::encounter_recallable_units(gamestate_);
preferences::encounter_map_terrain(map_);
LOG_NG << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
LOG_NG << "initializing display... " << (SDL_GetTicks() - ticks) << "\n";
const config* theme_cfg = get_theme(game_config_, level_["theme"]);
gui_ = new display(units_,video,map_,status_,teams_,*theme_cfg, game_config_, level_);
const config* replay_theme_cfg = theme_cfg->child("resolution")->child("replay");
if (NULL != replay_theme_cfg)
gui_->get_theme().modify(replay_theme_cfg);
mouse_handler_.set_gui(gui_);
theme::set_known_themes(&game_config_);
LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks) << "\n";
if(first_human_team != -1) {
gui_->set_team(first_human_team);
}
init_managers();
/*
if(recorder.is_skipping() == false) {
for(std::vector<config*>::const_iterator story_i = story.begin(); story_i != story.end(); ++story_i) {
show_intro(*gui_,**story_i, level_);
}
}
*/
gui_->labels().read(level_);
LOG_NG << "c... " << (SDL_GetTicks() - ticks) << "\n";
const std::string& music = level_["music"];
if(music != "") {
sound::play_music_repeatedly(music);
}
LOG_NG << "d... " << (SDL_GetTicks() - ticks) << "\n";
//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"], (**overlay)["halo"]);
}
game_events::fire("prestart");
gui_->begin_game();
gui_->adjust_colours(0,0,0);
std::deque<config> data_backlog;
const hotkey::basic_handler key_events_handler(gui_);
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display((*gui_).video(),false);
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
clear_shroud(*gui_,status_,map_,gameinfo_,units_,teams_,(t-teams_.begin()));
t->set_fog(false);
t->set_shroud(false);
}
LOG_NG << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
gui_->scroll_to_leader(units_, player_number_);
LOG_NG << "done scrolling... " << (SDL_GetTicks() - ticks) << "\n";
if(!loading_game_) {
game_events::fire("start");
gamestate_.set_variable("turn_number", "1");
}
update_gui();
units_start_ = units_;
teams_start_ = team_manager_.clone(teams_);
}
void replay_controller::init_managers(){
prefs_disp_manager_ = new preferences::display_manager(gui_);
tooltips_manager_ = new tooltips::manager(gui_->video());
//this *needs* to be created before the show_intro and show_map_scene
//as that functions use the manager state_of_game
events_manager_ = new game_events::manager(level_,*gui_,map_,units_,teams_,
gamestate_,status_,gameinfo_);
halo_manager_ = new halo::manager(*gui_);
}
std::vector<team>& replay_controller::get_teams(){
return teams_;
}
unit_map replay_controller::get_units(){
return units_;
}
display& replay_controller::get_gui(){
return *gui_;
}
gamemap& replay_controller::get_map(){
return map_;
}
const gamestatus& replay_controller::get_status(){
return status_;
}
const int replay_controller::get_player_number(){
return player_number_;
}
const bool replay_controller::is_loading_game(){
return loading_game_;
}
void replay_controller::objectives(){
menu_handler_.objectives(*gui_, level_, current_team());
}
void replay_controller::show_statistics(){
menu_handler_.show_statistics(*gui_, gameinfo_);
}
void replay_controller::unit_list(){
menu_handler_.unit_list(units_, *gui_, map_);
}
void replay_controller::status_table(){
menu_handler_.status_table(teams_, *gui_, units_);
}
void replay_controller::save_game(){
menu_handler_.save_game("",gui::OK_CANCEL, gamestate_, status_, *gui_, level_, teams_, units_, map_);
}
void replay_controller::load_game(){
menu_handler_.load_game(*gui_, game_config_, gameinfo_);
}
void replay_controller::preferences(){
menu_handler_.preferences(*gui_, game_config_);
}
void replay_controller::show_chat_log(){
menu_handler_.show_chat_log(teams_, *gui_);
}
void replay_controller::show_help(){
menu_handler_.show_help(*gui_);
}
void replay_controller::reset_replay(){
is_playing_ = false;
current_turn_ = 1;
player_number_ = 1;
recorder.start_replay();
units_ = *(new unit_map(units_start_));
status_ = *(new gamestatus(status_start_));
gamestate_ = *(new game_state(gamestate_start_));
teams_ = team_manager_.clone(teams_start_);
(*gui_).invalidate_all();
(*gui_).draw();
}
void replay_controller::stop_replay(){
is_playing_ = false;
}
void replay_controller::replay_next_turn(){
is_playing_ = true;
play_turn();
is_playing_ = false;
}
void replay_controller::replay_next_side(){
is_playing_ = true;
play_side(player_number_ - 1);
if ((size_t)player_number_ > teams_.size()){
player_number_ = 1;
current_turn_++;
}
is_playing_ = false;
}
void replay_controller::replay_switch_fog(){
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
t->set_fog(!t->uses_fog());
}
update_teams();
update_gui();
}
void replay_controller::replay_switch_shroud(){
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
t->set_shroud(!t->uses_shroud());
}
update_teams();
update_gui();
}
void replay_controller::replay_skip_animation(){
recorder.set_skip(!recorder.is_skipping());
}
void replay_controller::play_replay(){
if (recorder.at_end()){
return;
}
try{
is_playing_ = true;
LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";
for(; !recorder.at_end() && is_playing_; first_player_ = 0) {
play_turn();
replay_slice();
} //end for loop
is_playing_ = false;
}
catch(end_level_exception& e){
if (e.result == QUIT) { throw e; }
}
}
void replay_controller::play_turn(){
if (recorder.at_end()){
return;
}
//FixMe
//This is a little bit ugly at the moment, since it really should happen only once in a game
//Probably need to fix the unit xp_max calculation
const unit_type::experience_accelerator xp_mod(xp_modifier_ > 0 ? xp_modifier_ : 100);
LOG_NG << "turn: " << current_turn_ << "\n";
while ( ((size_t)player_number_ <= teams_.size()) && (!recorder.at_end()) ){
play_side(player_number_ - 1);
}
std::stringstream event_stream;
event_stream << status_.turn();
{
LOG_NG << "turn event..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
update_locker lock_display(gui_->video(),recorder.is_skipping());
const std::string turn_num = event_stream.str();
gamestate_.set_variable("turn_number",turn_num);
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
}
player_number_ = 1;
current_turn_++;
}
void replay_controller::play_side(const int team_index){
if (recorder.at_end()){
return;
}
team& current_team = teams_[team_index];
log_scope("player turn");
//FixMe
//This is a little bit ugly at the moment, since it really should happen only once in a game
//Probably need to fix the unit xp_max calculation
const unit_type::experience_accelerator xp_mod(xp_modifier_ > 0 ? xp_modifier_ : 100);
//if a side is dead, don't do their turn
if(!current_team.is_empty() && team_units(units_,player_number_) > 0) {
if(team_manager_.is_observer()) {
(*gui_).set_team(size_t(player_number_-1));
}
std::stringstream player_number_str;
player_number_str << player_number_;
gamestate_.set_variable("side_number",player_number_str.str());
//fire side turn event only if real side change occurs not counting changes from void to a side
if (team_index != first_player_ || current_turn_ > 1) {
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 = current_turn_ > 1 || loading_game_ && team_index != first_player_;
if(turn_refresh) {
for(unit_map::iterator i = units_.begin(); i != units_.end(); ++i) {
if(i->second.side() == (size_t)player_number_) {
i->second.new_turn(i->first);
}
}
current_team.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_) -
current_team.villages().size();
if(expense > 0) {
current_team.spend_gold(expense);
}
calculate_healing((*gui_),status_,map_,units_,player_number_,teams_, !recorder.is_skipping());
reset_resting(units_, player_number_);
}
current_team.set_time_of_day(int(status_.turn()),status_.get_time_of_day());
gui_->set_playing_team(size_t(player_number_-1));
if (!recorder.is_skipping()){
clear_shroud(*gui_,status_,map_,gameinfo_,units_,teams_,player_number_-1);
}
const hotkey::basic_handler key_events_handler(gui_);
LOG_NG << "doing replay " << player_number_ << "\n";
bool replaying;
try {
replaying = do_replay(*gui_,map_,gameinfo_,units_,teams_,
player_number_,status_,gamestate_);
} catch(replay::error&) {
gui::show_dialog(*gui_,NULL,"",_("The file you have tried to load is corrupt"),gui::OK_ONLY);
replaying = false;
}
LOG_NG << "result of replay: " << (replaying?"true":"false") << "\n";
for(unit_map::iterator uit = units_.begin(); uit != units_.end(); ++uit) {
if(uit->second.side() == (size_t)player_number_){
uit->second.end_turn();
}
else{
//this is necessary for replays in order to show possible movements
uit->second.new_turn(uit->first);
}
}
//This implements "delayed map sharing." It's meant as an alternative to shared vision.
if(current_team.copy_ally_shroud()) {
gui_->recalculate_minimap();
gui_->invalidate_all();
}
game_events::pump();
//check_victory(units_,teams_);
}
player_number_++;
if ((size_t)player_number_ > teams_.size()) {
status_.next_turn();
}
update_teams();
update_gui();
}
void replay_controller::update_teams(){
int next_team = player_number_;
if ((size_t)next_team > teams_.size()) { next_team = 1; }
if (teams_[next_team - 1].uses_fog()){
gui_->set_team(next_team - 1);
clear_shroud(*gui_, status_, map_, gameinfo_, units_, teams_, next_team - 1);
}
if (teams_[next_team - 1].uses_shroud()){
gui_->set_team(next_team - 1);
recalculate_fog(map_, status_, gameinfo_, units_, teams_, next_team - 1);
}
gui_->set_playing_team(next_team - 1);
(*gui_).scroll_to_leader(units_, next_team);
}
void replay_controller::update_gui(){
(*gui_).recalculate_minimap();
(*gui_).redraw_minimap();
(*gui_).invalidate_all();
events::raise_draw_event();
(*gui_).draw();
}
void replay_controller::replay_slice()
{
CKey key;
events::pump();
events::raise_process_event();
events::raise_draw_event();
const theme::menu* const m = gui_->menu_pressed();
if(m != NULL) {
const SDL_Rect& menu_loc = m->location(gui_->screen_area());
show_menu(m->items(),menu_loc.x+1,menu_loc.y + menu_loc.h + 1,false,*gui_);
return;
}
int mousex, mousey;
SDL_GetMouseState(&mousex,&mousey);
tooltips::process(mousex, mousey);
const int scroll_threshold = 5;
if(key[SDLK_UP] || mousey < scroll_threshold)
gui_->scroll(0,-preferences::scroll_speed());
if(key[SDLK_DOWN] || mousey > gui_->y()-scroll_threshold)
gui_->scroll(0,preferences::scroll_speed());
if(key[SDLK_LEFT] || mousex < scroll_threshold)
gui_->scroll(-preferences::scroll_speed(),0);
if(key[SDLK_RIGHT] || mousex > gui_->x()-scroll_threshold)
gui_->scroll(preferences::scroll_speed(),0);
gui_->draw();
if(!mouse_handler_.browse() && current_team().objectives_changed()) {
dialogs::show_objectives(*gui_, level_, current_team().objectives());
current_team().reset_objectives_changed();
}
}
bool replay_controller::can_execute_command(hotkey::HOTKEY_COMMAND command) const
{
switch(command) {
//commands we can always do
case hotkey::HOTKEY_LEADER:
case hotkey::HOTKEY_CYCLE_UNITS:
case hotkey::HOTKEY_CYCLE_BACK_UNITS:
case hotkey::HOTKEY_ZOOM_IN:
case hotkey::HOTKEY_ZOOM_OUT:
case hotkey::HOTKEY_ZOOM_DEFAULT:
case hotkey::HOTKEY_FULLSCREEN:
case hotkey::HOTKEY_SCREENSHOT:
case hotkey::HOTKEY_ACCELERATED:
case hotkey::HOTKEY_TOGGLE_GRID:
case hotkey::HOTKEY_STATUS_TABLE:
case hotkey::HOTKEY_MUTE:
case hotkey::HOTKEY_PREFERENCES:
case hotkey::HOTKEY_OBJECTIVES:
case hotkey::HOTKEY_UNIT_LIST:
case hotkey::HOTKEY_STATISTICS:
case hotkey::HOTKEY_QUIT_GAME:
case hotkey::HOTKEY_SEARCH:
case hotkey::HOTKEY_HELP:
case hotkey::HOTKEY_USER_CMD:
case hotkey::HOTKEY_SAVE_GAME:
case hotkey::HOTKEY_LOAD_GAME:
case hotkey::HOTKEY_PLAY_REPLAY:
case hotkey::HOTKEY_RESET_REPLAY:
case hotkey::HOTKEY_STOP_REPLAY:
case hotkey::HOTKEY_REPLAY_NEXT_TURN:
case hotkey::HOTKEY_REPLAY_NEXT_SIDE:
case hotkey::HOTKEY_REPLAY_FOG:
case hotkey::HOTKEY_REPLAY_SHROUD:
case hotkey::HOTKEY_REPLAY_SKIP_ANIMATION:
return true;
case hotkey::HOTKEY_CHAT_LOG:
return network::nconnections() > 0;
default:
return false;
}
}
void replay_controller::handle_event(const SDL_Event& event)
{
if(gui::in_dialog()) {
return;
}
switch(event.type) {
case SDL_KEYDOWN:
hotkey::key_event(*gui_,event.key,this);
//FixMe
//add textbox support
/*
//detect key press events, unless there is a textbox present on-screen
//in which case the key press events should go only to it.
if(textbox_.active() == false) {
hotkey::key_event(gui_,event.key,this);
} else if(event.key.keysym.sym == SDLK_ESCAPE) {
close_textbox();
} else if(event.key.keysym.sym == SDLK_TAB) {
tab_textbox();
} else if(event.key.keysym.sym == SDLK_RETURN) {
enter_textbox();
}
*/
//intentionally fall-through
case SDL_KEYUP:
//FixMe
//add unit-turn support
/*
//if the user has pressed 1 through 9, we want to show how far
//the unit can move in that many turns
if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '7') {
const int new_path_turns = (event.type == SDL_KEYDOWN) ?
event.key.keysym.sym - '1' : 0;
if(new_path_turns != path_turns_) {
path_turns_ = new_path_turns;
const unit_map::iterator u = selected_unit();
if(u != units_.end() && u->second.side() == team_num_) {
const bool ignore_zocs = u->second.type().is_skirmisher();
const bool teleport = u->second.type().teleports();
current_paths_ = paths(map_,status_,gameinfo_,units_,u->first,
teams_,ignore_zocs,teleport,
path_turns_);
gui_->set_paths(&current_paths_);
}
}
}
*/
break;
case SDL_MOUSEMOTION:
// ignore old mouse motion events in the event queue
SDL_Event new_event;
if(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0) {
while(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0);
mouse_handler_.mouse_motion(new_event.motion, player_number_);
} else {
mouse_handler_.mouse_motion(event.motion, player_number_);
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
mouse_handler_.mouse_press(event.button, player_number_);
break;
default:
break;
}
}
#include "global.hpp"
#include "ai_interface.hpp"
#include "config_adapter.hpp"
#include "cursor.hpp"
#include "filesystem.hpp"
#include "gettext.hpp"
#include "game_events.hpp"
#include "halo.hpp"
#include "help.hpp"
#include "intro.hpp"
#include "log.hpp"
#include "map_create.hpp"
#include "preferences.hpp"
#include "preferences_display.hpp"
#include "replay.hpp"
#include "replay_controller.hpp"
#include "sound.hpp"
#include "tooltips.hpp"
#include <iostream>
#include <iterator>
#define LOG_NG LOG_STREAM(info, engine)
LEVEL_RESULT play_replay_level(const game_data& gameinfo, const config& game_config,
const config* level, CVideo& video, game_state& state_of_game,
const std::vector<config*>& story)
{
try{
const int ticks = SDL_GetTicks();
const int num_turns = atoi((*level)["turns"].c_str());
LOG_NG << "creating objects... " << (SDL_GetTicks() - ticks) << "\n";
replay_controller replaycontroller(*level, gameinfo, state_of_game, ticks, num_turns, game_config, video, story);
LOG_NG << "created objects... " << (SDL_GetTicks() - replaycontroller.get_ticks()) << "\n";
const unit_type::experience_accelerator xp_mod(replaycontroller.get_xp_modifier() > 0 ? replaycontroller.get_xp_modifier() : 100);
//replay event-loop
for (;;){
replaycontroller.play_slice();
}
}
catch(end_level_exception&){
LOG_NG << "play_replay_level: end_level_exception\n";
}
return LEVEL_CONTINUE;
}
replay_controller::replay_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
const int ticks, const int num_turns, const config& game_config,
CVideo& video, const std::vector<config*>& story)
: play_controller(level, gameinfo, state_of_game, ticks, num_turns, game_config, video, false),
gamestate_start_(state_of_game), status_start_(level, num_turns)
{
delay_ = 0;
is_playing_ = false;
init(video, story);
}
replay_controller::~replay_controller(){
}
void replay_controller::init(CVideo& video, const std::vector<config*>& /*story*/){
LOG_NG << "in replay_controller::init()...\n";
//guarantee the cursor goes back to 'normal' at the end of the level
const cursor::setter cursor_setter(cursor::NORMAL);
LOG_NG << "initializing replay-display... " << (SDL_GetTicks() - ticks_) << "\n";
const config* theme_cfg = get_theme(game_config_, level_["theme"]);
const config* replay_theme_cfg = theme_cfg->child("resolution")->child("replay");
if (NULL != replay_theme_cfg)
gui_->get_theme().modify(replay_theme_cfg);
LOG_NG << "done initializing replay-display... " << (SDL_GetTicks() - ticks_) << "\n";
/*
if(recorder.is_skipping() == false) {
for(std::vector<config*>::const_iterator story_i = story.begin(); story_i != story.end(); ++story_i) {
show_intro(*gui_,**story_i, level_);
}
}
*/
fire_prestart(true);
init_gui();
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
fire_start(!loading_game_);
update_gui();
units_start_ = units_;
teams_start_ = team_manager_.clone(teams_);
}
void replay_controller::init_gui(){
LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks_) << "\n";
play_controller::init_gui();
gui_->scroll_to_leader(units_, player_number_);
update_locker lock_display((*gui_).video(),false);
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
t->set_fog(false);
t->set_shroud(false);
}
}
std::vector<team>& replay_controller::get_teams(){
return teams_;
}
unit_map replay_controller::get_units(){
return units_;
}
display& replay_controller::get_gui(){
return *gui_;
}
gamemap& replay_controller::get_map(){
return map_;
}
const gamestatus& replay_controller::get_status(){
return status_;
}
const int replay_controller::get_player_number(){
return player_number_;
}
const bool replay_controller::is_loading_game(){
return loading_game_;
}
void replay_controller::reset_replay(){
is_playing_ = false;
player_number_ = 1;
recorder.start_replay();
units_ = *(new unit_map(units_start_));
status_ = *(new gamestatus(status_start_));
gamestate_ = *(new game_state(gamestate_start_));
teams_ = team_manager_.clone(teams_start_);
(*gui_).invalidate_all();
(*gui_).draw();
}
void replay_controller::stop_replay(){
is_playing_ = false;
}
void replay_controller::replay_next_turn(){
is_playing_ = true;
play_turn();
is_playing_ = false;
}
void replay_controller::replay_next_side(){
is_playing_ = true;
play_side(player_number_ - 1);
if ((size_t)player_number_ > teams_.size()){
player_number_ = 1;
current_turn_++;
}
is_playing_ = false;
}
void replay_controller::replay_switch_fog(){
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
t->set_fog(!t->uses_fog());
}
update_teams();
update_gui();
}
void replay_controller::replay_switch_shroud(){
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
t->set_shroud(!t->uses_shroud());
}
update_teams();
update_gui();
}
void replay_controller::replay_skip_animation(){
recorder.set_skip(!recorder.is_skipping());
skip_replay_ = !skip_replay_;
}
void replay_controller::play_replay(){
if (recorder.at_end()){
return;
}
try{
is_playing_ = true;
LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";
for(; !recorder.at_end() && is_playing_; first_player_ = 0) {
play_turn();
play_slice();
} //end for loop
is_playing_ = false;
}
catch(end_level_exception& e){
if (e.result == QUIT) { throw e; }
}
}
void replay_controller::play_turn(){
if (recorder.at_end()){
return;
}
LOG_NG << "turn: " << current_turn_ << "\n";
while ( ((size_t)player_number_ <= teams_.size()) && (!recorder.at_end()) ){
play_side(player_number_ - 1);
}
finish_turn();
player_number_ = 1;
current_turn_++;
}
void replay_controller::play_side(const int team_index){
if (recorder.at_end()){
return;
}
play_controller::init_side(team_index);
//if a side is dead, don't do their turn
if(!current_team().is_empty() && team_units(units_,player_number_) > 0) {
do_replay(true);
finish_side_turn();
for(unit_map::iterator uit = units_.begin(); uit != units_.end(); ++uit) {
if(uit->second.side() != (size_t)player_number_){
//this is necessary for replays in order to show possible movements
uit->second.new_turn(uit->first);
}
}
}
player_number_++;
if ((size_t)player_number_ > teams_.size()) {
status_.next_turn();
}
update_teams();
update_gui();
}
void replay_controller::update_teams(){
int next_team = player_number_;
if ((size_t)next_team > teams_.size()) { next_team = 1; }
if (teams_[next_team - 1].uses_fog()){
gui_->set_team(next_team - 1);
::clear_shroud(*gui_, status_, map_, gameinfo_, units_, teams_, next_team - 1);
}
if (teams_[next_team - 1].uses_shroud()){
gui_->set_team(next_team - 1);
recalculate_fog(map_, status_, gameinfo_, units_, teams_, next_team - 1);
}
gui_->set_playing_team(next_team - 1);
(*gui_).scroll_to_leader(units_, next_team);
}
void replay_controller::update_gui(){
(*gui_).recalculate_minimap();
(*gui_).redraw_minimap();
(*gui_).invalidate_all();
events::raise_draw_event();
(*gui_).draw();
}
bool replay_controller::can_execute_command(hotkey::HOTKEY_COMMAND command) const
{
bool result = play_controller::can_execute_command(command);
switch(command) {
//commands we can always do
case hotkey::HOTKEY_PLAY_REPLAY:
case hotkey::HOTKEY_RESET_REPLAY:
case hotkey::HOTKEY_STOP_REPLAY:
case hotkey::HOTKEY_REPLAY_NEXT_TURN:
case hotkey::HOTKEY_REPLAY_NEXT_SIDE:
case hotkey::HOTKEY_REPLAY_FOG:
case hotkey::HOTKEY_REPLAY_SHROUD:
case hotkey::HOTKEY_REPLAY_SKIP_ANIMATION:
return true;
default:
return result;
}
}

View file

@ -21,15 +21,15 @@
#include "help.hpp"
#include "hotkeys.hpp"
#include "menu_events.hpp"
#include "mouse_events.hpp"
#include "playlevel.hpp"
#include "mouse_events.hpp"
#include "play_controller.hpp"
#include "preferences_display.hpp"
#include "tooltips.hpp"
#include "wml_separators.hpp"
#include <vector>
class replay_controller : public hotkey::command_executor, public events::handler
class replay_controller : public play_controller
{
public:
replay_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
@ -37,28 +37,17 @@ public:
const std::vector<config*>& story);
~replay_controller();
void handle_event(const SDL_Event& event);
void replay_slice();
bool can_execute_command(hotkey::HOTKEY_COMMAND command) const;
virtual bool can_execute_command(hotkey::HOTKEY_COMMAND command) const;
std::vector<team>& get_teams();
unit_map get_units();
display& get_gui();
gamemap& get_map();
const gamestatus& get_status();
const int get_player_number();
const int get_player_number();
const bool is_loading_game();
//event handlers
void objectives();
void show_statistics();
void unit_list();
void status_table();
void save_game();
void load_game();
void preferences();
void show_chat_log();
void show_help();
void play_replay();
void reset_replay();
void stop_replay();
@ -68,47 +57,24 @@ public:
void replay_switch_shroud();
void replay_skip_animation();
std::vector<team> teams_, teams_start_;
std::vector<team> teams_start_;
protected:
virtual void init_gui();
private:
void init(CVideo& video, const std::vector<config*>& story);
void init_managers();
void play_turn();
void play_side(const int team_index);
virtual void play_turn();
virtual void play_side(const int team_index);
void update_teams();
void update_gui();
team& current_team() { return teams_[player_number_-1]; }
const team& current_team() const { return teams_[player_number_-1]; }
game_state& gamestate_start_;
gamestatus status_start_;
unit_map units_start_;
//managers
const verification_manager verify_manager_;
teams_manager team_manager_;
halo::manager* halo_manager_;
font::floating_label_context labels_manager_;
preferences::display_manager* prefs_disp_manager_;
tooltips::manager* tooltips_manager_;
game_events::manager* events_manager_;
help::help_manager help_manager_;
const config& level_;
const config& game_config_;
const game_data& gameinfo_;
game_state& gamestate_, gamestate_start_;
display* gui_;
gamestatus status_, status_start_;
gamemap map_;
unit_map units_, units_start_;
events::mouse_handler mouse_handler_;
events::menu_handler menu_handler_;
const int ticks_;
int player_number_;
int first_player_;
bool loading_game_;
int delay_;
bool is_playing_;
int current_turn_;
const int xp_modifier_;
};

View file

@ -26,7 +26,6 @@
#include "key.hpp"
#include "log.hpp"
#include "marked-up_text.hpp"
#include "playlevel.hpp"
#include "show_dialog.hpp"
#include "thread.hpp"
#include "language.hpp"

View file

@ -55,6 +55,7 @@ namespace statistics
~disabler();
};
struct scenario_context
{
scenario_context(const std::string& name);

View file

@ -22,7 +22,8 @@
#include "unit.hpp"
struct upload_log
{
{
public:
struct manager {
manager() { };
~manager();

View file

@ -43,7 +43,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MD /W3 /Gm /GR /GX /ZI /Od /I "f:/wesnoth/src" /I "f:/SDL-1.2.7/include" /I "f:/SDL_image-1.2.3/include" /I "f:/SDL_mixer-1.2.5/include" /I "f:/SDL_net-1.2.5/include" /I "src/sdl_ttf" /I "f:/libintl-devel/include" /I "f:/intl/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "HAVE_FRIBIDI" /FR /YX /FD /GZ /c
# ADD CPP /nologo /MD /W3 /Gm /GR /GX /ZI /Od /I "f:/wesnoth/src" /I "f:/SDL-1.2.7/include" /I "f:/SDL_image-1.2.3/include" /I "f:/SDL_mixer-1.2.5/include" /I "f:/SDL_net-1.2.5/include" /I "src/sdl_ttf" /I "f:/libintl-devel/include" /I "f:/intl/include" /D "WIN32" /D "_DEBUG" /D "DEBUG" /D "_WINDOWS" /D "_MBCS" /D "HAVE_FRIBIDI" /FR /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
@ -53,7 +53,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_mixer.lib SDL_net.lib SDL_image.lib libintl.lib freetype.lib Ws2_32.lib fribidi.lib python24.lib /nologo /subsystem:windows /debug /machine:I386 /out:"wesnoth_work.exe" /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_mixer.lib SDL_net.lib SDL_image.lib libintl.lib freetype.lib Ws2_32.lib fribidi.lib python24.lib /nologo /subsystem:windows /debug /machine:I386 /out:"wesnoth.exe" /pdbtype:sept
!ELSEIF "$(CFG)" == "wesnoth - Win32 Release"
@ -209,6 +209,10 @@ SOURCE=.\src\filesystem.cpp
# End Source File
# Begin Source File
SOURCE=.\src\floating_textbox.cpp
# End Source File
# Begin Source File
SOURCE=.\src\font.cpp
# End Source File
# Begin Source File
@ -273,6 +277,10 @@ SOURCE=.\src\leader_list.cpp
# End Source File
# Begin Source File
SOURCE=.\src\loadscreen.cpp
# End Source File
# Begin Source File
SOURCE=.\src\log.cpp
# End Source File
# Begin Source File
@ -373,7 +381,11 @@ SOURCE=.\src\playcampaign.cpp
# End Source File
# Begin Source File
SOURCE=.\src\playlevel.cpp
SOURCE=.\src\playmp_controller.cpp
# End Source File
# Begin Source File
SOURCE=.\src\playsingle_controller.cpp
# End Source File
# Begin Source File
@ -469,10 +481,6 @@ SOURCE=.\src\terrain.cpp
# End Source File
# Begin Source File
SOURCE=.\src\test_log.cpp
# End Source File
# Begin Source File
SOURCE=.\src\widgets\textbox.cpp
# End Source File
# Begin Source File
@ -593,6 +601,10 @@ SOURCE=.\src\astarnode.hpp
# End Source File
# Begin Source File
SOURCE=.\src\attack_prediction.hpp
# End Source File
# Begin Source File
SOURCE=.\src\serialization\binary_or_text.hpp
# End Source File
# Begin Source File
@ -657,6 +669,10 @@ SOURCE=.\src\filesystem.hpp
# End Source File
# Begin Source File
SOURCE=.\src\floating_textbox.hpp
# End Source File
# Begin Source File
SOURCE=.\src\font.hpp
# End Source File
# Begin Source File
@ -725,6 +741,10 @@ SOURCE=.\src\leader_list.hpp
# End Source File
# Begin Source File
SOURCE=.\src\loadscreen.hpp
# End Source File
# Begin Source File
SOURCE=.\src\log.hpp
# End Source File
# Begin Source File
@ -829,7 +849,11 @@ SOURCE=.\src\playcampaign.hpp
# End Source File
# Begin Source File
SOURCE=.\src\playlevel.hpp
SOURCE=.\src\playmp_controller.hpp
# End Source File
# Begin Source File
SOURCE=.\src\playsingle_controller.hpp
# End Source File
# Begin Source File

View file

@ -27,18 +27,6 @@ Package=<4>
###############################################################################
Project: "wesnothd"=.\wesnothd\wesnothd.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>