merges the gameplay_refactoring branch
please report any problems to me
This commit is contained in:
commit
45dd38ff87
38 changed files with 4325 additions and 5148 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
29
src/ai.cpp
29
src/ai.cpp
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
180
src/floating_textbox.cpp
Normal 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
42
src/floating_textbox.hpp
Normal 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
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
1232
src/menu_events.cpp
1232
src/menu_events.cpp
File diff suppressed because it is too large
Load diff
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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(¤t_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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
650
src/play_controller.cpp
Normal 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
137
src/play_controller.hpp
Normal 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
|
|
@ -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) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef PLAYCAMPAIGN_H_INCLUDED
|
||||
#define PLAYCAMPAIGN_H_INCLUDED
|
||||
|
||||
#include "playlevel.hpp"
|
||||
#include "menu_events.hpp"
|
||||
|
||||
class display;
|
||||
struct game_state;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
210
src/playmp_controller.cpp
Normal 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
53
src/playmp_controller.hpp
Normal 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
|
498
src/playsingle_controller.cpp
Normal file
498
src/playsingle_controller.cpp
Normal 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);
|
||||
}
|
80
src/playsingle_controller.hpp
Normal file
80
src/playsingle_controller.hpp
Normal 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
|
3089
src/playturn.cpp
3089
src/playturn.cpp
File diff suppressed because it is too large
Load diff
200
src/playturn.hpp
200
src/playturn.hpp
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(¤t_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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -55,6 +55,7 @@ namespace statistics
|
|||
~disabler();
|
||||
};
|
||||
|
||||
|
||||
struct scenario_context
|
||||
{
|
||||
scenario_context(const std::string& name);
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
#include "unit.hpp"
|
||||
|
||||
struct upload_log
|
||||
{
|
||||
{
|
||||
public:
|
||||
struct manager {
|
||||
manager() { };
|
||||
~manager();
|
||||
|
|
40
wesnoth.dsp
40
wesnoth.dsp
|
@ -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
|
||||
|
||||
|
|
12
wesnoth.dsw
12
wesnoth.dsw
|
@ -27,18 +27,6 @@ Package=<4>
|
|||
|
||||
###############################################################################
|
||||
|
||||
Project: "wesnothd"=.\wesnothd\wesnothd.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
|
|
Loading…
Add table
Reference in a new issue