1 /*
2  * map.c
3  * Copyright (C) 2009-2020 Joachim de Groot <jdegroot@web.de>
4  *
5  * NLarn is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * NLarn is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <glib.h>
20 #include <stdlib.h>
21 
22 #include "container.h"
23 #include "display.h"
24 #include "items.h"
25 #include "map.h"
26 #include "extdefs.h"
27 #include "random.h"
28 #include "sobjects.h"
29 #include "spheres.h"
30 
31 static int map_fill_with_stationary_objects(map *maze);
32 static void map_fill_with_objects(map *m);
33 static void map_fill_with_traps(map *m);
34 
35 static gboolean map_load_from_file(map *m, const char *mazefile, guint which);
36 static void map_make_maze(map *m, int treasure_room);
37 static void map_make_maze_eat(map *m, int x, int y);
38 static void map_make_river(map *m, map_tile_t rivertype);
39 static void map_make_lake(map *m, map_tile_t laketype);
40 static void map_make_treasure_room(map *m, rectangle **rooms);
41 static int map_validate(map *m);
42 
map_sphere_destroy(sphere * s,map * m)43 static inline void map_sphere_destroy(sphere *s, map *m __attribute__((unused)))
44 {
45     sphere_destroy(s, nlarn);
46 }
47 
48 const map_tile_data map_tiles[LT_MAX] =
49 {
50     /* type         gly  color          desc           pa tr */
51     { LT_NONE,      ' ', COLOURLESS,    NULL,          0, 0 },
52     { LT_MOUNTAIN,  '^', LIGHTGRAY,  "a mountain",  0, 0 },
53     { LT_GRASS,     '"', LIGHTGREEN, "grass",       1, 1 },
54     { LT_DIRT,      '.', BROWN,      "dirt",        1, 1 },
55     { LT_TREE,      '&', GREEN,      "a tree",      0, 0 },
56     { LT_FLOOR,     '.', LIGHTGRAY,  "floor",       1, 1 },
57     { LT_WATER,     '~', LIGHTBLUE,  "water",       1, 1 },
58     { LT_DEEPWATER, '~', BLUE,       "deep water",  0, 1 },
59     { LT_LAVA,      '~', RED,        "lava",        0, 1 },
60     { LT_FIRE,      '*', RED,        "fire",        1, 1 },
61     { LT_CLOUD,     '*', WHITE,      "a gas cloud", 1, 1 },
62     { LT_WALL,      '#', LIGHTGRAY,  "a wall",      0, 0 },
63 };
64 
65 /* keep track which levels have been used before */
66 static int map_used[MAP_MAZE_NUM + 1] = { 1, 0 };
67 
68 const char *map_names[MAP_MAX] =
69 {
70     "Town",
71     "D1",
72     "D2",
73     "D3",
74     "D4",
75     "D5",
76     "D6",
77     "D7",
78     "D8",
79     "D9",
80     "D10",
81     "V1",
82     "V2",
83     "V3"
84 };
85 
is_town(int nlevel)86 static gboolean is_town(int nlevel)
87 {
88     return (nlevel == 0);
89 }
90 
is_caverns_bottom(int nlevel)91 static gboolean is_caverns_bottom(int nlevel)
92 {
93     return (nlevel == MAP_CMAX - 1);
94 }
95 
is_volcano_bottom(int nlevel)96 static gboolean is_volcano_bottom(int nlevel)
97 {
98     return (nlevel == MAP_MAX - 1);
99 }
100 
is_volcano_map(int nlevel)101 static gboolean is_volcano_map(int nlevel)
102 {
103     return (nlevel >= MAP_CMAX);
104 }
105 
map_new(int num,const char * mazefile)106 map *map_new(int num, const char *mazefile)
107 {
108     gboolean map_loaded = FALSE;
109 
110     map *nmap = nlarn->maps[num] = g_malloc0(sizeof(map));
111     nmap->nlevel = num;
112 
113     /* create map */
114     if ((num == 0) /* town is stored in file */
115             || is_caverns_bottom(num) /* level 10 */
116             || is_volcano_bottom(num) /* volcano level 3 */
117             || (num > 1 && chance(25)))
118     {
119         /* read maze from data file */
120         map_loaded = map_load_from_file(nmap, mazefile, (num == 0) ? 0 : -1);
121 
122         /* add stationary objects (not to the town) */
123         if (num > 0)
124         {
125             if (!map_fill_with_stationary_objects(nmap))
126             {
127                 /* adding stationary objects failed; generate a new map */
128                 map_destroy(nmap);
129                 return NULL;
130             }
131         }
132     }
133 
134     if (!map_loaded)
135     {
136         /* determine if to add treasure room */
137         gboolean treasure_room = num > 1 && chance(25);
138 
139         /* generate random map */
140         gboolean keep_maze = TRUE;
141         do
142         {
143             /* dig cave */
144             map_make_maze(nmap, treasure_room);
145 
146             /* check if entire map is reachable */
147             keep_maze = map_validate(nmap);
148         }
149         while (!keep_maze);
150     }
151 
152     if (num != 0)
153     {
154         /* home town is not filled with crap */
155         map_fill_with_objects(nmap);
156 
157         /* and not trapped */
158         map_fill_with_traps(nmap);
159     }
160 
161     /* add inhabitants to the map */
162     map_fill_with_life(nmap);
163 
164     return nmap;
165 }
166 
map_serialize(map * m)167 cJSON *map_serialize(map *m)
168 {
169     cJSON *mser, *grid, *tile;
170 
171     mser = cJSON_CreateObject();
172 
173     cJSON_AddNumberToObject(mser, "nlevel", m->nlevel);
174     cJSON_AddNumberToObject(mser, "visited", m->visited);
175 
176     cJSON_AddItemToObject(mser, "grid", grid = cJSON_CreateArray());
177 
178     for (int y = 0; y < MAP_MAX_Y; y++)
179     {
180         for (int x = 0; x < MAP_MAX_X; x++)
181         {
182             cJSON_AddItemToArray(grid, tile = cJSON_CreateObject());
183 
184             cJSON_AddNumberToObject(tile, "type", m->grid[y][x].type);
185 
186             if (m->grid[y][x].base_type > 0
187                     && m->grid[y][x].base_type != m->grid[y][x].type)
188             {
189                 cJSON_AddNumberToObject(tile, "base_type",
190                                         m->grid[y][x].base_type);
191             }
192 
193             if (m->grid[y][x].sobject)
194             {
195                 cJSON_AddNumberToObject(tile, "sobject",
196                                         m->grid[y][x].sobject);
197             }
198 
199             if (m->grid[y][x].trap)
200             {
201                 cJSON_AddNumberToObject(tile, "trap",
202                                         m->grid[y][x].trap);
203             }
204 
205             if (m->grid[y][x].timer)
206             {
207                 cJSON_AddNumberToObject(tile, "timer",
208                                         m->grid[y][x].timer);
209             }
210 
211             if (m->grid[y][x].m_oid)
212             {
213                 cJSON_AddNumberToObject(tile, "monster",
214                                         GPOINTER_TO_UINT(m->grid[y][x].m_oid));
215             }
216 
217             if (m->grid[y][x].ilist )
218             {
219                 cJSON_AddItemToObject(tile, "inventory",
220                                       inv_serialize(m->grid[y][x].ilist));
221             }
222         }
223     }
224 
225     return mser;
226 }
227 
map_deserialize(cJSON * mser)228 map *map_deserialize(cJSON *mser)
229 {
230     cJSON *grid, *tile, *obj;
231     map *m;
232 
233     m = g_malloc0(sizeof(map));
234 
235     m->nlevel = cJSON_GetObjectItem(mser, "nlevel")->valueint;
236     m->visited = cJSON_GetObjectItem(mser, "visited")->valueint;
237 
238     grid = cJSON_GetObjectItem(mser, "grid");
239 
240     for (int y = 0; y < MAP_MAX_Y; y++)
241     {
242         for (int x = 0; x < MAP_MAX_X; x++)
243         {
244             tile = cJSON_GetArrayItem(grid, x + (y * MAP_MAX_X));
245 
246             m->grid[y][x].type = cJSON_GetObjectItem(tile, "type")->valueint;
247 
248             obj = cJSON_GetObjectItem(tile, "base_type");
249             if (obj != NULL) m->grid[y][x].base_type = obj->valueint;
250 
251             obj = cJSON_GetObjectItem(tile, "sobject");
252             if (obj != NULL) m->grid[y][x].sobject = obj->valueint;
253 
254             obj = cJSON_GetObjectItem(tile, "trap");
255             if (obj != NULL) m->grid[y][x].trap = obj->valueint;
256 
257             obj = cJSON_GetObjectItem(tile, "timer");
258             if (obj != NULL) m->grid[y][x].timer = obj->valueint;
259 
260             obj = cJSON_GetObjectItem(tile, "monster");
261             if (obj != NULL) m->grid[y][x].m_oid = GUINT_TO_POINTER(obj->valueint);
262 
263             obj = cJSON_GetObjectItem(tile, "inventory");
264             if (obj != NULL) m->grid[y][x].ilist = inv_deserialize(obj);
265         }
266     }
267 
268     return m;
269 }
270 
map_dump(map * m,position ppos)271 char *map_dump(map *m, position ppos)
272 {
273     position pos = pos_invalid;
274     GString *dump;
275     monster *mon;
276 
277     dump = g_string_new_len(NULL, MAP_SIZE);
278 
279     Z(pos) = m->nlevel;
280 
281     for (Y(pos) = 0; Y(pos) < MAP_MAX_Y; Y(pos)++)
282     {
283         for (X(pos) = 0; X(pos) < MAP_MAX_X; X(pos)++)
284         {
285             if (pos_identical(pos, ppos))
286             {
287                 g_string_append_c(dump, '@');
288             }
289             else if ((mon = map_get_monster_at(m, pos)))
290             {
291                 g_string_append_c(dump, monster_glyph(mon));
292             }
293             else if (map_trap_at(m, pos))
294             {
295                 g_string_append_c(dump, '^');
296             }
297             else if (map_sobject_at(m, pos))
298             {
299                 g_string_append_c(dump, so_get_glyph(map_sobject_at(m, pos)));
300             }
301             else
302             {
303                 g_string_append_c(dump, mt_get_glyph(map_tiletype_at(m, pos)));
304             }
305         }
306         g_string_append_c(dump, '\n');
307     }
308 
309     return g_string_free(dump, FALSE);
310 }
311 
map_destroy(map * m)312 void map_destroy(map *m)
313 {
314     g_assert(m != NULL);
315 
316     /* destroy spheres on this level */
317     g_ptr_array_foreach(nlarn->spheres, (GFunc)map_sphere_destroy, m);
318 
319     /* destroy items and monsters */
320     for (int y = 0; y < MAP_MAX_Y; y++)
321         for (int x = 0; x < MAP_MAX_X; x++)
322         {
323             if (m->grid[y][x].m_oid != NULL) {
324                 monster *mon = game_monster_get(nlarn, m->grid[y][x].m_oid);
325 
326                 /* I wonder why it is possible that a monster ID is stored at
327                  * a position while there is no matching monster registered.
328                  * This seems to occur when called by game_destroy(). */
329                 if (mon != NULL) monster_destroy(mon);
330             }
331 
332             if (m->grid[y][x].ilist != NULL)
333                 inv_destroy(m->grid[y][x].ilist, TRUE);
334         }
335 
336     g_free(m);
337 }
338 
339 /* return coordinates of a free space */
map_find_space(map * m,map_element_t element,gboolean dead_end)340 position map_find_space(map *m, map_element_t element, gboolean dead_end)
341 {
342     rectangle entire_map = rect_new(1, 1, MAP_MAX_X - 2, MAP_MAX_Y - 2);
343     return map_find_space_in(m, entire_map, element, dead_end);
344 }
345 
map_find_space_in(map * m,rectangle where,map_element_t element,gboolean dead_end)346 position map_find_space_in(map *m,
347                            rectangle where,
348                            map_element_t element,
349                            gboolean dead_end)
350 {
351     position pos = pos_invalid;
352     int count, iteration = 0;
353 
354     g_assert (m != NULL && element < LE_MAX);
355 
356     X(pos) = rand_m_n(where.x1, where.x2);
357     Y(pos) = rand_m_n(where.y1, where.y2);
358     Z(pos) = m->nlevel;
359 
360     /* number of positions inside the rectangle */
361     count = (where.x2 - where.x1 + 1) * (where.y2 - where.y1 + 1);
362 
363     do
364     {
365         X(pos)++;
366 
367         if (X(pos) > where.x2)
368         {
369             X(pos) = where.x1;
370             Y(pos)++;
371         }
372 
373         if (Y(pos) > where.y2)
374         {
375             Y(pos) = where.y1;
376         }
377 
378         iteration++;
379     }
380     while (!map_pos_validate(m, pos, element, dead_end) && (iteration <= count));
381 
382     if (iteration > count )
383         pos = pos_invalid;
384 
385     return pos;
386 }
387 
map_get_surrounding(map * m,position pos,sobject_t type)388 int *map_get_surrounding(map *m, position pos, sobject_t type)
389 {
390     int nmove = 1;
391     int *dirs = g_malloc0(sizeof(int) * GD_MAX);
392 
393     while (nmove < GD_MAX)
394     {
395         position p = pos_move(pos, nmove);
396 
397         if (pos_valid(p) && map_sobject_at(m, p) == type)
398         {
399             dirs[nmove] = TRUE;
400         }
401 
402         nmove++;
403     }
404 
405     return dirs;
406 }
407 
map_find_sobject(map * m,sobject_t sobject)408 position map_find_sobject(map *m, sobject_t sobject)
409 {
410     position pos = pos_invalid;
411 
412     g_assert(m != NULL);
413 
414     Z(pos) = m->nlevel;
415 
416     for (Y(pos) = 0; Y(pos) < MAP_MAX_Y; Y(pos)++)
417         for (X(pos) = 0; X(pos) < MAP_MAX_X; X(pos)++)
418             if (map_sobject_at(m, pos) == sobject)
419                 return pos;
420 
421     /* if we reach this point, the sobject is not on the map */
422     return pos_invalid;
423 }
424 
map_pos_validate(map * m,position pos,map_element_t element,int dead_end)425 gboolean map_pos_validate(map *m, position pos, map_element_t element,
426                           int dead_end)
427 {
428     map_tile *tile;
429 
430     g_assert(m != NULL && element < LE_MAX);
431 
432     /* if the position is invalid it is invalid for the map as well */
433     if (!pos_valid(pos))
434         return FALSE;
435 
436     /* if the position is on another map it is invalid for this level */
437     if (Z(pos) != m->nlevel)
438         return FALSE;
439 
440     /* make shortcut */
441     tile = map_tile_at(m, pos);
442 
443     /* check for an dead end */
444     if (dead_end)
445     {
446         int wall_count = 0;
447         position p = pos;
448 
449         for (Y(p) = Y(pos) -1; Y(p) < Y(pos) + 2; Y(p)++)
450             for (X(p) = X(pos) -1; X(p) < X(pos) + 2; X(p)++)
451                 if (map_tiletype_at(m, p) == LT_WALL)
452                     wall_count++;
453 
454         if (wall_count < 7)
455         {
456             /* not enclosed by walls */
457             return FALSE;
458         }
459     }
460 
461     switch (element)
462     {
463     case LE_GROUND:
464         return mt_is_passable(tile->type);
465         break;
466 
467     case LE_SOBJECT:
468         if (mt_is_passable(tile->type) && (tile->sobject == LS_NONE))
469         {
470             /* find free space */
471             position p = pos;
472 
473             for (Y(p) = Y(pos) -1; Y(p) < Y(pos) + 2; Y(p)++)
474                 for (X(p) = X(pos) -1; X(p) < X(pos) + 2; X(p)++)
475                 {
476                     if (map_sobject_at(m, p) != LS_NONE)
477                         return FALSE;
478                 }
479 
480             return TRUE;
481         }
482         break;
483 
484     case LE_TRAP:
485         return (mt_is_passable(tile->type)
486                 && (tile->sobject == LS_NONE)
487                 && (tile->trap == TT_NONE));
488         break;
489 
490     case LE_ITEM:
491         /* we can stack like mad, so we only need to check if
492          * there is an open space */
493         return (map_pos_passable(m, pos) && (tile->sobject == LS_NONE));
494         break;
495 
496     case LE_MONSTER:
497     case LE_SWIMMING_MONSTER:
498     case LE_FLYING_MONSTER:
499     case LE_XORN:
500         /* not OK if player is standing on that tile */
501         if (pos_identical(pos, nlarn->p->pos))
502             return FALSE;
503 
504         if (map_is_monster_at(m, pos))
505             return FALSE;
506 
507         return monster_valid_dest(m, pos, element);
508         break;
509 
510     case LE_MAX:
511         return FALSE;
512         break;
513 
514     } /* switch */
515 
516     return FALSE;
517 }
518 
map_pos_is_visible(map * m,position s,position t)519 int map_pos_is_visible(map *m, position s, position t)
520 {
521     int delta_x, delta_y;
522     int x, y;
523     signed int ix, iy;
524 
525     /* positions on different levels? */
526     if (Z(s) != Z(t))
527         return FALSE;
528 
529     x = X(s);
530     y = Y(s);
531 
532     delta_x = abs(X(t) - X(s)) << 1;
533     delta_y = abs(Y(t) - Y(s)) << 1;
534 
535     /* if x1 == x2 or y1 == y2, then it does not matter what we set here */
536     ix = X(t) > X(s) ? 1 : -1;
537     iy = Y(t) > Y(s) ? 1 : -1;
538 
539     if (delta_x >= delta_y)
540     {
541         /* error may go below zero */
542         int error = delta_y - (delta_x >> 1);
543 
544         while (x != X(t))
545         {
546             if (error >= 0)
547             {
548                 if (error || (ix > 0))
549                 {
550                     y += iy;
551                     error -= delta_x;
552                 }
553             }
554 
555             x += ix;
556             error += delta_y;
557 
558             if (!mt_is_transparent(m->grid[y][x].type)
559                     || !so_is_transparent(m->grid[y][x].sobject))
560             {
561                 return FALSE;
562             }
563         }
564     }
565     else
566     {
567         /* error may go below zero */
568         int error = delta_x - (delta_y >> 1);
569 
570         while (y != Y(t))
571         {
572             if (error >= 0)
573             {
574                 if (error || (iy > 0))
575                 {
576                     x += ix;
577                     error -= delta_y;
578                 }
579             }
580 
581             y += iy;
582             error += delta_x;
583 
584             if (!mt_is_transparent(m->grid[y][x].type)
585                     || !so_is_transparent(m->grid[y][x].sobject))
586             {
587                 return FALSE;
588             }
589         }
590     }
591 
592     return TRUE;
593 }
594 
map_ray(map * m,position source,position target)595 GList *map_ray(map *m, position source, position target)
596 {
597     GList *ray = NULL;
598     int delta_x, delta_y;
599     int inc_x, inc_y;
600     position pos = source;
601 
602     /* Insert the source position */
603     ray = g_list_append(ray, GUINT_TO_POINTER(source.val));
604 
605     delta_x = abs(X(target) - X(source)) << 1;
606     delta_y = abs(Y(target) - Y(source)) << 1;
607 
608     /* if x1 == x2 or y1 == y2, then it does not matter what we set here */
609     inc_x = X(target) > X(source) ? 1 : -1;
610     inc_y = Y(target) > Y(source) ? 1 : -1;
611 
612     if (delta_x >= delta_y)
613     {
614         /* error may go below zero */
615         int error = delta_y - (delta_x >> 1);
616 
617         while (X(pos) != X(target))
618         {
619             if (error >= 0)
620             {
621                 if (error || (inc_x > 0))
622                 {
623                     Y(pos) += inc_y;
624                     error -= delta_x;
625                 }
626             }
627 
628             X(pos) += inc_x;
629             error += delta_y;
630 
631             /* append even the last position to the list */
632             ray = g_list_append(ray, GUINT_TO_POINTER(pos.val));
633 
634             if (!map_pos_transparent(m, pos))
635                 break; /* stop following ray */
636         }
637     }
638     else
639     {
640         /* error may go below zero */
641         int error = delta_x - (delta_y >> 1);
642 
643         while (Y(pos) != Y(target))
644         {
645             if (error >= 0)
646             {
647                 if (error || (inc_y > 0))
648                 {
649                     X(pos) += inc_x;
650                     error -= delta_y;
651                 }
652             }
653 
654             Y(pos) += inc_y;
655             error += delta_x;
656 
657             /* append even the last position to the list */
658             ray = g_list_append(ray, GUINT_TO_POINTER(pos.val));
659 
660             if (!map_pos_transparent(m, pos))
661                 break; /* stop following ray */
662         }
663     }
664 
665     if (ray && GPOINTER_TO_UINT(g_list_last(ray)->data) != target.val)
666     {
667         g_list_free(ray);
668         ray = NULL;
669     }
670 
671     return ray;
672 }
673 
map_trajectory(position source,position target,const damage_originator * const damo,trajectory_hit_sth pos_hitfun,gpointer data1,gpointer data2,gboolean reflectable,char glyph,int colour,gboolean keep_ray)674 gboolean map_trajectory(position source, position target,
675                         const damage_originator * const damo,
676                         trajectory_hit_sth pos_hitfun,
677                         gpointer data1, gpointer data2, gboolean reflectable,
678                         char glyph, int colour, gboolean keep_ray)
679 {
680     g_assert(pos_valid(source) && pos_valid(target));
681 
682     map *tmap = game_map(nlarn, Z(source));
683 
684     /* get the ray */
685     GList *ray = map_ray(tmap, source, target);
686     GList *iter = ray;
687 
688     /* it was impossible to get a ray for the given positions */
689     if (!ray)
690         return FALSE;
691 
692     /* follow the ray to determine if it hits something */
693     do
694     {
695         gboolean result = FALSE;
696         position cursor;
697         pos_val(cursor) = GPOINTER_TO_UINT(iter->data);
698 
699         /* skip the source position */
700         if (pos_identical(source, cursor))
701             continue;
702 
703         /* the position is affected, call the callback function */
704         if (pos_hitfun(iter, damo, data1, data2))
705         {
706             /* the callback returned that the ray if finished */
707             result = TRUE;
708         }
709 
710         /* check for reflection: mirrors and the amulet of reflection */
711         if (reflectable && ((map_sobject_at(tmap, cursor) == LS_MIRROR)
712                 || (pos_identical(cursor, nlarn->p->pos)
713                             && player_effect(nlarn->p, ET_REFLECTION))))
714         {
715             g_list_free(ray);
716             /* repaint the screen before showing the reflection, otherwise
717              * the reflection wouldn't be visible! */
718             display_paint_screen(nlarn->p);
719 
720             return map_trajectory(cursor, source, damo, pos_hitfun, data1,
721                                   data2, FALSE,  glyph, colour, keep_ray);
722         }
723 
724         /* after checking for reflection, abort the function if the
725            callback indicated success */
726         if (result == TRUE)
727         {
728             g_list_free(ray);
729             return result;
730         }
731 
732         /* show the position of the ray*/
733         /* FIXME: move curses functions to display.c */
734         attron(colour);
735         (void)mvaddch(Y(cursor), X(cursor), glyph);
736         attroff(colour);
737         display_draw();
738 
739         /* sleep a while to show the ray's position */
740         napms(100);
741         /* repaint the screen unless requested otherwise */
742         if (!keep_ray) display_paint_screen(nlarn->p);
743     }
744     while ((iter = iter->next));
745 
746     /* none of the trigger functions succeeded */
747     g_list_free(ray);
748     return FALSE;
749 }
750 
map_get_obstacles(map * m,position center,int radius,gboolean doors)751 area *map_get_obstacles(map *m, position center, int radius, gboolean doors)
752 {
753     area *narea;
754     position pos = pos_invalid;
755     int x, y;
756 
757     g_assert(m != NULL);
758 
759     if (!pos_valid(center))
760     {
761         return NULL;
762     }
763 
764     narea = area_new(X(center) - radius, Y(center) - radius,
765                      radius * 2 + 1, radius * 2 + 1);
766 
767     Z(pos) = m->nlevel;
768 
769     for (Y(pos) = Y(center) - radius, y = 0;
770          Y(pos) <= Y(center) + radius;
771          Y(pos)++, y++)
772     {
773         for (X(pos) = X(center) - radius, x = 0;
774              X(pos) <= X(center) + radius;
775              X(pos)++, x++)
776         {
777             if (!pos_valid(pos))
778             {
779                 area_point_set(narea, x, y);
780                 continue;
781             }
782             if (doors && map_sobject_at(m, pos) == LS_CLOSEDDOOR)
783                 continue;
784             else if (!map_pos_transparent(m, pos))
785                 area_point_set(narea, x, y);
786         }
787     }
788 
789     return narea;
790 }
791 
map_set_tiletype(map * m,area * ar,map_tile_t type,guint8 duration)792 void map_set_tiletype(map *m, area *ar, map_tile_t type, guint8 duration)
793 {
794     position pos = pos_invalid;
795     int x, y;
796 
797     g_assert (m != NULL && ar != NULL);
798 
799     position center = pos_invalid;
800     X(center) = ar->start_x + ar->size_x / 2;
801     Y(center) = ar->start_y + ar->size_y / 2;
802     Z(center) = m->nlevel;
803 
804     Z(pos) = m->nlevel;
805     for (Y(pos) = ar->start_y, y = 0;
806             Y(pos) < ar->start_y + ar->size_y;
807             Y(pos)++, y++)
808     {
809         for (X(pos) = ar->start_x, x = 0;
810                 X(pos) < ar->start_x + ar->size_x;
811                 X(pos)++, x++)
812         {
813             /* check if pos is inside the map */
814             if (!pos_valid(pos))
815                 continue;
816 
817             /* if the position is marked in area set the tile to type */
818             if (area_point_get(ar, x, y))
819             {
820                 map_tile *tile = map_tile_at(m, pos);
821 
822                 /* store original type if it has not been set already
823                    (this can occur when casting multiple flood
824                    spells on the same tile) */
825                 if (tile->base_type == LT_NONE)
826                     tile->base_type = map_tiletype_at(m, pos);
827 
828                 tile->type = type;
829                 /* if non-permanent, let the radius shrink with time */
830                 if (duration != 0)
831                     tile->timer = max(1, duration - 5 * pos_distance(pos, center));
832             }
833         }
834     }
835 }
836 
map_tile_damage(map * m,position pos,gboolean flying)837 damage *map_tile_damage(map *m, position pos, gboolean flying)
838 {
839     g_assert (m != NULL && pos_valid(pos));
840 
841     switch (map_tiletype_at(m, pos))
842     {
843     case LT_CLOUD:
844         return damage_new(DAM_ACID, ATT_NONE, 3 + rand_0n(2), DAMO_MAP, NULL);
845         break;
846 
847     case LT_FIRE:
848         return damage_new(DAM_FIRE, ATT_NONE, 5 + rand_0n(2), DAMO_MAP, NULL);
849         break;
850 
851     case LT_WATER:
852         if (flying)
853             return NULL;
854 
855         return damage_new(DAM_WATER, ATT_NONE, 4 + rand_0n(2), DAMO_MAP, NULL);
856         break;
857 
858     default:
859         return NULL;
860     }
861 }
862 
map_inv_description(map * m,position pos,const char * where,int (* ifilter)(item *))863 char *map_inv_description(map *m, position pos, const char* where, int (*ifilter)(item *))
864 {
865     g_assert(m != NULL);
866     g_assert(pos_valid(pos));
867     g_assert(m->nlevel == Z(pos));
868 
869     if (inv_length_filtered(*map_ilist_at(m, pos), ifilter) > 3)
870     {
871         return g_strdup_printf("There are multiple items %s.", where);
872     }
873 
874     GString *items_desc = NULL;
875 
876     for (guint idx = 0; idx < inv_length_filtered(*map_ilist_at(m, pos), ifilter); idx++)
877     {
878         item *it = inv_get_filtered(*map_ilist_at(m, pos), idx, ifilter);
879         gchar *item_desc = item_describe(it, player_item_known(nlarn->p, it),
880                                             FALSE, FALSE);
881 
882         if (idx > 0)
883             g_string_append_printf(items_desc, " and %s", item_desc);
884         else
885             items_desc = g_string_new(item_desc);
886 
887         g_free(item_desc);
888     }
889 
890     if (items_desc != NULL)
891     {
892         char *description = g_strdup_printf("You see %s %s.", items_desc->str, where);
893         g_string_free(items_desc, TRUE);
894 
895         return description;
896     }
897 
898     return NULL;
899 }
900 
map_pos_examine(position pos)901 char *map_pos_examine(position pos)
902 {
903     map *cm = game_map(nlarn, Z(pos));
904     monster *monst;
905     char *tmp = NULL;
906     const char *where;
907     GString *desc = g_string_new(NULL);
908 
909     g_assert(pos_valid(pos));
910 
911     if (pos_identical(pos, nlarn->p->pos))
912         where = "here";
913     else
914         where = "there";
915 
916     /* describe the level tile */
917     tmp = g_strdup(mt_get_desc(map_tiletype_at(cm, pos)));
918     tmp[0] = g_ascii_toupper(tmp[0]);
919     g_string_append_printf(desc, "%s. ", tmp);
920     g_free(tmp);
921 
922     gboolean has_mimic = FALSE;
923     /* add description of monster, if there is one on the tile */
924     if ((monst = map_get_monster_at(cm, pos)))
925     {
926         if (game_wizardmode(nlarn) || monster_in_sight(monst))
927         {
928             tmp = monster_desc(monst);
929 
930             tmp[0] = g_ascii_toupper(tmp[0]);
931             g_string_append_printf(desc, "%s. ", tmp);
932             g_free(tmp);
933 
934             if (monster_unknown(monst))
935                 has_mimic = TRUE;
936         }
937     }
938 
939     /* add message if target tile contains a stationary object */
940     if (map_sobject_at(cm, pos) > LS_NONE)
941     {
942         g_string_append_printf(desc, "You see %s %s. ",
943                                so_get_desc(map_sobject_at(cm, pos)), where);
944     }
945 
946     /* add message if target tile contains a known trap */
947     if (player_memory_of(nlarn->p, pos).trap)
948     {
949         g_string_append_printf(desc, "There is %s %s %s. ",
950                                a_an(trap_description(map_trap_at(cm, pos))),
951                                trap_description(map_trap_at(cm, pos)), where);
952     }
953 
954     /* add message if target tile contains items, but only if there's
955        no mimic there (items don't stack correctly otherwise) */
956     if (!has_mimic && inv_length(*map_ilist_at(cm, pos)) > 0)
957     {
958         char *inv_description = map_inv_description(cm, pos, where, NULL);
959         g_string_append(desc, inv_description);
960         g_free(inv_description);
961     }
962 
963     return g_string_free(desc, FALSE);
964 }
965 
map_get_monster_at(map * m,position pos)966 monster *map_get_monster_at(map *m, position pos)
967 {
968     g_assert(m != NULL && m->nlevel == Z(pos) && pos_valid(pos));
969 
970     gpointer mid = m->grid[Y(pos)][X(pos)].m_oid;
971     return (mid != NULL) ? game_monster_get(nlarn, mid) : NULL;
972 }
973 
map_fill_with_life(map * m)974 void map_fill_with_life(map *m)
975 {
976     g_assert(m != NULL);
977 
978     guint new_monster_count = rand_1n(14 + m->nlevel);
979 
980     if (m->nlevel == 0)
981     {
982         /* do not add an excessive amount of town people */
983         new_monster_count = min(5, new_monster_count);
984     }
985 
986     /* create monsters until the desired count is reached */
987     while (m->mcount <= new_monster_count)
988     {
989         position pos = pos_invalid;
990 
991         do
992         {
993             pos = map_find_space(m, LE_MONSTER, FALSE);
994 
995             if (!pos_valid(pos))
996             {
997                 /* it seems that the map is fully crowded,
998                    thus abort monster creation. */
999                 return;
1000             }
1001         }
1002         while (fov_get(nlarn->p->fv, pos));
1003 
1004         monster_new_by_level(pos);
1005     }
1006 }
1007 
map_is_exit_at(map * m,position pos)1008 gboolean map_is_exit_at(map *m, position pos)
1009 {
1010     g_assert (m != NULL && pos_valid(pos));
1011 
1012     switch (map_sobject_at(m, pos))
1013     {
1014     case LS_CAVERNS_ENTRY:
1015     case LS_CAVERNS_EXIT:
1016     case LS_ELEVATORDOWN:
1017     case LS_ELEVATORUP:
1018     case LS_STAIRSUP:
1019     case LS_STAIRSDOWN:
1020         return TRUE;
1021         break;
1022 
1023     default:
1024         return FALSE;
1025         break;
1026     }
1027 }
1028 
map_timer(map * m)1029 void map_timer(map *m)
1030 {
1031     position pos = pos_invalid;
1032     item_erosion_type erosion;
1033 
1034     g_assert (m != NULL);
1035 
1036     Z(pos) = m->nlevel;
1037 
1038     for (Y(pos) = 0; Y(pos) < MAP_MAX_Y; Y(pos)++)
1039     {
1040         for (X(pos) = 0; X(pos) < MAP_MAX_X; X(pos)++)
1041         {
1042             if (map_timer_at(m, pos))
1043             {
1044                 map_tile *tile = map_tile_at(m, pos);
1045                 tile->timer--;
1046 
1047                 /* affect items every three turns */
1048                 if ((tile->ilist != NULL) && (tile->timer % 5 == 0))
1049                 {
1050                     switch (tile->type)
1051                     {
1052                     case LT_CLOUD:
1053                         erosion = IET_CORRODE;
1054                         break;
1055 
1056                     case LT_FIRE:
1057                         erosion = IET_BURN;
1058                         break;
1059 
1060                     case LT_WATER:
1061                         erosion = IET_RUST;
1062                         break;
1063                     default:
1064                         erosion = IET_NONE;
1065                         break;
1066                     }
1067 
1068                     inv_erode(&tile->ilist, erosion,
1069                             fov_get(nlarn->p->fv, pos), NULL);
1070                 }
1071 
1072                 /* reset tile type if temporary effect has expired */
1073                 if (tile->timer == 0)
1074                 {
1075                     if ((tile->type == LT_FIRE)
1076                             && (tile->base_type == LT_GRASS))
1077                     {
1078                         tile->base_type = LT_NONE;
1079                         tile->type = LT_DIRT;
1080                     }
1081                     else
1082                     {
1083                         tile->type = tile->base_type;
1084                     }
1085                 }
1086             } /* if map_timer_at */
1087         } /* for X(pos) */
1088     } /* for Y(pos) */
1089 }
1090 
map_get_door_glyph(map * m,position pos)1091 char map_get_door_glyph(map *m, position pos)
1092 {
1093     position n, e, s, w;
1094 
1095     g_assert(m != NULL && pos_valid(pos));
1096 
1097     n = pos_move(pos, GD_NORTH);
1098     e = pos_move(pos, GD_EAST);
1099     s = pos_move(pos, GD_SOUTH);
1100     w = pos_move(pos, GD_WEST);
1101 
1102     /* some predefined maps have double doors, thus it is
1103        necessary to check for adjacent doors */
1104 
1105     if (map_sobject_at(m, pos) == LS_CLOSEDDOOR)
1106     {
1107         /* #-# */
1108         if (((map_tiletype_at(m, e) == LT_WALL)
1109              || (map_sobject_at(m, e) == LS_CLOSEDDOOR)
1110              || (map_sobject_at(m, e) == LS_OPENDOOR))
1111              && ((map_tiletype_at(m, w) == LT_WALL)
1112              || (map_sobject_at(m, w) == LS_CLOSEDDOOR)
1113              || (map_sobject_at(m, w) == LS_OPENDOOR)))
1114              return '-';
1115 
1116         /* #
1117          * |
1118          * # */
1119         if (((map_tiletype_at(m, n) == LT_WALL)
1120              || (map_sobject_at(m, n) == LS_CLOSEDDOOR)
1121              || (map_sobject_at(m, n) == LS_OPENDOOR))
1122              && ((map_tiletype_at(m, s) == LT_WALL)
1123              || (map_sobject_at(m, s) == LS_CLOSEDDOOR)
1124              || (map_sobject_at(m, s) == LS_OPENDOOR)))
1125             return '|';
1126 
1127     }
1128     else
1129     {
1130     /* #-# */
1131     if ((map_sobject_at(m, e) == LS_CLOSEDDOOR)
1132          || (map_sobject_at(m, e) == LS_OPENDOOR))
1133          return '/';
1134 
1135     if ((map_sobject_at(m, w) == LS_CLOSEDDOOR)
1136          || (map_sobject_at(m, w) == LS_OPENDOOR))
1137          return '\\';
1138 
1139     /* #
1140      * |
1141      * # */
1142     if ((map_sobject_at(m, s) == LS_CLOSEDDOOR)
1143          || (map_sobject_at(m, s) == LS_OPENDOOR))
1144         return '\\';
1145 
1146     if ((map_sobject_at(m, n) == LS_CLOSEDDOOR)
1147          || (map_sobject_at(m, n) == LS_OPENDOOR))
1148         return '/';
1149     }
1150 
1151     /* no idea. */
1152     return so_get_glyph(map_sobject_at(m, pos));
1153 }
1154 
map_fill_with_stationary_objects(map * m)1155 static int map_fill_with_stationary_objects(map *m)
1156 {
1157     position pos = pos_invalid;
1158 
1159     /* volcano shaft up from the temple */
1160     if (m->nlevel == MAP_CMAX)
1161     {
1162         pos = map_find_space(m, LE_SOBJECT, TRUE);
1163         if (!pos_valid(pos)) return FALSE;
1164         map_sobject_set(m, pos, LS_ELEVATORUP);
1165     }
1166 
1167     /*  make the fixed objects in the maze: STAIRS */
1168     if (!is_town(m->nlevel) && !is_caverns_bottom(m->nlevel)
1169             && !is_volcano_bottom(m->nlevel))
1170     {
1171         pos = map_find_space(m, LE_SOBJECT, TRUE);
1172         if (!pos_valid(pos)) return FALSE;
1173         map_sobject_set(m, pos, LS_STAIRSDOWN);
1174     }
1175 
1176     if ((m->nlevel > 1) && (m->nlevel != MAP_CMAX))
1177     {
1178         pos = map_find_space(m, LE_SOBJECT, TRUE);
1179         if (!pos_valid(pos)) return FALSE;
1180         map_sobject_set(m, pos, LS_STAIRSUP);
1181     }
1182 
1183     /* make the random objects in the maze */
1184     /* 33 percent chance for an altar */
1185     if (chance(33))
1186     {
1187         pos = map_find_space(m, LE_SOBJECT, FALSE);
1188         if (!pos_valid(pos)) return FALSE;
1189         map_sobject_set(m, pos, LS_ALTAR);
1190     }
1191 
1192     /* up to three statues */
1193     for (guint i = 0; i < rand_0n(3); i++)
1194     {
1195         pos = map_find_space(m, LE_SOBJECT, FALSE);
1196         if (!pos_valid(pos)) return FALSE;
1197         map_sobject_set(m, pos, LS_STATUE);
1198     }
1199 
1200     /* up to three fountains */
1201     for (guint i = 0; i < rand_0n(3); i++)
1202     {
1203         pos = map_find_space(m, LE_SOBJECT, FALSE);
1204         if (!pos_valid(pos)) return FALSE;
1205         map_sobject_set(m, pos, LS_FOUNTAIN);
1206     }
1207 
1208     /* up to two thrones */
1209     for (guint i = 0; i < rand_0n(2); i++)
1210     {
1211         pos = map_find_space(m, LE_SOBJECT, FALSE);
1212         if (!pos_valid(pos)) return FALSE;
1213         map_sobject_set(m, pos, LS_THRONE);
1214     }
1215 
1216     /* up to two  mirrors */
1217     for (guint i = 0; i < rand_0n(2); i++)
1218     {
1219         pos = map_find_space(m, LE_SOBJECT, FALSE);
1220         if (!pos_valid(pos)) return FALSE;
1221         map_sobject_set(m, pos, LS_MIRROR);
1222     }
1223 
1224     if (m->nlevel == 5)
1225     {
1226         /* branch office of the bank */
1227         pos = map_find_space(m, LE_SOBJECT, TRUE);
1228         if (!pos_valid(pos)) return FALSE;
1229         map_sobject_set(m, pos, LS_BANK2);
1230     }
1231 
1232     return TRUE;
1233 }
1234 
map_fill_with_objects(map * m)1235 static void map_fill_with_objects(map *m)
1236 {
1237     /* up to two pieces of armour */
1238     for (guint i = 0; i <= rand_0n(2); i++)
1239     {
1240         map_item_add(m, item_new_by_level(IT_ARMOUR, m->nlevel));
1241     }
1242 
1243     /* up to two amulets on levels > 5 */
1244     if (m->nlevel > 5)
1245     {
1246         for (guint i = 0; i <= rand_0n(2); i++)
1247             map_item_add(m, item_new_by_level(IT_AMULET, m->nlevel));
1248     }
1249 
1250     /* up to two piles of ammunition */
1251     for (guint i = 0; i <= rand_0n(2); i++)
1252     {
1253         map_item_add(m, item_new_by_level(IT_AMMO, m->nlevel));
1254     }
1255 
1256     /* up to three books */
1257     for (guint i = 0; i <= rand_0n(3); i++)
1258     {
1259         map_item_add(m, item_new_by_level(IT_BOOK, m->nlevel));
1260     }
1261 
1262     /* up to two containers */
1263     for (guint i = 1; i <= rand_0n(2); i++)
1264     {
1265         /* random container type */
1266         item *container = item_new(IT_CONTAINER, rand_1n(CT_MAX));
1267 
1268         /* up to 5 items inside the container */
1269         for (guint j = 0; j < rand_0n(5); j++)
1270         {
1271             item_t it;
1272 
1273             /* prevent containers inside the container */
1274             do
1275             {
1276                 it = rand_1n(IT_MAX - 1);
1277             }
1278             while (it == IT_CONTAINER);
1279 
1280             inv_add(&(container->content), item_new_by_level(it, m->nlevel));
1281         }
1282 
1283         /* there is a chance that the container is trapped */
1284         if (chance(33))
1285         {
1286             container->cursed = TRUE;
1287         }
1288 
1289         /* add the container to the map */
1290         map_item_add(m, container);
1291     }
1292 
1293     /* up to 10 piles of gold */
1294     for (guint i = 0; i <= rand_0n(10); i++)
1295     {
1296         /* There is nothing like a newly minted pound. */
1297         map_item_add(m, item_new(IT_GOLD, rand_m_n(10, (m->nlevel + 1) * 15)));
1298     }
1299 
1300     /* up to three gems */
1301     for (guint i = 0; i <= rand_0n(3); i++)
1302     {
1303         map_item_add(m, item_new_random(IT_GEM, FALSE));
1304     }
1305 
1306     /* up to four potions */
1307     for (guint i = 0; i <= rand_0n(4); i++)
1308     {
1309         map_item_add(m, item_new_by_level(IT_POTION, m->nlevel));
1310     }
1311 
1312     /* up to three scrolls */
1313     for (guint i = 0; i <= rand_0n(3); i++)
1314     {
1315         map_item_add(m, item_new_by_level(IT_SCROLL, m->nlevel));
1316     }
1317 
1318     /* up to two rings */
1319     for (guint i = 0; i <= rand_0n(2); i++)
1320     {
1321         map_item_add(m, item_new_by_level(IT_RING, m->nlevel));
1322     }
1323 
1324     /* up to two weapons */
1325     for (guint i = 0; i <= rand_0n(2); i++)
1326     {
1327         map_item_add(m, item_new_by_level(IT_WEAPON, m->nlevel));
1328     }
1329 
1330 } /* map_fill_with_objects */
1331 
map_fill_with_traps(map * m)1332 static void map_fill_with_traps(map *m)
1333 {
1334     g_assert(m != NULL);
1335 
1336     /* Trapdoor cannot be placed in the last caverns map and the last volcano map */
1337     gboolean trapdoor = (!is_caverns_bottom(m->nlevel)
1338             && !is_volcano_bottom(m->nlevel));
1339 
1340     for (guint count = 0; count < rand_0n((trapdoor ? 8 : 6)); count++)
1341     {
1342         position pos = map_find_space(m, LE_TRAP, FALSE);
1343         map_trap_set(m, pos, rand_1n(trapdoor ? TT_MAX : TT_TRAPDOOR));
1344     }
1345 } /* map_fill_with_traps */
1346 
1347 /* Subroutine to make the caverns for a given map. Only walls are made. */
map_make_maze(map * m,int treasure_room)1348 static void map_make_maze(map *m, int treasure_room)
1349 {
1350     position pos = pos_invalid;
1351     int mx, my;
1352     int nrooms;
1353     rectangle **rooms = NULL;
1354     gboolean want_monster = FALSE;
1355 
1356     g_assert (m != NULL);
1357 
1358     Z(pos) = m->nlevel;
1359 
1360 generate:
1361     /* reset map by filling it with walls */
1362     for (Y(pos) = 0; Y(pos) < MAP_MAX_Y; Y(pos)++)
1363         for (X(pos) = 0; X(pos) < MAP_MAX_X; X(pos)++)
1364         {
1365             monster *mon;
1366 
1367             map_tiletype_set(m, pos, LT_WALL);
1368             map_sobject_set(m, pos, LS_NONE);
1369 
1370             if ((mon = map_get_monster_at(m, pos)))
1371             {
1372                 monster_destroy(mon);
1373             }
1374 
1375             if (map_tile_at(m, pos)->ilist != NULL)
1376             {
1377                 inv_destroy(map_tile_at(m, pos)->ilist, TRUE);
1378                 map_tile_at(m, pos)->ilist = NULL;
1379             }
1380         }
1381 
1382     /* Maybe add a river or lake. */
1383     const map_tile_t rivertype = (is_volcano_map(m->nlevel) ? LT_LAVA : LT_DEEPWATER);
1384 
1385     if (m->nlevel > 1
1386             && (is_volcano_map(m->nlevel) ? chance(90) : chance(40)))
1387     {
1388         if (chance(70))
1389             map_make_river(m, rivertype);
1390         else
1391             map_make_lake(m, rivertype);
1392 
1393         if (m->grid[1][1].type == LT_WALL)
1394             map_make_maze_eat(m, 1, 1);
1395     }
1396     else
1397         map_make_maze_eat(m, 1, 1);
1398 
1399     /* add exit to town on map 1 */
1400     if (m->nlevel == 1)
1401     {
1402         m->grid[MAP_MAX_Y - 1][(MAP_MAX_X - 1) / 2].type = LT_FLOOR;
1403         m->grid[MAP_MAX_Y - 1][(MAP_MAX_X - 1) / 2].sobject = LS_CAVERNS_EXIT;
1404     }
1405 
1406     /* generate open spaces */
1407     nrooms = rand_1n(3) + 3;
1408     if (treasure_room)
1409         nrooms++;
1410 
1411     rooms = g_malloc0(sizeof(rectangle*) * (nrooms + 1));
1412 
1413     for (int room = 0; room < nrooms; room++)
1414     {
1415         rooms[room] = g_malloc0(sizeof(rectangle));
1416 
1417         my = rand_1n(11) + 2;
1418         rooms[room]->y1 = my - rand_1n(2);
1419         rooms[room]->y2 = my + rand_1n(2);
1420 
1421         if (is_volcano_map(m->nlevel))
1422         {
1423             mx = rand_1n(60) + 3;
1424             rooms[room]->x1 = mx - rand_1n(2);
1425             rooms[room]->x2 = mx + rand_1n(2);
1426 
1427             want_monster = TRUE;
1428         }
1429         else
1430         {
1431             mx = rand_1n(44) + 5;
1432             rooms[room]->x1 = mx - rand_1n(4);
1433             rooms[room]->x2 = mx + rand_1n(12) + 3;
1434         }
1435 
1436         for (Y(pos) = rooms[room]->y1 ; Y(pos) < rooms[room]->y2 ; Y(pos)++)
1437         {
1438             for (X(pos) = rooms[room]->x1 ; X(pos) < rooms[room]->x2 ; X(pos)++)
1439             {
1440                 map_tile *tile = map_tile_at(m, pos);
1441                 if (tile->type == rivertype)
1442                     continue;
1443 
1444                 tile->type = LT_FLOOR;
1445 
1446                 if (want_monster == TRUE)
1447                 {
1448                     monster_new_by_level(pos);
1449                     want_monster = FALSE;
1450                 }
1451             }
1452         }
1453     }
1454 
1455     /* mark the end of the rooms array */
1456     rooms[nrooms] = NULL;
1457 
1458     /* add stationary objects */
1459     if (!map_fill_with_stationary_objects(m))
1460     {
1461         /* adding stationary objects failed; generate a new map */
1462         goto generate;
1463     }
1464 
1465     /* add treasure room if requested */
1466     if (treasure_room)
1467         map_make_treasure_room(m, rooms);
1468 
1469     /* clean up */
1470     for (int room = 0; room < nrooms; room++)
1471         g_free(rooms[room]);
1472 
1473     g_free(rooms);
1474 }
1475 
1476 /* function to eat away a filled in maze */
map_make_maze_eat(map * m,int x,int y)1477 static void map_make_maze_eat(map *m, int x, int y)
1478 {
1479     int dir;
1480     int try = 2;
1481 
1482     dir = rand_1n(4);
1483 
1484     while (try)
1485     {
1486         switch (dir)
1487         {
1488         case 1: /* west */
1489             if ((x > 2) &&
1490                     (m->grid[y][x - 1].type == LT_WALL) &&
1491                     (m->grid[y][x - 2].type == LT_WALL))
1492             {
1493                 m->grid[y][x - 1].type = m->grid[y][x - 2].type = LT_FLOOR;
1494                 map_make_maze_eat(m, x - 2, y);
1495             }
1496             break;
1497 
1498         case 2: /* east */
1499             if (x < (MAP_MAX_X - 3) &&
1500                     (m->grid[y][x + 1].type == LT_WALL) &&
1501                     (m->grid[y][x + 2].type == LT_WALL))
1502             {
1503                 m->grid[y][x + 1].type = m->grid[y][x + 2].type = LT_FLOOR;
1504                 map_make_maze_eat(m, x + 2, y);
1505             }
1506             break;
1507 
1508         case 3: /* south */
1509             if ((y > 2) &&
1510                     (m->grid[y - 1][x].type == LT_WALL) &&
1511                     (m->grid[y - 2][x].type == LT_WALL))
1512             {
1513                 m->grid[y - 1][x].type = m->grid[y - 2][x].type = LT_FLOOR;
1514                 map_make_maze_eat(m, x, y - 2);
1515             }
1516             break;
1517 
1518         case 4: /* north */
1519             if ((y < MAP_MAX_Y - 3) &&
1520                     (m->grid[y + 1][x].type == LT_WALL) &&
1521                     (m->grid[y + 2][x].type == LT_WALL))
1522             {
1523                 m->grid[y + 1][x].type = m->grid[y + 2][x].type = LT_FLOOR;
1524                 map_make_maze_eat(m, x, y + 2);
1525             }
1526 
1527             break;
1528         };
1529         if (++dir > 4)
1530         {
1531             dir = 1;
1532             try--;
1533         }
1534     }
1535 }
1536 
1537 /* The river/lake creation algorithm has been copied in entirety
1538    from Dungeon Crawl Stone Soup, with only very slight changes. (jpeg) */
map_make_vertical_river(map * m,map_tile_t rivertype)1539 static void map_make_vertical_river(map *m, map_tile_t rivertype)
1540 {
1541     gint width  = 3 + rand_0n(4);
1542     gint startx = 6 - width + rand_0n(MAP_MAX_X - 8);
1543 
1544     const gint starty = rand_1n(4);
1545     const gint endy   = MAP_MAX_Y - (4 - starty);
1546     const gint minx   = rand_1n(3);
1547     const gint maxx   = MAP_MAX_X - rand_1n(3);
1548 
1549     position pos = pos_invalid;
1550     Z(pos) = m->nlevel;
1551     for (Y(pos) = starty; Y(pos) < endy; Y(pos)++)
1552     {
1553         if (chance(33)) startx++;
1554         if (chance(33)) startx--;
1555         if (chance(50)) width++;
1556         if (chance(50)) width--;
1557 
1558         if (width < 2) width = 2;
1559         if (width > 6) width = 6;
1560 
1561         for (X(pos) = startx; X(pos) < startx + width; X(pos)++)
1562         {
1563             if (X(pos) > minx && X(pos) < maxx && chance(99))
1564                 map_tiletype_set(m, pos, rivertype);
1565         }
1566     }
1567 }
1568 
map_make_river(map * m,map_tile_t rivertype)1569 static void map_make_river(map *m, map_tile_t rivertype)
1570 {
1571     if (chance(20))
1572     {
1573         map_make_vertical_river(m, rivertype);
1574         return;
1575     }
1576 
1577     gint width  = 3 + rand_0n(4);
1578     gint starty = 10 - width + rand_0n(MAP_MAX_Y - 12);
1579 
1580     const gint startx = rand_1n(7);
1581     const gint endx   = MAP_MAX_X - (7 - startx);
1582     const gint miny   = rand_1n(3);
1583     const gint maxy   = MAP_MAX_Y - rand_1n(3);
1584 
1585     position pos = pos_invalid;
1586     Z(pos) = m->nlevel;
1587     for (X(pos) = startx; X(pos) < endx; X(pos)++)
1588     {
1589         if (chance(33)) starty++;
1590         if (chance(33)) starty--;
1591         if (chance(50)) width++;
1592         if (chance(50)) width--;
1593 
1594         /* sanity checks */
1595         if (starty < 2) starty = 2;
1596         if (starty > maxy) starty = maxy;
1597         if (width < 2) width = 2;
1598         if (width > 6) width = 6;
1599 
1600         for (Y(pos) = starty; Y(pos) < starty + width; Y(pos)++)
1601         {
1602             if (Y(pos) > miny && Y(pos) < maxy && chance(99))
1603                 map_tiletype_set(m, pos, rivertype);
1604         }
1605     }
1606 }
1607 
map_make_lake(map * m,map_tile_t laketype)1608 static void map_make_lake(map *m, map_tile_t laketype)
1609 {
1610     gint x1 = 5 + rand_0n(MAP_MAX_X - 30);
1611     gint y1 = 3 + rand_0n(MAP_MAX_Y - 15);
1612     gint x2 = x1 + 4 + rand_0n(16);
1613     gint y2 = y1 + 4 + rand_0n(5);
1614 
1615     position pos = pos_invalid;
1616     Z(pos) = m->nlevel;
1617     for (Y(pos) = y1; Y(pos) < y2; Y(pos)++)
1618     {
1619         if (Y(pos) <= 1 || Y(pos) >= MAP_MAX_Y - 1)
1620             continue;
1621 
1622         if (chance(50))  x1 += rand_0n(3);
1623         if (chance(50))  x1 -= rand_0n(3);
1624         if (chance(50))  x2 += rand_0n(3);
1625         if (chance(50))  x2 -= rand_0n(3);
1626 
1627         if (Y(pos) - y1 < (y2 - y1) / 2)
1628         {
1629             x2 += rand_0n(3);
1630             x1 -= rand_0n(3);
1631         }
1632         else
1633         {
1634             x2 -= rand_0n(3);
1635             x1 += rand_0n(3);
1636         }
1637 
1638         for (X(pos) = x1; X(pos) < x2 ; X(pos)++)
1639         {
1640             if (X(pos) <= 1 || X(pos) >= MAP_MAX_X - 1)
1641                 continue;
1642 
1643             if (chance(99))
1644                 map_tiletype_set(m, pos, laketype);
1645         }
1646     }
1647 }
1648 
place_special_item(map * m,position npos)1649 static void place_special_item(map *m, position npos)
1650 {
1651     map_tile *tile = map_tile_at(m, npos);
1652 
1653     switch (m->nlevel)
1654     {
1655     case MAP_CMAX - 1: /* the amulet of larn */
1656         inv_add(&tile->ilist, item_new(IT_AMULET, AM_LARN));
1657 
1658         monster_new(MT_DEMONLORD_I + rand_0n(7), npos, NULL);
1659         break;
1660 
1661     case MAP_MAX - 1: /* potion of cure dianthroritis */
1662         inv_add(&tile->ilist, item_new(IT_POTION, PO_CURE_DIANTHR));
1663         monster_new(MT_DEMON_PRINCE, npos, NULL);
1664 
1665     default:
1666         /* plain level, add neither monster nor item */
1667         break;
1668     }
1669 }
1670 
1671 /*
1672  *  function to read in a maze from a data file
1673  *
1674  *  Format of maze data file:
1675  *  For each maze:  MAP_MAX_Y + 1 lines (MAP_MAX_Y used)
1676  *                  MAP_MAX_X characters per line
1677  *
1678  *  Special characters in maze data file:
1679  *
1680  *      #   wall
1681  *      +   door
1682  *      m   random monster
1683  *      !   potion of cure dianthroritis, or the amulet of larn, as appropriate
1684  *      o   random object
1685  */
map_load_from_file(map * m,const char * mazefile,guint which)1686 static gboolean map_load_from_file(map *m, const char *mazefile, guint which)
1687 {
1688     position pos;       /* current position on map */
1689     int map_num = 0;    /* number of selected map */
1690     item_t it;          /* item type for random objects */
1691 
1692     FILE *levelfile;
1693 
1694     if (!(levelfile = fopen(mazefile, "r")))
1695     {
1696         /* maze file cannot be opened */
1697         return FALSE;
1698     }
1699 
1700     if (feof(levelfile))
1701     {
1702         /* FIXME: debug output */
1703         fclose(levelfile);
1704 
1705         return FALSE;
1706     }
1707 
1708     /* FIXME: calculate how many levels are in the file  */
1709 
1710     /* roll the dice: which map? */
1711     if (which <= MAP_MAX_MAZE_NUM)
1712     {
1713         map_num = which;
1714     }
1715     else
1716     {
1717         int tries = 0;
1718         do
1719         {
1720             map_num = rand_1n(MAP_MAX_MAZE_NUM);
1721         }
1722         while (map_used[map_num] && ++tries < 100);
1723 
1724         map_used[map_num] = TRUE;
1725     }
1726 
1727     /* determine number of line separating character(s) */
1728     guint lslen;
1729 
1730     /* get first character at the end of the first line */
1731     fseek(levelfile, MAP_MAX_X, SEEK_SET);
1732     switch (fgetc(levelfile))
1733     {
1734         case 10: /* i.e. LF */
1735             lslen = 1;
1736             break;
1737         case 13: /* i.e. CR/LF*/
1738             lslen = 2;
1739             break;
1740         default:
1741             /* maze file is corrupted - show error message and quit */
1742             nlarn = game_destroy(nlarn);
1743             display_show_message("Error", "Maze file is corrupted. Please reinstall the game.", 0);
1744             exit(EXIT_FAILURE);
1745             break;
1746     }
1747 
1748     /* advance to desired maze */
1749     fseek(levelfile, (map_num * ((MAP_MAX_X + lslen) * MAP_MAX_Y + lslen)), SEEK_SET);
1750 
1751     if (feof(levelfile))
1752     {
1753         /* FIXME: debug output */
1754         fclose(levelfile);
1755 
1756         return FALSE;
1757     }
1758 
1759     // Sometimes flip the maps. (Never the town)
1760     gboolean flip_vertical   = (map_num > 0 && chance(50));
1761     gboolean flip_horizontal = (map_num > 0 && chance(50));
1762 
1763     /* replace which of 3 '!' with a special item? (if appropriate) */
1764     int spec_count = rand_0n(3);
1765 
1766     Z(pos) = m->nlevel;
1767     for (Y(pos) = 0; Y(pos) < MAP_MAX_Y; Y(pos)++)
1768     {
1769         for (X(pos) = 0; X(pos) < MAP_MAX_X; X(pos)++)
1770         {
1771             position map_pos = pos;
1772             if (flip_vertical)
1773                 X(map_pos) = MAP_MAX_X - X(pos) - 1;
1774             if (flip_horizontal)
1775                 Y(map_pos) = MAP_MAX_Y - Y(pos) - 1;
1776 
1777             map_tile *tile = map_tile_at(m, map_pos);
1778 
1779             tile->type = LT_FLOOR;    /* floor is default */
1780 
1781             switch (fgetc(levelfile))
1782             {
1783 
1784             case '^': /* mountain */
1785                 tile->type = LT_MOUNTAIN;
1786                 break;
1787 
1788             case '"': /* grass */
1789                 tile->type = LT_GRASS;
1790                 break;
1791 
1792             case '.': /* dirt */
1793                 tile->type = LT_DIRT;
1794                 break;
1795 
1796             case '&': /* tree */
1797                 tile->type = LT_TREE;
1798                 break;
1799 
1800             case '~': /* deep water */
1801                 tile->type = LT_DEEPWATER;
1802                 break;
1803 
1804             case '=': /* lava */
1805                 tile->type = LT_LAVA;
1806                 break;
1807 
1808             case '#': /* wall */
1809                 tile->type =  LT_WALL;
1810                 break;
1811 
1812             case '_': /* altar */
1813                 tile->sobject = LS_ALTAR;
1814                 break;
1815 
1816             case '+': /* door */
1817                 tile->sobject = LS_CLOSEDDOOR;
1818                 break;
1819 
1820             case 'O': /* caverns entrance */
1821                 tile->sobject = LS_CAVERNS_ENTRY;
1822                 break;
1823 
1824             case 'I': /* elevator */
1825                 tile->sobject = LS_ELEVATORDOWN;
1826                 break;
1827 
1828             case 'H': /* home */
1829                 tile->sobject = LS_HOME;
1830                 break;
1831 
1832             case 'D': /* dnd store */
1833                 tile->sobject = LS_DNDSTORE;
1834                 break;
1835 
1836             case 'T': /* trade post */
1837                 tile->sobject = LS_TRADEPOST;
1838                 break;
1839 
1840             case 'L': /* LRS */
1841                 tile->sobject = LS_LRS;
1842                 break;
1843 
1844             case 'S': /* school */
1845                 tile->sobject = LS_SCHOOL;
1846                 break;
1847 
1848             case 'B': /* bank */
1849                 tile->sobject = LS_BANK;
1850                 break;
1851 
1852             case 'M': /* monastery */
1853                 tile->sobject = LS_MONASTERY;
1854                 break;
1855 
1856             case '!': /* potion of cure dianthroritis, eye of larn */
1857                 if (spec_count-- == 0)
1858                     place_special_item(m, map_pos);
1859                 break;
1860 
1861             case 'm': /* random monster */
1862                 monster_new_by_level(map_pos);
1863                 break;
1864 
1865             case 'o': /* random item */
1866                 do
1867                 {
1868                     it = rand_1n(IT_MAX - 1);
1869                 }
1870                 while (it == IT_CONTAINER);
1871 
1872                 inv_add(&tile->ilist, item_new_by_level(it, m->nlevel));
1873                 break;
1874             };
1875         }
1876 
1877         (void)fgetc(levelfile); /* eat EOL */
1878     }
1879 
1880     fclose(levelfile);
1881 
1882     /* if the amulet of larn/pcd has not been placed yet, place it randomly */
1883     if (spec_count >= 0)
1884         place_special_item(m, map_find_space(m, LE_ITEM, FALSE));
1885 
1886     return TRUE;
1887 }
1888 
1889 /*
1890  * function to make a treasure room on a map
1891  */
map_make_treasure_room(map * m,rectangle ** rooms)1892 static void map_make_treasure_room(map *m, rectangle **rooms)
1893 {
1894     position pos = pos_invalid, npos = pos_invalid;
1895     sobject_t mst;
1896     item *itm;
1897     int success;
1898 
1899     int nrooms = 0; /* count of available rooms */
1900     int room;   /* number of chose room */
1901 
1902     /* There's nothing to do if there are no rooms. */
1903     if (rooms == NULL) { return; }
1904 
1905     /* determine number of rooms */
1906     while(rooms[nrooms] != NULL) { nrooms++; }
1907 
1908     /* choose a room to turn into an treasure room */
1909     room = rand_0n(nrooms);
1910 
1911     /* sanity check */
1912     if (rooms[room] == NULL) { return; }
1913 
1914     Z(pos) = Z(npos) = m->nlevel;
1915 
1916     for (Y(pos) = rooms[room]->y1; Y(pos) <= rooms[room]->y2; Y(pos)++)
1917     {
1918         for (X(pos) = rooms[room]->x1; X(pos) <= rooms[room]->x2; X(pos)++)
1919         {
1920             if ( (Y(pos) == rooms[room]->y1)
1921                     || (Y(pos) == rooms[room]->y2)
1922                     || (X(pos) == rooms[room]->x1)
1923                     || (X(pos) == rooms[room]->x2) )
1924             {
1925                 /* if we are on the border of a room, make wall */
1926                 map_tiletype_set(m, pos, LT_WALL);
1927             }
1928             else
1929             {
1930                 /* make sure there's floor here */
1931                 map_tiletype_set(m, pos, LT_FLOOR);
1932 
1933                 /* create loot */
1934                 itm = item_new_random(IT_GOLD, FALSE);
1935                 inv_add(map_ilist_at(m, pos), itm);
1936 
1937                 /* create a monster */
1938                 monster_new_by_level(pos);
1939             }
1940 
1941             /* now clear out interior */
1942             if ((mst = map_sobject_at(m, pos)))
1943             {
1944                 success = FALSE;
1945                 do
1946                 {
1947                     npos = map_find_space(m, LE_SOBJECT, FALSE);
1948                     if (!pos_in_rect(npos, *rooms[room]))
1949                     {
1950                         /* pos is outside of room */
1951                         map_sobject_set(m, npos, mst);
1952                         map_sobject_set(m, pos, LS_NONE);
1953 
1954                         success = TRUE;
1955                     }
1956                 }
1957                 while (!success);
1958             } /* if map_sobject_at() */
1959         } /* for x */
1960     } /* for y */
1961 
1962     /* place the door on the treasure room */
1963     switch (rand_1n(2))
1964     {
1965     case 1: /* horizontal */
1966         X(pos) = rand_m_n(rooms[room]->x1, rooms[room]->x2);
1967         Y(pos) = rand_0n(1) ? rooms[room]->y1 : rooms[room]->y2;
1968         break;
1969 
1970     case 2: /* vertical */
1971         X(pos) = rand_0n(1) ? rooms[room]->x1 : rooms[room]->x2;
1972         Y(pos) = rand_m_n(rooms[room]->y1, rooms[room]->y2);
1973         break;
1974     };
1975 
1976     map_tiletype_set(m, pos, LT_FLOOR);
1977     map_sobject_set(m, pos, LS_CLOSEDDOOR);
1978 }
1979 
1980 /* verify that every space on the map can be reached */
map_validate(map * m)1981 static int map_validate(map *m)
1982 {
1983     position pos = pos_invalid;
1984     int connected = TRUE;
1985     area *floodmap = NULL;
1986     area *obsmap = area_new(0, 0, MAP_MAX_X, MAP_MAX_Y);
1987 
1988     Z(pos) = m->nlevel;
1989 
1990     /* generate an obstacle map */
1991     for (Y(pos) = 0; Y(pos) < MAP_MAX_Y; Y(pos)++)
1992         for (X(pos) = 0; X(pos) < MAP_MAX_X; X(pos)++)
1993             if (!map_pos_passable(m, pos)
1994                     && (map_sobject_at(m, pos) != LS_CLOSEDDOOR))
1995             {
1996                 area_point_set(obsmap, X(pos), Y(pos));
1997             }
1998 
1999     /* get position of entrance */
2000     switch (m->nlevel)
2001     {
2002         /* caverns entrance */
2003     case 1:
2004         pos = map_find_sobject(m, LS_CAVERNS_EXIT);
2005         break;
2006 
2007         /* volcano entrance */
2008     case MAP_CMAX:
2009         pos = map_find_sobject(m, LS_ELEVATORUP);
2010         break;
2011 
2012     default:
2013         pos = map_find_sobject(m, LS_STAIRSDOWN);
2014         break;
2015     }
2016 
2017     /* flood fill the maze starting at the entrance */
2018     floodmap = area_flood(obsmap, X(pos), Y(pos));
2019 
2020     /* compare flooded area with obstacle map */
2021     for (Y(pos) = 0; Y(pos) < MAP_MAX_Y; Y(pos)++)
2022     {
2023         for (X(pos) = 0; X(pos) < MAP_MAX_X; X(pos)++)
2024         {
2025             int pp = map_pos_passable(m, pos);
2026             int cd = (map_sobject_at(m, pos) == LS_CLOSEDDOOR);
2027 
2028             /* point should be set on floodmap if it is passable */
2029             if (area_point_get(floodmap, X(pos), Y(pos)) != (pp || cd))
2030             {
2031                 connected = FALSE;
2032                 break;
2033             }
2034         }
2035 
2036         if (!connected)
2037             break;
2038     }
2039 
2040     area_destroy(floodmap);
2041 
2042     return connected;
2043 }
2044 
2045 /* subroutine to put an item onto an empty space */
map_item_add(map * m,item * what)2046 void map_item_add(map *m, item *what)
2047 {
2048     position pos = map_find_space(m, LE_ITEM, FALSE);
2049     inv_add(map_ilist_at(m, pos), what);
2050 }
2051