mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-07 16:59:01 +00:00
481 lines
21 KiB
C++
481 lines
21 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003 by David White <davidnwhite@verizon.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.
|
|
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_H_INCLUDED
|
|
#define UNIT_H_INCLUDED
|
|
|
|
#include "config.hpp"
|
|
#include "map.hpp"
|
|
#include "race.hpp"
|
|
#include "team.hpp"
|
|
#include "unit_types.hpp"
|
|
#include "unit_map.hpp"
|
|
|
|
class display;
|
|
class gamestatus;
|
|
class config_writer;
|
|
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
class unit_ability_list
|
|
{
|
|
public:
|
|
|
|
bool empty() const;
|
|
|
|
std::pair<int,gamemap::location> highest(const std::string& key, int def=0) const;
|
|
std::pair<int,gamemap::location> lowest(const std::string& key, int def=100) const;
|
|
|
|
std::vector<std::pair<config*,gamemap::location> > cfgs;
|
|
private:
|
|
|
|
};
|
|
|
|
|
|
class unit
|
|
{
|
|
public:
|
|
// clear the status caches for esch unit, this is should be called it
|
|
// the status of a unit changes
|
|
static void clear_status_caches();
|
|
|
|
friend struct unit_movement_resetter;
|
|
// Copy constructor
|
|
unit(const unit& u);
|
|
// Initilizes a unit from a config
|
|
unit(const game_data& gamedata, const config& cfg);
|
|
unit(const game_data* gamedata, unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams, const config& cfg);
|
|
// Initilizes 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);
|
|
unit(const game_data* gamedata, 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);
|
|
virtual ~unit();
|
|
unit& operator=(const unit&);
|
|
|
|
void set_game_context(const game_data* gamedata, unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams);
|
|
void write_checksum(std::string& str) const;
|
|
|
|
// Advances this unit to another type
|
|
void advance_to(const unit_type* t);
|
|
const std::vector<std::string> advances_to() const { return advances_to_; }
|
|
|
|
// the current type id
|
|
const std::string& id() const { return id_; }
|
|
const unit_type* type() const;
|
|
// the actual name of the unit
|
|
const std::string& name() const { if (description_.empty()) return language_name(); else return description_; }
|
|
void rename(const std::string& name) { if (!unrenamable_) custom_unit_description_ = name; }
|
|
// the unit type name
|
|
const std::string& description() const { return (custom_unit_description_ != "") ? custom_unit_description_ : description_; }
|
|
const std::string& underlying_description() const { return underlying_description_; }
|
|
const t_string& language_name() const { return language_name_; }
|
|
const std::string& undead_variation() const { return undead_variation_; }
|
|
// 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_["unit_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 maximum<int>(1,(max_experience_*unit_type::experience_accelerator::get_acceleration() + 50) / 100); }
|
|
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(); }
|
|
SDL_Colour hp_color() const;
|
|
SDL_Colour xp_color() const;
|
|
/** < Set to true for some scenario-specific units which should not be renamed */
|
|
bool unrenamable() const { return unrenamable_; }
|
|
unsigned int side() const { return side_; }
|
|
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 utils::string_bool(get_state("stoned"),false); }
|
|
const std::vector<std::string>& recruits() const { return recruits_; }
|
|
int total_movement() const { return max_movement_; }
|
|
int movement_left() const { return 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_; }
|
|
void set_movement(int moves);
|
|
void set_attacks(int left) { attacks_left_ = maximum<int>(0,minimum<int>(left,max_attacks_)); }
|
|
void unit_hold_position() { hold_position_ = end_turn_ = true; }
|
|
void new_turn();
|
|
void end_turn();
|
|
void new_level();
|
|
// called on every draw
|
|
void refresh(const display& disp,const gamemap::location& loc) {
|
|
if (state_ == STATE_IDLING && anim_ && anim_->animation_finished()) {
|
|
set_standing(disp, loc);
|
|
return;
|
|
}
|
|
if (state_ != STATE_STANDING || incapacitated() || (get_current_animation_tick() < next_idling_)) return;
|
|
if (get_current_animation_tick() > next_idling_ + 1000) { // prevent all units animating at the same
|
|
set_standing(disp,loc);
|
|
} else {
|
|
set_idling(disp, 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::string get_state(const std::string& state) const;
|
|
void set_state(const std::string& state, const std::string& value);
|
|
|
|
bool has_moved() const { return movement_left() != total_movement(); }
|
|
bool has_goto() const { return get_goto().valid(); }
|
|
int emits_zoc() const { return (incapacitated()) ? false : emit_zoc_; }
|
|
/* cfg: standard unit filter */
|
|
bool matches_filter(const vconfig& cfg,const gamemap::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_; }
|
|
/**
|
|
* Initializes this unit from a cfg object.
|
|
*
|
|
* \param cfg Configuration object from which to read the unit
|
|
*/
|
|
void read(const config& cfg);
|
|
void write(config& cfg) const;
|
|
void write(config_writer& out) const;
|
|
|
|
void assign_role(const std::string& role) { role_ = 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 gamemap::location& loc) const { return resistance_against(attack,attacker,loc); }
|
|
|
|
// a sdl surface, ready for display for place where we need a fix image of the unit
|
|
const surface still_image(bool scaled = false) const;
|
|
void redraw_unit(display& disp,gamemap::location hex);
|
|
|
|
void set_standing(const display& disp,const gamemap::location& loc, bool with_bars = true);
|
|
void set_defending(const display &disp,const gamemap::location& loc, int damage,const attack_type* attack,const attack_type* secondary_attack,int swing_num);
|
|
void set_leading(const display& disp,const gamemap::location& loc);
|
|
void set_healing(const display& disp,const gamemap::location& loc,int damage);
|
|
void set_leveling_in(const display& disp,const gamemap::location& loc);
|
|
void set_leveling_out(const display& disp,const gamemap::location& loc);
|
|
void set_teleporting (const display& disp,const gamemap::location& loc);
|
|
void set_extra_anim(const display& disp,const gamemap::location& loc, std::string flag);
|
|
void set_dying(const display &disp,const gamemap::location& loc,const attack_type* attack,const attack_type* secondary_attack);
|
|
void set_walking(const display& disp,const gamemap::location& loc);
|
|
const unit_animation & set_attacking( const display &disp,const gamemap::location& loc,int damage,const attack_type& type,const attack_type* secondary_attack,int swing_num);
|
|
void set_recruited(const display& disp,const gamemap::location& loc);
|
|
void set_healed(const display& disp,const gamemap::location& loc,int healing);
|
|
void set_victorious(const display &disp,const gamemap::location& loc,const attack_type* attack,const attack_type* secondary_attack);
|
|
void set_poisoned(const display& disp,const gamemap::location& loc,int damage);
|
|
void set_idling(const display& disp,const gamemap::location& loc);
|
|
void restart_animation(const display& disp,int start_time);
|
|
const unit_animation* get_animation() const { return anim_;};
|
|
void set_offset(double offset){offset_ = offset;}
|
|
void set_facing(gamemap::location::DIRECTION dir);
|
|
gamemap::location::DIRECTION facing() const { return facing_; }
|
|
|
|
std::set<gamemap::location> overlaps(const display &disp, const gamemap::location &loc) const;
|
|
const t_string& traits_description() const { return traits_description_; }
|
|
|
|
int value() const { return unit_value_; }
|
|
int cost () const { return unit_value_; }
|
|
|
|
const gamemap::location& get_goto() const { return goto_; }
|
|
void set_goto(const gamemap::location& new_goto) { goto_ = new_goto; }
|
|
|
|
int upkeep() const;
|
|
|
|
void set_hidden(bool state) {hidden_ = state;};
|
|
bool get_hidden() { 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_letter terrain) const;
|
|
int defense_modifier(t_translation::t_letter terrain, int recurse_count=0) const;
|
|
int resistance_against(const attack_type& damage_type,bool attacker,const gamemap::location& loc) 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;
|
|
|
|
config::child_list get_modification_advances() const;
|
|
const config::child_list& modification_advancements() const { return cfg_.get_children("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 gamemap::location& get_interrupted_move() const { return interrupted_move_; }
|
|
void set_interrupted_move(const gamemap::location& interrupted_move) { interrupted_move_ = interrupted_move; }
|
|
|
|
enum STATE { STATE_STANDING, STATE_ATTACKING, STATE_DEFENDING,
|
|
STATE_LEADING, STATE_HEALING, STATE_WALKING, STATE_LEVELIN,
|
|
STATE_LEVELOUT, STATE_DYING, STATE_EXTRA, STATE_TELEPORT,
|
|
STATE_RECRUITED, STATE_HEALED, STATE_POISONED, STATE_IDLEIN, STATE_IDLING, STATE_VICTORIOUS};
|
|
STATE state() const { return (state_ == STATE_IDLING) ? STATE_STANDING : state_; }
|
|
|
|
//the name of the file to 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& image_fighting(attack_type::RANGE range) const;
|
|
const std::string& image_healing() const;
|
|
const std::string& image_halo_healing() const { return cfg_["image_halo_healing"]; }
|
|
const std::string& get_hit_sound() const { return cfg_["get_hit_sound"]; }
|
|
const std::string& die_sound() const { return cfg_["die_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 std::string& race() const { return race_->name(); }
|
|
|
|
const defensive_animation& defend_animation(const display& disp, const gamemap::location& loc,
|
|
fighting_animation::hit_type hits,const attack_type* attack,
|
|
const attack_type* secondary_attack,int swing_num,int damage) const;
|
|
const unit_animation& teleport_animation(const display& disp, const gamemap::location& loc) const;
|
|
const unit_animation* extra_animation(const display& disp, const gamemap::location& loc,const std::string &flag) const;
|
|
const death_animation& die_animation(const display& disp, const gamemap::location& loc,
|
|
fighting_animation::hit_type hits,const attack_type* attack,const attack_type* secondary_attack) const;
|
|
const movement_animation& move_animation(const display& disp, const gamemap::location& loc) const;
|
|
const standing_animation& stand_animation(const display& disp, const gamemap::location& loc) const;
|
|
const leading_animation& lead_animation(const display& disp, const gamemap::location& loc) const;
|
|
const healing_animation& heal_animation(const display& disp, const gamemap::location& loc,int damage) const;
|
|
const victory_animation& victorious_animation(const display& disp, const gamemap::location& loc,
|
|
fighting_animation::hit_type hits,const attack_type* attack,const attack_type* secondary_attack) const;
|
|
const recruit_animation& recruiting_animation(const display& disp, const gamemap::location& loc) const;
|
|
const idle_animation* idling_animation(const display& disp, const gamemap::location& loc) const;
|
|
const levelin_animation& levelingin_animation(const display& disp, const gamemap::location& loc) const;
|
|
const levelout_animation& levelingout_animation(const display& disp, const gamemap::location& loc) const;
|
|
const healed_animation& get_healed_animation(const display& disp, const gamemap::location& loc,int healing) const;
|
|
const poison_animation& poisoned_animation(const display& disp, const gamemap::location& loc,int damage) const;
|
|
|
|
bool get_ability_bool(const std::string& ability, const gamemap::location& loc) const;
|
|
unit_ability_list get_abilities(const std::string& ability, const gamemap::location& loc) const;
|
|
std::vector<std::string> ability_tooltips(const gamemap::location& loc) const;
|
|
std::vector<std::string> unit_ability_tooltips() const;
|
|
bool has_ability_type(const std::string& ability) const;
|
|
|
|
void reset_modifications();
|
|
void backup_state();
|
|
void apply_modifications();
|
|
void remove_temporary_modifications();
|
|
void generate_traits();
|
|
void generate_traits_description();
|
|
std::string generate_description() const { return race_->generate_name(string_gender(cfg_["gender"])); }
|
|
|
|
bool invisible(const gamemap::location& loc,
|
|
const unit_map& units,const std::vector<team>& teams) const;
|
|
|
|
unit_race::GENDER generate_gender(const unit_type& type, bool gen);
|
|
std::string image_mods() const;
|
|
|
|
private:
|
|
|
|
bool internal_matches_filter(const vconfig& cfg,const gamemap::location& loc,
|
|
bool use_flat_tod) const;
|
|
/*
|
|
* cfg: an ability WML structure
|
|
*/
|
|
bool ability_active(const std::string& ability,const config& cfg,const gamemap::location& loc) const;
|
|
bool ability_affects_adjacent(const std::string& ability,const config& cfg,int dir,const gamemap::location& loc) const;
|
|
bool ability_affects_self(const std::string& ability,const config& cfg,const gamemap::location& loc) const;
|
|
bool resistance_filter_matches(const config& cfg,bool attacker,const attack_type& damage_type) const;
|
|
int movement_cost_internal(t_translation::t_letter terrain, int recurse_count=0) const;
|
|
bool has_ability_by_id(const std::string& ability) const;
|
|
|
|
config cfg_;
|
|
config movement_b_;
|
|
config defense_b_;
|
|
config resistance_b_;
|
|
|
|
std::vector<std::string> advances_to_;
|
|
std::string id_;
|
|
const unit_race* race_;
|
|
std::string name_;
|
|
std::string description_;
|
|
std::string custom_unit_description_;
|
|
std::string underlying_description_;
|
|
t_string language_name_;
|
|
std::string undead_variation_;
|
|
std::string variation_;
|
|
|
|
int hit_points_;
|
|
int max_hit_points_, max_hit_points_b_;
|
|
int experience_;
|
|
int max_experience_, max_experience_b_;
|
|
int level_;
|
|
unit_type::ALIGNMENT alignment_;
|
|
std::string flag_rgb_;
|
|
std::string image_mods_;
|
|
|
|
bool unrenamable_;
|
|
unsigned int side_;
|
|
unit_race::GENDER gender_;
|
|
|
|
fixed_t alpha_;
|
|
|
|
std::vector<std::string> recruits_;
|
|
|
|
int movement_;
|
|
int max_movement_, max_movement_b_;
|
|
mutable std::map<t_translation::t_letter, int> movement_costs_; // movement cost cache
|
|
bool hold_position_;
|
|
bool end_turn_;
|
|
bool resting_;
|
|
int attacks_left_;
|
|
int max_attacks_;
|
|
|
|
std::map<std::string,std::string> states_;
|
|
config variables_;
|
|
int emit_zoc_;
|
|
STATE state_;
|
|
|
|
std::vector<std::string> overlays_;
|
|
|
|
std::string role_;
|
|
std::string ai_special_;
|
|
std::vector<attack_type> attacks_, attacks_b_;
|
|
gamemap::location::DIRECTION facing_;
|
|
|
|
t_string traits_description_;
|
|
int unit_value_;
|
|
gamemap::location goto_, interrupted_move_;
|
|
bool flying_, is_fearless_, is_healthy_;
|
|
|
|
// std::map<terrain_type::TERRAIN,int> movement_costs_, movement_costs_b_;
|
|
// std::map<terrain_type::TERRAIN,int> defense_mods_, defense_mods_b_;
|
|
|
|
string_map modification_descriptions_;
|
|
// animations
|
|
std::vector<defensive_animation> defensive_animations_;
|
|
std::vector<unit_animation> teleport_animations_;
|
|
std::multimap<std::string,unit_animation> extra_animations_;
|
|
std::vector<death_animation> death_animations_;
|
|
std::vector<movement_animation> movement_animations_;
|
|
std::vector<standing_animation> standing_animations_;
|
|
std::vector<leading_animation> leading_animations_;
|
|
std::vector<healing_animation> healing_animations_;
|
|
std::vector<victory_animation> victory_animations_;
|
|
std::vector<recruit_animation> recruit_animations_;
|
|
std::vector<idle_animation> idle_animations_;
|
|
std::vector<levelin_animation> levelin_animations_;
|
|
std::vector<levelout_animation> levelout_animations_;
|
|
std::vector<healed_animation> healed_animations_;
|
|
std::vector<poison_animation> poison_animations_;
|
|
unit_animation *anim_;
|
|
int next_idling_;
|
|
int frame_begin_time_;
|
|
|
|
|
|
double offset_;
|
|
int unit_halo_;
|
|
int unit_anim_halo_;
|
|
bool getsHit_;
|
|
bool refreshing_; // avoid infinite recursion
|
|
bool hidden_;
|
|
bool draw_bars_;
|
|
|
|
config modifications_;
|
|
|
|
friend void attack_type::set_specials_context(const gamemap::location& loc,const unit& un) const;
|
|
const game_data* gamedata_;
|
|
const unit_map* units_;
|
|
const gamemap* map_;
|
|
const gamestatus* gamestatus_;
|
|
const std::vector<team>* teams_;
|
|
|
|
// hold the visibility status cache for a unit, mutable since it's a cache
|
|
mutable std::map<gamemap::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) : u_(u), moves_(u.movement_)
|
|
{
|
|
if(operate) {
|
|
u.movement_ = u.total_movement();
|
|
}
|
|
}
|
|
|
|
~unit_movement_resetter()
|
|
{
|
|
u_.movement_ = moves_;
|
|
}
|
|
|
|
private:
|
|
unit& u_;
|
|
int moves_;
|
|
};
|
|
|
|
void sort_units(std::vector< unit > &);
|
|
|
|
int team_units(const unit_map& units, unsigned int team_num);
|
|
int team_upkeep(const unit_map& units, unsigned int team_num);
|
|
unit_map::const_iterator team_leader(unsigned int side, const unit_map& units);
|
|
unit_map::iterator find_visible_unit(unit_map& units,
|
|
const gamemap::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 gamemap::location loc,
|
|
const gamemap& map,
|
|
const std::vector<team>& teams, const team& current_team,
|
|
bool see_all=false);
|
|
|
|
struct team_data
|
|
{
|
|
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);
|
|
|
|
const std::set<gamemap::location> vacant_villages(const std::set<gamemap::location>& villages, 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 gamemap::location& loc, const unit& u);
|
|
~temporary_unit_placer();
|
|
|
|
private:
|
|
unit_map& m_;
|
|
const gamemap::location& loc_;
|
|
std::pair<gamemap::location,unit> *temp_;
|
|
};
|
|
|
|
#endif
|