Apply the new ground_level method.
[crawl.git] / crawl-ref / source / los.cc
blob382f543951711bbb464593c3c6d4f4fd8bcccace
1 /*
2 * File: los.cc
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
12 * other.
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).
18 * == Terminology ==
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.
29 * See ray.cc.
31 * == Overview ==
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.
43 #include "AppHdr.h"
45 #include "los.h"
47 #include <cmath>
48 #include <algorithm>
50 #include "areas.h"
51 #include "bitary.h"
52 #include "coord.h"
53 #include "coord-circle.h"
54 #include "coordit.h"
55 #include "debug.h"
56 #include "externs.h"
57 #include "geom2d.h"
58 #include "losglobal.h"
59 #include "losparam.h"
60 #include "player.h"
61 #include "ray.h"
62 #include "stuff.h"
63 #include "env.h"
64 #include "terrain.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.
83 struct los_ray;
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.
99 struct cellray;
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
110 public:
111 quadrant_iterator()
112 : rectangle_iterator(coord_def(0,0),
113 coord_def(LOS_MAX_RANGE, LOS_MAX_RANGE))
118 void clear_rays_on_exit()
120 delete dead_rays;
121 delete smoke_rays;
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;
135 invalidate_los();
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].
154 unsigned int start;
155 unsigned int length;
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;
170 coord_def c;
171 coord_def old;
172 int cellnum;
173 for (cellnum = 0; true; ++cellnum)
175 old = c;
176 if (!copy.advance())
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);
182 //#endif
183 cs.clear();
184 break;
186 c = copy.pos();
187 if (!bds_precalc.contains(c))
188 break;
189 cs.push_back(c);
190 ASSERT((c - old).rdist() == 1);
192 return cs;
195 coord_def operator[](unsigned int i)
197 ASSERT(i < length);
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())
206 return false;
207 for (unsigned int i = 0; i < ray.length; i++)
208 if (ray[i] != newray[i])
209 return false;
210 return true;
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))
218 return true;
219 return false;
222 // A cellray given by fullray and index of end-point.
223 struct cellray
225 // A cellray passes through cells ray_coords[ray.start..ray.start+end].
226 los_ray ray;
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); }
237 // The end-point.
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)
243 ASSERT(i <= end);
244 return ray_coords[ray.start+i];
247 // Parameters used in find_ray. These need to be calculated
248 // only for the minimal cellrays.
249 int imbalance;
250 bool first_diag;
252 void calc_params();
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)
266 return (true);
267 else if (a.imbalance > b.imbalance)
268 return (false);
269 else
270 return (a.first_diag && !b.first_diag);
273 enum compare_type
275 C_SUBRAY,
276 C_SUPERRAY,
277 C_NEITHER,
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())
285 return C_NEITHER;
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)
300 maybe_super = false;
301 curb++;
303 if (pa.x < pb.x || pa.y < pb.y)
305 maybe_sub = false;
306 cura++;
308 if (pa == pb)
310 cura++;
311 curb++;
314 maybe_sub = maybe_sub && (cura == enda);
315 maybe_super = maybe_super && (curb == endb);
317 if (maybe_sub)
318 return C_SUBRAY; // includes equality
319 else if (maybe_super)
320 return C_SUPERRAY;
321 else
322 return C_NEITHER;
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?
339 bool dup = false;
340 cellray c(ray, i);
341 std::list<cellray>& min = minima(c.target());
343 bool erased = false;
344 for (min_it = min.begin();
345 min_it != min.end() && !dup;)
347 switch(_compare_cellrays(*min_it, c))
349 case C_SUBRAY:
350 dup = true;
351 break;
352 case C_SUPERRAY:
353 min_it = min.erase(min_it);
354 erased = true;
355 // clear this should be added, but might have
356 // to erase more
357 break;
358 case C_NEITHER:
359 default:
360 break;
362 if (!erased)
363 min_it++;
364 else
365 erased = false;
367 if (!dup)
368 min.push_back(c);
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());
385 return result;
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))
395 return;
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);
455 #endif
458 static int _gcd(int x, int y)
460 int tmp;
461 while (y != 0)
463 x %= y;
464 tmp = x;
465 x = y;
466 y = tmp;
468 return x;
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;
477 // Cast all rays
478 static void raycast()
480 static bool done_raycast = false;
481 if (done_raycast)
482 return;
484 // Creating all rays for first quadrant
485 // We have a considerable amount of overkill.
486 done_raycast = true;
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);
518 double ystart = 0.5;
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
527 _create_blockrays();
530 static int _imbalance(ray_def ray, const coord_def& target)
532 int imb = 0;
533 int diags = 0, straights = 0;
534 while (ray.pos() != target)
536 coord_def old = ray.pos();
537 if (!ray.advance())
538 die("can't advance ray");
539 switch((ray.pos() - old).abs())
541 case 1:
542 diags = 0;
543 if (++straights > imb)
544 imb = straights;
545 break;
546 case 2:
547 straights = 0;
548 if (++diags > imb)
549 imb = diags;
550 break;
551 default:
552 die("ray imbalance out of range");
555 return imb;
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,
570 bool cycle)
572 ASSERT(target.x >= 0 && target.y >= 0 && !target.origin());
573 if (!bds.contains(target))
574 return false;
576 ASSERT(bds_precalc.contains(target));
578 // Ensure the precalculations have been done.
579 raycast();
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
587 if (cycle)
588 mprf(MSGCH_DIAGNOSTICS, "cycling from %d (total %d)",
589 ray.cycle_idx, min.size());
590 #endif
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();
600 c = min[index];
601 blocked = OPC_CLEAR;
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)
607 return (false);
609 ray = c.ray;
610 ray.cycle_idx = index;
612 return (true);
615 // Coordinate transformation so we can find_ray quadrant-by-quadrant.
616 struct opacity_trans : public opacity_func
618 const coord_def& source;
619 int signx, signy;
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)
627 CLONE(opacity_trans)
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
642 // appropriately.
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,
649 bool cycle)
651 if (target == source || !map_bounds(source) || !map_bounds(target))
652 return false;
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))
662 return (false);
664 if (signx < 0)
665 ray.r.start.x = 1.0 - ray.r.start.x;
666 if (signy < 0)
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;
674 return (true);
677 bool exists_ray(const coord_def& source, const coord_def& target,
678 const opacity_func& opc, const circle_def &bds)
680 ray_def ray;
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)
689 ray_def ray;
690 if (!find_ray(source, target, ray, opc_default))
692 ASSERT (you.xray_vision);
693 return (NUM_FEATURES);
696 ray.advance();
697 int blocked = 0;
698 while (ray.pos() != target)
700 blocked += opc_solid(ray.pos());
701 if (blocked >= OPC_OPAQUE)
702 return (env.grid(ray.pos()));
703 ray.advance();
705 ASSERT (false);
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,
711 ray_def& ray)
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)
731 ray_def ray;
732 int count = 0;
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)
745 ray.advance();
746 max_dist--;
749 int dist = 0;
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))
761 count++;
763 if (just_check) // Only needs to be > 0.
764 return (count);
767 if (reached_target)
768 break;
770 ray.advance();
773 return (count);
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:
783 // PRECOMPUTATION:
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.
787 // ACTUAL LOS:
788 // Unite the ray-killers for the given map; this tells you which rays
789 // are dead.
790 // Look up which cells the surviving rays have, and that's your LOS!
791 // OPTIMIZATIONS:
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
795 // do...)
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.
806 // PERFORMANCE:
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.
812 // IMPROVEMENTS:
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();
820 dead_rays->reset();
821 smoke_rays->reset();
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))
827 continue;
829 switch (dat.opacity(p))
831 case OPC_OPAQUE:
832 // Block the appropriate rays.
833 *dead_rays |= *blockrays(*qi);
834 break;
835 case OPC_HALF:
836 // Block rays which have already seen a cloud.
837 *dead_rays |= (*smoke_rays & *blockrays(*qi));
838 *smoke_rays |= *blockrays(*qi);
839 break;
840 default:
841 break;
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))
856 sh(p) = true;
861 void losight(los_grid& sh, const los_param& dat)
863 sh.init(false);
865 // Do precomputations if necessary.
866 raycast();
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);
875 sh(o) = true;
878 struct los_param_funcs : public los_param
880 coord_def center;
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()
914 invalidate_agrid();
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();
951 void los_changed()
953 invalidate_los();
954 _handle_los_change();