Overhaul of unit_movement_type
This is a general overhaul of the class embodying movement types, featuring: * Better data encapsulation * Less duplication of code between unit.cpp and unit_type.cpp * Easier to use * New files for the class (VC and XCode projects still need updating) * New (shorter) name for the class Some additional revisions will be coming. The primary motivation for this was to get a class that embodies movement costs (part of the data encapsulation).
This commit is contained in:
parent
3d9a8e4213
commit
bb70a29017
18 changed files with 909 additions and 572 deletions
|
@ -550,6 +550,8 @@
|
|||
<Unit filename="../../src/mouse_events.hpp" />
|
||||
<Unit filename="../../src/mouse_handler_base.cpp" />
|
||||
<Unit filename="../../src/mouse_handler_base.hpp" />
|
||||
<Unit filename="../../src/movetype.cpp" />
|
||||
<Unit filename="../../src/movetype.hpp" />
|
||||
<Unit filename="../../src/mp_game_settings.cpp" />
|
||||
<Unit filename="../../src/mp_game_settings.hpp" />
|
||||
<Unit filename="../../src/multiplayer.cpp" />
|
||||
|
|
|
@ -739,6 +739,8 @@
|
|||
<Unit filename="..\..\src\mouse_events.hpp" />
|
||||
<Unit filename="..\..\src\mouse_handler_base.cpp" />
|
||||
<Unit filename="..\..\src\mouse_handler_base.hpp" />
|
||||
<Unit filename="..\..\src\movetype.cpp" />
|
||||
<Unit filename="..\..\src\movetype.hpp" />
|
||||
<Unit filename="..\..\src\mp_depcheck.cpp" />
|
||||
<Unit filename="..\..\src\mp_depcheck.hpp" />
|
||||
<Unit filename="..\..\src\mp_game_settings.cpp" />
|
||||
|
|
|
@ -705,7 +705,7 @@ set(wesnoth-main_SRC
|
|||
gui/dialogs/debug_clock.cpp
|
||||
gui/dialogs/dialog.cpp
|
||||
gui/dialogs/edit_label.cpp
|
||||
gui/dialogs/editor/editor_edit_label.cpp
|
||||
gui/dialogs/editor/editor_edit_label.cpp
|
||||
gui/dialogs/editor_generate_map.cpp
|
||||
gui/dialogs/editor_new_map.cpp
|
||||
gui/dialogs/editor_resize_map.cpp
|
||||
|
@ -752,6 +752,7 @@ set(wesnoth-main_SRC
|
|||
menu_events.cpp
|
||||
mouse_events.cpp
|
||||
mouse_handler_base.cpp
|
||||
movetype.cpp
|
||||
mp_depcheck.cpp
|
||||
mp_game_settings.cpp
|
||||
mp_options.cpp
|
||||
|
|
|
@ -426,6 +426,7 @@ wesnoth_sources = Split("""
|
|||
menu_events.cpp
|
||||
mouse_events.cpp
|
||||
mouse_handler_base.cpp
|
||||
movetype.cpp
|
||||
mp_depcheck.cpp
|
||||
mp_game_settings.cpp
|
||||
mp_options.cpp
|
||||
|
|
|
@ -326,7 +326,7 @@ int ai_default_recruitment_stage::average_resistance_against(const unit_type& a,
|
|||
j_end = terrain.end(); j != j_end; ++j)
|
||||
{
|
||||
// Use only reachable tiles when computing the average defense.
|
||||
if (a.movement_type().movement_cost(j->first) < unit_movement_type::UNREACHABLE) {
|
||||
if (a.movement_type().movement_cost(j->first) < movetype::UNREACHABLE) {
|
||||
defense += a.movement_type().defense_modifier(j->first) * j->second;
|
||||
weighting_sum += j->second;
|
||||
}
|
||||
|
|
|
@ -1154,7 +1154,7 @@ private:
|
|||
}
|
||||
|
||||
for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
|
||||
if (unit_it->movement_cost((*resources::game_map)[*loc_iter]) < unit_movement_type::UNREACHABLE )
|
||||
if (unit_it->movement_cost((*resources::game_map)[*loc_iter]) < movetype::UNREACHABLE )
|
||||
locations.push_back( variant( new location_callable(*loc_iter) ));
|
||||
else
|
||||
break;
|
||||
|
|
|
@ -371,7 +371,7 @@ int recruitment_phase::average_resistance_against(const unit_type& a, const unit
|
|||
j_end = terrain.end(); j != j_end; ++j)
|
||||
{
|
||||
// Use only reachable tiles when computing the average defense.
|
||||
if (a.movement_type().movement_cost(j->first) < unit_movement_type::UNREACHABLE) {
|
||||
if (a.movement_type().movement_cost(j->first) < movetype::UNREACHABLE) {
|
||||
defense += a.movement_type().defense_modifier(j->first) * j->second;
|
||||
weighting_sum += j->second;
|
||||
}
|
||||
|
|
|
@ -257,7 +257,7 @@ static int average_resistance_against(const unit_type& a, const unit_type& b)
|
|||
j_end = terrain.end(); j != j_end; ++j)
|
||||
{
|
||||
// Use only reachable tiles when computing the average defense.
|
||||
if (a.movement_type().movement_cost(j->first) < unit_movement_type::UNREACHABLE) {
|
||||
if (a.movement_type().movement_cost(j->first) < movetype::UNREACHABLE) {
|
||||
defense += a.movement_type().defense_modifier(j->first) * j->second;
|
||||
weighting_sum += j->second;
|
||||
}
|
||||
|
|
|
@ -1573,7 +1573,7 @@ public:
|
|||
push_header(first_res_row, _("Attack Type"));
|
||||
push_header(first_res_row, _("Resistance"));
|
||||
resistance_table.push_back(first_res_row);
|
||||
const unit_movement_type &movement_type = type_.movement_type();
|
||||
const movetype &movement_type = type_.movement_type();
|
||||
utils::string_map dam_tab = movement_type.damage_table();
|
||||
for(utils::string_map::const_iterator dam_it = dam_tab.begin(), dam_end = dam_tab.end();
|
||||
dam_it != dam_end; ++dam_it) {
|
||||
|
@ -1660,7 +1660,7 @@ public:
|
|||
row.push_back(std::make_pair(markup,
|
||||
font::line_width(str.str(), normal_font_size)));
|
||||
|
||||
//movement - range: 1 .. 5, unit_movement_type::UNREACHABLE=impassable
|
||||
//movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
|
||||
str.str(clear_stringstream);
|
||||
const bool cannot_move = moves > type_.movement();
|
||||
if (cannot_move) // cannot move in this terrain
|
||||
|
|
607
src/movetype.cpp
Normal file
607
src/movetype.cpp
Normal file
|
@ -0,0 +1,607 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2013 - 2013 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
|
||||
* Handle movement types.
|
||||
*/
|
||||
|
||||
#include "movetype.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
#include "map.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "terrain_translation.hpp"
|
||||
#include "unit_types.hpp" // for attack_type
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
||||
static lg::log_domain log_config("config");
|
||||
#define ERR_CF LOG_STREAM(err, log_config)
|
||||
#define WRN_CF LOG_STREAM(warn, log_config)
|
||||
|
||||
|
||||
/* *** parameters *** */
|
||||
|
||||
|
||||
namespace { // Some functions for use with parameters::eval.
|
||||
|
||||
/// Converts config defense values to a "max" value.
|
||||
int config_to_max(int value)
|
||||
{
|
||||
return value < 0 ? -value : value;
|
||||
}
|
||||
|
||||
/// Converts config defense values to a "min" value.
|
||||
int config_to_min(int value)
|
||||
{
|
||||
return value < 0 ? -value : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The parameters used when calculating a terrain-based value.
|
||||
struct movetype::terrain_info::parameters
|
||||
{
|
||||
int min_value; /// The smallest allowable value.
|
||||
int max_value; /// The largest allowable value.
|
||||
int default_value; /// The default value (if no data is available).
|
||||
|
||||
int (*eval)(int); /// Converter for values taken from a config. May be NULL.
|
||||
|
||||
bool use_move; /// Whether to look at underlying movement or defense terrains.
|
||||
bool high_is_good; /// Whether we are looking for highest or lowest (unless inverted by the underlying terrain).
|
||||
|
||||
parameters(int min, int max, int (*eval_fun)(int)=NULL, bool move=true, bool high=false) :
|
||||
min_value(min), max_value(max), default_value(high ? min : max),
|
||||
eval(eval_fun), use_move(move), high_is_good(high)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
const movetype::terrain_info::parameters
|
||||
movetype::terrain_costs::params_(1, movetype::UNREACHABLE);
|
||||
|
||||
const movetype::terrain_info::parameters
|
||||
movetype::terrain_defense::params_min_(0, 100, config_to_min, false, true);
|
||||
const movetype::terrain_info::parameters
|
||||
movetype::terrain_defense::params_max_(0, 100, config_to_max, false, false);
|
||||
|
||||
|
||||
/* *** data *** */
|
||||
|
||||
|
||||
class movetype::terrain_info::data
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// @a params must be long-lived (typically a static variable).
|
||||
explicit data(const parameters & params) :
|
||||
cfg_(), cache_(), params_(params)
|
||||
{}
|
||||
/// Constructor.
|
||||
/// @a params must be long-lived (typically a static variable).
|
||||
data(const config & cfg, const parameters & params) :
|
||||
cfg_(cfg), cache_(), params_(params)
|
||||
{}
|
||||
|
||||
data(const data & that) :
|
||||
cfg_(that.cfg_), cache_(that.cache_), params_(that.params_)
|
||||
{}
|
||||
|
||||
/// Tests for no data in this object.
|
||||
bool empty() const { return cfg_.empty(); }
|
||||
/// Merges the given config over the existing costs.
|
||||
void merge(const config & new_values, bool overwrite);
|
||||
/// Returns the value associated with the given terrain.
|
||||
int value(const t_translation::t_terrain & terrain) const
|
||||
{ return value(terrain, 0); }
|
||||
/// If there is data, writes it to the config.
|
||||
void write(config & out_cfg, const std::string & child_name) const;
|
||||
|
||||
private:
|
||||
/// Calculates the value associated with the given terrain.
|
||||
int calc_value(const t_translation::t_terrain & terrain,
|
||||
unsigned recurse_count) const;
|
||||
/// Returns the value associated with the given terrain (possibly cached).
|
||||
int value(const t_translation::t_terrain & terrain,
|
||||
unsigned recurse_count) const;
|
||||
|
||||
private:
|
||||
typedef std::map<t_translation::t_terrain, int> cache_t;
|
||||
|
||||
/// Config describing the terrain values.
|
||||
config cfg_;
|
||||
/// Cache of values based on the config.
|
||||
mutable cache_t cache_;
|
||||
/// Various parameters used when calculating values.
|
||||
const parameters & params_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Merges the given config over the existing costs.
|
||||
* @param[in] new_values The new values.
|
||||
* @param[in] overwrite If true, the new values overwrite the old.
|
||||
* If false, the new values are added to the old.
|
||||
* @param[in] cascade Cache clearing will be cascaded into this terrain_info.
|
||||
*/
|
||||
void movetype::terrain_info::data::merge(const config & new_values, bool overwrite)
|
||||
{
|
||||
if ( overwrite )
|
||||
// We do not support child tags here, so do not copy any that might
|
||||
// be in the input. (If in the future we need to support child tags,
|
||||
// change "merge_attributes" to "merge_with".)
|
||||
cfg_.merge_attributes(new_values);
|
||||
else {
|
||||
BOOST_FOREACH( const config::attribute & a, new_values.attribute_range() ) {
|
||||
config::attribute_value & dest = cfg_[a.first];
|
||||
int old = dest.to_int(params_.max_value);
|
||||
|
||||
// The new value is the absolute value of the old plus the
|
||||
// provided value, capped between minimum and maximum, then
|
||||
// given the sign of the old value.
|
||||
// (Think defenses for why we might have negative values.)
|
||||
int value = abs(old) + a.second.to_int(0);
|
||||
value = std::max(params_.min_value, std::min(value, params_.max_value));
|
||||
if ( old < 0 )
|
||||
value = -value;
|
||||
|
||||
dest = value;
|
||||
}
|
||||
}
|
||||
|
||||
// The new data has invalidated the cache.
|
||||
cache_.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If there is data, writes it to a config.
|
||||
* @param[out] out_cfg The config that will receive the data.
|
||||
* @param[in] child_name If not empty, create and write to a child config with this tag.
|
||||
* This child will *not* be created if there is no data to write.
|
||||
*/
|
||||
void movetype::terrain_info::data::write(
|
||||
config & out_cfg, const std::string & child_name) const
|
||||
{
|
||||
if ( cfg_.empty() )
|
||||
return;
|
||||
|
||||
if ( child_name.empty() )
|
||||
out_cfg.merge_with(cfg_);
|
||||
else
|
||||
out_cfg.add_child(child_name, cfg_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the value associated with the given terrain.
|
||||
* This is separate from value() to separate the calculating of the
|
||||
* value from the caching of it.
|
||||
* @param[in] terrain The terrain whose value is requested.
|
||||
* @param[in] recurse_count Detects (probable) infinite recursion.
|
||||
*/
|
||||
int movetype::terrain_info::data::calc_value(
|
||||
const t_translation::t_terrain & terrain,
|
||||
unsigned recurse_count) const
|
||||
{
|
||||
// Infinite recursion detection:
|
||||
if ( recurse_count > 100 ) {
|
||||
ERR_CF << "infinite terrain_info recursion on "
|
||||
<< (params_.use_move ? "movement" : "defense") << ": "
|
||||
<< t_translation::write_terrain_code(terrain)
|
||||
<< " depth " << recurse_count << '\n';
|
||||
return params_.default_value;
|
||||
}
|
||||
assert(resources::game_map);
|
||||
gamemap & map = *resources::game_map;
|
||||
|
||||
// Get a list of underlying terrains.
|
||||
const t_translation::t_list & underlying = params_.use_move ?
|
||||
map.underlying_mvt_terrain(terrain) :
|
||||
map.underlying_def_terrain(terrain);
|
||||
assert(!underlying.empty());
|
||||
|
||||
|
||||
if ( underlying.size() == 1 && underlying.front() == terrain )
|
||||
{
|
||||
// This is not an alias; get the value directly.
|
||||
int result = params_.default_value;
|
||||
|
||||
const std::string & id = map.get_terrain_info(terrain).id();
|
||||
if (const config::attribute_value *val = cfg_.get(id)) {
|
||||
// Read the value from our config.
|
||||
result = val->to_int(params_.default_value);
|
||||
if ( params_.eval != NULL )
|
||||
result = params_.eval(result);
|
||||
}
|
||||
|
||||
// Validate the value.
|
||||
if ( result < params_.min_value ) {
|
||||
WRN_CF << "Terrain '" << terrain << "' has evaluated to " << result
|
||||
<< " (" << (params_.use_move ? "cost" : "defense")
|
||||
<< "), which is less than " << params_.min_value
|
||||
<< "; resetting to " << params_.min_value << ".\n";
|
||||
result = params_.min_value;
|
||||
}
|
||||
if ( result > params_.max_value ) {
|
||||
WRN_CF << "Terrain '" << terrain << "' has evaluated to " << result
|
||||
<< " (" << (params_.use_move ? "cost" : "defense")
|
||||
<< "), which is more than " << params_.max_value
|
||||
<< "; resetting to " << params_.max_value << ".\n";
|
||||
result = params_.max_value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an alias; select the best of all underlying terrains.
|
||||
bool prefer_high = params_.high_is_good;
|
||||
int result = params_.default_value;
|
||||
if ( underlying.front() == t_translation::MINUS )
|
||||
// Use the other value as the initial value.
|
||||
result = result == params_.max_value ? params_.min_value :
|
||||
params_.max_value;
|
||||
|
||||
// Loop through all underlying terrains.
|
||||
t_translation::t_list::const_iterator i;
|
||||
for ( i = underlying.begin(); i != underlying.end(); ++i )
|
||||
{
|
||||
if ( *i == t_translation::PLUS ) {
|
||||
// Prefer what is good.
|
||||
prefer_high = params_.high_is_good;
|
||||
}
|
||||
else if ( *i == t_translation::MINUS ) {
|
||||
// Prefer what is bad.
|
||||
prefer_high = !params_.high_is_good;
|
||||
}
|
||||
else {
|
||||
// Test the underlying terrain's value against the best so far.
|
||||
const int num = value(*i, recurse_count + 1);
|
||||
|
||||
if ( ( prefer_high && num > result) ||
|
||||
(!prefer_high && num < result) )
|
||||
result = num;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value associated with the given terrain (possibly cached).
|
||||
* @param[in] terrain The terrain whose value is requested.
|
||||
* @param[in] recurse_count Detects (probable) infinite recursion.
|
||||
*/
|
||||
int movetype::terrain_info::data::value(
|
||||
const t_translation::t_terrain & terrain,
|
||||
unsigned recurse_count) const
|
||||
{
|
||||
// Check the cache.
|
||||
std::pair<cache_t::iterator, bool> cache_it =
|
||||
cache_.insert(std::make_pair(terrain, -127)); // Bogus value that should never be seen.
|
||||
if ( cache_it.second )
|
||||
// The cache did not have an entry for this terrain, so calculate the value.
|
||||
cache_it.first->second = calc_value(terrain, recurse_count);
|
||||
|
||||
return cache_it.first->second;
|
||||
}
|
||||
|
||||
|
||||
/* *** terrain_info *** */
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param[in] params The parameters to use when calculating values.
|
||||
* This is stored as a reference, so it must be long-lived (typically a static variable).
|
||||
* @param[in] fallback Used as a backup in case we have no data (think vision costs falling back to movement costs).
|
||||
* @note The fallback mechanism is a bit fragile and really should only
|
||||
* be used by movetype.
|
||||
*/
|
||||
movetype::terrain_info::terrain_info(const parameters & params,
|
||||
const terrain_info * fallback) :
|
||||
data_(new data(params)),
|
||||
fallback_(fallback)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param[in] cfg An initial data set.
|
||||
* @param[in] params The parameters to use when calculating values.
|
||||
* This is stored as a reference, so it must be long-lived (typically a static variable).
|
||||
* @param[in] fallback Used as a backup in case we have no data (think vision costs falling back to movement costs).
|
||||
* @note The fallback mechanism is a bit fragile and really should only
|
||||
* be used by movetype.
|
||||
*/
|
||||
movetype::terrain_info::terrain_info(const config & cfg, const parameters & params,
|
||||
const terrain_info * fallback) :
|
||||
data_(new data(cfg, params)),
|
||||
fallback_(fallback)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param[in] that The terran_info to copy.
|
||||
* @param[in] fallback Used as a backup in case we have no data (think vision costs falling back to movement costs).
|
||||
* @note The fallback mechanism is a bit fragile and really should only
|
||||
* be used by movetype.
|
||||
*/
|
||||
movetype::terrain_info::terrain_info(const terrain_info & that,
|
||||
const terrain_info * fallback) :
|
||||
// If we do not have a fallback, we need to incorporate that's fallback.
|
||||
// (See also the assignment operator.)
|
||||
data_(new data(fallback ? *that.data_ : that.get_merged())),
|
||||
fallback_(fallback)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
movetype::terrain_info::~terrain_info()
|
||||
{
|
||||
delete data_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assignment operator.
|
||||
*/
|
||||
movetype::terrain_info & movetype::terrain_info::operator=(const terrain_info & that)
|
||||
{
|
||||
if ( this != &that ) {
|
||||
delete data_;
|
||||
// If we do not have a fallback, we need to incorporate that's fallback.
|
||||
// (See also the copy constructor.)
|
||||
data_ = new data(fallback_ ? *that.data_ : that.get_merged());
|
||||
|
||||
// We do not change our fallback.
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merges the given config over the existing values.
|
||||
* @param[in] new_values The new values.
|
||||
* @param[in] overwrite If true, the new values overwrite the old.
|
||||
* If false, the new values are added to the old.
|
||||
*/
|
||||
void movetype::terrain_info::merge(const config & new_values, bool overwrite)
|
||||
{
|
||||
data_->merge(new_values, overwrite);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value associated with the given terrain.
|
||||
*/
|
||||
int movetype::terrain_info::value(const t_translation::t_terrain & terrain) const
|
||||
{
|
||||
if ( fallback_ && data_->empty() )
|
||||
return fallback_->value(terrain);
|
||||
|
||||
return data_->value(terrain);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes our data to a config.
|
||||
* @param[out] cfg The config that will receive the data.
|
||||
* @param[in] child_name If not empty, create and write to a child config with this tag.
|
||||
* @param[in] merged If true, our data will be merged with our fallback's, and it is possible an empty child will be created.
|
||||
* If false, data will not be merged, and an empty child will not be created.
|
||||
*/
|
||||
void movetype::terrain_info::write(config & cfg, const std::string & child_name,
|
||||
bool merged) const
|
||||
{
|
||||
if ( !merged )
|
||||
data_->write(cfg, child_name);
|
||||
else
|
||||
{
|
||||
// Get a place to write to.
|
||||
config & merged_cfg = child_name.empty() ? cfg : cfg.add_child(child_name);
|
||||
|
||||
if ( fallback_ && data_->empty() )
|
||||
fallback_->write(merged_cfg, "", true);
|
||||
else
|
||||
data_->write(merged_cfg, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns data that incorporates our fallback.
|
||||
*/
|
||||
const movetype::terrain_info::data & movetype::terrain_info::get_merged() const
|
||||
{
|
||||
if ( !fallback_ || !data_->empty() )
|
||||
return *data_;
|
||||
else
|
||||
return fallback_->get_merged();
|
||||
}
|
||||
|
||||
|
||||
/* *** resistances *** */
|
||||
|
||||
|
||||
/**
|
||||
* Returns a map from attack types to resistances.
|
||||
*/
|
||||
utils::string_map movetype::resistances::damage_table() const
|
||||
{
|
||||
utils::string_map result;
|
||||
|
||||
BOOST_FOREACH( const config::attribute & attrb, cfg_.attribute_range() )
|
||||
result[attrb.first] = attrb.second;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the resistance against the indicated attack.
|
||||
*/
|
||||
int movetype::resistances::resistance_against(const attack_type & attack) const
|
||||
{
|
||||
return cfg_[attack.type()].to_int(100);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the resistance against the indicated damage type.
|
||||
*/
|
||||
int movetype::resistances::resistance_against(const std::string & damage_type) const
|
||||
{
|
||||
return cfg_[damage_type].to_int(100);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merges the given config over the existing costs.
|
||||
* If @a overwrite is false, the new values will be added to the old.
|
||||
*/
|
||||
void movetype::resistances::merge(const config & new_data, bool overwrite)
|
||||
{
|
||||
if ( overwrite )
|
||||
// We do not support child tags here, so do not copy any that might
|
||||
// be in the input. (If in the future we need to support child tags,
|
||||
// change "merge_attributes" to "merge_with".)
|
||||
cfg_.merge_attributes(new_data);
|
||||
else
|
||||
BOOST_FOREACH( const config::attribute & a, new_data.attribute_range() ) {
|
||||
config::attribute_value & dest = cfg_[a.first];
|
||||
dest = std::max(0, dest.to_int(100) + a.second.to_int(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes our data to a config, as a child if @a child_name is specified.
|
||||
* (No child is created if there is no data.)
|
||||
*/
|
||||
void movetype::resistances::write(config & out_cfg, const std::string & child_name) const
|
||||
{
|
||||
if ( cfg_.empty() )
|
||||
return;
|
||||
|
||||
if ( child_name.empty() )
|
||||
out_cfg.merge_with(cfg_);
|
||||
else
|
||||
out_cfg.add_child(child_name, cfg_);
|
||||
}
|
||||
|
||||
|
||||
/* *** movetype *** */
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
movetype::movetype() :
|
||||
movement_(NULL),
|
||||
vision_(&movement_),
|
||||
jamming_(NULL),
|
||||
defense_(),
|
||||
resist_(),
|
||||
flying_(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor from a config
|
||||
*/
|
||||
movetype::movetype(const config & cfg) :
|
||||
movement_(cfg.child_or_empty("movement_costs"), NULL),
|
||||
vision_(cfg.child_or_empty("vision_costs"), &movement_),
|
||||
jamming_(cfg.child_or_empty("jamming_costs"), NULL),
|
||||
defense_(cfg.child_or_empty("defense")),
|
||||
resist_(cfg.child_or_empty("resistance")),
|
||||
flying_(cfg["flies"].to_bool(false))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*/
|
||||
movetype::movetype(const movetype & that) :
|
||||
movement_(that.movement_, NULL),
|
||||
vision_(that.vision_, &movement_),
|
||||
jamming_(that.jamming_, NULL),
|
||||
defense_(that.defense_),
|
||||
resist_(that.resist_),
|
||||
flying_(that.flying_)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merges the given config over the existing data.
|
||||
* If @a overwrite is false, the new values will be added to the old.
|
||||
*/
|
||||
void movetype::merge(const config & new_cfg, bool overwrite)
|
||||
{
|
||||
BOOST_FOREACH( const config & child, new_cfg.child_range("movement_costs") )
|
||||
movement_.merge(child, overwrite);
|
||||
|
||||
BOOST_FOREACH( const config & child, new_cfg.child_range("vision_costs") )
|
||||
vision_.merge(child, overwrite);
|
||||
|
||||
BOOST_FOREACH( const config & child, new_cfg.child_range("jamming_costs") )
|
||||
jamming_.merge(child, overwrite);
|
||||
|
||||
BOOST_FOREACH( const config & child, new_cfg.child_range("defense") )
|
||||
defense_.merge(child, overwrite);
|
||||
|
||||
BOOST_FOREACH( const config & child, new_cfg.child_range("resistance") )
|
||||
resist_.merge(child, overwrite);
|
||||
|
||||
// "flies" is used when WML defines a movetype.
|
||||
// "flying" is used when WML defines a unit.
|
||||
// It's easier to support both than to track which case we are in.
|
||||
flying_ = new_cfg["flies"].to_bool(flying_);
|
||||
flying_ = new_cfg["flying"].to_bool(flying_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the movement type data to the provided config.
|
||||
*/
|
||||
void movetype::write(config & cfg) const
|
||||
{
|
||||
movement_.write(cfg, "movement_costs", false);
|
||||
vision_.write(cfg, "vision_costs", false);
|
||||
jamming_.write(cfg, "jamming_costs", false);
|
||||
defense_.write(cfg, "defense");
|
||||
resist_.write(cfg, "resistance");
|
||||
|
||||
if ( flying_ )
|
||||
cfg["flying"] = true;
|
||||
}
|
||||
|
218
src/movetype.hpp
Normal file
218
src/movetype.hpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2013 - 2013 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.
|
||||
*/
|
||||
#ifndef MOVETYPE_H_INCLUDED
|
||||
#define MOVETYPE_H_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
class attack_type;
|
||||
namespace t_translation { class t_terrain; }
|
||||
|
||||
|
||||
/// The basic "size" of the unit - flying, small land, large land, etc.
|
||||
/// This encompasses terrain costs, defenses, and resistances.
|
||||
class movetype
|
||||
{
|
||||
/// Stores a set of data based on terrain.
|
||||
class terrain_info
|
||||
{
|
||||
/// The terrain-based data.
|
||||
class data;
|
||||
// The data class is not defined here to keep the header file cleaner.
|
||||
|
||||
public:
|
||||
/// The parameters used when calculating a terrain-based value.
|
||||
struct parameters;
|
||||
|
||||
explicit terrain_info(const parameters & params,
|
||||
const terrain_info * fallback=NULL);
|
||||
terrain_info(const config & cfg, const parameters & params,
|
||||
const terrain_info * fallback=NULL);
|
||||
terrain_info(const terrain_info & that,
|
||||
const terrain_info * fallback=NULL);
|
||||
~terrain_info();
|
||||
|
||||
terrain_info & operator=(const terrain_info & that);
|
||||
|
||||
|
||||
/// Merges the given config over the existing values.
|
||||
void merge(const config & new_values, bool overwrite);
|
||||
/// Returns the value associated with the given terrain.
|
||||
int value(const t_translation::t_terrain & terrain) const;
|
||||
/// Writes our data to a config.
|
||||
void write(config & cfg, const std::string & child_name="", bool merged=true) const;
|
||||
|
||||
private:
|
||||
// Returns data that incorporates our fallback.
|
||||
const data & get_merged() const;
|
||||
|
||||
private:
|
||||
data * data_; /// Never NULL
|
||||
const terrain_info * const fallback_;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
/// Magic value that signifies a hex is unreachable.
|
||||
/// The UNREACHABLE macro in the data tree should match this value.
|
||||
static const int UNREACHABLE = 99;
|
||||
|
||||
/// Stores a set of terrain costs (for movement, vision, or "jamming").
|
||||
class terrain_costs : public terrain_info
|
||||
{
|
||||
static const parameters params_;
|
||||
public:
|
||||
explicit terrain_costs(const terrain_costs * fallback=NULL) :
|
||||
terrain_info(params_, fallback)
|
||||
{}
|
||||
explicit terrain_costs(const config & cfg,
|
||||
const terrain_costs * fallback=NULL) :
|
||||
terrain_info(cfg, params_, fallback)
|
||||
{}
|
||||
terrain_costs(const terrain_costs & that,
|
||||
const terrain_costs * fallback=NULL) :
|
||||
terrain_info(that, fallback)
|
||||
{}
|
||||
|
||||
/// Returns the cost associated with the given terrain.
|
||||
int cost(const t_translation::t_terrain & terrain) const
|
||||
{ return value(terrain); }
|
||||
|
||||
// Inherited from terrain_info:
|
||||
//void merge(const config & new_values, bool overwrite);
|
||||
//void write(config & cfg, const std::string & child_name="", bool merged=true) const;
|
||||
};
|
||||
|
||||
/// Stores a set of defense levels.
|
||||
class terrain_defense
|
||||
{
|
||||
static const terrain_info::parameters params_min_;
|
||||
static const terrain_info::parameters params_max_;
|
||||
|
||||
public:
|
||||
terrain_defense() : min_(params_min_), max_(params_max_) {}
|
||||
explicit terrain_defense(const config & cfg) :
|
||||
min_(cfg, params_min_), max_(cfg, params_max_)
|
||||
{}
|
||||
|
||||
/// Returns the defense associated with the given terrain.
|
||||
int defense(const t_translation::t_terrain & terrain) const
|
||||
{ return std::max(min_.value(terrain), max_.value(terrain)); }
|
||||
/// Merges the given config over the existing costs.
|
||||
/// (Not overwriting implies adding.)
|
||||
void merge(const config & new_data, bool overwrite)
|
||||
{ min_.merge(new_data, overwrite); max_.merge(new_data, overwrite); }
|
||||
/// Writes our data to a config, as a child if @a child_name is specified.
|
||||
/// (No child is created if there is no data.)
|
||||
void write(config & cfg, const std::string & child_name="") const
|
||||
{ max_.write(cfg, child_name, false); }
|
||||
|
||||
private:
|
||||
// There will be duplication of the config here, but it is a small
|
||||
// config, and the duplication allows greater code sharing.
|
||||
terrain_info min_;
|
||||
terrain_info max_;
|
||||
};
|
||||
|
||||
/// Stores a set of resistances.
|
||||
class resistances
|
||||
{
|
||||
public:
|
||||
resistances() : cfg_() {}
|
||||
explicit resistances(const config & cfg) : cfg_(cfg) {}
|
||||
|
||||
/// Returns a map from attack types to resistances.
|
||||
utils::string_map damage_table() const;
|
||||
/// Returns the resistance against the indicated attack.
|
||||
int resistance_against(const attack_type & attack) const;
|
||||
/// Returns the resistance against the indicated damage type.
|
||||
int resistance_against(const std::string & damage_type) const;
|
||||
/// Merges the given config over the existing costs.
|
||||
void merge(const config & new_data, bool overwrite);
|
||||
/// Writes our data to a config, as a child if @a child_name is specified.
|
||||
void write(config & out_cfg, const std::string & child_name="") const;
|
||||
|
||||
private:
|
||||
config cfg_;
|
||||
};
|
||||
|
||||
public:
|
||||
movetype();
|
||||
explicit movetype(const config & cfg);
|
||||
movetype(const movetype & that);
|
||||
|
||||
// This class is basically just a holder for its various pieces, so
|
||||
// provide access to those pieces on demenad.
|
||||
terrain_costs & get_movement() { return movement_; }
|
||||
terrain_costs & get_vision() { return vision_; }
|
||||
terrain_costs & get_jamming() { return jamming_; }
|
||||
terrain_defense & get_defense() { return defense_; }
|
||||
resistances & get_resistances() { return resist_; }
|
||||
// And const access:
|
||||
const terrain_costs & get_movement() const { return movement_; }
|
||||
const terrain_costs & get_vision() const { return vision_; }
|
||||
const terrain_costs & get_jamming() const { return jamming_; }
|
||||
const terrain_defense & get_defense() const { return defense_; }
|
||||
const resistances & get_resistances() const { return resist_; }
|
||||
|
||||
/// Returns whether or not *this is flagged as a flying movement type.
|
||||
bool is_flying() const { return flying_; }
|
||||
/// Sets whether or not *this is flagged as a flying movement type.
|
||||
void set_flying(bool flies=true) { flying_ = flies; }
|
||||
|
||||
/// Returns the cost to move through the indicated terrain.
|
||||
int movement_cost(const t_translation::t_terrain & terrain) const
|
||||
{ return movement_.cost(terrain); }
|
||||
/// Returns the cost to see through the indicated terrain.
|
||||
int vision_cost(const t_translation::t_terrain & terrain) const
|
||||
{ return vision_.cost(terrain); }
|
||||
/// Returns the cost to "jam" through the indicated terrain.
|
||||
int jamming_cost(const t_translation::t_terrain & terrain) const
|
||||
{ return jamming_.cost(terrain); }
|
||||
|
||||
/// Returns the defensive value of the indicated terrain.
|
||||
int defense_modifier(const t_translation::t_terrain & terrain) const
|
||||
{ return defense_.defense(terrain); }
|
||||
|
||||
/// Returns the resistance against the indicated attack.
|
||||
int resistance_against(const attack_type & attack) const
|
||||
{ return resist_.resistance_against(attack); }
|
||||
/// Returns the resistance against the indicated damage type.
|
||||
int resistance_against(const std::string & damage_type) const
|
||||
{ return resist_.resistance_against(damage_type); }
|
||||
/// Returns a map from attack types to resistances.
|
||||
utils::string_map damage_table() const
|
||||
{ return resist_.damage_table(); }
|
||||
|
||||
/// Merges the given config over the existing data.
|
||||
void merge(const config & new_cfg, bool overwrite=true);
|
||||
|
||||
/// Writes the movement type data to the provided config.
|
||||
void write(config & cfg) const;
|
||||
|
||||
private:
|
||||
terrain_costs movement_;
|
||||
terrain_costs vision_;
|
||||
terrain_costs jamming_;
|
||||
terrain_defense defense_;
|
||||
resistances resist_;
|
||||
|
||||
bool flying_;
|
||||
};
|
||||
|
||||
|
||||
#endif // MOVETYPE_H_INCLUDED
|
||||
|
|
@ -81,7 +81,7 @@ map_location pathfind::find_vacant_tile(const map_location& loc,
|
|||
//If this area is not a castle but should, skip it.
|
||||
if (vacancy == pathfind::VACANT_CASTLE && !map.is_castle(loc)) continue;
|
||||
const bool pass_check_and_unreachable = pass_check
|
||||
&& pass_check->movement_cost(map[loc]) == unit_movement_type::UNREACHABLE;
|
||||
&& pass_check->movement_cost(map[loc]) == movetype::UNREACHABLE;
|
||||
//If the unit can't reach the tile and we have searched
|
||||
//an area of at least radius 10 (arbitrary), skip the tile.
|
||||
//Neccessary for cases such as an unreachable
|
||||
|
@ -693,7 +693,7 @@ double pathfind::shortest_path_calculator::cost(const map_location& loc, const d
|
|||
return move_cost + (defense_subcost + other_unit_subcost) / 10000.0;
|
||||
}
|
||||
|
||||
pathfind::move_type_path_calculator::move_type_path_calculator(const unit_movement_type& mt, int movement_left, int total_movement, team const &t, gamemap const &map)
|
||||
pathfind::move_type_path_calculator::move_type_path_calculator(const movetype& mt, int movement_left, int total_movement, team const &t, gamemap const &map)
|
||||
: movement_type_(mt), movement_left_(movement_left),
|
||||
total_movement_(total_movement), viewing_team_(t), map_(map)
|
||||
{}
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
#define PATHFIND_H_INCLUDED
|
||||
|
||||
class gamemap;
|
||||
class movetype;
|
||||
class team;
|
||||
class unit;
|
||||
class unit_map;
|
||||
class unit_movement_type;
|
||||
|
||||
#include "map_location.hpp"
|
||||
|
||||
|
@ -227,11 +227,11 @@ private:
|
|||
|
||||
struct move_type_path_calculator : cost_calculator
|
||||
{
|
||||
move_type_path_calculator(const unit_movement_type& mt, int movement_left, int total_movement, const team& t, const gamemap& map);
|
||||
move_type_path_calculator(const movetype& mt, int movement_left, int total_movement, const team& t, const gamemap& map);
|
||||
virtual double cost(const map_location& loc, const double so_far) const;
|
||||
|
||||
private:
|
||||
const unit_movement_type &movement_type_;
|
||||
const movetype &movement_type_;
|
||||
const int movement_left_;
|
||||
const int total_movement_;
|
||||
team const &viewing_team_;
|
||||
|
|
|
@ -601,7 +601,7 @@ static config unit_moves(const unit* u)
|
|||
tooltip << name << ": ";
|
||||
|
||||
std::string color;
|
||||
//movement - range: 1 .. 5, unit_movement_type::UNREACHABLE=impassable
|
||||
//movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
|
||||
const bool cannot_move = moves > u->total_movement();
|
||||
if (cannot_move) // cannot move in this terrain
|
||||
color = "red";
|
||||
|
@ -1263,7 +1263,7 @@ REPORT_GENERATOR(position)
|
|||
|
||||
int move_cost = u->movement_cost(terrain);
|
||||
int defense = 100 - u->defense_modifier(terrain);
|
||||
if (move_cost < unit_movement_type::UNREACHABLE) {
|
||||
if (move_cost < movetype::UNREACHABLE) {
|
||||
str << " (" << defense << "%," << move_cost << ')';
|
||||
} else if (mouseover_hex == displayed_unit_hex) {
|
||||
str << " (" << defense << "%,‒)";
|
||||
|
|
190
src/unit.cpp
190
src/unit.cpp
|
@ -145,12 +145,9 @@ 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_),
|
||||
jamming_(o.jamming_),
|
||||
jamming_costs_(o.jamming_costs_),
|
||||
defense_mods_(o.defense_mods_),
|
||||
movement_type_(o.movement_type_),
|
||||
hold_position_(o.hold_position_),
|
||||
end_turn_(o.end_turn_),
|
||||
resting_(o.resting_),
|
||||
|
@ -176,7 +173,6 @@ unit::unit(const unit& o):
|
|||
unit_value_(o.unit_value_),
|
||||
goto_(o.goto_),
|
||||
interrupted_move_(o.interrupted_move_),
|
||||
flying_(o.flying_),
|
||||
is_fearless_(o.is_fearless_),
|
||||
is_healthy_(o.is_healthy_),
|
||||
|
||||
|
@ -231,12 +227,9 @@ unit::unit(const config &cfg, bool use_traits, game_state* state, const vconfig*
|
|||
formula_vars_(),
|
||||
movement_(0),
|
||||
max_movement_(0),
|
||||
movement_costs_(),
|
||||
vision_(-1),
|
||||
vision_costs_(),
|
||||
jamming_(0),
|
||||
jamming_costs_(),
|
||||
defense_mods_(),
|
||||
movement_type_(),
|
||||
hold_position_(false),
|
||||
end_turn_(false),
|
||||
resting_(false),
|
||||
|
@ -258,7 +251,6 @@ unit::unit(const config &cfg, bool use_traits, game_state* state, const vconfig*
|
|||
unit_value_(),
|
||||
goto_(),
|
||||
interrupted_move_(),
|
||||
flying_(false),
|
||||
is_fearless_(false),
|
||||
is_healthy_(false),
|
||||
modification_descriptions_(),
|
||||
|
@ -345,9 +337,6 @@ unit::unit(const config &cfg, bool use_traits, game_state* state, const vconfig*
|
|||
if (const config::attribute_value *v = cfg.get("zoc")) {
|
||||
emit_zoc_ = v->to_bool(level_ > 0);
|
||||
}
|
||||
if (const config::attribute_value *v = cfg.get("flying")) {
|
||||
flying_ = v->to_bool();
|
||||
}
|
||||
if (const config::attribute_value *v = cfg.get("description")) {
|
||||
cfg_["description"] = *v;
|
||||
}
|
||||
|
@ -416,50 +405,9 @@ 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 defense if this config has its own defined
|
||||
cfg_range = cfg.child_range("defense");
|
||||
if(cfg_range.first != cfg_range.second) {
|
||||
config &target = cfg_.child_or_add("defense");
|
||||
do {
|
||||
target.append(*cfg_range.first);
|
||||
} while(++cfg_range.first != cfg_range.second);
|
||||
}
|
||||
|
||||
//adjust the unit_type's movement costs if this config has its own defined
|
||||
cfg_range = cfg.child_range("movement_costs");
|
||||
if(cfg_range.first != cfg_range.second) {
|
||||
config &target = cfg_.child_or_add("movement_costs");
|
||||
do {
|
||||
target.append(*cfg_range.first);
|
||||
} 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 jamming costs if this config has its own defined
|
||||
cfg_range = cfg.child_range("jamming_costs");
|
||||
if(cfg_range.first != cfg_range.second) {
|
||||
config &target = cfg_.child_or_add("jamming_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) {
|
||||
config &target = cfg_.child_or_add("resistance");
|
||||
do {
|
||||
target.append(*cfg_range.first);
|
||||
} while(++cfg_range.first != cfg_range.second);
|
||||
}
|
||||
// Adjust the unit's defense, movement, vision, jamming, resistances, and
|
||||
// flying status if this config has its own defined.
|
||||
movement_type_.merge(cfg);
|
||||
|
||||
if (const config &status_flags = cfg.child("status"))
|
||||
{
|
||||
|
@ -607,12 +555,9 @@ unit::unit(const unit_type &u_type, int side, bool real_unit,
|
|||
formula_vars_(),
|
||||
movement_(0),
|
||||
max_movement_(0),
|
||||
movement_costs_(),
|
||||
vision_(-1),
|
||||
vision_costs_(),
|
||||
jamming_(0),
|
||||
jamming_costs_(),
|
||||
defense_mods_(),
|
||||
movement_type_(),
|
||||
hold_position_(false),
|
||||
end_turn_(false),
|
||||
resting_(false),
|
||||
|
@ -634,7 +579,6 @@ unit::unit(const unit_type &u_type, int side, bool real_unit,
|
|||
unit_value_(),
|
||||
goto_(),
|
||||
interrupted_move_(),
|
||||
flying_(false),
|
||||
is_fearless_(false),
|
||||
is_healthy_(false),
|
||||
modification_descriptions_(),
|
||||
|
@ -820,10 +764,6 @@ void unit::advance_to(const config &old_cfg, const unit_type &u_type,
|
|||
|
||||
// Clear modification-related caches
|
||||
modification_descriptions_.clear();
|
||||
movement_costs_.clear();
|
||||
vision_costs_.clear();
|
||||
jamming_costs_.clear();
|
||||
defense_mods_.clear();
|
||||
|
||||
// Clear the stored config and replace it with the one from the unit type,
|
||||
// except for a few attributes.
|
||||
|
@ -836,15 +776,6 @@ void unit::advance_to(const config &old_cfg, const unit_type &u_type,
|
|||
}
|
||||
}
|
||||
|
||||
if ( new_type.movement_type().get_parent() ) {
|
||||
new_cfg.merge_with(new_type.movement_type().get_parent()->get_cfg());
|
||||
// Convert movement type's "flies" to unit's "flying".
|
||||
if ( const config::attribute_value * flies = new_cfg.get("flies") ) {
|
||||
new_cfg["flying"] = flies->to_bool();
|
||||
new_cfg.remove_attribute("flies");
|
||||
}
|
||||
}
|
||||
|
||||
// Inherit from the new unit type.
|
||||
new_cfg.merge_with(new_type.get_cfg_for_units());
|
||||
|
||||
|
@ -881,10 +812,10 @@ void unit::advance_to(const config &old_cfg, const unit_type &u_type,
|
|||
max_movement_ = new_type.movement();
|
||||
vision_ = new_type.vision();
|
||||
jamming_ = new_type.jamming();
|
||||
movement_type_ = new_type.movement_type();
|
||||
emit_zoc_ = new_type.has_zoc();
|
||||
attacks_ = new_type.attacks();
|
||||
unit_value_ = new_type.cost();
|
||||
flying_ = new_type.movement_type().is_flying();
|
||||
|
||||
max_attacks_ = new_type.max_attacks();
|
||||
|
||||
|
@ -1673,6 +1604,7 @@ bool unit::internal_matches_filter(const vconfig& cfg, const map_location& loc,
|
|||
void unit::write(config& cfg) const
|
||||
{
|
||||
cfg.append(cfg_);
|
||||
movement_type_.write(cfg);
|
||||
|
||||
if ( cfg["description"] == type().unit_description() ) {
|
||||
cfg.remove_attribute("description");
|
||||
|
@ -1724,11 +1656,8 @@ void unit::write(config& cfg) const
|
|||
}
|
||||
|
||||
cfg["gender"] = gender_string(gender_);
|
||||
|
||||
cfg["variation"] = variation_;
|
||||
|
||||
cfg["role"] = role_;
|
||||
cfg["flying"] = flying_;
|
||||
|
||||
config status_flags;
|
||||
std::map<std::string,std::string> all_states = get_states();
|
||||
|
@ -2207,10 +2136,9 @@ bool unit::loyal() const
|
|||
|
||||
int unit::movement_cost(const t_translation::t_terrain & terrain) const
|
||||
{
|
||||
const int res = movement_cost_internal(movement_costs_,
|
||||
cfg_.child("movement_costs"), NULL, terrain);
|
||||
const int res = movement_type_.movement_cost(terrain);
|
||||
|
||||
if (res == unit_movement_type::UNREACHABLE) {
|
||||
if ( res == movetype::UNREACHABLE ) {
|
||||
return res;
|
||||
} else if(get_state(STATE_SLOWED)) {
|
||||
return res*2;
|
||||
|
@ -2220,12 +2148,9 @@ int unit::movement_cost(const t_translation::t_terrain & terrain) const
|
|||
|
||||
int unit::vision_cost(const t_translation::t_terrain & terrain) const
|
||||
{
|
||||
if (cfg_.child_count("vision_costs") == 0) return movement_cost(terrain);
|
||||
const int res = movement_type_.vision_cost(terrain);
|
||||
|
||||
const int res = movement_cost_internal(vision_costs_,
|
||||
cfg_.child("vision_costs"), NULL, terrain);
|
||||
|
||||
if (res == unit_movement_type::UNREACHABLE) {
|
||||
if ( res == movetype::UNREACHABLE ) {
|
||||
return res;
|
||||
} else if(get_state(STATE_SLOWED)) {
|
||||
return res*2;
|
||||
|
@ -2235,12 +2160,9 @@ int unit::vision_cost(const t_translation::t_terrain & terrain) const
|
|||
|
||||
int unit::jamming_cost(const t_translation::t_terrain & terrain) const
|
||||
{
|
||||
// if (cfg_.child_count("jamming_costs") == 0) return movement_cost(terrain);
|
||||
const int res = movement_type_.jamming_cost(terrain);
|
||||
|
||||
const int res = movement_cost_internal(jamming_costs_,
|
||||
cfg_.child("jamming_costs"), NULL, terrain);
|
||||
|
||||
if (res == unit_movement_type::UNREACHABLE) {
|
||||
if ( res == movetype::UNREACHABLE ) {
|
||||
return res;
|
||||
} else if(get_state(STATE_SLOWED)) {
|
||||
return res*2;
|
||||
|
@ -2250,7 +2172,7 @@ int unit::jamming_cost(const t_translation::t_terrain & terrain) const
|
|||
|
||||
int unit::defense_modifier(const t_translation::t_terrain & terrain) const
|
||||
{
|
||||
int def = defense_modifier_internal(defense_mods_, cfg_, NULL, terrain);
|
||||
int def = movement_type_.defense_modifier(terrain);
|
||||
#if 0
|
||||
// A [defense] ability is too costly and doesn't take into account target locations.
|
||||
// Left as a comment in case someone ever wonders why it isn't a good idea.
|
||||
|
@ -2289,11 +2211,7 @@ bool unit::resistance_filter_matches(const config& cfg, bool attacker, const std
|
|||
|
||||
int unit::resistance_against(const std::string& damage_name,bool attacker,const map_location& loc) const
|
||||
{
|
||||
int res = 100;
|
||||
|
||||
if (const config &resistance = cfg_.child("resistance")) {
|
||||
res = resistance[damage_name].to_int(100);
|
||||
}
|
||||
int res = movement_type_.resistance_against(damage_name);
|
||||
|
||||
unit_ability_list resistance_abilities = get_abilities("resistance",loc);
|
||||
for (unit_ability_list::iterator i = resistance_abilities.begin(); i != resistance_abilities.end();) {
|
||||
|
@ -2312,26 +2230,6 @@ int unit::resistance_against(const std::string& damage_name,bool attacker,const
|
|||
return res;
|
||||
}
|
||||
|
||||
utils::string_map unit::get_base_resistances() const
|
||||
{
|
||||
if (const config &resistance = cfg_.child("resistance"))
|
||||
{
|
||||
utils::string_map res;
|
||||
BOOST_FOREACH(const config::attribute &i, resistance.attribute_range()) {
|
||||
res[i.first] = i.second;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return utils::string_map();
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::map<terrain_type::TERRAIN,int> unit::movement_type() const
|
||||
{
|
||||
return movement_costs_;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::map<std::string,std::string> unit::advancement_icons() const
|
||||
{
|
||||
std::map<std::string,std::string> temp;
|
||||
|
@ -2431,16 +2329,6 @@ size_t unit::modification_count(const std::string& mod_type, const std::string&
|
|||
return res;
|
||||
}
|
||||
|
||||
/** Helper function for add_modifications */
|
||||
static void mod_mdr_merge(config& dst, const config& mod, bool delta, int minimum)
|
||||
{
|
||||
BOOST_FOREACH(const config::attribute &i, mod.attribute_range()) {
|
||||
int base = delta ? dst[i.first].to_int() : 0;
|
||||
|
||||
dst[i.first] = std::max(minimum, base + i.second.to_int());
|
||||
}
|
||||
}
|
||||
|
||||
void unit::add_modification(const std::string& mod_type, const config& mod, bool no_add)
|
||||
{
|
||||
//some trait activate specific flags
|
||||
|
@ -2635,59 +2523,27 @@ void unit::add_modification(const std::string& mod_type, const config& mod, bool
|
|||
set_state(remove, false);
|
||||
set_poisoned = set_poisoned && remove != "poisoned";
|
||||
}
|
||||
// Note: It would not be hard to define a new "applies_to=" that
|
||||
// combines the next five options (the movetype effects).
|
||||
} else if (apply_to == "movement_costs") {
|
||||
config &mv = cfg_.child_or_add("movement_costs");
|
||||
if (const config &ap = effect.child("movement_costs")) {
|
||||
mod_mdr_merge(mv, ap, !effect["replace"].to_bool(), 1);
|
||||
movement_type_.get_movement().merge(ap, effect["replace"].to_bool());
|
||||
}
|
||||
movement_costs_.clear();
|
||||
} else if (apply_to == "vision_costs") {
|
||||
config &vi = cfg_.child_or_add("vision_costs");
|
||||
if (const config &ap = effect.child("vision_costs")) {
|
||||
mod_mdr_merge(vi, ap, !effect["replace"].to_bool(), 1);
|
||||
movement_type_.get_vision().merge(ap, effect["replace"].to_bool());
|
||||
}
|
||||
vision_costs_.clear();
|
||||
} else if (apply_to == "jamming_costs") {
|
||||
config &jm = cfg_.child_or_add("jamming_costs");
|
||||
if (const config &ap = effect.child("jamming_costs")) {
|
||||
mod_mdr_merge(jm, ap, !effect["replace"].to_bool(), 1);
|
||||
movement_type_.get_jamming().merge(ap, effect["replace"].to_bool());
|
||||
}
|
||||
jamming_costs_.clear();
|
||||
} else if (apply_to == "defense") {
|
||||
config &def = cfg_.child_or_add("defense");
|
||||
if (const config &ap = effect.child("defense")) {
|
||||
bool replace = effect["replace"].to_bool();
|
||||
BOOST_FOREACH(const config::attribute &i, ap.attribute_range()) {
|
||||
// The new value:
|
||||
int value = i.second.to_int(replace ? 100 : 0);
|
||||
// Where the value gets stored:
|
||||
config::attribute_value &dst = def[i.first];
|
||||
|
||||
if ( replace ) {
|
||||
// The new value gets capped between -100 and +100.
|
||||
value = std::max(-100, std::min(value, 100));
|
||||
}
|
||||
else {
|
||||
int old = dst.to_int(100);
|
||||
|
||||
// Add the absolute value of the old value.
|
||||
value += abs(old);
|
||||
// This gets capped between 0 and 100.
|
||||
value = std::max(0, std::min(value, 100));
|
||||
// Restore the sign of the old value.
|
||||
if ( old < 0 )
|
||||
value = -value;
|
||||
}
|
||||
|
||||
// Save this value.
|
||||
dst = value;
|
||||
}
|
||||
movement_type_.get_defense().merge(ap, effect["replace"].to_bool());
|
||||
}
|
||||
defense_mods_.clear();
|
||||
} else if (apply_to == "resistance") {
|
||||
config &mv = cfg_.child_or_add("resistance");
|
||||
if (const config &ap = effect.child("resistance")) {
|
||||
mod_mdr_merge(mv, ap, !effect["replace"].to_bool(), 0);
|
||||
movement_type_.get_resistances().merge(ap, effect["replace"].to_bool());
|
||||
}
|
||||
} else if (apply_to == "zoc") {
|
||||
if (const config::attribute_value *v = effect.get("value")) {
|
||||
|
|
15
src/unit.hpp
15
src/unit.hpp
|
@ -21,7 +21,9 @@
|
|||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
#include "formula_callable.hpp"
|
||||
#include "portrait.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "unit_animation.hpp"
|
||||
#include "unit_types.hpp"
|
||||
#include "unit_map.hpp"
|
||||
|
||||
|
@ -265,7 +267,7 @@ public:
|
|||
|
||||
void set_hidden(bool state);
|
||||
bool get_hidden() const { return hidden_; }
|
||||
bool is_flying() const { return flying_; }
|
||||
bool is_flying() const { return movement_type_.is_flying(); }
|
||||
bool is_fearless() const { return is_fearless_; }
|
||||
bool is_healthy() const { return is_healthy_; }
|
||||
int movement_cost(const t_translation::t_terrain & terrain) const;
|
||||
|
@ -277,8 +279,8 @@ public:
|
|||
{return resistance_against(damage_type.type(), attacker, loc);};
|
||||
|
||||
//return resistances without any abilities applied
|
||||
utils::string_map get_base_resistances() const;
|
||||
// std::map<terrain_type::TERRAIN,int> movement_type() const;
|
||||
utils::string_map get_base_resistances() const { return movement_type_.damage_table(); }
|
||||
const movetype & movement_type() const { return movement_type_; }
|
||||
|
||||
bool can_advance() const { return advances_to_.empty()==false || get_modification_advances().empty() == false; }
|
||||
bool advances() const { return experience_ >= max_experience() && can_advance(); }
|
||||
|
@ -449,12 +451,9 @@ 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
|
||||
int jamming_;
|
||||
mutable std::map<t_translation::t_terrain, int> jamming_costs_; // jamming cost cache
|
||||
mutable defense_cache defense_mods_; // defense modifiers cache
|
||||
movetype movement_type_;
|
||||
bool hold_position_;
|
||||
bool end_turn_;
|
||||
bool resting_;
|
||||
|
@ -482,7 +481,7 @@ private:
|
|||
int unit_value_;
|
||||
map_location goto_, interrupted_move_;
|
||||
|
||||
bool flying_, is_fearless_, is_healthy_;
|
||||
bool is_fearless_, is_healthy_;
|
||||
|
||||
utils::string_map modification_descriptions_;
|
||||
// Animations:
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
#include "gettext.hpp"
|
||||
#include "loadscreen.hpp"
|
||||
#include "log.hpp"
|
||||
#include "map.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "portrait.hpp"
|
||||
#include "unit_animation.hpp"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
@ -337,284 +337,6 @@ bool attack_type::describe_modification(const config& cfg,std::string* descripti
|
|||
}
|
||||
|
||||
|
||||
/* ** unit_movement_type ** */
|
||||
|
||||
|
||||
unit_movement_type::unit_movement_type(const config& cfg, const unit_movement_type* parent) :
|
||||
moveCosts_(),
|
||||
visionCosts_(),
|
||||
jammingCosts_(),
|
||||
defenseMods_(),
|
||||
parent_(parent),
|
||||
cfg_()
|
||||
{
|
||||
//the unit_type give its whole cfg, we don't need all that.
|
||||
//so we filter to keep only keys related to movement_type
|
||||
//FIXME: This helps but it's still not clean, both cfg use a "name" key
|
||||
|
||||
const t_string& name = cfg["name"];
|
||||
if (!name.empty())
|
||||
cfg_["name"]= cfg["name"];
|
||||
|
||||
const t_string& flies = cfg["flies"];
|
||||
if (!flies.empty())
|
||||
cfg_["flies"]= cfg["flies"];
|
||||
|
||||
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 &jamming_costs = cfg.child("jamming_costs"))
|
||||
cfg_.add_child("jamming_costs", jamming_costs);
|
||||
|
||||
if (const config &defense = cfg.child("defense"))
|
||||
cfg_.add_child("defense", defense);
|
||||
|
||||
if (const config &resistance = cfg.child("resistance"))
|
||||
cfg_.add_child("resistance", resistance);
|
||||
}
|
||||
|
||||
unit_movement_type::unit_movement_type(): moveCosts_(), visionCosts_(), jammingCosts_(), defenseMods_(), parent_(NULL), cfg_()
|
||||
{}
|
||||
|
||||
std::string unit_movement_type::name() const
|
||||
{
|
||||
if (!cfg_.has_attribute("name") && parent_)
|
||||
return parent_->name();
|
||||
else
|
||||
return cfg_["name"];
|
||||
}
|
||||
|
||||
int unit_movement_type::resistance_against(const attack_type& attack) const
|
||||
{
|
||||
bool result_found = false;
|
||||
int res = 100;
|
||||
|
||||
if (const config &resistance = cfg_.child("resistance"))
|
||||
{
|
||||
if (const::config::attribute_value *val = resistance.get(attack.type())) {
|
||||
res = *val;
|
||||
result_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!result_found && parent_ != NULL) {
|
||||
res = parent_->resistance_against(attack);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
utils::string_map unit_movement_type::damage_table() const
|
||||
{
|
||||
utils::string_map res;
|
||||
if(parent_ != NULL)
|
||||
res = parent_->damage_table();
|
||||
|
||||
if (const config &resistance = cfg_.child("resistance"))
|
||||
{
|
||||
BOOST_FOREACH(const config::attribute &i, resistance.attribute_range()) {
|
||||
res[i.first] = i.second;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool unit_movement_type::is_flying() const
|
||||
{
|
||||
if (!cfg_.has_attribute("flies") && parent_)
|
||||
return parent_->is_flying();
|
||||
|
||||
return cfg_["flies"].to_bool();
|
||||
}
|
||||
|
||||
int movement_cost_internal(std::map<t_translation::t_terrain, int>& move_costs,
|
||||
const config& cfg, const unit_movement_type* parent,
|
||||
const t_translation::t_terrain & terrain, int recurse_count)
|
||||
{
|
||||
assert(resources::game_map != NULL);
|
||||
const gamemap & map = *resources::game_map;
|
||||
const int impassable = unit_movement_type::UNREACHABLE;
|
||||
|
||||
const std::map<t_translation::t_terrain, int>::const_iterator i = move_costs.find(terrain);
|
||||
|
||||
if (i != move_costs.end()) return i->second;
|
||||
|
||||
// If this is an alias, then select the best of all underlying terrains.
|
||||
const t_translation::t_list& underlying = map.underlying_mvt_terrain(terrain);
|
||||
assert(!underlying.empty());
|
||||
|
||||
if (underlying.size() != 1 || underlying.front() != terrain) {
|
||||
bool revert = (underlying.front() == t_translation::MINUS ? true : false);
|
||||
if (recurse_count >= 100) {
|
||||
ERR_CF << "infinite movement_cost recursion: "
|
||||
<< t_translation::write_terrain_code(terrain)
|
||||
<< " depth " << recurse_count << "\n";
|
||||
move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, impassable));
|
||||
return impassable;
|
||||
}
|
||||
|
||||
int ret_value = revert ? 0 : impassable;
|
||||
for (t_translation::t_list::const_iterator i = underlying.begin();
|
||||
i != underlying.end(); ++i)
|
||||
{
|
||||
if (*i == t_translation::PLUS) {
|
||||
revert = false;
|
||||
continue;
|
||||
} else if (*i == t_translation::MINUS) {
|
||||
revert = true;
|
||||
continue;
|
||||
}
|
||||
const int value = movement_cost_internal(move_costs, cfg,
|
||||
parent, *i, recurse_count + 1);
|
||||
|
||||
if (value < ret_value && !revert) {
|
||||
ret_value = value;
|
||||
} else if (value > ret_value && revert) {
|
||||
ret_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, ret_value));
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
bool result_found = false;
|
||||
int res = impassable;
|
||||
|
||||
if (cfg) {
|
||||
if (underlying.size() != 1) {
|
||||
ERR_CF << "Terrain '" << terrain << "' has "
|
||||
<< underlying.size() << " underlying names - 0 expected.\n";
|
||||
|
||||
move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, impassable));
|
||||
return impassable;
|
||||
}
|
||||
|
||||
const std::string& id = map.get_terrain_info(underlying.front()).id();
|
||||
if (const config::attribute_value *val = cfg.get(id)) {
|
||||
res = *val;
|
||||
result_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result_found && parent != NULL) {
|
||||
res = parent->movement_cost(terrain);
|
||||
}
|
||||
|
||||
if (res <= 0) {
|
||||
WRN_CF << "Terrain '" << terrain << "' has a movement cost of '"
|
||||
<< res << "' which is '<= 0'; resetting to 1.\n";
|
||||
res = 1;
|
||||
}
|
||||
|
||||
move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, res));
|
||||
return res;
|
||||
}
|
||||
|
||||
const defense_range &defense_range_modifier_internal(defense_cache &defense_mods,
|
||||
const config& cfg, const unit_movement_type* parent,
|
||||
const t_translation::t_terrain & terrain, int recurse_count)
|
||||
{
|
||||
assert(resources::game_map != NULL);
|
||||
const gamemap & map = *resources::game_map;
|
||||
|
||||
defense_range dummy = { 0, 100 };
|
||||
std::pair<defense_cache::iterator, bool> ib =
|
||||
defense_mods.insert(defense_cache::value_type(terrain, dummy));
|
||||
if (!ib.second) return ib.first->second;
|
||||
|
||||
defense_range &res = ib.first->second;
|
||||
|
||||
// If this is an alias, then select the best of all underlying terrains.
|
||||
const t_translation::t_list& underlying = map.underlying_def_terrain(terrain);
|
||||
assert(!underlying.empty());
|
||||
|
||||
if (underlying.size() != 1 || underlying.front() != terrain) {
|
||||
bool revert = underlying.front() == t_translation::MINUS;
|
||||
if(recurse_count >= 90) {
|
||||
ERR_CF << "infinite defense_modifier recursion: "
|
||||
<< t_translation::write_terrain_code(terrain)
|
||||
<< " depth " << recurse_count << "\n";
|
||||
}
|
||||
if (recurse_count >= 100) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (revert) {
|
||||
res.max_ = 0;
|
||||
res.min_ = 100;
|
||||
}
|
||||
|
||||
for (t_translation::t_list::const_iterator i = underlying.begin();
|
||||
i != underlying.end(); ++i) {
|
||||
|
||||
if (*i == t_translation::PLUS) {
|
||||
revert = false;
|
||||
continue;
|
||||
} else if (*i == t_translation::MINUS) {
|
||||
revert = true;
|
||||
continue;
|
||||
}
|
||||
const defense_range &inh = defense_range_modifier_internal
|
||||
(defense_mods, cfg, parent, *i, recurse_count + 1);
|
||||
|
||||
if (!revert) {
|
||||
if (inh.max_ < res.max_) res.max_ = inh.max_;
|
||||
if (inh.min_ > res.min_) res.min_ = inh.min_;
|
||||
} else {
|
||||
if (inh.max_ > res.max_) res.max_ = inh.max_;
|
||||
if (inh.min_ < res.min_) res.min_ = inh.min_;
|
||||
}
|
||||
}
|
||||
|
||||
goto check;
|
||||
}
|
||||
|
||||
if (const config& defense = cfg.child("defense"))
|
||||
{
|
||||
const std::string& id = map.get_terrain_info(underlying.front()).id();
|
||||
if (const config::attribute_value *val = defense.get(id)) {
|
||||
int def = *val;
|
||||
if (def >= 0) res.max_ = def;
|
||||
else res.max_ = res.min_ = -def;
|
||||
goto check;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
/* Assign to the reference res to put the value in the defense_cache. */
|
||||
res = parent->defense_range_modifier(terrain);
|
||||
return res;
|
||||
}
|
||||
|
||||
check:
|
||||
|
||||
if (res.min_ < 0) {
|
||||
WRN_CF << "Defense '" << res.min_ << "' is '< 0' reset to 0 (100% defense).\n";
|
||||
res.min_ = 0;
|
||||
}
|
||||
if (res.max_ > 100) {
|
||||
WRN_CF << "Defense '" << res.max_ << "' is '> 100' reset to 100 (0% defense).\n";
|
||||
res.max_ = 100;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int defense_modifier_internal(defense_cache &defense_mods,
|
||||
const config &cfg, const unit_movement_type *parent,
|
||||
const t_translation::t_terrain & terrain, int recurse_count)
|
||||
{
|
||||
const defense_range &def = defense_range_modifier_internal(defense_mods,
|
||||
cfg, parent, terrain, recurse_count);
|
||||
return (std::max)(def.max_, def.min_);
|
||||
}
|
||||
|
||||
|
||||
/* ** unit_type ** */
|
||||
|
||||
|
||||
|
@ -655,7 +377,7 @@ unit_type::unit_type(const unit_type& o) :
|
|||
experience_needed_(o.experience_needed_),
|
||||
in_advancefrom_(o.in_advancefrom_),
|
||||
alignment_(o.alignment_),
|
||||
movementType_(o.movementType_),
|
||||
movement_type_(o.movement_type_),
|
||||
possibleTraits_(o.possibleTraits_),
|
||||
genders_(o.genders_),
|
||||
animations_(o.animations_),
|
||||
|
@ -709,7 +431,7 @@ unit_type::unit_type(const config &cfg, const std::string & parent_id) :
|
|||
experience_needed_(0),
|
||||
in_advancefrom_(false),
|
||||
alignment_(),
|
||||
movementType_(),
|
||||
movement_type_(),
|
||||
possibleTraits_(),
|
||||
genders_(),
|
||||
animations_(),
|
||||
|
@ -795,21 +517,8 @@ void unit_type::build_full(const movement_type_map &mv_types,
|
|||
alpha_ = ftofxp(alpha_blend.to_double());
|
||||
}
|
||||
|
||||
const std::string& move_type = cfg_["movement_type"];
|
||||
|
||||
const movement_type_map::const_iterator it = mv_types.find(move_type);
|
||||
|
||||
if(it != mv_types.end()) {
|
||||
DBG_UT << "setting parent for movement_type " << move_type << "\n";
|
||||
movementType_.set_parent(&(it->second));
|
||||
}
|
||||
else{
|
||||
DBG_UT << "no parent found for movement_type " << move_type << "\n";
|
||||
}
|
||||
|
||||
game_config::add_color_info(cfg_);
|
||||
|
||||
|
||||
BOOST_FOREACH(const config &portrait, cfg_.child_range("portrait")) {
|
||||
portraits_.push_back(tportrait(portrait));
|
||||
}
|
||||
|
@ -906,7 +615,18 @@ void unit_type::build_help_index(const movement_type_map &mv_types,
|
|||
}
|
||||
}
|
||||
|
||||
movementType_ = unit_movement_type(cfg_);
|
||||
// Set the movement type.
|
||||
const std::string move_type = cfg_["movement_type"];
|
||||
const movement_type_map::const_iterator find_it = mv_types.find(move_type);
|
||||
if ( find_it != mv_types.end() ) {
|
||||
DBG_UT << "inheriting from movement_type '" << move_type << "'\n";
|
||||
movement_type_ = find_it->second;
|
||||
}
|
||||
else if ( !move_type.empty() ) {
|
||||
DBG_UT << "movement_type '" << move_type << "' not found\n";
|
||||
}
|
||||
// Override parts of the movement type with what is in our config.
|
||||
movement_type_.merge(cfg_);
|
||||
|
||||
BOOST_FOREACH(const config &t, traits)
|
||||
{
|
||||
|
@ -1335,6 +1055,13 @@ const config & unit_type::build_unit_cfg() const
|
|||
unit_cfg_.clear_children("male");
|
||||
unit_cfg_.clear_children("female");
|
||||
|
||||
// Remove movement type data (it will be received via a movetype object).
|
||||
unit_cfg_.clear_children("movement_costs");
|
||||
unit_cfg_.clear_children("vision_costs");
|
||||
unit_cfg_.clear_children("jamming_costs");
|
||||
unit_cfg_.clear_children("defense");
|
||||
unit_cfg_.clear_children("resistance");
|
||||
|
||||
built_unit_cfg_ = true;
|
||||
return unit_cfg_;
|
||||
}
|
||||
|
@ -1478,9 +1205,7 @@ void unit_type_data::set_config(config &cfg)
|
|||
|
||||
BOOST_FOREACH(const config &mt, cfg.child_range("movetype"))
|
||||
{
|
||||
const unit_movement_type move_type(mt);
|
||||
movement_types_.insert(
|
||||
std::pair<std::string,unit_movement_type>(move_type.name(), move_type));
|
||||
movement_types_.insert(std::make_pair(mt["name"].str(), movetype(mt)));
|
||||
loadscreen::increment_progress();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,17 +15,19 @@
|
|||
#ifndef UNIT_TYPES_H_INCLUDED
|
||||
#define UNIT_TYPES_H_INCLUDED
|
||||
|
||||
#include "unit_animation.hpp"
|
||||
#include "portrait.hpp"
|
||||
#include "map_location.hpp"
|
||||
#include "movetype.hpp"
|
||||
#include "race.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
class gamemap;
|
||||
class unit;
|
||||
class tportrait;
|
||||
class unit_ability_list;
|
||||
class unit_map;
|
||||
class unit_type_data;
|
||||
class unit_animation;
|
||||
|
||||
|
||||
typedef std::map<std::string, movetype> movement_type_map;
|
||||
|
||||
|
||||
//the 'attack type' is the type of attack, how many times it strikes,
|
||||
|
@ -106,82 +108,6 @@ private:
|
|||
int parry_;
|
||||
};
|
||||
|
||||
class unit_movement_type;
|
||||
|
||||
/**
|
||||
* Possible range of the defense. When a single value is needed, #max_
|
||||
* (maximum defense) is selected, unless #min_ is bigger.
|
||||
*/
|
||||
struct defense_range
|
||||
{
|
||||
int min_, max_;
|
||||
};
|
||||
|
||||
typedef std::map<t_translation::t_terrain, defense_range> defense_cache;
|
||||
|
||||
const defense_range &defense_range_modifier_internal(defense_cache &defense_mods,
|
||||
const config &cfg, const unit_movement_type *parent,
|
||||
const t_translation::t_terrain & terrain, int recurse_count = 0);
|
||||
|
||||
int defense_modifier_internal(defense_cache &defense_mods,
|
||||
const config &cfg, const unit_movement_type *parent,
|
||||
const t_translation::t_terrain & terrain, int recurse_count = 0);
|
||||
|
||||
int movement_cost_internal(std::map<t_translation::t_terrain, int> &move_costs,
|
||||
const config &cfg, const unit_movement_type *parent,
|
||||
const t_translation::t_terrain & terrain, int recurse_count = 0);
|
||||
|
||||
//the 'unit movement type' is the basic size of the unit - flying, small land,
|
||||
//large land, etc etc.
|
||||
class unit_movement_type
|
||||
{
|
||||
public:
|
||||
//this move distance means a hex is unreachable
|
||||
//if there is an UNREACHABLE macro declared in the data tree
|
||||
//it should match this value.
|
||||
static const int UNREACHABLE = 99;
|
||||
|
||||
//this class assumes that the passed in reference will remain valid
|
||||
//for at least as long as the class instance
|
||||
explicit unit_movement_type(const config& cfg, const unit_movement_type* parent=NULL);
|
||||
unit_movement_type();
|
||||
|
||||
std::string name() const;
|
||||
int movement_cost(const t_translation::t_terrain & terrain) const
|
||||
{ return movement_cost_internal(moveCosts_, cfg_.child("movement_costs"), parent_, terrain); }
|
||||
int vision_cost(const t_translation::t_terrain & terrain) const
|
||||
{ return movement_cost_internal(visionCosts_, cfg_.child("vision_costs"), parent_, terrain); }
|
||||
int jamming_cost(const t_translation::t_terrain & terrain) const
|
||||
{ return movement_cost_internal(jammingCosts_, cfg_.child("jamming_costs"), parent_, terrain); }
|
||||
int defense_modifier(const t_translation::t_terrain & terrain) const
|
||||
{ return defense_modifier_internal(defenseMods_, cfg_, parent_, terrain); }
|
||||
const defense_range &defense_range_modifier(const t_translation::t_terrain & terrain) const
|
||||
{ return defense_range_modifier_internal(defenseMods_, cfg_, parent_, terrain); }
|
||||
int damage_against(const attack_type& attack) const { return resistance_against(attack); }
|
||||
int resistance_against(const attack_type& attack) const;
|
||||
|
||||
utils::string_map damage_table() const;
|
||||
|
||||
void set_parent(const unit_movement_type* parent) { parent_ = parent; }
|
||||
|
||||
bool is_flying() const;
|
||||
|
||||
const config& get_cfg() const { return cfg_; }
|
||||
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 std::map<t_translation::t_terrain, int> jammingCosts_;
|
||||
|
||||
mutable defense_cache defenseMods_;
|
||||
|
||||
const unit_movement_type* parent_;
|
||||
|
||||
config cfg_;
|
||||
};
|
||||
|
||||
typedef std::map<std::string,unit_movement_type> movement_type_map;
|
||||
|
||||
class unit_type
|
||||
{
|
||||
public:
|
||||
|
@ -274,7 +200,7 @@ public:
|
|||
const std::string& flag_rgb() const { return flag_rgb_; }
|
||||
|
||||
std::vector<attack_type> attacks() const;
|
||||
const unit_movement_type& movement_type() const { return movementType_; }
|
||||
const movetype & movement_type() const { return movement_type_; }
|
||||
|
||||
int experience_needed(bool with_acceleration=true) const;
|
||||
|
||||
|
@ -387,7 +313,7 @@ private:
|
|||
|
||||
ALIGNMENT alignment_;
|
||||
|
||||
unit_movement_type movementType_;
|
||||
movetype movement_type_;
|
||||
|
||||
config possibleTraits_;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue