Split actions.cpp into six pieces (into a new "actions" subdirectory).

Most of the project files have also been updated, but I ran into some
difficulties trying to update VC9 and Xcode.
This commit is contained in:
J. Tyne 2012-08-25 14:06:46 +00:00
parent d7e8b4c364
commit 724618c287
49 changed files with 4689 additions and 4249 deletions

View file

@ -43,8 +43,18 @@
</Unit>
<Unit filename="../../src/about.cpp" />
<Unit filename="../../src/about.hpp" />
<Unit filename="../../src/actions.cpp" />
<Unit filename="../../src/actions.hpp" />
<Unit filename="../../src/actions/attack.cpp" />
<Unit filename="../../src/actions/attack.hpp" />
<Unit filename="../../src/actions/create.cpp" />
<Unit filename="../../src/actions/create.hpp" />
<Unit filename="../../src/actions/heal.cpp" />
<Unit filename="../../src/actions/heal.hpp" />
<Unit filename="../../src/actions/move.cpp" />
<Unit filename="../../src/actions/move.hpp" />
<Unit filename="../../src/actions/undo.cpp" />
<Unit filename="../../src/actions/undo.hpp" />
<Unit filename="../../src/actions/vision.cpp" />
<Unit filename="../../src/actions/vision.hpp" />
<Unit filename="../../src/addon/manager.cpp" />
<Unit filename="../../src/addon/manager.hpp" />
<Unit filename="../../src/addon/validation.cpp" />

View file

@ -80,8 +80,18 @@
</Unit>
<Unit filename="..\..\src\about.cpp" />
<Unit filename="..\..\src\about.hpp" />
<Unit filename="..\..\src\actions.cpp" />
<Unit filename="..\..\src\actions.hpp" />
<Unit filename="..\..\src\actions\attack.cpp" />
<Unit filename="..\..\src\actions\attack.hpp" />
<Unit filename="..\..\src\actions\create.cpp" />
<Unit filename="..\..\src\actions\create.hpp" />
<Unit filename="..\..\src\actions\heal.cpp" />
<Unit filename="..\..\src\actions\heal.hpp" />
<Unit filename="..\..\src\actions\move.cpp" />
<Unit filename="..\..\src\actions\move.hpp" />
<Unit filename="..\..\src\actions\undo.cpp" />
<Unit filename="..\..\src\actions\undo.hpp" />
<Unit filename="..\..\src\actions\vision.cpp" />
<Unit filename="..\..\src\actions\vision.hpp" />
<Unit filename="..\..\src\addon\client.cpp" />
<Unit filename="..\..\src\addon\client.hpp" />
<Unit filename="..\..\src\addon\info.cpp" />

View file

@ -582,7 +582,12 @@ add_library(wesnoth-schema_validator
set(wesnoth-main_SRC
about.cpp
actions.cpp
actions/attack.cpp
actions/create.cpp
actions/heal.cpp
actions/move.cpp
actions/undo.cpp
actions/vision.cpp
addon/client.cpp
addon/info.cpp
addon/manager.cpp

View file

@ -162,7 +162,12 @@ libcutter = client_env.Library("cutter", libcutter_sources)
# Used by both 'wesnoth' and 'test' targets
wesnoth_sources = Split("""
about.cpp
actions.cpp
actions/attack.cpp
actions/create.cpp
actions/heal.cpp
actions/move.cpp
actions/undo.cpp
actions/vision.cpp
addon/client.cpp
addon/info.cpp
addon/manager.cpp

File diff suppressed because it is too large Load diff

View file

@ -1,442 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Various functions which implement in-game events and commands.
*/
#ifndef ACTIONS_H_INCLUDED
#define ACTIONS_H_INCLUDED
class attack_type;
class game_display;
class replay;
struct combatant;
class team;
struct time_of_day;
#include "unit.hpp"
class unit_creator {
public:
unit_creator(team &tm, const map_location &start_pos);
unit_creator& allow_show(bool b);
unit_creator& allow_get_village(bool b);
unit_creator& allow_rename_side(bool b);
unit_creator& allow_invalidate(bool b);
unit_creator& allow_discover(bool b);
unit_creator& allow_add_to_recall(bool b);
/**
* finds a suitable location for unit
* @retval map_location::null_location if unit is to be put into recall list
* @retval valid on-board map location otherwise
*/
map_location find_location(const config &cfg, const unit* pass_check=NULL);
/**
* adds a unit on map without firing any events (so, usable during team construction in gamestatus)
*/
void add_unit(const config &cfg, const vconfig* vcfg = NULL);
private:
void post_create(const map_location &loc, const unit &new_unit, bool anim);
bool add_to_recall_;
bool discover_;
bool get_village_;
bool invalidate_;
bool rename_side_;
bool show_;
const map_location start_pos_;
team &team_;
};
/// Checks to see if a leader at @a leader_loc could recruit on @a recruit_loc.
bool can_recruit_on(const map_location& leader_loc, const map_location& recruit_loc, int side);
/// Checks to see if @a leader (assumed a leader) can recruit on @a recruit_loc.
/// This takes into account terrain, shroud, and whether or not there is already
/// a visible unit at recruit_loc.
inline bool can_recruit_on(const unit& leader, const map_location& recruit_loc)
{ return can_recruit_on(leader.get_location(), recruit_loc, leader.side()); }
/**
* Finds a location to place a unit.
* A leader of the @a side must be on a keep
* connected by castle to a legal recruiting location. Otherwise, an error
* message explaining this is returned.
*
* If no errors are encountered, the location where a unit can be recruited
* is stored in @a recruit_location. Its value is considered first, if it is a
* legal option.
*
* @return an empty string on success. Otherwise a human-readable message
* describing the failure is returned.
*/
std::string find_recruit_location(const int side, map_location &recruit_location, map_location& recruited_from, const std::string& unit_type);
/**
* Finds a location to recall @a unit_recall.
* A leader of the @a side must be on a keep
* connected by castle to a legal recalling location. Otherwise, an error
* message explaining this is returned.
*
* If no errors are encountered, the location where a unit can be recalled
* is stored in @a recall_location. Its value is considered first, if it is a
* legal option.
*
* @return an empty string on success. Otherwise a human-readable message
* describing the failure is returned.
*/
std::string find_recall_location(const int side, map_location& recall_location, map_location& recall_from, const unit &unit_recall);
/**
* Get's the recruitable units from a side's leaders' personal recruit lists who can recruit on a specific hex field.
* @param side of the leaders to search for their personal recruit lists.
* @param recruit_location the hex field being part of the castle the player wants to recruit on.
* @return a set of units that can be recruited by leaders on a keep connected by castle tiles with @a recruit_location.
*/
const std::set<std::string> get_recruits_for_location(int side, const map_location &recruit_location);
/**
* Get's the recruitable units from a side's leaders' personal recruit lists who can recruit on a specific hex field.
* If no leader is able to recruit on the given location the full recall list of the side is returned.
* @param side of the leaders to search for their personal recruit lists.
* @param recruit_location the hex field being part of the castle the player wants to recruit on.
* @return a set of units that can be recruited by @a side on @a recall_loc or the full recall list of @a side.
*/
const std::vector<const unit*> get_recalls_for_location(int side, const map_location &recruit_location);
/**
* Place a unit into the game.
* The unit will be placed on @a recruit_location, which should be retrieved
* through a call to recruit_location().
*/
void place_recruit(const unit &u, const map_location &recruit_location, const map_location& recruited_from,
bool is_recall, bool show = false, bool fire_event = true, bool full_movement = false,
bool wml_triggered = false);
/** Structure describing the statistics of a unit involved in the battle. */
struct battle_context_unit_stats
{
const attack_type *weapon; /**< The weapon used by the unit to attack the opponent, or NULL if there is none. */
int attack_num; /**< Index into unit->attacks() or -1 for none. */
bool is_attacker; /**< True if the unit is the attacker. */
bool is_poisoned; /**< True if the unit is poisoned at the beginning of the battle. */
bool is_slowed; /**< True if the unit is slowed at the beginning of the battle. */
bool slows; /**< Attack slows opponent when it hits. */
bool drains; /**< Attack drains opponent when it hits. */
bool petrifies; /**< Attack petrifies opponent when it hits. */
bool plagues; /**< Attack turns opponent into a zombie when fatal. */
bool poisons; /**< Attack poisons opponent when it hits. */
bool backstab_pos; /**<
* True if the attacker is in *position* to backstab the defender (this is used to
* determine whether to apply the backstab bonus in case the attacker has backstab).
*/
bool swarm; /**< Attack has swarm special. */
bool firststrike; /**< Attack has firststrike special. */
unsigned int experience, max_experience;
unsigned int level;
unsigned int rounds; /**< Berserk special can force us to fight more than one round. */
unsigned int hp; /**< Hitpoints of the unit at the beginning of the battle. */
unsigned int max_hp; /**< Maximum hitpoints of the unit. */
unsigned int chance_to_hit; /**< Effective chance to hit as a percentage (all factors accounted for). */
int damage; /**< Effective damage of the weapon (all factors accounted for). */
int slow_damage; /**< Effective damage if unit becomes slowed (== damage, if already slowed) */
int drain_percent; /**< Percentage of damage recovered as health */
int drain_constant; /**< Base HP drained regardless of damage dealt */
unsigned int num_blows; /**< Effective number of blows, takes swarm into account. */
unsigned int swarm_min; /**< Minimum number of blows with swarm (equal to num_blows if swarm isn't used). */
unsigned int swarm_max; /**< Maximum number of blows with swarm (equal to num_blows if swarm isn't used). */
std::string plague_type; /**< The plague type used by the attack, if any. */
battle_context_unit_stats(const unit &u, const map_location& u_loc,
int u_attack_num, bool attacking,
const unit &opp, const map_location& opp_loc,
const attack_type *opp_weapon,
const unit_map& units);
~battle_context_unit_stats();
/** Dumps the statistics of a unit on stdout. Remove it eventually. */
void dump() const;
};
/** Computes the statistics of a battle between an attacker and a defender unit. */
class battle_context
{
public:
/**
* If no attacker_weapon is given, we select the best one,
* based on harm_weight (1.0 means 1 hp lost counters 1 hp damage,
* 0.0 means we ignore harm weight).
* prev_def is for predicting multiple attacks against a defender.
*/
battle_context(const unit_map &units,
const map_location& attacker_loc, const map_location& defender_loc,
int attacker_weapon = -1, int defender_weapon = -1, double aggression = 0.0, const combatant *prev_def = NULL, const unit* attacker_ptr=NULL);
/** Used by the AI which caches battle_context_unit_stats */
battle_context(const battle_context_unit_stats &att, const battle_context_unit_stats &def);
battle_context(const battle_context &other);
~battle_context();
battle_context& operator=(const battle_context &other);
/** This method returns the statistics of the attacker. */
const battle_context_unit_stats& get_attacker_stats() const { return *attacker_stats_; }
/** This method returns the statistics of the defender. */
const battle_context_unit_stats& get_defender_stats() const { return *defender_stats_; }
/** Get the simulation results. */
const combatant &get_attacker_combatant(const combatant *prev_def = NULL);
const combatant &get_defender_combatant(const combatant *prev_def = NULL);
/** Given this harm_weight, is this attack better than that? */
bool better_attack(class battle_context &that, double harm_weight);
private:
bool better_combat(const combatant &us_a, const combatant &them_a,
const combatant &us_b, const combatant &them_b,
double harm_weight);
int choose_attacker_weapon(const unit &attacker, const unit &defender,
const unit_map& units,
const map_location& attacker_loc, const map_location& defender_loc,
double harm_weight, int *defender_weapon, const combatant *prev_def);
int choose_defender_weapon(const unit &attacker, const unit &defender, unsigned attacker_weapon,
const unit_map& units,
const map_location& attacker_loc, const map_location& defender_loc, const combatant *prev_def);
/** Statistics of the units. */
battle_context_unit_stats *attacker_stats_, *defender_stats_;
/** Outcome of simulated fight. */
combatant *attacker_combatant_, *defender_combatant_;
};
/** Performs an attack. */
void attack_unit(const map_location &attacker, const map_location &defender,
int attack_with, int defend_with, bool update_display = true);
class move_unit_spectator {
public:
/** add a location of a seen friend */
void add_seen_friend(const unit_map::const_iterator &u);
/** add the location of new seen enemy */
void add_seen_enemy(const unit_map::const_iterator &u);
/** get the location of an ambusher */
const unit_map::const_iterator& get_ambusher() const;
/** get the location of a failed teleport */
const unit_map::const_iterator& get_failed_teleport() const;
/** get the locations of seen enemies */
const std::vector<unit_map::const_iterator>& get_seen_enemies() const;
/** get the locations of seen friends */
const std::vector<unit_map::const_iterator>& get_seen_friends() const;
/** get new location of moved unit */
const unit_map::const_iterator& get_unit() const;
/** constructor */
move_unit_spectator(const unit_map &units);
/** destructor */
virtual ~move_unit_spectator();
/** reset all locations to empty values*/
void reset(const unit_map &units);
/** set the location of an ambusher */
void set_ambusher(const unit_map::const_iterator &u);
/** set the location of a failed teleport */
void set_failed_teleport(const unit_map::const_iterator &u);
/** set the iterator to moved unit*/
void set_unit(const unit_map::const_iterator &u);
private:
unit_map::const_iterator ambusher_;
unit_map::const_iterator failed_teleport_;
std::vector<unit_map::const_iterator> seen_enemies_;
std::vector<unit_map::const_iterator> seen_friends_;
unit_map::const_iterator unit_;
};
/**
* Makes it so the village at the given location is owned by the given side.
* Returns true if getting the village triggered a mutating event.
*/
bool get_village(const map_location& loc, int side, int *time_bonus = NULL);
/**
* Resets resting for all units on this side: should be called after calculate_healing().
* @todo FIXME: Try moving this to unit::new_turn, then move it above calculate_healing().
*/
void reset_resting(unit_map& units, 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(int side, bool update_display);
/**
* Returns the advanced version of unit (with traits and items retained).
*/
unit get_advanced_unit(const unit &u, const std::string &advance_to);
/**
* Function which will advance the unit at @a loc to 'advance_to'.
* Note that 'loc' is not a reference, because if it were a reference,
* we couldn't safely pass in a reference to the item in the map
* that we're going to delete, since deletion would invalidate the reference.
*/
void advance_unit(map_location loc, const std::string &advance_to, const bool &fire_event = true);
/**
* function which tests if the unit at loc is currently affected by leadership.
* (i.e. has a higher-level 'leadership' unit next to it).
* If it does, then the location of the leader unit will be returned,
* Otherwise map_location::null_location will be returned.
* If 'bonus' is not NULL, the % bonus will be stored in it.
*/
map_location under_leadership(const unit_map& units,
const map_location& loc, int* bonus=NULL);
/**
* Returns the amount that a unit's damage should be multiplied by
* due to the current time of day.
*/
int combat_modifier(const map_location &loc,
unit_type::ALIGNMENT alignment, bool is_fearless);
/** Records information to be able to undo a movement. */
struct undo_action {
enum ACTION_TYPE { NONE, RECRUIT, RECALL, DISMISS };
undo_action(const unit& u,
const std::vector<map_location>::const_iterator & begin,
const std::vector<map_location>::const_iterator & end,
int sm, int timebonus=0, int orig=-1,
const map_location::DIRECTION dir=map_location::NDIRECTIONS) :
route(begin, end),
starting_moves(sm),
original_village_owner(orig),
recall_loc(),
recall_from(),
type(NONE),
affected_unit(u),
countdown_time_bonus(timebonus),
starting_dir(dir == map_location::NDIRECTIONS ? u.facing() : dir)
{
}
undo_action(const unit& u, const map_location& loc, const map_location& from,
const ACTION_TYPE action_type=NONE) :
route(),
starting_moves(),
original_village_owner(),
recall_loc(loc),
recall_from(from),
type(action_type),
affected_unit(u),
countdown_time_bonus(1),
starting_dir(u.facing())
{}
std::vector<map_location> route;
int starting_moves;
int original_village_owner;
map_location recall_loc;
map_location recall_from;
ACTION_TYPE type;
unit affected_unit;
int countdown_time_bonus;
map_location::DIRECTION starting_dir;
bool is_dismiss() const { return type == DISMISS; }
bool is_recall() const { return type == RECALL; }
bool is_recruit() const { return type == RECRUIT; }
};
typedef std::vector<undo_action> undo_list;
/// Moves a unit across the board.
size_t move_unit(const std::vector<map_location> &steps,
replay* move_recorder, undo_list* undo_stack,
bool continued_move = false, bool show_move = true,
bool* interrupted = NULL,
move_unit_spectator* move_spectator = NULL,
const map_location* replay_dest = NULL);
/// Function that recalculates the fog of war.
void recalculate_fog(int side);
/// Function that will clear shroud (and fog) based on current unit positions.
bool clear_shroud(int side, bool reset_fog=false);
/**
* Function to apply pending shroud changes in the undo stack.
*/
void apply_shroud_changes(undo_list &undos, int side);
/**
* Will return true iff the unit @a u has any possible moves
* it can do (including attacking etc).
*/
bool unit_can_move(const unit &u);
/**
* Function to check if an attack will satisfy the requirements for backstab.
* Input:
* - the location from which the attack will occur,
* - the defending unit location,
* - the list of units on the map and
* - the list of teams.
* The defender and opposite units should be in place already.
* The attacking unit doesn't need to be, but if it isn't,
* an external check should be made to make sure the opposite unit
* isn't also the attacker.
*/
bool backstab_check(const map_location& attacker_loc,
const map_location& defender_loc,
const unit_map& units, const std::vector<team>& teams);
#endif

1376
src/actions/attack.cpp Normal file

File diff suppressed because it is too large Load diff

195
src/actions/attack.hpp Normal file
View file

@ -0,0 +1,195 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Various functions that implement attacks and attack calculations.
* Unit advancements are also included, as they usually occur as a
* result of combat.
*/
#ifndef ACTIONS_ATTACK_H_INCLUDED
#define ACTIONS_ATTACK_H_INCLUDED
struct combatant;
struct map_location;
class team;
class unit;
class unit_map;
#include "../unit_types.hpp"
#include <vector>
/** Structure describing the statistics of a unit involved in the battle. */
struct battle_context_unit_stats
{
const attack_type *weapon; /**< The weapon used by the unit to attack the opponent, or NULL if there is none. */
int attack_num; /**< Index into unit->attacks() or -1 for none. */
bool is_attacker; /**< True if the unit is the attacker. */
bool is_poisoned; /**< True if the unit is poisoned at the beginning of the battle. */
bool is_slowed; /**< True if the unit is slowed at the beginning of the battle. */
bool slows; /**< Attack slows opponent when it hits. */
bool drains; /**< Attack drains opponent when it hits. */
bool petrifies; /**< Attack petrifies opponent when it hits. */
bool plagues; /**< Attack turns opponent into a zombie when fatal. */
bool poisons; /**< Attack poisons opponent when it hits. */
bool backstab_pos; /**<
* True if the attacker is in *position* to backstab the defender (this is used to
* determine whether to apply the backstab bonus in case the attacker has backstab).
*/
bool swarm; /**< Attack has swarm special. */
bool firststrike; /**< Attack has firststrike special. */
unsigned int experience, max_experience;
unsigned int level;
unsigned int rounds; /**< Berserk special can force us to fight more than one round. */
unsigned int hp; /**< Hitpoints of the unit at the beginning of the battle. */
unsigned int max_hp; /**< Maximum hitpoints of the unit. */
unsigned int chance_to_hit; /**< Effective chance to hit as a percentage (all factors accounted for). */
int damage; /**< Effective damage of the weapon (all factors accounted for). */
int slow_damage; /**< Effective damage if unit becomes slowed (== damage, if already slowed) */
int drain_percent; /**< Percentage of damage recovered as health */
int drain_constant; /**< Base HP drained regardless of damage dealt */
unsigned int num_blows; /**< Effective number of blows, takes swarm into account. */
unsigned int swarm_min; /**< Minimum number of blows with swarm (equal to num_blows if swarm isn't used). */
unsigned int swarm_max; /**< Maximum number of blows with swarm (equal to num_blows if swarm isn't used). */
std::string plague_type; /**< The plague type used by the attack, if any. */
battle_context_unit_stats(const unit &u, const map_location& u_loc,
int u_attack_num, bool attacking,
const unit &opp, const map_location& opp_loc,
const attack_type *opp_weapon,
const unit_map& units);
~battle_context_unit_stats();
/** Dumps the statistics of a unit on stdout. Remove it eventually. */
void dump() const;
};
/** Computes the statistics of a battle between an attacker and a defender unit. */
class battle_context
{
public:
/**
* If no attacker_weapon is given, we select the best one,
* based on harm_weight (1.0 means 1 hp lost counters 1 hp damage,
* 0.0 means we ignore harm weight).
* prev_def is for predicting multiple attacks against a defender.
*/
battle_context(const unit_map &units,
const map_location& attacker_loc, const map_location& defender_loc,
int attacker_weapon = -1, int defender_weapon = -1,
double aggression = 0.0, const combatant *prev_def = NULL,
const unit* attacker_ptr=NULL);
/** Used by the AI which caches battle_context_unit_stats */
battle_context(const battle_context_unit_stats &att, const battle_context_unit_stats &def);
battle_context(const battle_context &other);
~battle_context();
battle_context& operator=(const battle_context &other);
/** This method returns the statistics of the attacker. */
const battle_context_unit_stats& get_attacker_stats() const { return *attacker_stats_; }
/** This method returns the statistics of the defender. */
const battle_context_unit_stats& get_defender_stats() const { return *defender_stats_; }
/** Get the simulation results. */
const combatant &get_attacker_combatant(const combatant *prev_def = NULL);
const combatant &get_defender_combatant(const combatant *prev_def = NULL);
/** Given this harm_weight, is this attack better than that? */
bool better_attack(class battle_context &that, double harm_weight);
private:
bool better_combat(const combatant &us_a, const combatant &them_a,
const combatant &us_b, const combatant &them_b,
double harm_weight);
int choose_attacker_weapon(const unit &attacker, const unit &defender,
const unit_map& units,
const map_location& attacker_loc, const map_location& defender_loc,
double harm_weight, int *defender_weapon,
const combatant *prev_def);
int choose_defender_weapon(const unit &attacker, const unit &defender,
unsigned attacker_weapon, const unit_map& units,
const map_location& attacker_loc,
const map_location& defender_loc,
const combatant *prev_def);
/** Statistics of the units. */
battle_context_unit_stats *attacker_stats_, *defender_stats_;
/** Outcome of simulated fight. */
combatant *attacker_combatant_, *defender_combatant_;
};
/** Performs an attack. */
void attack_unit(const map_location &attacker, const map_location &defender,
int attack_with, int defend_with, bool update_display = true);
/**
* Returns the advanced version of unit (with traits and items retained).
*/
unit get_advanced_unit(const unit &u, const std::string &advance_to);
/**
* Function which will advance the unit at @a loc to 'advance_to'.
* Note that 'loc' is not a reference, because if it were a reference,
* we couldn't safely pass in a reference to the item in the map
* that we're going to delete, since deletion would invalidate the reference.
*/
void advance_unit(map_location loc, const std::string &advance_to, const bool &fire_event = true);
/**
* function which tests if the unit at loc is currently affected by leadership.
* (i.e. has a higher-level 'leadership' unit next to it).
* If it does, then the location of the leader unit will be returned,
* Otherwise map_location::null_location will be returned.
* If 'bonus' is not NULL, the % bonus will be stored in it.
*/
map_location under_leadership(const unit_map& units, const map_location& loc,
int* bonus=NULL);
/**
* Returns the amount that a unit's damage should be multiplied by
* due to the current time of day.
*/
int combat_modifier(const map_location &loc, unit_type::ALIGNMENT alignment,
bool is_fearless);
/**
* Function to check if an attack will satisfy the requirements for backstab.
* Input:
* - the location from which the attack will occur,
* - the defending unit location,
* - the list of units on the map and
* - the list of teams.
* The defender and opposite units should be in place already.
* The attacking unit doesn't need to be, but if it isn't,
* an external check should be made to make sure the opposite unit
* isn't also the attacker.
*/
bool backstab_check(const map_location& attacker_loc,
const map_location& defender_loc,
const unit_map& units, const std::vector<team>& teams);
#endif

646
src/actions/create.cpp Normal file
View file

@ -0,0 +1,646 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Recruiting, recalling.
*/
#include "create.hpp"
#include "move.hpp"
#include "../config.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../game_preferences.hpp"
#include "../gettext.hpp"
#include "../log.hpp"
#include "../map.hpp"
#include "../pathfind/pathfind.hpp"
#include "../random.hpp"
#include "../replay.hpp"
#include "../resources.hpp"
#include "../team.hpp"
#include "../unit_display.hpp"
#include "../variable.hpp"
#include "../whiteboard/manager.hpp"
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
static lg::log_domain log_engine("engine");
#define DBG_NG LOG_STREAM(debug, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
#define ERR_NG LOG_STREAM(err, log_engine)
struct castle_cost_calculator : pathfind::cost_calculator
{
castle_cost_calculator(const gamemap& map, const team & view_team) :
map_(map),
viewer_(view_team),
use_shroud_(view_team.uses_shroud())
{}
virtual double cost(const map_location& loc, const double) const
{
if(!map_.is_castle(loc))
return 10000;
if ( use_shroud_ && viewer_.shrouded(loc) )
return 10000;
return 1;
}
private:
const gamemap& map_;
const team& viewer_;
const bool use_shroud_; // Allows faster checks when shroud is disabled.
};
unit_creator::unit_creator(team &tm, const map_location &start_pos)
: add_to_recall_(false),discover_(false),get_village_(false),invalidate_(false), rename_side_(false), show_(false), start_pos_(start_pos), team_(tm)
{
}
unit_creator& unit_creator::allow_show(bool b)
{
show_=b;
return *this;
}
unit_creator& unit_creator::allow_get_village(bool b)
{
get_village_=b;
return *this;
}
unit_creator& unit_creator::allow_rename_side(bool b)
{
rename_side_=b;
return *this;
}
unit_creator& unit_creator::allow_invalidate(bool b)
{
invalidate_=b;
return *this;
}
unit_creator& unit_creator::allow_discover(bool b)
{
discover_=b;
return *this;
}
unit_creator& unit_creator::allow_add_to_recall(bool b)
{
add_to_recall_=b;
return *this;
}
map_location unit_creator::find_location(const config &cfg, const unit* pass_check)
{
DBG_NG << "finding location for unit with id=["<<cfg["id"]<<"] placement=["<<cfg["placement"]<<"] x=["<<cfg["x"]<<"] y=["<<cfg["y"]<<"] for side " << team_.side() << "\n";
std::vector< std::string > placements = utils::split(cfg["placement"]);
placements.push_back("map");
placements.push_back("recall");
BOOST_FOREACH(std::string place, placements) {
map_location loc;
bool pass((place == "leader_passable") || (place == "map_passable"));
if ( place == "recall" ) {
return map_location::null_location;
}
else if ( place == "leader" || place == "leader_passable" ) {
unit_map::const_iterator leader = resources::units->find_leader(team_.side());
//todo: take 'leader in recall list' possibility into account
if (leader.valid()) {
loc = leader->get_location();
} else {
loc = start_pos_;
}
}
// "map", "map_passable", and "map_overwrite".
else if ( place == "map" || place.compare(0, 4, "map_") == 0 ) {
loc = map_location(cfg,resources::gamedata);
}
if(loc.valid() && resources::game_map->on_board(loc)) {
if ( place != "map_overwrite" ) {
loc = find_vacant_tile(loc, pathfind::VACANT_ANY,
pass ? pass_check : NULL);
}
if(loc.valid() && resources::game_map->on_board(loc)) {
return loc;
}
}
}
return map_location::null_location;
}
void unit_creator::add_unit(const config &cfg, const vconfig* vcfg)
{
config temp_cfg(cfg);
temp_cfg["side"] = team_.side();
temp_cfg.remove_attribute("player_id");
temp_cfg.remove_attribute("faction_from_recruit");
const std::string& id =(cfg)["id"];
bool animate = temp_cfg["animate"].to_bool();
temp_cfg.remove_attribute("animate");
std::vector<unit> &recall_list = team_.recall_list();
std::vector<unit>::iterator recall_list_element = find_if_matches_id(recall_list, id);
if ( recall_list_element == recall_list.end() ) {
//make a temporary unit
boost::scoped_ptr<unit> temp_unit(new unit(temp_cfg, true, resources::state_of_game, vcfg));
map_location loc = find_location(temp_cfg, temp_unit.get());
if ( loc.valid() ) {
unit *new_unit = temp_unit.get();
//add temporary unit to map
resources::units->replace(loc, *new_unit);
LOG_NG << "inserting unit for side " << new_unit->side() << "\n";
post_create(loc,*(resources::units->find(loc)),animate);
}
else if ( add_to_recall_ ) {
//add to recall list
unit *new_unit = temp_unit.get();
recall_list.push_back(*new_unit);
DBG_NG << "inserting unit with id=["<<id<<"] on recall list for side " << new_unit->side() << "\n";
preferences::encountered_units().insert(new_unit->type_id());
}
} else {
//get unit from recall list
map_location loc = find_location(temp_cfg, &(*recall_list_element));
if ( loc.valid() ) {
resources::units->replace(loc, *recall_list_element);
LOG_NG << "inserting unit from recall list for side " << recall_list_element->side()<< " with id="<< id << "\n";
post_create(loc,*(resources::units->find(loc)),animate);
//if id is not empty, delete units with this ID from recall list
erase_if_matches_id(recall_list, id);
}
else if ( add_to_recall_ ) {
LOG_NG << "wanted to insert unit on recall list, but recall list for side " << (cfg)["side"] << "already contains id=" <<id<<"\n";
return;
}
}
}
void unit_creator::post_create(const map_location &loc, const unit &new_unit, bool anim)
{
if (discover_) {
preferences::encountered_units().insert(new_unit.type_id());
}
bool show = show_ && (resources::screen !=NULL) && !resources::screen->fogged(loc);
bool animate = show && anim;
if (get_village_) {
if (resources::game_map->is_village(loc)) {
get_village(loc, new_unit.side());
}
}
if (resources::screen!=NULL) {
if (invalidate_ ) {
resources::screen->invalidate(loc);
}
if (animate) {
unit_display::unit_recruited(loc);
} else if (show) {
resources::screen->draw();
}
}
}
/**
* Checks to see if a leader at @a leader_loc could recruit on @a recruit_loc.
* This takes into account terrain, shroud (for side @a side), and whether or
* not there is already a visible unit at recruit_loc.
* The behavior for an invalid @a side is subject to change for future needs.
*/
bool can_recruit_on(const map_location& leader_loc, const map_location& recruit_loc, int side)
{
const gamemap& map = *resources::game_map;
if( !map.is_castle(recruit_loc) )
return false;
if( !map.is_keep(leader_loc) )
return false;
if ( side < 1 || resources::teams == NULL ||
resources::teams->size() < static_cast<size_t>(side) ) {
// Invalid side specified.
// Currently this cannot happen, but it could conceivably be used in
// the future to request that shroud and visibility be ignored. Until
// that comes to pass, just return.
return false;
}
const team & view_team = (*resources::teams)[side-1];
if ( view_team.shrouded(recruit_loc) )
return false;
if ( get_visible_unit(recruit_loc, view_team) != NULL )
return false;
castle_cost_calculator calc(map, view_team);
// The limit computed in the third argument is more than enough for
// any convex castle on the map. Strictly speaking it could be
// reduced to sqrt(map.w()**2 + map.h()**2).
pathfind::plain_route rt =
pathfind::a_star_search(leader_loc, recruit_loc, map.w()+map.h(), &calc,
map.w(), map.h());
return !rt.steps.empty();
}
const std::set<std::string> get_recruits_for_location(int side, const map_location &recruit_loc)
{
LOG_NG << "getting recruit list for side " << side << " at location " << recruit_loc << "\n";
const std::set<std::string>& recruit_list = (*resources::teams)[side -1].recruits();
std::set<std::string> local_result;
std::set<std::string> global_result;
unit_map::const_iterator u = resources::units->begin(),
u_end = resources::units->end();
bool leader_in_place = false;
bool recruit_loc_is_castle = resources::game_map->is_castle(recruit_loc);
for(; u != u_end; ++u) {
//Only consider leaders on this side.
if (!(u->can_recruit() && u->side() == side))
continue;
// Check if the leader is on a connected keep.
if ( can_recruit_on(*u, recruit_loc) ) {
leader_in_place= true;
local_result.insert(u->recruits().begin(), u->recruits().end());
} else
global_result.insert(u->recruits().begin(), u->recruits().end());
}
bool global = !(recruit_loc_is_castle && leader_in_place);
if (global)
global_result.insert(recruit_list.begin(),recruit_list.end());
else if (leader_in_place)
local_result.insert(recruit_list.begin(),recruit_list.end());
return global ? global_result : local_result;
}
const std::vector<const unit*> get_recalls_for_location(int side, const map_location &recall_loc) {
LOG_NG << "getting recall list for side " << side << " at location " << recall_loc << "\n";
const team& t = (*resources::teams)[side-1];
const std::vector<unit>& recall_list = t.recall_list();
std::vector<const unit*> result;
/*
* We have two use cases:
* 1. A castle tile is highlighted, we only present the units recallable there.
* 2. A non castle tile is highlighted, we present all units in the recall list.
*/
bool leader_in_place = false;
bool recall_loc_is_castle = resources::game_map->is_castle(recall_loc);
if (recall_loc_is_castle) {
unit_map::const_iterator u = resources::units->begin(),
u_end = resources::units->end();
std::set<size_t> valid_local_recalls;
for(; u != u_end; ++u) {
//We only consider leaders on our side.
if (!(u->can_recruit() && u->side() == side))
continue;
// Check if the leader is on a connected keep.
if ( can_recruit_on(*u, recall_loc) )
leader_in_place= true;
else continue;
BOOST_FOREACH(const unit& recall_unit, recall_list)
{
//Only units which match the leaders recall filter are valid.
scoped_recall_unit this_unit("this_unit", t.save_id(), &recall_unit - &recall_list[0]);
if (!(recall_unit.matches_filter(vconfig(u->recall_filter()), map_location::null_location)))
continue;
//Do not add a unit twice.
if (valid_local_recalls.find(recall_unit.underlying_id())
== valid_local_recalls.end()) {
valid_local_recalls.insert(recall_unit.underlying_id());
result.push_back(&recall_unit);
}
}
}
}
if (!(recall_loc_is_castle && leader_in_place)) {
BOOST_FOREACH(const unit &recall, recall_list)
{
result.push_back(&recall);
}
}
return result;
}
std::string find_recall_location(const int side, map_location& recall_loc, map_location& recall_from, const unit &recall_unit)
{
LOG_NG << "finding recall location for side " << side << " and unit " << recall_unit.id() << "\n";
unit_map::const_iterator u = resources::units->begin(),
u_end = resources::units->end(), leader = u_end, leader_keep = u_end, leader_fit = u_end,
leader_able = u_end, leader_opt = u_end;
map_location alternate_location = map_location::null_location;
map_location alternate_from = map_location::null_location;
for(; u != u_end; ++u) {
//quit if it is not a leader on the @side
if (!(u->can_recruit() && u->side() == side))
continue;
leader = u;
//quit if the leader is not able to recall the @recall_unit
const team& t = (*resources::teams)[side-1];
scoped_recall_unit this_unit("this_unit",
t.save_id(),
&recall_unit - &t.recall_list()[0]);
if (!(recall_unit.matches_filter(vconfig(leader->recall_filter()), map_location::null_location)))
continue;
leader_able = leader;
//quit if the leader is not on a keep
if (!(resources::game_map->is_keep(leader->get_location())))
continue;
leader_keep = leader_able;
//find a place to recall in the leader's keep
map_location tmp_location = pathfind::find_vacant_castle(*leader_keep);
//quit if there is no place to recruit on
if (tmp_location == map_location::null_location)
continue;
leader_fit = leader_keep;
if ( can_recruit_on(*leader_fit, recall_loc) ) {
leader_opt = leader_fit;
if (resources::units->count(recall_loc) == 1)
recall_loc = tmp_location;
recall_from = leader_opt->get_location();
break;
} else {
alternate_location = tmp_location;
alternate_from = leader_fit->get_location();
}
}
if (leader == u_end) {
LOG_NG << "No Leader on side " << side << " when recalling " << recall_unit.id() << '\n';
return _("You dont have a leader to recall with.");
}
if (leader_able == u_end) {
LOG_NG << "No Leader able to recall unit: " << recall_unit.id() << " on side " << side << '\n';
return _("None of your leaders is able to recall that unit.");
}
if (leader_keep == u_end) {
LOG_NG << "No Leader on a keep to recall the unit " << recall_unit.id() << " at " << recall_loc << '\n';
return _("You must have a leader on a keep who is able to recall that unit.");
}
if (leader_fit == u_end) {
LOG_NG << "No vacant castle tiles on a keep available to recall the unit " << recall_unit.id() << " at " << recall_loc << '\n';
return _("There are no vacant castle tiles in which to recall the unit.");
}
if (leader_opt == u_end) {
recall_loc = alternate_location;
recall_from = alternate_from;
}
return std::string();
}
std::string find_recruit_location(const int side, map_location& recruit_location, map_location& recruited_from, const std::string& unit_type)
{
LOG_NG << "finding recruit location for side " << side << "\n";
unit_map::const_iterator u = resources::units->begin(), u_end = resources::units->end(),
leader = u_end, leader_keep = u_end, leader_fit = u_end,
leader_able = u_end, leader_opt = u_end;
map_location alternate_location = map_location::null_location;
map_location alternate_from = map_location::null_location;
const std::set<std::string>& recruit_list = (*resources::teams)[side -1].recruits();
std::set<std::string>::const_iterator recruit_it = recruit_list.find(unit_type);
bool is_on_team_list = (recruit_it != recruit_list.end());
for(; u != u_end; ++u) {
if (!(u->can_recruit() && u->side() == side))
continue;
leader = u;
bool can_recruit_unit = is_on_team_list;
if (!can_recruit_unit) {
BOOST_FOREACH(const std::string &recruitable, leader->recruits()) {
if (recruitable == unit_type) {
can_recruit_unit = true;
break;
}
}
}
if (!can_recruit_unit)
continue;
leader_able = leader;
if (!(resources::game_map->is_keep(leader_able->get_location())))
continue;
leader_keep = leader_able;
map_location tmp_location = pathfind::find_vacant_castle(*leader_keep);
if (tmp_location == map_location::null_location)
continue;
leader_fit = leader_keep;
if ( can_recruit_on(*leader_fit, recruit_location) ) {
leader_opt = leader_fit;
if (resources::units->count(recruit_location) == 1)
recruit_location = tmp_location;
recruited_from = leader_opt->get_location();
break;
} else {
alternate_location = tmp_location;
alternate_from = leader_fit->get_location();
}
}
if (leader == u_end) {
LOG_NG << "No Leader on side " << side << " when recruiting " << unit_type << '\n';
return _("You dont have a leader to recruit with.");
}
if (leader_keep == u_end) {
LOG_NG << "Leader not on start: leader is on " << leader->get_location() << '\n';
return _("You must have a leader on a keep who is able to recruit the unit.");
}
if (leader_fit == u_end) {
LOG_NG << "No vacant castle tiles on a keep available to recruit the unit " << unit_type << " at " << recruit_location << '\n';
return _("There are no vacant castle tiles in which to recruit the unit.");
}
if (leader_opt == u_end) {
recruit_location = alternate_location;
recruited_from = alternate_from;
}
return std::string();
}
void place_recruit(const unit &u, const map_location &recruit_location, const map_location& recruited_from,
bool is_recall, bool show, bool fire_event, bool full_movement,
bool wml_triggered)
{
LOG_NG << "placing new unit on location " << recruit_location << "\n";
assert(resources::units->count(recruit_location) == 0);
unit new_unit = u;
if (full_movement) {
new_unit.set_movement(new_unit.total_movement());
} else {
new_unit.set_movement(0);
new_unit.set_attacks(0);
}
new_unit.heal_all();
new_unit.set_hidden(true);
resources::units->add(recruit_location, new_unit);
if (is_recall)
{
if (fire_event) {
LOG_NG << "firing prerecall event\n";
game_events::fire("prerecall",recruit_location, recruited_from);
}
}
else
{
LOG_NG << "firing prerecruit event\n";
game_events::fire("prerecruit",recruit_location, recruited_from);
}
const unit_map::iterator new_unit_itor = resources::units->find(recruit_location);
if (new_unit_itor.valid()) {
new_unit_itor->set_hidden(false);
if (resources::game_map->is_village(recruit_location)) {
get_village(recruit_location,new_unit_itor->side());
}
preferences::encountered_units().insert(new_unit_itor->type_id());
}
unit_map::iterator leader = resources::units->begin();
for(; leader != resources::units->end(); ++leader)
if (leader->can_recruit() &&
leader->side() == new_unit.side() &&
can_recruit_on(*leader, recruit_location))
break;
if (show) {
if (leader.valid()) {
unit_display::unit_recruited(recruit_location, leader->get_location());
} else {
unit_display::unit_recruited(recruit_location);
}
}
if (is_recall)
{
if (fire_event) {
LOG_NG << "firing recall event\n";
game_events::fire("recall", recruit_location, recruited_from);
}
}
else
{
LOG_NG << "firing recruit event\n";
game_events::fire("recruit",recruit_location, recruited_from);
}
const std::string checksum = get_checksum(new_unit);
const config* ran_results = get_random_results();
if(ran_results != NULL) {
// When recalling from WML there should be no random results, if we use
// random we might get the replay out of sync.
assert(!wml_triggered);
const std::string rc = (*ran_results)["checksum"];
if(rc != checksum) {
std::stringstream error_msg;
error_msg << "SYNC: In recruit " << new_unit.type_id() <<
": has checksum " << checksum <<
" while datasource has checksum " <<
rc << "\n";
ERR_NG << error_msg.str();
config cfg_unit1;
new_unit.write(cfg_unit1);
DBG_NG << cfg_unit1;
replay::process_error(error_msg.str());
}
} else if(wml_triggered == false) {
config cfg;
cfg["checksum"] = checksum;
set_random_results(cfg);
}
resources::whiteboard->on_gamestate_change();
}

134
src/actions/create.hpp Normal file
View file

@ -0,0 +1,134 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Various functions related to the creation of units (recruits, recalls,
* and placed units).
*/
#ifndef ACTIONS_CREATE_H_INCLUDED
#define ACTIONS_CREATE_H_INCLUDED
class config;
class team;
class vconfig;
#include "../map_location.hpp"
#include "../unit.hpp"
class unit_creator {
public:
unit_creator(team &tm, const map_location &start_pos);
unit_creator& allow_show(bool b);
unit_creator& allow_get_village(bool b);
unit_creator& allow_rename_side(bool b);
unit_creator& allow_invalidate(bool b);
unit_creator& allow_discover(bool b);
unit_creator& allow_add_to_recall(bool b);
/**
* finds a suitable location for unit
* @retval map_location::null_location if unit is to be put into recall list
* @retval valid on-board map location otherwise
*/
map_location find_location(const config &cfg, const unit* pass_check=NULL);
/**
* adds a unit on map without firing any events (so, usable during team construction in gamestatus)
*/
void add_unit(const config &cfg, const vconfig* vcfg = NULL);
private:
void post_create(const map_location &loc, const unit &new_unit, bool anim);
bool add_to_recall_;
bool discover_;
bool get_village_;
bool invalidate_;
bool rename_side_;
bool show_;
const map_location start_pos_;
team &team_;
};
/// Checks to see if a leader at @a leader_loc could recruit on @a recruit_loc.
bool can_recruit_on(const map_location& leader_loc, const map_location& recruit_loc, int side);
/// Checks to see if @a leader (assumed a leader) can recruit on @a recruit_loc.
/// This takes into account terrain, shroud, and whether or not there is already
/// a visible unit at recruit_loc.
inline bool can_recruit_on(const unit& leader, const map_location& recruit_loc)
{ return can_recruit_on(leader.get_location(), recruit_loc, leader.side()); }
/**
* Finds a location to place a unit.
* A leader of the @a side must be on a keep
* connected by castle to a legal recruiting location. Otherwise, an error
* message explaining this is returned.
*
* If no errors are encountered, the location where a unit can be recruited
* is stored in @a recruit_location. Its value is considered first, if it is a
* legal option.
*
* @return an empty string on success. Otherwise a human-readable message
* describing the failure is returned.
*/
std::string find_recruit_location(const int side, map_location &recruit_location, map_location& recruited_from, const std::string& unit_type);
/**
* Finds a location to recall @a unit_recall.
* A leader of the @a side must be on a keep
* connected by castle to a legal recalling location. Otherwise, an error
* message explaining this is returned.
*
* If no errors are encountered, the location where a unit can be recalled
* is stored in @a recall_location. Its value is considered first, if it is a
* legal option.
*
* @return an empty string on success. Otherwise a human-readable message
* describing the failure is returned.
*/
std::string find_recall_location(const int side, map_location& recall_location, map_location& recall_from, const unit &unit_recall);
/**
* Get's the recruitable units from a side's leaders' personal recruit lists who can recruit on a specific hex field.
* @param side of the leaders to search for their personal recruit lists.
* @param recruit_location the hex field being part of the castle the player wants to recruit on.
* @return a set of units that can be recruited by leaders on a keep connected by castle tiles with @a recruit_location.
*/
const std::set<std::string> get_recruits_for_location(int side, const map_location &recruit_location);
/**
* Get's the recruitable units from a side's leaders' personal recruit lists who can recruit on a specific hex field.
* If no leader is able to recruit on the given location the full recall list of the side is returned.
* @param side of the leaders to search for their personal recruit lists.
* @param recruit_location the hex field being part of the castle the player wants to recruit on.
* @return a set of units that can be recruited by @a side on @a recall_loc or the full recall list of @a side.
*/
const std::vector<const unit*> get_recalls_for_location(int side, const map_location &recruit_location);
/**
* Place a unit into the game.
* The unit will be placed on @a recruit_location, which should be retrieved
* through a call to recruit_location().
*/
void place_recruit(const unit &u, const map_location &recruit_location, const map_location& recruited_from,
bool is_recall, bool show = false, bool fire_event = true, bool full_movement = false,
bool wml_triggered = false);
#endif

265
src/actions/heal.cpp Normal file
View file

@ -0,0 +1,265 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Healing (at start of side turn).
*/
#include "heal.hpp"
#include "../game_display.hpp"
#include "../log.hpp"
#include "../map.hpp"
#include "../replay.hpp"
#include "../resources.hpp"
#include "../team.hpp"
#include "../unit.hpp"
#include "../unit_abilities.hpp"
#include "../unit_display.hpp"
#include "../unit_map.hpp"
#include <boost/foreach.hpp>
#include <list>
#include <vector>
static lg::log_domain log_engine("engine");
#define DBG_NG LOG_STREAM(debug, log_engine)
// Simple algorithm: no maximum number of patients per healer.
void reset_resting(unit_map& units, int side)
{
BOOST_FOREACH(unit &u, units) {
if (u.side() == side)
u.set_resting(true);
}
}
/* Contains all the data used to display healing */
struct unit_healing_struct {
unit *healed;
std::vector<unit *> healers;
int healing;
};
void calculate_healing(int side, bool update_display)
{
DBG_NG << "beginning of healing calculations\n";
unit_map &units = *resources::units;
std::list<unit_healing_struct> l;
// We look for all allied units, then we see if our healer is near them.
BOOST_FOREACH(unit &u, units) {
if (u.get_state("unhealable") || u.incapacitated())
continue;
DBG_NG << "found healable unit at (" << u.get_location() << ")\n";
unit_map::iterator curer = units.end();
std::vector<unit *> healers;
int healing = 0;
int rest_healing = 0;
std::string curing;
unit_ability_list heal = u.get_abilities("heals");
const bool is_poisoned = u.get_state(unit::STATE_POISONED);
if(is_poisoned) {
// Remove the enemies' healers to determine if poison is slowed or cured
for (std::vector<std::pair<const config *, map_location> >::iterator
h_it = heal.cfgs.begin(); h_it != heal.cfgs.end();) {
unit_map::iterator potential_healer = units.find(h_it->second);
assert(potential_healer != units.end());
if ((*resources::teams)[potential_healer->side() - 1].is_enemy(side)) {
h_it = heal.cfgs.erase(h_it);
} else {
++h_it;
}
}
for (std::vector<std::pair<const config *, map_location> >::const_iterator
heal_it = heal.cfgs.begin(); heal_it != heal.cfgs.end(); ++heal_it) {
if((*heal_it->first)["poison"] == "cured") {
curer = units.find(heal_it->second);
// Full curing only occurs on the healer turn (may be changed)
if(curer->side() == side) {
curing = "cured";
} else if(curing != "cured") {
curing = "slowed";
}
} else if(curing != "cured" && (*heal_it->first)["poison"] == "slowed") {
curer = units.find(heal_it->second);
curing = "slowed";
}
}
}
// For heal amounts, only consider healers on side which is starting now.
// Remove all healers not on this side.
for (std::vector<std::pair<const config *, map_location> >::iterator h_it =
heal.cfgs.begin(); h_it != heal.cfgs.end();) {
unit_map::iterator potential_healer = units.find(h_it->second);
assert(potential_healer != units.end());
if(potential_healer->side() != side) {
h_it = heal.cfgs.erase(h_it);
} else {
++h_it;
}
}
unit_abilities::effect heal_effect(heal,0,false);
healing = heal_effect.get_composite_value();
for(std::vector<unit_abilities::individual_effect>::const_iterator heal_loc = heal_effect.begin(); heal_loc != heal_effect.end(); ++heal_loc) {
healers.push_back(&*units.find(heal_loc->loc));
}
if (!healers.empty()) {
DBG_NG << "Unit has " << healers.size() << " potential healers\n";
}
if (u.side() == side) {
unit_ability_list regen = u.get_abilities("regenerate");
unit_abilities::effect regen_effect(regen,0,false);
if(regen_effect.get_composite_value() > healing) {
healing = regen_effect.get_composite_value();
healers.clear();
}
if(!regen.cfgs.empty()) {
for (std::vector<std::pair<const config *, map_location> >::const_iterator regen_it = regen.cfgs.begin(); regen_it != regen.cfgs.end(); ++regen_it) {
if((*regen_it->first)["poison"] == "cured") {
curer = units.end();
curing = "cured";
} else if(curing != "cured" && (*regen_it->first)["poison"] == "slowed") {
curer = units.end();
curing = "slowed";
}
}
}
if (int h = resources::game_map->gives_healing(u.get_location())) {
if (h > healing) {
healing = h;
healers.clear();
}
/** @todo FIXME */
curing = "cured";
curer = units.end();
}
if (u.resting() || u.is_healthy()) {
rest_healing = game_config::rest_heal_amount;
healing += rest_healing;
}
}
if(is_poisoned) {
if(curing == "cured") {
u.set_state(unit::STATE_POISONED, false);
healing = rest_healing;
healers.clear();
if (curer != units.end())
healers.push_back(&*curer);
} else if(curing == "slowed") {
healing = rest_healing;
healers.clear();
if (curer != units.end())
healers.push_back(&*curer);
} else {
healers.clear();
healing = rest_healing;
if (u.side() == side) {
healing -= game_config::poison_amount;
}
}
}
if (curing == "" && healing==0) {
continue;
}
int pos_max = u.max_hitpoints() - u.hitpoints();
int neg_max = -(u.hitpoints() - 1);
if(healing > 0 && pos_max <= 0) {
// Do not try to "heal" if HP >= max HP
continue;
}
if(healing > pos_max) {
healing = pos_max;
} else if(healing < neg_max) {
healing = neg_max;
}
if (!healers.empty()) {
DBG_NG << "Just before healing animations, unit has " << healers.size() << " potential healers\n";
}
if (!recorder.is_skipping() && update_display &&
!(u.invisible(u.get_location()) &&
(*resources::teams)[resources::screen->viewing_team()].is_enemy(side)))
{
unit_healing_struct uhs = { &u, healers, healing };
l.push_front(uhs);
}
if (healing > 0)
u.heal(healing);
else if (healing < 0)
u.take_hit(-healing);
resources::screen->invalidate_unit();
}
// Display healing with nearest first algorithm.
if (!l.empty()) {
// The first unit to be healed is chosen arbitrarily.
unit_healing_struct uhs = l.front();
l.pop_front();
unit_display::unit_healing(*uhs.healed, uhs.healed->get_location(),
uhs.healers, uhs.healing);
/* next unit to be healed is nearest from uhs left in list l */
while (!l.empty()) {
std::list<unit_healing_struct>::iterator nearest;
int min_d = INT_MAX;
/* for each unit in l, remember nearest */
for (std::list<unit_healing_struct>::iterator i =
l.begin(), i_end = l.end(); i != i_end; ++i)
{
int d = distance_between(uhs.healed->get_location(), i->healed->get_location());
if (d < min_d) {
min_d = d;
nearest = i;
}
}
uhs = *nearest;
l.erase(nearest);
unit_display::unit_healing(*uhs.healed, uhs.healed->get_location(),
uhs.healers, uhs.healing);
}
}
DBG_NG << "end of healing calculations\n";
}

38
src/actions/heal.hpp Normal file
View file

@ -0,0 +1,38 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Various functions that implement healing of units (when a side turn starts).
*/
#ifndef ACTIONS_HEAL_H_INCLUDED
#define ACTIONS_HEAL_H_INCLUDED
class unit_map;
/**
* Resets resting for all units on this side: should be called after calculate_healing().
* @todo FIXME: Try moving this to unit::new_turn, then move it above calculate_healing().
*/
void reset_resting(unit_map& units, 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(int side, bool update_display);
#endif

1260
src/actions/move.cpp Normal file

File diff suppressed because it is too large Load diff

113
src/actions/move.hpp Normal file
View file

@ -0,0 +1,113 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Various functions related to moving units.
*/
#ifndef ACTIONS_MOVE_H_INCLUDED
#define ACTIONS_MOVE_H_INCLUDED
struct map_location;
class replay;
class unit;
#include "undo.hpp"
#include "../unit_map.hpp"
#include <vector>
class move_unit_spectator {
public:
/** add a location of a seen friend */
void add_seen_friend(const unit_map::const_iterator &u);
/** add the location of new seen enemy */
void add_seen_enemy(const unit_map::const_iterator &u);
/** get the location of an ambusher */
const unit_map::const_iterator& get_ambusher() const;
/** get the location of a failed teleport */
const unit_map::const_iterator& get_failed_teleport() const;
/** get the locations of seen enemies */
const std::vector<unit_map::const_iterator>& get_seen_enemies() const;
/** get the locations of seen friends */
const std::vector<unit_map::const_iterator>& get_seen_friends() const;
/** get new location of moved unit */
const unit_map::const_iterator& get_unit() const;
/** constructor */
move_unit_spectator(const unit_map &units);
/** destructor */
virtual ~move_unit_spectator();
/** reset all locations to empty values*/
void reset(const unit_map &units);
/** set the location of an ambusher */
void set_ambusher(const unit_map::const_iterator &u);
/** set the location of a failed teleport */
void set_failed_teleport(const unit_map::const_iterator &u);
/** set the iterator to moved unit*/
void set_unit(const unit_map::const_iterator &u);
private:
unit_map::const_iterator ambusher_;
unit_map::const_iterator failed_teleport_;
std::vector<unit_map::const_iterator> seen_enemies_;
std::vector<unit_map::const_iterator> seen_friends_;
unit_map::const_iterator unit_;
};
/**
* Makes it so the village at the given location is owned by the given side.
* Returns true if getting the village triggered a mutating event.
*/
bool get_village(const map_location& loc, int side, int *time_bonus = NULL);
/// Moves a unit across the board.
size_t move_unit(const std::vector<map_location> &steps,
replay* move_recorder, undo_list* undo_stack,
bool continued_move = false, bool show_move = true,
bool* interrupted = NULL,
move_unit_spectator* move_spectator = NULL,
const map_location* replay_dest = NULL);
/**
* Will return true iff the unit @a u has any possible moves
* it can do (including attacking etc).
*/
bool unit_can_move(const unit &u);
#endif

130
src/actions/undo.cpp Normal file
View file

@ -0,0 +1,130 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Undoing, redoing.
*/
#include "undo.hpp"
#include "vision.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../log.hpp"
#include "../resources.hpp"
#include "../team.hpp"
#include "../unit_map.hpp"
static lg::log_domain log_engine("engine");
#define LOG_NG LOG_STREAM(info, log_engine)
void apply_shroud_changes(undo_list &undos, int side)
{
team &tm = (*resources::teams)[side - 1];
// No need to do this if the team isn't using fog or shroud.
if (!tm.uses_shroud() && !tm.uses_fog())
return;
game_display &disp = *resources::screen;
unit_map &units = *resources::units;
/*
This function works thusly:
1. run through the list of undo_actions
2. for each one, play back the unit's move
3. for each location along the route, clear any "shrouded" hexes that the unit can see
and record sighted events
4. render shroud/fog cleared.
5. pump all events
6. call clear_shroud to update the fog of war for each unit
7. fix up associated display stuff (done in a similar way to turn_info::undo())
*/
bool cleared_shroud = false; // for further optimization
bool sighted_event = false;
for(undo_list::iterator un = undos.begin(); un != undos.end(); ++un) {
LOG_NG << "Turning an undo...\n";
//NOTE: for the moment shroud cleared during recall seems never delayed
//Shroud update during recall can be delayed, during recruit as well
//if we have a non-random recruit (e.g. undead)
//if(un->is_recall() || un->is_recruit()) continue;
// Make a temporary unit move in map and hide the original
const unit_map::const_unit_iterator unit_itor = units.find(un->affected_unit.underlying_id());
// check if the unit is still existing (maybe killed by an event)
// FIXME: A wml-killed unit will not update the shroud explored before its death
if(unit_itor == units.end())
continue;
// Cache the unit's current actual location for raising the sighted events.
const map_location actual_location = unit_itor->get_location();
std::vector<map_location> route(un->route.begin(), un->route.end());
if ( un->recall_loc.valid() )
route.push_back(un->recall_loc);
std::vector<map_location>::const_iterator step;
for(step = route.begin(); step != route.end(); ++step) {
// Clear the shroud, and collect new seen_units
std::set<map_location> seen_units;
std::set<map_location> petrified_units;
std::map<map_location, int> jamming_map;
calculate_jamming(side, jamming_map);
cleared_shroud |= clear_shroud_unit(*step, *unit_itor, tm, jamming_map,
NULL, &seen_units, &petrified_units);
// Fire sighted events
// Try to keep same order (petrified units after normal units)
// as with move_unit for replay
for (std::set<map_location>::iterator sight_it = seen_units.begin();
sight_it != seen_units.end(); ++sight_it)
{
unit_map::const_iterator new_unit = units.find(*sight_it);
assert(new_unit != units.end());
game_events::raise("sighted", *sight_it, actual_location);
sighted_event = true;
}
for (std::set<map_location>::iterator sight_it = petrified_units.begin();
sight_it != petrified_units.end(); ++sight_it)
{
unit_map::const_iterator new_unit = units.find(*sight_it);
assert(new_unit != units.end());
game_events::raise("sighted", *sight_it, actual_location);
sighted_event = true;
}
}
}
// Optimization: if nothing was cleared and there are no sighted events,
// then there is nothing to redraw. (Technically, "nothing was cleared"
// implies "no sighted events", but checking both is cheap.)
if ( cleared_shroud || sighted_event ) {
// Update the display before pumping events.
invalidate_after_clearing_shroud();
disp.draw();
if ( sighted_event && game_events::pump() ) {
// Updates in case WML changed stuff.
disp.invalidate_unit();
clear_shroud(side);
disp.draw();
}
}
}

85
src/actions/undo.hpp Normal file
View file

@ -0,0 +1,85 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Various functions that implement the undoing (and redoing) of in-game commands.
*/
#ifndef ACTIONS_UNDO_H_INCLUDED
#define ACTIONS_UNDO_H_INCLUDED
#include "../map_location.hpp"
#include "../unit.hpp"
#include <vector>
/** Records information to be able to undo a movement. */
struct undo_action {
enum ACTION_TYPE { NONE, RECRUIT, RECALL, DISMISS };
undo_action(const unit& u,
const std::vector<map_location>::const_iterator & begin,
const std::vector<map_location>::const_iterator & end,
int sm, int timebonus=0, int orig=-1,
const map_location::DIRECTION dir=map_location::NDIRECTIONS) :
route(begin, end),
starting_moves(sm),
original_village_owner(orig),
recall_loc(),
recall_from(),
type(NONE),
affected_unit(u),
countdown_time_bonus(timebonus),
starting_dir(dir == map_location::NDIRECTIONS ? u.facing() : dir)
{
}
undo_action(const unit& u, const map_location& loc, const map_location& from,
const ACTION_TYPE action_type=NONE) :
route(),
starting_moves(),
original_village_owner(),
recall_loc(loc),
recall_from(from),
type(action_type),
affected_unit(u),
countdown_time_bonus(1),
starting_dir(u.facing())
{}
std::vector<map_location> route;
int starting_moves;
int original_village_owner;
map_location recall_loc;
map_location recall_from;
ACTION_TYPE type;
unit affected_unit;
int countdown_time_bonus;
map_location::DIRECTION starting_dir;
bool is_dismiss() const { return type == DISMISS; }
bool is_recall() const { return type == RECALL; }
bool is_recruit() const { return type == RECRUIT; }
};
typedef std::vector<undo_action> undo_list;
/**
* Function to apply pending shroud changes in the undo stack.
*/
void apply_shroud_changes(undo_list &undos, int side);
#endif

316
src/actions/vision.cpp Normal file
View file

@ -0,0 +1,316 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Sighting.
*/
#include "vision.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../map.hpp"
#include "../map_label.hpp"
#include "../map_location.hpp"
#include "../pathfind/pathfind.hpp"
#include "../resources.hpp"
#include "../team.hpp"
#include "../unit.hpp"
#include <boost/foreach.hpp>
namespace {
/**
* Clears shroud from a single location.
*
* In a few cases, this will also clear corner hexes that otherwise would
* not normally get cleared.
* @param tm The team whose fog/shroud is affected.
* @param loc The location to clear.
* @param viewer The unit doing the viewing.
* @param seen_units If the location was cleared and contained a visible,
* non-petrified unit, it gets added to this set.
* @param petrified_units If the location was cleared and contained a visible,
* petrified unit, it gets added to this set.
* @param known_units These locations are excluded from being added to
* seen_units and petrified_units.
*
* @return whether or not information was uncovered (i.e. returns true if
* the specified location was fogged/ shrouded under shared vision/maps).
*/
bool clear_shroud_loc(team &tm, const map_location& loc, const unit & viewer,
std::set<map_location>* seen_units = NULL,
std::set<map_location>* petrified_units = NULL,
const std::set<map_location>* known_units = NULL)
{
gamemap &map = *resources::game_map;
// This counts as clearing a tile for the return value if it is on the
// board and currently fogged under shared vision. (No need to explicitly
// check for shrouded since shrouded implies fogged.)
bool was_fogged = tm.fogged(loc);
bool result = was_fogged && map.on_board(loc);
// Clear the border as well as the board, so that the half-hexes
// at the edge can also be cleared of fog/shroud.
if ( map.on_board_with_border(loc)) {
// Both functions should be executed so don't use || which
// uses short-cut evaluation.
// (This is different than the return value because shared vision does
// not apply here.)
if ( tm.clear_shroud(loc) | tm.clear_fog(loc) ) {
// If we are near a corner, the corner might also need to be cleared.
// This happens at the lower-left corner and at either the upper- or
// lower- right corner (depending on the width).
// Lower-left corner:
if ( loc.x == 0 && loc.y == map.h()-1 ) {
const map_location corner(-1, map.h());
tm.clear_shroud(corner);
tm.clear_fog(corner);
}
// Lower-right corner, odd width:
else if ( is_odd(map.w()) && loc.x == map.w()-1 && loc.y == map.h()-1 ) {
const map_location corner(map.w(), map.h());
tm.clear_shroud(corner);
tm.clear_fog(corner);
}
// Upper-right corner, even width:
else if ( is_even(map.w()) && loc.x == map.w()-1 && loc.y == 0) {
const map_location corner(map.w(), -1);
tm.clear_shroud(corner);
tm.clear_fog(corner);
}
}
}
// Possible screen invalidation.
if ( was_fogged ) {
resources::screen->invalidate(loc);
// Need to also invalidate adjacent hexes to get rid of the
// "fog edge" graphics.
map_location adjacent[6];
get_adjacent_tiles(loc, adjacent);
for ( int i = 0; i != 6; ++i )
resources::screen->invalidate(adjacent[i]);
}
// Does the caller want a list of discovered units?
if ( result && (seen_units || petrified_units) ) {
// Allow known_units to override fogged().
if ( loc != viewer.get_location() &&
(known_units == NULL || known_units->count(loc) == 0) )
{
// Is there a visible unit here?
const unit_map::const_iterator sighted = resources::units->find(loc);
if ( sighted.valid() ) {
if ( !tm.is_enemy(sighted->side()) ||
!sighted->invisible(loc) )
{
// Add this unit to the appropriate list.
if ( !sighted->get_state(unit::STATE_PETRIFIED) )
{
if ( seen_units != NULL )
seen_units->insert(loc);
}
else if ( petrified_units != NULL )
petrified_units->insert(loc);
}
}
}
}
return result;
}
}
/**
* Clears shroud (and fog) around the provided location for @a view_team as
* if a unit with @a viewer's sight range was standing there.
* (This uses a team parameter instead of a side since it is assumed that
* the caller already checked for fog or shroud being in use. Hence the
* caller has the team readily available.)
*
* @a seen_units will return new units that have been seen by this unit.
*
* @return whether or not information was uncovered (i.e. returns true if any
* locations in visual range were fogged/shrouded under shared vision/maps).
*/
bool clear_shroud_unit(const map_location &view_loc, const unit &viewer,
team &view_team, const std::map<map_location, int>& jamming_map,
const std::set<map_location>* known_units,
std::set<map_location>* seen_units,
std::set<map_location>* petrified_units)
{
bool cleared_something = false;
// Clear the fog.
pathfind::vision_path sight(*resources::game_map, viewer, view_loc, jamming_map);
BOOST_FOREACH(const pathfind::paths::step &dest, sight.destinations) {
if ( clear_shroud_loc(view_team, dest.curr, viewer, seen_units,
petrified_units, known_units) )
cleared_something = true;
}
//TODO guard with game_config option
BOOST_FOREACH(const map_location &dest, sight.edges) {
if ( clear_shroud_loc(view_team, dest, viewer, seen_units,
petrified_units, known_units) )
cleared_something = true;
}
return cleared_something;
}
/**
* Wrapper for the invalidations that should occur after fog or
* shroud is cleared. (Needed in multiple places, so this makes
* sure the same things are called each time.) This would be
* called after one is done calling clear_shroud_unit().
*/
void invalidate_after_clearing_shroud()
{
resources::screen->invalidate_game_status();
resources::screen->recalculate_minimap();
resources::screen->labels().recalculate_shroud();
// The tiles are invalidated as they are cleared, so no need
// to invalidate them here.
}
void calculate_jamming(int side, std::map<map_location, int>& jamming_map)
{
team& viewer_tm = (*resources::teams)[side - 1];
BOOST_FOREACH(const unit &u, *resources::units)
{
if (!viewer_tm.is_enemy(u.side())) continue;
if (u.jamming() < 1) continue;
int current = jamming_map[u.get_location()];
if (current < u.jamming()) jamming_map[u.get_location()] = u.jamming();
pathfind::jamming_path jamming(*resources::game_map, u, u.get_location());
BOOST_FOREACH(const pathfind::paths::step& st, jamming.destinations) {
current = jamming_map[st.curr];
if (current < st.move_left)
jamming_map[st.curr] = st.move_left;
}
}
}
/**
* Function that recalculates the fog of war.
*
* This is used at the end of a turn and for the defender at the end of
* combat. As a back-up, it is also called when clearing shroud at the
* beginning of a turn.
* This function does nothing if the indicated side does not use fog.
* The display is invalidated as needed.
*
* @param[in] side The side whose fog will be recalculated.
*/
void recalculate_fog(int side)
{
team &tm = (*resources::teams)[side - 1];
if (!tm.uses_fog())
return;
// The following lines will be useful at some point, but not yet.
// So they are commented out for now.
//std::set<map_location> visible_locs;
//// Loop through all units, looking for those that are visible.
//BOOST_FOREACH(const unit &u, *resources::units) {
// const map_location & u_location = u.get_location();
//
// if ( !tm.fogged(u_location) )
// visible_locs.insert(u_location);
//}
tm.refog();
// Invalidate the screen before clearing the shroud.
// This speeds up the invalidations within clear_shroud_unit().
resources::screen->invalidate_all();
std::map<map_location, int> jamming_map;
calculate_jamming(side, jamming_map);
BOOST_FOREACH(const unit &u, *resources::units)
{
if (u.side() == side) {
clear_shroud_unit(u.get_location(), u, tm, jamming_map);
}
}
//FIXME: This pump don't catch any sighted events (they are not fired by
// clear_shroud_unit) and if it caches another old event, maybe the caller
// don't want to pump it here
game_events::pump();
// Update the screen.
invalidate_after_clearing_shroud();
}
/**
* Function that will clear shroud (and fog) based on current unit positions.
*
* This will not re-fog hexes unless reset_fog is set to true.
* This function will do nothing if the side uses neither shroud nor fog.
* The display is invalidated as needed.
*
* @param[in] side The side whose shroud (and fog) will be cleared.
* @param[in] reset_fog If set to true, the fog will also be recalculated
* (refogging hexes that can no longer be seen).
* @returns true if some shroud/fog is actually cleared away.
*/
bool clear_shroud(int side, bool reset_fog)
{
team &tm = (*resources::teams)[side - 1];
if (!tm.uses_shroud() && !tm.uses_fog())
return false;
bool result = false;
std::map<map_location, int> jamming_map;
calculate_jamming(side, jamming_map);
BOOST_FOREACH(const unit &u, *resources::units)
{
if (u.side() == side) {
result |= clear_shroud_unit(u.get_location(), u, tm, jamming_map);
}
}
//FIXME: This pump don't catch any sighted events (they are not fired by
// clear_shroud_unit) and if it caches another old event, maybe the caller
// don't want to pump it here
game_events::pump();
if ( reset_fog ) {
// Note: This will not reveal any new tiles, so result is not affected.
// Note: This will call invalidate_after_clearing_shroud().
recalculate_fog(side);
}
else if ( result ) {
// Update the screen.
invalidate_after_clearing_shroud();
}
return result;
}

51
src/actions/vision.hpp Normal file
View file

@ -0,0 +1,51 @@
/*
Copyright (C) 2003 - 2012 by David White <dave@whitevine.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 as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Various functions implementing vision (through fog of war and shroud).
*/
#ifndef ACTIONS_VISION_H_INCLUDED
#define ACTIONS_VISION_H_INCLUDED
struct map_location;
class team;
class unit;
#include <map>
#include <set>
/// Clears shroud (and fog) around the provided location for @a view_team as
/// if a unit with @a viewer's sight range was standing there.
bool clear_shroud_unit(const map_location &view_loc, const unit &viewer,
team &view_team, const std::map<map_location, int>& jamming_map,
const std::set<map_location>* known_units = NULL,
std::set<map_location>* seen_units = NULL,
std::set<map_location>* petrified_units = NULL);
/// Wrapper for the invalidations that should occur after fog or
/// shroud is cleared.
void invalidate_after_clearing_shroud();
void calculate_jamming(int side, std::map<map_location, int>& jamming_map);
/// Function that recalculates the fog of war.
void recalculate_fog(int side);
/// Function that will clear shroud (and fog) based on current unit positions.
bool clear_shroud(int side, bool reset_fog=false);
#endif

View file

@ -36,7 +36,9 @@
#include "actions.hpp"
#include "manager.hpp"
#include "../actions.hpp"
#include "../actions/attack.hpp"
#include "../actions/create.hpp"
#include "../actions/move.hpp"
#include "../dialogs.hpp"
#include "../game_end_exceptions.hpp"
#include "../game_preferences.hpp"

View file

@ -23,12 +23,14 @@
#include "game_info.hpp"
#include "../actions.hpp"
#include "../actions/move.hpp"
namespace pathfind {
struct plain_route;
} // of namespace pathfind
class unit;
class unit_type;
class team;
class gamemap;

View file

@ -21,6 +21,7 @@
#include <boost/foreach.hpp>
#include <map>
#include "../../actions/attack.hpp"
#include "../../attack_prediction.hpp"
#include "../../resources.hpp"
#include "../../log.hpp"
@ -29,7 +30,6 @@
#include "../../unit_display.hpp"
#include "../../unit_map.hpp"
#include "../../unit_types.hpp"
#include "../actions.hpp"
#include "../composite/rca.hpp"
#include "../default/ai.hpp"

View file

@ -26,7 +26,7 @@
#include "property_handler.hpp"
#include "stage.hpp"
#include "../manager.hpp"
#include "../../actions.hpp"
#include "../../actions/attack.hpp"
#include "../../log.hpp"
#include <boost/bind.hpp>

View file

@ -29,9 +29,8 @@
#include "default/ai.hpp"
#include "../callable_objects.hpp"
#include "../actions/attack.hpp"
#include "../formula.hpp"
#include "../formula_callable.hpp"
#include "../formula_function.hpp"
#include "../formula_fwd.hpp"
#include "../game_display.hpp"

View file

@ -21,9 +21,9 @@
#include "../../global.hpp"
#include "ai.hpp"
#include "../actions.hpp"
#include "../manager.hpp"
#include "../../actions/attack.hpp"
#include "../../attack_prediction.hpp"
#include "../../game_config.hpp"
#include "../../log.hpp"

View file

@ -16,7 +16,7 @@
#define FORMULA_AI_CALLABLE_OBJECTS_HPP_INCLUDED
#include "../game_info.hpp"
#include "../../actions.hpp"
#include "../../actions/attack.hpp"
#include "../../callable_objects.hpp"
#include "../../formula.hpp"
#include "../../formula_callable.hpp"

View file

@ -31,8 +31,6 @@
#include "../../scripting/lua_api.hpp"
#include "lua_object.hpp" // (Nephro)
#include "../../actions.hpp"
#include "../../attack_prediction.hpp"
#include "../../filesystem.hpp"
#include "../../game_display.hpp"

View file

@ -21,7 +21,7 @@
#include "aspect_attacks.hpp"
#include "../manager.hpp"
#include "../../actions.hpp"
#include "../../actions/attack.hpp"
#include "../../log.hpp"
#include "../../map.hpp"
#include "../../team.hpp"

View file

@ -22,8 +22,6 @@
#include "../composite/ai.hpp"
#include "../actions.hpp"
#include "../../game_display.hpp"
#include "../../log.hpp"
#include "../../map.hpp"

View file

@ -27,7 +27,7 @@
#include "attack_prediction.hpp"
#include "actions.hpp"
#include "actions/attack.hpp"
#include "game_config.hpp"
// Compile with -O3 -DBENCHMARK for speed testing,

View file

@ -16,6 +16,7 @@
#include "attack_prediction_display.hpp"
#include "actions/attack.hpp"
#include "attack_prediction.hpp"
#include "gettext.hpp"
#include "game_display.hpp"

View file

@ -17,7 +17,11 @@
#ifndef ATTACK_PREDICTION_DISPLAY_H_INCLUDED
#define ATTACK_PREDICTION_DISPLAY_H_INCLUDED
#include "actions.hpp"
class attack_type;
class battle_context;
class battle_context_unit_stats;
struct map_location;
class unit;
#include "show_dialog.hpp"
// This preview pane is shown in the "Damage Calculations" dialog.

View file

@ -20,7 +20,7 @@
#include "global.hpp"
#include "actions.hpp"
#include "actions/attack.hpp"
#include "dialogs.hpp"
#include "game_events.hpp"
#include "game_display.hpp"

View file

@ -20,7 +20,9 @@
#include "global.hpp"
#include "actions.hpp"
#include "actions/create.hpp"
#include "actions/move.hpp"
#include "actions/vision.hpp"
#include "ai/manager.hpp"
#include "dialogs.hpp"
#include "game_display.hpp"

View file

@ -23,7 +23,7 @@
#include "gamestatus.hpp"
#include "actions.hpp"
#include "actions/create.hpp"
#include "filesystem.hpp"
#include "gettext.hpp"
#include "log.hpp"

View file

@ -16,7 +16,7 @@
#ifndef GUI_DIALOGS_UNIT_ATTACK_HPP_INCLUDED
#define GUI_DIALOGS_UNIT_ATTACK_HPP_INCLUDED
#include "actions.hpp"
#include "actions/attack.hpp"
#include "gui/dialogs/dialog.hpp"
#include "unit_map.hpp"

View file

@ -22,6 +22,9 @@
#include "global.hpp"
#include "actions/create.hpp"
#include "actions/move.hpp"
#include "actions/vision.hpp"
#include "builder.hpp"
#include "ai/manager.hpp"
#include "dialogs.hpp"

View file

@ -18,7 +18,7 @@
#include "mouse_events.hpp"
#include "actions.hpp"
#include "actions/move.hpp"
#include "attack_prediction_display.hpp"
#include "dialogs.hpp"
#include "game_end_exceptions.hpp"

View file

@ -20,6 +20,9 @@
*/
#include "play_controller.hpp"
#include "actions/create.hpp"
#include "actions/heal.hpp"
#include "actions/vision.hpp"
#include "dialogs.hpp"
#include "game_events.hpp"
#include "gettext.hpp"

View file

@ -17,7 +17,7 @@
#ifndef PLAY_CONTROLLER_H_INCLUDED
#define PLAY_CONTROLLER_H_INCLUDED
#include "actions.hpp"
#include "actions/undo.hpp"
#include "controller_base.hpp"
#include "game_end_exceptions.hpp"
#include "help.hpp"

View file

@ -22,7 +22,6 @@
#include "playsingle_controller.hpp"
#include "actions.hpp"
#include "ai/manager.hpp"
#include "ai/game_info.hpp"
#include "ai/testing.hpp"

View file

@ -22,6 +22,10 @@
#include "global.hpp"
#include "actions/attack.hpp"
#include "actions/create.hpp"
#include "actions/move.hpp"
#include "actions/vision.hpp"
#include "dialogs.hpp"
#include "game_display.hpp"
#include "game_end_exceptions.hpp"

View file

@ -16,6 +16,7 @@
#include "global.hpp"
#include "actions/vision.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
#include "gettext.hpp"

View file

@ -15,6 +15,7 @@
#include "global.hpp"
#include "actions/attack.hpp"
#include "attack_prediction.hpp"
#include "editor/editor_controller.hpp"
#include "editor/palette/terrain_palettes.hpp"

View file

@ -35,6 +35,7 @@
#include "scripting/lua.hpp"
#include "scripting/lua_api.hpp"
#include "actions/attack.hpp"
#include "ai/manager.hpp"
#include "ai/composite/component.hpp"
#include "ai/composite/engine_lua.hpp"

View file

@ -21,7 +21,6 @@
class config;
class config_writer;
class unit;
//#include "actions.hpp"
#include <string>
#include <map>
#include <vector>

View file

@ -20,6 +20,7 @@
#include "unit.hpp"
#include "actions/move.hpp"
#include "callable_objects.hpp"
#include "formula.hpp"
#include "game_display.hpp"

View file

@ -29,7 +29,7 @@
#include "side_actions.hpp"
#include "utility.hpp"
#include "actions.hpp"
#include "actions/create.hpp"
#include "arrow.hpp"
#include "chat_events.hpp"
#include "formula_string_utils.hpp"

View file

@ -34,7 +34,7 @@
#include "highlighter.hpp"
#include "utility.hpp"
#include "actions.hpp"
#include "actions/create.hpp"
#include "game_display.hpp"
#include "game_end_exceptions.hpp"
#include "map.hpp"

View file

@ -26,7 +26,7 @@
#include "manager.hpp"
#include "side_actions.hpp"
#include "actions.hpp"
#include "actions/create.hpp"
#include "game_display.hpp"
#include "map.hpp"
#include "play_controller.hpp"