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:
parent
d7e8b4c364
commit
724618c287
49 changed files with 4689 additions and 4249 deletions
|
@ -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" />
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
3775
src/actions.cpp
3775
src/actions.cpp
File diff suppressed because it is too large
Load diff
442
src/actions.hpp
442
src/actions.hpp
|
@ -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
1376
src/actions/attack.cpp
Normal file
File diff suppressed because it is too large
Load diff
195
src/actions/attack.hpp
Normal file
195
src/actions/attack.hpp
Normal 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
646
src/actions/create.cpp
Normal 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 don’t 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 don’t 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
134
src/actions/create.hpp
Normal 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
265
src/actions/heal.cpp
Normal 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
38
src/actions/heal.hpp
Normal 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
1260
src/actions/move.cpp
Normal file
File diff suppressed because it is too large
Load diff
113
src/actions/move.hpp
Normal file
113
src/actions/move.hpp
Normal 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
130
src/actions/undo.cpp
Normal 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
85
src/actions/undo.hpp
Normal 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
316
src/actions/vision.cpp
Normal 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
51
src/actions/vision.hpp
Normal 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
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
|
||||
#include "../composite/ai.hpp"
|
||||
|
||||
#include "../actions.hpp"
|
||||
|
||||
#include "../../game_display.hpp"
|
||||
#include "../../log.hpp"
|
||||
#include "../../map.hpp"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "attack_prediction_display.hpp"
|
||||
|
||||
#include "actions/attack.hpp"
|
||||
#include "attack_prediction.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "game_display.hpp"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "gamestatus.hpp"
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "actions/create.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "log.hpp"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include "playsingle_controller.hpp"
|
||||
|
||||
#include "actions.hpp"
|
||||
#include "ai/manager.hpp"
|
||||
#include "ai/game_info.hpp"
|
||||
#include "ai/testing.hpp"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "global.hpp"
|
||||
|
||||
#include "actions/vision.hpp"
|
||||
#include "game_end_exceptions.hpp"
|
||||
#include "game_events.hpp"
|
||||
#include "gettext.hpp"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
class config;
|
||||
class config_writer;
|
||||
class unit;
|
||||
//#include "actions.hpp"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "unit.hpp"
|
||||
|
||||
#include "actions/move.hpp"
|
||||
#include "callable_objects.hpp"
|
||||
#include "formula.hpp"
|
||||
#include "game_display.hpp"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue