The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
team.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * @file
18 * Team-management, allies, setup at start of scenario.
19 */
20
21#include "team.hpp"
22
23#include "ai/manager.hpp"
24#include "color.hpp"
25#include "formula/string_utils.hpp" // for VGETTEXT
26#include "game_config.hpp"
27#include "game_data.hpp"
28#include "game_events/pump.hpp"
29#include "lexical_cast.hpp"
30#include "map/map.hpp"
31#include "play_controller.hpp"
33#include "resources.hpp"
36#include "synced_context.hpp"
37#include "units/types.hpp"
38#include "utils/general.hpp"
40
41static lg::log_domain log_engine("engine");
42#define DBG_NG LOG_STREAM(debug, log_engine)
43#define LOG_NG LOG_STREAM(info, log_engine)
44#define WRN_NG LOG_STREAM(warn, log_engine)
45#define ERR_NG LOG_STREAM(err, log_engine)
46
47static lg::log_domain log_engine_enemies("engine/enemies");
48#define DBG_NGE LOG_STREAM(debug, log_engine_enemies)
49#define LOG_NGE LOG_STREAM(info, log_engine_enemies)
50#define WRN_NGE LOG_STREAM(warn, log_engine_enemies)
51
52// Update this list of attributes if you change what is used to define a side
53// (excluding those attributes used to define the side's leader).
54const std::set<std::string> team::attributes {
55 "ai_config",
56 "carryover_add",
57 "carryover_percentage",
58 "color",
59 "controller",
60 "current_player",
61 "defeat_condition",
62 "flag",
63 "flag_icon",
64 "fog",
65 "fog_data",
66 "gold",
67 "hidden",
68 "income",
69 "no_leader",
70 "objectives",
71 "objectives_changed",
72 "persistent",
73 "lost",
74 "recall_cost",
75 "recruit",
76 "previous_recruits",
77 "save_id",
78 "scroll_to_leader",
79 "share_vision",
80 "share_maps",
81 "share_view",
82 "shroud",
83 "shroud_data",
84 "start_gold",
85 "suppress_end_turn_confirmation",
86 "team_name",
87 "user_team_name",
88 "side_name",
89 "village_gold",
90 "village_support",
91 "is_local",
92 // Multiplayer attributes.
93 "player_id",
94 "is_host",
95 "action_bonus_count",
96 "allow_changes",
97 "allow_player",
98 "color_lock",
99 "countdown_time",
100 "disallow_observers",
101 "faction",
102 "faction_from_recruit",
103 "faction_name",
104 "faction_lock",
105 "gold_lock",
106 "income_lock",
107 "leader_lock",
108 "random_leader",
109 "team_lock",
110 "terrain_liked",
111 "user_description",
112 "controller_lock",
113 "chose_random",
114 "disallow_shuffle",
115 "description"
116};
117
118// Update this list of child tags if you change what is used to define a side
119// (excluding those attributes used to define the side's leader).
120const std::set<std::string> team::tags {
121 "ai",
122 "leader",
123 "unit",
124 "variables",
125 "village"
126};
128 : gold(0)
129 , start_gold(0)
130 , income(0)
131 , income_per_village(0)
132 , support_per_village(1)
133 , minimum_recruit_price(0)
134 , recall_cost(0)
135 , can_recruit()
136 , team_name()
137 , user_team_name()
138 , side_name()
139 , faction()
140 , faction_name()
141 , save_id()
142 , current_player()
143 , countdown_time()
144 , action_bonus_count(0)
145 , flag()
146 , flag_icon()
147 , id()
148 , scroll_to_leader(true)
149 , objectives()
150 , objectives_changed(false)
151 , controller()
152 , is_local(true)
153 , defeat_cond(defeat_condition::type::no_leader_left)
154 , proxy_controller(side_proxy_controller::type::human)
155 , share_vision(team_shared_vision::type::all)
156 , disallow_observers(false)
157 , allow_player(false)
158 , chose_random(false)
159 , no_leader(true)
160 , hidden(true)
161 , no_turn_confirmation(false)
162 , color()
163 , side(1)
164 , persistent(false)
165 , lost(false)
166 , carryover_percentage(game_config::gold_carryover_percentage)
167 , carryover_add(false)
168 , carryover_bonus(0)
169 , carryover_gold(0)
170{
171}
172
174{
175 gold = cfg["gold"].to_int();
176 income = cfg["income"].to_int();
177 team_name = cfg["team_name"].str(cfg["side"].str());
178 user_team_name = cfg["user_team_name"].t_str();
179 side_name = cfg["side_name"].t_str();
180 faction = cfg["faction"].str();
181 faction_name = cfg["faction_name"].t_str();
182 id = cfg["id"].str();
183 save_id = cfg["save_id"].str(id);
184 current_player = cfg["current_player"].str();
185 countdown_time = cfg["countdown_time"].str();
186 action_bonus_count = cfg["action_bonus_count"].to_int();
187 flag = cfg["flag"].str();
188 flag_icon = cfg["flag_icon"].str();
189 scroll_to_leader = cfg["scroll_to_leader"].to_bool(true);
190 objectives = cfg["objectives"].t_str();
191 objectives_changed = cfg["objectives_changed"].to_bool();
192 disallow_observers = cfg["disallow_observers"].to_bool();
193 allow_player = cfg["allow_player"].to_bool(true);
194 chose_random = cfg["chose_random"].to_bool(false);
195 no_leader = cfg["no_leader"].to_bool();
196 defeat_cond = defeat_condition::get_enum(cfg["defeat_condition"].str()).value_or(defeat_condition::type::no_leader_left);
197 lost = cfg["lost"].to_bool(false);
198 hidden = cfg["hidden"].to_bool();
199 no_turn_confirmation = cfg["suppress_end_turn_confirmation"].to_bool();
200 side = cfg["side"].to_int(1);
201 carryover_percentage = cfg["carryover_percentage"].to_int(game_config::gold_carryover_percentage);
202 carryover_add = cfg["carryover_add"].to_bool(false);
203 carryover_bonus = cfg["carryover_bonus"].to_double(1);
204 carryover_gold = cfg["carryover_gold"].to_int(0);
205 variables = cfg.child_or_empty("variables");
206 is_local = cfg["is_local"].to_bool(true);
207
209
210 // If starting new scenario override settings from [ai] tags
213
215 if(cfg.has_attribute("ai_config")) {
217 } else {
219 }
220 }
221
222 std::vector<std::string> recruits = utils::split(cfg["recruit"]);
223 can_recruit.insert(recruits.begin(), recruits.end());
224
225 // at the start of a scenario "start_gold" is not set, we need to take the
226 // value from the gold setting (or fall back to the gold default)
227 start_gold = cfg["start_gold"].to_int(gold);
228 income_per_village = cfg["village_gold"].to_int(game_config::village_income);
229 recall_cost = cfg["recall_cost"].to_int(game_config::recall_cost);
230
231 const std::string& village_support = cfg["village_support"];
232 if(village_support.empty()) {
233 support_per_village = game_config::village_support;
234 } else {
235 support_per_village = lexical_cast_default<int>(village_support, game_config::village_support);
236 }
237
238 controller = side_controller::get_enum(cfg["controller"].str()).value_or(side_controller::type::ai);
239
240 // TODO: Why do we read disallow observers differently when controller is empty?
241 if(controller == side_controller::type::none) {
242 disallow_observers = cfg["disallow_observers"].to_bool(true);
243 }
244
245 // override persistence flag if it is explicitly defined in the config
246 // by default, persistence of a team is set depending on the controller
247 persistent = cfg["persistent"].to_bool(this->controller == side_controller::type::human);
248
249 //========================================================
250 // END OF MESSY CODE
251
252 // Share_view and share_maps can't both be enabled,
253 // so share_view overrides share_maps.
254 share_vision = team_shared_vision::get_enum(cfg["share_vision"].str()).value_or(team_shared_vision::type::all);
256
257 LOG_NG << "team_info::team_info(...): team_name: " << team_name << ", share_vision: " << team_shared_vision::get_string(share_vision) << ".";
258}
259
261{
262 if(cfg.has_attribute("share_view") || cfg.has_attribute("share_maps")) {
263 if(cfg["share_view"].to_bool()) {
264 share_vision = team_shared_vision::type::all;
265 } else if(cfg["share_maps"].to_bool(true)) {
266 share_vision = team_shared_vision::type::shroud;
267 } else {
268 share_vision = team_shared_vision::type::none;
269 }
270 }
271}
272
274{
275 cfg["gold"] = gold;
276 cfg["start_gold"] = start_gold;
277 cfg["income"] = income;
278 cfg["team_name"] = team_name;
279 cfg["user_team_name"] = user_team_name;
280 cfg["side_name"] = side_name;
281 cfg["faction"] = faction;
282 cfg["faction_name"] = faction_name;
283 cfg["save_id"] = save_id;
284 cfg["current_player"] = current_player;
285 cfg["flag"] = flag;
286 cfg["flag_icon"] = flag_icon;
287 cfg["id"] = id;
288 cfg["objectives"] = objectives;
289 cfg["objectives_changed"] = objectives_changed;
290 cfg["countdown_time"] = countdown_time;
291 cfg["action_bonus_count"] = action_bonus_count;
292 cfg["village_gold"] = income_per_village;
293 cfg["village_support"] = support_per_village;
294 cfg["recall_cost"] = recall_cost;
295 cfg["disallow_observers"] = disallow_observers;
296 cfg["allow_player"] = allow_player;
297 cfg["chose_random"] = chose_random;
298 cfg["no_leader"] = no_leader;
299 cfg["defeat_condition"] = defeat_condition::get_string(defeat_cond);
300 cfg["hidden"] = hidden;
301 cfg["suppress_end_turn_confirmation"] = no_turn_confirmation;
302 cfg["scroll_to_leader"] = scroll_to_leader;
304 cfg["recruit"] = utils::join(can_recruit);
306
307 cfg["color"] = color;
308 cfg["persistent"] = persistent;
309 cfg["lost"] = lost;
310 cfg["carryover_percentage"] = carryover_percentage;
311 cfg["carryover_add"] = carryover_add;
312 cfg["carryover_bonus"] = carryover_bonus;
313 cfg["carryover_gold"] = carryover_gold;
314
315 if(!variables.empty()) {
316 cfg.add_child("variables", variables);
317 }
318
320}
321
323 : villages_()
324 , shroud_()
325 , fog_()
326 , fog_clearer_()
328 , info_()
329 , countdown_time_(0)
331 , recall_list_()
332 , last_recruit_()
333 , enemies_()
334 , ally_shroud_()
335 , ally_fog_()
337{
338}
339
341{
342}
343
344void team::build(const config& cfg, const gamemap& map)
345{
346 info_.read(cfg);
347
348 fog_.set_enabled(cfg["fog"].to_bool());
349 fog_.read(cfg["fog_data"]);
350 shroud_.set_enabled(cfg["shroud"].to_bool());
351 shroud_.read(cfg["shroud_data"]);
352 auto_shroud_updates_ = cfg["auto_shroud"].to_bool(auto_shroud_updates_);
353
354 LOG_NG << "team::team(...): team_name: " << info_.team_name << ", shroud: " << uses_shroud()
355 << ", fog: " << uses_fog() << ".";
356
357 // Load the WML-cleared fog.
358 auto fog_override = cfg.optional_child("fog_override");
359 if(fog_override) {
360 const std::vector<map_location> fog_vector
361 = map.parse_location_range(fog_override["x"], fog_override["y"], true);
362 fog_clearer_.insert(fog_vector.begin(), fog_vector.end());
363 }
364
365 // Load in the villages the side controls at the start
366 for(const config& v : cfg.child_range("village")) {
367 map_location loc(v);
368 if(map.is_village(loc)) {
369 villages_.insert(loc);
370 } else {
371 WRN_NG << "[side] " << current_player() << " [village] points to a non-village location " << loc;
372 }
373 }
374
375 countdown_time_ = chrono::parse_duration<std::chrono::milliseconds>(cfg["countdown_time"]);
376 action_bonus_count_ = cfg["action_bonus_count"].to_int();
377
379 planned_actions_->set_team_index(info_.side - 1);
380}
381
383{
384 info_.write(cfg);
385 cfg["auto_shroud"] = auto_shroud_updates_;
386 cfg["shroud"] = uses_shroud();
387 cfg["fog"] = uses_fog();
388
389 // Write village locations
390 for(const map_location& loc : villages_) {
391 loc.write(cfg.add_child("village"));
392 }
393
394 cfg["shroud_data"] = shroud_.write();
395 cfg["fog_data"] = fog_.write();
396 if(!fog_clearer_.empty())
398
399 cfg["countdown_time"] = countdown_time_;
400 cfg["action_bonus_count"] = action_bonus_count_;
401}
402
404{
406}
407
408void team::set_base_income(int amount)
409{
411}
412
414{
415 for (auto it = villages_.begin(); it != villages_.end(); ) {
416 if (map.is_village(*it)) {
417 ++it;
418 } else {
419 it = villages_.erase(it);
420 }
421 }
422}
423
425{
426 villages_.insert(loc);
428
429 if(gamedata) {
430 config::attribute_value var_owner_side;
431 var_owner_side = owner_side;
432 std::swap(var_owner_side, gamedata->get_variable("owner_side"));
433
434 // During team building, game_events pump is not guaranteed to exist yet. (At current revision.) We skip capture
435 // events in this case.
437 res = resources::game_events->pump().fire("capture", loc);
438 }
439
440 if(var_owner_side.blank()) {
441 gamedata->clear_variable("owner_side");
442 } else {
443 std::swap(var_owner_side, gamedata->get_variable("owner_side"));
444 }
445 }
446
447 return res;
448}
449
451{
452 const std::set<map_location>::const_iterator vil = villages_.find(loc);
453 assert(vil != villages_.end());
454 villages_.erase(vil);
455}
456
457void team::set_recruits(const std::set<std::string>& recruits)
458{
461 // this method gets called from the editor, which obviously has no AI present
464 }
465}
466
467void team::add_recruit(const std::string& recruit)
468{
469 info_.can_recruit.insert(recruit);
472}
473
475{
478 }
479 int min = 20;
480 for(std::string recruit : info_.can_recruit) {
481 const unit_type* ut = unit_types.find(recruit);
482 if(!ut) {
483 continue;
484 } else if(ut->cost() < min) {
485 min = ut->cost();
486 }
487 }
488
490
492}
493
494void team::calculate_enemies(std::size_t index) const
495{
496 if(!resources::gameboard || index >= resources::gameboard->teams().size()) {
497 return;
498 }
499
500 while(enemies_.size() <= index) {
501 enemies_.push_back(calculate_is_enemy(enemies_.size()));
502 }
503}
504
505bool team::calculate_is_enemy(std::size_t index) const
506{
507 // We're not enemies of ourselves
508 if(&resources::gameboard->teams()[index] == this) {
509 return false;
510 }
511
512 // We are friends with anyone who we share a teamname with
513 std::vector<std::string> our_teams = utils::split(info_.team_name);
514 std::vector<std::string> their_teams = utils::split(resources::gameboard->teams()[index].info_.team_name);
515
516 LOG_NGE << "team " << info_.side << " calculates if it has enemy in team " << index + 1 << "; our team_name ["
517 << info_.team_name << "], their team_name is [" << resources::gameboard->teams()[index].info_.team_name
518 << "]" << std::endl;
519
520 for(const std::string& t : our_teams) {
521 if(utils::contains(their_teams, t)) {
522 LOG_NGE << "team " << info_.side << " found same team name [" << t << "] in team " << index + 1;
523 return false;
524 } else {
525 LOG_NGE << "team " << info_.side << " not found same team name [" << t << "] in team " << index + 1;
526 }
527 }
528
529 LOG_NGE << "team " << info_.side << " has enemy in team " << index + 1;
530 return true;
531}
532
533namespace
534{
535class controller_server_choice : public synced_context::server_choice
536{
537public:
538 controller_server_choice(side_controller::type new_controller, const team& team)
539 : new_controller_(new_controller)
540 , team_(team)
541 {
542 }
543
544 /** We are in a game with no mp server and need to do this choice locally */
545 virtual config local_choice() const
546 {
547 return config{"controller", side_controller::get_string(new_controller_), "is_local", true};
548 }
549
550 /** The request which is sent to the mp server. */
551 virtual config request() const
552 {
553 return config{
554 "new_controller", side_controller::get_string(new_controller_), "old_controller", side_controller::get_string(team_.controller()), "side", team_.side(),
555 };
556 }
557
558 virtual const char* name() const
559 {
560 return "change_controller_wml";
561 }
562
563private:
564 side_controller::type new_controller_;
565 const team& team_;
566};
567} // end anon namespace
568
569void team::change_controller_by_wml(const std::string& new_controller_string)
570{
571 auto new_controller = side_controller::get_enum(new_controller_string);
572 if(!new_controller) {
573 WRN_NG << "ignored attempt to change controller to " << new_controller_string;
574 return;
575 }
576
577 if(new_controller == side_controller::type::none && resources::controller->current_side() == this->side()) {
578 WRN_NG << "ignored attempt to change the currently playing side's controller to 'null'";
579 return;
580 }
581
582 config choice = synced_context::ask_server_choice(controller_server_choice(*new_controller, *this));
583 if(!side_controller::get_enum(choice["controller"].str())) {
584 WRN_NG << "Received an invalid controller string from the server" << choice["controller"];
585 } else {
586 new_controller = side_controller::get_enum(choice["controller"].str());
587 }
588
589 if(!resources::controller->is_replay()) {
590 set_local(choice["is_local"].to_bool());
591 }
592
594 if(pc->current_side() == side() && new_controller != controller()) {
595 pc->set_player_type_changed();
596 }
597 }
598
599 change_controller(*new_controller);
600}
601
602void team::change_team(const std::string& name, const t_string& user_name)
603{
604 info_.team_name = name;
605
606 if(!user_name.empty()) {
607 info_.user_team_name = user_name;
608 } else {
609 info_.user_team_name = name;
610 }
611
612 clear_caches();
613}
614
616{
617 // Reset the cache of allies for all teams
619 for(auto& t : resources::gameboard->teams()) {
620 t.enemies_.clear();
621 t.ally_shroud_.clear();
622 t.ally_fog_.clear();
623 }
624 }
625}
626
627void team::set_objectives(const t_string& new_objectives, bool silently)
628{
629 info_.objectives = new_objectives;
630
631 if(!silently) {
633 }
634}
635
637{
639 return shroud_.value(loc.wml_x(), loc.wml_y());
640 }
641
643}
644
645bool team::fogged(const map_location& loc) const
646{
647 if(shrouded(loc)) {
648 return true;
649 }
650
651 // Check for an override of fog.
652 if(fog_clearer_.count(loc) > 0) {
653 return false;
654 }
655
657 return fog_.value(loc.wml_x(), loc.wml_y());
658 }
659
661}
662
663const std::vector<const shroud_map*>& team::ally_shroud(const std::vector<team>& teams) const
664{
665 if(ally_shroud_.empty()) {
666 for(const team& t : teams) {
667 if(!is_enemy(t.side()) && (&t == this || t.share_view() || t.share_maps())) {
668 ally_shroud_.push_back(&t.shroud_);
669 }
670 }
671 }
672
673 return ally_shroud_;
674}
675
676const std::vector<const shroud_map*>& team::ally_fog(const std::vector<team>& teams) const
677{
678 if(ally_fog_.empty()) {
679 for(const team& t : teams) {
680 if(!is_enemy(t.side()) && (&t == this || t.share_view())) {
681 ally_fog_.push_back(&t.fog_);
682 }
683 }
684 }
685
686 return ally_fog_;
687}
688
689bool team::knows_upkeep(const team& t) const
690{
691 // We know about our own team
692 if(this == &t) {
693 return true;
694 }
695
696 // If we aren't using shroud or fog, then we know about everyone
697 if(!uses_shroud() && !uses_fog()) {
698 return true;
699 }
700
701 // We don't know about enemies
702 if(is_enemy(t.side())) {
703 return false;
704 }
705
706 // We know our human allies.
707 if(t.is_human()) {
708 return true;
709 }
710
711 // We know about allies we're sharing maps with
712 if(share_maps() && t.uses_shroud()) {
713 return true;
714 }
715
716 // We know about allies we're sharing view with
717 if(share_view() && (t.uses_fog() || t.uses_shroud())) {
718 return true;
719 }
720
721 return false;
722}
723
724/**
725 * Removes the record of hexes that were cleared of fog via WML.
726 * @param[in] hexes The hexes to no longer keep clear.
727 */
728void team::remove_fog_override(const std::set<map_location>& hexes)
729{
730 // Take a set difference.
731 std::vector<map_location> result(fog_clearer_.size());
732 std::vector<map_location>::iterator result_end =
733 std::set_difference(fog_clearer_.begin(), fog_clearer_.end(), hexes.begin(), hexes.end(), result.begin());
734
735 // Put the result into fog_clearer_.
736 fog_clearer_.clear();
737 fog_clearer_.insert(result.begin(), result_end);
738}
739
740void validate_side(int side)
741{
743 return;
744 }
745
746 if(side < 1 || side > static_cast<int>(resources::gameboard->teams().size())) {
747 throw game::game_error("invalid side(" + std::to_string(side) + ") found in unit definition");
748 }
749}
750
752{
753 return data_.size();
754}
755
757{
758 if(data_.size() == 0) return 0;
759 return std::max_element(data_.begin(), data_.end(), [](const auto& a, const auto& b) {
760 return a.size() < b.size();
761 })->size();
762}
763
764bool shroud_map::clear(int x, int y)
765{
766 if(enabled_ == false || x < 0 || y < 0) {
767 return false;
768 }
769
770 if(x >= static_cast<int>(data_.size())) {
771 data_.resize(x + 1);
772 }
773
774 if(y >= static_cast<int>(data_[x].size())) {
775 data_[x].resize(y + 1);
776 }
777
778 if(data_[x][y] == false) {
779 data_[x][y] = true;
780 return true;
781 }
782
783 return false;
784}
785
786void shroud_map::place(int x, int y)
787{
788 if(enabled_ == false || x < 0 || y < 0) {
789 return;
790 }
791
792 if(x >= static_cast<int>(data_.size())) {
793 DBG_NG << "Couldn't place shroud on invalid x coordinate: (" << x << ", " << y
794 << ") - max x: " << data_.size() - 1;
795 } else if(y >= static_cast<int>(data_[x].size())) {
796 DBG_NG << "Couldn't place shroud on invalid y coordinate: (" << x << ", " << y
797 << ") - max y: " << data_[x].size() - 1;
798 } else {
799 data_[x][y] = false;
800 }
801}
802
804{
805 if(enabled_ == false) {
806 return;
807 }
808
809 for(auto& i : data_) {
810 std::fill(i.begin(), i.end(), false);
811 }
812}
813
814bool shroud_map::value(int x, int y) const
815{
816 if(!enabled_) {
817 return false;
818 }
819
820 // Locations for which we have no data are assumed to still be covered.
821 if(x < 0 || x >= static_cast<int>(data_.size())) {
822 return true;
823 }
824
825 if(y < 0 || y >= static_cast<int>(data_[x].size())) {
826 return true;
827 }
828
829 // data_ stores whether or not a location has been cleared, while
830 // we want to return whether or not a location is covered.
831 return !data_[x][y];
832}
833
834bool shroud_map::shared_value(const std::vector<const shroud_map*>& maps, int x, int y) const
835{
836 if(!enabled_) {
837 return false;
838 }
839
840 // A quick abort:
841 if(x < 0 || y < 0) {
842 return true;
843 }
844
845 // A tile is uncovered if it is uncovered on any shared map.
846 for(const shroud_map* const shared_map : maps) {
847 if(shared_map->enabled_ && !shared_map->value(x, y)) {
848 return false;
849 }
850 }
851
852 return true;
853}
854
855std::string shroud_map::write() const
856{
857 std::stringstream shroud_str;
858 for(const auto& sh : data_) {
859 shroud_str << '|';
860
861 for(bool i : sh) {
862 shroud_str << (i ? '1' : '0');
863 }
864
865 shroud_str << '\n';
866 }
867
868 return shroud_str.str();
869}
870
871void shroud_map::read(const std::string& str)
872{
873 data_.clear();
874
875 for(const char sh : str) {
876 if(sh == '|') {
877 data_.resize(data_.size() + 1);
878 }
879
880 if(data_.empty() == false) {
881 if(sh == '1') {
882 data_.back().push_back(true);
883 } else if(sh == '0') {
884 data_.back().push_back(false);
885 }
886 }
887 }
888}
889
890void shroud_map::merge(const std::string& str)
891{
892 int x = 0, y = 0;
893 for(std::size_t i = 1; i < str.length(); ++i) {
894 if(str[i] == '|') {
895 y = 0;
896 x++;
897 } else if(str[i] == '1') {
898 clear(x, y);
899 y++;
900 } else if(str[i] == '0') {
901 y++;
902 }
903 }
904}
905
906bool shroud_map::copy_from(const std::vector<const shroud_map*>& maps)
907{
908 if(enabled_ == false) {
909 return false;
910 }
911
912 bool cleared = false;
913 for(const shroud_map* m : maps) {
914 if(m->enabled_ == false) {
915 continue;
916 }
917
918 const std::vector<std::vector<bool>>& v = m->data_;
919 for(std::size_t x = 0; x != v.size(); ++x) {
920 for(std::size_t y = 0; y != v[x].size(); ++y) {
921 if(v[x][y]) {
922 cleared |= clear(x, y);
923 }
924 }
925 }
926 }
927
928 return cleared;
929}
930
932{
933 std::string index = get_side_color_id(side);
934 auto iter = game_config::team_rgb_range.find(index);
935
936 if(iter != game_config::team_rgb_range.end()) {
937 return iter->second;
938 }
939
940 return color_range({255, 0, 0}, {255, 255, 255}, {0, 0, 0}, {255, 0, 0}, {255, 0, 0});
941}
942
944{
945 return get_side_color_range(side).mid();
946}
947
949{
950 // Note: use mid() instead of rep() unless
951 // high contrast is needed over a map or minimap!
952 return get_side_color_range(side).rep();
953}
954
959
960std::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.
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 LOG_NG << "Adding recruitable units:";
1022 for(const std::string& recruit : info_.can_recruit) {
1023 LOG_NG << recruit;
1024 }
1025
1026 LOG_NG << "Added all recruitable units";
1027}
1028
1030{
1031 config cfg;
1032 config& result = cfg.add_child("side");
1033 write(result);
1034 return result;
1035}
1036
1037std::string team::allied_human_teams() const
1038{
1039 std::vector<int> res;
1040 for(const team& t : resources::gameboard->teams()) {
1041 if(!t.is_enemy(this->side()) && t.is_human()) {
1042 res.push_back(t.side());
1043 }
1044 }
1045
1046 return utils::join(res);
1047}
map_location loc
Definition move.cpp:172
#define WRN_NG
#define DBG_NG
#define LOG_NG
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
double t
static manager & get_singleton()
Definition manager.hpp:140
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:482
static bool has_manager()
Definition manager.hpp:146
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:472
void raise_recruit_list_changed()
Notifies all observers of 'ai_recruit_list_changed' event.
Definition manager.cpp:460
Representing the "team color" information, this C++ class contains a subset of WML's [color_range] ta...
color_t font_color() const
High-contrast to dark backgrounds, intended for the text in the chat log, etc.
color_t rep() const
High-contrast against typical terrain colors, intended for the minimap markers.
color_t mid() const
Average color shade.
Variant for storing WML attributes.
bool blank() const
Tests for an attribute that was never set.
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
child_itors child_range(std::string_view key)
Definition config.cpp:268
bool has_attribute(std::string_view key) const
Definition config.cpp:157
const config & child_or_empty(std::string_view key) const
Returns the first child with the given key, or an empty config if there is none.
Definition config.cpp:390
bool empty() const
Definition config.cpp:823
virtual const std::vector< team > & teams() const override
game_events::wml_event_pump & pump()
Definition manager.cpp:257
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:399
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's dimensions as bounds.
Definition map.cpp:396
Encapsulates the map of the game.
Definition map.hpp:176
bool is_village(const map_location &loc) const
Definition map.cpp:60
bool copy_from(const std::vector< const shroud_map * > &maps)
Definition team.cpp:906
int height() const
Definition team.cpp:756
void read(const std::string &shroud_data)
Definition team.cpp:871
void set_enabled(bool enabled)
Definition team.hpp:60
std::vector< std::vector< bool > > data_
Definition team.hpp:66
void place(int x, int y)
Definition team.cpp:786
int width() const
Definition team.cpp:751
bool shared_value(const std::vector< const shroud_map * > &maps, int x, int y) const
Definition team.cpp:834
void merge(const std::string &shroud_data)
Definition team.cpp:890
bool value(int x, int y) const
Definition team.cpp:814
std::string write() const
Definition team.cpp:855
void reset()
Definition team.cpp:803
bool enabled_
Definition team.hpp:65
bool clear(int x, int y)
Definition team.cpp:764
virtual const char * name() const =0
virtual config request() const =0
The request which is sent to the mp server.
virtual config local_choice() const =0
We are in a game with no mp server and need to do this choice locally.
static config ask_server_choice(const server_choice &)
If we are in a mp game, ask the server, otherwise generate the answer ourselves.
static t_string from_serialized(const std::string &string)
Definition tstring.hpp:165
bool translatable() const
Definition tstring.hpp:207
bool empty() const
Definition tstring.hpp:200
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
bool uses_shroud() const
Definition team.hpp:344
const t_string & faction_name() const
Definition team.hpp:338
int carryover_gold() const
Definition team.hpp:389
const std::string & side_name() const
Definition team.hpp:334
const std::string & faction() const
Definition team.hpp:337
static color_t get_minimap_color(int side)
Definition team.cpp:948
void build(const config &cfg, const gamemap &map)
Definition team.cpp:344
bool no_turn_confirmation() const
Definition team.hpp:393
void fix_villages(const gamemap &map)
Definition team.cpp:413
const std::set< std::string > & recruits() const
Definition team.hpp:250
recall_list_manager recall_list_
Definition team.hpp:460
int side() const
Definition team.hpp:182
game_events::pump_result_t get_village(const map_location &, const int owner_side, game_data *fire_event)
Acquires a village from owner_side.
Definition team.cpp:424
const std::string & save_id() const
Definition team.hpp:258
int village_support() const
Definition team.hpp:207
std::vector< const shroud_map * > ally_shroud_
Definition team.hpp:468
int recall_cost() const
Definition team.hpp:198
void set_local(bool local)
Definition team.hpp:299
void set_recruits(const std::set< std::string > &recruits)
Definition team.cpp:457
bool objectives_changed() const
Definition team.hpp:268
bool is_local() const
Definition team.hpp:288
static std::string get_side_color_id_from_config(const config &cfg)
Definition team.cpp:999
std::chrono::milliseconds countdown_time_
Definition team.hpp:457
bool no_leader() const
Definition team.hpp:369
bool is_enemy(int n) const
Definition team.hpp:270
config to_config() const
Definition team.cpp:1029
team_shared_vision::type share_vision() const
Definition team.hpp:419
int action_bonus_count() const
Definition team.hpp:238
shroud_map fog_
Definition team.hpp:449
bool calculate_is_enemy(std::size_t index) const
Definition team.cpp:505
const std::string & flag_icon() const
Definition team.hpp:328
void change_team(const std::string &name, const t_string &user_name)
Definition team.cpp:602
int gold() const
Definition team.hpp:183
static color_range get_side_color_range(int side)
Definition team.cpp:931
team()
Definition team.cpp:322
const t_string & objectives() const
Definition team.hpp:267
defeat_condition::type defeat_cond() const
Definition team.hpp:370
void set_objectives(const t_string &new_objectives, bool silently=false)
Definition team.cpp:627
const std::string & current_player() const
Definition team.hpp:261
bool carryover_add() const
Definition team.hpp:385
static const std::set< std::string > tags
Stores the child tags recognized by [side].
Definition team.hpp:120
static t_string get_side_color_name_for_UI(unsigned side)
Definition team.cpp:987
void handle_legacy_share_vision(const config &cfg)
Definition team.hpp:431
int carryover_percentage() const
Definition team.hpp:383
void write(config &cfg) const
Definition team.cpp:382
static std::string get_side_color_id(unsigned side)
Definition team.cpp:960
int raw_income() const
Definition team.hpp:189
std::vector< const shroud_map * > ally_fog_
Definition team.hpp:468
static color_t get_font_color(int side)
Definition team.cpp:955
team_info info_
Definition team.hpp:455
void calculate_enemies(std::size_t index) const
Definition team.cpp:494
void add_recruit(const std::string &)
Definition team.cpp:467
side_controller::type controller() const
Definition team.hpp:282
int minimum_recruit_price() const
Definition team.cpp:474
double carryover_bonus() const
Definition team.hpp:387
void change_controller(const std::string &new_controller)
Definition team.hpp:302
virtual ~team()
Definition team.cpp:340
static void clear_caches()
clear the shroud, fog, and enemies cache for all teams
Definition team.cpp:615
bool auto_shroud_updates_
Definition team.hpp:453
bool share_maps() const
Definition team.hpp:417
void set_raw_income(int amount)
Sets the income of this side to the given value.
Definition team.hpp:228
std::set< map_location > fog_clearer_
Stores hexes that have been cleared of fog via WML.
Definition team.hpp:451
std::string allied_human_teams() const
Definition team.cpp:1037
int action_bonus_count_
Definition team.hpp:458
bool shrouded(const map_location &loc) const
Definition team.cpp:636
std::set< map_location > villages_
Definition team.hpp:447
bool knows_upkeep(const team &other) const
Whether other's count of villages, units, upkeep and income should be visible in the status table.
Definition team.cpp:689
const std::string & team_name() const
Definition team.hpp:323
std::chrono::milliseconds countdown_time() const
Definition team.hpp:236
int start_gold() const
Definition team.hpp:184
shroud_map shroud_
Definition team.hpp:449
const std::vector< const shroud_map * > & ally_fog(const std::vector< team > &teams) const
Definition team.cpp:676
int base_income() const
Definition team.cpp:403
bool uses_fog() const
Definition team.hpp:345
static color_t get_side_color(int side)
Definition team.cpp:943
static const std::set< std::string > attributes
Stores the attributes recognized by [side].
Definition team.hpp:54
bool persistent() const
Definition team.hpp:377
bool hidden() const
Definition team.hpp:375
std::string last_recruit_
Definition team.hpp:461
std::shared_ptr< wb::side_actions > planned_actions_
Whiteboard planned actions for this team.
Definition team.hpp:473
bool share_view() const
Definition team.hpp:418
const std::string & color() const
Definition team.hpp:283
const std::string & flag() const
Definition team.hpp:327
void lose_village(const map_location &)
Definition team.cpp:450
void set_base_income(int amount)
Sets the income of this side to the given value with base income from [game_config] added to it.
Definition team.cpp:408
const t_string & user_team_name() const
Definition team.hpp:324
boost::dynamic_bitset enemies_
Definition team.hpp:466
config & variables()
Definition team.hpp:390
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:728
bool fogged(const map_location &loc) const
Definition team.cpp:645
bool lost() const
Definition team.hpp:380
void change_controller_by_wml(const std::string &new_controller)
Definition team.cpp:569
bool chose_random() const
Definition team.hpp:437
void log_recruitable() const
Definition team.cpp:1019
const std::vector< const shroud_map * > & ally_shroud(const std::vector< team > &teams) const
Definition team.cpp:663
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:1253
A single unit type that the player may recruit.
Definition types.hpp:43
int cost() const
Definition types.hpp:176
This internal whiteboard class holds the planned action queues for a team, and offers many utility me...
const config * cfg
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
New lexcical_cast header.
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,...
Definition location.cpp:436
Game configuration data as global variables.
std::map< std::string, color_range, std::less<> > team_rgb_range
Colors defined by WML [color_range] tags.
std::map< std::string, t_string, std::less<> > team_rgb_name
const int gold_carryover_percentage
Default percentage gold carried over to the next scenario.
std::vector< std::string > default_colors
The [color_range]id= attributes, equivalently the keys in the team_rgb_* maps, for which [color_range...
std::tuple< bool, bool > pump_result_t
Definition fwd.hpp:29
game_board * gameboard
Definition resources.cpp:20
game_events::manager * game_events
Definition resources.cpp:24
play_controller * controller
Definition resources.cpp:21
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
std::string join(const Range &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
Define the game's event mechanism.
The basic class for representing 8-bit RGB or RGBA colour values.
Definition color.hpp:51
Error used for any general game error, e.g.
Encapsulates the map of the game.
Definition location.hpp:46
int wml_y() const
Definition location.hpp:187
int wml_x() const
Definition location.hpp:186
void write(config &cfg) const
Definition location.cpp:223
The base template for associating string values with enum values.
Definition enum_base.hpp:33
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition enum_base.hpp:57
std::string team_name
Definition team.hpp:89
void read(const config &cfg)
Definition team.cpp:173
t_string user_team_name
Definition team.hpp:90
int minimum_recruit_price
Definition team.hpp:86
void handle_legacy_share_vision(const config &cfg)
Definition team.cpp:260
t_string objectives
Definition team.hpp:107
void write(config &cfg) const
Definition team.cpp:273
std::set< std::string > can_recruit
Definition team.hpp:88
bool objectives_changed
< Team's objectives for the current level.
Definition team.hpp:112
#define LOG_NGE
Definition team.cpp:49
static lg::log_domain log_engine("engine")
static lg::log_domain log_engine_enemies("engine/enemies")
void validate_side(int side)
Definition team.cpp:740
const std::string & gamedata
unit_type_data unit_types
Definition types.cpp:1499
#define i
#define c
#define b