home · contact · privacy
Add (crude) field of view to player's view of map.
[plomrogue] / src / server / field_of_view.c
1 /* src/server/field_of_view.c */
2
3 #define _POSIX_C_SOURCE 200809L /* strdup() */
4 #include "field_of_view.h"
5 #include <stdlib.h> /* free() */
6 #include <stdint.h> /* uint8_t, uint16_t, uint32_t */
7 #include <string.h> /* memset(), strchr(), strdup() */
8 #include "../common/rexit.h" /* exit_trouble() */
9 #include "../common/try_malloc.h" /* try_malloc() */
10 #include "map_objects.h" /* MapObj, MapObjDef, get_player() */
11 #include "yx_uint8.h" /* yx_uint8 */
12 #include "world.h" /* global world  */
13
14
15
16 /* States that cells in the fov map may be in. */
17 enum fov_cell_states {
18     VISIBLE      = 0x01,
19     HIDDEN       = 0x02,
20     SHADOW_LEFT  = 0x04,
21     SHADOW_RIGHT = 0x08,
22     LIMIT        = 0x10,
23     HIDE_LATER   = 0x20
24 };
25
26 /* Values for mv_yx_in_dir_wrap()'s wrapping directory memory. */
27 enum wraps
28 {
29     WRAP_N = 0x01,
30     WRAP_S = 0x02,
31     WRAP_E = 0x04,
32     WRAP_W = 0x08
33 };
34
35 /* Transform "yx" to an index position in the world map. */
36 static uint16_t yx_to_pos(struct yx_uint8 * yx);
37
38 /* Move "yx" into hex direction "d". If this moves "yx" beyond the minimal (0)
39  * or maximal (UINT8_MAX) column or row, it wraps to the opposite side. Such
40  * wrapping is returned as a wraps enum value and stored, so that further calls
41  * to move "yx" back into the opposite direction may unwrap it again. Pass an
42  * "unwrap" of UNWRAP to re-set the internal wrap memory to 0.
43  */
44 static uint8_t mv_yx_in_dir_wrap(char d, struct yx_uint8 * yx, uint8_t unwrap);
45
46 /* Wrapper to "mv_yx_in_dir_wrap()", returns 1 if the wrapped function moved
47  * "yx" within the wrap borders and the map size, else 0.
48  */
49 extern uint8_t mv_yx_in_dir_legal(char dir, struct yx_uint8 * yx);
50
51 /* Return one by one hex dir characters of walking through a circle of "radius".
52  * The circle is initialized by passing a "new_circle" of 1 and the "radius"
53  * and only returns non-null hex direction characters if "new_circle" is 0.
54  */
55 static char next_circle_dir(uint8_t new_circle, uint8_t radius_new);
56
57 /* Draw circle of hexes flagged LIMIT "radius" away from "yx" to "fov_map". */
58 extern void draw_border_circle(struct yx_uint8 yx, uint8_t radius,
59                                uint8_t * fov_map);
60
61 /* eye_to_cell_dir_ratio() helper. */
62 static void geometry_to_char_ratio(uint8_t * n1, uint8_t * n2, uint8_t indent,
63                                    int16_t diff_y, int16_t diff_x,
64                                    uint8_t vertical, uint8_t variant);
65
66 /* From the chars in "available_dirs" and the geometry described by the other
67  * parameters return a string of hex direction characters representing the
68  * approximation of a straight line. "variant" marks the direction as either in
69  * the northern, north-eastern or south-western hex neighborhood if 1, or the
70  * others if 0.
71  */
72 static char * eye_to_cell_dir_ratio(char * available_dirs, uint8_t indent,
73                                     int16_t diff_y, int16_t diff_x,
74                                     uint8_t vertical, uint8_t variant,
75                                     uint8_t shift_right);
76
77 /* Return string approximating in one or two hex direction chars the direction
78  * that a "diff_y" and "diff_x" lead to in the internal half-indented 2D
79  * encoding of hexagonal maps, with "indent" the movement's start indentation.
80  */
81 static char * dir_from_delta(uint8_t indent, int16_t diff_y, int16_t diff_x);
82
83 /* Return string of hex movement direction characters describing the best
84  * possible hex approximation to a straight line from "yx_eye" to "yx_cell". If
85  * "right" is set and the string is of length two, return it with the direction
86  * strings scarcer character appearing first.
87  */
88 static char * eye_to_cell(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_cell,
89                           uint8_t right);
90
91 /* Return string of hex movement direction characters describing the best
92  * possible hex approximation to a straight line from "yx_eye" to "yx_cell". If
93  * "right" is set and the string is of length two, return it with the direction
94  * strings scarcer character appearing first.
95  */
96 static char * eye_to_cell(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_cell,
97                           uint8_t right);
98
99 /* fill_shadow() helper, determining if map's top left cell starts a shadow. */
100 static uint8_t is_top_left_shaded(uint16_t pos_a, uint16_t pos_b,
101                                   int16_t a_y_on_left);
102
103 /* Flag as HIDDEN all cells in "fov_map" that are enclosed by 1) the map's
104  * borders or cells flagged LIMIT and 2) the shadow arms of cells flagged
105  * SHADOW_LEFT and SHADOW_RIGHT extending from "yx_cell", as seen as left and
106  * right as seen from "yx_eye". "pos_a" and "pos_b" store the terminal positions
107  * of these arms in "fov_map" ("pos_a" for the left, "pos_b" for the right one).
108  */
109 static void fill_shadow(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_cell,
110                         uint8_t * fov_map, uint16_t pos_a, uint16_t pos_b);
111
112 /* Flag with "flag" cells of a path from "yx_start" to the end of the map or (if
113  * closer) the view border circle of the cells flagged as LIMIT, in a direction
114  * parallel to the one determined by walking a path from "yx_eye" to the cell
115  * reachable by moving one step into "dir" from "yx_start". If "shift_right" is
116  * set, choose among the possible paths the one whose starting cell is set most
117  * to the right, else do the opposite.
118  */
119 static uint16_t shadow_arm(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_start,
120                            uint8_t * fov_map, char dir, uint8_t flag,
121                            uint8_t shift_right);
122
123 /* From "yx_start", draw shadow of what is invisible as seen from "yx_eye" into
124  * "fov_map" by extending shadow arms from "yx_start" as shadow borders until
125  * the edges of the map or, if smaller, the maximum viewing distance, flag these
126  * shadow arms' cells as HIDE_LATER and the area enclosed by them as HIDDEN.
127  * "dir_left" and "dir_right" are hex directions to move to from "yx_start" for
128  * cells whose shortest straight path to "yx_eye" serve as the lines of sight
129  * enclosing the shadow left and right (left and right as seen from "yx_eye").
130  */
131 static void shadow(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_start,
132                    uint8_t * fov_map, char dir_left, char dir_right);
133
134 /* In "fov_map", if cell of position "yx_cell" is not HIDDEN, set it as VISIBLE,
135  * and if an obstacle to view is positioned there in the game map, flag cells
136  *behind it, unseen from "yx_eye", as HIDDEN on the interior and HIDE_LATER on
137  * their borders.
138  *
139  * The shape and width of shadows is determined by 1) calculating an approximate
140  * direction of "yx_cell" as seen from "yx_eye" as one hex movement direction,
141  * or two directly neighboring each other (i.e. "east", "east and north-east"),
142  * 2) deriving the two hex movement directions clockwise immediately preceding
143  * the first (or only) direction and immediately succeeding the second (or only)
144  * one and 3) passing the two directions thus gained as shadow arm direction
145  * calibration values to shadow() (after this function's other arguments).
146  */
147 static void set_view_of_cell_and_shadows(struct yx_uint8 * yx_cell,
148                                          struct yx_uint8 * yx_eye,
149                                          uint8_t * fov_map);
150
151 /* Return overlay of world map wherein all cell positions visible from player's
152  * positions have flag VISIBLE set.
153  *
154  * This is achieved by spiraling out clock-wise from the player position,
155  * flagging cells as VISIBLE unless they're already marked as HIDDEN, and, on
156  * running into obstacles for view that are not HIDDEN, casting shadows from
157  * these, i.e. drawing cells as HIDDEN that would be hidden by said obstacle,
158  * before continuing the original spiraling path.
159  *
160  * Shadowcasting during spiraling is initially lazy, flagging only the shadows'
161  * interior cells as HIDDEN and their border cells as HIDE_LATER. Only at the
162  * end are all cells flagged HIDE_LATER flagged as HIDDEN. This is to handle
163  * cases where obstacles to view sit right at the border of pre-estabilshed
164  * shadows, therefore might be ignored if HIDDEN and not cast shadows on their
165  * own that may slightly extend beyond the pre-established shadows they border.
166  */
167 static uint8_t * build_fov_map();
168
169
170
171 static uint16_t yx_to_pos(struct yx_uint8 * yx)
172 {
173     return (yx->y * world.map.size.x) + yx->x;
174 }
175
176
177
178 static uint8_t mv_yx_in_dir_wrap(char d, struct yx_uint8 * yx, uint8_t unwrap)
179 {
180     static uint8_t wrap = 0;
181     if (unwrap)
182     {
183         wrap = 0;
184         return 0;
185     }
186     struct yx_uint8 original;
187     original.y = yx->y;
188     original.x = yx->x;
189     if     (d == 'e')
190     {
191         yx->x = yx->x + (yx->y % 2);
192         yx->y--;
193     }
194     else if (d == 'd')
195     {
196         yx->x++;
197     }
198     else if (d == 'c')
199     {
200         yx->x = yx->x + (yx->y % 2);
201         yx->y++;
202     }
203     else if (d == 'x')
204     {
205         yx->x = yx->x - !(yx->y % 2);
206         yx->y++;
207     }
208     else if (d == 's')
209     {
210         yx->x--;
211     }
212     else if (d == 'w')
213     {
214         yx->x = yx->x - !(yx->y % 2);
215         yx->y--;
216     }
217     else
218     {
219         exit_trouble(1, "mv_yx_in_dir_wrap()", "illegal direction");
220     }
221     if      (strchr("edc", d) && yx->x < original.x)
222     {
223         wrap = wrap & WRAP_W ? wrap ^ WRAP_W : wrap | WRAP_E;
224     }
225     else if (strchr("xsw", d) && yx->x > original.x)
226     {
227         wrap = wrap & WRAP_E ? wrap ^ WRAP_E : wrap | WRAP_W;
228     }
229     if      (strchr("we", d) && yx->y > original.y)
230     {
231         wrap = wrap & WRAP_S ? wrap ^ WRAP_S : wrap | WRAP_N;
232     }
233     else if (strchr("xc", d) && yx->y < original.y)
234     {
235         wrap = wrap & WRAP_N ? wrap ^ WRAP_N : wrap | WRAP_S;
236     }
237     return wrap;
238 }
239
240
241
242 extern uint8_t mv_yx_in_dir_legal(char dir, struct yx_uint8 * yx)
243 {
244     uint8_t wraptest = mv_yx_in_dir_wrap(dir, yx, 0);
245     if (!wraptest && yx->x < world.map.size.x && yx->y < world.map.size.y)
246     {
247         return 1;
248     }
249     return 0;
250 }
251
252
253
254 static char next_circle_dir(uint8_t new_circle, uint8_t radius_new)
255 {
256     static uint8_t i_dirs = 0;
257     static uint8_t i_dist = 0;
258     static uint8_t radius = 0;
259     char * dirs = "dcxswe";
260     if (new_circle)
261     {
262         i_dirs = 0;
263         i_dist = 0;
264         radius = radius_new;
265         return '\0';
266     }
267     char ret_dir = dirs[i_dirs];
268     i_dist++;
269     if (i_dist == radius)
270     {
271         i_dist = 0;
272         i_dirs++;
273     }
274     return ret_dir;
275 }
276
277
278
279 extern void draw_border_circle(struct yx_uint8 yx, uint8_t radius,
280                                uint8_t * fov_map)
281 {
282     uint8_t dist;
283     for (dist = 1; dist <= radius; dist++)
284     {
285         mv_yx_in_dir_wrap('w', &yx, 0);
286     }
287     next_circle_dir(1, radius);
288     char dir;
289     while ('\0' != (dir = next_circle_dir(0, 0)))
290     {
291          if (mv_yx_in_dir_legal(dir, &yx))
292          {
293             uint16_t pos = yx_to_pos(&yx);
294             fov_map[pos] = LIMIT;
295         }
296     }
297     mv_yx_in_dir_wrap(0, NULL, 1);
298 }
299
300
301
302 static void geometry_to_char_ratio(uint8_t * n1, uint8_t * n2, uint8_t indent,
303                                    int16_t diff_y, int16_t diff_x,
304                                    uint8_t vertical, uint8_t variant)
305 {
306     if      (vertical)
307     {
308         *n1 = (diff_y / 2) - diff_x + ( indent * (diff_y % 2));
309         *n2 = (diff_y / 2) + diff_x + (!indent * (diff_y % 2));
310     }
311     else if (!vertical)
312     {
313         *n1 = diff_y;
314         *n2 = diff_x - (diff_y / 2) - (indent * (diff_y % 2));
315     }
316     if (!variant)
317     {
318         uint8_t tmp = *n1;
319         *n1 = *n2;
320         *n2 = tmp;
321     }
322 }
323
324
325
326 static char * eye_to_cell_dir_ratio(char * available_dirs, uint8_t indent,
327                                     int16_t diff_y, int16_t diff_x,
328                                     uint8_t vertical, uint8_t variant,
329                                     uint8_t shift_right)
330 {
331     char * f_name = "eye_to_cell_dir_ratio()";
332     uint8_t n1, n2;
333     geometry_to_char_ratio(&n1, &n2, indent, diff_y, diff_x, vertical, variant);
334     uint8_t size_chars = n1 + n2;
335     char * dirs = try_malloc(size_chars + 1, f_name);
336     uint8_t n_strong_char = n1 / n2;
337     uint8_t more_char1 = 0 < n_strong_char;
338     n_strong_char = !more_char1 ? (n2 / n1) : n_strong_char;
339     uint16_t i, i_alter;
340     uint8_t i_of_char = shift_right;
341     for (i = 0, i_alter = 0; i < size_chars; i++)
342     {
343         char dirchar = available_dirs[i_of_char];
344         if (more_char1 != i_of_char)
345         {
346             i_alter++;
347             if (i_alter == n_strong_char)
348             {
349                 i_alter = 0;
350                 i_of_char = !i_of_char;
351             }
352         }
353         else
354         {
355             i_of_char = !i_of_char;
356         }
357
358         dirs[i] = dirchar;
359     }
360     dirs[i] = '\0';
361     return dirs;
362 }
363
364
365
366 static char * dir_from_delta(uint8_t indent, int16_t diff_y, int16_t diff_x)
367 {
368     int16_t double_x = 2 * diff_x;
369     int16_t indent_corrected_double_x_pos =  double_x - indent  + !indent;
370     int16_t indent_corrected_double_x_neg = -double_x - !indent +  indent;
371     if (diff_y > 0)
372     {
373         if (diff_y ==  double_x || diff_y == indent_corrected_double_x_pos)
374         {
375             return "c";
376         }
377         if (diff_y == -double_x || diff_y == indent_corrected_double_x_neg)
378         {
379             return "x";
380         }
381         if (diff_y  <  double_x || diff_y  < indent_corrected_double_x_pos)
382         {
383             return "dc";
384         }
385         if (diff_y  < -double_x || diff_y  < indent_corrected_double_x_neg)
386         {
387             return "xs";
388         }
389         return "cx";
390     }
391     if (diff_y < 0)
392     {
393         if (diff_y ==  double_x || diff_y == indent_corrected_double_x_pos)
394         {
395             return "w";
396         }
397         if (diff_y == -double_x || diff_y == indent_corrected_double_x_neg)
398         {
399             return "e";
400         }
401         if (diff_y  >  double_x || diff_y  > indent_corrected_double_x_pos)
402         {
403             return "sw";
404         }
405         if (diff_y  > -double_x || diff_y  > indent_corrected_double_x_neg)
406         {
407             return "ed";
408         }
409         return "we";
410     }
411     return 0 > diff_x ? "s" : "d";
412 }
413
414
415
416 static char * eye_to_cell(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_cell,
417                           uint8_t right)
418 {
419     int16_t diff_y = yx_cell->y - yx_eye->y;
420     int16_t diff_x = yx_cell->x - yx_eye->x;
421     uint8_t indent = yx_eye->y % 2;
422     char * dir = dir_from_delta(indent, diff_y, diff_x);
423     char * dirs;
424     if (1 == strlen(dir))
425     {
426         return strdup(dir);
427     }
428     else if (!strcmp(dir, "dc"))
429     {
430         dirs = eye_to_cell_dir_ratio(dir, indent,  diff_y,  diff_x,  0,0,right);
431     }
432     else if (!strcmp(dir, "xs"))
433     {
434         dirs = eye_to_cell_dir_ratio(dir, !indent,  diff_y, -diff_x, 0,1,right);
435     }
436     else if (!strcmp(dir, "cx"))
437     {
438         dirs = eye_to_cell_dir_ratio(dir, indent,  diff_y,  diff_x,  1,0,right);
439     }
440     else if (!strcmp(dir, "sw"))
441     {
442         dirs = eye_to_cell_dir_ratio(dir, !indent, -diff_y, -diff_x, 0,0,right);
443     }
444     else if (!strcmp(dir, "ed"))
445     {
446         dirs = eye_to_cell_dir_ratio(dir, indent, -diff_y,  diff_x, 0,1,right);
447     }
448     else if (!strcmp(dir, "we"))
449     {
450         dirs = eye_to_cell_dir_ratio(dir, indent, -diff_y,  diff_x, 1,1,right);
451     }
452     return dirs;
453 }
454
455
456
457 static uint8_t is_top_left_shaded(uint16_t pos_a, uint16_t pos_b,
458                                   int16_t a_y_on_left)
459 {
460     uint16_t start_last_row = world.map.size.x * (world.map.size.y - 1);
461     uint8_t a_on_left_or_bottom =    0 <= a_y_on_left
462                                   || (pos_a >= start_last_row);
463     uint8_t b_on_top_or_right =    pos_b < world.map.size.x
464                                 || pos_b % world.map.size.x==world.map.size.x-1;
465     return pos_a != pos_b && b_on_top_or_right && a_on_left_or_bottom;
466 }
467
468
469
470 static void fill_shadow(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_cell,
471                         uint8_t * fov_map, uint16_t pos_a, uint16_t pos_b)
472 {
473     int16_t a_y_on_left = !(pos_a%world.map.size.x)? pos_a/world.map.size.x :-1;
474     int16_t b_y_on_left = !(pos_b%world.map.size.x)? pos_b/world.map.size.x :-1;
475     uint8_t top_left_shaded = is_top_left_shaded(pos_a, pos_b, a_y_on_left);
476     uint16_t pos;
477     uint8_t y, x, in_shade;
478     for (y = 0; y < world.map.size.y; y++)
479     {
480         in_shade =    (top_left_shaded || (b_y_on_left >= 0 && y > b_y_on_left))
481                    && (a_y_on_left < 0 || y < a_y_on_left);
482         for (x = 0; x < world.map.size.x; x++)
483         {
484             pos = (y * world.map.size.x) + x;
485             if (yx_eye->y == yx_cell->y && yx_eye->x < yx_cell->x)
486             {
487                 uint8_t val = fov_map[pos] & (SHADOW_LEFT | SHADOW_RIGHT);
488                 in_shade = 0 < val ? 1 : in_shade;
489             }
490             else if (yx_eye->y == yx_cell->y && yx_eye->x > yx_cell->x)
491             {
492                 uint8_t val = fov_map[pos] & (SHADOW_LEFT | SHADOW_RIGHT);
493                 in_shade = 0 < val ? 0 : in_shade;
494             }
495             else if (yx_eye->y > yx_cell->y && y <= yx_cell->y)
496             {
497                 in_shade = 0 < (fov_map[pos] & SHADOW_LEFT) ? 1 : in_shade;
498                 in_shade = (fov_map[pos] & SHADOW_RIGHT) ? 0 : in_shade;
499             }
500             else if (yx_eye->y < yx_cell->y && y >= yx_cell->y)
501             {
502                 in_shade = 0 < (fov_map[pos] & SHADOW_RIGHT) ? 1 : in_shade;
503                 in_shade = (fov_map[pos] & SHADOW_LEFT) ? 0 : in_shade;
504             }
505             if (!(fov_map[pos] & (SHADOW_LEFT | SHADOW_RIGHT))
506                 && in_shade)
507             {
508                 fov_map[pos] = fov_map[pos] | HIDDEN;
509             }
510         }
511     }
512 }
513
514
515
516 static uint16_t shadow_arm(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_start,
517                            uint8_t * fov_map, char dir, uint8_t flag,
518                            uint8_t shift_right)
519 {
520     struct yx_uint8 yx_border = *yx_start;
521     uint16_t pos;
522     if (mv_yx_in_dir_legal(dir, &yx_border))
523     {
524         uint8_t met_limit = 0;
525         uint8_t i_dirs = 0;
526         char * dirs = eye_to_cell(yx_eye, &yx_border, shift_right);
527         yx_border = *yx_start;
528         while (!met_limit && mv_yx_in_dir_legal(dirs[i_dirs], &yx_border))
529         {
530             pos = yx_to_pos(&yx_border);
531             met_limit = fov_map[pos] & LIMIT;
532             fov_map[pos] = fov_map[pos] | flag;
533             i_dirs = dirs[i_dirs + 1] ? i_dirs + 1 : 0;
534         }
535         free(dirs);
536     }
537     mv_yx_in_dir_wrap(0, NULL, 1);
538     return pos;
539 }
540
541
542
543 static void shadow(struct yx_uint8 * yx_eye, struct yx_uint8 * yx_start,
544                    uint8_t * fov_map, char dir_left, char dir_right)
545 {
546     uint16_t pos_a, pos_b, pos_start, i;
547     pos_a = shadow_arm(yx_eye, yx_start, fov_map, dir_left, SHADOW_LEFT, 0);
548     pos_b = shadow_arm(yx_eye, yx_start, fov_map, dir_right, SHADOW_RIGHT, 1);
549     pos_start = yx_to_pos(yx_start);
550     fov_map[pos_start] = fov_map[pos_start] | SHADOW_LEFT | SHADOW_RIGHT;
551     fill_shadow(yx_eye, yx_start, fov_map, pos_a, pos_b);
552     for (i = 0; i < world.map.size.y * world.map.size.x; i++)
553     {
554         if (fov_map[i] & (SHADOW_LEFT | SHADOW_RIGHT) && i != pos_start)
555         {
556             fov_map[i] = fov_map[i] | HIDE_LATER;
557         }
558         fov_map[i] = fov_map[i] ^ (fov_map[i] & SHADOW_LEFT);
559         fov_map[i] = fov_map[i] ^ (fov_map[i] & SHADOW_RIGHT);
560     }
561     return;
562 }
563
564
565
566 static void set_view_of_cell_and_shadows(struct yx_uint8 * yx_cell,
567                                          struct yx_uint8 * yx_eye,
568                                          uint8_t * fov_map)
569 {
570     char * dirs = "dcxswe";
571     uint16_t pos = yx_to_pos(yx_cell);
572     if (!(fov_map[pos] & HIDDEN))
573     {
574         fov_map[pos] = fov_map[pos] | VISIBLE;
575         if ('X' == world.map.cells[pos])
576         {
577             uint8_t last_pos = strlen(dirs) - 1;
578             int16_t diff_y = yx_cell->y - yx_eye->y;
579             int16_t diff_x = yx_cell->x - yx_eye->x;
580             uint8_t indent = yx_eye->y % 2;
581             char * dir = dir_from_delta(indent, diff_y, diff_x);
582             uint8_t start_pos = strchr(dirs, dir[0]) - dirs;
583             char prev = start_pos > 0 ? dirs[start_pos - 1] : dirs[last_pos];
584             char next = start_pos < last_pos ? dirs[start_pos + 1] : dirs[0];
585             if (dir[1])
586             {
587                 uint8_t end_pos = strchr(dirs, dir[1]) - dirs;
588                 next = end_pos < last_pos ? dirs[end_pos + 1] : dirs[0];
589             }
590             shadow(yx_eye, yx_cell, fov_map, prev, next);
591         }
592     }
593 }
594
595
596
597 static uint8_t * build_fov_map()
598 {
599     char * f_name = "build_fov_map()";
600     uint8_t radius = 2 * world.map.size.y;
601     uint32_t map_size = world.map.size.y * world.map.size.x;
602     struct MapObj * player = get_player();
603     struct yx_uint8 yx = player->pos;
604     uint8_t * fov_map = try_malloc(map_size, f_name);
605     memset(fov_map, 0, map_size);
606     draw_border_circle(yx, radius, fov_map);
607     fov_map[yx_to_pos(&yx)] = VISIBLE;
608     uint8_t dist;
609     for (dist = 1; dist <= radius; dist++)
610     {
611         uint8_t first_round = 1;
612         char dir;
613         next_circle_dir(1, dist);
614         while ('\0' != (dir = next_circle_dir(0, 0)))
615         {
616             char i_dir = first_round ? 'e' : dir;
617             first_round = 0;
618             if (mv_yx_in_dir_legal(i_dir, &yx))
619             {
620                 set_view_of_cell_and_shadows(&yx, &player->pos, fov_map);
621             }
622         }
623     }
624     uint16_t i;
625     for (i = 0; i < world.map.size.y * world.map.size.x; i++)
626     {
627         if (fov_map[i] & HIDE_LATER)
628         {
629               fov_map[i] = fov_map[i] ^ (fov_map[i] & VISIBLE);
630         }
631     }
632     return fov_map;
633 }
634
635
636
637 extern char * build_visible_map()
638 {
639     char * f_name = "build_visible_map()";
640     uint8_t * fov_map = build_fov_map();
641     uint32_t map_size = world.map.size.y * world.map.size.x;
642     char * visible_map = try_malloc(map_size, f_name);
643     memset(visible_map, ' ', map_size);
644     uint16_t pos_i;
645     for (pos_i = 0; pos_i < map_size; pos_i++)
646     {
647         if (fov_map[pos_i] & VISIBLE)
648         {
649             visible_map[pos_i] = world.map.cells[pos_i];
650         }
651     }
652     struct MapObj * o;
653     struct MapObjDef * d;
654     char c;
655     uint8_t i;
656     for (i = 0; i < 2; i++)
657     {
658         for (o = world.map_objs; o != 0; o = o->next)
659         {
660             if (   fov_map[yx_to_pos(&o->pos)] & VISIBLE
661                 && (   (0 == i && 0 == o->lifepoints)
662                     || (1 == i && 0 < o->lifepoints)))
663             {
664                 d = get_map_object_def(o->type);
665                 c = d->char_on_map;
666                 visible_map[yx_to_pos(&o->pos)] = c;
667             }
668         }
669     }
670     free(fov_map);
671     return visible_map;
672 }