The Battle for Wesnoth  1.19.5+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  utils::optional<ignore_units_filter_context> ignore_context;
122  if (ignore_units) {
123  ignore_context.emplace(*resources::filter_con);
124  fc = &ignore_context.value();
125  }
126 
127  vconfig filter(cfg_.child_or_empty("filter"), true);
128  vconfig source(cfg_.child_or_empty("source"), true);
129  vconfig target(cfg_.child_or_empty("target"), true);
130  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)
131  if (ufilt.matches(u)) {
132  terrain_filter source_filter(source, fc, false);
133  source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first, u);
134 
135  terrain_filter target_filter(target, fc, false);
136  target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second, u);
137  }
138 }
139 
140 const std::string& teleport_group::get_teleport_id() const {
141  return id_;
142 }
143 
145  return cfg_["always_visible"].to_bool(false);
146 }
147 
149  return cfg_["pass_allied_units"].to_bool(true);
150 }
151 
153  return cfg_["allow_vision"].to_bool(true);
154 }
155 
157  config retval = cfg_;
158  retval["saved"] = "yes";
159  retval["reversed"] = reversed_ ? "yes" : "no";
160  retval["id"] = id_;
161  return retval;
162 }
163 
165  const std::vector<teleport_group>& groups
166  , const unit& unit
167  , const team &viewing_team
168  , const bool see_all
169  , const bool ignore_units
170  , const bool check_vision)
171  : teleport_map_()
172  , sources_()
173  , targets_()
174 {
175 
176  for (const teleport_group& group : groups) {
177 
178  teleport_pair locations;
179 
180  if (check_vision && !group.allow_vision()) {
181  continue;
182  }
183 
184  group.get_teleport_pair(locations, unit, ignore_units);
185  if (!see_all && !group.always_visible() && viewing_team.is_enemy(unit.side())) {
186  teleport_pair filter_locs;
187  for (const map_location &loc : locations.first) {
188  if(!viewing_team.fogged(loc))
189  filter_locs.first.insert(loc);
190  }
191  for (const map_location &loc : locations.second) {
192  if(!viewing_team.fogged(loc))
193  filter_locs.second.insert(loc);
194  }
195  locations.first.swap(filter_locs.first);
196  locations.second.swap(filter_locs.second);
197  }
198 
199  if (!group.pass_allied_units() && !ignore_units && !check_vision) {
200  std::set<map_location>::iterator loc = locations.second.begin();
201  while(loc != locations.second.end()) {
203  if (see_all) {
204  u = resources::gameboard->units().find(*loc);
205  } else {
206  u = resources::gameboard->find_visible_unit(*loc, viewing_team);
207  }
208  if (u != resources::gameboard->units().end()) {
209  loc = locations.second.erase(loc);
210  } else {
211  ++loc;
212  }
213  }
214  }
215 
216  std::set<map_location>::iterator source_it = locations.first.begin();
217  for (; source_it != locations.first.end(); ++source_it ) {
218  auto map_it = teleport_map_.find(*source_it);
219 
220  if(map_it == teleport_map_.end()) {
221  teleport_map_.emplace(*source_it, std::unordered_set(locations.second.begin(), locations.second.end()));
222  } else {
223  map_it->second.insert(locations.second.begin(), locations.second.end());
224  }
225  }
226  sources_.insert(locations.first.begin(), locations.first.end());
227  targets_.insert(locations.second.begin(), locations.second.end());
228  }
229 }
230 
231 const std::unordered_set<map_location>& teleport_map::get_adjacents(map_location loc) const
232 {
233  const auto iter = teleport_map_.find(loc);
234  if(iter == teleport_map_.end()) {
235  return empty_set_;
236  }
237 
238  return iter->second;
239 }
240 
241 const std::unordered_set<map_location>& teleport_map::get_sources() const
242 {
243  return sources_;
244 }
245 
246 const std::unordered_set<map_location>& teleport_map::get_targets() const
247 {
248  return targets_;
249 }
250 
252  const team &viewing_team,
253  bool see_all, bool ignore_units, bool check_vision)
254 {
255  std::vector<teleport_group> groups;
256 
257  for (const unit_ability & teleport : u.get_abilities("teleport")) {
258  const int tunnel_count = (teleport.ability_cfg)->child_count("tunnel");
259  for(int i = 0; i < tunnel_count; ++i) {
260  config teleport_group_cfg = (teleport.ability_cfg)->mandatory_child("tunnel", i);
261  groups.emplace_back(vconfig(teleport_group_cfg, true), false);
262  }
263  }
264 
265  const std::vector<teleport_group>& global_groups = resources::tunnels->get();
266  groups.insert(groups.end(), global_groups.begin(), global_groups.end());
267 
268  return teleport_map(groups, u, viewing_team, see_all, ignore_units, check_vision);
269 }
270 
271 manager::manager(const config &cfg) : tunnels_(), id_(cfg["next_teleport_group_id"].to_int(0)) {
272  const int tunnel_count = cfg.child_count("tunnel");
273  for(int i = 0; i < tunnel_count; ++i) {
274  const config& t = cfg.mandatory_child("tunnel", i);
275  if(!t["saved"].to_bool()) {
276  lg::log_to_chat() << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag.\n";
277  ERR_WML << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag.";
278  continue;
279  }
280  const teleport_group tunnel(t);
281  this->add(tunnel);
282  }
283 }
284 
285 void manager::add(const teleport_group &group) {
286  tunnels_.push_back(group);
287 }
288 
289 void manager::remove(const std::string &id) {
291  for(;t != tunnels_.end();) {
292  if (t->get_teleport_id() == id || t->get_teleport_id() == id + reversed_suffix) {
293  t = tunnels_.erase(t);
294  } else {
295  ++t;
296  }
297  }
298 }
299 
300 const std::vector<teleport_group>& manager::get() const {
301  return tunnels_;
302 }
303 
305  config store;
306 
307  std::vector<teleport_group>::const_iterator tunnel = tunnels_.begin();
308  for(; tunnel != tunnels_.end(); ++tunnel) {
309  store.add_child("tunnel", tunnel->to_config());
310  }
311  store["next_teleport_group_id"] = std::to_string(id_);
312 
313  return store;
314 }
315 
316 std::string manager::next_unique_id() {
317  return std::to_string(++id_);
318 }
319 
320 
321 }//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:172
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:394
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:366
std::size_t child_count(config_key_type key) const
Definition: config.cpp:296
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
config & add_child(config_key_type key)
Definition: config.cpp:440
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:107
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:304
void add(const teleport_group &group)
Definition: teleport.cpp:285
std::vector< teleport_group > tunnels_
Definition: teleport.hpp:187
void remove(const std::string &id)
Definition: teleport.cpp:289
const std::vector< teleport_group > & get() const
Definition: teleport.cpp:300
manager(const config &cfg)
Definition: teleport.cpp:271
std::string next_unique_id()
Definition: teleport.cpp:316
config to_config() const
Inherited from savegame_config.
Definition: teleport.cpp:156
bool always_visible() const
Definition: teleport.cpp:144
const std::string & get_teleport_id() const
Definition: teleport.cpp:140
teleport_group(const config &cfg)
Definition: teleport.cpp:44
bool pass_allied_units() const
Definition: teleport.cpp:148
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:152
const std::unordered_set< map_location > & get_sources() const
Returns the locations that are an entrance of the tunnel.
Definition: teleport.cpp:241
std::unordered_set< map_location > targets_
Definition: teleport.hpp:144
std::unordered_map< map_location, std::unordered_set< map_location > > teleport_map_
Definition: teleport.hpp:142
std::unordered_set< map_location > sources_
Definition: teleport.hpp:143
const std::unordered_set< map_location > & get_targets() const
Returns the locations that are an exit of the tunnel.
Definition: teleport.cpp:246
std::unordered_set< map_location > empty_set_
Definition: teleport.hpp:145
const std::unordered_set< map_location > & get_adjacents(map_location loc) const
Definition: teleport.cpp:231
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
bool is_enemy(int n) const
Definition: team.hpp:229
bool fogged(const map_location &loc) const
Definition: team.cpp:660
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:1028
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:216
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:543
std::pair< std::set< map_location >, std::set< map_location > > teleport_pair
Definition: teleport.hpp:32
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:251
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:45
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.