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