1 /* 2 Copyright (C) 2003 - 2018 by David White <dave@whitevine.net> 3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/ 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY. 11 12 See the COPYING file for more details. 13 */ 14 15 #pragma once 16 17 #include "color_range.hpp" 18 #include "game_config.hpp" 19 #include "game_events/fwd.hpp" 20 #include "utils/make_enum.hpp" 21 #include "map/location.hpp" 22 #include "recall_list_manager.hpp" 23 #include "units/ptr.hpp" 24 #include "config.hpp" 25 26 #include <set> 27 28 #include <cstdint> 29 #include <boost/dynamic_bitset.hpp> 30 31 class game_data; 32 class gamemap; 33 struct color_t; 34 35 36 namespace wb { 37 class side_actions; 38 } 39 40 /** 41 * This class stores all the data for a single 'side' (in game nomenclature). 42 * E.g., there is only one leader unit per team. 43 */ 44 class team 45 { 46 public: 47 48 MAKE_ENUM(CONTROLLER, 49 (HUMAN, "human") 50 (AI, "ai") 51 (EMPTY, "null") 52 ) 53 54 MAKE_ENUM(PROXY_CONTROLLER, 55 (PROXY_HUMAN, "human") 56 (PROXY_AI, "ai") 57 (PROXY_IDLE, "idle") 58 ) 59 60 MAKE_ENUM(DEFEAT_CONDITION, 61 (NO_LEADER, "no_leader_left") 62 (NO_UNITS, "no_units_left") 63 (NEVER, "never") 64 (ALWAYS, "always") 65 ) 66 67 MAKE_ENUM(SHARE_VISION, 68 (ALL, "all") 69 (SHROUD, "shroud") 70 (NONE, "none") 71 ) 72 73 private: 74 class shroud_map { 75 public: shroud_map()76 shroud_map() : enabled_(false), data_() {} 77 78 void place(int x, int y); 79 bool clear(int x, int y); 80 void reset(); 81 82 bool value(int x, int y) const; 83 bool shared_value(const std::vector<const shroud_map*>& maps, int x, int y) const; 84 85 bool copy_from(const std::vector<const shroud_map*>& maps); 86 87 std::string write() const; 88 void read(const std::string& shroud_data); 89 void merge(const std::string& shroud_data); 90 enabled() const91 bool enabled() const { return enabled_; } set_enabled(bool enabled)92 void set_enabled(bool enabled) { enabled_ = enabled; } 93 private: 94 bool enabled_; 95 std::vector<std::vector<bool>> data_; 96 }; 97 98 struct team_info 99 { 100 team_info(); 101 void read(const config &cfg); 102 void write(config& cfg) const; 103 int gold; 104 int start_gold; 105 int income; 106 int income_per_village; 107 int support_per_village; 108 mutable int minimum_recruit_price; 109 int recall_cost; 110 std::set<std::string> can_recruit; 111 std::string team_name; 112 t_string user_team_name; 113 t_string side_name; 114 std::string faction; 115 t_string faction_name; 116 std::string save_id; 117 // 'id' of the current player (not necessarily unique) 118 std::string current_player; 119 std::string countdown_time; 120 int action_bonus_count; 121 122 std::string flag; 123 std::string flag_icon; 124 125 std::string id; 126 127 bool scroll_to_leader; 128 129 t_string objectives; /** < Team's objectives for the current level. */ 130 131 /** Set to true when the objectives for this time changes. 132 * Reset to false when the objectives for this team have been 133 * displayed to the user. */ 134 mutable bool objectives_changed; 135 136 CONTROLLER controller; 137 bool is_local; 138 DEFEAT_CONDITION defeat_condition; 139 140 PROXY_CONTROLLER proxy_controller; // when controller == HUMAN, the proxy controller determines what input method is actually used. 141 // proxy controller is an interface property, not gamestate. it is not synced, not known to server. 142 // also not saved in save game file 143 SHARE_VISION share_vision; 144 bool disallow_observers; 145 bool allow_player; 146 bool chose_random; 147 bool no_leader; 148 bool hidden; 149 bool no_turn_confirmation; // Can suppress confirmations when ending a turn. 150 151 std::string color; 152 153 int side; 154 bool persistent; 155 bool lost; 156 157 int carryover_percentage; 158 bool carryover_add; 159 // TODO: maybe make this integer percentage? I like the float version more but this might cause OOS error because of floating point rounding differences on different hardware. 160 double carryover_bonus; 161 int carryover_gold; 162 config variables; 163 void handle_legacy_share_vision(const config& cfg); 164 }; 165 166 static const int default_team_gold_; 167 168 public: 169 team(); 170 virtual ~team(); 171 172 /// Stores the attributes recognized by [side]. These should be stripped 173 /// from a side's config before using it to create the side's leader. 174 static const std::set<std::string> attributes; 175 176 void build(const config &cfg, const gamemap &map, int gold = default_team_gold_); 177 178 void write(config& cfg) const; 179 180 game_events::pump_result_t get_village(const map_location&, const int owner_side, game_data * fire_event); //!< Acquires a village from owner_side. Pointer fire_event should be the game_data for the game if it is desired to fire an event -- a "capture" event with owner_side variable scoped in will be fired. For no event, pass it nullptr. Default is the resources::gamedata pointer 181 void lose_village(const map_location&); clear_villages()182 void clear_villages() { villages_.clear(); } villages() const183 const std::set<map_location>& villages() const { return villages_; } owns_village(const map_location & loc) const184 bool owns_village(const map_location& loc) const 185 { return villages_.count(loc) > 0; } 186 side() const187 int side() const { return info_.side; } gold() const188 int gold() const { return gold_; } start_gold() const189 int start_gold() const { return info_.start_gold; } base_income() const190 int base_income() const { return info_.income + game_config::base_income; } village_gold() const191 int village_gold() const { return info_.income_per_village; } recall_cost() const192 int recall_cost() const { return info_.recall_cost; } set_village_gold(int income)193 void set_village_gold(int income) { info_.income_per_village = income; } set_recall_cost(int cost)194 void set_recall_cost(int cost) { info_.recall_cost = cost; } total_income() const195 int total_income() const { return base_income() + villages_.size() * info_.income_per_village; } 196 /** @return The number of unit levels each village can support, 197 i.e. how much upkeep each village can bear. */ village_support() const198 int village_support() const { return info_.support_per_village; } 199 /** @param support The number of unit levels each village can support */ set_village_support(int support)200 void set_village_support(int support) { info_.support_per_village = support; } 201 /** Calculate total support capacity, based on support_per_village. */ support() const202 int support() const { return villages_.size()*village_support(); } new_turn()203 void new_turn() { gold_ += total_income(); } 204 void get_shared_maps(); set_gold(int amount)205 void set_gold(int amount) { gold_ = amount; } set_start_gold(const int amount)206 void set_start_gold(const int amount) { info_.start_gold = amount; } spend_gold(const int amount)207 void spend_gold(const int amount) { gold_ -= amount; } set_base_income(int amount)208 void set_base_income(int amount) { info_.income = amount - game_config::base_income; } countdown_time() const209 int countdown_time() const { return countdown_time_; } set_countdown_time(const int amount) const210 void set_countdown_time (const int amount) const 211 { countdown_time_ = amount; } action_bonus_count() const212 int action_bonus_count() const { return action_bonus_count_; } set_action_bonus_count(const int count)213 void set_action_bonus_count(const int count) { action_bonus_count_ = count; } recall_list()214 recall_list_manager& recall_list() {return recall_list_;} recall_list() const215 const recall_list_manager & recall_list() const {return recall_list_;} set_current_player(const std::string & player)216 void set_current_player(const std::string& player) 217 { info_.current_player = player; } 218 get_scroll_to_leader() const219 bool get_scroll_to_leader() const {return info_.scroll_to_leader;} set_scroll_to_leader(bool value)220 void set_scroll_to_leader(bool value) { info_.scroll_to_leader = value; } 221 recruits() const222 const std::set<std::string>& recruits() const 223 { return info_.can_recruit; } 224 void add_recruit(const std::string &); 225 void set_recruits(const std::set<std::string>& recruits); 226 int minimum_recruit_price() const; last_recruit() const227 const std::string& last_recruit() const { return last_recruit_; } last_recruit(const std::string & u_type)228 void last_recruit(const std::string & u_type) { last_recruit_ = u_type; } 229 save_id() const230 const std::string& save_id() const { return info_.save_id; } save_id_or_number() const231 std::string save_id_or_number() const { return info_.save_id.empty() ? std::to_string(info_.side) : info_.save_id; } set_save_id(const std::string & save_id)232 void set_save_id(const std::string& save_id) { info_.save_id = save_id; } current_player() const233 const std::string& current_player() const { return info_.current_player; } 234 235 void set_objectives(const t_string& new_objectives, bool silently=false); set_objectives_changed(bool c=true) const236 void set_objectives_changed(bool c = true) const { info_.objectives_changed = c; } reset_objectives_changed() const237 void reset_objectives_changed() const { info_.objectives_changed = false; } 238 objectives() const239 const t_string& objectives() const { return info_.objectives; } objectives_changed() const240 bool objectives_changed() const { return info_.objectives_changed; } 241 is_enemy(int n) const242 bool is_enemy(int n) const { 243 const size_t index = size_t(n-1); 244 if(index >= enemies_.size()) { 245 calculate_enemies(index); 246 } 247 if(index < enemies_.size()) { 248 return enemies_[index]; 249 } else { 250 return false; 251 } 252 } 253 controller() const254 CONTROLLER controller() const { return info_.controller; } color() const255 const std::string& color() const { return info_.color; } set_color(const std::string & color)256 void set_color(const std::string& color) { info_.color = color; } is_empty() const257 bool is_empty() const { return info_.controller == CONTROLLER::EMPTY; } 258 is_local() const259 bool is_local() const { return !is_empty() && info_.is_local; } is_network() const260 bool is_network() const { return !is_empty() && !info_.is_local; } 261 is_human() const262 bool is_human() const { return info_.controller == CONTROLLER::HUMAN; } is_ai() const263 bool is_ai() const { return info_.controller == CONTROLLER::AI; } 264 is_local_human() const265 bool is_local_human() const { return is_human() && is_local(); } is_local_ai() const266 bool is_local_ai() const { return is_ai() && is_local(); } is_network_human() const267 bool is_network_human() const { return is_human() && is_network(); } is_network_ai() const268 bool is_network_ai() const { return is_ai() && is_network(); } 269 set_local(bool local)270 void set_local(bool local) { info_.is_local = local; } make_human()271 void make_human() { info_.controller = CONTROLLER::HUMAN; } make_ai()272 void make_ai() { info_.controller = CONTROLLER::AI; } change_controller(const std::string & new_controller)273 void change_controller(const std::string& new_controller) { 274 info_.controller = CONTROLLER::AI; 275 info_.controller.parse(new_controller); 276 } change_controller(CONTROLLER controller)277 void change_controller(CONTROLLER controller) { info_.controller = controller; } 278 void change_controller_by_wml(const std::string& new_controller); 279 proxy_controller() const280 PROXY_CONTROLLER proxy_controller() const { return info_.proxy_controller; } is_proxy_human() const281 bool is_proxy_human() const { return info_.proxy_controller == PROXY_CONTROLLER::PROXY_HUMAN; } is_droid() const282 bool is_droid() const { return info_.proxy_controller == PROXY_CONTROLLER::PROXY_AI; } is_idle() const283 bool is_idle() const { return info_.proxy_controller == PROXY_CONTROLLER::PROXY_IDLE; } 284 make_droid()285 void make_droid() { info_.proxy_controller = PROXY_CONTROLLER::PROXY_AI; } make_idle()286 void make_idle() { info_.proxy_controller = PROXY_CONTROLLER::PROXY_IDLE; } make_proxy_human()287 void make_proxy_human() { info_.proxy_controller = PROXY_CONTROLLER::PROXY_HUMAN; } clear_proxy()288 void clear_proxy() { make_proxy_human(); } 289 change_proxy(PROXY_CONTROLLER proxy)290 void change_proxy(PROXY_CONTROLLER proxy) { info_.proxy_controller = proxy; } 291 toggle_droid()292 void toggle_droid() { info_.proxy_controller = (info_.proxy_controller == PROXY_CONTROLLER::PROXY_AI ) ? PROXY_CONTROLLER::PROXY_HUMAN : PROXY_CONTROLLER::PROXY_AI; } toggle_idle()293 void toggle_idle() { info_.proxy_controller = (info_.proxy_controller == PROXY_CONTROLLER::PROXY_IDLE) ? PROXY_CONTROLLER::PROXY_HUMAN : PROXY_CONTROLLER::PROXY_IDLE; } 294 team_name() const295 const std::string& team_name() const { return info_.team_name; } user_team_name() const296 const t_string &user_team_name() const { return info_.user_team_name; } 297 void change_team(const std::string &name, const t_string &user_name); 298 flag() const299 const std::string& flag() const { return info_.flag; } flag_icon() const300 const std::string& flag_icon() const { return info_.flag_icon; } 301 set_flag(const std::string & flag)302 void set_flag(const std::string& flag) { info_.flag = flag; } set_flag_icon(const std::string & flag_icon)303 void set_flag_icon(const std::string& flag_icon) { info_.flag_icon = flag_icon; } 304 side_name() const305 const std::string& side_name() const { return info_.side_name.empty() ? info_.current_player : info_.side_name.str(); } side_name_tstr() const306 t_string side_name_tstr() const { return info_.side_name.empty() ? t_string(info_.current_player) : info_.side_name; } set_side_name(const t_string & new_name)307 void set_side_name(const t_string& new_name) {info_.side_name = new_name;} faction() const308 const std::string& faction() const { return info_.faction; } faction_name() const309 const t_string& faction_name() const { return info_.faction_name; } 310 //Returns true if the hex is shrouded/fogged for this side, or 311 //any other ally with shared vision. 312 bool shrouded(const map_location& loc) const; 313 bool fogged(const map_location& loc) const; 314 uses_shroud() const315 bool uses_shroud() const { return shroud_.enabled(); } uses_fog() const316 bool uses_fog() const { return fog_.enabled(); } fog_or_shroud() const317 bool fog_or_shroud() const { return uses_shroud() || uses_fog(); } clear_shroud(const map_location & loc)318 bool clear_shroud(const map_location& loc) { return shroud_.clear(loc.wml_x(),loc.wml_y()); } place_shroud(const map_location & loc)319 void place_shroud(const map_location& loc) { shroud_.place(loc.wml_x(),loc.wml_y()); } clear_fog(const map_location & loc)320 bool clear_fog(const map_location& loc) { return fog_.clear(loc.wml_x(),loc.wml_y()); } reshroud()321 void reshroud() { shroud_.reset(); } refog()322 void refog() { fog_.reset(); } set_shroud(bool shroud)323 void set_shroud(bool shroud) { shroud_.set_enabled(shroud); } set_fog(bool fog)324 void set_fog(bool fog) { fog_.set_enabled(fog); } 325 326 /** Merge a WML shroud map with the shroud data of this player. */ merge_shroud_map_data(const std::string & shroud_data)327 void merge_shroud_map_data(const std::string& shroud_data) { shroud_.merge(shroud_data); } 328 329 bool knows_about_team(size_t index) const; 330 /// Records hexes that were cleared of fog via WML. add_fog_override(const std::set<map_location> & hexes)331 void add_fog_override(const std::set<map_location> &hexes) { fog_clearer_.insert(hexes.begin(), hexes.end()); } 332 /// Removes the record of hexes that were cleared of fog via WML. 333 void remove_fog_override(const std::set<map_location> &hexes); 334 auto_shroud_updates() const335 bool auto_shroud_updates() const { return auto_shroud_updates_; } set_auto_shroud_updates(bool value)336 void set_auto_shroud_updates(bool value) { auto_shroud_updates_ = value; } get_disallow_observers() const337 bool get_disallow_observers() const {return info_.disallow_observers; } no_leader() const338 bool no_leader() const { return info_.no_leader; } defeat_condition() const339 DEFEAT_CONDITION defeat_condition() const { return info_.defeat_condition; } set_defeat_condition(DEFEAT_CONDITION value)340 void set_defeat_condition(DEFEAT_CONDITION value) { info_.defeat_condition = value; } 341 ///sets the defeat condition if @param value is a valid defeat condition, otherwise nothing happes. set_defeat_condition_string(const std::string & value)342 void set_defeat_condition_string(const std::string& value) { info_.defeat_condition.parse(value); } have_leader(bool value=true)343 void have_leader(bool value=true) { info_.no_leader = !value; } hidden() const344 bool hidden() const { return info_.hidden; } set_hidden(bool value)345 void set_hidden(bool value) { info_.hidden=value; } persistent() const346 bool persistent() const { return info_.persistent; } set_persistent(bool value)347 void set_persistent(bool value) { info_.persistent = value; } set_lost(bool value=true)348 void set_lost(bool value=true) { info_.lost = value; } lost() const349 bool lost() const { return info_.lost; } 350 set_carryover_percentage(int value)351 void set_carryover_percentage(int value) { info_.carryover_percentage = value; } carryover_percentage() const352 int carryover_percentage() const { return info_.carryover_percentage; } set_carryover_add(bool value)353 void set_carryover_add(bool value) { info_.carryover_add = value; } carryover_add() const354 bool carryover_add() const { return info_.carryover_add; } set_carryover_bonus(double value)355 void set_carryover_bonus(double value) { info_.carryover_bonus = value; } carryover_bonus() const356 double carryover_bonus() const { return info_.carryover_bonus; } set_carryover_gold(int value)357 void set_carryover_gold(int value) { info_.carryover_gold = value; } carryover_gold() const358 int carryover_gold() const { return info_.carryover_gold; } variables()359 config& variables() { return info_.variables; } variables() const360 const config& variables() const { return info_.variables; } 361 no_turn_confirmation() const362 bool no_turn_confirmation() const { return info_.no_turn_confirmation; } set_no_turn_confirmation(bool value)363 void set_no_turn_confirmation(bool value) { info_.no_turn_confirmation = value; } 364 365 //function which, when given a 1-based side will return the color used by that side. 366 static const color_range get_side_color_range(int side); 367 368 static color_t get_side_color(int side); 369 static color_t get_minimap_color(int side); 370 371 static std::string get_side_color_id(unsigned side); 372 static std::string get_side_color_id_from_config(const config& cfg); 373 static std::string get_side_highlight_pango(int side); 374 375 void log_recruitable() const; 376 377 /** clear the shroud, fog, and enemies cache for all teams*/ 378 static void clear_caches(); 379 380 /** get the whiteboard planned actions for this team */ get_side_actions() const381 std::shared_ptr<wb::side_actions> get_side_actions() const { return planned_actions_; } 382 383 config to_config() const; 384 share_maps() const385 bool share_maps() const { return info_.share_vision != SHARE_VISION::NONE ; } share_view() const386 bool share_view() const { return info_.share_vision == SHARE_VISION::ALL; } share_vision() const387 SHARE_VISION share_vision() const { return info_.share_vision; } 388 set_share_vision(const std::string & vision_status)389 void set_share_vision(const std::string& vision_status) { 390 info_.share_vision = SHARE_VISION::ALL; 391 info_.share_vision.parse(vision_status); 392 clear_caches(); 393 } 394 set_share_vision(SHARE_VISION vision_status)395 void set_share_vision(SHARE_VISION vision_status) { 396 info_.share_vision = vision_status; 397 clear_caches(); 398 } 399 handle_legacy_share_vision(const config & cfg)400 void handle_legacy_share_vision(const config& cfg) 401 { 402 info_.handle_legacy_share_vision(cfg); 403 } 404 std::string allied_human_teams() const; 405 chose_random() const406 bool chose_random() const 407 { 408 return info_.chose_random; 409 } 410 411 private: 412 413 const std::vector<const shroud_map*>& ally_shroud(const std::vector<team>& teams) const; 414 const std::vector<const shroud_map*>& ally_fog(const std::vector<team>& teams) const; 415 416 int gold_; 417 std::set<map_location> villages_; 418 419 shroud_map shroud_, fog_; 420 /// Stores hexes that have been cleared of fog via WML. 421 std::set<map_location> fog_clearer_; 422 423 bool auto_shroud_updates_; 424 425 team_info info_; 426 427 mutable int countdown_time_; 428 int action_bonus_count_; 429 430 recall_list_manager recall_list_; 431 std::string last_recruit_; 432 433 private: 434 void calculate_enemies(size_t index) const; 435 bool calculate_is_enemy(size_t index) const; 436 mutable boost::dynamic_bitset<> enemies_; 437 438 mutable std::vector<const shroud_map*> ally_shroud_, ally_fog_; 439 440 /** 441 * Whiteboard planned actions for this team. 442 */ 443 std::shared_ptr<wb::side_actions> planned_actions_; 444 }; 445 446 //function which will validate a side. Throws game::game_error 447 //if the side is invalid 448 void validate_side(int side); //throw game::game_error 449