The Battle for Wesnoth  1.17.0-dev
vision.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
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  * Sighting.
19  */
20 
21 #include "actions/vision.hpp"
22 
23 #include "actions/move.hpp"
24 
25 #include "config.hpp"
26 #include "game_events/pump.hpp"
27 #include "log.hpp"
28 #include "map/map.hpp"
29 #include "map/label.hpp"
30 #include "map/location.hpp"
31 #include "pathfind/pathfind.hpp"
32 #include "play_controller.hpp"
33 #include "resources.hpp"
34 #include "team.hpp"
35 #include "units/unit.hpp"
36 
37 #include <boost/dynamic_bitset.hpp>
38 
39 class unit_animation;
40 
41 static lg::log_domain log_engine("engine");
42 #define DBG_NG LOG_STREAM(debug, log_engine)
43 #define ERR_NG LOG_STREAM(err, log_engine)
44 
45 
46 static const std::string sighted_str("sighted");
47 
48 
49 void actions::create_jamming_map(std::map<map_location, int> & jamming,
50  const team & view_team)
51 {
52  // Reset the map.
53  jamming.clear();
54 
55  // Build the map.
56  for (const unit &u : resources::gameboard->units())
57  {
58  if ( u.jamming() < 1 || !view_team.is_enemy(u.side()) )
59  continue;
60 
61  pathfind::jamming_path jam_path(u, u.get_location());
62  for (const pathfind::paths::step& st : jam_path.destinations) {
63  if ( jamming[st.curr] < st.move_left )
64  jamming[st.curr] = st.move_left;
65  }
66  }
67 }
68 
69 
70 /**
71  * Determines if @a loc is within @a viewer's visual range.
72  * This is a moderately expensive function (vision is recalculated
73  * with each call), so avoid using it heavily.
74  * If @a jamming is left as nullptr, the jamming map is also calculated
75  * with each invocation.
76  */
77 static bool can_see(const unit & viewer, const map_location & loc,
78  const std::map<map_location, int> * jamming = nullptr)
79 {
80  // Make sure we have a "jamming" map.
81  std::map<map_location, int> local_jamming;
82  if ( jamming == nullptr ) {
83  actions::create_jamming_map(local_jamming, resources::gameboard->get_team(viewer.side()));
84  jamming = &local_jamming;
85  }
86 
87  // Determine which hexes this unit can see.
88  pathfind::vision_path sight(viewer, viewer.get_location(), *jamming);
89 
90  return sight.destinations.contains(loc) || sight.edges.count(loc) != 0;
91 }
92 
93 
94 namespace actions {
95 
96 
97 /**
98  * Constructor from a unit.
99  */
101  underlying_id(viewer.underlying_id()),
102  sight_range(viewer.vision()),
103  slowed(viewer.get_state(unit::STATE_SLOWED)),
104  costs(viewer.movement_type().get_vision().make_standalone())
105 {
106 }
107 
108 /**
109  * Constructor from a config.
110  */
112  underlying_id(cfg["underlying_id"].to_size_t()),
113  sight_range(cfg["vision"].to_int()),
114  slowed(cfg.child_or_empty("status")["slowed"].to_bool()),
115  costs(movetype::read_terrain_costs(cfg.child_or_empty("vision_costs")))
116 {
117 }
118 
119 /**
120  * Writes to a config.
121  */
122 void clearer_info::write(config & cfg) const
123 {
124  // The key and tag names are intended to mirror those used by [unit]
125  // (so a clearer_info can be constructed from a unit's config).
126  cfg["underlying_id"] = underlying_id;
127  cfg["vision"] = sight_range;
128  if ( slowed )
129  cfg.add_child("status")["slowed"] = true;
130  costs->write(cfg, "vision_costs");
131 }
132 
133 
134 /**
135  * A record of a sighting event.
136  * Records the unit doing a sighting, the location of that unit at the
137  * time of the sighting, and the location of the sighted unit.
138  */
140  sight_data(std::size_t viewed_id, const map_location & viewed_loc,
141  std::size_t viewer_id, const map_location & viewer_loc) :
142  seen_id(viewed_id), seen_loc(viewed_loc),
143  sighter_id(viewer_id), sighter_loc(viewer_loc)
144  {}
145 
146  std::size_t seen_id;
148  std::size_t sighter_id;
150 };
151 
152 
153 /**
154  * Convenience wrapper for adding sighting data to the sightings_ vector.
155  */
157  const unit & seen, const map_location & seen_loc,
158  std::size_t sighter_id, const map_location & sighter_loc)
159 {
160  sightings_.emplace_back(seen.underlying_id(), seen_loc, sighter_id, sighter_loc);
161 }
162 
163 
164 /**
165  * Default constructor.
166  */
167 shroud_clearer::shroud_clearer() : jamming_(), sightings_(), view_team_(nullptr)
168 {}
169 
170 
171 /**
172  * Destructor.
173  * The purpose of explicitly defining this is so we can log an error if the
174  * sighted events were neither fired nor explicitly ignored.
175  */
177 {
178  if ( !sightings_.empty() ) {
179  ERR_NG << sightings_.size() << " sighted events were ignored." << std::endl;
180  }
181 }
182 
183 /**
184  * Causes this object's "jamming" map to be recalculated.
185  * This gets called as needed, and can also be manually invoked
186  * via cache_units().
187  * @param[in] new_team The team whose vision will be used. If nullptr, the
188  * jamming map will be cleared.
189  */
191 {
192  // Reset data.
193  jamming_.clear();
194  view_team_ = new_team;
195 
196  if ( view_team_ == nullptr )
197  return;
198 
199  // Build the map.
201 }
202 
203 
204 /**
205  * Clears shroud from a single location.
206  * This also records sighted events for later firing.
207  *
208  * In a few cases, this will also clear corner hexes that otherwise would
209  * not normally get cleared.
210  * @param tm The team whose fog/shroud is affected.
211  * @param loc The location to clear.
212  * @param view_loc The location viewer is assumed at (for sighted events).
213  * @param event_non_loc The unit at this location cannot be sighted
214  * (used to prevent a unit from sighting itself).
215  * @param viewer_id The underlying ID of the unit doing the sighting (for events).
216  * @param check_units If false, there is no checking for an uncovered unit.
217  * @param enemy_count Incremented if an enemy is uncovered.
218  * @param friend_count Incremented if a friend is uncovered.
219  * @param spectator Will be told if a unit is uncovered.
220  *
221  * @return whether or not information was uncovered (i.e. returns true if
222  * the specified location was fogged/ shrouded under shared vision/maps).
223  */
225  const map_location &view_loc,
226  const map_location &event_non_loc,
227  std::size_t viewer_id, bool check_units,
228  std::size_t &enemy_count, std::size_t &friend_count,
229  move_unit_spectator * spectator)
230 {
231  const gamemap &map = resources::gameboard->map();
232  // This counts as clearing a tile for the return value if it is on the
233  // board and currently fogged under shared vision. (No need to explicitly
234  // check for shrouded since shrouded implies fogged.)
235  bool was_fogged = tm.fogged(loc);
236  bool result = was_fogged && map.on_board(loc);
237 
238  // Clear the border as well as the board, so that the half-hexes
239  // at the edge can also be cleared of fog/shroud.
240  if ( map.on_board_with_border(loc) ) {
241  // Both functions should be executed so don't use || which
242  // uses short-cut evaluation.
243  // (This is different than the return value because shared vision does
244  // not apply here.)
245  if ( tm.clear_shroud(loc) | tm.clear_fog(loc) ) {
246  // If we are near a corner, the corner might also need to be cleared.
247  // This happens at the lower-left corner and at either the upper- or
248  // lower- right corner (depending on the width).
249 
250  // Lower-left corner:
251  if ( loc.x == 0 && loc.y == map.h()-1 ) {
252  const map_location corner(-1, map.h());
253  tm.clear_shroud(corner);
254  tm.clear_fog(corner);
255  }
256  // Lower-right corner, odd width:
257  else if ( is_odd(map.w()) && loc.x == map.w()-1 && loc.y == map.h()-1 ) {
258  const map_location corner(map.w(), map.h());
259  tm.clear_shroud(corner);
260  tm.clear_fog(corner);
261  }
262  // Upper-right corner, even width:
263  else if ( is_even(map.w()) && loc.x == map.w()-1 && loc.y == 0) {
264  const map_location corner(map.w(), -1);
265  tm.clear_shroud(corner);
266  tm.clear_fog(corner);
267  }
268  }
269  }
270 
271  // Possible screen invalidation.
272  if ( was_fogged ) {
274  // Need to also invalidate adjacent hexes to get rid of the "fog edge" graphics.
275  for(const map_location& adj : get_adjacent_tiles(loc)) {
277  }
278  }
279 
280  // Check for units?
281  if ( result && check_units && loc != event_non_loc ) {
282  // Uncovered a unit?
284  if ( sight_it.valid() ) {
285  record_sighting(*sight_it, loc, viewer_id, view_loc);
286 
287  // Track this?
288  if ( !sight_it->get_state(unit::STATE_PETRIFIED) ) {
289  if ( tm.is_enemy(sight_it->side()) ) {
290  ++enemy_count;
291  if ( spectator )
292  spectator->add_seen_enemy(sight_it);
293  } else {
294  ++friend_count;
295  if ( spectator )
296  spectator->add_seen_friend(sight_it);
297  }
298  }
299  }
300  }
301 
302  return result;
303 }
304 
305 
306 /**
307  * Clears shroud (and fog) around the provided location for @a view_team
308  * based on @a sight_range, @a costs, and @a slowed.
309  * This will also record sighted events, which should be either fired or
310  * explicitly dropped. (The sighter is the unit with underlying id @a viewer_id.)
311  *
312  * This should only be called if delayed shroud updates is off.
313  * It is wasteful to call this if view_team uses neither fog nor shroud.
314  *
315  * @param view_loc The location to clear fog from.
316  * @param view_team The team who will have the fog cleared from their map.
317  * @param viewer_id The underlying ID of the unit doing the sighting (for events).
318  * @param sight_range
319  * @param slowed Whether the unit is slowed.
320  * @param costs The terrain costs for the unit.
321  * @param real_loc The actual location of the viewing unit.
322  * (This is used to avoid having a unit sight itself.)
323  * @param known_units These locations are not checked for uncovered units.
324  * @param enemy_count Incremented for each enemy uncovered (excluding known_units).
325  * @param friend_count Incremented for each friend uncovered (excluding known_units).
326  * @param spectator Will be told of uncovered units (excluding known_units).
327  * @param instant If false, then drawing delays (used to make movement look better) are allowed.
328  *
329  * @return whether or not information was uncovered (i.e. returns true if any
330  * locations in visual range were fogged/shrouded under shared vision/maps).
331  */
332 bool shroud_clearer::clear_unit(const map_location &view_loc, team &view_team,
333  std::size_t viewer_id, int sight_range, bool slowed,
334  const movetype::terrain_costs & costs,
335  const map_location & real_loc,
336  const std::set<map_location>* known_units,
337  std::size_t * enemy_count, std::size_t * friend_count,
338  move_unit_spectator * spectator, bool instant)
339 {
340  // Give animations a chance to progress; see bug #20324.
341  if ( !instant && display::get_singleton() )
342  display::get_singleton()->draw(true);
343 
344  bool cleared_something = false;
345  // Dummy variables to make some logic simpler.
346  std::size_t enemies=0, friends=0;
347  if ( enemy_count == nullptr )
348  enemy_count = &enemies;
349  if ( friend_count == nullptr )
350  friend_count = &friends;
351 
352  // Make sure the jamming map is up-to-date.
353  if ( view_team_ != &view_team ) {
354  calculate_jamming(&view_team);
355  // Give animations a chance to progress; see bug #20324.
356  if ( !instant && display::get_singleton() )
357  display::get_singleton()->draw(true);
358  }
359 
360  // Determine the hexes to clear.
361  pathfind::vision_path sight(costs, slowed, sight_range, view_loc, jamming_);
362  // Give animations a chance to progress; see bug #20324.
363  if ( !instant && display::get_singleton() )
364  display::get_singleton()->draw(true);
365 
366  // Clear the fog.
367  for (const pathfind::paths::step &dest : sight.destinations) {
368  bool known = known_units && known_units->count(dest.curr) != 0;
369  if ( clear_loc(view_team, dest.curr, view_loc, real_loc, viewer_id, !known,
370  *enemy_count, *friend_count, spectator) )
371  cleared_something = true;
372  }
373  //TODO guard with game_config option
374  for (const map_location &dest : sight.edges) {
375  bool known = known_units && known_units->count(dest) != 0;
376  if ( clear_loc(view_team, dest, view_loc, real_loc, viewer_id, !known,
377  *enemy_count, *friend_count, spectator) )
378  cleared_something = true;
379  }
380 
381  return cleared_something;
382 }
383 
384 
385 /**
386  * Clears shroud (and fog) around the provided location for @a view_team
387  * as if @a viewer was standing there.
388  * This will also record sighted events, which should be either fired or
389  * explicitly dropped.
390  *
391  * This should only be called if delayed shroud updates is off.
392  * It is wasteful to call this if view_team uses neither fog nor shroud.
393  *
394  * @param view_loc The location to clear fog from.
395  * @param viewer The unit whose vision range will be used to clear the fog.
396  * @param view_team The team who will have the fog cleared from their map.
397  * @param known_units These locations are not checked for uncovered units.
398  * @param enemy_count Incremented for each enemy uncovered (excluding known_units).
399  * @param friend_count Incremented for each friend uncovered (excluding known_units).
400  * @param spectator Will be told of uncovered units (excluding known_units).
401  * @param instant If false, then drawing delays (used to make movement look better) are allowed.
402  *
403  * @return whether or not information was uncovered (i.e. returns true if any
404  * locations in visual range were fogged/shrouded under shared vision/maps).
405  */
407  const unit &viewer, team &view_team,
408  const std::set<map_location>* known_units,
409  std::size_t * enemy_count, std::size_t * friend_count,
410  move_unit_spectator * spectator, bool instant)
411 {
412  // This is just a translation to the more general interface. It is
413  // not inlined so that vision.hpp does not have to include unit.hpp.
414  return clear_unit(view_loc, view_team, viewer.underlying_id(),
415  viewer.vision(), viewer.get_state(unit::STATE_SLOWED),
416  viewer.movement_type().get_vision(), viewer.get_location(),
417  known_units, enemy_count, friend_count, spectator, instant);
418 }
419 
420 
421 /**
422  * Clears shroud (and fog) around the provided location for @a view_team
423  * as if @a viewer was standing there.
424  * This will also record sighted events, which should be either fired or
425  * explicitly dropped.
426  *
427  * This should only be called if delayed shroud updates is off.
428  * It is wasteful to call this if view_team uses neither fog nor shroud.
429  *
430  * @param view_loc The location to clear fog from.
431  * @param viewer The unit whose vision range will be used to clear the fog.
432  * @param view_team The team who will have the fog cleared from their map.
433  * @param instant If false, then drawing delays (used to make movement look better) are allowed.
434  *
435  * @return whether or not information was uncovered (i.e. returns true if any
436  * locations in visual range were fogged/shrouded under shared vision/maps).
437  */
438 bool shroud_clearer::clear_unit(const map_location &view_loc, team &view_team,
439  const clearer_info &viewer, bool instant)
440 {
441  // Locate the unit in question.
443  const map_location & real_loc = find_it == resources::gameboard->units().end() ?
445  find_it->get_location();
446 
447  return clear_unit(view_loc, view_team, viewer.underlying_id,
448  viewer.sight_range, viewer.slowed, *viewer.costs,
449  real_loc, nullptr, nullptr, nullptr, nullptr, instant);
450 }
451 
452 
453 /**
454  * Clears shroud (and fog) around the provided location as if @a viewer
455  * was standing there.
456  * This version of shroud_clearer::clear_unit() will abort if the viewer's
457  * team uses neither fog nor shroud. If @a can_delay is left as true, then
458  * this function also aborts on the viewing team's turn if delayed shroud
459  * updates is on. (Not supplying a team suggests that it would be inconvenient
460  * for the caller to check these.)
461  * In addition, if @a invalidate is left as true, invalidate_after_clear()
462  * will be called.
463  * Setting @a instant to false allows some drawing delays that are used to
464  * make movement look better.
465  *
466  * @return whether or not information was uncovered (i.e. returns true if any
467  * locations in visual range were fogged/shrouded under shared vision/maps).
468  */
469 bool shroud_clearer::clear_unit(const map_location &view_loc, const unit &viewer,
470  bool can_delay, bool invalidate, bool instant)
471 {
472  team & viewing_team = resources::gameboard->get_team(viewer.side());
473 
474  // Abort if there is nothing to clear.
475  if ( !viewing_team.fog_or_shroud() )
476  return false;
477  if ( can_delay && !viewing_team.auto_shroud_updates() &&
478  viewer.side() == resources::controller->current_side() )
479  return false;
480 
481  if ( !clear_unit(view_loc, viewer, viewing_team, instant) )
482  // Nothing uncovered.
483  return false;
484 
485  if ( invalidate )
487 
488  return true;
489 }
490 
491 
492 /**
493  * Clears shroud (and fog) at the provided location and its immediate neighbors.
494  * This is an aid for the [teleport] action, allowing the destination to be
495  * cleared before teleporting, while the unit's full visual range gets cleared
496  * after.
497  * The @a viewer is needed for correct firing of sighted events.
498  *
499  * @return whether or not information was uncovered (i.e. returns true if the
500  * locations in question were fogged/shrouded under shared vision/maps).
501  */
502 bool shroud_clearer::clear_dest(const map_location &dest, const unit &viewer)
503 {
504  team & viewing_team = resources::gameboard->get_team(viewer.side());
505  // A pair of dummy variables needed to simplify some logic.
506  std::size_t enemies, friends;
507 
508  // Abort if there is nothing to clear.
509  if ( !viewing_team.fog_or_shroud() )
510  return false;
511 
512  // Cache some values.
513  const map_location & real_loc = viewer.get_location();
514  const std::size_t viewer_id = viewer.underlying_id();
515 
516  // Clear the destination.
517  bool cleared_something = clear_loc(viewing_team, dest, dest, real_loc,
518  viewer_id, true, enemies, friends);
519 
520  // Clear the adjacent hexes (will be seen even if vision is 0, and the
521  // graphics do not work so well for an isolated cleared hex).
522  for(const map_location& adj : get_adjacent_tiles(dest)) {
523  if(clear_loc(viewing_team, adj, dest, real_loc, viewer_id, true, enemies, friends)) {
524  cleared_something = true;
525  }
526  }
527 
528  if ( cleared_something )
530 
531  return cleared_something;
532 }
533 
534 
535 /**
536  * Clears the record of sighted events from earlier fog/shroud clearing.
537  * This should be called if the events are to be ignored and not fired.
538  * (Non-cleared, non-fired events will be logged as an error.)
539  */
541 {
542  if ( !sightings_.empty() ) {
543  DBG_NG << sightings_.size() << " sighted events were dropped.\n";
544  }
545  sightings_.clear();
546 }
547 
548 
549 /**
550  * Fires the sighted events that were recorded by earlier fog/shroud clearing.
551  * @return true if the events have mutated the game state.
552  */
554 {
555  const unit_map & units = resources::gameboard->units();
556 
557  // Possible/probable quick abort.
558  if ( sightings_.empty() )
560 
561  // In case of exceptions, clear sightings_ before processing events.
562  std::vector<sight_data> sight_list;
563  sight_list.swap(sightings_);
564 
565  for (const sight_data & event : sight_list) {
566  // Try to locate the sighting unit.
567  unit_map::const_iterator find_it = units.find(event.sighter_id);
568  const map_location & sight_loc =
569  find_it == units.end() ? map_location::null_location() :
570  find_it->get_location();
571 
572  { // Raise the event based on the latest data.
574  game_events::entity_location(event.seen_loc, event.seen_id),
575  game_events::entity_location(sight_loc, event.sighter_id, event.sighter_loc));
576  }
577  }
578 
579  return resources::game_events->pump()();
580 }
581 
582 
583 /**
584  * The invalidations that should occur after invoking clear_unit().
585  * This is separate since clear_unit() might be invoked several
586  * times in a row, and the invalidations might only need to be done once.
587  */
589 {
593  // The tiles are invalidated as they are cleared, so no need
594  // to invalidate them here.
595 }
596 
597 
598 /**
599  * Returns the sides that cannot currently see @a target.
600  * (Used to cache visibility before a move.)
601  */
602 std::vector<int> get_sides_not_seeing(const unit & target)
603 {
604  const std::vector<team> & teams = resources::gameboard->teams();
605  std::vector<int> not_seeing;
606 
607  std::size_t team_size = teams.size();
608  for ( std::size_t i = 0; i != team_size; ++i)
609  if ( !target.is_visible_to_team(teams[i], false) )
610  // not_see contains side numbers; i is a team index, so add 1.
611  not_seeing.push_back(i+1);
612 
613  return not_seeing;
614 }
615 
616 
617 /**
618  * Fires sighted events for the sides that can see @a target.
619  * If @a cache is supplied, only those sides might get events.
620  * If @a cache is nullptr, all sides might get events.
621  * This function is for the sighting *of* units that clear the shroud; it is
622  * the complement of shroud_clearer::fire_events(), which handles sighting *by*
623  * units that clear the shroud.
624  *
625  * See get_sides_not_seeing() for a way to obtain a cache.
626  *
627  * @returns true if an event has mutated the game state.
628  */
629 game_events::pump_result_t actor_sighted(const unit & target, const std::vector<int> * cache)
630 /* Current logic:
631  * 1) One event is fired per side that can see the target.
632  * 2) The second unit for the event is one that can see the target, if possible.
633  * 3) If no units on a side can see the target, a second unit is chosen as
634  * close as possible (but this behavior should not be relied on; it is
635  * subject to change at any time, should it become inconvenient).
636  * 4) A side with no units at all will not get a sighted event.
637  * 5) Sides that do not use fog or shroud CAN get sighted events.
638  */
639 {
640  const std::vector<team> & teams = resources::gameboard->teams();
641  const std::size_t teams_size = teams.size();
642  const map_location & target_loc = target.get_location();
643 
644  // Determine the teams that (probably) should get events.
645  boost::dynamic_bitset<> needs_event;
646  needs_event.resize(teams_size, cache == nullptr);
647  if ( cache != nullptr ) {
648  // Flag just the sides in the cache as needing events.
649  for (int side : *cache)
650  needs_event[side-1] = true;
651  }
652  // Exclude the target's own team.
653  needs_event[target.side()-1] = false;
654  // Exclude those teams that cannot see the target.
655  for ( std::size_t i = 0; i != teams_size; ++i )
656  needs_event[i] = needs_event[i] && target.is_visible_to_team(teams[i], false);
657 
658  // Cache "jamming".
659  std::vector< std::map<map_location, int>> jamming_cache(teams_size);
660  for ( std::size_t i = 0; i != teams_size; ++i )
661  if ( needs_event[i] )
662  create_jamming_map(jamming_cache[i], teams[i]);
663 
664  // Look for units that can be used as the second unit in sighted events.
665  std::vector<const unit *> second_units(teams_size, nullptr);
666  std::vector<std::size_t> distances(teams_size, UINT_MAX);
667  for (const unit & viewer : resources::gameboard->units()) {
668  const std::size_t index = viewer.side() - 1;
669  // Does viewer belong to a team for which we still need a unit?
670  if ( needs_event[index] && distances[index] != 0 ) {
671  if ( can_see(viewer, target_loc, &jamming_cache[index]) ) {
672  // Definitely use viewer as the second unit.
673  second_units[index] = &viewer;
674  distances[index] = 0;
675  }
676  else {
677  // Consider viewer as a backup if it is close.
678  std::size_t viewer_distance =
679  distance_between(target_loc, viewer.get_location());
680  if ( viewer_distance < distances[index] ) {
681  second_units[index] = &viewer;
682  distances[index] = viewer_distance;
683  }
684  }
685  }
686  }
687 
688  // Raise events for the appropriate teams.
689  const game_events::entity_location target_entity(target);
690  for ( std::size_t i = 0; i != teams_size; ++i )
691  if ( second_units[i] != nullptr ) {
692  resources::game_events->pump().raise(sighted_str, target_entity, game_events::entity_location(*second_units[i]));
693  }
694 
695  // Fire the events and return.
696  return resources::game_events->pump()();
697 }
698 
699 
700 /**
701  * Function that recalculates the fog of war.
702  *
703  * This is used at the end of a turn and for the defender at the end of
704  * combat. As a back-up, it is also called when clearing shroud at the
705  * beginning of a turn.
706  * This function does nothing if the indicated side does not use fog.
707  * This function ignores the "delayed shroud updates" setting.
708  * The display is invalidated as needed.
709  *
710  * @param[in] side The side whose fog will be recalculated.
711  */
712 void recalculate_fog(int side)
713 {
714  team &tm = resources::gameboard->get_team(side);
715 
716  if (!tm.uses_fog())
717  return;
718 
719  // Exclude currently seen units from sighted events.
720  std::set<map_location> visible_locs;
721  for (const unit &u : resources::gameboard->units()) {
722  const map_location & u_location = u.get_location();
723 
724  if ( !tm.fogged(u_location) )
725  visible_locs.insert(u_location);
726  }
727 
728  tm.refog();
729  // Invalidate the screen before clearing the shroud.
730  // This speeds up the invalidations within clear_shroud_unit().
732 
733  shroud_clearer clearer;
734  for (const unit &u : resources::gameboard->units())
735  {
736  if ( u.side() == side )
737  clearer.clear_unit(u.get_location(), u, tm, &visible_locs);
738  }
739  // Update the screen.
740  clearer.invalidate_after_clear();
741 
742  // Fire any sighted events we picked up.
743  clearer.fire_events();
744 }
745 
746 
747 /**
748  * Function that will clear shroud (and fog) based on current unit positions.
749  *
750  * This will not re-fog hexes unless reset_fog is set to true.
751  * This function will do nothing if the side uses neither shroud nor fog.
752  * This function ignores the "delayed shroud updates" setting.
753  * The display is invalidated as needed.
754  *
755  * @param[in] side The side whose shroud (and fog) will be cleared.
756  * @param[in] reset_fog If set to true, the fog will also be recalculated
757  * (refogging hexes that can no longer be seen).
758  * @param[in] fire_events If set to false, sighted events will not be fired.
759  * @returns true if some shroud/fog is actually cleared away.
760  */
761 bool clear_shroud(int side, bool reset_fog, bool fire_events)
762 {
763  team &tm = resources::gameboard->get_team(side);
764  if (!tm.uses_shroud() && !tm.uses_fog())
765  return false;
766 
767  bool result = false;
768 
769  shroud_clearer clearer;
770  for (const unit &u : resources::gameboard->units())
771  {
772  if ( u.side() == side )
773  result |= clearer.clear_unit(u.get_location(), u, tm);
774  }
775  // Update the screen.
776  if ( result )
777  clearer.invalidate_after_clear();
778 
779  // Sighted events.
780  if ( fire_events )
781  clearer.fire_events();
782  else
783  clearer.drop_events();
784 
785  if ( reset_fog ) {
786  // Note: This will not reveal any new tiles, so result is not affected.
787  // Also, we do not have to check fire_events at this point.
788  recalculate_fog(side);
789  }
790 
791  return result;
792 }
793 
794 }//namespace actions
Class that stores the part of a unit&#39;s data that is needed for fog clearing.
Definition: vision.hpp:53
play_controller * controller
Definition: resources.cpp:22
void recalculate_shroud()
Definition: label.cpp:278
A const-only interface for how many (movement, vision, or "jamming") points a unit needs for each hex...
Definition: movetype.hpp:53
bool is_odd(T num)
Definition: math.hpp:36
unit_iterator end()
Definition: map.hpp:429
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:92
std::vector< sight_data > sightings_
Definition: vision.hpp:166
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:85
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:296
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:475
virtual const unit_map & units() const override
Definition: game_board.hpp:112
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3026
int vision() const
Gets the unit&#39;s vision points.
Definition: unit.hpp:1389
This class represents a single unit of a specific type.
Definition: unit.hpp:121
A record of a sighting event.
Definition: vision.cpp:139
Various functions implementing vision (through fog of war and shroud).
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition: unit.cpp:1311
bool clear_fog(const map_location &loc)
Definition: team.hpp:335
const movetype & movement_type() const
Get the unit&#39;s movement type.
Definition: unit.hpp:1419
void refog()
Definition: team.hpp:337
A refinement of paths for use when calculating jamming.
Definition: pathfind.hpp:123
std::size_t underlying_id
Definition: vision.hpp:54
~shroud_clearer()
Destructor.
Definition: vision.cpp:176
virtual const gamemap & map() const override
Definition: game_board.hpp:102
dest_vect destinations
Definition: pathfind.hpp:101
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:853
void create_jamming_map(std::map< map_location, int > &jamming, const team &view_team)
Helper function that creates the map of enemy anti-vision that&#39;s needed when creating a pathfinding::...
Definition: vision.cpp:49
clearer_info(const unit &viewer)
Constructor from a unit.
Definition: vision.cpp:100
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2478
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:44
void calculate_jamming(const team *new_team)
Causes this object&#39;s "jamming" map to be recalculated.
Definition: vision.cpp:190
Definitions for the interface to Wesnoth Markup Language (WML).
bool clear_dest(const map_location &dest, const unit &viewer)
Clears shroud (and fog) at the provided location and its immediate neighbors.
Definition: vision.cpp:502
bool uses_fog() const
Definition: team.hpp:331
bool clear_unit(const map_location &view_loc, team &view_team, std::size_t viewer_id, int sight_range, bool slowed, const movetype::terrain_costs &costs, const map_location &real_loc, const std::set< map_location > *known_units=nullptr, std::size_t *enemy_count=nullptr, std::size_t *friend_count=nullptr, move_unit_spectator *spectator=nullptr, bool instant=true)
Clears shroud (and fog) around the provided location for view_team based on sight_range, costs, and slowed.
Definition: vision.cpp:332
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
team & get_team(int i)
Definition: game_board.hpp:97
game_events::pump_result_t fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:553
static lg::log_domain log_engine("engine")
int w() const
Effective map width.
Definition: map.hpp:50
bool uses_shroud() const
Definition: team.hpp:330
void add_seen_enemy(const unit_map::const_iterator &u)
add the location of new seen enemy
Definition: move.cpp:66
map_location curr
Definition: pathfind.hpp:89
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.hpp:619
bool is_even(T num)
Definition: math.hpp:33
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:390
game_board * gameboard
Definition: resources.cpp:21
const terrain_costs & get_vision() const
Definition: movetype.hpp:271
#define DBG_NG
Definition: vision.cpp:42
bool fog_or_shroud() const
Definition: team.hpp:332
Encapsulates the map of the game.
Definition: map.hpp:171
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:712
bool is_enemy(int n) const
Definition: team.hpp:255
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3019
game_events::manager * game_events
Definition: resources.cpp:25
Encapsulates the map of the game.
Definition: location.hpp:38
bool auto_shroud_updates() const
Definition: team.hpp:351
Various functions related to moving units.
unit_iterator find(std::size_t id)
Definition: map.cpp:310
void drop_events()
Erases the record of sighted events from earlier fog/shroud clearing.
Definition: vision.cpp:540
std::size_t i
Definition: function.cpp:967
static bool can_see(const unit &viewer, const map_location &loc, const std::map< map_location, int > *jamming=nullptr)
Determines if loc is within viewer&#39;s visual range.
Definition: vision.cpp:77
bool clear_shroud(const map_location &loc)
Definition: team.hpp:333
void raise(const std::string &event, const std::string &id, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Definition: pump.cpp:498
bool clear_loc(team &tm, const map_location &loc, const map_location &view_loc, const map_location &event_non_loc, std::size_t viewer_id, bool check_units, std::size_t &enemy_count, std::size_t &friend_count, move_unit_spectator *spectator=nullptr)
Clears shroud from a single location.
Definition: vision.cpp:224
#define ERR_NG
Definition: vision.cpp:43
static tcache cache
Definition: minimap.cpp:124
Define the game&#39;s event mechanism.
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
A refinement of paths for use when calculating vision.
Definition: pathfind.hpp:107
void write(config &cfg) const
Writes to a config.
Definition: vision.cpp:122
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:72
void record_sighting(const unit &seen, const map_location &seen_loc, std::size_t sighter_id, const map_location &sighter_loc)
Convenience wrapper for adding sighting data to the sightings_ vector.
Definition: vision.cpp:156
config & add_child(config_key_type key)
Definition: config.cpp:514
bool is_visible_to_team(const team &team, bool const see_all=true) const
Definition: unit.cpp:2479
bool clear_shroud(int side, bool reset_fog, bool fire_events)
Function that will clear shroud (and fog) based on current unit positions.
Definition: vision.cpp:761
std::unique_ptr< movetype::terrain_costs > costs
costs is always non-null, all of the constructors initialize it
Definition: vision.hpp:58
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.cpp:546
const team * view_team_
Keeps track of the team associated with jamming_.
Definition: vision.hpp:168
void add_seen_friend(const unit_map::const_iterator &u)
add a location of a seen friend
Definition: move.cpp:60
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1346
std::map< map_location, int > jamming_
Definition: vision.hpp:165
std::set< map_location > edges
The edges are the non-destination hexes bordering the destinations.
Definition: pathfind.hpp:117
bool contains(const map_location &) const
Definition: pathfind.cpp:515
Standard logging facilities (interface).
int current_side() const
Returns the number of the side whose turn it is.
static const map_location & null_location()
Definition: location.hpp:81
Container associating units to locations.
Definition: map.hpp:98
sight_data(std::size_t viewed_id, const map_location &viewed_loc, std::size_t viewer_id, const map_location &viewer_loc)
Definition: vision.cpp:140
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp:72
bool fogged(const map_location &loc) const
Definition: team.cpp:661
map_labels & labels()
Definition: display.cpp:2536
int side() const
The side this unit belongs to.
Definition: unit.hpp:334
game_events::wml_event_pump & pump()
Definition: manager.cpp:230
std::tuple< bool, bool > pump_result_t
Definition: fwd.hpp:29
void invalidate_after_clear()
The invalidations that should occur after invoking clear_unit().
Definition: vision.cpp:588
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
bool valid() const
Definition: map.hpp:274
shroud_clearer()
Default constructor.
Definition: vision.cpp:167
int h() const
Effective map height.
Definition: map.hpp:53
std::vector< int > get_sides_not_seeing(const unit &target)
Returns the sides that cannot currently see target.
Definition: vision.cpp:602
static const std::string sighted_str("sighted")
This module contains various pathfinding functions and utilities.
std::size_t underlying_id() const
This unit&#39;s unique internal ID.
Definition: unit.hpp:383
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:183
game_events::pump_result_t actor_sighted(const unit &target, const std::vector< int > *cache)
Fires sighted events for the sides that can see target.
Definition: vision.cpp:629