The Battle for Wesnoth  1.19.0+dev
teleport.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2024
3  by Fabian Mueller <fabianmueller5@gmx.de>
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 #include "pathfind/teleport.hpp"
17 
18 #include "display_context.hpp"
19 #include "filter_context.hpp"
20 #include "game_board.hpp"
21 #include "log.hpp"
22 #include "resources.hpp"
23 #include "team.hpp"
24 #include "terrain/filter.hpp"
25 #include "units/unit.hpp"
26 #include "units/filter.hpp"
27 #include "units/map.hpp"
28 #include "wml_exception.hpp"
29 
30 static lg::log_domain log_engine("engine");
31 #define ERR_PF LOG_STREAM(err, log_engine)
32 
33 static lg::log_domain log_wml("wml");
34 #define ERR_WML LOG_STREAM(err, log_wml)
35 
36 namespace pathfind {
37 
38 
39 namespace {
40  const std::string reversed_suffix = "-__REVERSED__";
41 }
42 
43 // This constructor is *only* meant for loading from saves
44 teleport_group::teleport_group(const config& cfg) : cfg_(cfg), reversed_(cfg["reversed"].to_bool(false)), id_(cfg["id"])
45 {
46  VALIDATE(cfg.has_attribute("id"), missing_mandatory_wml_key("tunnel", "id"));
47  VALIDATE(cfg.has_attribute("reversed"), missing_mandatory_wml_key("tunnel", "reversed"));
48 
49  VALIDATE(cfg_.child_count("source") == 1, "The tunnel should have only one 'source' child.");
50  VALIDATE(cfg_.child_count("target") == 1, "The tunnel should have only one 'target' child.");
51  VALIDATE(cfg_.child_count("filter") == 1, "The tunnel should have only one 'filter' child.");
52 }
53 
54 teleport_group::teleport_group(const vconfig& cfg, bool reversed) : cfg_(cfg.get_config()), reversed_(reversed), id_()
55 {
56  VALIDATE(cfg_.child_count("source") == 1, "The tunnel should have only one 'source' child.");
57  VALIDATE(cfg_.child_count("target") == 1, "The tunnel should have only one 'target' child.");
58  VALIDATE(cfg_.child_count("filter") == 1, "The tunnel should have only one 'filter' child.");
59  if (cfg["id"].empty()) {
61  } else {
62  id_ = cfg["id"].str();
63  if (reversed_) // Differentiate the reverse tunnel from the forward one
64  id_ += reversed_suffix;
65  }
66 }
67 
69 public:
71  : um_()
72  , gm_(&dc.map())
73  , tm_(&dc.teams())
75  {
76  static unit_map empty_unit_map;
77  um_ = &empty_unit_map;
78  }
79  const unit_map & units() const override { return *um_; }
80  const gamemap & map() const override { return *gm_; }
81  const std::vector<team> & teams() const override { return *tm_; }
82  const std::vector<std::string> & hidden_label_categories() const override { return *lbls_; }
83  std::vector<std::string>& hidden_label_categories() override { throw "Writable hidden label categories not supported in this context"; }
84 
85 private:
86  const unit_map * um_;
87  const gamemap * gm_;
88  const std::vector<team> * tm_;
89  const std::vector<std::string> * lbls_;
90 };
91 
93 public:
95  : dc_(fc.get_disp_context())
96  , tod_(&fc.get_tod_man())
97  , gd_(fc.get_game_data())
98  , lk_(fc.get_lua_kernel())
99  {}
100 
101  const display_context & get_disp_context() const override { return dc_; }
102  const tod_manager & get_tod_man() const override { return *tod_; }
103  const game_data * get_game_data() const override { return gd_; }
104  game_lua_kernel * get_lua_kernel() const override { return lk_; }
105 
106 private:
108  const tod_manager * tod_;
109  const game_data * gd_;
111 };
112 
114  teleport_pair& loc_pair
115  , const unit& u
116  , const bool ignore_units) const
117 {
119  assert(fc);
120 
121  if (ignore_units) {
123  }
124 
125  vconfig filter(cfg_.child_or_empty("filter"), true);
126  vconfig source(cfg_.child_or_empty("source"), true);
127  vconfig target(cfg_.child_or_empty("target"), true);
128  const unit_filter ufilt(filter); //Note: Don't use the ignore units filter context here, only for the terrain filters. (That's how it worked before the filter contexts were introduced)
129  if (ufilt.matches(u)) {
130  terrain_filter source_filter(source, fc, false);
131  source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first, u);
132 
133  terrain_filter target_filter(target, fc, false);
134  target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second, u);
135  }
136 
137  if (ignore_units) {
138  delete fc;
139  }
140 }
141 
142 const std::string& teleport_group::get_teleport_id() const {
143  return id_;
144 }
145 
147  return cfg_["always_visible"].to_bool(false);
148 }
149 
151  return cfg_["pass_allied_units"].to_bool(true);
152 }
153 
155  return cfg_["allow_vision"].to_bool(true);
156 }
157 
159  config retval = cfg_;
160  retval["saved"] = "yes";
161  retval["reversed"] = reversed_ ? "yes" : "no";
162  retval["id"] = id_;
163  return retval;
164 }
165 
167  const std::vector<teleport_group>& groups
168  , const unit& unit
169  , const team &viewing_team
170  , const bool see_all
171  , const bool ignore_units
172  , const bool check_vision)
173  : teleport_map_()
174  , sources_()
175  , targets_()
176 {
177 
178  for (const teleport_group& group : groups) {
179 
180  teleport_pair locations;
181 
182  if (check_vision && !group.allow_vision()) {
183  continue;
184  }
185 
186  group.get_teleport_pair(locations, unit, ignore_units);
187  if (!see_all && !group.always_visible() && viewing_team.is_enemy(unit.side())) {
188  teleport_pair filter_locs;
189  for (const map_location &loc : locations.first) {
190  if(!viewing_team.fogged(loc))
191  filter_locs.first.insert(loc);
192  }
193  for (const map_location &loc : locations.second) {
194  if(!viewing_team.fogged(loc))
195  filter_locs.second.insert(loc);
196  }
197  locations.first.swap(filter_locs.first);
198  locations.second.swap(filter_locs.second);
199  }
200 
201  if (!group.pass_allied_units() && !ignore_units && !check_vision) {
202  std::set<map_location>::iterator loc = locations.second.begin();
203  while(loc != locations.second.end()) {
205  if (see_all) {
206  u = resources::gameboard->units().find(*loc);
207  } else {
208  u = resources::gameboard->find_visible_unit(*loc, viewing_team);
209  }
210  if (u != resources::gameboard->units().end()) {
211  loc = locations.second.erase(loc);
212  } else {
213  ++loc;
214  }
215  }
216  }
217 
218  std::string teleport_id = group.get_teleport_id();
219  std::set<map_location>::iterator source_it = locations.first.begin();
220  for (; source_it != locations.first.end(); ++source_it ) {
221  if(teleport_map_.count(*source_it) == 0) {
222  std::set<std::string> id_set;
223  id_set.insert(teleport_id);
224  teleport_map_.emplace(*source_it, id_set);
225  } else {
226  (teleport_map_.find(*source_it)->second).insert(teleport_id);
227  }
228  }
229  sources_.emplace(teleport_id, locations.first);
230  targets_.emplace(teleport_id, locations.second);
231  }
232 }
233 
234 std::set<map_location> teleport_map::get_adjacents(map_location loc) const
235 {
236  const auto iter = teleport_map_.find(loc);
237  if(iter == teleport_map_.end()) {
238  return {};
239  }
240 
241  std::set<map_location> res;
242  for(const std::string& key : iter->second) {
243  const auto& target = targets_.find(key)->second;
244  res.insert(target.begin(), target.end());
245  }
246 
247  return res;
248 }
249 
250 std::set<map_location> teleport_map::get_sources() const
251 {
252  std::set<map_location> res;
253  for(const auto& src : sources_) {
254  res.insert(src.second.begin(), src.second.end());
255  }
256 
257  return res;
258 }
259 
260 std::set<map_location> teleport_map::get_targets() const
261 {
262  std::set<map_location> res;
263  for(const auto& tgt : targets_) {
264  res.insert(tgt.second.begin(), tgt.second.end());
265  }
266 
267  return res;
268 }
269 
271  const team &viewing_team,
272  bool see_all, bool ignore_units, bool check_vision)
273 {
274  std::vector<teleport_group> groups;
275 
276  for (const unit_ability & teleport : u.get_abilities("teleport")) {
277  const int tunnel_count = (teleport.ability_cfg)->child_count("tunnel");
278  for(int i = 0; i < tunnel_count; ++i) {
279  config teleport_group_cfg = (teleport.ability_cfg)->mandatory_child("tunnel", i);
280  groups.emplace_back(vconfig(teleport_group_cfg, true), false);
281  }
282  }
283 
284  const std::vector<teleport_group>& global_groups = resources::tunnels->get();
285  groups.insert(groups.end(), global_groups.begin(), global_groups.end());
286 
287  return teleport_map(groups, u, viewing_team, see_all, ignore_units, check_vision);
288 }
289 
290 manager::manager(const config &cfg) : tunnels_(), id_(cfg["next_teleport_group_id"].to_int(0)) {
291  const int tunnel_count = cfg.child_count("tunnel");
292  for(int i = 0; i < tunnel_count; ++i) {
293  const config& t = cfg.mandatory_child("tunnel", i);
294  if(!t["saved"].to_bool()) {
295  lg::log_to_chat() << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag.\n";
296  ERR_WML << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag.";
297  continue;
298  }
299  const teleport_group tunnel(t);
300  this->add(tunnel);
301  }
302 }
303 
304 void manager::add(const teleport_group &group) {
305  tunnels_.push_back(group);
306 }
307 
308 void manager::remove(const std::string &id) {
310  for(;t != tunnels_.end();) {
311  if (t->get_teleport_id() == id || t->get_teleport_id() == id + reversed_suffix) {
312  t = tunnels_.erase(t);
313  } else {
314  ++t;
315  }
316  }
317 }
318 
319 const std::vector<teleport_group>& manager::get() const {
320  return tunnels_;
321 }
322 
324  config store;
325 
326  std::vector<teleport_group>::const_iterator tunnel = tunnels_.begin();
327  for(; tunnel != tunnels_.end(); ++tunnel) {
328  store.add_child("tunnel", tunnel->to_config());
329  }
330  store["next_teleport_group_id"] = std::to_string(id_);
331 
332  return store;
333 }
334 
335 std::string manager::next_unique_id() {
336  return std::to_string(++id_);
337 }
338 
339 
340 }//namespace pathfind
double t
Definition: astarsearch.cpp:63
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
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:395
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:367
std::size_t child_count(config_key_type key) const
Definition: config.cpp:297
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
config & add_child(config_key_type key)
Definition: config.cpp:441
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:185
virtual const unit_map & units() const override
Definition: game_board.hpp:106
Encapsulates the map of the game.
Definition: map.hpp:172
std::vector< std::string > & hidden_label_categories() override
Definition: teleport.cpp:83
const std::vector< team > * tm_
Definition: teleport.cpp:88
const std::vector< std::string > * lbls_
Definition: teleport.cpp:89
const gamemap & map() const override
Definition: teleport.cpp:80
const unit_map & units() const override
Definition: teleport.cpp:79
const std::vector< team > & teams() const override
Definition: teleport.cpp:81
ignore_units_display_context(const display_context &dc)
Definition: teleport.cpp:70
const std::vector< std::string > & hidden_label_categories() const override
Definition: teleport.cpp:82
const tod_manager & get_tod_man() const override
Definition: teleport.cpp:102
const display_context & get_disp_context() const override
Definition: teleport.cpp:101
const ignore_units_display_context dc_
Definition: teleport.cpp:107
game_lua_kernel * get_lua_kernel() const override
Definition: teleport.cpp:104
const game_data * get_game_data() const override
Definition: teleport.cpp:103
ignore_units_filter_context(const filter_context &fc)
Definition: teleport.cpp:94
config to_config() const
Inherited from savegame_config.
Definition: teleport.cpp:323
void add(const teleport_group &group)
Definition: teleport.cpp:304
std::vector< teleport_group > tunnels_
Definition: teleport.hpp:183
void remove(const std::string &id)
Definition: teleport.cpp:308
const std::vector< teleport_group > & get() const
Definition: teleport.cpp:319
manager(const config &cfg)
Definition: teleport.cpp:290
std::string next_unique_id()
Definition: teleport.cpp:335
config to_config() const
Inherited from savegame_config.
Definition: teleport.cpp:158
bool always_visible() const
Definition: teleport.cpp:146
const std::string & get_teleport_id() const
Definition: teleport.cpp:142
teleport_group(const config &cfg)
Definition: teleport.cpp:44
bool pass_allied_units() const
Definition: teleport.cpp:150
void get_teleport_pair(teleport_pair &loc_pair, const unit &u, const bool ignore_units) const
Definition: teleport.cpp:113
bool allow_vision() const
Definition: teleport.cpp:154
std::map< map_location, std::set< std::string > > teleport_map_
Definition: teleport.hpp:139
std::map< std::string, std::set< map_location > > targets_
Definition: teleport.hpp:141
std::set< map_location > get_targets() const
Returns the locations that are an exit of the tunnel.
Definition: teleport.cpp:260
std::map< std::string, std::set< map_location > > sources_
Definition: teleport.hpp:140
std::set< map_location > get_adjacents(map_location loc) const
Definition: teleport.cpp:234
std::set< map_location > get_sources() const
Returns the locations that are an entrance of the tunnel.
Definition: teleport.cpp:250
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
bool is_enemy(int n) const
Definition: team.hpp:229
bool fogged(const map_location &loc) const
Definition: team.cpp:659
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:123
Container associating units to locations.
Definition: map.hpp:98
unit_iterator find(std::size_t id)
Definition: map.cpp:302
This class represents a single unit of a specific type.
Definition: unit.hpp:133
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
std::size_t i
Definition: function.cpp:968
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Gets the unit's active abilities of a particular type if it were on a specified location.
Definition: abilities.cpp:218
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
Standard logging facilities (interface).
retval
Default window/dialog return values.
Definition: retval.hpp:30
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:544
std::pair< std::set< map_location >, std::set< map_location > > teleport_pair
Definition: teleport.hpp:29
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units, bool check_vision)
Definition: teleport.cpp:270
game_board * gameboard
Definition: resources.cpp:20
pathfind::manager * tunnels
Definition: resources.cpp:31
filter_context * filter_con
Definition: resources.cpp:23
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Encapsulates the map of the game.
Definition: location.hpp:38
Data typedef for unit_ability_list.
Definition: unit.hpp:38
static lg::log_domain log_engine("engine")
#define ERR_WML
Definition: teleport.cpp:34
static lg::log_domain log_wml("wml")
std::string missing_mandatory_wml_key(const std::string &section, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key (attribute).
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE(cond, message)
The macro to use for the validation of WML.