The Battle for Wesnoth  1.15.2+dev
team.cpp
Go to the documentation of this file.
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 /**
16  * @file
17  * Team-management, allies, setup at start of scenario.
18  */
19 
20 #include "team.hpp"
21 
22 #include "ai/manager.hpp"
23 #include "color.hpp"
24 #include "formula/string_utils.hpp" // for VGETTEXT
25 #include "game_data.hpp"
26 #include "game_events/pump.hpp"
27 #include "lexical_cast.hpp"
28 #include "map/map.hpp"
29 #include "play_controller.hpp"
31 #include "preferences/game.hpp"
32 #include "resources.hpp"
34 #include "synced_context.hpp"
35 #include "units/types.hpp"
37 
38 #include <boost/dynamic_bitset.hpp>
39 
40 static lg::log_domain log_engine("engine");
41 #define DBG_NG LOG_STREAM(debug, log_engine)
42 #define LOG_NG LOG_STREAM(info, log_engine)
43 #define WRN_NG LOG_STREAM(warn, log_engine)
44 #define ERR_NG LOG_STREAM(err, log_engine)
45 
46 static lg::log_domain log_engine_enemies("engine/enemies");
47 #define DBG_NGE LOG_STREAM(debug, log_engine_enemies)
48 #define LOG_NGE LOG_STREAM(info, log_engine_enemies)
49 #define WRN_NGE LOG_STREAM(warn, log_engine_enemies)
50 
51 // Static member initialization
52 const int team::default_team_gold_ = 100;
53 
54 // Update this list of attributes if you change what is used to define a side
55 // (excluding those attributes used to define the side's leader).
56 const std::set<std::string> team::attributes {
57  "ai_config",
58  "carryover_add",
59  "carryover_percentage",
60  "color",
61  "controller",
62  "current_player",
63  "defeat_condition",
64  "flag",
65  "flag_icon",
66  "fog",
67  "fog_data",
68  "gold",
69  "hidden",
70  "income",
71  "no_leader",
72  "objectives",
73  "objectives_changed",
74  "persistent",
75  "lost",
76  "recall_cost",
77  "recruit",
78  "save_id",
79  "scroll_to_leader",
80  "share_vision",
81  "share_maps",
82  "share_view",
83  "shroud",
84  "shroud_data",
85  "start_gold",
86  "suppress_end_turn_confirmation",
87  "team_name",
88  "user_team_name",
89  "side_name",
90  "village_gold",
91  "village_support",
92  "is_local",
93  // Multiplayer attributes.
94  "player_id",
95  "is_host",
96  "action_bonus_count",
97  "allow_changes",
98  "allow_player",
99  "color_lock",
100  "countdown_time",
101  "disallow_observers",
102  "faction",
103  "faction_from_recruit",
104  "faction_name",
105  "faction_lock",
106  "gold_lock",
107  "income_lock",
108  "leader",
109  "leader_lock",
110  "random_leader",
111  "team_lock",
112  "terrain_liked",
113  "user_description",
114  "controller_lock",
115  "chose_random",
116  "disallow_shuffle",
117  "description"
118 };
119 
121  : gold(0)
122  , start_gold(0)
123  , income(0)
124  , income_per_village(0)
125  , support_per_village(1)
126  , minimum_recruit_price(0)
127  , recall_cost(0)
128  , can_recruit()
129  , team_name()
130  , user_team_name()
131  , side_name()
132  , faction()
133  , faction_name()
134  , save_id()
135  , current_player()
136  , countdown_time()
137  , action_bonus_count(0)
138  , flag()
139  , flag_icon()
140  , id()
141  , scroll_to_leader(true)
142  , objectives()
143  , objectives_changed(false)
144  , controller()
145  , is_local(true)
146  , defeat_condition(team::DEFEAT_CONDITION::NO_LEADER)
147  , proxy_controller(team::PROXY_CONTROLLER::PROXY_HUMAN)
148  , share_vision(team::SHARE_VISION::ALL)
149  , disallow_observers(false)
150  , allow_player(false)
151  , chose_random(false)
152  , no_leader(true)
153  , hidden(true)
154  , no_turn_confirmation(false)
155  , color()
156  , side(1)
157  , persistent(false)
158  , lost(false)
159  , carryover_percentage(game_config::gold_carryover_percentage)
160  , carryover_add(false)
161  , carryover_bonus(0)
162  , carryover_gold(0)
163 {
164 }
165 
167 {
168  gold = cfg["gold"];
169  income = cfg["income"];
170  team_name = cfg["team_name"].str();
171  user_team_name = cfg["user_team_name"];
172  side_name = cfg["side_name"];
173  faction = cfg["faction"].str();
174  faction_name = cfg["faction_name"];
175  save_id = cfg["save_id"].str();
176  current_player = cfg["current_player"].str();
177  countdown_time = cfg["countdown_time"].str();
178  action_bonus_count = cfg["action_bonus_count"];
179  flag = cfg["flag"].str();
180  flag_icon = cfg["flag_icon"].str();
181  id = cfg["id"].str();
182  scroll_to_leader = cfg["scroll_to_leader"].to_bool(true);
183  objectives = cfg["objectives"];
184  objectives_changed = cfg["objectives_changed"].to_bool();
185  disallow_observers = cfg["disallow_observers"].to_bool();
186  allow_player = cfg["allow_player"].to_bool(true);
187  chose_random = cfg["chose_random"].to_bool(false);
188  no_leader = cfg["no_leader"].to_bool();
189  defeat_condition = cfg["defeat_condition"].to_enum<team::DEFEAT_CONDITION>(team::DEFEAT_CONDITION::NO_LEADER);
190  lost = cfg["lost"].to_bool(false);
191  hidden = cfg["hidden"].to_bool();
192  no_turn_confirmation = cfg["suppress_end_turn_confirmation"].to_bool();
193  side = cfg["side"].to_int(1);
194  carryover_percentage = cfg["carryover_percentage"].to_int(game_config::gold_carryover_percentage);
195  carryover_add = cfg["carryover_add"].to_bool(false);
196  carryover_bonus = cfg["carryover_bonus"].to_double(1);
197  carryover_gold = cfg["carryover_gold"].to_int(0);
198  variables = cfg.child_or_empty("variables");
199  is_local = cfg["is_local"].to_bool(true);
200 
202 
203  // If starting new scenario override settings from [ai] tags
206 
208  if(cfg.has_attribute("ai_config")) {
209  ai::manager::get_singleton().add_ai_for_side_from_file(side, cfg["ai_config"], true);
210  } else {
212  }
213  }
214 
215  std::vector<std::string> recruits = utils::split(cfg["recruit"]);
216  can_recruit.insert(recruits.begin(), recruits.end());
217 
218  // at the start of a scenario "start_gold" is not set, we need to take the
219  // value from the gold setting (or fall back to the gold default)
220  if(!cfg["start_gold"].empty()) {
221  start_gold = cfg["start_gold"];
222  } else if(!cfg["gold"].empty()) {
223  start_gold = gold;
224  } else {
226  }
227 
228  if(team_name.empty()) {
229  team_name = cfg["side"].str();
230  }
231 
232  if(save_id.empty()) {
233  save_id = id;
234  }
235 
236  income_per_village = cfg["village_gold"].to_int(game_config::village_income);
237  recall_cost = cfg["recall_cost"].to_int(game_config::recall_cost);
238 
239  const std::string& village_support = cfg["village_support"];
240  if(village_support.empty()) {
242  } else {
244  }
245 
246  controller = team::CONTROLLER::AI;
247  controller.parse(cfg["controller"].str());
248 
249  // TODO: Why do we read disallow observers differently when controller is empty?
250  if(controller == CONTROLLER::EMPTY) {
251  disallow_observers = cfg["disallow_observers"].to_bool(true);
252  }
253 
254  // override persistence flag if it is explicitly defined in the config
255  // by default, persistence of a team is set depending on the controller
256  persistent = cfg["persistent"].to_bool(this->controller == CONTROLLER::HUMAN);
257 
258  //========================================================
259  // END OF MESSY CODE
260 
261  // Share_view and share_maps can't both be enabled,
262  // so share_view overrides share_maps.
263  share_vision = cfg["share_vision"].to_enum<team::SHARE_VISION>(team::SHARE_VISION::ALL);
265 
266  LOG_NG << "team_info::team_info(...): team_name: " << team_name << ", share_vision: " << share_vision << ".\n";
267 }
268 
270 {
271  if(cfg.has_attribute("share_view") || cfg.has_attribute("share_maps")) {
272  if(cfg["share_view"].to_bool()) {
273  share_vision = team::SHARE_VISION::ALL;
274  } else if(cfg["share_maps"].to_bool(true)) {
275  share_vision = team::SHARE_VISION::SHROUD;
276  } else {
278  }
279  }
280 }
281 
283 {
284  cfg["gold"] = gold;
285  cfg["start_gold"] = start_gold;
286  cfg["income"] = income;
287  cfg["team_name"] = team_name;
288  cfg["user_team_name"] = user_team_name;
289  cfg["side_name"] = side_name;
290  cfg["faction"] = faction;
291  cfg["faction_name"] = faction_name;
292  cfg["save_id"] = save_id;
293  cfg["current_player"] = current_player;
294  cfg["flag"] = flag;
295  cfg["flag_icon"] = flag_icon;
296  cfg["id"] = id;
297  cfg["objectives"] = objectives;
298  cfg["objectives_changed"] = objectives_changed;
299  cfg["countdown_time"] = countdown_time;
300  cfg["action_bonus_count"] = action_bonus_count;
301  cfg["village_gold"] = income_per_village;
302  cfg["village_support"] = support_per_village;
303  cfg["recall_cost"] = recall_cost;
304  cfg["disallow_observers"] = disallow_observers;
305  cfg["allow_player"] = allow_player;
306  cfg["chose_random"] = chose_random;
307  cfg["no_leader"] = no_leader;
308  cfg["defeat_condition"] = defeat_condition;
309  cfg["hidden"] = hidden;
310  cfg["suppress_end_turn_confirmation"] = no_turn_confirmation;
311  cfg["scroll_to_leader"] = scroll_to_leader;
312  cfg["controller"] = controller;
313  cfg["recruit"] = utils::join(can_recruit);
314  cfg["share_vision"] = share_vision;
315 
316  cfg["color"] = color;
317  cfg["persistent"] = persistent;
318  cfg["lost"] = lost;
319  cfg["carryover_percentage"] = carryover_percentage;
320  cfg["carryover_add"] = carryover_add;
321  cfg["carryover_bonus"] = carryover_bonus;
322  cfg["carryover_gold"] = carryover_gold;
323 
324  if(!variables.empty()) {
325  cfg.add_child("variables", variables);
326  }
327 
329 }
330 
332  : gold_(0)
333  , villages_()
334  , shroud_()
335  , fog_()
336  , fog_clearer_()
337  , auto_shroud_updates_(true)
338  , info_()
339  , countdown_time_(0)
341  , recall_list_()
342  , last_recruit_()
343  , enemies_()
344  , ally_shroud_()
345  , ally_fog_()
346  , planned_actions_()
347 {
348 }
349 
351 {
352 }
353 
354 void team::build(const config& cfg, const gamemap& map, int gold)
355 {
356  gold_ = gold;
357  info_.read(cfg);
358 
359  fog_.set_enabled(cfg["fog"].to_bool());
360  fog_.read(cfg["fog_data"]);
361  shroud_.set_enabled(cfg["shroud"].to_bool());
362  shroud_.read(cfg["shroud_data"]);
363  auto_shroud_updates_ = cfg["auto_shroud"].to_bool(auto_shroud_updates_);
364 
365  LOG_NG << "team::team(...): team_name: " << info_.team_name << ", shroud: " << uses_shroud()
366  << ", fog: " << uses_fog() << ".\n";
367 
368  // Load the WML-cleared fog.
369  const config& fog_override = cfg.child("fog_override");
370  if(fog_override) {
371  const std::vector<map_location> fog_vector
372  = map.parse_location_range(fog_override["x"], fog_override["y"], true);
373  fog_clearer_.insert(fog_vector.begin(), fog_vector.end());
374  }
375 
376  // To ensure some minimum starting gold,
377  // gold is the maximum of 'gold' and what is given in the config file
378  gold_ = std::max(gold, info_.gold);
379  if(gold_ != info_.gold) {
381  }
382 
383  // Old code was doing:
384  // info_.start_gold = std::to_string(gold) + " (" + info_.start_gold + ")";
385  // Was it correct?
386 
387  // Load in the villages the side controls at the start
388  for(const config& v : cfg.child_range("village")) {
389  map_location loc(v);
390  if(map.is_village(loc)) {
391  villages_.insert(loc);
392  } else {
393  WRN_NG << "[side] " << current_player() << " [village] points to a non-village location " << loc
394  << std::endl;
395  }
396  }
397 
398  countdown_time_ = cfg["countdown_time"];
399  action_bonus_count_ = cfg["action_bonus_count"];
400 
401  planned_actions_.reset(new wb::side_actions());
402  planned_actions_->set_team_index(info_.side - 1);
403 }
404 
405 void team::write(config& cfg) const
406 {
407  info_.write(cfg);
408  cfg["auto_shroud"] = auto_shroud_updates_;
409  cfg["shroud"] = uses_shroud();
410  cfg["fog"] = uses_fog();
411  cfg["gold"] = gold_;
412 
413  // Write village locations
414  for(const map_location& loc : villages_) {
415  loc.write(cfg.add_child("village"));
416  }
417 
418  cfg["shroud_data"] = shroud_.write();
419  cfg["fog_data"] = fog_.write();
420  if(!fog_clearer_.empty())
421  write_location_range(fog_clearer_, cfg.add_child("fog_override"));
422 
423  cfg["countdown_time"] = countdown_time_;
424  cfg["action_bonus_count"] = action_bonus_count_;
425 }
426 
427 void team::fix_villages(const gamemap &map)
428 {
429  for (auto it = villages_.begin(); it != villages_.end(); ) {
430  if (map.is_village(*it)) {
431  ++it;
432  }
433  else {
434  it = villages_.erase(it);
435  }
436  }
437 }
438 
440 {
441  villages_.insert(loc);
443 
444  if(gamedata) {
445  config::attribute_value& var = gamedata->get_variable("owner_side");
446  const config::attribute_value old_value = var;
447  var = owner_side;
448 
449  // During team building, game_events pump is not guaranteed to exist yet. (At current revision.) We skip capture
450  // events in this case.
452  res = resources::game_events->pump().fire("capture", loc);
453  }
454 
455  if(old_value.blank()) {
456  gamedata->clear_variable("owner_side");
457  } else {
458  var = old_value;
459  }
460  }
461 
462  return res;
463 }
464 
466 {
467  const std::set<map_location>::const_iterator vil = villages_.find(loc);
468  assert(vil != villages_.end());
469  villages_.erase(vil);
470 }
471 
472 void team::set_recruits(const std::set<std::string>& recruits)
473 {
477 }
478 
479 void team::add_recruit(const std::string& recruit)
480 {
481  info_.can_recruit.insert(recruit);
484 }
485 
487 {
490  }
491  int min = 20;
492  for(std::string recruit : info_.can_recruit) {
493  const unit_type* ut = unit_types.find(recruit);
494  if(!ut) {
495  continue;
496  } else {
497  if(ut->cost() < min) {
498  min = ut->cost();
499  }
500  }
501  }
502 
504 
506 }
507 
508 void team::calculate_enemies(std::size_t index) const
509 {
510  if(!resources::gameboard || index >= resources::gameboard->teams().size()) {
511  return;
512  }
513 
514  while(enemies_.size() <= index) {
515  enemies_.push_back(calculate_is_enemy(enemies_.size()));
516  }
517 }
518 
519 bool team::calculate_is_enemy(std::size_t index) const
520 {
521  // We're not enemies of ourselves
522  if(&resources::gameboard->teams()[index] == this) {
523  return false;
524  }
525 
526  // We are friends with anyone who we share a teamname with
527  std::vector<std::string> our_teams = utils::split(info_.team_name);
528  std::vector<std::string> their_teams = utils::split(resources::gameboard->teams()[index].info_.team_name);
529 
530  LOG_NGE << "team " << info_.side << " calculates if it has enemy in team " << index + 1 << "; our team_name ["
531  << info_.team_name << "], their team_name is [" << resources::gameboard->teams()[index].info_.team_name
532  << "]" << std::endl;
533 
534  for(const std::string& t : our_teams) {
535  if(std::find(their_teams.begin(), their_teams.end(), t) != their_teams.end()) {
536  LOG_NGE << "team " << info_.side << " found same team name [" << t << "] in team " << index + 1
537  << std::endl;
538  return false;
539  } else {
540  LOG_NGE << "team " << info_.side << " not found same team name [" << t << "] in team " << index + 1
541  << std::endl;
542  }
543  }
544 
545  LOG_NGE << "team " << info_.side << " has enemy in team " << index + 1 << std::endl;
546  return true;
547 }
548 
549 namespace
550 {
551 class controller_server_choice : public synced_context::server_choice
552 {
553 public:
554  controller_server_choice(team::CONTROLLER new_controller, const team& team)
555  : new_controller_(new_controller)
556  , team_(team)
557  {
558  }
559 
560  /// We are in a game with no mp server and need to do this choice locally
561  virtual config local_choice() const
562  {
563  return config{"controller", new_controller_, "is_local", true};
564  }
565 
566  /// tThe request which is sent to the mp server.
567  virtual config request() const
568  {
569  return config{
570  "new_controller", new_controller_, "old_controller", team_.controller(), "side", team_.side(),
571  };
572  }
573 
574  virtual const char* name() const
575  {
576  return "change_controller_wml";
577  }
578 
579 private:
580  team::CONTROLLER new_controller_;
581  const team& team_;
582 };
583 } // end anon namespace
584 
585 void team::change_controller_by_wml(const std::string& new_controller_string)
586 {
587  CONTROLLER new_controller;
588  if(!new_controller.parse(new_controller_string)) {
589  WRN_NG << "ignored attempt to change controller to " << new_controller_string << std::endl;
590  return;
591  }
592 
593  if(new_controller == CONTROLLER::EMPTY && resources::controller->current_side() == this->side()) {
594  WRN_NG << "ignored attempt to change the currently playing side's controller to 'null'" << std::endl;
595  return;
596  }
597 
598  config choice = synced_context::ask_server_choice(controller_server_choice(new_controller, *this));
599  if(!new_controller.parse(choice["controller"])) {
600  // TODO: this should be more than a ERR_NG message.
601  // GL-2016SEP02 Oh? So why was ERR_NG defined as warning level? Making the call fit the definition.
602  WRN_NG << "Received an invalid controller string from the server" << choice["controller"] << std::endl;
603  }
604 
605  if(!resources::controller->is_replay()) {
606  set_local(choice["is_local"].to_bool());
607  }
608 
609  if(playsingle_controller* pc = dynamic_cast<playsingle_controller*>(resources::controller)) {
610  if(pc->current_side() == side() && new_controller != controller()) {
611  pc->set_player_type_changed();
612  }
613  }
614 
615  change_controller(new_controller);
616 }
617 
618 void team::change_team(const std::string& name, const t_string& user_name)
619 {
620  info_.team_name = name;
621 
622  if(!user_name.empty()) {
623  info_.user_team_name = user_name;
624  } else {
625  info_.user_team_name = name;
626  }
627 
628  clear_caches();
629 }
630 
632 {
633  // Reset the cache of allies for all teams
635  for(auto& t : resources::gameboard->teams()) {
636  t.enemies_.clear();
637  t.ally_shroud_.clear();
638  t.ally_fog_.clear();
639  }
640  }
641 }
642 
643 void team::set_objectives(const t_string& new_objectives, bool silently)
644 {
645  info_.objectives = new_objectives;
646 
647  if(!silently) {
648  info_.objectives_changed = true;
649  }
650 }
651 
652 bool team::shrouded(const map_location& loc) const
653 {
654  if(!resources::gameboard) {
655  return shroud_.value(loc.wml_x(), loc.wml_y());
656  }
657 
658  return shroud_.shared_value(ally_shroud(resources::gameboard->teams()), loc.wml_x(), loc.wml_y());
659 }
660 
661 bool team::fogged(const map_location& loc) const
662 {
663  if(shrouded(loc)) {
664  return true;
665  }
666 
667  // Check for an override of fog.
668  if(fog_clearer_.count(loc) > 0) {
669  return false;
670  }
671 
672  if(!resources::gameboard) {
673  return fog_.value(loc.wml_x(), loc.wml_y());
674  }
675 
676  return fog_.shared_value(ally_fog(resources::gameboard->teams()), loc.wml_x(), loc.wml_y());
677 }
678 
679 const std::vector<const team::shroud_map*>& team::ally_shroud(const std::vector<team>& teams) const
680 {
681  if(ally_shroud_.empty()) {
682  for(std::size_t i = 0; i < teams.size(); ++i) {
683  if(!is_enemy(i + 1) && (&(teams[i]) == this || teams[i].share_view() || teams[i].share_maps())) {
684  ally_shroud_.push_back(&(teams[i].shroud_));
685  }
686  }
687  }
688 
689  return ally_shroud_;
690 }
691 
692 const std::vector<const team::shroud_map*>& team::ally_fog(const std::vector<team>& teams) const
693 {
694  if(ally_fog_.empty()) {
695  for(std::size_t i = 0; i < teams.size(); ++i) {
696  if(!is_enemy(i + 1) && (&(teams[i]) == this || teams[i].share_view())) {
697  ally_fog_.push_back(&(teams[i].fog_));
698  }
699  }
700  }
701 
702  return ally_fog_;
703 }
704 
705 bool team::knows_about_team(std::size_t index) const
706 {
707  const team& t = resources::gameboard->teams()[index];
708 
709  // We know about our own team
710  if(this == &t) {
711  return true;
712  }
713 
714  // If we aren't using shroud or fog, then we know about everyone
715  if(!uses_shroud() && !uses_fog()) {
716  return true;
717  }
718 
719  // We don't know about enemies
720  if(is_enemy(index + 1)) {
721  return false;
722  }
723 
724  // We know our human allies.
725  if(t.is_human()) {
726  return true;
727  }
728 
729  // We know about allies we're sharing maps with
730  if(share_maps() && t.uses_shroud()) {
731  return true;
732  }
733 
734  // We know about allies we're sharing view with
735  if(share_view() && (t.uses_fog() || t.uses_shroud())) {
736  return true;
737  }
738 
739  return false;
740 }
741 
742 /**
743  * Removes the record of hexes that were cleared of fog via WML.
744  * @param[in] hexes The hexes to no longer keep clear.
745  */
746 void team::remove_fog_override(const std::set<map_location>& hexes)
747 {
748  // Take a set difference.
749  std::vector<map_location> result(fog_clearer_.size());
751  std::set_difference(fog_clearer_.begin(), fog_clearer_.end(), hexes.begin(), hexes.end(), result.begin());
752 
753  // Put the result into fog_clearer_.
754  fog_clearer_.clear();
755  fog_clearer_.insert(result.begin(), result_end);
756 }
757 
759 {
760  if(!resources::gameboard) {
761  return;
762  }
763 
764  if(side < 1 || side > static_cast<int>(resources::gameboard->teams().size())) {
765  throw game::game_error("invalid side(" + std::to_string(side) + ") found in unit definition");
766  }
767 }
768 
769 bool team::shroud_map::clear(int x, int y)
770 {
771  if(enabled_ == false || x < 0 || y < 0) {
772  return false;
773  }
774 
775  if(x >= static_cast<int>(data_.size())) {
776  data_.resize(x + 1);
777  }
778 
779  if(y >= static_cast<int>(data_[x].size())) {
780  data_[x].resize(y + 1);
781  }
782 
783  if(data_[x][y] == false) {
784  data_[x][y] = true;
785  return true;
786  }
787 
788  return false;
789 }
790 
791 void team::shroud_map::place(int x, int y)
792 {
793  if(enabled_ == false || x < 0 || y < 0) {
794  return;
795  }
796 
797  if(x >= static_cast<int>(data_.size())) {
798  DBG_NG << "Couldn't place shroud on invalid x coordinate: (" << x << ", " << y
799  << ") - max x: " << data_.size() - 1 << "\n";
800  } else if(y >= static_cast<int>(data_[x].size())) {
801  DBG_NG << "Couldn't place shroud on invalid y coordinate: (" << x << ", " << y
802  << ") - max y: " << data_[x].size() - 1 << "\n";
803  } else {
804  data_[x][y] = false;
805  }
806 }
807 
809 {
810  if(enabled_ == false) {
811  return;
812  }
813 
814  for(auto& i : data_) {
815  std::fill(i.begin(), i.end(), false);
816  }
817 }
818 
819 bool team::shroud_map::value(int x, int y) const
820 {
821  if(!enabled_) {
822  return false;
823  }
824 
825  // Locations for which we have no data are assumed to still be covered.
826  if(x < 0 || x >= static_cast<int>(data_.size())) {
827  return true;
828  }
829 
830  if(y < 0 || y >= static_cast<int>(data_[x].size())) {
831  return true;
832  }
833 
834  // data_ stores whether or not a location has been cleared, while
835  // we want to return whether or not a location is covered.
836  return !data_[x][y];
837 }
838 
839 bool team::shroud_map::shared_value(const std::vector<const shroud_map*>& maps, int x, int y) const
840 {
841  if(!enabled_) {
842  return false;
843  }
844 
845  // A quick abort:
846  if(x < 0 || y < 0) {
847  return true;
848  }
849 
850  // A tile is uncovered if it is uncovered on any shared map.
851  for(const shroud_map* const shared_map : maps) {
852  if(shared_map->enabled_ && !shared_map->value(x, y)) {
853  return false;
854  }
855  }
856 
857  return true;
858 }
859 
860 std::string team::shroud_map::write() const
861 {
862  std::stringstream shroud_str;
863  for(const auto& sh : data_) {
864  shroud_str << '|';
865 
866  for(bool i : sh) {
867  shroud_str << (i ? '1' : '0');
868  }
869 
870  shroud_str << '\n';
871  }
872 
873  return shroud_str.str();
874 }
875 
876 void team::shroud_map::read(const std::string& str)
877 {
878  data_.clear();
879 
880  for(const char sh : str) {
881  if(sh == '|') {
882  data_.resize(data_.size() + 1);
883  }
884 
885  if(data_.empty() == false) {
886  if(sh == '1') {
887  data_.back().push_back(true);
888  } else if(sh == '0') {
889  data_.back().push_back(false);
890  }
891  }
892  }
893 }
894 
895 void team::shroud_map::merge(const std::string& str)
896 {
897  int x = 0, y = 0;
898  for(std::size_t i = 1; i < str.length(); ++i) {
899  if(str[i] == '|') {
900  y = 0;
901  x++;
902  } else if(str[i] == '1') {
903  clear(x, y);
904  y++;
905  } else if(str[i] == '0') {
906  y++;
907  }
908  }
909 }
910 
911 bool team::shroud_map::copy_from(const std::vector<const shroud_map*>& maps)
912 {
913  if(enabled_ == false) {
914  return false;
915  }
916 
917  bool cleared = false;
918  for(const shroud_map* m : maps) {
919  if(m->enabled_ == false) {
920  continue;
921  }
922 
923  const std::vector<std::vector<bool>>& v = m->data_;
924  for(std::size_t x = 0; x != v.size(); ++x) {
925  for(std::size_t y = 0; y != v[x].size(); ++y) {
926  if(v[x][y]) {
927  cleared |= clear(x, y);
928  }
929  }
930  }
931  }
932 
933  return cleared;
934 }
935 
937 {
938  std::string index = get_side_color_id(side);
940 
941  if(gp != game_config::team_rgb_range.end()) {
942  return (gp->second);
943  }
944 
945  return color_range({255, 0, 0}, {255, 255, 255}, {0, 0, 0}, {255, 0, 0});
946 }
947 
949 {
950  return get_side_color_range(side).mid();
951 }
952 
954 {
955  // Note: use mid() instead of rep() unless
956  // high contrast is needed over a map or minimap!
957  return get_side_color_range(side).rep();
958 }
959 
960 std::string team::get_side_color_id(unsigned side)
961 {
962  try {
963  const unsigned index = side - 1;
964 
965  // If no gameboard (and by extension, team list) is available, use the default side color.
966  if(!resources::gameboard) {
967  return game_config::default_colors.at(index);
968  }
969 
970  // Else, try to fetch the color from the side's config.
971  const std::string& side_color = resources::gameboard->teams().at(index).color();
972 
973  if(!side_color.empty()) {
974  return side_color;
975  }
976 
977  // If the side color data was empty, fall back to the default color. This should only
978  // happen if the side data hadn't been initialized yet, which is the case if this function
979  // is being called to set up said side data. :P
980  return game_config::default_colors.at(index);
981  } catch(const std::out_of_range&) {
982  // Side index was invalid! Coloring will fail!
983  return "";
984  }
985 }
986 
988 {
989  const std::string& color_id = team::get_side_color_id(side);
990  const auto& rgb_name = game_config::team_rgb_name[color_id];
991  if(rgb_name.empty())
992  // TRANSLATORS: $color_id is the internal identifier of a side color, for example, 'lightred'.
993  // Translate the quotation marks only; leave "color_id" untranslated, as it's a variable name.
994  return VGETTEXT("“$color_id”", {{ "color_id", color_id }});
995  else
996  return rgb_name;
997 }
998 
1000 {
1001  const config::attribute_value& c = cfg["color"];
1002 
1003  // If no color key or value was provided, use the given color for that side.
1004  // If outside a game context (ie, where a list of teams has been constructed),
1005  // this will just be the side's default color.
1006  if(c.blank() || c.empty()) {
1007  return get_side_color_id(cfg["side"].to_unsigned());
1008  }
1009 
1010  // Do the same as above for numeric color key values.
1011  if(unsigned side = c.to_unsigned()) {
1012  return get_side_color_id(side);
1013  }
1014 
1015  // Else, we should have a color id at this point. Return it.
1016  return c.str();
1017 }
1018 
1020 {
1021  return get_side_color_range(side).mid().to_hex_string();
1022 }
1023 
1025 {
1026  LOG_NG << "Adding recruitable units: \n";
1027  for(const std::string& recruit : info_.can_recruit) {
1028  LOG_NG << recruit << std::endl;
1029  }
1030 
1031  LOG_NG << "Added all recruitable units\n";
1032 }
1033 
1035 {
1036  config cfg;
1037  config& result = cfg.add_child("side");
1038  write(result);
1039  return result;
1040 }
1041 
1042 std::string team::allied_human_teams() const
1043 {
1044  std::vector<int> res;
1045  for(const team& t : resources::gameboard->teams()) {
1046  if(!t.is_enemy(this->side()) && t.is_human()) {
1047  res.push_back(t.side());
1048  }
1049  }
1050 
1051  return utils::join(res);
1052 }
play_controller * controller
Definition: resources.cpp:21
bool empty() const
Tests for an attribute that either was never set or was set to "".
std::string id
Definition: team.hpp:125
bool allow_player
Definition: team.hpp:145
static const int default_team_gold_
Definition: team.hpp:166
void place(int x, int y)
Definition: team.cpp:791
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:420
bool no_turn_confirmation
Definition: team.hpp:149
std::string last_recruit_
Definition: team.hpp:435
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1271
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:92
DEFEAT_CONDITION defeat_condition
Definition: team.hpp:138
std::map< std::string, color_range > team_rgb_range
unsigned to_unsigned(unsigned def=0) const
SHARE_VISION share_vision
Definition: team.hpp:143
int village_support
Definition: game_config.cpp:55
void handle_legacy_share_vision(const config &cfg)
Definition: team.cpp:269
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
CONTROLLER controller
Definition: team.hpp:136
void set_objectives(const t_string &new_objectives, bool silently=false)
Definition: team.cpp:643
int carryover_gold
Definition: team.hpp:161
static std::string get_side_color_id_from_config(const config &cfg)
Definition: team.cpp:999
static manager & get_singleton()
Definition: manager.hpp:151
#define DBG_NG
Definition: team.cpp:41
int minimum_recruit_price() const
Definition: team.cpp:486
void write(config &cfg) const
Definition: team.cpp:282
Variant for storing WML attributes.
New lexcical_cast header.
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
static const color_range get_side_color_range(int side)
Definition: team.cpp:936
static bool has_manager()
Definition: manager.hpp:157
int support_per_village
Definition: team.hpp:107
bool copy_from(const std::vector< const shroud_map *> &maps)
Definition: team.cpp:911
t_string objectives
Definition: team.hpp:129
void calculate_enemies(std::size_t index) const
Definition: team.cpp:508
bool share_view() const
Definition: team.hpp:390
void fix_villages(const gamemap &map)
Definition: team.cpp:427
static const std::set< std::string > attributes
Stores the attributes recognized by [side].
Definition: team.hpp:174
child_itors child_range(config_key_type key)
Definition: config.cpp:362
void clear(const std::string &key)
Definition: general.cpp:205
bool value(int x, int y) const
Definition: team.cpp:819
int wml_x() const
Definition: location.hpp:157
#define WRN_NG
Definition: team.cpp:43
unit_type_data unit_types
Definition: types.cpp:1529
void build(const config &cfg, const gamemap &map, int gold=default_team_gold_)
Definition: team.cpp:354
int minimum_recruit_price
Definition: team.hpp:108
team()
Definition: team.cpp:331
t_string faction_name
Definition: team.hpp:115
std::shared_ptr< wb::side_actions > planned_actions_
Whiteboard planned actions for this team.
Definition: team.hpp:447
std::string flag_icon
Definition: team.hpp:123
bool objectives_changed
< Team&#39;s objectives for the current level.
Definition: team.hpp:134
void add_recruit(const std::string &)
Definition: team.cpp:479
-file sdl_utils.hpp
void change_controller(const std::string &new_controller)
Definition: team.hpp:275
std::string faction
Definition: team.hpp:114
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
int village_support() const
Definition: team.hpp:199
A single unit type that the player may recruit.
Definition: types.hpp:42
double carryover_bonus
Definition: team.hpp:160
game_data * gamedata
Definition: resources.cpp:22
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:62
bool calculate_is_enemy(std::size_t index) const
Definition: team.cpp:519
static std::string get_side_highlight_pango(int side)
Definition: team.cpp:1019
std::string flag_icon
bool uses_fog() const
Definition: team.hpp:319
int gold() const
Definition: team.hpp:189
shroud_map shroud_
Definition: team.hpp:423
static const t_string get_side_color_name_for_UI(unsigned side)
Definition: team.cpp:987
static lg::log_domain log_engine_enemies("engine/enemies")
bool knows_about_team(std::size_t index) const
Definition: team.cpp:705
team_info info_
Definition: team.hpp:429
bool add_ai_for_side_from_file(side_number side, const std::string &file, bool replace=true)
Adds active AI for specified side from file.
Definition: manager.cpp:607
std::string team_name
Definition: team.hpp:111
void write(config &cfg) const
Definition: team.cpp:405
void raise_recruit_list_changed()
Notifies all observers of &#39;ai_recruit_list_changed&#39; event.
Definition: manager.cpp:440
std::vector< map_location > parse_location_range(const std::string &xvals, const std::string &yvals, bool with_border=false) const
Parses ranges of locations into a vector of locations, using this map&#39;s dimensions as bounds...
Definition: map.cpp:416
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
std::string allied_human_teams() const
Definition: team.cpp:1042
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
bool chose_random
Definition: team.hpp:146
int cost() const
Definition: types.hpp:161
bool is_local
Definition: team.hpp:137
void read(const config &cfg)
Definition: team.cpp:166
bool uses_shroud() const
Definition: team.hpp:318
config variables
Definition: team.hpp:162
int wml_y() const
Definition: location.hpp:158
const int gold_carryover_percentage
Default percentage gold carried over to the next scenario.
Definition: game_config.cpp:63
void set_local(bool local)
Definition: team.hpp:272
bool blank() const
Tests for an attribute that was never set.
game_board * gameboard
Definition: resources.cpp:20
bool shared_value(const std::vector< const shroud_map *> &maps, int x, int y) const
Definition: team.cpp:839
Encapsulates the map of the game.
Definition: map.hpp:36
bool disallow_observers
Definition: team.hpp:144
bool is_enemy(int n) const
Definition: team.hpp:243
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
void merge(const std::string &shroud_data)
Definition: team.cpp:895
const std::vector< const shroud_map * > & ally_fog(const std::vector< team > &teams) const
Definition: team.cpp:692
bool persistent
Definition: team.hpp:154
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
bool auto_shroud_updates_
Definition: team.hpp:427
void remove_fog_override(const std::set< map_location > &hexes)
Removes the record of hexes that were cleared of fog via WML.
Definition: team.cpp:746
std::vector< const shroud_map * > ally_fog_
Definition: team.hpp:442
game_events::manager * game_events
Definition: resources.cpp:24
boost::dynamic_bitset enemies_
Definition: team.hpp:440
static std::string get_side_color_id(unsigned side)
Definition: team.cpp:960
Encapsulates the map of the game.
Definition: location.hpp:42
bool shrouded(const map_location &loc) const
Definition: team.cpp:652
static void clear_caches()
clear the shroud, fog, and enemies cache for all teams
Definition: team.cpp:631
std::string current_player
Definition: team.hpp:118
int start_gold
Definition: team.hpp:104
std::size_t i
Definition: function.cpp:933
virtual ~team()
Definition: team.cpp:350
void write_location_range(const std::set< map_location > &locs, config &cfg)
Write a set of locations into a config using ranges, adding keys x=x1,..,xn and y=y1a-y1b,..,yna-ynb.
Definition: location.cpp:398
int carryover_percentage
Definition: team.hpp:157
Default, unset return value.
Definition: retval.hpp:31
void read(const std::string &shroud_data)
Definition: team.cpp:876
int countdown_time_
Definition: team.hpp:431
void set_enabled(bool enabled)
Definition: team.hpp:92
Game configuration data as global variables.
Definition: build_info.cpp:49
std::set< map_location > villages_
Definition: team.hpp:421
int village_income
Definition: game_config.cpp:54
#define LOG_NGE
Definition: team.cpp:48
std::string to_hex_string() const
Returns the stored color in rrggbb hex format.
Definition: color.cpp:97
Define the game&#39;s event mechanism.
A color range definition is made of four reference RGB colors, used for calculating conversions from ...
Definition: color_range.hpp:50
const std::string & current_player() const
Definition: team.hpp:234
int recall_cost
Definition: team.hpp:109
static t_string from_serialized(const std::string &string)
Definition: tstring.hpp:149
void validate_side(int side)
Definition: team.cpp:758
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
bool add_ai_for_side_from_config(side_number side, const config &cfg, bool replace=true)
Adds active AI for specified side from cfg.
Definition: manager.cpp:618
std::vector< const shroud_map * > ally_shroud_
Definition: team.hpp:442
recall_list_manager recall_list_
Definition: team.hpp:434
void change_controller_by_wml(const std::string &new_controller)
Definition: team.cpp:585
bool clear(int x, int y)
Definition: team.cpp:769
static lg::log_domain log_engine("engine")
config & add_child(config_key_type key)
Definition: config.cpp:476
#define LOG_NG
Definition: team.cpp:42
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:486
bool is_village(const map_location &loc) const
Definition: map.cpp:65
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 ...
Definition: team.cpp:439
bool is_human() const
Definition: team.hpp:264
static color_t get_minimap_color(int side)
Definition: team.cpp:953
bool empty() const
Definition: tstring.hpp:182
void reset()
Definition: team.cpp:808
bool share_maps() const
Definition: team.hpp:389
std::set< map_location > fog_clearer_
Stores hexes that have been cleared of fog via WML.
Definition: team.hpp:425
double t
Definition: astarsearch.cpp:64
t_string side_name
Definition: team.hpp:113
color_t rep() const
High-contrast shade, intended for the minimap markers.
Definition: color_range.hpp:96
bool find(E event, F functor)
Tests whether an event handler is available.
void lose_village(const map_location &)
Definition: team.cpp:465
shroud_map fog_
Definition: team.hpp:423
std::string color
Definition: team.hpp:151
int income_per_village
Definition: team.hpp:106
int gold_
Definition: team.hpp:420
t_string user_team_name
Definition: team.hpp:112
static color_t get_side_color(int side)
Definition: team.cpp:948
bool carryover_add
Definition: team.hpp:158
int action_bonus_count_
Definition: team.hpp:432
int action_bonus_count
Definition: team.hpp:120
void log_recruitable() const
Definition: team.cpp:1024
void change_team(const std::string &name, const t_string &user_name)
Definition: team.cpp:618
std::set< std::string > can_recruit
Definition: team.hpp:110
std::string write() const
Definition: team.cpp:860
bool fogged(const map_location &loc) const
Definition: team.cpp:661
std::string save_id
Definition: team.hpp:116
bool translatable() const
Definition: tstring.hpp:188
game_events::wml_event_pump & pump()
Definition: manager.cpp:229
void clear_variable(const std::string &varname)
Clears attributes config children does nothing if varname is no valid variable name.
Definition: game_data.cpp:114
void set_recruits(const std::set< std::string > &recruits)
Definition: team.cpp:472
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:453
const std::vector< const shroud_map * > & ally_shroud(const std::vector< team > &teams) const
Definition: team.cpp:679
std::vector< std::string > default_colors
std::tuple< bool, bool > pump_result_t
Definition: fwd.hpp:28
int side() const
Definition: team.hpp:188
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
mock_char c
std::string flag
Definition: team.hpp:122
std::map< std::string, t_string > team_rgb_name
This internal whiteboard class holds the planned action queues for a team, and offers many utility me...
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
bool empty() const
Definition: config.cpp:884
std::string countdown_time
Definition: team.hpp:119
bool scroll_to_leader
Definition: team.hpp:127
bool no_leader
Definition: team.hpp:147
color_t mid() const
Average color shade.
Definition: color_range.hpp:87
const std::set< std::string > & recruits() const
Definition: team.hpp:223
static config ask_server_choice(const server_choice &)
std::string str(const std::string &fallback="") const
config to_config() const
Definition: team.cpp:1034
CONTROLLER controller() const
Definition: team.hpp:255