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