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