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