1 /* SCCS Id: @(#)dungeon.c 3.4 1999/10/30 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include "hack.h"
6 #include "dgn_file.h"
7 #include "dlb.h"
8 #include "display.h"
9
10 #define DUNGEON_AREA FILE_AREA_UNSHARE
11 #define DUNGEON_FILE "dungeon"
12
13 #define X_START "x-strt"
14 #define X_LOCATE "x-loca"
15 #define X_GOAL "x-goal"
16
17 struct proto_dungeon {
18 struct tmpdungeon tmpdungeon[MAXDUNGEON];
19 struct tmplevel tmplevel[LEV_LIMIT];
20 s_level *final_lev[LEV_LIMIT]; /* corresponding level pointers */
21 struct tmpbranch tmpbranch[BRANCH_LIMIT];
22
23 int start; /* starting index of current dungeon sp levels */
24 int n_levs; /* number of tmplevel entries */
25 int n_brs; /* number of tmpbranch entries */
26 };
27
28 int n_dgns; /* number of dungeons (used here, */
29 /* and mklev.c) */
30 static branch *branches = (branch *) 0; /* dungeon branch list */
31
32 struct lchoice {
33 int idx;
34 schar lev[MAXLINFO];
35 schar playerlev[MAXLINFO];
36 xchar dgn[MAXLINFO];
37 char menuletter;
38 };
39
40 static void FDECL(Fread, (genericptr_t, int, int, dlb *));
41 STATIC_DCL xchar FDECL(dname_to_dnum, (const char *));
42 STATIC_DCL int FDECL(find_branch, (const char *, struct proto_dungeon *));
43 STATIC_DCL mapseen* FDECL(find_level_by_custom_name, (const char *));
44 STATIC_DCL xchar FDECL(parent_dnum, (const char *, struct proto_dungeon *));
45 STATIC_DCL int FDECL(level_range, (XCHAR_P,int,int,int,struct proto_dungeon *,int *));
46 STATIC_DCL xchar FDECL(parent_dlevel, (const char *, struct proto_dungeon *));
47 STATIC_DCL int FDECL(correct_branch_type, (struct tmpbranch *));
48 STATIC_DCL branch *FDECL(add_branch, (int, int, struct proto_dungeon *));
49 STATIC_DCL void FDECL(add_level, (s_level *));
50 STATIC_DCL void FDECL(init_level, (int,int,struct proto_dungeon *));
51 STATIC_DCL int FDECL(possible_places, (int, boolean *, struct proto_dungeon *));
52 STATIC_DCL xchar FDECL(pick_level, (boolean *, int));
53 STATIC_DCL boolean FDECL(place_level, (int, struct proto_dungeon *));
54 #ifdef WIZARD
55 STATIC_DCL const char *FDECL(br_string, (int));
56 STATIC_DCL void FDECL(print_branch, (winid, int, int, int, BOOLEAN_P, struct lchoice *));
57 #endif
58 #ifdef RANDOMIZED_PLANES
59 STATIC_DCL void NDECL(shuffle_planes);
60 #endif
61
62 mapseen *mapseenchn = (struct mapseen *)0;
63 /*STATIC_DCL void FDECL(free_mapseen, (mapseen *));*/
64 STATIC_DCL mapseen *FDECL(load_mapseen, (int));
65 STATIC_DCL void FDECL(save_mapseen, (int, mapseen *));
66 STATIC_DCL mapseen *FDECL(find_mapseen, (d_level *));
67 STATIC_DCL void FDECL(print_mapseen, (winid,mapseen *, boolean, boolean, boolean));
68 STATIC_DCL boolean FDECL(interest_mapseen, (mapseen *));
69 STATIC_DCL char *FDECL(seen_string, (xchar, const char *));
70 STATIC_DCL const char *FDECL(br_string2, (branch *));
71
72 #ifdef DEBUG
73 #define DD dungeons[i]
74 STATIC_DCL void NDECL(dumpit);
75
76 STATIC_OVL void
dumpit()77 dumpit()
78 {
79 int i;
80 s_level *x;
81 branch *br;
82
83 for(i = 0; i < n_dgns; i++) {
84 fprintf(stderr, "\n#%d \"%s\" (%s):\n", i,
85 DD.dname, DD.proto);
86 fprintf(stderr, " num_dunlevs %d, dunlev_ureached %d\n",
87 DD.num_dunlevs, DD.dunlev_ureached);
88 fprintf(stderr, " depth_start %d, ledger_start %d\n",
89 DD.depth_start, DD.ledger_start);
90 fprintf(stderr, " flags:%s%s%s\n",
91 DD.flags.rogue_like ? " rogue_like" : "",
92 DD.flags.maze_like ? " maze_like" : "",
93 DD.flags.hellish ? " hellish" : "");
94 getchar();
95 }
96 fprintf(stderr,"\nSpecial levels:\n");
97 for(x = sp_levchn; x; x = x->next) {
98 fprintf(stderr, "%s (%d): ", x->proto, x->rndlevs);
99 fprintf(stderr, "on %d, %d; ", x->dlevel.dnum, x->dlevel.dlevel);
100 fprintf(stderr, "flags:%s%s%s%s\n",
101 x->flags.rogue_like ? " rogue_like" : "",
102 x->flags.maze_like ? " maze_like" : "",
103 x->flags.hellish ? " hellish" : "",
104 x->flags.town ? " town" : "");
105 getchar();
106 }
107 fprintf(stderr,"\nBranches:\n");
108 for (br = branches; br; br = br->next) {
109 fprintf(stderr, "%d: %s, end1 %d %d, end2 %d %d, %s\n",
110 br->id,
111 br->type == BR_STAIR ? "stair" :
112 br->type == BR_NO_END1 ? "no end1" :
113 br->type == BR_NO_END2 ? "no end2" :
114 br->type == BR_PORTAL ? "portal" :
115 "unknown",
116 br->end1.dnum, br->end1.dlevel,
117 br->end2.dnum, br->end2.dlevel,
118 br->end1_up ? "end1 up" : "end1 down");
119 }
120 getchar();
121 fprintf(stderr,"\nDone\n");
122 getchar();
123 }
124 #endif
125
126 /* Save the dungeon structures. */
127 void
save_dungeon(fd,perform_write,free_data)128 save_dungeon(fd, perform_write, free_data)
129 int fd;
130 boolean perform_write, free_data;
131 {
132 branch *curr, *next;
133 mapseen *curr_ms, *next_ms;
134 int count;
135
136 if (perform_write) {
137 bwrite(fd, (genericptr_t) &n_dgns, sizeof n_dgns);
138 bwrite(fd, (genericptr_t) dungeons, sizeof(dungeon) * (unsigned)n_dgns);
139 bwrite(fd, (genericptr_t) &dungeon_topology, sizeof dungeon_topology);
140 bwrite(fd, (genericptr_t) tune, sizeof tune);
141
142 for (count = 0, curr = branches; curr; curr = curr->next)
143 count++;
144 bwrite(fd, (genericptr_t) &count, sizeof(count));
145
146 for (curr = branches; curr; curr = curr->next)
147 bwrite(fd, (genericptr_t) curr, sizeof (branch));
148
149 count = maxledgerno();
150 bwrite(fd, (genericptr_t) &count, sizeof count);
151 bwrite(fd, (genericptr_t) level_info,
152 (unsigned)count * sizeof (struct linfo));
153 bwrite(fd, (genericptr_t) &inv_pos, sizeof inv_pos);
154
155 for (count = 0, curr_ms = mapseenchn; curr_ms; curr_ms = curr_ms->next)
156 count++;
157 bwrite(fd, (genericptr_t) &count, sizeof(count));
158
159 for (curr_ms = mapseenchn; curr_ms; curr_ms = curr_ms->next)
160 save_mapseen(fd, curr_ms);
161 }
162
163 if (free_data) {
164 for (curr = branches; curr; curr = next) {
165 next = curr->next;
166 free((genericptr_t) curr);
167 }
168 branches = 0;
169 for (curr_ms = mapseenchn; curr_ms; curr_ms = next_ms) {
170 next_ms = curr_ms->next;
171 if (curr_ms->custom)
172 free((genericptr_t)curr_ms->custom);
173 free((genericptr_t) curr_ms);
174 }
175 mapseenchn = 0;
176 }
177 }
178
179 /* Restore the dungeon structures. */
180 void
restore_dungeon(fd)181 restore_dungeon(fd)
182 int fd;
183 {
184 branch *curr, *last;
185 mapseen *curr_ms, *last_ms;
186 int count, i;
187
188 mread(fd, (genericptr_t) &n_dgns, sizeof(n_dgns));
189 mread(fd, (genericptr_t) dungeons, sizeof(dungeon) * (unsigned)n_dgns);
190 mread(fd, (genericptr_t) &dungeon_topology, sizeof dungeon_topology);
191 mread(fd, (genericptr_t) tune, sizeof tune);
192
193 last = branches = (branch *) 0;
194
195 mread(fd, (genericptr_t) &count, sizeof(count));
196 for (i = 0; i < count; i++) {
197 curr = (branch *) alloc(sizeof(branch));
198 mread(fd, (genericptr_t) curr, sizeof(branch));
199 curr->next = (branch *) 0;
200 if (last)
201 last->next = curr;
202 else
203 branches = curr;
204 last = curr;
205 }
206
207 mread(fd, (genericptr_t) &count, sizeof(count));
208 if (count >= MAXLINFO)
209 panic("level information count larger (%d) than allocated size", count);
210 mread(fd, (genericptr_t) level_info, (unsigned)count*sizeof(struct linfo));
211 mread(fd, (genericptr_t) &inv_pos, sizeof inv_pos);
212
213 mread(fd, (genericptr_t) &count, sizeof(count));
214 last_ms = (mapseen *) 0;
215 for (i = 0; i < count; i++) {
216 curr_ms = load_mapseen(fd);
217 curr_ms->next = (mapseen *) 0;
218 if (last_ms)
219 last_ms->next = curr_ms;
220 else
221 mapseenchn = curr_ms;
222 last_ms = curr_ms;
223 }
224 }
225
226 static void
Fread(ptr,size,nitems,stream)227 Fread(ptr, size, nitems, stream)
228 genericptr_t ptr;
229 int size, nitems;
230 dlb *stream;
231 {
232 int cnt;
233
234 if((cnt = dlb_fread(ptr, size, nitems, stream)) != nitems) {
235 panic(
236 "Premature EOF on dungeon description file!\r\nExpected %d bytes - got %d.",
237 (size * nitems), (size * cnt));
238 terminate(EXIT_FAILURE);
239 }
240 }
241
242 STATIC_OVL xchar
dname_to_dnum(s)243 dname_to_dnum(s)
244 const char *s;
245 {
246 xchar i;
247
248 for (i = 0; i < n_dgns; i++)
249 if (!strcmp(dungeons[i].dname, s)) return i;
250
251 panic("Couldn't resolve dungeon number for name \"%s\".", s);
252 /*NOT REACHED*/
253 return (xchar)0;
254 }
255
256 s_level *
find_level(s)257 find_level(s)
258 const char *s;
259 {
260 s_level *curr;
261 for(curr = sp_levchn; curr; curr = curr->next)
262 if (!strcmpi(s, curr->proto)) break;
263 return curr;
264 }
265
266 mapseen *
find_level_by_custom_name(s)267 find_level_by_custom_name(s)
268 const char *s;
269 {
270 mapseen *mptr;
271 for (mptr = mapseenchn; mptr; mptr = mptr->next)
272 if (mptr->custom && !strcmpi(s, mptr->custom)) break;
273 return mptr;
274 }
275
276 #ifdef RANDOMIZED_PLANES
277 /**
278 * Returns the next plane to go to.
279 *
280 * Returns NULL if on Astral Plane or not in endgame.
281 */
282 s_level *
get_next_elemental_plane(lev)283 get_next_elemental_plane(lev)
284 d_level *lev;
285 {
286 if (!In_endgame(lev)) {
287 pline("get_next_elemental_plane not in Endgame.");
288 return (s_level *)0;
289 }
290
291 s_level *curr = find_level("astral"),
292 *plane = (s_level *)0;
293 for (curr = sp_levchn; curr; curr = curr->next) {
294 if (on_level(lev, &(curr->dlevel))) {
295 return plane;
296 }
297 plane = curr;
298 }
299 return (s_level *)0;
300 }
301
302 /**
303 * Returns the first elemental plane of the endgame.
304 */
305 d_level *
get_first_elemental_plane()306 get_first_elemental_plane()
307 {
308 s_level *curr,
309 *dummy = find_level("dummy");
310 for (curr = find_level("astral"); curr; curr = curr->next) {
311 if (curr->next == dummy) {
312 return &curr->dlevel;
313 }
314 }
315 return (d_level *)0;
316 }
317 #endif
318
319 /* Find the branch that links the named dungeon. */
320 STATIC_OVL int
find_branch(s,pd)321 find_branch(s, pd)
322 const char *s; /* dungeon name */
323 struct proto_dungeon *pd;
324 {
325 int i;
326
327 if (pd) {
328 for (i = 0; i < pd->n_brs; i++)
329 if (!strcmp(pd->tmpbranch[i].name, s)) break;
330 if (i == pd->n_brs) panic("find_branch: can't find %s", s);
331 } else {
332 /* support for level tport by name */
333 branch *br;
334 const char *dnam;
335
336 for (br = branches; br; br = br->next) {
337 dnam = dungeons[br->end2.dnum].dname;
338 if (!strcmpi(dnam, s) ||
339 (!strncmpi(dnam, "The ", 4) && !strcmpi(dnam + 4, s)))
340 break;
341 }
342 i = br ? ((ledger_no(&br->end1) << 8) | ledger_no(&br->end2)) : -1;
343 }
344 return i;
345 }
346
347
348 /*
349 * Find the "parent" by searching the prototype branch list for the branch
350 * listing, then figuring out to which dungeon it belongs.
351 */
352 STATIC_OVL xchar
parent_dnum(s,pd)353 parent_dnum(s, pd)
354 const char *s; /* dungeon name */
355 struct proto_dungeon *pd;
356 {
357 int i;
358 xchar pdnum;
359
360 i = find_branch(s, pd);
361 /*
362 * Got branch, now find parent dungeon. Stop if we have reached
363 * "this" dungeon (if we haven't found it by now it is an error).
364 */
365 for (pdnum = 0; strcmp(pd->tmpdungeon[pdnum].name, s); pdnum++)
366 if ((i -= pd->tmpdungeon[pdnum].branches) < 0)
367 return(pdnum);
368
369 panic("parent_dnum: couldn't resolve branch.");
370 /*NOT REACHED*/
371 return (xchar)0;
372 }
373
374 /*
375 * Return a starting point and number of successive positions a level
376 * or dungeon entrance can occupy.
377 *
378 * Note: This follows the acouple (instead of the rcouple) rules for a
379 * negative random component (rand < 0). These rules are found
380 * in dgn_comp.y. The acouple [absolute couple] section says that
381 * a negative random component means from the (adjusted) base to the
382 * end of the dungeon.
383 */
384 STATIC_OVL int
level_range(dgn,base,rand,chain,pd,adjusted_base)385 level_range(dgn, base, rand, chain, pd, adjusted_base)
386 xchar dgn;
387 int base, rand, chain;
388 struct proto_dungeon *pd;
389 int *adjusted_base;
390 {
391 int lmax = dungeons[dgn].num_dunlevs;
392
393 if (chain >= 0) { /* relative to a special level */
394 s_level *levtmp = pd->final_lev[chain];
395 if (!levtmp) panic("level_range: empty chain level!");
396
397 base += levtmp->dlevel.dlevel;
398 } else { /* absolute in the dungeon */
399 /* from end of dungeon */
400 if (base < 0) base = (lmax + base + 1);
401 }
402
403 if (base < 1 || base > lmax)
404 panic("level_range: base value out of range. base: %d, lmax; %d",base,lmax);
405
406 *adjusted_base = base;
407
408 if (rand == -1) { /* from base to end of dungeon */
409 return (lmax - base + 1);
410 } else if (rand) {
411 /* make sure we don't run off the end of the dungeon */
412 return (((base + rand - 1) > lmax) ? lmax-base+1 : rand);
413 } /* else only one choice */
414 return 1;
415 }
416
417 STATIC_OVL xchar
parent_dlevel(s,pd)418 parent_dlevel(s, pd)
419 const char *s;
420 struct proto_dungeon *pd;
421 {
422 int i, j, num, base, dnum = parent_dnum(s, pd);
423 branch *curr;
424
425
426 i = find_branch(s, pd);
427 num = level_range(dnum, pd->tmpbranch[i].lev.base,
428 pd->tmpbranch[i].lev.rand,
429 pd->tmpbranch[i].chain,
430 pd, &base);
431
432 /* KMH -- Try our best to find a level without an existing branch */
433 i = j = rn2(num);
434 do {
435 if (++i >= num) i = 0;
436 for (curr = branches; curr; curr = curr->next)
437 if ((curr->end1.dnum == dnum && curr->end1.dlevel == base+i) ||
438 (curr->end2.dnum == dnum && curr->end2.dlevel == base+i))
439 break;
440 } while (curr && i != j);
441 return (base + i);
442 }
443
444 /* Convert from the temporary branch type to the dungeon branch type. */
445 STATIC_OVL int
correct_branch_type(tbr)446 correct_branch_type(tbr)
447 struct tmpbranch *tbr;
448 {
449 switch (tbr->type) {
450 case TBR_STAIR: return BR_STAIR;
451 case TBR_NO_UP: return tbr->up ? BR_NO_END1 : BR_NO_END2;
452 case TBR_NO_DOWN: return tbr->up ? BR_NO_END2 : BR_NO_END1;
453 case TBR_PORTAL: return BR_PORTAL;
454 }
455 impossible("correct_branch_type: unknown branch type");
456 return BR_STAIR;
457 }
458
459 /*
460 * Add the given branch to the branch list. The branch list is ordered
461 * by end1 dungeon and level followed by end2 dungeon and level. If
462 * extract_first is true, then the branch is already part of the list
463 * but needs to be repositioned.
464 */
465 void
insert_branch(new_branch,extract_first)466 insert_branch(new_branch, extract_first)
467 branch *new_branch;
468 boolean extract_first;
469 {
470 branch *curr, *prev;
471 long new_val, curr_val, prev_val;
472
473 if (extract_first) {
474 for (prev = 0, curr = branches; curr; prev = curr, curr = curr->next)
475 if (curr == new_branch) break;
476
477 if (!curr) panic("insert_branch: not found");
478 if (prev)
479 prev->next = curr->next;
480 else
481 branches = curr->next;
482 }
483 new_branch->next = (branch *) 0;
484
485 /* Convert the branch into a unique number so we can sort them. */
486 #define branch_val(bp) \
487 ((((long)(bp)->end1.dnum * (MAXLEVEL+1) + \
488 (long)(bp)->end1.dlevel) * (MAXDUNGEON+1) * (MAXLEVEL+1)) + \
489 ((long)(bp)->end2.dnum * (MAXLEVEL+1) + (long)(bp)->end2.dlevel))
490
491 /*
492 * Insert the new branch into the correct place in the branch list.
493 */
494 prev = (branch *) 0;
495 prev_val = -1;
496 new_val = branch_val(new_branch);
497 for (curr = branches; curr;
498 prev_val = curr_val, prev = curr, curr = curr->next) {
499 curr_val = branch_val(curr);
500 if (prev_val < new_val && new_val <= curr_val) break;
501 }
502 if (prev) {
503 new_branch->next = curr;
504 prev->next = new_branch;
505 } else {
506 new_branch->next = branches;
507 branches = new_branch;
508 }
509 }
510
511 /* Add a dungeon branch to the branch list. */
512 STATIC_OVL branch *
add_branch(dgn,child_entry_level,pd)513 add_branch(dgn, child_entry_level, pd)
514 int dgn;
515 int child_entry_level;
516 struct proto_dungeon *pd;
517 {
518 static int branch_id = 0;
519 int branch_num;
520 branch *new_branch;
521
522 branch_num = find_branch(dungeons[dgn].dname,pd);
523 new_branch = (branch *) alloc(sizeof(branch));
524 new_branch->next = (branch *) 0;
525 new_branch->id = branch_id++;
526 new_branch->type = correct_branch_type(&pd->tmpbranch[branch_num]);
527 new_branch->end1.dnum = parent_dnum(dungeons[dgn].dname, pd);
528 new_branch->end1.dlevel = parent_dlevel(dungeons[dgn].dname, pd);
529 new_branch->end2.dnum = dgn;
530 new_branch->end2.dlevel = child_entry_level;
531 new_branch->end1_up = pd->tmpbranch[branch_num].up ? TRUE : FALSE;
532
533 insert_branch(new_branch, FALSE);
534 return new_branch;
535 }
536
537 /*
538 * Add new level to special level chain. Insert it in level order with the
539 * other levels in this dungeon. This assumes that we are never given a
540 * level that has a dungeon number less than the dungeon number of the
541 * last entry.
542 */
543 STATIC_OVL void
add_level(new_lev)544 add_level(new_lev)
545 s_level *new_lev;
546 {
547 s_level *prev, *curr;
548
549 prev = (s_level *) 0;
550 for (curr = sp_levchn; curr; curr = curr->next) {
551 if (curr->dlevel.dnum == new_lev->dlevel.dnum &&
552 curr->dlevel.dlevel > new_lev->dlevel.dlevel)
553 break;
554 prev = curr;
555 }
556 if (!prev) {
557 new_lev->next = sp_levchn;
558 sp_levchn = new_lev;
559 } else {
560 new_lev->next = curr;
561 prev->next = new_lev;
562 }
563 }
564
565 STATIC_OVL void
init_level(dgn,proto_index,pd)566 init_level(dgn, proto_index, pd)
567 int dgn, proto_index;
568 struct proto_dungeon *pd;
569 {
570 s_level *new_level;
571 struct tmplevel *tlevel = &pd->tmplevel[proto_index];
572
573 pd->final_lev[proto_index] = (s_level *) 0; /* no "real" level */
574 #ifdef WIZARD
575 if (!wizard)
576 #endif
577 if (tlevel->chance <= rn2(100)) return;
578
579 pd->final_lev[proto_index] = new_level =
580 (s_level *) alloc(sizeof(s_level));
581 /* load new level with data */
582 Strcpy(new_level->proto, tlevel->name);
583 new_level->boneid = tlevel->boneschar;
584 new_level->dlevel.dnum = dgn;
585 new_level->dlevel.dlevel = 0; /* for now */
586
587 new_level->flags.town = !!(tlevel->flags & TOWN);
588 new_level->flags.hellish = !!(tlevel->flags & HELLISH);
589 new_level->flags.maze_like = !!(tlevel->flags & MAZELIKE);
590 new_level->flags.rogue_like = !!(tlevel->flags & ROGUELIKE);
591 new_level->flags.align = ((tlevel->flags & D_ALIGN_MASK) >> 4);
592 if (!new_level->flags.align)
593 new_level->flags.align =
594 ((pd->tmpdungeon[dgn].flags & D_ALIGN_MASK) >> 4);
595
596 new_level->rndlevs = tlevel->rndlevs;
597 new_level->next = (s_level *) 0;
598 }
599
600 STATIC_OVL int
possible_places(idx,map,pd)601 possible_places(idx, map, pd)
602 int idx; /* prototype index */
603 boolean *map; /* array MAXLEVEL+1 in length */
604 struct proto_dungeon *pd;
605 {
606 int i, start, count;
607 s_level *lev = pd->final_lev[idx];
608
609 /* init level possibilities */
610 for (i = 0; i <= MAXLEVEL; i++) map[i] = FALSE;
611
612 /* get base and range and set those entried to true */
613 count = level_range(lev->dlevel.dnum, pd->tmplevel[idx].lev.base,
614 pd->tmplevel[idx].lev.rand,
615 pd->tmplevel[idx].chain,
616 pd, &start);
617 for (i = start; i < start+count; i++)
618 map[i] = TRUE;
619
620 /* mark off already placed levels */
621 for (i = pd->start; i < idx; i++) {
622 if (pd->final_lev[i] && map[pd->final_lev[i]->dlevel.dlevel]) {
623 map[pd->final_lev[i]->dlevel.dlevel] = FALSE;
624 --count;
625 }
626 }
627
628 return count;
629 }
630
631 /* Pick the nth TRUE entry in the given boolean array. */
632 STATIC_OVL xchar
pick_level(map,nth)633 pick_level(map, nth)
634 boolean *map; /* an array MAXLEVEL+1 in size */
635 int nth;
636 {
637 int i;
638 for (i = 1; i <= MAXLEVEL; i++)
639 if (map[i] && !nth--) return (xchar) i;
640 panic("pick_level: ran out of valid levels");
641 return 0;
642 }
643
644 #ifdef DDEBUG
645 static void FDECL(indent,(int));
646
647 static void
indent(d)648 indent(d)
649 int d;
650 {
651 while (d-- > 0) fputs(" ", stderr);
652 }
653 #endif
654
655 /*
656 * Place a level. First, find the possible places on a dungeon map
657 * template. Next pick one. Then try to place the next level. If
658 * sucessful, we're done. Otherwise, try another (and another) until
659 * all possible places have been tried. If all possible places have
660 * been exausted, return false.
661 */
662 STATIC_OVL boolean
place_level(proto_index,pd)663 place_level(proto_index, pd)
664 int proto_index;
665 struct proto_dungeon *pd;
666 {
667 boolean map[MAXLEVEL+1]; /* valid levels are 1..MAXLEVEL inclusive */
668 s_level *lev;
669 int npossible;
670 #ifdef DDEBUG
671 int i;
672 #endif
673
674 if (proto_index == pd->n_levs) return TRUE; /* at end of proto levels */
675
676 lev = pd->final_lev[proto_index];
677
678 /* No level created for this prototype, goto next. */
679 if (!lev) return place_level(proto_index+1, pd);
680
681 npossible = possible_places(proto_index, map, pd);
682
683 for (; npossible; --npossible) {
684 lev->dlevel.dlevel = pick_level(map, rn2(npossible));
685 #ifdef DDEBUG
686 indent(proto_index-pd->start);
687 fprintf(stderr,"%s: trying %d [ ", lev->proto, lev->dlevel.dlevel);
688 for (i = 1; i <= MAXLEVEL; i++)
689 if (map[i]) fprintf(stderr,"%d ", i);
690 fprintf(stderr,"]\n");
691 #endif
692 if (place_level(proto_index+1, pd)) return TRUE;
693 map[lev->dlevel.dlevel] = FALSE; /* this choice didn't work */
694 }
695 #ifdef DDEBUG
696 indent(proto_index-pd->start);
697 fprintf(stderr,"%s: failed\n", lev->proto);
698 #endif
699 return FALSE;
700 }
701
702
703 struct level_map {
704 const char *lev_name;
705 d_level *lev_spec;
706 } level_map[] = {
707 { "advcal", &advcal_level },
708 { "air", &air_level },
709 { "asmodeus", &asmodeus_level },
710 { "astral", &astral_level },
711 { "baalz", &baalzebub_level },
712 { "bigroom", &bigroom_level },
713 { "castle", &stronghold_level },
714 { "earth", &earth_level },
715 { "fakewiz1", &portal_level },
716 { "fire", &fire_level },
717 { "juiblex", &juiblex_level },
718 { "knox", &knox_level },
719 { "nymph", &nymph_level },
720 #ifdef BLACKMARKET
721 { "blkmar", &blackmarket_level },
722 #endif /* BLACKMARKET */
723 { "medusa", &medusa_level },
724 { "oracle", &oracle_level },
725 { "orcus", &orcus_level },
726 #ifdef REINCARNATION
727 { "rogue", &rogue_level },
728 #endif
729 { "sanctum", &sanctum_level },
730 { "valley", &valley_level },
731 { "water", &water_level },
732 { "wizard1", &wiz1_level },
733 { "wizard2", &wiz2_level },
734 { "wizard3", &wiz3_level },
735 #ifdef RECORD_ACHIEVE
736 { "minend", &mineend_level },
737 { "soko1", &sokoend_level },
738 #endif
739 { X_START, &qstart_level },
740 { X_LOCATE, &qlocate_level },
741 { X_GOAL, &nemesis_level },
742 { "minetn", &minetown_level },
743 { "town", &town_level },
744 { "moria3", &moria_level },
745 { "", (d_level *)0 }
746 };
747
748 void
init_dungeons()749 init_dungeons() /* initialize the "dungeon" structs */
750 {
751 dlb *dgn_file;
752 register int i, cl = 0, cb = 0;
753 register s_level *x;
754 struct proto_dungeon pd;
755 struct level_map *lev_map;
756 struct version_info vers_info;
757
758 pd.n_levs = pd.n_brs = 0;
759
760 dgn_file = dlb_fopen_area(DUNGEON_AREA, DUNGEON_FILE, RDBMODE);
761 if (!dgn_file) {
762 char tbuf[BUFSZ];
763 Sprintf(tbuf, "Cannot open dungeon description - \"%s",
764 DUNGEON_FILE);
765 #ifdef DLBRSRC /* using a resource from the executable */
766 Strcat(tbuf, "\" resource!");
767 #else /* using a file or DLB file */
768 # if defined(DLB)
769 Strcat(tbuf, "\" from ");
770 # ifdef PREFIXES_IN_USE
771 Strcat(tbuf, "\n\"");
772 if (fqn_prefix[DATAPREFIX]) Strcat(tbuf, fqn_prefix[DATAPREFIX]);
773 # else
774 Strcat(tbuf, "\"");
775 # endif
776 Strcat(tbuf, DLBFILE);
777 # endif
778 Strcat(tbuf, "\" file!");
779 #endif
780 #ifdef WIN32
781 interject_assistance(1, INTERJECT_PANIC, (genericptr_t)tbuf,
782 (genericptr_t)fqn_prefix[DATAPREFIX]);
783 #endif
784 panic("%s", tbuf);
785 }
786
787 /* validate the data's version against the program's version */
788 Fread((genericptr_t) &vers_info, sizeof vers_info, 1, dgn_file);
789 /* we'd better clear the screen now, since when error messages come from
790 * check_version() they will be printed using pline(), which doesn't
791 * mix with the raw messages that might be already on the screen
792 */
793 if (iflags.window_inited) clear_nhwindow(WIN_MAP);
794 if (!check_version(&vers_info, DUNGEON_FILE, TRUE))
795 panic("Dungeon description not valid.");
796
797 /*
798 * Read in each dungeon and transfer the results to the internal
799 * dungeon arrays.
800 */
801 sp_levchn = (s_level *) 0;
802 Fread((genericptr_t)&n_dgns, sizeof(int), 1, dgn_file);
803 if (n_dgns >= MAXDUNGEON)
804 panic("init_dungeons: too many dungeons");
805
806 for (i = 0; i < n_dgns; i++) {
807 Fread((genericptr_t)&pd.tmpdungeon[i],
808 sizeof(struct tmpdungeon), 1, dgn_file);
809 #ifdef WIZARD
810 if(!wizard)
811 #endif
812 if(pd.tmpdungeon[i].chance && (pd.tmpdungeon[i].chance <= rn2(100))) {
813 int j;
814
815 /* skip over any levels or branches */
816 for(j = 0; j < pd.tmpdungeon[i].levels; j++)
817 Fread((genericptr_t)&pd.tmplevel[cl], sizeof(struct tmplevel),
818 1, dgn_file);
819
820 for(j = 0; j < pd.tmpdungeon[i].branches; j++)
821 Fread((genericptr_t)&pd.tmpbranch[cb],
822 sizeof(struct tmpbranch), 1, dgn_file);
823 n_dgns--; i--;
824 continue;
825 }
826
827 Strcpy(dungeons[i].dname, pd.tmpdungeon[i].name);
828 Strcpy(dungeons[i].proto, pd.tmpdungeon[i].protoname);
829 dungeons[i].boneid = pd.tmpdungeon[i].boneschar;
830
831 if(pd.tmpdungeon[i].lev.rand)
832 dungeons[i].num_dunlevs = (xchar)rn1(pd.tmpdungeon[i].lev.rand,
833 pd.tmpdungeon[i].lev.base);
834 else dungeons[i].num_dunlevs = (xchar)pd.tmpdungeon[i].lev.base;
835
836 if(!i) {
837 dungeons[i].ledger_start = 0;
838 dungeons[i].depth_start = 1;
839 dungeons[i].dunlev_ureached = 1;
840 } else {
841 dungeons[i].ledger_start = dungeons[i-1].ledger_start +
842 dungeons[i-1].num_dunlevs;
843 dungeons[i].dunlev_ureached = 0;
844 }
845
846 dungeons[i].flags.hellish = !!(pd.tmpdungeon[i].flags & HELLISH);
847 dungeons[i].flags.maze_like = !!(pd.tmpdungeon[i].flags & MAZELIKE);
848 dungeons[i].flags.rogue_like = !!(pd.tmpdungeon[i].flags & ROGUELIKE);
849 dungeons[i].flags.align = ((pd.tmpdungeon[i].flags & D_ALIGN_MASK) >> 4);
850 /*
851 * Set the entry level for this dungeon. The pd.tmpdungeon entry
852 * value means:
853 * < 0 from bottom (-1 == bottom level)
854 * 0 default (top)
855 * > 0 actual level (1 = top)
856 *
857 * Note that the entry_lev field in the dungeon structure is
858 * redundant. It is used only here and in print_dungeon().
859 */
860 if (pd.tmpdungeon[i].entry_lev < 0) {
861 dungeons[i].entry_lev = dungeons[i].num_dunlevs +
862 pd.tmpdungeon[i].entry_lev + 1;
863 if (dungeons[i].entry_lev <= 0) dungeons[i].entry_lev = 1;
864 } else if (pd.tmpdungeon[i].entry_lev > 0) {
865 dungeons[i].entry_lev = pd.tmpdungeon[i].entry_lev;
866 if (dungeons[i].entry_lev > dungeons[i].num_dunlevs)
867 dungeons[i].entry_lev = dungeons[i].num_dunlevs;
868 } else { /* default */
869 dungeons[i].entry_lev = 1; /* defaults to top level */
870 }
871
872 if (i) { /* set depth */
873 branch *br;
874 schar from_depth;
875 boolean from_up;
876
877 br = add_branch(i, dungeons[i].entry_lev, &pd);
878
879 /* Get the depth of the connecting end. */
880 if (br->end1.dnum == i) {
881 from_depth = depth(&br->end2);
882 from_up = !br->end1_up;
883 } else {
884 from_depth = depth(&br->end1);
885 from_up = br->end1_up;
886 }
887
888 /*
889 * Calculate the depth of the top of the dungeon via
890 * its branch. First, the depth of the entry point:
891 *
892 * depth of branch from "parent" dungeon
893 * + -1 or 1 depending on a up or down stair or
894 * 0 if portal
895 *
896 * Followed by the depth of the top of the dungeon:
897 *
898 * - (entry depth - 1)
899 *
900 * We'll say that portals stay on the same depth.
901 */
902 dungeons[i].depth_start = from_depth
903 + (br->type == BR_PORTAL ? 0 :
904 (from_up ? -1 : 1))
905 - (dungeons[i].entry_lev - 1);
906 }
907
908 /* this is redundant - it should have been flagged by dgn_comp */
909 if(dungeons[i].num_dunlevs > MAXLEVEL)
910 dungeons[i].num_dunlevs = MAXLEVEL;
911
912 pd.start = pd.n_levs; /* save starting point */
913 pd.n_levs += pd.tmpdungeon[i].levels;
914 if (pd.n_levs > LEV_LIMIT)
915 panic("init_dungeon: too many special levels");
916 /*
917 * Read in the prototype special levels. Don't add generated
918 * special levels until they are all placed.
919 */
920 for(; cl < pd.n_levs; cl++) {
921 Fread((genericptr_t)&pd.tmplevel[cl],
922 sizeof(struct tmplevel), 1, dgn_file);
923 init_level(i, cl, &pd);
924 }
925 /*
926 * Recursively place the generated levels for this dungeon. This
927 * routine will attempt all possible combinations before giving
928 * up.
929 */
930 if (!place_level(pd.start, &pd))
931 panic("init_dungeon: couldn't place levels");
932 #ifdef DDEBUG
933 fprintf(stderr, "--- end of dungeon %d ---\n", i);
934 fflush(stderr);
935 getchar();
936 #endif
937 for (; pd.start < pd.n_levs; pd.start++)
938 if (pd.final_lev[pd.start]) add_level(pd.final_lev[pd.start]);
939
940
941 pd.n_brs += pd.tmpdungeon[i].branches;
942 if (pd.n_brs > BRANCH_LIMIT)
943 panic("init_dungeon: too many branches");
944 for(; cb < pd.n_brs; cb++)
945 Fread((genericptr_t)&pd.tmpbranch[cb],
946 sizeof(struct tmpbranch), 1, dgn_file);
947 }
948 (void) dlb_fclose(dgn_file);
949
950 for (i = 0; i < 5; i++) tune[i] = 'A' + rn2(7);
951 tune[5] = 0;
952
953 /*
954 * Find most of the special levels and dungeons so we can access their
955 * locations quickly.
956 */
957 for (lev_map = level_map; lev_map->lev_name[0]; lev_map++) {
958 x = find_level(lev_map->lev_name);
959 if (x) {
960 assign_level(lev_map->lev_spec, &x->dlevel);
961 if (!strncmp(lev_map->lev_name, "x-", 2)) {
962 /* This is where the name substitution on the
963 * levels of the quest dungeon occur.
964 */
965 Sprintf(x->proto, "%s%s", urole.filecode, &lev_map->lev_name[1]);
966 } else if (lev_map->lev_spec == &knox_level) {
967 branch *br;
968 /*
969 * Kludge to allow floating Knox entrance. We
970 * specify a floating entrance by the fact that
971 * its entrance (end1) has a bogus dnum, namely
972 * n_dgns.
973 */
974 for (br = branches; br; br = br->next)
975 if (on_level(&br->end2, &knox_level)) break;
976
977 if (br) br->end1.dnum = n_dgns;
978 /* adjust the branch's position on the list */
979 insert_branch(br, TRUE);
980 #ifdef ADVENT_CALENDAR
981 } else if (lev_map->lev_spec == &advcal_level) {
982 branch *br;
983 /*
984 * Kludge to allow floating Knox entrance. We
985 * specify a floating entrance by the fact that
986 * its entrance (end1) has a bogus dnum, namely
987 * n_dgns.
988 */
989 for (br = branches; br; br = br->next)
990 if (on_level(&br->end2, &advcal_level)) break;
991
992 if (br) br->end1.dnum = n_dgns;
993 /* adjust the branch's position on the list */
994 insert_branch(br, TRUE);
995 #endif
996 }
997 }
998 }
999 /*
1000 * I hate hardwiring these names. :-(
1001 */
1002 quest_dnum = dname_to_dnum("The Quest");
1003 sokoban_dnum = dname_to_dnum("Sokoban");
1004 mines_dnum = dname_to_dnum("The Gnomish Mines");
1005 tower_dnum = dname_to_dnum("Vlad's Tower");
1006 mall_dnum = dname_to_dnum("Town");
1007 /*
1008 #ifdef BLACKMARKET
1009 blackmarket_dnum = dname_to_dnum("The Black Market");
1010 #endif
1011 */
1012 sheol_dnum = dname_to_dnum("Sheol");
1013
1014 /* one special fixup for dummy surface level */
1015 if ((x = find_level("dummy")) != 0) {
1016 i = x->dlevel.dnum;
1017 /* the code above puts earth one level above dungeon level #1,
1018 making the dummy level overlay level 1; but the whole reason
1019 for having the dummy level is to make earth have depth -1
1020 instead of 0, so adjust the start point to shift endgame up */
1021 if (dunlevs_in_dungeon(&x->dlevel) > 1 - dungeons[i].depth_start)
1022 dungeons[i].depth_start -= 1;
1023 /* TO DO: strip "dummy" out all the way here,
1024 so that it's hidden from <ctrl/O> feedback. */
1025 }
1026
1027 #ifdef RANDOMIZED_PLANES
1028 shuffle_planes();
1029 #endif
1030
1031 #ifdef DEBUG
1032 dumpit();
1033 #endif
1034 }
1035
1036 #ifdef RANDOMIZED_PLANES
1037 void
shuffle_planes()1038 shuffle_planes() /* randomizes order of elemental planes */
1039 {
1040 /* original order */
1041 s_level *dummy = find_level("dummy") ,
1042 *earth = find_level("earth") ,
1043 *air = find_level("air"),
1044 *fire = find_level("fire"),
1045 *water = find_level("water"),
1046 *astral = find_level("astral");
1047 s_level *array[] = { water, fire, air, earth };
1048 int j, pos;
1049 s_level *tmp;
1050
1051 /* Fisher-Yates shuffle aka Knuth shuffle */
1052 for(j = 3; j > 0; j--) {
1053 pos = rn2(j+1);
1054 tmp = array[pos]; array[pos] = array[j]; array[j] = tmp;
1055 }
1056
1057 /* reorder planes */
1058 astral->next = array[3];
1059 for(j = 3; j > 0; j--) {
1060 array[j]->next = array[j-1];
1061 }
1062 array[0]->next = dummy;
1063 }
1064 #endif
1065
1066 xchar
dunlev(lev)1067 dunlev(lev) /* return the level number for lev in *this* dungeon */
1068 d_level *lev;
1069 {
1070 return(lev->dlevel);
1071 }
1072
1073 xchar
dunlevs_in_dungeon(lev)1074 dunlevs_in_dungeon(lev) /* return the lowest level number for *this* dungeon*/
1075 d_level *lev;
1076 {
1077 return(dungeons[lev->dnum].num_dunlevs);
1078 }
1079
1080 xchar
deepest_lev_reached(noquest)1081 deepest_lev_reached(noquest) /* return the lowest level explored in the game*/
1082 boolean noquest;
1083 {
1084 /* this function is used for three purposes: to provide a factor
1085 * of difficulty in monster generation; to provide a factor of
1086 * difficulty in experience calculations (botl.c and end.c); and
1087 * to insert the deepest level reached in the game in the topten
1088 * display. the 'noquest' arg switch is required for the latter.
1089 *
1090 * from the player's point of view, going into the Quest is _not_
1091 * going deeper into the dungeon -- it is going back "home", where
1092 * the dungeon starts at level 1. given the setup in dungeon.def,
1093 * the depth of the Quest (thought of as starting at level 1) is
1094 * never lower than the level of entry into the Quest, so we exclude
1095 * the Quest from the topten "deepest level reached" display
1096 * calculation. _However_ the Quest is a difficult dungeon, so we
1097 * include it in the factor of difficulty calculations.
1098 */
1099 register int i;
1100 d_level tmp;
1101 register schar ret = 0;
1102
1103 for(i = 0; i < n_dgns; i++) {
1104 if((tmp.dlevel = dungeons[i].dunlev_ureached) == 0) continue;
1105 if(!strcmp(dungeons[i].dname, "The Quest") && noquest) continue;
1106
1107 tmp.dnum = i;
1108 if(depth(&tmp) > ret) ret = depth(&tmp);
1109 }
1110 return((xchar) ret);
1111 }
1112
1113 /* return a bookkeeping level number for purpose of comparisons and
1114 * save/restore */
1115 xchar
ledger_no(lev)1116 ledger_no(lev)
1117 d_level *lev;
1118 {
1119 return((xchar)(lev->dlevel + dungeons[lev->dnum].ledger_start));
1120 }
1121
1122 /*
1123 * The last level in the bookkeeping list of level is the bottom of the last
1124 * dungeon in the dungeons[] array.
1125 *
1126 * Maxledgerno() -- which is the max number of levels in the bookkeeping
1127 * list, should not be confused with dunlevs_in_dungeon(lev) -- which
1128 * returns the max number of levels in lev's dungeon, and both should
1129 * not be confused with deepest_lev_reached() -- which returns the lowest
1130 * depth visited by the player.
1131 */
1132 xchar
maxledgerno()1133 maxledgerno()
1134 {
1135 return (xchar) (dungeons[n_dgns-1].ledger_start +
1136 dungeons[n_dgns-1].num_dunlevs);
1137 }
1138
1139 /* return the dungeon that this ledgerno exists in */
1140 xchar
ledger_to_dnum(ledgerno)1141 ledger_to_dnum(ledgerno)
1142 xchar ledgerno;
1143 {
1144 register int i;
1145
1146 /* find i such that (i->base + 1) <= ledgerno <= (i->base + i->count) */
1147 for (i = 0; i < n_dgns; i++)
1148 if (dungeons[i].ledger_start < ledgerno &&
1149 ledgerno <= dungeons[i].ledger_start + dungeons[i].num_dunlevs)
1150 return (xchar)i;
1151
1152 panic("level number out of range [ledger_to_dnum(%d)]", (int)ledgerno);
1153 /*NOT REACHED*/
1154 return (xchar)0;
1155 }
1156
1157 /* return the level of the dungeon this ledgerno exists in */
1158 xchar
ledger_to_dlev(ledgerno)1159 ledger_to_dlev(ledgerno)
1160 xchar ledgerno;
1161 {
1162 return((xchar)(ledgerno - dungeons[ledger_to_dnum(ledgerno)].ledger_start));
1163 }
1164
1165 /* returns the depth of a level, in floors below the surface */
1166 /* (note levels in different dungeons can have the same depth). */
1167 schar
depth(lev)1168 depth(lev)
1169 d_level *lev;
1170 {
1171 return((schar)( dungeons[lev->dnum].depth_start + lev->dlevel - 1));
1172 }
1173
1174 boolean
on_level(lev1,lev2)1175 on_level(lev1, lev2) /* are "lev1" and "lev2" actually the same? */
1176 d_level *lev1, *lev2;
1177 {
1178 return((boolean)((lev1->dnum == lev2->dnum) && (lev1->dlevel == lev2->dlevel)));
1179 }
1180
1181 /* is this level referenced in the special level chain? */
1182 s_level *
Is_special(lev)1183 Is_special(lev)
1184 d_level *lev;
1185 {
1186 s_level *levtmp;
1187
1188 for (levtmp = sp_levchn; levtmp; levtmp = levtmp->next)
1189 if (on_level(lev, &levtmp->dlevel)) return(levtmp);
1190
1191 return((s_level *)0);
1192 }
1193
1194 /*
1195 * Is this a multi-dungeon branch level? If so, return a pointer to the
1196 * branch. Otherwise, return null.
1197 */
1198 branch *
Is_branchlev(lev)1199 Is_branchlev(lev)
1200 d_level *lev;
1201 {
1202 branch *curr;
1203
1204 for (curr = branches; curr; curr = curr->next) {
1205 if (on_level(lev, &curr->end1) || on_level(lev, &curr->end2))
1206 return curr;
1207 }
1208 return (branch *) 0;
1209 }
1210
1211 /* goto the next level (or appropriate dungeon) */
1212 void
next_level(at_stairs)1213 next_level(at_stairs)
1214 boolean at_stairs;
1215 {
1216 if (at_stairs && u.ux == sstairs.sx && u.uy == sstairs.sy) {
1217 /* Taking a down dungeon branch. */
1218 goto_level(&sstairs.tolev, at_stairs, FALSE, FALSE);
1219 } else {
1220 /* Going down a stairs or jump in a trap door. */
1221 d_level newlevel;
1222
1223 newlevel.dnum = u.uz.dnum;
1224 newlevel.dlevel = u.uz.dlevel + 1;
1225 goto_level(&newlevel, at_stairs, !at_stairs, FALSE);
1226 }
1227 }
1228
1229 /* goto the previous level (or appropriate dungeon) */
1230 void
prev_level(at_stairs)1231 prev_level(at_stairs)
1232 boolean at_stairs;
1233 {
1234 if (at_stairs && u.ux == sstairs.sx && u.uy == sstairs.sy) {
1235 /* Taking an up dungeon branch. */
1236 /* KMH -- Upwards branches are okay if not level 1 */
1237 /* (Just make sure it doesn't go above depth 1) */
1238 if(!u.uz.dnum && u.uz.dlevel == 1 && !u.uhave.amulet) done(ESCAPED);
1239 else goto_level(&sstairs.tolev, at_stairs, FALSE, FALSE);
1240 } else {
1241 /* Going up a stairs or rising through the ceiling. */
1242 d_level newlevel;
1243 newlevel.dnum = u.uz.dnum;
1244 newlevel.dlevel = u.uz.dlevel - 1;
1245 goto_level(&newlevel, at_stairs, FALSE, FALSE);
1246 }
1247 }
1248
1249 void
u_on_newpos(x,y)1250 u_on_newpos(x, y)
1251 int x, y;
1252 {
1253 u.ux = x;
1254 u.uy = y;
1255 #ifdef CLIPPING
1256 cliparound(u.ux, u.uy);
1257 #endif
1258 #ifdef STEED
1259 /* ridden steed always shares hero's location */
1260 if (u.usteed) u.usteed->mx = u.ux, u.usteed->my = u.uy;
1261 #endif
1262 }
1263
1264 void
u_on_sstairs()1265 u_on_sstairs() { /* place you on the special staircase */
1266
1267 if (sstairs.sx) {
1268 u_on_newpos(sstairs.sx, sstairs.sy);
1269 } else {
1270 /* code stolen from goto_level */
1271 int trycnt = 0;
1272 xchar x, y;
1273 #ifdef DEBUG
1274 pline("u_on_sstairs: picking random spot");
1275 #endif
1276 #define badspot(x,y) ((levl[x][y].typ != ROOM && levl[x][y].typ != CORR) || MON_AT(x, y))
1277 do {
1278 x = rnd(COLNO-1);
1279 y = rn2(ROWNO);
1280 if (!badspot(x, y)) {
1281 u_on_newpos(x, y);
1282 return;
1283 }
1284 } while (++trycnt <= 500);
1285 panic("u_on_sstairs: could not relocate player!");
1286 #undef badspot
1287 }
1288 }
1289
1290 void
u_on_upstairs()1291 u_on_upstairs() /* place you on upstairs (or special equivalent) */
1292 {
1293 if (xupstair) {
1294 u_on_newpos(xupstair, yupstair);
1295 } else
1296 u_on_sstairs();
1297 }
1298
1299 void
u_on_dnstairs()1300 u_on_dnstairs() /* place you on dnstairs (or special equivalent) */
1301 {
1302 if (xdnstair) {
1303 u_on_newpos(xdnstair, ydnstair);
1304 } else
1305 u_on_sstairs();
1306 }
1307
1308 boolean
On_stairs(x,y)1309 On_stairs(x, y)
1310 xchar x, y;
1311 {
1312 return((boolean)((x == xupstair && y == yupstair) ||
1313 (x == xdnstair && y == ydnstair) ||
1314 (x == xdnladder && y == ydnladder) ||
1315 (x == xupladder && y == yupladder) ||
1316 (x == sstairs.sx && y == sstairs.sy)));
1317 }
1318
1319 boolean
Is_botlevel(lev)1320 Is_botlevel(lev)
1321 d_level *lev;
1322 {
1323 return((boolean)(lev->dlevel == dungeons[lev->dnum].num_dunlevs));
1324 }
1325
1326 boolean
Can_dig_down(lev)1327 Can_dig_down(lev)
1328 d_level *lev;
1329 {
1330 return((boolean)(!level.flags.hardfloor
1331 && !Is_botlevel(lev) && !Invocation_lev(lev)));
1332 }
1333
1334 /*
1335 * Like Can_dig_down (above), but also allows falling through on the
1336 * stronghold level. Normally, the bottom level of a dungeon resists
1337 * both digging and falling.
1338 */
1339 boolean
Can_fall_thru(lev)1340 Can_fall_thru(lev)
1341 d_level *lev;
1342 {
1343 return((boolean)(Can_dig_down(lev) || Is_stronghold(lev)));
1344 }
1345
1346 /*
1347 * True if one can rise up a level (e.g. cursed gain level).
1348 * This happens on intermediate dungeon levels or on any top dungeon
1349 * level that has a stairwell style branch to the next higher dungeon.
1350 * Checks for amulets and such must be done elsewhere.
1351 */
1352 boolean
Can_rise_up(x,y,lev)1353 Can_rise_up(x, y, lev)
1354 int x, y;
1355 d_level *lev;
1356 {
1357 /* can't rise up from inside the top of the Wizard's tower */
1358 /* KMH -- or in sokoban */
1359 if (In_endgame(lev) || In_sokoban(lev) ||
1360 (Is_wiz1_level(lev) && In_W_tower(x, y, lev)))
1361 return FALSE;
1362 return (boolean)(lev->dlevel > 1 ||
1363 (dungeons[lev->dnum].entry_lev == 1 && ledger_no(lev) != 1 &&
1364 sstairs.sx && sstairs.up));
1365 }
1366
1367 /*
1368 * It is expected that the second argument of get_level is a depth value,
1369 * either supplied by the user (teleport control) or randomly generated.
1370 * But more than one level can be at the same depth. If the target level
1371 * is "above" the present depth location, get_level must trace "up" from
1372 * the player's location (through the ancestors dungeons) the dungeon
1373 * within which the target level is located. With only one exception
1374 * which does not pass through this routine (see level_tele), teleporting
1375 * "down" is confined to the current dungeon. At present, level teleport
1376 * in dungeons that build up is confined within them.
1377 */
1378 void
get_level(newlevel,levnum)1379 get_level(newlevel, levnum)
1380 d_level *newlevel;
1381 int levnum;
1382 {
1383 branch *br;
1384 xchar dgn = u.uz.dnum;
1385
1386 if (levnum <= 0) {
1387 /* can only currently happen in endgame */
1388 levnum = u.uz.dlevel;
1389 } else if (levnum > dungeons[dgn].depth_start
1390 + dungeons[dgn].num_dunlevs - 1) {
1391 /* beyond end of dungeon, jump to last level */
1392 levnum = dungeons[dgn].num_dunlevs;
1393 } else {
1394 /* The desired level is in this dungeon or a "higher" one. */
1395
1396 /*
1397 * Branch up the tree until we reach a dungeon that contains the
1398 * levnum.
1399 */
1400 if (levnum < dungeons[dgn].depth_start) {
1401
1402 do {
1403 /*
1404 * Find the parent dungeon of this dungeon.
1405 *
1406 * This assumes that end2 is always the "child" and it is
1407 * unique.
1408 */
1409 for (br = branches; br; br = br->next)
1410 if (br->end2.dnum == dgn) break;
1411 if (!br)
1412 panic("get_level: can't find parent dungeon");
1413
1414 dgn = br->end1.dnum;
1415 } while (levnum < dungeons[dgn].depth_start);
1416 }
1417
1418 /* We're within the same dungeon; calculate the level. */
1419 levnum = levnum - dungeons[dgn].depth_start + 1;
1420 }
1421
1422 newlevel->dnum = dgn;
1423 newlevel->dlevel = levnum;
1424 }
1425
1426 boolean
In_quest(lev)1427 In_quest(lev) /* are you in the quest dungeon? */
1428 d_level *lev;
1429 {
1430 return((boolean)(lev->dnum == quest_dnum));
1431 }
1432
1433 boolean
In_mines(lev)1434 In_mines(lev) /* are you in the mines dungeon? */
1435 d_level *lev;
1436 {
1437 return((boolean)(lev->dnum == mines_dnum));
1438 }
1439
1440 boolean
In_sheol(lev)1441 In_sheol(lev) /* are you in Sheol? */
1442 d_level *lev;
1443 {
1444 return((boolean)(lev->dnum == sheol_dnum));
1445 }
1446
1447 /*
1448 * Return the branch for the given dungeon.
1449 *
1450 * This function assumes:
1451 * + This is not called with "Dungeons of Doom".
1452 * + There is only _one_ branch to a given dungeon.
1453 * + Field end2 is the "child" dungeon.
1454 */
1455 branch *
dungeon_branch(s)1456 dungeon_branch(s)
1457 const char *s;
1458 {
1459 branch *br;
1460 xchar dnum;
1461
1462 dnum = dname_to_dnum(s);
1463
1464 /* Find the branch that connects to dungeon i's branch. */
1465 for (br = branches; br; br = br->next)
1466 if (br->end2.dnum == dnum) break;
1467
1468 if (!br) panic("dgn_entrance: can't find entrance to %s", s);
1469
1470 return br;
1471 }
1472
1473 /*
1474 * This returns true if the hero is on the same level as the entrance to
1475 * the named dungeon.
1476 *
1477 * Called from do.c and mklev.c.
1478 *
1479 * Assumes that end1 is always the "parent".
1480 */
1481 boolean
at_dgn_entrance(s)1482 at_dgn_entrance(s)
1483 const char *s;
1484 {
1485 branch *br;
1486
1487 br = dungeon_branch(s);
1488 return((boolean)(on_level(&u.uz, &br->end1) ? TRUE : FALSE));
1489 }
1490
1491 boolean
In_V_tower(lev)1492 In_V_tower(lev) /* is `lev' part of Vlad's tower? */
1493 d_level *lev;
1494 {
1495 return((boolean)(lev->dnum == tower_dnum));
1496 }
1497
1498 boolean
On_W_tower_level(lev)1499 On_W_tower_level(lev) /* is `lev' a level containing the Wizard's tower? */
1500 d_level *lev;
1501 {
1502 return (boolean)(Is_wiz1_level(lev) ||
1503 Is_wiz2_level(lev) ||
1504 Is_wiz3_level(lev));
1505 }
1506
1507 boolean
In_W_tower(x,y,lev)1508 In_W_tower(x, y, lev) /* is <x,y> of `lev' inside the Wizard's tower? */
1509 int x, y;
1510 d_level *lev;
1511 {
1512 if (!On_W_tower_level(lev)) return FALSE;
1513 /*
1514 * Both of the exclusion regions for arriving via level teleport
1515 * (from above or below) define the tower's boundary.
1516 * assert( updest.nIJ == dndest.nIJ for I={l|h},J={x|y} );
1517 */
1518 if (dndest.nlx > 0)
1519 return (boolean)within_bounded_area(x, y, dndest.nlx, dndest.nly,
1520 dndest.nhx, dndest.nhy);
1521 else
1522 impossible("No boundary for Wizard's Tower?");
1523 return FALSE;
1524 }
1525
1526 boolean
In_hell(lev)1527 In_hell(lev) /* are you in one of the Hell levels? */
1528 d_level *lev;
1529 {
1530 return((boolean)(dungeons[lev->dnum].flags.hellish));
1531 }
1532
1533 void
find_hell(lev)1534 find_hell(lev) /* sets *lev to be the gateway to Gehennom... */
1535 d_level *lev;
1536 {
1537 lev->dnum = valley_level.dnum;
1538 lev->dlevel = 1;
1539 }
1540
1541 void
goto_hell(at_stairs,falling)1542 goto_hell(at_stairs, falling) /* go directly to hell... */
1543 boolean at_stairs, falling;
1544 {
1545 d_level lev;
1546
1547 find_hell(&lev);
1548 goto_level(&lev, at_stairs, falling, FALSE);
1549 }
1550
1551 void
assign_level(dest,src)1552 assign_level(dest, src) /* equivalent to dest = source */
1553 d_level *dest, *src;
1554 {
1555 dest->dnum = src->dnum;
1556 dest->dlevel = src->dlevel;
1557 }
1558
1559 void
assign_rnd_level(dest,src,range)1560 assign_rnd_level(dest, src, range) /* dest = src + rn1(range) */
1561 d_level *dest, *src;
1562 int range;
1563 {
1564 dest->dnum = src->dnum;
1565 dest->dlevel = src->dlevel + ((range > 0) ? rnd(range) : -rnd(-range)) ;
1566
1567 if(dest->dlevel > dunlevs_in_dungeon(dest))
1568 dest->dlevel = dunlevs_in_dungeon(dest);
1569 else if(dest->dlevel < 1)
1570 dest->dlevel = 1;
1571 }
1572
1573 int
induced_align(pct)1574 induced_align(pct)
1575 int pct;
1576 {
1577 s_level *lev = Is_special(&u.uz);
1578 aligntyp al;
1579
1580 if (lev && lev->flags.align)
1581 if(rn2(100) < pct) return(lev->flags.align);
1582
1583 if(dungeons[u.uz.dnum].flags.align)
1584 if(rn2(100) < pct) return(dungeons[u.uz.dnum].flags.align);
1585
1586 al = rn2(3) - 1;
1587 return(Align2amask(al));
1588 }
1589
1590 boolean
Invocation_lev(lev)1591 Invocation_lev(lev)
1592 d_level *lev;
1593 {
1594 return((boolean)(In_hell(lev) && !In_sheol(lev) &&
1595 lev->dlevel == (dungeons[lev->dnum].num_dunlevs - 1)));
1596 }
1597
1598 /* use instead of depth() wherever a degree of difficulty is made
1599 * dependent on the location in the dungeon (eg. monster creation).
1600 */
1601 xchar
level_difficulty()1602 level_difficulty()
1603 {
1604 if (In_endgame(&u.uz))
1605 return((xchar)(depth(&sanctum_level) + u.ulevel/2));
1606 else
1607 if (u.uhave.amulet)
1608 return(deepest_lev_reached(FALSE));
1609 else
1610 return((xchar) depth(&u.uz));
1611 }
1612
1613 /* Take one word and try to match it to a level.
1614 * Recognized levels are as shown by print_dungeon().
1615 */
1616 schar
lev_by_name(nam)1617 lev_by_name(nam)
1618 const char *nam;
1619 {
1620 schar lev = 0;
1621 s_level *slev;
1622 mapseen *mseen;
1623 d_level dlev;
1624 const char *p;
1625 int idx, idxtoo;
1626 char buf[BUFSZ];
1627
1628 /* look at the player's custom level annotations first */
1629 if ((mseen = find_level_by_custom_name(nam)) != 0) {
1630 dlev = mseen->lev;
1631 } else {
1632 /* no matching annotation, check whether they used a name we know */
1633
1634 /* allow strings like "the oracle level" to find "oracle" */
1635 if (!strncmpi(nam, "the ", 4)) nam += 4;
1636 if ((p = strstri(nam, " level")) != 0 && p == eos((char*)nam) - 6) {
1637 nam = strcpy(buf, nam);
1638 *(eos(buf) - 6) = '\0';
1639 }
1640 /* hell is the old name, and wouldn't match; gehennom would match its
1641 branch, yielding the castle level instead of the valley of the dead */
1642 if (!strcmpi(nam, "gehennom") || !strcmpi(nam, "hell")) {
1643 if (In_V_tower(&u.uz)) nam = " to Vlad's tower"; /* branch to... */
1644 else nam = "valley";
1645 }
1646
1647 if ((slev = find_level(nam)) != 0)
1648 dlev = slev->dlevel;
1649 }
1650
1651 if (mseen || slev) {
1652 /* found a match, see if it's reachable from here */
1653 idx = ledger_no(&dlev);
1654 if ((dlev.dnum == u.uz.dnum ||
1655 /* within same branch, or else main dungeon <-> gehennom */
1656 (u.uz.dnum == valley_level.dnum &&
1657 dlev.dnum == medusa_level.dnum) ||
1658 (u.uz.dnum == medusa_level.dnum &&
1659 dlev.dnum == valley_level.dnum)) &&
1660 ( /* either wizard mode or else seen and not forgotten */
1661 #ifdef WIZARD
1662 wizard ||
1663 #endif
1664 (level_info[idx].flags & (FORGOTTEN|VISITED)) == VISITED)) {
1665 lev = depth(&dlev);
1666 }
1667 } else { /* not a specific level; try branch names */
1668 idx = find_branch(nam, (struct proto_dungeon *)0);
1669 /* "<branch> to Xyzzy" */
1670 if (idx < 0 && (p = strstri(nam, " to ")) != 0)
1671 idx = find_branch(p + 4, (struct proto_dungeon *)0);
1672
1673 if (idx >= 0) {
1674 idxtoo = (idx >> 8) & 0x00FF;
1675 idx &= 0x00FF;
1676 if ( /* either wizard mode, or else _both_ sides of branch seen */
1677 #ifdef WIZARD
1678 wizard ||
1679 #endif
1680 ((level_info[idx].flags & (FORGOTTEN|VISITED)) == VISITED &&
1681 (level_info[idxtoo].flags & (FORGOTTEN|VISITED)) == VISITED)) {
1682 if (ledger_to_dnum(idxtoo) == u.uz.dnum) idx = idxtoo;
1683 dlev.dnum = ledger_to_dnum(idx);
1684 dlev.dlevel = ledger_to_dlev(idx);
1685 lev = depth(&dlev);
1686 }
1687 }
1688 }
1689 return lev;
1690 }
1691
1692 #ifdef WIZARD
1693
1694 /* Convert a branch type to a string usable by print_dungeon(). */
1695 STATIC_OVL const char *
br_string(type)1696 br_string(type)
1697 int type;
1698 {
1699 switch (type) {
1700 case BR_PORTAL: return "Portal";
1701 case BR_NO_END1: return "Connection";
1702 case BR_NO_END2: return "One way stair";
1703 case BR_STAIR: return "Stair";
1704 }
1705 return " (unknown)";
1706 }
1707
1708 /* Print all child branches between the lower and upper bounds. */
1709 STATIC_OVL void
print_branch(win,dnum,lower_bound,upper_bound,bymenu,lchoices)1710 print_branch(win, dnum, lower_bound, upper_bound, bymenu, lchoices)
1711 winid win;
1712 int dnum;
1713 int lower_bound;
1714 int upper_bound;
1715 boolean bymenu;
1716 struct lchoice *lchoices;
1717 {
1718 branch *br;
1719 char buf[BUFSZ];
1720 anything any;
1721
1722 /* This assumes that end1 is the "parent". */
1723 for (br = branches; br; br = br->next) {
1724 if (br->end1.dnum == dnum && lower_bound < br->end1.dlevel &&
1725 br->end1.dlevel <= upper_bound) {
1726 Sprintf(buf," %s to %s: %d",
1727 br_string(br->type),
1728 dungeons[br->end2.dnum].dname,
1729 depth(&br->end1));
1730 if (bymenu) {
1731 lchoices->lev[lchoices->idx] = br->end1.dlevel;
1732 lchoices->dgn[lchoices->idx] = br->end1.dnum;
1733 lchoices->playerlev[lchoices->idx] = depth(&br->end1);
1734 any.a_void = 0;
1735 any.a_int = lchoices->idx + 1;
1736 add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, lchoices->menuletter,
1737 0, ATR_NONE, buf, MENU_UNSELECTED);
1738 if (lchoices->menuletter == 'z') lchoices->menuletter = 'A';
1739 else if (lchoices->menuletter == 'Z') lchoices->menuletter = 'a';
1740 else lchoices->menuletter++;
1741 lchoices->idx++;
1742 } else
1743 putstr(win, 0, buf);
1744 }
1745 }
1746 }
1747
1748 /* Print available dungeon information. */
1749 schar
print_dungeon(bymenu,rlev,rdgn)1750 print_dungeon(bymenu, rlev, rdgn)
1751 boolean bymenu;
1752 schar *rlev;
1753 xchar *rdgn;
1754 {
1755 int i, last_level, nlev;
1756 char buf[BUFSZ];
1757 boolean first;
1758 s_level *slev;
1759 dungeon *dptr;
1760 branch *br;
1761 anything any;
1762 struct lchoice lchoices;
1763
1764 winid win = create_nhwindow(NHW_MENU);
1765 if (bymenu) {
1766 start_menu(win);
1767 lchoices.idx = 0;
1768 lchoices.menuletter = 'a';
1769 }
1770
1771 for (i = 0, dptr = dungeons; i < n_dgns; i++, dptr++) {
1772 nlev = dptr->num_dunlevs;
1773 if (nlev > 1)
1774 Sprintf(buf, "%s: levels %d to %d", dptr->dname, dptr->depth_start,
1775 dptr->depth_start + nlev - 1);
1776 else
1777 Sprintf(buf, "%s: level %d", dptr->dname, dptr->depth_start);
1778
1779 /* Most entrances are uninteresting. */
1780 if (dptr->entry_lev != 1) {
1781 if (dptr->entry_lev == nlev)
1782 Strcat(buf, ", entrance from below");
1783 else
1784 Sprintf(eos(buf), ", entrance on %d",
1785 dptr->depth_start + dptr->entry_lev - 1);
1786 }
1787 if (bymenu) {
1788 any.a_void = 0;
1789 add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, 0, 0, iflags.menu_headings, buf, MENU_UNSELECTED);
1790 } else
1791 putstr(win, 0, buf);
1792
1793 /*
1794 * Circle through the special levels to find levels that are in
1795 * this dungeon.
1796 */
1797 for (slev = sp_levchn, last_level = 0; slev; slev = slev->next) {
1798 if (slev->dlevel.dnum != i) continue;
1799
1800 /* print any branches before this level */
1801 print_branch(win, i, last_level, slev->dlevel.dlevel, bymenu, &lchoices);
1802
1803 Sprintf(buf, " %s: %d", slev->proto, depth(&slev->dlevel));
1804 if (Is_stronghold(&slev->dlevel))
1805 Sprintf(eos(buf), " (tune %s)", tune);
1806 if (bymenu) {
1807 /* If other floating branches are added, this will need to change */
1808 #ifdef ADVENT_CALENDAR
1809 if ((i != advcal_level.dnum) && (i != knox_level.dnum)) {
1810 #else
1811 if (i != knox_level.dnum) {
1812 #endif
1813 lchoices.lev[lchoices.idx] = slev->dlevel.dlevel;
1814 lchoices.dgn[lchoices.idx] = i;
1815 } else {
1816 lchoices.lev[lchoices.idx] = depth(&slev->dlevel);
1817 lchoices.dgn[lchoices.idx] = 0;
1818 }
1819 lchoices.playerlev[lchoices.idx] = depth(&slev->dlevel);
1820 any.a_void = 0;
1821 any.a_int = lchoices.idx + 1;
1822 add_menu(win, NO_GLYPH, MENU_DEFCNT, &any, lchoices.menuletter,
1823 0, ATR_NONE, buf, MENU_UNSELECTED);
1824 if (lchoices.menuletter == 'z') lchoices.menuletter = 'A';
1825 else if (lchoices.menuletter == 'Z') lchoices.menuletter = 'a';
1826 else lchoices.menuletter++;
1827 lchoices.idx++;
1828 } else
1829 putstr(win, 0, buf);
1830
1831 last_level = slev->dlevel.dlevel;
1832 }
1833 /* print branches after the last special level */
1834 print_branch(win, i, last_level, MAXLEVEL, bymenu, &lchoices);
1835 }
1836
1837 /* Print out floating branches (if any). */
1838 for (first = TRUE, br = branches; br; br = br->next) {
1839 if (br->end1.dnum == n_dgns) {
1840 if (first) {
1841 if (!bymenu) {
1842 putstr(win, 0, "");
1843 putstr(win, 0, "Floating branches");
1844 }
1845 first = FALSE;
1846 }
1847 Sprintf(buf, " %s to %s",
1848 br_string(br->type), dungeons[br->end2.dnum].dname);
1849 if (!bymenu)
1850 putstr(win, 0, buf);
1851 }
1852 }
1853 if (bymenu) {
1854 int n;
1855 menu_item *selected;
1856 int idx;
1857
1858 end_menu(win, "Level teleport to where:");
1859 n = select_menu(win, PICK_ONE, &selected);
1860 destroy_nhwindow(win);
1861 if (n > 0) {
1862 idx = selected[0].item.a_int - 1;
1863 free((genericptr_t)selected);
1864 if (rlev && rdgn) {
1865 *rlev = lchoices.lev[idx];
1866 *rdgn = lchoices.dgn[idx];
1867 return lchoices.playerlev[idx];
1868 }
1869 }
1870 return 0;
1871 }
1872
1873 /* I hate searching for the invocation pos while debugging. -dean */
1874 if (Invocation_lev(&u.uz)) {
1875 putstr(win, 0, "");
1876 Sprintf(buf, "Invocation position @ (%d,%d), hero @ (%d,%d)",
1877 inv_pos.x, inv_pos.y, u.ux, u.uy);
1878 putstr(win, 0, buf);
1879 }
1880 /*
1881 * The following is based on the assumption that the inter-level portals
1882 * created by the level compiler (not the dungeon compiler) only exist
1883 * one per level (currently true, of course).
1884 */
1885 else if (Is_earthlevel(&u.uz) || Is_waterlevel(&u.uz)
1886 || Is_firelevel(&u.uz) || Is_airlevel(&u.uz)) {
1887 struct trap *trap;
1888 for (trap = ftrap; trap; trap = trap->ntrap)
1889 if (trap->ttyp == MAGIC_PORTAL) break;
1890
1891 putstr(win, 0, "");
1892 if (trap)
1893 Sprintf(buf, "Portal @ (%d,%d), hero @ (%d,%d)",
1894 trap->tx, trap->ty, u.ux, u.uy);
1895 else
1896 Sprintf(buf, "No portal found.");
1897 putstr(win, 0, buf);
1898 }
1899
1900 display_nhwindow(win, TRUE);
1901 destroy_nhwindow(win);
1902 return 0;
1903 }
1904 #endif /* WIZARD */
1905
1906 /* Record that the player knows about a branch from a level. This function
1907 * will determine whether or not it was a "real" branch that was taken.
1908 * This function should not be called for a transition done via level
1909 * teleport or via the Eye.
1910 */
1911 void
recbranch_mapseen(source,dest)1912 recbranch_mapseen(source, dest)
1913 d_level *source;
1914 d_level *dest;
1915 {
1916 mapseen *mptr;
1917 branch* br;
1918
1919 /* not a branch */
1920 if (source->dnum == dest->dnum) return;
1921
1922 /* we only care about forward branches */
1923 for (br = branches; br; br = br->next) {
1924 if (on_level(source, &br->end1) && on_level(dest, &br->end2)) break;
1925 if (on_level(source, &br->end2) && on_level(dest, &br->end1)) return;
1926 }
1927
1928 /* branch not found, so not a real branch. */
1929 if (!br) return;
1930
1931 if ((mptr = find_mapseen(source))) {
1932 if (mptr->br && br != mptr->br)
1933 impossible("Two branches on the same level?");
1934 mptr->br = br;
1935 } else {
1936 impossible("Can't note branch for unseen level (%d, %d)",
1937 source->dnum, source->dlevel);
1938 }
1939 }
1940
1941 /* add a custom name to the current level */
1942 int
donamelevel()1943 donamelevel()
1944 {
1945 mapseen *mptr;
1946 char qbuf[QBUFSZ]; /* Buffer for query text */
1947 char nbuf[BUFSZ]; /* Buffer for response */
1948 int len;
1949
1950 if (!(mptr = find_mapseen(&u.uz))) return 0;
1951
1952 if (mptr->custom) {
1953 Sprintf(qbuf,"Replace previous annotation \"%s\" with?", mptr->custom);
1954 } else {
1955 Sprintf(qbuf,"What do you want to call this dungeon level?");
1956 }
1957 getlin(qbuf, nbuf);
1958
1959 if (index(nbuf, '\033')) return 0;
1960
1961 len = strlen(nbuf) + 1;
1962 if (mptr->custom) {
1963 free((genericptr_t)mptr->custom);
1964 mptr->custom = (char *)0;
1965 mptr->custom_lth = 0;
1966 }
1967
1968 if (*nbuf) {
1969 mptr->custom = (char *) alloc(sizeof(char) * len);
1970 mptr->custom_lth = len;
1971 strcpy(mptr->custom, nbuf);
1972 }
1973
1974 return 0;
1975 }
1976
1977 /* find the particular mapseen object in the chain */
1978 /* may return 0 */
1979 STATIC_OVL mapseen *
find_mapseen(lev)1980 find_mapseen(lev)
1981 d_level *lev;
1982 {
1983 mapseen *mptr;
1984
1985 for (mptr = mapseenchn; mptr; mptr = mptr->next)
1986 if (on_level(&(mptr->lev), lev)) break;
1987
1988 return mptr;
1989 }
1990
1991 void
forget_mapseen(ledger_no)1992 forget_mapseen(ledger_no)
1993 int ledger_no;
1994 {
1995 mapseen *mptr;
1996
1997 for (mptr = mapseenchn; mptr; mptr = mptr->next)
1998 if (dungeons[mptr->lev.dnum].ledger_start +
1999 mptr->lev.dlevel == ledger_no) break;
2000
2001 /* if not found, then nothing to forget */
2002 if (mptr) {
2003 mptr->feat.forgot = 1;
2004 mptr->br = (branch *)0;
2005
2006 /* custom names are erased, not forgotten until revisted */
2007 if (mptr->custom) {
2008 mptr->custom_lth = 0;
2009 free((genericptr_t)mptr->custom);
2010 mptr->custom = (char *)0;
2011 }
2012
2013 memset((genericptr_t) mptr->rooms, 0, sizeof(mptr->rooms));
2014 }
2015 }
2016
2017 STATIC_OVL void
save_mapseen(fd,mptr)2018 save_mapseen(fd, mptr)
2019 int fd;
2020 mapseen *mptr;
2021 {
2022 branch *curr;
2023 int count;
2024
2025 count = 0;
2026 for (curr = branches; curr; curr = curr->next) {
2027 if (curr == mptr->br) break;
2028 count++;
2029 }
2030
2031 bwrite(fd, (genericptr_t) &count, sizeof(int));
2032 bwrite(fd, (genericptr_t) &mptr->lev, sizeof(d_level));
2033 bwrite(fd, (genericptr_t) &mptr->feat, sizeof(mapseen_feat));
2034 bwrite(fd, (genericptr_t) &mptr->custom_lth, sizeof(unsigned));
2035 if (mptr->custom_lth)
2036 bwrite(fd, (genericptr_t) mptr->custom,
2037 sizeof(char) * mptr->custom_lth);
2038 bwrite(fd, (genericptr_t) &mptr->rooms, sizeof(mptr->rooms));
2039 }
2040
2041 STATIC_OVL mapseen *
load_mapseen(fd)2042 load_mapseen(fd)
2043 int fd;
2044 {
2045 int branchnum, count;
2046 mapseen *load;
2047 branch *curr;
2048
2049 load = (mapseen *) alloc(sizeof(mapseen));
2050 mread(fd, (genericptr_t) &branchnum, sizeof(int));
2051
2052 count = 0;
2053 for (curr = branches; curr; curr = curr->next) {
2054 if (count == branchnum) break;
2055 count++;
2056 }
2057 load->br = curr;
2058
2059 mread(fd, (genericptr_t) &load->lev, sizeof(d_level));
2060 mread(fd, (genericptr_t) &load->feat, sizeof(mapseen_feat));
2061 mread(fd, (genericptr_t) &load->custom_lth, sizeof(unsigned));
2062 if (load->custom_lth > 0) {
2063 load->custom = (char *) alloc(sizeof(char) * load->custom_lth);
2064 mread(fd, (genericptr_t) load->custom,
2065 sizeof(char) * load->custom_lth);
2066 } else load->custom = (char *) 0;
2067 mread(fd, (genericptr_t) &load->rooms, sizeof(load->rooms));
2068
2069 return load;
2070 }
2071
2072 /* Remove all mapseen objects for a particular dnum.
2073 * Useful during quest expulsion to remove quest levels.
2074 */
2075 void
remdun_mapseen(dnum)2076 remdun_mapseen(dnum)
2077 int dnum;
2078 {
2079 mapseen *mptr, *prev;
2080
2081 prev = mapseenchn;
2082 if (!prev) return;
2083 mptr = prev->next;
2084
2085 for (; mptr; prev = mptr, mptr = mptr->next) {
2086 if (mptr->lev.dnum == dnum) {
2087 prev->next = mptr->next;
2088 free((genericptr_t) mptr);
2089 mptr = prev;
2090 }
2091 }
2092 }
2093
2094 void
init_mapseen(lev)2095 init_mapseen(lev)
2096 d_level *lev;
2097 {
2098 /* Create a level and insert in "sorted" order. This is an insertion
2099 * sort first by dungeon (in order of discovery) and then by level number.
2100 */
2101 mapseen *mptr;
2102 mapseen *init;
2103 mapseen *old;
2104
2105 init = (mapseen *) alloc(sizeof(mapseen));
2106 (void) memset((genericptr_t)init, 0, sizeof(mapseen));
2107 init->lev.dnum = lev->dnum;
2108 init->lev.dlevel = lev->dlevel;
2109
2110 if (!mapseenchn) {
2111 mapseenchn = init;
2112 return;
2113 }
2114
2115 /* walk until we get to the place where we should
2116 * insert init between mptr and mptr->next
2117 */
2118 for (mptr = mapseenchn; mptr->next; mptr = mptr->next) {
2119 if (mptr->next->lev.dnum == init->lev.dnum) break;
2120 }
2121 for (; mptr->next; mptr = mptr->next) {
2122 if ((mptr->next->lev.dnum != init->lev.dnum) ||
2123 (mptr->next->lev.dlevel > init->lev.dlevel)) break;
2124 }
2125
2126 old = mptr->next;
2127 mptr->next = init;
2128 init->next = old;
2129 }
2130
2131 #define INTEREST(feat) \
2132 ((feat).nfount) || \
2133 ((feat).nsink) || \
2134 ((feat).nthrone) || \
2135 ((feat).naltar) || \
2136 ((feat).nshop) || \
2137 ((feat).ntemple) || \
2138 ((feat).ntree)
2139 /*
2140 || ((feat).water) || \
2141 ((feat).ice) || \
2142 ((feat).lava)
2143 */
2144
2145 /* returns true if this level has something interesting to print out */
2146 STATIC_OVL boolean
interest_mapseen(mptr)2147 interest_mapseen(mptr)
2148 mapseen *mptr;
2149 {
2150 return (on_level(&u.uz, &mptr->lev) ||
2151 ((!mptr->feat.forgot) &&
2152 (INTEREST(mptr->feat) ||
2153 (mptr->custom) ||
2154 (mptr->br)))
2155 );
2156 }
2157
2158 /* recalculate mapseen for the current level */
2159 void
recalc_mapseen()2160 recalc_mapseen()
2161 {
2162 mapseen *mptr;
2163 struct monst *shkp;
2164 unsigned x, y, ridx;
2165
2166 /* Should not happen in general, but possible if in the process
2167 * of being booted from the quest. The mapseen object gets
2168 * removed during the expulsion but prior to leaving the level
2169 */
2170 if (!(mptr = find_mapseen(&u.uz))) return;
2171
2172 /* reset all features */
2173 memset((genericptr_t) &mptr->feat, 0, sizeof(mapseen_feat));
2174
2175 /* track rooms the hero is in */
2176 for (x = 0; x < sizeof(u.urooms); x++) {
2177 if (!u.urooms[x]) continue;
2178
2179 ridx = u.urooms[x] - ROOMOFFSET;
2180 if (rooms[ridx].rtype < SHOPBASE ||
2181 ((shkp = shop_keeper(u.urooms[x])) && inhishop(shkp)))
2182 mptr->rooms[ridx] |= MSR_SEEN;
2183 else
2184 /* shops without shopkeepers are no shops at all */
2185 mptr->rooms[ridx] &= ~MSR_SEEN;
2186 }
2187
2188 /* recalculate room knowledge: for now, just shops and temples
2189 * this could be extended to an array of 0..SHOPBASE
2190 */
2191 for (x = 0; x < sizeof(mptr->rooms); x++) {
2192 if (mptr->rooms[x] & MSR_SEEN) {
2193 if (rooms[x].rtype >= SHOPBASE) {
2194 #ifdef BLACKMARKET
2195 /* don't record the large single shop room on the blackmarket level */
2196 if (rooms[x].rtype != BLACKSHOP) {
2197 #endif
2198 if (!mptr->feat.nshop)
2199 mptr->feat.shoptype = rooms[x].rtype;
2200 else if (mptr->feat.shoptype != rooms[x].rtype)
2201 mptr->feat.shoptype = 0;
2202 mptr->feat.nshop = min(mptr->feat.nshop + 1, 3);
2203 #ifdef BLACKMARKET
2204 }
2205 #endif
2206 } else if (rooms[x].rtype == TEMPLE)
2207 /* altar and temple alignment handled below */
2208 mptr->feat.ntemple = min(mptr->feat.ntemple + 1, 3);
2209 }
2210 }
2211
2212 /* Update styp with typ if and only if it is in sight or the hero can
2213 * feel it on their current location (i.e. not levitating). This *should*
2214 * give the "last known typ" for each dungeon location. (At the very least,
2215 * it's a better assumption than determining what the player knows from
2216 * the glyph and the typ (which is isn't quite enough information in some
2217 * cases).
2218 *
2219 * It was reluctantly added to struct rm to track. Alternatively
2220 * we could track "features" and then update them all here, and keep
2221 * track of when new features are created or destroyed, but this
2222 * seemed the most elegant, despite adding more data to struct rm.
2223 *
2224 * Although no current windowing systems (can) do this, this would add the
2225 * ability to have non-dungeon glyphs float above the last known dungeon
2226 * glyph (i.e. items on fountains).
2227 *
2228 * (vision-related styp update done in loop below)
2229 */
2230 if (!Levitation)
2231 levl[u.ux][u.uy].styp = levl[u.ux][u.uy].typ;
2232
2233 for (x = 0; x < COLNO; x++) {
2234 for (y = 0; y < ROWNO; y++) {
2235 /* update styp from viz_array */
2236 if (viz_array[y][x] & IN_SIGHT)
2237 levl[x][y].styp = levl[x][y].typ;
2238
2239 switch (levl[x][y].styp) {
2240 /*
2241 case ICE:
2242 mptr->feat.ice = 1;
2243 break;
2244 case POOL:
2245 case MOAT:
2246 case WATER:
2247 mptr->feat.water = 1;
2248 break;
2249 case LAVAPOOL:
2250 mptr->feat.lava = 1;
2251 break;
2252 */
2253 case TREE:
2254 mptr->feat.ntree = min(mptr->feat.ntree + 1, 3);
2255 break;
2256 case FOUNTAIN:
2257 mptr->feat.nfount = min(mptr->feat.nfount + 1, 3);
2258 break;
2259 case THRONE:
2260 mptr->feat.nthrone = min(mptr->feat.nthrone + 1, 3);
2261 break;
2262 case SINK:
2263 mptr->feat.nsink = min(mptr->feat.nsink + 1, 3);
2264 break;
2265 case ALTAR:
2266 if (!mptr->feat.naltar)
2267 mptr->feat.msalign = Amask2msa(levl[x][y].altarmask & ~AM_SHRINE);
2268 else if (mptr->feat.msalign != Amask2msa(levl[x][y].altarmask & ~AM_SHRINE))
2269 mptr->feat.msalign = MSA_NONE;
2270
2271 mptr->feat.naltar = min(mptr->feat.naltar + 1, 3);
2272 break;
2273 }
2274 }
2275 }
2276 }
2277
2278 int
dooverview()2279 dooverview()
2280 {
2281 winid win;
2282 mapseen *mptr;
2283 boolean first;
2284 boolean printdun;
2285 int lastdun=-1;
2286
2287 first = TRUE;
2288
2289 /* lazy intialization */
2290 (void) recalc_mapseen();
2291
2292 win = create_nhwindow(NHW_MENU);
2293
2294 for (mptr = mapseenchn; mptr; mptr = mptr->next) {
2295
2296 /* only print out info for a level or a dungeon if interest */
2297 if (interest_mapseen(mptr)) {
2298 printdun = (first || lastdun != mptr->lev.dnum);
2299 /* if (!first) putstr(win, 0, ""); */
2300 print_mapseen(win, mptr, printdun, FALSE, FALSE);
2301
2302 if (printdun) {
2303 first = FALSE;
2304 lastdun = mptr->lev.dnum;
2305 }
2306 }
2307 }
2308
2309 display_nhwindow(win, TRUE);
2310 destroy_nhwindow(win);
2311
2312 return 0;
2313 }
2314
2315 #ifdef DUMP_LOG
2316 int
dumpoverview()2317 dumpoverview()
2318 {
2319 mapseen *mptr;
2320 boolean printdun;
2321 int lastdun=-1;
2322 boolean first = TRUE;
2323 boolean previous_was_interesting = FALSE;
2324
2325 first = TRUE;
2326
2327 /* lazy intialization */
2328 (void) recalc_mapseen();
2329
2330 for (mptr = mapseenchn; mptr; mptr = mptr->next) {
2331 /* try to find out if the last branch printed something */
2332 if (!first && lastdun != mptr->lev.dnum && previous_was_interesting) {
2333 dump_html("</li>\n", "");
2334 previous_was_interesting = FALSE;
2335 }
2336 /* only print out info for a level or a dungeon if interest */
2337 if (interest_mapseen(mptr)) {
2338 previous_was_interesting = TRUE;
2339 printdun = (first || lastdun != mptr->lev.dnum);
2340
2341 if (first) {
2342 /* Always print header as there will at least
2343 * be the output of the current level */
2344 dump_title("Dungeon overview");
2345 dump_list_start();
2346 }
2347 /* currently dumping is always at the end of the game */
2348 print_mapseen(0, mptr, printdun, TRUE, TRUE);
2349
2350 if (printdun) {
2351 first = FALSE;
2352 lastdun = mptr->lev.dnum;
2353 }
2354 }
2355 }
2356 if (!first) {
2357 dump_html("</li>\n", "");
2358 dump_list_end();
2359 }
2360
2361 return 0;
2362 }
2363 #endif
2364
2365 STATIC_OVL char *
seen_string(x,obj)2366 seen_string(x, obj)
2367 xchar x;
2368 const char *obj;
2369 {
2370 /* players are computer scientists: 0, 1, 2, n */
2371 switch(x) {
2372 case 0: return "no";
2373 /* an() returns too much. index is ok in this case */
2374 case 1: return index(vowels, *obj) ? "an" : "a";
2375 case 2: return "some";
2376 case 3: return "many";
2377 }
2378
2379 return "(unknown)";
2380 }
2381
2382 /* better br_string */
2383 STATIC_OVL const char *
br_string2(br)2384 br_string2(br)
2385 branch *br;
2386 {
2387 /* Special case: quest portal says closed if kicked from quest */
2388 boolean closed_portal =
2389 (br->end2.dnum == quest_dnum && u.uevent.qexpelled);
2390 switch(br->type)
2391 {
2392 case BR_PORTAL: return closed_portal ? "Sealed portal" : "Portal";
2393 case BR_NO_END1: return "Connection";
2394 case BR_NO_END2: return (br->end1_up) ? "One way stairs up" :
2395 "One way stairs down";
2396 case BR_STAIR: return (br->end1_up) ? "Stairs up" : "Stairs down";
2397 }
2398
2399 return "(unknown)";
2400 }
2401
2402 STATIC_OVL const char*
shop_string(rtype)2403 shop_string(rtype)
2404 int rtype;
2405 {
2406 /* Yuck, redundancy...but shclass.name doesn't cut it as a noun */
2407 switch(rtype) {
2408 case SHOPBASE:
2409 return "a general store";
2410 case ARMORSHOP:
2411 return "an armor shop";
2412 case SCROLLSHOP:
2413 return "a scroll shop";
2414 case POTIONSHOP:
2415 return "a potion shop";
2416 case WEAPONSHOP:
2417 return "a weapon shop";
2418 case FOODSHOP:
2419 return "a delicatessen store";
2420 case RINGSHOP:
2421 return "a jewelry store";
2422 case WANDSHOP:
2423 return "a wand shop";
2424 case BOOKSHOP:
2425 return "a bookstore";
2426 case CANDLESHOP:
2427 return "a lighting shop";
2428 case TOOLSHOP:
2429 return "a tool shop";
2430 case INSTRUMENTSHOP:
2431 return "a music store";
2432 case TINSHOP:
2433 return "a tin shop";
2434 case PETSHOP:
2435 return "a pet store";
2436 case BLACKSHOP:
2437 return "the Blackmarket";
2438 default:
2439 /* In case another patch adds a shop type that doesn't exist,
2440 * do something reasonable like "a shop".
2441 */
2442 warning("Unknown shop number: %d", rtype);
2443 return "shop";
2444 }
2445 }
2446
2447 /* some utility macros for print_mapseen */
2448 #define TAB " "
2449 #define BULLET ""
2450 #define PREFIX TAB TAB BULLET
2451 #define COMMA (i++ > 0 ? ", " : PREFIX)
2452 #define ADDNTOBUF(nam, var) { if (var) \
2453 Sprintf(eos(buf), "%s%s " nam "%s", COMMA, seen_string((var), (nam)), \
2454 ((var) != 1 ? "s" : "")); }
2455 #define ADDTOBUF(nam, var) { if (var) Sprintf(eos(buf), "%s " nam, COMMA); }
2456
2457 STATIC_OVL void
print_mapseen(win,mptr,printdun,dump,final)2458 print_mapseen(win, mptr, printdun, dump, final)
2459 winid win;
2460 mapseen *mptr;
2461 boolean printdun;
2462 boolean dump; /**< if information should be dumped to file */
2463 boolean final; /**< if game is finished */
2464 {
2465 char buf[BUFSZ];
2466 int i, depthstart;
2467
2468 /* Damnable special cases */
2469 /* The quest and knox should appear to be level 1 to match
2470 * other text.
2471 */
2472 if (mptr->lev.dnum == quest_dnum || mptr->lev.dnum == knox_level.dnum)
2473 depthstart = 1;
2474 else
2475 depthstart = dungeons[mptr->lev.dnum].depth_start;
2476
2477 if (printdun) {
2478 /* Sokoban lies about dunlev_ureached and we should
2479 * suppress the negative numbers in the endgame.
2480 */
2481 if (dungeons[mptr->lev.dnum].dunlev_ureached == 1 ||
2482 mptr->lev.dnum == sokoban_dnum || In_endgame(&mptr->lev))
2483 Sprintf(buf, "%s:", dungeons[mptr->lev.dnum].dname);
2484 else
2485 Sprintf(buf, "%s: levels %d to %d",
2486 dungeons[mptr->lev.dnum].dname,
2487 depthstart, depthstart +
2488 dungeons[mptr->lev.dnum].dunlev_ureached - 1);
2489 if (!dump) {
2490 putstr(win, ATR_INVERSE, buf);
2491 } else {
2492 #ifdef DUMP_LOG
2493 dump_text(" %s\n", buf);
2494 dump_html("<li>%s\n", buf);
2495 #endif
2496 }
2497 }
2498
2499 #ifdef DUMP_LOG
2500 dump_definition_list_start();
2501 #endif
2502 /* calculate level number */
2503 i = depthstart + mptr->lev.dlevel - 1;
2504 if (Is_astralevel(&mptr->lev))
2505 Sprintf(buf, TAB "Astral Plane:");
2506 else if (In_endgame(&mptr->lev))
2507 /* Negative numbers are mildly confusing, since they are never
2508 * shown to the player, except in wizard mode. We could show
2509 * "Level -1" for the earth plane, for example. Instead,
2510 * show "Plane 1" for the earth plane to differentiate from
2511 * level 1. There's not much to show, but maybe the player
2512 * wants to #annotate them for some bizarre reason.
2513 */
2514 Sprintf(buf, TAB "Plane %i:", -i);
2515 else
2516 Sprintf(buf, TAB "Level %d:", i);
2517
2518 #ifdef WIZARD
2519 /* wizmode prints out proto dungeon names for clarity */
2520 if (wizard || final) {
2521 s_level *slev;
2522 if ((slev = Is_special(&mptr->lev)))
2523 Sprintf(eos(buf), " [%s]", slev->proto);
2524 }
2525 #endif
2526
2527 if (mptr->custom)
2528 Sprintf(eos(buf), " (%s)", mptr->custom);
2529
2530 /* print out glyph or something more interesting? */
2531 Sprintf(eos(buf), "%s", on_level(&u.uz, &mptr->lev) ?
2532 (final ? " <- You were here" : " <- You are here") : "");
2533 if (!dump) {
2534 putstr(win, ATR_BOLD, buf);
2535 } else {
2536 #ifdef DUMP_LOG
2537 dump_definition_list_dt(buf);
2538 #endif
2539 }
2540
2541 if (mptr->feat.forgot) {
2542 #ifdef DUMP_LOG
2543 if (dump) dump_definition_list_end();
2544 #endif
2545 return;
2546 }
2547
2548 if (INTEREST(mptr->feat)) {
2549 buf[0] = 0;
2550
2551 i = 0; /* interest counter */
2552
2553 /* List interests in an order vaguely corresponding to
2554 * how important they are.
2555 */
2556 if (mptr->feat.nshop > 1)
2557 ADDNTOBUF("shop", mptr->feat.nshop)
2558 else if (mptr->feat.nshop == 1)
2559 Sprintf(eos(buf), "%s%s", COMMA,
2560 shop_string(mptr->feat.shoptype));
2561
2562 /* Temples + non-temple altars get munged into just "altars" */
2563 if (!mptr->feat.ntemple || mptr->feat.ntemple != mptr->feat.naltar)
2564 ADDNTOBUF("altar", mptr->feat.naltar)
2565 else
2566 ADDNTOBUF("temple", mptr->feat.ntemple)
2567
2568 /* only print out altar's god if they are all to your god */
2569 if (Amask2align(Msa2amask(mptr->feat.msalign)) == u.ualign.type)
2570 Sprintf(eos(buf), " to %s", align_gname(u.ualign.type));
2571
2572 ADDNTOBUF("fountain", mptr->feat.nfount)
2573 ADDNTOBUF("sink", mptr->feat.nsink)
2574 ADDNTOBUF("throne", mptr->feat.nthrone)
2575 ADDNTOBUF("tree", mptr->feat.ntree);
2576 /*
2577 ADDTOBUF("water", mptr->feat.water)
2578 ADDTOBUF("lava", mptr->feat.lava)
2579 ADDTOBUF("ice", mptr->feat.ice)
2580 */
2581
2582 /* capitalize afterwards */
2583 i = strlen(PREFIX);
2584 *buf = highc(*buf);
2585
2586 if (!dump) {
2587 putstr(win, 0, buf);
2588 } else {
2589 #ifdef DUMP_LOG
2590 dump_definition_list_dd(buf);
2591 #endif
2592 }
2593 }
2594
2595 /* print out branches */
2596 if (mptr->br) {
2597 Sprintf(buf, PREFIX "%s to %s", br_string2(mptr->br),
2598 dungeons[mptr->br->end2.dnum].dname);
2599
2600 /* since mapseen objects are printed out in increasing order
2601 * of dlevel, clarify which level this branch is going to
2602 * if the branch goes upwards. Unless it's the end game
2603 */
2604 if (mptr->br->end1_up && !In_endgame(&(mptr->br->end2)))
2605 Sprintf(eos(buf), ", level %d", depth(&(mptr->br->end2)));
2606 if (!dump) {
2607 putstr(win, 0, buf);
2608 } else {
2609 #ifdef DUMP_LOG
2610 dump_definition_list_dd(buf);
2611 #endif
2612 }
2613 }
2614
2615 #ifdef DUMP_LOG
2616 if (dump) dump_definition_list_end();
2617 #endif
2618 }
2619
2620 /** Get annotation for a specific level. */
2621 char *
get_annotation(lev)2622 get_annotation(lev)
2623 d_level *lev;
2624 {
2625 mapseen *mptr;
2626
2627 if (!(mptr = find_mapseen(&u.uz))) {
2628 return NULL;
2629 } else {
2630 return mptr->custom;
2631 }
2632 }
2633
2634 /** Return a generic description of the current level like "plane" for the
2635 * planes, "tower" for Vlad's tower, or "dungeon" for a normal level. */
2636 const char *
get_generic_level_description(lev)2637 get_generic_level_description(lev)
2638 d_level *lev;
2639 {
2640 if (Is_astralevel(lev)) {
2641 return "astral plane";
2642 } else if (In_endgame(lev)) {
2643 return "plane";
2644 } else if (Is_sanctum(lev)) {
2645 return "sanctum";
2646 } else if (Is_blackmarket(lev)) {
2647 return "blackmarket";
2648 } else if (In_sokoban(lev)) {
2649 return "puzzle";
2650 } else if (In_V_tower(lev)) {
2651 return "tower";
2652 } else if (level.flags.sky) {
2653 return "ground";
2654 } else {
2655 return "dungeon";
2656 }
2657 }
2658
2659 /*dungeon.c*/
2660