3 * Summary: Line-of-sight algorithm.
7 * == Definition of visibility ==
9 * Two cells are in view of each other if there is any straight
10 * line that meets both cells and that doesn't meet any opaque
11 * cell in between, and if the cells are in LOS range of each
14 * Here, to "meet" a cell means to intersect the interiour. In
15 * particular, rays can pass between to diagonally adjacent
16 * walls (as can the player).
20 * A _ray_ is a line, specified by starting point (accx, accy)
21 * and slope. A ray determines its _footprint_: the sequence of
22 * cells whose interiour it meets.
24 * Any prefix of the footprint of a ray is called a _cellray_.
26 * For the purposes of LOS calculation, only the footprints
27 * are relevant, but rays are also used for shooting beams,
28 * which may travel beyond LOS and which can be reflected.
33 * At first use, the LOS code makes some precomputations,
34 * filling a list of all relevant rays in one quadrant,
35 * and filling data structures that allow calculating LOS
36 * in a quadrant without checking each ray.
38 * The code provides functions for filling LOS information
39 * around a given center efficiently, and for querying rays
40 * between two given cells.
53 #include "coord-circle.h"
58 #include "losglobal.h"
66 // This determines which cells are considered out of range during
67 // precalculations (only positive quadrant used).
68 // For the LOS code to work correctly, any LOS shape that
69 // is used needs to be contained in bds_precalc.
70 const circle_def bds_precalc
= circle_def(LOS_MAX_RANGE
, C_ROUND
);
72 // These determine what rays are cast in the precomputation,
73 // and affect start-up time significantly.
74 // XXX: Argue that these values are sufficient.
75 #define LOS_MAX_ANGLE (2*LOS_MAX_RANGE-2)
76 #define LOS_INTERCEPT_MULT (2)
78 // These store all unique (in terms of footprint) full rays.
79 // The footprint of ray=fullray[i] consists of ray.length cells,
80 // stored in ray_coords[ray.start..ray.length-1].
81 // These are filled during precomputation (_register_ray).
82 // XXX: fullrays is not needed anymore after precomputation.
84 std::vector
<los_ray
> fullrays
;
85 std::vector
<coord_def
> ray_coords
;
87 // These store all unique minimal cellrays. For each i,
88 // cellray i ends in cellray_ends[i] and passes through
89 // thoses cells p that have blockrays(p)[i] set. In other
90 // words, blockrays(p)[i] is set iff an opaque cell p blocks
91 // the cellray with index i.
92 std::vector
<coord_def
> cellray_ends
;
93 typedef FixedArray
<bit_array
*, LOS_MAX_RANGE
+1, LOS_MAX_RANGE
+1> blockrays_t
;
94 blockrays_t blockrays
;
96 // We also store the minimal cellrays by target position
97 // for efficient retrieval by find_ray.
98 // XXX: Consider condensing this representation.
100 FixedArray
<std::vector
<cellray
>, LOS_MAX_RANGE
+1, LOS_MAX_RANGE
+1> min_cellrays
;
102 // Temporary arrays used in losight() to track which rays
103 // are blocked or have seen a smoke cloud.
104 // Allocated when doing the precomputations.
105 bit_array
*dead_rays
= NULL
;
106 bit_array
*smoke_rays
= NULL
;
108 class quadrant_iterator
: public rectangle_iterator
112 : rectangle_iterator(coord_def(0,0),
113 coord_def(LOS_MAX_RANGE
, LOS_MAX_RANGE
))
118 void clear_rays_on_exit()
122 for (quadrant_iterator qi
; qi
; qi
++)
123 delete blockrays(*qi
);
126 // Pre-squared LOS radius.
127 int _los_radius_sq
= LOS_RADIUS_SQ
;
129 static void _handle_los_change();
131 void set_los_radius(int r
)
133 ASSERT(r
<= LOS_MAX_RADIUS
);
134 _los_radius_sq
= r
* r
+ 1;
136 _handle_los_change();
139 // XXX: just for monster_los
140 int get_los_radius_sq()
142 return _los_radius_sq
;
145 bool double_is_zero(const double x
)
147 return (x
> -EPSILON_VALUE
) && (x
< EPSILON_VALUE
);
150 struct los_ray
: public ray_def
152 // The footprint of this ray is stored in
153 // ray_coords[start..start+length-1].
157 los_ray(geom::ray _r
)
158 : ray_def(_r
), start(0), length(0)
162 // Shoot a ray from the given start point (accx, accy) with the given
163 // slope, bounded by the pre-calc bounds shape.
164 // Returns the cells it travels through, excluding the origin.
165 // Returns an empty vector if this was a bad ray.
166 std::vector
<coord_def
> footprint()
168 std::vector
<coord_def
> cs
;
169 los_ray copy
= *this;
173 for (cellnum
= 0; true; ++cellnum
)
178 //#ifdef DEBUG_DIAGNOSTICS
179 // mprf(MSGCH_DIAGNOSTICS,
180 // "discarding corner ray (%f,%f) + t*(%f,%f)",
181 // r.start.x, r.start.y, r.dir.x, r.dir.y);
187 if (!bds_precalc
.contains(c
))
190 ASSERT((c
- old
).rdist() == 1);
195 coord_def
operator[](unsigned int i
)
198 return ray_coords
[start
+i
];
202 // Check if the passed rays have identical footprint.
203 static bool _is_same_ray(los_ray ray
, std::vector
<coord_def
> newray
)
205 if (ray
.length
!= newray
.size())
207 for (unsigned int i
= 0; i
< ray
.length
; i
++)
208 if (ray
[i
] != newray
[i
])
213 // Check if the passed ray has already been created.
214 static bool _is_duplicate_ray(std::vector
<coord_def
> newray
)
216 for (unsigned int i
= 0; i
< fullrays
.size(); ++i
)
217 if (_is_same_ray(fullrays
[i
], newray
))
222 // A cellray given by fullray and index of end-point.
225 // A cellray passes through cells ray_coords[ray.start..ray.start+end].
227 unsigned int end
; // Relative index (inside ray) of end cell.
229 cellray(const los_ray
& r
, unsigned int e
)
230 : ray(r
), end(e
), imbalance(-1), first_diag(false)
234 // The end-point's index inside ray_coord.
235 int index() const { return (ray
.start
+ end
); }
238 coord_def
target() const { return ray_coords
[index()]; }
240 // XXX: Currently ray/cellray[0] is the first point outside the origin.
241 coord_def
operator[](unsigned int i
)
244 return ray_coords
[ray
.start
+i
];
247 // Parameters used in find_ray. These need to be calculated
248 // only for the minimal cellrays.
255 // Compare two cellrays to the same target.
256 // This determines which ray is considered better by find_ray,
257 // used with list::sort.
258 // Returns true if a is strictly better than b, false else.
259 static bool _is_better(const cellray
& a
, const cellray
& b
)
261 // Only compare cellrays with equal target.
262 ASSERT(a
.target() == b
.target());
263 // calc_params() has been called.
264 ASSERT(a
.imbalance
>= 0 && b
.imbalance
>= 0);
265 if (a
.imbalance
< b
.imbalance
)
267 else if (a
.imbalance
> b
.imbalance
)
270 return (a
.first_diag
&& !b
.first_diag
);
280 // Check whether one of the passed cellrays is a subray of the
281 // other in terms of footprint.
282 static compare_type
_compare_cellrays(const cellray
& a
, const cellray
& b
)
284 if (a
.target() != b
.target())
287 int cura
= a
.ray
.start
;
288 int curb
= b
.ray
.start
;
289 int enda
= cura
+ a
.end
;
290 int endb
= curb
+ b
.end
;
291 bool maybe_sub
= true;
292 bool maybe_super
= true;
294 while (cura
< enda
&& curb
< endb
&& (maybe_sub
|| maybe_super
))
296 coord_def pa
= ray_coords
[cura
];
297 coord_def pb
= ray_coords
[curb
];
298 if (pa
.x
> pb
.x
|| pa
.y
> pb
.y
)
303 if (pa
.x
< pb
.x
|| pa
.y
< pb
.y
)
314 maybe_sub
= maybe_sub
&& (cura
== enda
);
315 maybe_super
= maybe_super
&& (curb
== endb
);
318 return C_SUBRAY
; // includes equality
319 else if (maybe_super
)
325 // Determine all minimal cellrays.
326 // They're stored globally by target in min_cellrays,
327 // and returned as a list of indices into ray_coords.
328 static std::vector
<int> _find_minimal_cellrays()
330 FixedArray
<std::list
<cellray
>, LOS_MAX_RANGE
+1, LOS_MAX_RANGE
+1> minima
;
331 std::list
<cellray
>::iterator min_it
;
333 for (unsigned int r
= 0; r
< fullrays
.size(); ++r
)
335 los_ray ray
= fullrays
[r
];
336 for (unsigned int i
= 0; i
< ray
.length
; ++i
)
338 // Is the cellray ray[0..i] duplicated so far?
341 std::list
<cellray
>& min
= minima(c
.target());
344 for (min_it
= min
.begin();
345 min_it
!= min
.end() && !dup
;)
347 switch(_compare_cellrays(*min_it
, c
))
353 min_it
= min
.erase(min_it
);
355 // clear this should be added, but might have
372 std::vector
<int> result
;
373 for (quadrant_iterator qi
; qi
; qi
++)
375 std::list
<cellray
>& min
= minima(*qi
);
376 for (min_it
= min
.begin(); min_it
!= min
.end(); min_it
++)
378 // Calculate imbalance and slope difference for sorting.
379 min_it
->calc_params();
380 result
.push_back(min_it
->index());
382 min
.sort(_is_better
);
383 min_cellrays(*qi
) = std::vector
<cellray
>(min
.begin(), min
.end());
388 // Create and register the ray defined by the arguments.
389 static void _register_ray(geom::ray r
)
391 los_ray ray
= los_ray(r
);
392 std::vector
<coord_def
> coords
= ray
.footprint();
394 if (coords
.empty() || _is_duplicate_ray(coords
))
397 ray
.start
= ray_coords
.size();
398 ray
.length
= coords
.size();
399 for (unsigned int i
= 0; i
< coords
.size(); i
++)
400 ray_coords
.push_back(coords
[i
]);
401 fullrays
.push_back(ray
);
404 static void _create_blockrays()
406 // First, we calculate blocking information for all cell rays.
407 // Cellrays are numbered according to the index of their end
408 // cell in ray_coords.
409 const int n_cellrays
= ray_coords
.size();
410 blockrays_t all_blockrays
;
411 for (quadrant_iterator qi
; qi
; qi
++)
412 all_blockrays(*qi
) = new bit_array(n_cellrays
);
414 for (unsigned int r
= 0; r
< fullrays
.size(); ++r
)
416 los_ray ray
= fullrays
[r
];
417 for (unsigned int i
= 0; i
< ray
.length
; ++i
)
419 // Every cell is contained in (thus blocks)
420 // all following cellrays.
421 for (unsigned int j
= i
+ 1; j
< ray
.length
; ++j
)
422 all_blockrays(ray
[i
])->set(ray
.start
+ j
);
426 // We've built the basic blockray array; now compress it, keeping
427 // only the nonduplicated cellrays.
429 // Determine minimal cellrays and store their indices in ray_coords.
430 std::vector
<int> min_indices
= _find_minimal_cellrays();
431 const int n_min_rays
= min_indices
.size();
432 cellray_ends
.resize(n_min_rays
);
433 for (int i
= 0; i
< n_min_rays
; ++i
)
434 cellray_ends
[i
] = ray_coords
[min_indices
[i
]];
436 // Compress blockrays accordingly.
437 for (quadrant_iterator qi
; qi
; qi
++)
439 blockrays(*qi
) = new bit_array(n_min_rays
);
440 for (int i
= 0; i
< n_min_rays
; ++i
)
441 blockrays(*qi
)->set(i
, all_blockrays(*qi
)
442 ->get(min_indices
[i
]));
445 // We can throw away all_blockrays now.
446 for (quadrant_iterator qi
; qi
; qi
++)
447 delete all_blockrays(*qi
);
449 dead_rays
= new bit_array(n_min_rays
);
450 smoke_rays
= new bit_array(n_min_rays
);
452 #ifdef DEBUG_DIAGNOSTICS
453 mprf(MSGCH_DIAGNOSTICS
, "Cellrays: %d Fullrays: %u Minimal cellrays: %u",
454 n_cellrays
, fullrays
.size(), n_min_rays
);
458 static int _gcd(int x
, int y
)
471 bool complexity_lt(const std::pair
<int,int>& lhs
,
472 const std::pair
<int,int>& rhs
)
474 return lhs
.first
* lhs
.second
< rhs
.first
* rhs
.second
;
478 static void raycast()
480 static bool done_raycast
= false;
484 // Creating all rays for first quadrant
485 // We have a considerable amount of overkill.
488 // register perpendiculars FIRST, to make them top choice
489 // when selecting beams
490 _register_ray(geom::ray(0.5, 0.5, 0.0, 1.0));
491 _register_ray(geom::ray(0.5, 0.5, 1.0, 0.0));
493 // For a slope of M = y/x, every x we move on the X axis means
494 // that we move y on the y axis. We want to look at the resolution
495 // of x/y: in that case, every step on the X axis means an increase
496 // of 1 in the Y axis at the intercept point. We can assume gcd(x,y)=1,
497 // so we look at steps of 1/y.
499 // Changing the order a bit. We want to order by the complexity
500 // of the beam, which is log(x) + log(y) ~ xy.
501 std::vector
<std::pair
<int,int> > xyangles
;
502 for (int xangle
= 1; xangle
<= LOS_MAX_ANGLE
; ++xangle
)
503 for (int yangle
= 1; yangle
<= LOS_MAX_ANGLE
; ++yangle
)
505 if (_gcd(xangle
, yangle
) == 1)
506 xyangles
.push_back(std::pair
<int,int>(xangle
, yangle
));
509 std::sort(xyangles
.begin(), xyangles
.end(), complexity_lt
);
510 for (unsigned int i
= 0; i
< xyangles
.size(); ++i
)
512 const int xangle
= xyangles
[i
].first
;
513 const int yangle
= xyangles
[i
].second
;
515 for (int intercept
= 1; intercept
< LOS_INTERCEPT_MULT
*yangle
; ++intercept
)
517 double xstart
= ((double)intercept
) / (LOS_INTERCEPT_MULT
*yangle
);
520 _register_ray(geom::ray(xstart
, ystart
, xangle
, yangle
));
521 // also draw the identical ray in octant 2
522 _register_ray(geom::ray(ystart
, xstart
, yangle
, xangle
));
526 // Now create the appropriate blockrays array
530 static int _imbalance(ray_def ray
, const coord_def
& target
)
533 int diags
= 0, straights
= 0;
534 while (ray
.pos() != target
)
536 coord_def old
= ray
.pos();
538 die("can't advance ray");
539 switch((ray
.pos() - old
).abs())
543 if (++straights
> imb
)
552 die("ray imbalance out of range");
558 void cellray::calc_params()
560 coord_def trg
= target();
561 imbalance
= _imbalance(ray
, trg
);
562 first_diag
= ((*this)[0].abs() == 2);
565 // Find ray in positive quadrant.
566 // opc has been translated for this quadrant.
567 // XXX: Allow finding ray of minimum opacity.
568 static bool _find_ray_se(const coord_def
& target
, ray_def
& ray
,
569 const opacity_func
& opc
, const circle_def
& bds
,
572 ASSERT(target
.x
>= 0 && target
.y
>= 0 && !target
.origin());
573 if (!bds
.contains(target
))
576 ASSERT(bds_precalc
.contains(target
));
578 // Ensure the precalculations have been done.
581 const std::vector
<cellray
> &min
= min_cellrays(target
);
582 ASSERT(min
.size() > 0);
583 cellray c
= min
[0]; // XXX: const cellray &c ?
584 unsigned int index
= 0;
586 #ifdef DEBUG_DIAGNOSTICS
588 mprf(MSGCH_DIAGNOSTICS
, "cycling from %d (total %d)",
589 ray
.cycle_idx
, min
.size());
592 unsigned int start
= cycle
? ray
.cycle_idx
+ 1 : 0;
593 ASSERT(start
<= min
.size());
595 int blocked
= OPC_OPAQUE
;
596 for (unsigned int i
= start
;
597 (blocked
>= OPC_OPAQUE
) && (i
< start
+ min
.size()); i
++)
599 index
= i
% min
.size();
602 // Check all inner points.
603 for (unsigned int j
= 0; j
< c
.end
&& blocked
< OPC_OPAQUE
; j
++)
604 blocked
+= opc(c
[j
]);
606 if (blocked
>= OPC_OPAQUE
)
610 ray
.cycle_idx
= index
;
615 // Coordinate transformation so we can find_ray quadrant-by-quadrant.
616 struct opacity_trans
: public opacity_func
618 const coord_def
& source
;
620 const opacity_func
& orig
;
622 opacity_trans(const opacity_func
& opc
, const coord_def
& s
, int sx
, int sy
)
623 : source(s
), signx(sx
), signy(sy
), orig(opc
)
629 opacity_type
operator()(const coord_def
&l
) const
631 return orig(transform(l
));
634 coord_def
transform(const coord_def
&l
) const
636 return coord_def(source
.x
+ signx
*l
.x
, source
.y
+ signy
*l
.y
);
640 // Find a nonblocked ray from source to target. Return false if no
641 // such ray could be found, otherwise return true and fill ray
643 // if range is too great or all rays are blocked.
644 // If cycle is false, find the first fitting ray. If it is true,
645 // assume that ray is appropriately filled in, and look for the next
646 // ray. We only ever use ray.cycle_idx.
647 bool find_ray(const coord_def
& source
, const coord_def
& target
,
648 ray_def
& ray
, const opacity_func
& opc
, const circle_def
&bds
,
651 if (target
== source
|| !map_bounds(source
) || !map_bounds(target
))
654 const int signx
= ((target
.x
- source
.x
>= 0) ? 1 : -1);
655 const int signy
= ((target
.y
- source
.y
>= 0) ? 1 : -1);
656 const int absx
= signx
* (target
.x
- source
.x
);
657 const int absy
= signy
* (target
.y
- source
.y
);
658 const coord_def abs
= coord_def(absx
, absy
);
659 opacity_trans opc_trans
= opacity_trans(opc
, source
, signx
, signy
);
661 if (!_find_ray_se(abs
, ray
, opc_trans
, bds
, cycle
))
665 ray
.r
.start
.x
= 1.0 - ray
.r
.start
.x
;
667 ray
.r
.start
.y
= 1.0 - ray
.r
.start
.y
;
668 ray
.r
.dir
.x
*= signx
;
669 ray
.r
.dir
.y
*= signy
;
671 ray
.r
.start
.x
+= source
.x
;
672 ray
.r
.start
.y
+= source
.y
;
677 bool exists_ray(const coord_def
& source
, const coord_def
& target
,
678 const opacity_func
& opc
, const circle_def
&bds
)
681 return (find_ray(source
, target
, ray
, opc
, bds
));
684 // Assuming that target is in view of source, but line of
685 // fire is blocked, what is it blocked by?
686 dungeon_feature_type
ray_blocker(const coord_def
& source
,
687 const coord_def
& target
)
690 if (!find_ray(source
, target
, ray
, opc_default
))
692 ASSERT (you
.xray_vision
);
693 return (NUM_FEATURES
);
698 while (ray
.pos() != target
)
700 blocked
+= opc_solid(ray
.pos());
701 if (blocked
>= OPC_OPAQUE
)
702 return (env
.grid(ray
.pos()));
706 return (NUM_FEATURES
);
709 // Returns a straight ray from source to target.
710 void fallback_ray(const coord_def
& source
, const coord_def
& target
,
713 ray
.r
.start
.x
= source
.x
+ 0.5;
714 ray
.r
.start
.y
= source
.y
+ 0.5;
715 coord_def diff
= target
- source
;
716 ray
.r
.dir
.x
= diff
.x
;
717 ray
.r
.dir
.y
= diff
.y
;
718 ray
.on_corner
= false;
721 // Count the number of matching features between two points along
722 // a beam-like path; the path will pass through solid features.
723 // By default, it excludes end points from the count.
724 // If just_check is true, the function will return early once one
725 // such feature is encountered.
726 int num_feats_between(const coord_def
& source
, const coord_def
& target
,
727 dungeon_feature_type min_feat
,
728 dungeon_feature_type max_feat
,
729 bool exclude_endpoints
, bool just_check
)
733 int max_dist
= grid_distance(source
, target
);
735 ASSERT(map_bounds(source
) && map_bounds(target
));
737 if (source
== target
)
738 return (0); // XXX: might want to count the cell.
740 // We don't need to find the shortest beam, any beam will suffice.
741 fallback_ray(source
, target
, ray
);
743 if (exclude_endpoints
&& ray
.pos() == source
)
750 bool reached_target
= false;
751 while (dist
++ <= max_dist
)
753 const dungeon_feature_type feat
= grd(ray
.pos());
755 if (ray
.pos() == target
)
756 reached_target
= true;
758 if (feat
>= min_feat
&& feat
<= max_feat
759 && (!exclude_endpoints
|| !reached_target
))
763 if (just_check
) // Only needs to be > 0.
776 // Is p2 visible from p1, disregarding half-opaque objects?
777 bool cell_see_cell(const coord_def
& p1
, const coord_def
& p2
)
779 return exists_ray(p1
, p2
, opc_fullyopaque
);
782 // We use raycasting. The algorithm:
784 // Create a large bundle of rays and cast them.
785 // Mark, for each one, which cells kill it (and where.)
786 // Also, for each one, note which cells it passes.
788 // Unite the ray-killers for the given map; this tells you which rays
790 // Look up which cells the surviving rays have, and that's your LOS!
792 // WLOG, we can assume that we're in a specific quadrant - say the
793 // first quadrant - and just mirror everything after that. We can
794 // likely get away with a single octant, but we don't do that. (To
796 // Rays are actually split by each cell they pass. So each "ray" only
797 // identifies a single cell, and we can do logical ORs. Once a cell
798 // kills a cellray, it will kill all remaining cellrays of that ray.
799 // Also, rays are checked to see if they are duplicates of each
800 // other. If they are, they're eliminated.
801 // Some cellrays can also be eliminated. In general, a cellray is
802 // unnecessary if there is another cellray with the same coordinates,
803 // and whose path (up to those coordinates) is a subset, not necessarily
804 // proper, of the original path. We still store the original cellrays
805 // fully for beam detection and such.
807 // With reasonable values we have around 6000 cellrays, meaning
808 // around 600Kb (75 KB) of data. This gets cut down to 700 cellrays
809 // after removing duplicates. That means that we need to do
810 // around 22*100*4 ~ 9,000 memory reads + writes per LOS call on a
811 // 32-bit system. Not too bad.
813 // Smoke will now only block LOS after two cells of smoke. This is
814 // done by updating with a second array.
816 static void _losight_quadrant(los_grid
& sh
, const los_param
& dat
, int sx
, int sy
)
818 const unsigned int num_cellrays
= cellray_ends
.size();
823 for (quadrant_iterator qi
; qi
; qi
++)
825 coord_def p
= coord_def(sx
*(qi
->x
), sy
*(qi
->y
));
826 if (!dat
.los_bounds(p
))
829 switch (dat
.opacity(p
))
832 // Block the appropriate rays.
833 *dead_rays
|= *blockrays(*qi
);
836 // Block rays which have already seen a cloud.
837 *dead_rays
|= (*smoke_rays
& *blockrays(*qi
));
838 *smoke_rays
|= *blockrays(*qi
);
845 // Ray calculation done. Now work out which cells in this
846 // quadrant are visible.
847 for (unsigned int rayidx
= 0; rayidx
< num_cellrays
; ++rayidx
)
849 // make the cells seen by this ray at this point visible
850 if (!dead_rays
->get(rayidx
))
852 // This ray is alive, thus the end cell is visible.
853 const coord_def p
= coord_def(sx
* cellray_ends
[rayidx
].x
,
854 sy
* cellray_ends
[rayidx
].y
);
855 if (dat
.los_bounds(p
))
861 void losight(los_grid
& sh
, const los_param
& dat
)
865 // Do precomputations if necessary.
868 const int quadrant_x
[4] = { 1, -1, -1, 1 };
869 const int quadrant_y
[4] = { 1, 1, -1, -1 };
870 for (int q
= 0; q
< 4; ++q
)
871 _losight_quadrant(sh
, dat
, quadrant_x
[q
], quadrant_y
[q
]);
873 // Center is always visible.
874 const coord_def o
= coord_def(0,0);
878 struct los_param_funcs
: public los_param
881 const opacity_func
& opc
;
882 const circle_def
& bounds
;
884 los_param_funcs(const coord_def
& c
,
885 const opacity_func
& o
, const circle_def
& b
)
886 : center(c
), opc(o
), bounds(b
)
890 bool los_bounds(const coord_def
& p
) const
892 return (map_bounds(p
+ center
) && bounds
.contains(p
));
895 opacity_type
opacity(const coord_def
& p
) const
897 return (opc(p
+ center
));
901 void losight(los_grid
& sh
, const coord_def
& center
,
902 const opacity_func
& opc
, const circle_def
& bounds
)
904 losight(sh
, los_param_funcs(center
, opc
, bounds
));
907 /////////////////////////////////////
908 // A start at tracking LOS changes.
910 // Something that affects LOS (with default parameters)
911 // has changed somewhere.
912 static void _handle_los_change()
917 void los_actor_moved(const actor
* act
, const coord_def
& oldpos
)
919 if (act
->atype() == ACT_MONSTER
920 && act
->as_monster()->type
== MONS_BUSH
)
922 invalidate_los_around(oldpos
);
923 invalidate_los_around(act
->pos());
924 _handle_los_change();
928 void los_monster_died(const monster
* mon
)
930 if (mon
->type
== MONS_BUSH
)
932 invalidate_los_around(mon
->pos());
933 _handle_los_change();
937 // Might want to pass new/old terrain.
938 void los_terrain_changed(const coord_def
& p
)
940 invalidate_los_around(p
);
941 _handle_los_change();
944 // Might want to pass new/old cloud type.
945 void los_cloud_changed(const coord_def
& p
)
947 invalidate_los_around(p
);
948 _handle_los_change();
954 _handle_los_change();