mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-24 19:23:53 +00:00
558 lines
20 KiB
C++
558 lines
20 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003 - 2009 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 version 2
|
|
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 unit.hpp */
|
|
|
|
#ifndef UNIT_H_INCLUDED
|
|
#define UNIT_H_INCLUDED
|
|
|
|
#include "config.hpp"
|
|
#include "formula_callable.hpp"
|
|
#include "map_location.hpp"
|
|
#include "portrait.hpp"
|
|
#include "race.hpp"
|
|
#include "team.hpp"
|
|
#include "unit_types.hpp"
|
|
#include "unit_map.hpp"
|
|
#include "variable.hpp"
|
|
#include "game_display.hpp"
|
|
|
|
class gamestatus;
|
|
class game_state;
|
|
class config_writer;
|
|
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
class unit_ability_list
|
|
{
|
|
public:
|
|
unit_ability_list() :
|
|
cfgs()
|
|
{
|
|
}
|
|
|
|
bool empty() const;
|
|
|
|
std::pair<int,map_location> highest(const std::string& key, int def=0) const;
|
|
std::pair<int,map_location> lowest(const std::string& key, int def=100) const;
|
|
|
|
std::vector<std::pair<const config *, map_location> > cfgs;
|
|
};
|
|
|
|
|
|
class unit
|
|
{
|
|
public:
|
|
/**
|
|
* Clear the unit status cache for all units. Currently only the hidden
|
|
* status of units is cached this way.
|
|
*/
|
|
static void clear_status_caches();
|
|
|
|
friend struct unit_movement_resetter;
|
|
// Copy constructor
|
|
unit(const unit& u);
|
|
/** Initilizes a unit from a config */
|
|
unit(const config& cfg, bool use_traits=false);
|
|
unit(unit_map* unitmap, const gamemap* map,
|
|
const gamestatus* game_status, const std::vector<team>* teams,
|
|
const config& cfg, bool use_traits=false, game_state* state = 0);
|
|
/** Initializes a unit from a unit type */
|
|
unit(const unit_type* t, int side, bool use_traits=false, bool dummy_unit=false, unit_race::GENDER gender=unit_race::MALE, std::string variation="");
|
|
unit(unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams, const unit_type* t, int side, bool use_traits=false, bool dummy_unit=false, unit_race::GENDER gender=unit_race::MALE, std::string variation="", bool force_gender=false);
|
|
virtual ~unit();
|
|
unit& operator=(const unit&);
|
|
|
|
void set_game_context(unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams);
|
|
|
|
/** Advances this unit to another type */
|
|
void advance_to(const unit_type* t, bool use_traits=false, game_state* state = 0);
|
|
const std::vector<std::string>& advances_to() const { return advances_to_; }
|
|
|
|
/** The type id of the unit */
|
|
const std::string& type_id() const { return type_; }
|
|
const unit_type* type() const;
|
|
|
|
/** id assigned by wml */
|
|
const std::string& id() const { if (id_.empty()) return type_name(); else return id_; }
|
|
/** The unique internal ID of the unit */
|
|
size_t underlying_id() const { return underlying_id_; }
|
|
|
|
/** The unit type name */
|
|
const t_string& type_name() const {return type_name_;}
|
|
const std::string& undead_variation() const {return undead_variation_;}
|
|
|
|
/** The unit name for display */
|
|
const t_string &name() const { return name_; }
|
|
void rename(const std::string& name) {if (!unrenamable_) name_= name;}
|
|
|
|
/** The unit's profile */
|
|
const std::string& profile() const;
|
|
/** Information about the unit -- a detailed description of it */
|
|
const std::string& unit_description() const { return cfg_["description"]; }
|
|
|
|
int hitpoints() const { return hit_points_; }
|
|
int max_hitpoints() const { return max_hit_points_; }
|
|
int experience() const { return experience_; }
|
|
int max_experience() const { return max_experience_; }
|
|
int level() const { return level_; }
|
|
/**
|
|
* Adds 'xp' points to the units experience; returns true if advancement
|
|
* should occur
|
|
*/
|
|
bool get_experience(int xp) { experience_ += xp; return advances(); }
|
|
/** Colors for the unit's hitpoints. */
|
|
SDL_Colour hp_color() const;
|
|
/** Colors for the unit's XP. */
|
|
SDL_Colour xp_color() const;
|
|
/** Set to true for some scenario-specific units which should not be renamed */
|
|
bool unrenamable() const { return unrenamable_; }
|
|
int side() const { return side_; }
|
|
std::string side_id() const {return teams_manager::get_teams()[side()-1].save_id(); }
|
|
Uint32 team_rgb() const { return(team::get_side_rgb(side())); }
|
|
const std::string& team_color() const { return flag_rgb_; }
|
|
unit_race::GENDER gender() const { return gender_; }
|
|
void set_side(unsigned int new_side) { side_ = new_side; }
|
|
fixed_t alpha() const { return alpha_; }
|
|
|
|
bool can_recruit() const { return utils::string_bool(cfg_["canrecruit"]); }
|
|
bool incapacitated() const { return get_state(STATE_PETRIFIED); }
|
|
const std::vector<std::string>& recruits() const { return recruits_; }
|
|
int total_movement() const { return max_movement_; }
|
|
int movement_left() const { return (movement_ == 0 || incapacitated()) ? 0 : movement_; }
|
|
void set_hold_position(bool value) { hold_position_ = value; }
|
|
bool hold_position() const { return hold_position_; }
|
|
void set_user_end_turn(bool value=true) { end_turn_ = value; }
|
|
bool user_end_turn() const { return end_turn_; }
|
|
int attacks_left() const { return (attacks_left_ == 0 || incapacitated()) ? 0 : attacks_left_; }
|
|
void set_movement(int moves);
|
|
void set_attacks(int left) { attacks_left_ = std::max<int>(0,std::min<int>(left,max_attacks_)); }
|
|
void unit_hold_position() { hold_position_ = end_turn_ = true; }
|
|
void new_turn();
|
|
void end_turn();
|
|
void new_scenario();
|
|
/** Called on every draw */
|
|
void refresh(const game_display &disp, const map_location &loc);
|
|
|
|
bool take_hit(int damage) { hit_points_ -= damage; return hit_points_ <= 0; }
|
|
void heal(int amount);
|
|
void heal_all() { hit_points_ = max_hitpoints(); }
|
|
bool resting() const { return resting_; }
|
|
void set_resting(bool rest) { resting_ = rest; }
|
|
|
|
const std::map<std::string,std::string> get_states() const;
|
|
std::string get_state(const std::string& state) const;
|
|
void set_state(const std::string& state, const std::string& value);
|
|
void set_state(size_t state, bool value);
|
|
bool get_state(size_t state) const;
|
|
static size_t get_known_boolean_state_id(const std::string &state);
|
|
static std::map<std::string,size_t> get_known_boolean_state_names();
|
|
const static size_t STATE_UNKNOWN = -1;
|
|
const static size_t STATE_SLOWED = 0;
|
|
const static size_t STATE_POISONED = 1;
|
|
const static size_t STATE_PETRIFIED = 2;
|
|
const static size_t STATE_HIDDEN = 3;
|
|
const static size_t STATE_NOT_MOVED = 4;
|
|
|
|
bool has_moved() const { return movement_left() != total_movement(); }
|
|
bool has_goto() const { return get_goto().valid(); }
|
|
bool emits_zoc() const { return emit_zoc_ && !incapacitated();}
|
|
/* cfg: standard unit filter */
|
|
bool matches_filter(const vconfig& cfg,const map_location& loc,bool use_flat_tod=false) const;
|
|
void add_overlay(const std::string& overlay) { overlays_.push_back(overlay); }
|
|
void remove_overlay(const std::string& overlay) { overlays_.erase(std::remove(overlays_.begin(),overlays_.end(),overlay),overlays_.end()); }
|
|
const std::vector<std::string>& overlays() const { return overlays_; }
|
|
|
|
/**
|
|
* Initialize this unit from a cfg object.
|
|
*
|
|
* @param cfg Configuration object from which to read the unit.
|
|
* @param use_traits ??
|
|
* */
|
|
void read(const config& cfg, bool use_traits=true, game_state* state = 0);
|
|
void write(config& cfg) const;
|
|
// void write(config_writer& out) const;
|
|
|
|
void set_role(const std::string& role) { role_ = role; }
|
|
const std::string &get_role() const { return role_; }
|
|
|
|
void assign_ai_special(const std::string& s) { ai_special_ = s;}
|
|
std::string get_ai_special() const { return(ai_special_); }
|
|
const std::vector<attack_type>& attacks() const { return attacks_; }
|
|
std::vector<attack_type>& attacks() { return attacks_; }
|
|
|
|
int damage_from(const attack_type& attack,bool attacker,const map_location& loc) const { return resistance_against(attack,attacker,loc); }
|
|
|
|
/** A SDL surface, ready for display for place where we need a still-image of the unit. */
|
|
const surface still_image(bool scaled = false) const;
|
|
|
|
/** draw a unit. */
|
|
void redraw_unit(game_display& disp, const map_location& loc);
|
|
/** Clear unit_halo_ */
|
|
void clear_haloes();
|
|
|
|
|
|
void set_standing(const map_location& loc, bool with_bars = true);
|
|
void set_standing(bool with_bars = true)
|
|
{ set_standing(loc_, with_bars); }
|
|
|
|
void set_idling(const game_display& disp,const map_location& loc);
|
|
void set_selecting();
|
|
unit_animation* get_animation() { return anim_;};
|
|
const unit_animation* get_animation() const { return anim_;};
|
|
void set_facing(map_location::DIRECTION dir);
|
|
map_location::DIRECTION facing() const { return facing_; }
|
|
|
|
bool invalidate(const map_location &loc);
|
|
const t_string& traits_description() const { return traits_description_; }
|
|
std::vector<std::string> get_traits_list() const;
|
|
|
|
int cost () const { return unit_value_; }
|
|
|
|
const map_location &get_location() const { return loc_; }
|
|
/** To be called by unit_map only. (For now.) */
|
|
void set_location(const map_location &loc) { loc_ = loc; }
|
|
|
|
const map_location& get_goto() const { return goto_; }
|
|
void set_goto(const map_location& new_goto) { goto_ = new_goto; }
|
|
|
|
int upkeep() const;
|
|
bool loyal() const {return cfg_["upkeep"]=="loyal"; }
|
|
|
|
void set_hidden(bool state);
|
|
bool get_hidden() const { return hidden_; }
|
|
bool is_flying() const { return 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;
|
|
int defense_modifier(t_translation::t_terrain terrain, int recurse_count=0) const;
|
|
int resistance_against(const std::string& damage_name,bool attacker,const map_location& loc) const;
|
|
int resistance_against(const attack_type& damage_type,bool attacker,const map_location& loc) const
|
|
{return resistance_against(damage_type.type(), attacker, loc);};
|
|
|
|
//return resistances without any abililities applied
|
|
string_map get_base_resistances() const;
|
|
// std::map<terrain_type::TERRAIN,int> movement_type() const;
|
|
|
|
bool can_advance() const { return advances_to_.empty()==false || get_modification_advances().empty() == false; }
|
|
bool advances() const { return experience_ >= max_experience() && can_advance(); }
|
|
|
|
std::map<std::string,std::string> advancement_icons() const;
|
|
std::vector<std::pair<std::string,std::string> > amla_icons() const;
|
|
|
|
std::vector<config> get_modification_advances() const;
|
|
config::const_child_itors modification_advancements() const
|
|
{ return cfg_.child_range("advancement"); }
|
|
|
|
size_t modification_count(const std::string& type, const std::string& id) const;
|
|
|
|
void add_modification(const std::string& type, const config& modification,
|
|
bool no_add=false);
|
|
|
|
const t_string& modification_description(const std::string& type) const;
|
|
|
|
bool move_interrupted() const { return movement_left() > 0 && interrupted_move_.x >= 0 && interrupted_move_.y >= 0; }
|
|
const map_location& get_interrupted_move() const { return interrupted_move_; }
|
|
void set_interrupted_move(const map_location& interrupted_move) { interrupted_move_ = interrupted_move; }
|
|
|
|
/** States for animation. */
|
|
enum STATE {
|
|
STATE_STANDING, /** anim must fit in a hex */
|
|
STATE_FORGET, /** animation will be automaticaly replaced by a standing anim when finished */
|
|
STATE_ANIM}; /** normal anims */
|
|
void start_animation(const int start_time , const map_location &loc,const unit_animation* animation, bool with_bars,bool cycles=false,const std::string text = "", const Uint32 text_color =0,STATE state = STATE_ANIM);
|
|
|
|
/** The name of the file to game_display (used in menus). */
|
|
const std::string& absolute_image() const { return cfg_["image"]; }
|
|
const std::string& image_halo() const { return cfg_["halo"]; }
|
|
|
|
const std::string& get_hit_sound() const { return cfg_["get_hit_sound"]; }
|
|
const std::string& image_ellipse() const { return cfg_["ellipse"]; }
|
|
|
|
const std::string& usage() const { return cfg_["usage"]; }
|
|
unit_type::ALIGNMENT alignment() const { return alignment_; }
|
|
const unit_race* race() const { return race_; }
|
|
|
|
const unit_animation* choose_animation(const game_display& disp,
|
|
const map_location& loc, const std::string& event,
|
|
const map_location& second_loc = map_location::null_location,
|
|
const int damage=0,
|
|
const unit_animation::hit_type hit_type = unit_animation::INVALID,
|
|
const attack_type* attack=NULL,const attack_type* second_attack = NULL,
|
|
int swing_num =0) const;
|
|
|
|
bool get_ability_bool(const std::string& ability, const map_location& loc) const;
|
|
unit_ability_list get_abilities(const std::string& ability, const map_location& loc) const;
|
|
std::vector<std::string> ability_tooltips(const map_location& loc) const;
|
|
std::vector<std::string> unit_ability_tooltips() const;
|
|
std::vector<std::string> get_ability_list() const;
|
|
bool has_ability_type(const std::string& ability) const;
|
|
|
|
const game_logic::map_formula_callable_ptr& formula_vars() const { return formula_vars_; }
|
|
void add_formula_var(std::string str, variant var);
|
|
bool has_formula() const { return !unit_formula_.empty(); }
|
|
bool has_loop_formula() const { return !unit_loop_formula_.empty(); }
|
|
bool has_priority_formula() const { return !unit_priority_formula_.empty(); }
|
|
bool has_on_fail_formula() const { return !unit_on_fail_formula_.empty(); }
|
|
const std::string& get_formula() const { return unit_formula_; }
|
|
const std::string& get_loop_formula() const { return unit_loop_formula_; }
|
|
const std::string& get_priority_formula() const { return unit_priority_formula_; }
|
|
const std::string& get_on_fail_formula() const { return unit_on_fail_formula_; }
|
|
|
|
void reset_modifications();
|
|
void backup_state();
|
|
void apply_modifications();
|
|
void remove_temporary_modifications();
|
|
void generate_traits(bool musthaveonly=false, game_state* state = 0);
|
|
void generate_traits_description();
|
|
std::string generate_name( rand_rng::simple_rng* rng = 0) const
|
|
{ return race_->generate_name(string_gender(cfg_["gender"]), rng); }
|
|
|
|
// Only see_all=true use caching
|
|
bool invisible(const map_location& loc,
|
|
const unit_map& units,const std::vector<team>& teams, bool see_all=true) const;
|
|
|
|
/** Mark this unit as clone so it can be insterted to unit_map
|
|
* @returns self (for convenience)
|
|
**/
|
|
unit& clone(bool is_temporary=true);
|
|
|
|
/**
|
|
* Make sure that invisibility cache is revalidate
|
|
* after ambush;
|
|
**/
|
|
void ambush() const;
|
|
|
|
unit_race::GENDER generate_gender(const unit_type& type, bool gen, game_state* state = 0);
|
|
std::string image_mods() const;
|
|
|
|
/**
|
|
* Gets the portrait for a unit.
|
|
*
|
|
* @param size The size of the portrait.
|
|
* @param side The side the portrait is shown on.
|
|
*
|
|
* @returns The portrait with the wanted size.
|
|
* @retval NULL The wanted portrait doesn't exist.
|
|
*/
|
|
const tportrait* portrait(
|
|
const unsigned size, const tportrait::tside side) const;
|
|
|
|
private:
|
|
|
|
bool internal_matches_filter(const vconfig& cfg,const map_location& loc,
|
|
bool use_flat_tod) const;
|
|
/*
|
|
* cfg: an ability WML structure
|
|
*/
|
|
bool ability_active(const std::string& ability,const config& cfg,const map_location& loc) const;
|
|
bool ability_affects_adjacent(const std::string& ability,const config& cfg,int dir,const map_location& loc) const;
|
|
bool ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const;
|
|
bool resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name) const;
|
|
bool resistance_filter_matches(const config& cfg,bool attacker,const attack_type& damage_type) const
|
|
{return resistance_filter_matches(cfg, attacker, damage_type.type()); };
|
|
|
|
int movement_cost_internal(t_translation::t_terrain terrain, int recurse_count=0) const;
|
|
bool has_ability_by_id(const std::string& ability) const;
|
|
void remove_ability_by_id(const std::string& ability);
|
|
|
|
void set_underlying_id();
|
|
|
|
config cfg_;
|
|
map_location loc_;
|
|
|
|
std::vector<std::string> advances_to_;
|
|
std::string type_;
|
|
const unit_race* race_;
|
|
std::string id_;
|
|
t_string name_;
|
|
size_t underlying_id_;
|
|
t_string type_name_;
|
|
std::string undead_variation_;
|
|
std::string variation_;
|
|
|
|
int hit_points_;
|
|
int max_hit_points_;
|
|
int experience_;
|
|
int max_experience_;
|
|
int level_;
|
|
unit_type::ALIGNMENT alignment_;
|
|
std::string flag_rgb_;
|
|
std::string image_mods_;
|
|
|
|
bool unrenamable_;
|
|
int side_;
|
|
unit_race::GENDER gender_;
|
|
|
|
fixed_t alpha_;
|
|
|
|
std::string unit_formula_;
|
|
std::string unit_loop_formula_;
|
|
std::string unit_priority_formula_;
|
|
std::string unit_on_fail_formula_;
|
|
game_logic::map_formula_callable_ptr formula_vars_;
|
|
|
|
std::vector<std::string> recruits_;
|
|
|
|
int movement_;
|
|
int max_movement_;
|
|
mutable std::map<t_translation::t_terrain, int> movement_costs_; // movement cost cache
|
|
mutable std::map<t_translation::t_terrain, int> defense_mods_; // defense modifiers cache
|
|
bool hold_position_;
|
|
bool end_turn_;
|
|
bool resting_;
|
|
int attacks_left_;
|
|
int max_attacks_;
|
|
|
|
std::map<std::string,std::string> states_;
|
|
std::vector<bool> known_boolean_states_;
|
|
static std::map<std::string,size_t> known_boolean_state_names_;
|
|
config variables_;
|
|
int emit_zoc_;
|
|
STATE state_;
|
|
|
|
std::vector<std::string> overlays_;
|
|
|
|
std::string role_;
|
|
std::string ai_special_;
|
|
std::vector<attack_type> attacks_;
|
|
map_location::DIRECTION facing_;
|
|
|
|
t_string traits_description_;
|
|
int unit_value_;
|
|
map_location goto_, interrupted_move_;
|
|
bool flying_, is_fearless_, is_healthy_;
|
|
|
|
string_map modification_descriptions_;
|
|
// Animations:
|
|
std::vector<unit_animation> animations_;
|
|
|
|
unit_animation *anim_;
|
|
int next_idling_;
|
|
int frame_begin_time_;
|
|
|
|
|
|
int unit_halo_;
|
|
bool getsHit_;
|
|
bool refreshing_; // avoid infinite recursion
|
|
bool hidden_;
|
|
bool draw_bars_;
|
|
|
|
config modifications_;
|
|
|
|
friend void attack_type::set_specials_context(const map_location& loc, const map_location&, const unit& un, bool) const;
|
|
const unit_map* units_;
|
|
const gamemap* map_;
|
|
const gamestatus* gamestatus_;
|
|
|
|
/** Hold the visibility status cache for a unit, mutable since it's a cache. */
|
|
mutable std::map<map_location, bool> invisibility_cache_;
|
|
|
|
/**
|
|
* Clears the cache.
|
|
*
|
|
* Since we don't change the state of the object we're marked const (also
|
|
* required since the objects in the cache need to be marked const).
|
|
*/
|
|
void clear_visibility_cache() const { invisibility_cache_.clear(); }
|
|
};
|
|
|
|
/** Object which temporarily resets a unit's movement */
|
|
struct unit_movement_resetter
|
|
{
|
|
unit_movement_resetter(unit& u, bool operate=true);
|
|
~unit_movement_resetter();
|
|
|
|
private:
|
|
unit& u_;
|
|
int moves_;
|
|
};
|
|
|
|
void sort_units(std::vector< unit > &);
|
|
|
|
/** Returns the number of units of the side @a side_num. */
|
|
int side_units(const unit_map& units, int side_num);
|
|
|
|
/** Returns the total cost of units of side @a side_num. */
|
|
int side_units_cost(const unit_map& units, int side_num);
|
|
|
|
int side_upkeep(const unit_map& units, int side_num);
|
|
|
|
unit_map::iterator find_visible_unit(unit_map& units,
|
|
const map_location loc,
|
|
const gamemap& map,
|
|
const std::vector<team>& teams, const team& current_team,
|
|
bool see_all=false);
|
|
unit_map::const_iterator find_visible_unit(const unit_map& units,
|
|
const map_location loc,
|
|
const gamemap& map,
|
|
const std::vector<team>& teams, const team& current_team,
|
|
bool see_all=false);
|
|
|
|
const unit *get_visible_unit(const unit_map &units, const map_location &loc,
|
|
const gamemap &map, const std::vector<team> &teams, const team ¤t_team,
|
|
bool see_all = false);
|
|
|
|
struct team_data
|
|
{
|
|
team_data() :
|
|
units(0),
|
|
upkeep(0),
|
|
villages(0),
|
|
expenses(0),
|
|
net_income(0),
|
|
gold(0),
|
|
teamname()
|
|
{
|
|
}
|
|
|
|
int units, upkeep, villages, expenses, net_income, gold;
|
|
std::string teamname;
|
|
};
|
|
|
|
team_data calculate_team_data(const class team& tm, int side, const unit_map& units);
|
|
|
|
/**
|
|
* This object is used to temporary place a unit in the unit map, swapping out
|
|
* any unit that is already there. On destruction, it restores the unit map to
|
|
* its original.
|
|
*/
|
|
struct temporary_unit_placer
|
|
{
|
|
temporary_unit_placer(unit_map& m, const map_location& loc, unit& u);
|
|
~temporary_unit_placer();
|
|
|
|
private:
|
|
unit_map& m_;
|
|
const map_location& loc_;
|
|
std::pair<map_location,unit> *temp_;
|
|
};
|
|
|
|
/**
|
|
* Gets a checksum for a unit.
|
|
*
|
|
* In MP games the descriptions are locally generated and might differ, so it
|
|
* should be possible to discard them. Not sure whether replays suffer the
|
|
* same problem.
|
|
*
|
|
* @param u the unit
|
|
*
|
|
* @returns the checksum for a unit
|
|
*/
|
|
std::string get_checksum(const unit& u);
|
|
|
|
#endif
|