Support for vision= in [unit] and [unit_type].

If present it's used instead of the movement to calculate the sight of the unit.

Support for [vision_costs] in [movement_type].

If present it's used for the costs when calculationg the sight of the unit.
This commit is contained in:
Fabian Müller 2012-04-30 18:03:48 +00:00
parent 809c0e23ed
commit b6597782ee
8 changed files with 91 additions and 33 deletions

View File

@ -162,6 +162,8 @@ Version 1.11.0-svn:
* Fixed bug #19618: Problems with ending turn with impossible moves
* Fixed bug #19615: Can see part of opponents' planned moves in local multiplayer game
* WML engine:
* new key: [untit]/[unit_type] vision=<number>, decouples movement and sight range.
* new tag: [movement_type] [vision_costs], used for calculating sight range if present.
* new action tag: [clear_menu_item] id=...
* new key: [set_menu_item][command]delayed_variable_substitution=yes|no
* Removed support for the deprecated "colour=", "debug_border_colour=",

View File

@ -92,7 +92,8 @@ Version 1.11.0-svn:
* Fix wrong preferences path suffix (1.1 instead of 1.10) on Linux and other
platforms using XDG layout (no compiled-in preferences path override).
* Fixed bug #19503 : "maximum auto saves" setting now works correctly.
* Units can now have a new value ("vision") which is used for sight range instead of movement.
* Vision range can have its own costs now.
Version 1.10.0:
* Campaigns:

View File

@ -179,7 +179,7 @@ struct comp {
/**
* Creates a list of routes that a unit can traverse from the provided location.
* (This is called when creating pathfind::paths and descendent classes.)
* (This is called when creating pathfind::paths and descendant classes.)
*
* @param map[in] The gamemap to use (for identifying terrain).
* @param units[in] Currently unused.
@ -201,20 +201,21 @@ struct comp {
* @param see_all[in] Set to true to remove unit visibility from consideration.
* @param ignore_units[in] Set to true if units should never obstruct paths
* (implies ignoring ZoC as well).
* @param vision[in] Set if the move_costs or the vision_costs are used.
*/
static void find_routes(const gamemap& map, const unit& u, const map_location& loc,
int move_left, pathfind::paths::dest_vect &destinations,
std::set<map_location> *edges, const team &current_team,
bool force_ignore_zocs, bool allow_teleport, int turns_left,
const team &viewing_team,
bool see_all, bool ignore_units)
bool see_all, bool ignore_units, bool vision)
{
pathfind::teleport_map teleports;
if (allow_teleport) {
teleports = pathfind::get_teleport_locations(u, viewing_team, see_all, ignore_units);
}
const int total_movement = u.total_movement();
const int total_movement = move_left;
search_counter += 2;
if (search_counter == 0) search_counter = 2;
@ -265,7 +266,8 @@ static void find_routes(const gamemap& map, const unit& u, const map_location& l
// Thus, 'src-..-n-next' can't be shorter.
if (next_visited) continue;
const int move_cost = u.movement_cost(map[locs[i]]);
const int move_cost =
vision ? u.vision_cost(map[locs[i]]) : u.movement_cost(map[locs[i]]);
node t = node(n.movement_left, n.turns_left, n.curr, locs[i]);
if (t.movement_left < move_cost) {
@ -416,7 +418,7 @@ pathfind::paths::paths(gamemap const &map, unit_map const &/*units*/,
find_routes(map, u, u.get_location(), u.movement_left(), destinations, NULL,
teams[u.side()-1], force_ignore_zoc, allow_teleport,
additional_turns, viewing_team, see_all, ignore_units);
additional_turns, viewing_team, see_all, ignore_units, false);
}
/**
@ -437,16 +439,13 @@ pathfind::paths::~paths()
* @param viewer The unit doing the viewing.
* @param loc The location from which the viewing occurs
* (does not have to be the unit's location).
* @param full_move Usually this will be true, but if false, the unit's current
* movement will be used instead of its maximum moves.
*/
pathfind::vision_path::vision_path(gamemap const &map, const unit& viewer,
map_location const &loc, bool full_move)
map_location const &loc)
: paths(), edges()
{
const team & viewer_team = (*resources::teams)[viewer.side()-1];
const int sight_range = full_move ? viewer.total_movement() :
viewer.movement_left();
const int sight_range = viewer.vision();
// Finding routes: ignore ZoC, disallow teleports, zero turns left,
// (viewing team), see all, and ignore units.
@ -454,7 +453,7 @@ pathfind::vision_path::vision_path(gamemap const &map, const unit& viewer,
// not allowed and units are ignored. If something changes to make it
// significant, I might have incorrectly guessed the appropriate value.)
find_routes(map, viewer, loc, sight_range, destinations, &edges,
viewer_team, true, false, 0, viewer_team, true, true);
viewer_team, true, false, 0, viewer_team, true, true, true);
}
/// Default destructor

View File

@ -110,7 +110,7 @@ struct vision_path : public paths
{
/// Construct a list of seen hexes for a unit.
vision_path(gamemap const &map, const unit& viewer,
map_location const &loc, bool full_move=true);
map_location const &loc);
virtual ~vision_path();
/// The edges are the non-destination hexes bordering the destinations.

View File

@ -140,6 +140,8 @@ unit::unit(const unit& o):
movement_(o.movement_),
max_movement_(o.max_movement_),
movement_costs_(o.movement_costs_),
vision_(o.vision_),
vision_costs_(o.vision_costs_),
defense_mods_(o.defense_mods_),
hold_position_(o.hold_position_),
end_turn_(o.end_turn_),
@ -222,6 +224,8 @@ unit::unit(const config &cfg, bool use_traits, game_state* state, const vconfig*
movement_(0),
max_movement_(0),
movement_costs_(),
vision_(-1),
vision_costs_(),
defense_mods_(),
hold_position_(false),
end_turn_(false),
@ -421,6 +425,15 @@ unit::unit(const config &cfg, bool use_traits, game_state* state, const vconfig*
} while(++cfg_range.first != cfg_range.second);
}
//adjust the unit_type's vision costs if this config has its own defined
cfg_range = cfg.child_range("vision_costs");
if(cfg_range.first != cfg_range.second) {
config &target = cfg_.child_or_add("vision_costs");
do {
target.append(*cfg_range.first);
} while(++cfg_range.first != cfg_range.second);
}
//adjust the unit_type's resistance if this config has its own defined
cfg_range = cfg.child_range("resistance");
if(cfg_range.first != cfg_range.second) {
@ -512,7 +525,7 @@ unit::unit(const config &cfg, bool use_traits, game_state* state, const vconfig*
"side", "underlying_id", "overlays", "facing", "race",
"level", "undead_variation", "max_attacks",
"attacks_left", "alpha", "zoc", "flying", "cost",
"max_hitpoints", "max_moves", "max_experience",
"max_hitpoints", "max_moves", "vision", "max_experience",
"advances_to", "hitpoints", "goto_x", "goto_y", "moves",
"experience", "resting", "unrenamable", "alignment",
"canrecruit", "extra_recruit", "x", "y", "placement",
@ -582,6 +595,8 @@ unit::unit(const unit_type *t, int side, bool real_unit,
movement_(0),
max_movement_(0),
movement_costs_(),
vision_(-1),
vision_costs_(),
defense_mods_(),
hold_position_(false),
end_turn_(false),
@ -789,6 +804,7 @@ void unit::advance_to(const config &old_cfg, const unit_type *t,
// Clear modification-related caches
modification_descriptions_.clear();
movement_costs_.clear();
vision_costs_.clear();
defense_mods_.clear();
// Clear the stored config and replace it with the one from the unit type,
@ -848,6 +864,7 @@ void unit::advance_to(const config &old_cfg, const unit_type *t,
hit_points_ = t->hitpoints();
max_hit_points_ = t->hitpoints();
max_movement_ = t->movement();
vision_ = t->vision();
emit_zoc_ = t->has_zoc();
attacks_ = t->attacks();
unit_value_ = t->cost();
@ -1623,7 +1640,7 @@ void unit::write(config& cfg) const
//support for unit formulas in [ai] and unit-specific variables in [ai] [vars]
if ( has_formula() || has_loop_formula() || (formula_vars_ && formula_vars_->empty() == false) ) {
if ( has_formula() || has_loop_formula() || (formula_vars_ && formula_vars_->empty() == false) ) {
config &ai = cfg.add_child("ai");
@ -1641,18 +1658,18 @@ void unit::write(config& cfg) const
{
config &ai_vars = ai.add_child("vars");
std::string str;
for(game_logic::map_formula_callable::const_iterator i = formula_vars_->begin(); i != formula_vars_->end(); ++i)
{
i->second.serialize_to_string(str);
if (!str.empty())
{
std::string str;
for(game_logic::map_formula_callable::const_iterator i = formula_vars_->begin(); i != formula_vars_->end(); ++i)
{
i->second.serialize_to_string(str);
if (!str.empty())
{
ai_vars[i->first] = str;
str.clear();
}
}
}
}
str.clear();
}
}
}
}
cfg["gender"] = gender_string(gender_);
@ -1694,6 +1711,7 @@ void unit::write(config& cfg) const
cfg["moves"] = movement_;
cfg["max_moves"] = max_movement_;
cfg["vision"] = vision_;
cfg["resting"] = resting_;
@ -2136,7 +2154,23 @@ int unit::movement_cost(const t_translation::t_terrain terrain) const
{
assert(resources::game_map != NULL);
const int res = movement_cost_internal(movement_costs_,
cfg_, NULL, *resources::game_map, terrain);
cfg_.child("movement_costs"), NULL, *resources::game_map, terrain);
if (res == unit_movement_type::UNREACHABLE) {
return res;
} else if(get_state(STATE_SLOWED)) {
return res*2;
}
return res;
}
int unit::vision_cost(const t_translation::t_terrain terrain) const
{
if (cfg_.child_count("vision_costs") == 0) return movement_cost(terrain);
assert(resources::game_map != NULL);
const int res = movement_cost_internal(vision_costs_,
cfg_.child("vision_costs"), NULL, *resources::game_map, terrain);
if (res == unit_movement_type::UNREACHABLE) {
return res;
@ -2521,6 +2555,12 @@ void unit::add_modification(const std::string& type, const config& mod, bool no_
mod_mdr_merge(mv, ap, !effect["replace"].to_bool());
}
movement_costs_.clear();
} else if (apply_to == "vision_costs") {
config &mv = cfg_.child_or_add("vision_costs");
if (const config &ap = effect.child("vision_costs")) {
mod_mdr_merge(mv, ap, !effect["replace"].to_bool());
}
vision_costs_.clear();
} else if (apply_to == "defense") {
config &def = cfg_.child_or_add("defense");
if (const config &ap = effect.child("defense")) {
@ -3181,7 +3221,7 @@ std::string get_checksum(const unit& u) {
child.recursive_clear_value("name");
}
const std::string child_keys[] = {"advance_from", "defense", "movement_costs", "resistance", ""};
const std::string child_keys[] = {"advance_from", "defense", "movement_costs", "vision_costs" "resistance", ""};
for (int i = 0; !child_keys[i].empty(); ++i)
{

View File

@ -146,6 +146,7 @@ public:
bool incapacitated() const { return get_state(STATE_PETRIFIED); }
int total_movement() const { return max_movement_; }
int movement_left() const { return (movement_ == 0 || incapacitated()) ? 0 : movement_; }
int vision() const { return vision_ < 0 ? max_movement_ : vision_; }
void set_hold_position(bool value) { hold_position_ = value; }
bool hold_position() const { return hold_position_; }
void set_user_end_turn(bool value=true) { end_turn_ = value; }
@ -238,6 +239,7 @@ public:
bool is_fearless() const { return is_fearless_; }
bool is_healthy() const { return is_healthy_; }
int movement_cost(const t_translation::t_terrain terrain) const;
int vision_cost(const t_translation::t_terrain terrain) const;
int defense_modifier(t_translation::t_terrain terrain) const;
int resistance_against(const std::string& damage_name,bool attacker,const map_location& loc) const;
int resistance_against(const attack_type& damage_type,bool attacker,const map_location& loc) const
@ -415,6 +417,8 @@ private:
int movement_;
int max_movement_;
mutable std::map<t_translation::t_terrain, int> movement_costs_; // movement cost cache
int vision_;
mutable std::map<t_translation::t_terrain, int> vision_costs_; // view cost cache
mutable defense_cache defense_mods_; // defense modifiers cache
bool hold_position_;
bool end_turn_;

View File

@ -210,7 +210,7 @@ bool attack_type::apply_modification(const config& cfg,std::string* description)
if(description != NULL) {
int inc_acc = lexical_cast<int>(increase_accuracy);
// Help xgettext with a directive to recognise the string as a non C printf-like string
// Help xgettext with a directive to recognize the string as a non C printf-like string
// xgettext:no-c-format
desc << utils::signed_value(inc_acc) << _("% accuracy");
}
@ -280,6 +280,7 @@ bool attack_type::describe_modification(const config& cfg,std::string* descripti
unit_movement_type::unit_movement_type(const config& cfg, const unit_movement_type* parent) :
moveCosts_(),
visionCosts_(),
defenseMods_(),
parent_(parent),
cfg_()
@ -299,6 +300,9 @@ unit_movement_type::unit_movement_type(const config& cfg, const unit_movement_ty
if (const config &movement_costs = cfg.child("movement_costs"))
cfg_.add_child("movement_costs", movement_costs);
if (const config &vision_costs = cfg.child("vision_costs"))
cfg_.add_child("vision_costs", vision_costs);
if (const config &defense = cfg.child("defense"))
cfg_.add_child("defense", defense);
@ -306,7 +310,7 @@ unit_movement_type::unit_movement_type(const config& cfg, const unit_movement_ty
cfg_.add_child("resistance", resistance);
}
unit_movement_type::unit_movement_type(): moveCosts_(), defenseMods_(), parent_(NULL), cfg_()
unit_movement_type::unit_movement_type(): moveCosts_(), visionCosts_(), defenseMods_(), parent_(NULL), cfg_()
{}
std::string unit_movement_type::name() const
@ -413,7 +417,7 @@ int movement_cost_internal(std::map<t_translation::t_terrain, int>& move_costs,
bool result_found = false;
int res = impassable;
if (const config& movement_costs = cfg.child("movement_costs")) {
if (cfg) {
if (underlying.size() != 1) {
ERR_CF << "Terrain '" << terrain << "' has "
<< underlying.size() << " underlying names - 0 expected.\n";
@ -423,7 +427,7 @@ int movement_cost_internal(std::map<t_translation::t_terrain, int>& move_costs,
}
const std::string& id = map.get_terrain_info(underlying.front()).id();
if (const config::attribute_value *val = movement_costs.get(id)) {
if (const config::attribute_value *val = cfg.get(id)) {
res = *val;
result_found = true;
}
@ -563,6 +567,7 @@ unit_type::unit_type(const unit_type& o) :
hitpoints_(o.hitpoints_),
level_(o.level_),
movement_(o.movement_),
vision_(o.vision_),
max_attacks_(o.max_attacks_),
cost_(o.cost_),
usage_(o.usage_),
@ -610,6 +615,7 @@ unit_type::unit_type(config &cfg) :
hitpoints_(0),
level_(0),
movement_(0),
vision_(-1),
max_attacks_(0),
cost_(0),
usage_(),
@ -772,6 +778,7 @@ void unit_type::build_help_index(const movement_type_map &mv_types,
hitpoints_ = cfg["hitpoints"].to_int(1);
level_ = cfg["level"];
movement_ = cfg["movement"].to_int(1);
vision_ = cfg["vision"].to_int(-1);
max_attacks_ = cfg["attacks"].to_int(1);
cost_ = cfg["cost"].to_int(1);
usage_ = cfg_["usage"].str();

View File

@ -136,7 +136,9 @@ public:
std::string name() const;
int movement_cost(const gamemap &map, t_translation::t_terrain terrain) const
{ return movement_cost_internal(moveCosts_, cfg_, parent_, map, terrain); }
{ return movement_cost_internal(moveCosts_, cfg_.child("movement_costs"), parent_, map, terrain); }
int vision_cost(const gamemap &map, t_translation::t_terrain terrain) const
{ return movement_cost_internal(visionCosts_, cfg_.child("vision_costs"), parent_, map, terrain); }
int defense_modifier(const gamemap &map, t_translation::t_terrain terrain) const
{ return defense_modifier_internal(defenseMods_, cfg_, parent_, map, terrain); }
const defense_range &defense_range_modifier(const gamemap &map, t_translation::t_terrain terrain) const
@ -154,6 +156,7 @@ public:
const unit_movement_type* get_parent() const { return parent_; }
private:
mutable std::map<t_translation::t_terrain, int> moveCosts_;
mutable std::map<t_translation::t_terrain, int> visionCosts_;
mutable defense_cache defenseMods_;
const unit_movement_type* parent_;
@ -225,6 +228,7 @@ public:
int hitpoints() const { return hitpoints_; }
int level() const { return level_; }
int movement() const { return movement_; }
int vision() const { return vision_; }
int max_attacks() const { return max_attacks_; }
int cost() const { return cost_; }
const std::string& usage() const { return usage_; }
@ -302,6 +306,7 @@ private:
int hitpoints_;
int level_;
int movement_;
int vision_;
int max_attacks_;
int cost_;
std::string usage_;