mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-04 16:17:59 +00:00
put unit attack_type in new file.
This commit is contained in:
parent
637514e523
commit
06228f09ec
@ -1001,6 +1001,7 @@ set(wesnoth-main_SRC
|
||||
unit_abilities.cpp
|
||||
unit_animation.cpp
|
||||
unit_animation_component.cpp
|
||||
unit_attack_type.cpp
|
||||
unit_display.cpp
|
||||
unit_drawer.cpp
|
||||
unit_filter.cpp
|
||||
|
@ -572,6 +572,7 @@ wesnoth_sources = Split("""
|
||||
unit_abilities.cpp
|
||||
unit_animation.cpp
|
||||
unit_animation_component.cpp
|
||||
unit_attack_type.cpp
|
||||
unit_display.cpp
|
||||
unit_drawer.cpp
|
||||
unit_filter.cpp
|
||||
|
349
src/unit_attack_type.cpp
Normal file
349
src/unit_attack_type.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
Copyright (C) 2003 - 2014 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 unit-type specific attributes, animations, advancement.
|
||||
*/
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include "unit_attack_type.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "gettext.hpp"
|
||||
#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)
|
||||
#define LOG_CONFIG LOG_STREAM(info, log_config)
|
||||
#define DBG_CF LOG_STREAM(debug, log_config)
|
||||
|
||||
static lg::log_domain log_unit("unit");
|
||||
#define DBG_UT LOG_STREAM(debug, log_unit)
|
||||
#define ERR_UT LOG_STREAM(err, log_unit)
|
||||
|
||||
|
||||
/* ** attack_type ** */
|
||||
|
||||
|
||||
attack_type::attack_type(const config& cfg) :
|
||||
self_loc_(),
|
||||
other_loc_(),
|
||||
is_attacker_(false),
|
||||
other_attack_(NULL),
|
||||
cfg_(cfg),
|
||||
description_(cfg["description"].t_str()),
|
||||
id_(cfg["name"]),
|
||||
type_(cfg["type"]),
|
||||
icon_(cfg["icon"]),
|
||||
range_(cfg["range"]),
|
||||
min_range_(cfg["min_range"].to_int(1)),
|
||||
max_range_(cfg["max_range"].to_int(1)),
|
||||
damage_(cfg["damage"]),
|
||||
num_attacks_(cfg["number"]),
|
||||
attack_weight_(cfg["attack_weight"].to_double(1.0)),
|
||||
defense_weight_(cfg["defense_weight"].to_double(1.0)),
|
||||
accuracy_(cfg["accuracy"]),
|
||||
movement_used_(cfg["movement_used"].to_int(100000)),
|
||||
parry_(cfg["parry"])
|
||||
|
||||
{
|
||||
if (description_.empty())
|
||||
description_ = translation::egettext(id_.c_str());
|
||||
|
||||
if(icon_.empty()){
|
||||
if (id_ != "")
|
||||
icon_ = "attacks/" + id_ + ".png";
|
||||
else
|
||||
icon_ = "attacks/blank-attack.png";
|
||||
}
|
||||
}
|
||||
|
||||
attack_type::~attack_type()
|
||||
{
|
||||
}
|
||||
|
||||
std::string attack_type::accuracy_parry_description() const
|
||||
{
|
||||
if(accuracy_ == 0 && parry_ == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << utils::signed_percent(accuracy_);
|
||||
|
||||
if(parry_ != 0) {
|
||||
s << "/" << utils::signed_percent(parry_);
|
||||
}
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not *this matches the given @a filter, ignoring the
|
||||
* complexities introduced by [and], [or], and [not].
|
||||
*/
|
||||
static bool matches_simple_filter(const attack_type & attack, const config & filter)
|
||||
{
|
||||
const std::vector<std::string>& filter_range = utils::split(filter["range"]);
|
||||
const std::string& filter_damage = filter["damage"];
|
||||
const std::vector<std::string> filter_name = utils::split(filter["name"]);
|
||||
const std::vector<std::string> filter_type = utils::split(filter["type"]);
|
||||
const std::string filter_special = filter["special"];
|
||||
|
||||
if ( !filter_range.empty() && std::find(filter_range.begin(), filter_range.end(), attack.range()) == filter_range.end() )
|
||||
return false;
|
||||
|
||||
if ( !filter_damage.empty() && !in_ranges(attack.damage(), utils::parse_ranges(filter_damage)) )
|
||||
return false;
|
||||
|
||||
if ( !filter_name.empty() && std::find(filter_name.begin(), filter_name.end(), attack.id()) == filter_name.end() )
|
||||
return false;
|
||||
|
||||
if ( !filter_type.empty() && std::find(filter_type.begin(), filter_type.end(), attack.type()) == filter_type.end() )
|
||||
return false;
|
||||
|
||||
if ( !filter_special.empty() && !attack.get_special_bool(filter_special, true) )
|
||||
return false;
|
||||
|
||||
// Passed all tests.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not *this matches the given @a filter.
|
||||
*/
|
||||
bool attack_type::matches_filter(const config& filter) const
|
||||
{
|
||||
// Handle the basic filter.
|
||||
bool matches = matches_simple_filter(*this, filter);
|
||||
|
||||
// Handle [and], [or], and [not] with in-order precedence
|
||||
BOOST_FOREACH( const config::any_child &condition, filter.all_children_range() )
|
||||
{
|
||||
// Handle [and]
|
||||
if ( condition.key == "and" )
|
||||
matches = matches && matches_filter(condition.cfg);
|
||||
|
||||
// Handle [or]
|
||||
else if ( condition.key == "or" )
|
||||
matches = matches || matches_filter(condition.cfg);
|
||||
|
||||
// Handle [not]
|
||||
else if ( condition.key == "not" )
|
||||
matches = matches && !matches_filter(condition.cfg);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void add_and(std::stringstream &ss) {
|
||||
if(ss.tellp() > 0)
|
||||
ss << t_string(N_(" and "), "wesnoth");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies *this using the specifications in @a cfg, but only if *this matches
|
||||
* @a cfg viewed as a filter.
|
||||
*
|
||||
* If *description is provided, it will be set to a (translated) description
|
||||
* of the modification(s) applied (currently only changes to the number of
|
||||
* strikes, damage, accuracy, and parry are included in this description).
|
||||
*
|
||||
* @returns whether or not @c this matched the @a cfg as a filter.
|
||||
*/
|
||||
bool attack_type::apply_modification(const config& cfg,std::string* description)
|
||||
{
|
||||
if( !matches_filter(cfg) )
|
||||
return false;
|
||||
|
||||
const std::string& set_name = cfg["set_name"];
|
||||
const t_string& set_desc = cfg["set_description"];
|
||||
const std::string& set_type = cfg["set_type"];
|
||||
const std::string& set_icon = cfg["set_icon"];
|
||||
const std::string& del_specials = cfg["remove_specials"];
|
||||
const config &set_specials = cfg.child("set_specials");
|
||||
const std::string& increase_damage = cfg["increase_damage"];
|
||||
const std::string& increase_attacks = cfg["increase_attacks"];
|
||||
const std::string& set_attack_weight = cfg["attack_weight"];
|
||||
const std::string& set_defense_weight = cfg["defense_weight"];
|
||||
const std::string& increase_accuracy = cfg["increase_accuracy"];
|
||||
const std::string& increase_parry = cfg["increase_parry"];
|
||||
|
||||
std::stringstream desc;
|
||||
|
||||
if(set_name.empty() == false) {
|
||||
id_ = set_name;
|
||||
cfg_["name"] = id_;
|
||||
}
|
||||
|
||||
if(set_desc.empty() == false) {
|
||||
description_ = set_desc;
|
||||
cfg_["description"] = description_;
|
||||
}
|
||||
|
||||
if(set_type.empty() == false) {
|
||||
type_ = set_type;
|
||||
cfg_["type"] = type_;
|
||||
}
|
||||
|
||||
if(set_icon.empty() == false) {
|
||||
icon_ = set_icon;
|
||||
cfg_["icon"] = icon_;
|
||||
}
|
||||
|
||||
if(del_specials.empty() == false) {
|
||||
const std::vector<std::string>& dsl = utils::split(del_specials);
|
||||
if (config &specials = cfg_.child("specials"))
|
||||
{
|
||||
config new_specials;
|
||||
BOOST_FOREACH(const config::any_child &vp, specials.all_children_range()) {
|
||||
std::vector<std::string>::const_iterator found_id =
|
||||
std::find(dsl.begin(), dsl.end(), vp.cfg["id"].str());
|
||||
if (found_id == dsl.end()) {
|
||||
new_specials.add_child(vp.key, vp.cfg);
|
||||
}
|
||||
}
|
||||
cfg_.clear_children("specials");
|
||||
cfg_.add_child("specials",new_specials);
|
||||
}
|
||||
}
|
||||
|
||||
if (set_specials) {
|
||||
const std::string &mode = set_specials["mode"];
|
||||
if (mode != "append") {
|
||||
cfg_.clear_children("specials");
|
||||
}
|
||||
config &new_specials = cfg_.child_or_add("specials");
|
||||
BOOST_FOREACH(const config::any_child &value, set_specials.all_children_range()) {
|
||||
new_specials.add_child(value.key, value.cfg);
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_damage.empty() == false) {
|
||||
damage_ = utils::apply_modifier(damage_, increase_damage, 0);
|
||||
if (damage_ < 0) {
|
||||
damage_ = 0;
|
||||
}
|
||||
cfg_["damage"] = damage_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_damage = lexical_cast<int>(increase_damage);
|
||||
desc << utils::print_modifier(increase_damage) << " "
|
||||
<< _n("damage","damage", inc_damage);
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_attacks.empty() == false) {
|
||||
num_attacks_ = utils::apply_modifier(num_attacks_, increase_attacks, 1);
|
||||
cfg_["number"] = num_attacks_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_attacks = lexical_cast<int>(increase_attacks);
|
||||
desc << utils::print_modifier(increase_attacks) << " "
|
||||
<< _n("strike", "strikes", inc_attacks);
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_accuracy.empty() == false) {
|
||||
accuracy_ = utils::apply_modifier(accuracy_, increase_accuracy, 1);
|
||||
cfg_["accuracy"] = accuracy_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_acc = lexical_cast<int>(increase_accuracy);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_parry.empty() == false) {
|
||||
parry_ = utils::apply_modifier(parry_, increase_parry, 1);
|
||||
cfg_["parry"] = parry_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_parry = lexical_cast<int>(increase_parry);
|
||||
// xgettext:no-c-format
|
||||
desc << utils::signed_value(inc_parry) << _("% parry");
|
||||
}
|
||||
}
|
||||
|
||||
if(set_attack_weight.empty() == false) {
|
||||
attack_weight_ = lexical_cast_default<double>(set_attack_weight,1.0);
|
||||
cfg_["attack_weight"] = attack_weight_;
|
||||
}
|
||||
|
||||
if(set_defense_weight.empty() == false) {
|
||||
defense_weight_ = lexical_cast_default<double>(set_defense_weight,1.0);
|
||||
cfg_["defense_weight"] = defense_weight_;
|
||||
}
|
||||
|
||||
if(description != NULL) {
|
||||
*description = desc.str();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trimmed down version of apply_modification(), with no modifications actually
|
||||
* made. This can be used to get a description of the modification(s) specified
|
||||
* by @a cfg (if *this matches cfg as a filter).
|
||||
*
|
||||
* If *description is provided, it will be set to a (translated) description
|
||||
* of the modification(s) that would be applied to the number of strikes
|
||||
* and damage.
|
||||
*
|
||||
* @returns whether or not @c this matched the @a cfg as a filter.
|
||||
*/
|
||||
bool attack_type::describe_modification(const config& cfg,std::string* description)
|
||||
{
|
||||
if( !matches_filter(cfg) )
|
||||
return false;
|
||||
|
||||
// Did the caller want the description?
|
||||
if(description != NULL) {
|
||||
const std::string& increase_damage = cfg["increase_damage"];
|
||||
const std::string& increase_attacks = cfg["increase_attacks"];
|
||||
|
||||
std::stringstream desc;
|
||||
|
||||
if(increase_damage.empty() == false) {
|
||||
add_and(desc);
|
||||
int inc_damage = lexical_cast<int>(increase_damage);
|
||||
desc << utils::print_modifier(increase_damage) << " "
|
||||
<< _n("damage","damage", inc_damage);
|
||||
}
|
||||
|
||||
if(increase_attacks.empty() == false) {
|
||||
add_and(desc);
|
||||
int inc_attacks = lexical_cast<int>(increase_attacks);
|
||||
desc << utils::print_modifier(increase_attacks) << " "
|
||||
<< _n("strike", "strikes", inc_attacks);
|
||||
}
|
||||
|
||||
*description = desc.str();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
107
src/unit_attack_type.hpp
Normal file
107
src/unit_attack_type.hpp
Normal file
@ -0,0 +1,107 @@
|
||||
|
||||
//the 'attack type' is the type of attack, how many times it strikes,
|
||||
/*
|
||||
Copyright (C) 2003 - 2014 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 UNIT_ATTACK_TYPE_H_INCLUDED
|
||||
#define UNIT_ATTACK_TYPE_H_INCLUDED
|
||||
|
||||
#include "map_location.hpp"
|
||||
#include "util.hpp"
|
||||
#include "tstring.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class unit_ability_list;
|
||||
|
||||
//the 'attack type' is the type of attack, how many times it strikes,
|
||||
//and how much damage it does.
|
||||
class attack_type
|
||||
{
|
||||
public:
|
||||
|
||||
explicit attack_type(const config& cfg);
|
||||
/** Default implementation, but defined out-of-line for efficiency reasons. */
|
||||
~attack_type();
|
||||
const t_string& name() const { return description_; }
|
||||
const std::string& id() const { return id_; }
|
||||
const std::string& type() const { return type_; }
|
||||
const std::string& icon() const { return icon_; }
|
||||
const std::string& range() const { return range_; }
|
||||
int min_range() const { return min_range_; }
|
||||
int max_range() const { return max_range_; }
|
||||
std::string accuracy_parry_description() const;
|
||||
int accuracy() const { return accuracy_; }
|
||||
int parry() const { return parry_; }
|
||||
int damage() const { return damage_; }
|
||||
int num_attacks() const { return num_attacks_; }
|
||||
double attack_weight() const { return attack_weight_; }
|
||||
double defense_weight() const { return defense_weight_; }
|
||||
|
||||
// In unit_abilities.cpp:
|
||||
|
||||
bool get_special_bool(const std::string& special, bool simple_check=false) const;
|
||||
unit_ability_list get_specials(const std::string& special) const;
|
||||
std::vector<std::pair<t_string, t_string> > special_tooltips(std::vector<bool> *active_list=NULL) const;
|
||||
std::string weapon_specials(bool only_active=false, bool is_backstab=false) const;
|
||||
void set_specials_context(const map_location& unit_loc, const map_location& other_loc,
|
||||
bool attacking, const attack_type *other_attack) const;
|
||||
void set_specials_context(const map_location& loc, bool attacking = true) const;
|
||||
|
||||
/// Calculates the number of attacks this weapon has, considering specials.
|
||||
void modified_attacks(bool is_backstab, unsigned & min_attacks,
|
||||
unsigned & max_attacks) const;
|
||||
/// Returns the damage per attack of this weapon, considering specials.
|
||||
int modified_damage(bool is_backstab) const;
|
||||
|
||||
// In unit_types.cpp:
|
||||
|
||||
bool matches_filter(const config& filter) const;
|
||||
bool apply_modification(const config& cfg,std::string* description);
|
||||
bool describe_modification(const config& cfg,std::string* description);
|
||||
|
||||
int movement_used() const { return movement_used_; }
|
||||
|
||||
const config& get_cfg() const { return cfg_; }
|
||||
|
||||
private:
|
||||
// In unit_abilities.cpp:
|
||||
|
||||
// Configured as a bit field, in case that is useful.
|
||||
enum AFFECTS { AFFECT_SELF=1, AFFECT_OTHER=2, AFFECT_EITHER=3 };
|
||||
bool special_active(const config& special, AFFECTS whom,
|
||||
bool include_backstab=true) const;
|
||||
|
||||
// Used via set_specials_context() to control which specials are
|
||||
// considered active.
|
||||
mutable map_location self_loc_, other_loc_;
|
||||
mutable bool is_attacker_;
|
||||
mutable const attack_type* other_attack_;
|
||||
|
||||
config cfg_;
|
||||
t_string description_;
|
||||
std::string id_;
|
||||
std::string type_;
|
||||
std::string icon_;
|
||||
std::string range_;
|
||||
int min_range_, max_range_;
|
||||
int damage_;
|
||||
int num_attacks_;
|
||||
double attack_weight_;
|
||||
double defense_weight_;
|
||||
|
||||
int accuracy_;
|
||||
int movement_used_;
|
||||
int parry_;
|
||||
};
|
||||
#endif
|
@ -45,318 +45,6 @@ static lg::log_domain log_unit("unit");
|
||||
#define ERR_UT LOG_STREAM(err, log_unit)
|
||||
|
||||
|
||||
/* ** attack_type ** */
|
||||
|
||||
|
||||
attack_type::attack_type(const config& cfg) :
|
||||
self_loc_(),
|
||||
other_loc_(),
|
||||
is_attacker_(false),
|
||||
other_attack_(NULL),
|
||||
cfg_(cfg),
|
||||
description_(cfg["description"].t_str()),
|
||||
id_(cfg["name"]),
|
||||
type_(cfg["type"]),
|
||||
icon_(cfg["icon"]),
|
||||
range_(cfg["range"]),
|
||||
min_range_(cfg["min_range"].to_int(1)),
|
||||
max_range_(cfg["max_range"].to_int(1)),
|
||||
damage_(cfg["damage"]),
|
||||
num_attacks_(cfg["number"]),
|
||||
attack_weight_(cfg["attack_weight"].to_double(1.0)),
|
||||
defense_weight_(cfg["defense_weight"].to_double(1.0)),
|
||||
accuracy_(cfg["accuracy"]),
|
||||
movement_used_(cfg["movement_used"].to_int(100000)),
|
||||
parry_(cfg["parry"])
|
||||
|
||||
{
|
||||
if (description_.empty())
|
||||
description_ = translation::egettext(id_.c_str());
|
||||
|
||||
if(icon_.empty()){
|
||||
if (id_ != "")
|
||||
icon_ = "attacks/" + id_ + ".png";
|
||||
else
|
||||
icon_ = "attacks/blank-attack.png";
|
||||
}
|
||||
}
|
||||
|
||||
attack_type::~attack_type()
|
||||
{
|
||||
}
|
||||
|
||||
std::string attack_type::accuracy_parry_description() const
|
||||
{
|
||||
if(accuracy_ == 0 && parry_ == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << utils::signed_percent(accuracy_);
|
||||
|
||||
if(parry_ != 0) {
|
||||
s << "/" << utils::signed_percent(parry_);
|
||||
}
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not *this matches the given @a filter, ignoring the
|
||||
* complexities introduced by [and], [or], and [not].
|
||||
*/
|
||||
static bool matches_simple_filter(const attack_type & attack, const config & filter)
|
||||
{
|
||||
const std::vector<std::string>& filter_range = utils::split(filter["range"]);
|
||||
const std::string& filter_damage = filter["damage"];
|
||||
const std::vector<std::string> filter_name = utils::split(filter["name"]);
|
||||
const std::vector<std::string> filter_type = utils::split(filter["type"]);
|
||||
const std::string filter_special = filter["special"];
|
||||
|
||||
if ( !filter_range.empty() && std::find(filter_range.begin(), filter_range.end(), attack.range()) == filter_range.end() )
|
||||
return false;
|
||||
|
||||
if ( !filter_damage.empty() && !in_ranges(attack.damage(), utils::parse_ranges(filter_damage)) )
|
||||
return false;
|
||||
|
||||
if ( !filter_name.empty() && std::find(filter_name.begin(), filter_name.end(), attack.id()) == filter_name.end() )
|
||||
return false;
|
||||
|
||||
if ( !filter_type.empty() && std::find(filter_type.begin(), filter_type.end(), attack.type()) == filter_type.end() )
|
||||
return false;
|
||||
|
||||
if ( !filter_special.empty() && !attack.get_special_bool(filter_special, true) )
|
||||
return false;
|
||||
|
||||
// Passed all tests.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not *this matches the given @a filter.
|
||||
*/
|
||||
bool attack_type::matches_filter(const config& filter) const
|
||||
{
|
||||
// Handle the basic filter.
|
||||
bool matches = matches_simple_filter(*this, filter);
|
||||
|
||||
// Handle [and], [or], and [not] with in-order precedence
|
||||
BOOST_FOREACH( const config::any_child &condition, filter.all_children_range() )
|
||||
{
|
||||
// Handle [and]
|
||||
if ( condition.key == "and" )
|
||||
matches = matches && matches_filter(condition.cfg);
|
||||
|
||||
// Handle [or]
|
||||
else if ( condition.key == "or" )
|
||||
matches = matches || matches_filter(condition.cfg);
|
||||
|
||||
// Handle [not]
|
||||
else if ( condition.key == "not" )
|
||||
matches = matches && !matches_filter(condition.cfg);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void add_and(std::stringstream &ss) {
|
||||
if(ss.tellp() > 0)
|
||||
ss << t_string(N_(" and "), "wesnoth");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies *this using the specifications in @a cfg, but only if *this matches
|
||||
* @a cfg viewed as a filter.
|
||||
*
|
||||
* If *description is provided, it will be set to a (translated) description
|
||||
* of the modification(s) applied (currently only changes to the number of
|
||||
* strikes, damage, accuracy, and parry are included in this description).
|
||||
*
|
||||
* @returns whether or not @c this matched the @a cfg as a filter.
|
||||
*/
|
||||
bool attack_type::apply_modification(const config& cfg,std::string* description)
|
||||
{
|
||||
if( !matches_filter(cfg) )
|
||||
return false;
|
||||
|
||||
const std::string& set_name = cfg["set_name"];
|
||||
const t_string& set_desc = cfg["set_description"];
|
||||
const std::string& set_type = cfg["set_type"];
|
||||
const std::string& set_icon = cfg["set_icon"];
|
||||
const std::string& del_specials = cfg["remove_specials"];
|
||||
const config &set_specials = cfg.child("set_specials");
|
||||
const std::string& increase_damage = cfg["increase_damage"];
|
||||
const std::string& increase_attacks = cfg["increase_attacks"];
|
||||
const std::string& set_attack_weight = cfg["attack_weight"];
|
||||
const std::string& set_defense_weight = cfg["defense_weight"];
|
||||
const std::string& increase_accuracy = cfg["increase_accuracy"];
|
||||
const std::string& increase_parry = cfg["increase_parry"];
|
||||
|
||||
std::stringstream desc;
|
||||
|
||||
if(set_name.empty() == false) {
|
||||
id_ = set_name;
|
||||
cfg_["name"] = id_;
|
||||
}
|
||||
|
||||
if(set_desc.empty() == false) {
|
||||
description_ = set_desc;
|
||||
cfg_["description"] = description_;
|
||||
}
|
||||
|
||||
if(set_type.empty() == false) {
|
||||
type_ = set_type;
|
||||
cfg_["type"] = type_;
|
||||
}
|
||||
|
||||
if(set_icon.empty() == false) {
|
||||
icon_ = set_icon;
|
||||
cfg_["icon"] = icon_;
|
||||
}
|
||||
|
||||
if(del_specials.empty() == false) {
|
||||
const std::vector<std::string>& dsl = utils::split(del_specials);
|
||||
if (config &specials = cfg_.child("specials"))
|
||||
{
|
||||
config new_specials;
|
||||
BOOST_FOREACH(const config::any_child &vp, specials.all_children_range()) {
|
||||
std::vector<std::string>::const_iterator found_id =
|
||||
std::find(dsl.begin(), dsl.end(), vp.cfg["id"].str());
|
||||
if (found_id == dsl.end()) {
|
||||
new_specials.add_child(vp.key, vp.cfg);
|
||||
}
|
||||
}
|
||||
cfg_.clear_children("specials");
|
||||
cfg_.add_child("specials",new_specials);
|
||||
}
|
||||
}
|
||||
|
||||
if (set_specials) {
|
||||
const std::string &mode = set_specials["mode"];
|
||||
if (mode != "append") {
|
||||
cfg_.clear_children("specials");
|
||||
}
|
||||
config &new_specials = cfg_.child_or_add("specials");
|
||||
BOOST_FOREACH(const config::any_child &value, set_specials.all_children_range()) {
|
||||
new_specials.add_child(value.key, value.cfg);
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_damage.empty() == false) {
|
||||
damage_ = utils::apply_modifier(damage_, increase_damage, 0);
|
||||
if (damage_ < 0) {
|
||||
damage_ = 0;
|
||||
}
|
||||
cfg_["damage"] = damage_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_damage = lexical_cast<int>(increase_damage);
|
||||
desc << utils::print_modifier(increase_damage) << " "
|
||||
<< _n("damage","damage", inc_damage);
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_attacks.empty() == false) {
|
||||
num_attacks_ = utils::apply_modifier(num_attacks_, increase_attacks, 1);
|
||||
cfg_["number"] = num_attacks_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_attacks = lexical_cast<int>(increase_attacks);
|
||||
desc << utils::print_modifier(increase_attacks) << " "
|
||||
<< _n("strike", "strikes", inc_attacks);
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_accuracy.empty() == false) {
|
||||
accuracy_ = utils::apply_modifier(accuracy_, increase_accuracy, 1);
|
||||
cfg_["accuracy"] = accuracy_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_acc = lexical_cast<int>(increase_accuracy);
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
if(increase_parry.empty() == false) {
|
||||
parry_ = utils::apply_modifier(parry_, increase_parry, 1);
|
||||
cfg_["parry"] = parry_;
|
||||
|
||||
if(description != NULL) {
|
||||
add_and(desc);
|
||||
int inc_parry = lexical_cast<int>(increase_parry);
|
||||
// xgettext:no-c-format
|
||||
desc << utils::signed_value(inc_parry) << _("% parry");
|
||||
}
|
||||
}
|
||||
|
||||
if(set_attack_weight.empty() == false) {
|
||||
attack_weight_ = lexical_cast_default<double>(set_attack_weight,1.0);
|
||||
cfg_["attack_weight"] = attack_weight_;
|
||||
}
|
||||
|
||||
if(set_defense_weight.empty() == false) {
|
||||
defense_weight_ = lexical_cast_default<double>(set_defense_weight,1.0);
|
||||
cfg_["defense_weight"] = defense_weight_;
|
||||
}
|
||||
|
||||
if(description != NULL) {
|
||||
*description = desc.str();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trimmed down version of apply_modification(), with no modifications actually
|
||||
* made. This can be used to get a description of the modification(s) specified
|
||||
* by @a cfg (if *this matches cfg as a filter).
|
||||
*
|
||||
* If *description is provided, it will be set to a (translated) description
|
||||
* of the modification(s) that would be applied to the number of strikes
|
||||
* and damage.
|
||||
*
|
||||
* @returns whether or not @c this matched the @a cfg as a filter.
|
||||
*/
|
||||
bool attack_type::describe_modification(const config& cfg,std::string* description)
|
||||
{
|
||||
if( !matches_filter(cfg) )
|
||||
return false;
|
||||
|
||||
// Did the caller want the description?
|
||||
if(description != NULL) {
|
||||
const std::string& increase_damage = cfg["increase_damage"];
|
||||
const std::string& increase_attacks = cfg["increase_attacks"];
|
||||
|
||||
std::stringstream desc;
|
||||
|
||||
if(increase_damage.empty() == false) {
|
||||
add_and(desc);
|
||||
int inc_damage = lexical_cast<int>(increase_damage);
|
||||
desc << utils::print_modifier(increase_damage) << " "
|
||||
<< _n("damage","damage", inc_damage);
|
||||
}
|
||||
|
||||
if(increase_attacks.empty() == false) {
|
||||
add_and(desc);
|
||||
int inc_attacks = lexical_cast<int>(increase_attacks);
|
||||
desc << utils::print_modifier(increase_attacks) << " "
|
||||
<< _n("strike", "strikes", inc_attacks);
|
||||
}
|
||||
|
||||
*description = desc.str();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ** unit_type ** */
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "map_location.hpp"
|
||||
#include "movetype.hpp"
|
||||
#include "race.hpp"
|
||||
#include "unit_attack_type.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
@ -35,87 +36,6 @@ 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,
|
||||
//and how much damage it does.
|
||||
class attack_type
|
||||
{
|
||||
public:
|
||||
|
||||
explicit attack_type(const config& cfg);
|
||||
/** Default implementation, but defined out-of-line for efficiency reasons. */
|
||||
~attack_type();
|
||||
const t_string& name() const { return description_; }
|
||||
const std::string& id() const { return id_; }
|
||||
const std::string& type() const { return type_; }
|
||||
const std::string& icon() const { return icon_; }
|
||||
const std::string& range() const { return range_; }
|
||||
int min_range() const { return min_range_; }
|
||||
int max_range() const { return max_range_; }
|
||||
std::string accuracy_parry_description() const;
|
||||
int accuracy() const { return accuracy_; }
|
||||
int parry() const { return parry_; }
|
||||
int damage() const { return damage_; }
|
||||
int num_attacks() const { return num_attacks_; }
|
||||
double attack_weight() const { return attack_weight_; }
|
||||
double defense_weight() const { return defense_weight_; }
|
||||
|
||||
// In unit_abilities.cpp:
|
||||
|
||||
bool get_special_bool(const std::string& special, bool simple_check=false) const;
|
||||
unit_ability_list get_specials(const std::string& special) const;
|
||||
std::vector<std::pair<t_string, t_string> > special_tooltips(std::vector<bool> *active_list=NULL) const;
|
||||
std::string weapon_specials(bool only_active=false, bool is_backstab=false) const;
|
||||
void set_specials_context(const map_location& unit_loc, const map_location& other_loc,
|
||||
bool attacking, const attack_type *other_attack) const;
|
||||
void set_specials_context(const map_location& loc, bool attacking = true) const;
|
||||
|
||||
/// Calculates the number of attacks this weapon has, considering specials.
|
||||
void modified_attacks(bool is_backstab, unsigned & min_attacks,
|
||||
unsigned & max_attacks) const;
|
||||
/// Returns the damage per attack of this weapon, considering specials.
|
||||
int modified_damage(bool is_backstab) const;
|
||||
|
||||
// In unit_types.cpp:
|
||||
|
||||
bool matches_filter(const config& filter) const;
|
||||
bool apply_modification(const config& cfg,std::string* description);
|
||||
bool describe_modification(const config& cfg,std::string* description);
|
||||
|
||||
int movement_used() const { return movement_used_; }
|
||||
|
||||
const config& get_cfg() const { return cfg_; }
|
||||
|
||||
private:
|
||||
// In unit_abilities.cpp:
|
||||
|
||||
// Configured as a bit field, in case that is useful.
|
||||
enum AFFECTS { AFFECT_SELF=1, AFFECT_OTHER=2, AFFECT_EITHER=3 };
|
||||
bool special_active(const config& special, AFFECTS whom,
|
||||
bool include_backstab=true) const;
|
||||
|
||||
// Used via set_specials_context() to control which specials are
|
||||
// considered active.
|
||||
mutable map_location self_loc_, other_loc_;
|
||||
mutable bool is_attacker_;
|
||||
mutable const attack_type* other_attack_;
|
||||
|
||||
config cfg_;
|
||||
t_string description_;
|
||||
std::string id_;
|
||||
std::string type_;
|
||||
std::string icon_;
|
||||
std::string range_;
|
||||
int min_range_, max_range_;
|
||||
int damage_;
|
||||
int num_attacks_;
|
||||
double attack_weight_;
|
||||
double defense_weight_;
|
||||
|
||||
int accuracy_;
|
||||
int movement_used_;
|
||||
int parry_;
|
||||
};
|
||||
|
||||
class unit_type
|
||||
{
|
||||
public:
|
||||
|
Loading…
x
Reference in New Issue
Block a user