The Battle for Wesnoth  1.17.17+dev
vision.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
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.";
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 uses short-cut evaluation.
242  // (This is different than the return value because shared vision does not apply here.)
243  bool clear_shroud = tm.clear_shroud(loc);
244  bool clear_fog = tm.clear_fog(loc);
245  if ( clear_shroud || clear_fog ) {
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  bool cleared_something = false;
341  // Dummy variables to make some logic simpler.
342  std::size_t enemies=0, friends=0;
343  if ( enemy_count == nullptr )
344  enemy_count = &enemies;
345  if ( friend_count == nullptr )
346  friend_count = &friends;
347 
348  // Make sure the jamming map is up-to-date.
349  if ( view_team_ != &view_team ) {
350  calculate_jamming(&view_team);
351  }
352 
353  // Determine the hexes to clear.
354  pathfind::vision_path sight(costs, slowed, sight_range, view_loc, jamming_);
355 
356  // Clear the fog.
357  for (const pathfind::paths::step &dest : sight.destinations) {
358  bool known = known_units && known_units->count(dest.curr) != 0;
359  if ( clear_loc(view_team, dest.curr, view_loc, real_loc, viewer_id, !known,
360  *enemy_count, *friend_count, spectator) )
361  cleared_something = true;
362  }
363  //TODO guard with game_config option
364  for (const map_location &dest : sight.edges) {
365  bool known = known_units && known_units->count(dest) != 0;
366  if ( clear_loc(view_team, dest, view_loc, real_loc, viewer_id, !known,
367  *enemy_count, *friend_count, spectator) )
368  cleared_something = true;
369  }
370 
371  return cleared_something;
372 }
373 
374 
375 /**
376  * Clears shroud (and fog) around the provided location for @a view_team
377  * as if @a viewer was standing there.
378  * This will also record sighted events, which should be either fired or
379  * explicitly dropped.
380  *
381  * This should only be called if delayed shroud updates is off.
382  * It is wasteful to call this if view_team uses neither fog nor shroud.
383  *
384  * @param view_loc The location to clear fog from.
385  * @param viewer The unit whose vision range will be used to clear the fog.
386  * @param view_team The team who will have the fog cleared from their map.
387  * @param known_units These locations are not checked for uncovered units.
388  * @param enemy_count Incremented for each enemy uncovered (excluding known_units).
389  * @param friend_count Incremented for each friend uncovered (excluding known_units).
390  * @param spectator Will be told of uncovered units (excluding known_units).
391  * @param instant If false, then drawing delays (used to make movement look better) are allowed.
392  *
393  * @return whether or not information was uncovered (i.e. returns true if any
394  * locations in visual range were fogged/shrouded under shared vision/maps).
395  */
397  const unit &viewer, team &view_team,
398  const std::set<map_location>* known_units,
399  std::size_t * enemy_count, std::size_t * friend_count,
400  move_unit_spectator * spectator, bool instant)
401 {
402  // This is just a translation to the more general interface. It is
403  // not inlined so that vision.hpp does not have to include unit.hpp.
404  return clear_unit(view_loc, view_team, viewer.underlying_id(),
405  viewer.vision(), viewer.get_state(unit::STATE_SLOWED),
406  viewer.movement_type().get_vision(), viewer.get_location(),
407  known_units, enemy_count, friend_count, spectator, instant);
408 }
409 
410 
411 /**
412  * Clears shroud (and fog) around the provided location for @a view_team
413  * as if @a viewer was standing there.
414  * This will also record sighted events, which should be either fired or
415  * explicitly dropped.
416  *
417  * This should only be called if delayed shroud updates is off.
418  * It is wasteful to call this if view_team uses neither fog nor shroud.
419  *
420  * @param view_loc The location to clear fog from.
421  * @param viewer The unit whose vision range will be used to clear the fog.
422  * @param view_team The team who will have the fog cleared from their map.
423  * @param instant If false, then drawing delays (used to make movement look better) are allowed.
424  *
425  * @return whether or not information was uncovered (i.e. returns true if any
426  * locations in visual range were fogged/shrouded under shared vision/maps).
427  */
428 bool shroud_clearer::clear_unit(const map_location &view_loc, team &view_team,
429  const clearer_info &viewer, bool instant)
430 {
431  // Locate the unit in question.
433  const map_location & real_loc = find_it == resources::gameboard->units().end() ?
435  find_it->get_location();
436 
437  return clear_unit(view_loc, view_team, viewer.underlying_id,
438  viewer.sight_range, viewer.slowed, *viewer.costs,
439  real_loc, nullptr, nullptr, nullptr, nullptr, instant);
440 }
441 
442 
443 /**
444  * Clears shroud (and fog) around the provided location as if @a viewer
445  * was standing there.
446  * This version of shroud_clearer::clear_unit() will abort if the viewer's
447  * team uses neither fog nor shroud. If @a can_delay is left as true, then
448  * this function also aborts on the viewing team's turn if delayed shroud
449  * updates is on. (Not supplying a team suggests that it would be inconvenient
450  * for the caller to check these.)
451  * In addition, if @a invalidate is left as true, invalidate_after_clear()
452  * will be called.
453  * Setting @a instant to false allows some drawing delays that are used to
454  * make movement look better.
455  *
456  * @return whether or not information was uncovered (i.e. returns true if any
457  * locations in visual range were fogged/shrouded under shared vision/maps).
458  */
459 bool shroud_clearer::clear_unit(const map_location &view_loc, const unit &viewer,
460  bool can_delay, bool invalidate, bool instant)
461 {
462  team & viewing_team = resources::gameboard->get_team(viewer.side());
463 
464  // Abort if there is nothing to clear.
465  if ( !viewing_team.fog_or_shroud() )
466  return false;
467  if ( can_delay && !viewing_team.auto_shroud_updates() &&
468  viewer.side() == resources::controller->current_side() )
469  return false;
470 
471  if ( !clear_unit(view_loc, viewer, viewing_team, instant) )
472  // Nothing uncovered.
473  return false;
474 
475  if ( invalidate )
477 
478  return true;
479 }
480 
481 
482 /**
483  * Clears shroud (and fog) at the provided location and its immediate neighbors.
484  * This is an aid for the [teleport] action, allowing the destination to be
485  * cleared before teleporting, while the unit's full visual range gets cleared
486  * after.
487  * The @a viewer is needed for correct firing of sighted events.
488  *
489  * @return whether or not information was uncovered (i.e. returns true if the
490  * locations in question were fogged/shrouded under shared vision/maps).
491  */
492 bool shroud_clearer::clear_dest(const map_location &dest, const unit &viewer)
493 {
494  team & viewing_team = resources::gameboard->get_team(viewer.side());
495  // A pair of dummy variables needed to simplify some logic.
496  std::size_t enemies, friends;
497 
498  // Abort if there is nothing to clear.
499  if ( !viewing_team.fog_or_shroud() )
500  return false;
501 
502  // Cache some values.
503  const map_location & real_loc = viewer.get_location();
504  const std::size_t viewer_id = viewer.underlying_id();
505 
506  // Clear the destination.
507  bool cleared_something = clear_loc(viewing_team, dest, dest, real_loc,
508  viewer_id, true, enemies, friends);
509 
510  // Clear the adjacent hexes (will be seen even if vision is 0, and the
511  // graphics do not work so well for an isolated cleared hex).
512  for(const map_location& adj : get_adjacent_tiles(dest)) {
513  if(clear_loc(viewing_team, adj, dest, real_loc, viewer_id, true, enemies, friends)) {
514  cleared_something = true;
515  }
516  }
517 
518  if ( cleared_something )
520 
521  return cleared_something;
522 }
523 
524 
525 /**
526  * Clears the record of sighted events from earlier fog/shroud clearing.
527  * This should be called if the events are to be ignored and not fired.
528  * (Non-cleared, non-fired events will be logged as an error.)
529  */
531 {
532  if ( !sightings_.empty() ) {
533  DBG_NG << sightings_.size() << " sighted events were dropped.";
534  }
535  sightings_.clear();
536 }
537 
538 
539 /**
540  * Fires the sighted events that were recorded by earlier fog/shroud clearing.
541  * @return true if the events have mutated the game state.
542  */
544 {
545  const unit_map & units = resources::gameboard->units();
546 
547  // Possible/probable quick abort.
548  if ( sightings_.empty() )
550 
551  // In case of exceptions, clear sightings_ before processing events.
552  std::vector<sight_data> sight_list;
553  sight_list.swap(sightings_);
554 
555  for (const sight_data & event : sight_list) {
556  // Try to locate the sighting unit.
557  unit_map::const_iterator find_it = units.find(event.sighter_id);
558  const map_location & sight_loc =
559  find_it == units.end() ? map_location::null_location() :
560  find_it->get_location();
561 
562  { // Raise the event based on the latest data.
564  game_events::entity_location(event.seen_loc, event.seen_id),
565  game_events::entity_location(sight_loc, event.sighter_id, event.sighter_loc));
566  }
567  }
568 
569  return resources::game_events->pump()();
570 }
571 
572 
573 /**
574  * The invalidations that should occur after invoking clear_unit().
575  * This is separate since clear_unit() might be invoked several
576  * times in a row, and the invalidations might only need to be done once.
577  */
579 {
583  // The tiles are invalidated as they are cleared, so no need
584  // to invalidate them here.
585 }
586 
587 
588 /**
589  * Returns the sides that cannot currently see @a target.
590  * (Used to cache visibility before a move.)
591  */
592 std::vector<int> get_sides_not_seeing(const unit & target)
593 {
594  const std::vector<team> & teams = resources::gameboard->teams();
595  std::vector<int> not_seeing;
596 
597  std::size_t team_size = teams.size();
598  for ( std::size_t i = 0; i != team_size; ++i)
599  if ( !target.is_visible_to_team(teams[i], false) )
600  // not_see contains side numbers; i is a team index, so add 1.
601  not_seeing.push_back(i+1);
602 
603  return not_seeing;
604 }
605 
606 
607 /**
608  * Fires sighted events for the sides that can see @a target.
609  * If @a cache is supplied, only those sides might get events.
610  * If @a cache is nullptr, all sides might get events.
611  * This function is for the sighting *of* units that clear the shroud; it is
612  * the complement of shroud_clearer::fire_events(), which handles sighting *by*
613  * units that clear the shroud.
614  *
615  * See get_sides_not_seeing() for a way to obtain a cache.
616  *
617  * @returns true if an event has mutated the game state.
618  */
619 game_events::pump_result_t actor_sighted(const unit & target, const std::vector<int> * cache)
620 /* Current logic:
621  * 1) One event is fired per side that can see the target.
622  * 2) The second unit for the event is one that can see the target, if possible.
623  * 3) If no units on a side can see the target, a second unit is chosen as
624  * close as possible (but this behavior should not be relied on; it is
625  * subject to change at any time, should it become inconvenient).
626  * 4) A side with no units at all will not get a sighted event.
627  * 5) Sides that do not use fog or shroud CAN get sighted events.
628  */
629 {
630  const std::vector<team> & teams = resources::gameboard->teams();
631  const std::size_t teams_size = teams.size();
632  const map_location & target_loc = target.get_location();
633 
634  // Determine the teams that (probably) should get events.
635  boost::dynamic_bitset<> needs_event;
636  needs_event.resize(teams_size, cache == nullptr);
637  if ( cache != nullptr ) {
638  // Flag just the sides in the cache as needing events.
639  for (int side : *cache)
640  needs_event[side-1] = true;
641  }
642  // Exclude the target's own team.
643  needs_event[target.side()-1] = false;
644  // Exclude those teams that cannot see the target.
645  for ( std::size_t i = 0; i != teams_size; ++i )
646  needs_event[i] = needs_event[i] && target.is_visible_to_team(teams[i], false);
647 
648  // Cache "jamming".
649  std::vector< std::map<map_location, int>> jamming_cache(teams_size);
650  for ( std::size_t i = 0; i != teams_size; ++i )
651  if ( needs_event[i] )
652  create_jamming_map(jamming_cache[i], teams[i]);
653 
654  // Look for units that can be used as the second unit in sighted events.
655  std::vector<const unit *> second_units(teams_size, nullptr);
656  std::vector<std::size_t> distances(teams_size, UINT_MAX);
657  for (const unit & viewer : resources::gameboard->units()) {
658  const std::size_t index = viewer.side() - 1;
659  // Does viewer belong to a team for which we still need a unit?
660  if ( needs_event[index] && distances[index] != 0 ) {
661  if ( can_see(viewer, target_loc, &jamming_cache[index]) ) {
662  // Definitely use viewer as the second unit.
663  second_units[index] = &viewer;
664  distances[index] = 0;
665  }
666  else {
667  // Consider viewer as a backup if it is close.
668  std::size_t viewer_distance =
669  distance_between(target_loc, viewer.get_location());
670  if ( viewer_distance < distances[index] ) {
671  second_units[index] = &viewer;
672  distances[index] = viewer_distance;
673  }
674  }
675  }
676  }
677 
678  // Raise events for the appropriate teams.
679  const game_events::entity_location target_entity(target);
680  for ( std::size_t i = 0; i != teams_size; ++i )
681  if ( second_units[i] != nullptr ) {
682  resources::game_events->pump().raise(sighted_str, target_entity, game_events::entity_location(*second_units[i]));
683  }
684 
685  // Fire the events and return.
686  return resources::game_events->pump()();
687 }
688 
689 
690 /**
691  * Function that recalculates the fog of war.
692  *
693  * This is used at the end of a turn and for the defender at the end of
694  * combat. As a back-up, it is also called when clearing shroud at the
695  * beginning of a turn.
696  * This function does nothing if the indicated side does not use fog.
697  * This function ignores the "delayed shroud updates" setting.
698  * The display is invalidated as needed.
699  *
700  * @param[in] side The side whose fog will be recalculated.
701  */
702 void recalculate_fog(int side)
703 {
704  team &tm = resources::gameboard->get_team(side);
705 
706  if (!tm.uses_fog())
707  return;
708 
709  // Exclude currently seen units from sighted events.
710  std::set<map_location> visible_locs;
711  for (const unit &u : resources::gameboard->units()) {
712  const map_location & u_location = u.get_location();
713 
714  if ( !tm.fogged(u_location) )
715  visible_locs.insert(u_location);
716  }
717 
718  tm.refog();
719  // Invalidate the screen before clearing the shroud.
720  // This speeds up the invalidations within clear_shroud_unit().
722 
723  shroud_clearer clearer;
724  for (const unit &u : resources::gameboard->units())
725  {
726  if ( u.side() == side )
727  clearer.clear_unit(u.get_location(), u, tm, &visible_locs);
728  }
729  // Update the screen.
730  clearer.invalidate_after_clear();
731 
732  // Fire any sighted events we picked up.
733  clearer.fire_events();
734 }
735 
736 
737 /**
738  * Function that will clear shroud (and fog) based on current unit positions.
739  *
740  * This will not re-fog hexes unless reset_fog is set to true.
741  * This function will do nothing if the side uses neither shroud nor fog.
742  * This function ignores the "delayed shroud updates" setting.
743  * The display is invalidated as needed.
744  *
745  * @param[in] side The side whose shroud (and fog) will be cleared.
746  * @param[in] reset_fog If set to true, the fog will also be recalculated
747  * (refogging hexes that can no longer be seen).
748  * @param[in] fire_events If set to false, sighted events will not be fired.
749  * @returns true if some shroud/fog is actually cleared away.
750  */
751 bool clear_shroud(int side, bool reset_fog, bool fire_events)
752 {
753  team &tm = resources::gameboard->get_team(side);
754  if (!tm.uses_shroud() && !tm.uses_fog())
755  return false;
756 
757  bool result = false;
758 
759  shroud_clearer clearer;
760  for (const unit &u : resources::gameboard->units())
761  {
762  if ( u.side() == side )
763  result |= clearer.clear_unit(u.get_location(), u, tm);
764  }
765  // Update the screen.
766  if ( result )
767  clearer.invalidate_after_clear();
768 
769  // Sighted events.
770  if ( fire_events )
771  clearer.fire_events();
772  else
773  clearer.drop_events();
774 
775  if ( reset_fog ) {
776  // Note: This will not reveal any new tiles, so result is not affected.
777  // Also, we do not have to check fire_events at this point.
778  recalculate_fog(side);
779  }
780 
781  return result;
782 }
783 
784 }//namespace actions
Various functions related to moving units.
void add_seen_friend(const unit_map::const_iterator &u)
add a location of a seen friend
Definition: move.cpp:60
void add_seen_enemy(const unit_map::const_iterator &u)
add the location of new seen enemy
Definition: move.cpp:66
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp: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
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
void invalidate_after_clear()
The invalidations that should occur after invoking clear_unit().
Definition: vision.cpp:578
void drop_events()
Erases the record of sighted events from earlier fog/shroud clearing.
Definition: vision.cpp:530
const team * view_team_
Keeps track of the team associated with jamming_.
Definition: vision.hpp:168
void calculate_jamming(const team *new_team)
Causes this object's "jamming" map to be recalculated.
Definition: vision.cpp:190
std::vector< sight_data > sightings_
Definition: vision.hpp:166
shroud_clearer()
Default constructor.
Definition: vision.cpp:167
~shroud_clearer()
Destructor.
Definition: vision.cpp:176
std::map< map_location, int > jamming_
Definition: vision.hpp:165
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:492
game_events::pump_result_t fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:543
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,...
Definition: vision.cpp:332
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
config & add_child(config_key_type key)
Definition: config.cpp:445
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.cpp:1621
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3094
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:313
map_labels & labels()
Definition: display.cpp:2572
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3087
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:101
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:86
team & get_team(int i)
Definition: game_board.hpp:98
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:184
virtual const unit_map & units() const override
Definition: game_board.hpp:113
virtual const gamemap & map() const override
Definition: game_board.hpp:103
game_events::wml_event_pump & pump()
Definition: manager.cpp:252
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:419
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:390
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
Encapsulates the map of the game.
Definition: map.hpp:172
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:54
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:45
const terrain_costs & get_vision() const
Definition: movetype.hpp:271
int current_side() const
Returns the number of the side whose turn it is.
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:76
bool uses_shroud() const
Definition: team.hpp:305
bool clear_shroud(const map_location &loc)
Definition: team.hpp:308
bool fog_or_shroud() const
Definition: team.hpp:307
bool auto_shroud_updates() const
Definition: team.hpp:326
void refog()
Definition: team.hpp:312
bool clear_fog(const map_location &loc)
Definition: team.hpp:310
bool is_enemy(int n) const
Definition: team.hpp:231
bool uses_fog() const
Definition: team.hpp:306
bool fogged(const map_location &loc) const
Definition: team.cpp:657
Container associating units to locations.
Definition: map.hpp:99
unit_iterator end()
Definition: map.hpp:429
unit_iterator find(std::size_t id)
Definition: map.cpp:301
This class represents a single unit of a specific type.
Definition: unit.hpp:134
std::size_t i
Definition: function.cpp:968
bool is_visible_to_team(const team &team, bool const see_all=true) const
Definition: unit.cpp:2683
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition: unit.cpp:1327
int side() const
The side this unit belongs to.
Definition: unit.hpp:344
std::size_t underlying_id() const
This unit's unique internal ID.
Definition: unit.hpp:393
@ STATE_SLOWED
Definition: unit.hpp:861
@ STATE_PETRIFIED
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:863
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1358
const movetype & movement_type() const
Get the unit's movement type.
Definition: unit.hpp:1431
int vision() const
Gets the unit's vision points.
Definition: unit.hpp:1401
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
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
Standard logging facilities (interface).
constexpr bool is_even(T num)
Definition: math.hpp:33
constexpr bool is_odd(T num)
Definition: math.hpp:36
std::vector< int > get_sides_not_seeing(const unit &target)
Returns the sides that cannot currently see target.
Definition: vision.cpp:592
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:751
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:619
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's needed when creating a pathfinding::...
Definition: vision.cpp:49
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:702
std::tuple< bool, bool > pump_result_t
Definition: fwd.hpp:29
game_board * gameboard
Definition: resources.cpp:21
game_events::manager * game_events
Definition: resources.cpp:25
play_controller * controller
Definition: resources.cpp:22
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
This module contains various pathfinding functions and utilities.
Define the game's event mechanism.
Class that stores the part of a unit's data that is needed for fog clearing.
Definition: vision.hpp:53
void write(config &cfg) const
Writes to a config.
Definition: vision.cpp:122
std::size_t underlying_id
Definition: vision.hpp:54
clearer_info(const unit &viewer)
Constructor from a unit.
Definition: vision.cpp:100
std::unique_ptr< movetype::terrain_costs > costs
costs is always non-null, all of the constructors initialize it
Definition: vision.hpp:58
A record of a sighting event.
Definition: vision.cpp:139
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
Encapsulates the map of the game.
Definition: location.hpp:38
static const map_location & null_location()
Definition: location.hpp:81
A refinement of paths for use when calculating jamming.
Definition: pathfind.hpp:124
bool contains(const map_location &) const
Definition: pathfind.cpp:514
map_location curr
Definition: pathfind.hpp:89
dest_vect destinations
Definition: pathfind.hpp:101
A refinement of paths for use when calculating vision.
Definition: pathfind.hpp:108
std::set< map_location > edges
The edges are the non-destination hexes bordering the destinations.
Definition: pathfind.hpp:117
bool valid() const
Definition: map.hpp:274
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: vision.cpp:43
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's visual range.
Definition: vision.cpp:77
#define DBG_NG
Definition: vision.cpp:42
static const std::string sighted_str("sighted")
Various functions implementing vision (through fog of war and shroud).