2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * @(#)level.c 8.1 (Berkeley) 5/31/93
37 * $FreeBSD: src/games/rogue/level.c,v 1.3 1999/11/30 03:49:23 billf Exp $
38 * $DragonFly: src/games/rogue/level.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
44 * This source herein may be modified and/or distributed by anybody who
45 * so desires, with the following restrictions:
46 * 1.) No portion of this notice shall be removed.
47 * 2.) Credit shall not be taken for the creation of this source.
48 * 3.) This code is not to be traded, sold, or used for personal
55 #define swap(x,y) {t = x; x = y; y = t;}
60 const char *new_level_message = 0;
61 short party_room = NO_ROOM;
64 const long level_points[MAX_EXP_LEVEL] = {
88 short random_rooms[MAXROOMS] = {3, 7, 5, 2, 0, 6, 1, 4, 8};
90 extern boolean being_held, wizard, detect_monster;
91 extern boolean see_invisible;
92 extern short bear_trap, levitate, extra_hp, less_hp;
94 static void make_room(short, short, short, short);
95 static boolean connect_rooms(short, short);
96 static void put_door(room *, short, short *, short *);
97 static void draw_simple_passage(short, short, short, short, short);
98 static boolean same_row(short, short);
99 static boolean same_col(short, short);
100 static void add_mazes(void);
101 static void fill_out_level(void);
102 static void fill_it(int, boolean);
103 static void recursive_deadend(short, const short *, short, short);
104 static boolean mask_room(short, short *, short *, unsigned short);
105 static void make_maze(short, short, short, short, short, short);
106 static void hide_boxed_passage(short, short, short, short, short);
107 static short get_exp_level(long);
108 static void mix_random_rooms(void);
114 short must_1, must_2 = 0, must_3 = 0;
117 if (cur_level < LAST_DUNGEON) {
120 if (cur_level > max_level) {
121 max_level = cur_level;
123 must_1 = get_rand(0, 5);
157 if (rand_percent(8)) {
160 big_room = ((party_room != NO_ROOM) && rand_percent(1));
162 make_room(BIG_ROOM, 0, 0, 0);
164 for (i = 0; i < MAXROOMS; i++) {
165 make_room(i, must_1, must_2, must_3);
173 for (j = 0; j < MAXROOMS; j++) {
177 if (i < (MAXROOMS-1)) {
178 connect_rooms(i, i+1);
180 if (i < (MAXROOMS-3)) {
181 connect_rooms(i, i+3);
183 if (i < (MAXROOMS-2)) {
184 if (rooms[i+1].is_room & R_NOTHING) {
185 if (connect_rooms(i, i+2)) {
186 rooms[i+1].is_room = R_CROSS;
190 if (i < (MAXROOMS-6)) {
191 if (rooms[i+3].is_room & R_NOTHING) {
192 if (connect_rooms(i, i+6)) {
193 rooms[i+3].is_room = R_CROSS;
197 if (is_all_connected()) {
203 if (!has_amulet() && (cur_level >= AMULET_LEVEL)) {
209 make_room(short rn, short r1, short r2, short r3)
211 short left_col, right_col, top_row, bottom_row;
213 short row_offset, col_offset;
216 left_col = right_col = top_row = bottom_row = 0;
259 bottom_row = DROWS - 2;
265 bottom_row = DROWS - 2;
271 bottom_row = DROWS - 2;
274 top_row = get_rand(MIN_ROW, MIN_ROW+5);
275 bottom_row = get_rand(DROWS-7, DROWS-2);
276 left_col = get_rand(0, 10);
277 right_col = get_rand(DCOLS-11, DCOLS-1);
281 height = get_rand(4, (bottom_row - top_row + 1));
282 width = get_rand(7, (right_col - left_col - 2));
284 row_offset = get_rand(0, ((bottom_row - top_row) - height + 1));
285 col_offset = get_rand(0, ((right_col - left_col) - width + 1));
287 top_row += row_offset;
288 bottom_row = top_row + height - 1;
290 left_col += col_offset;
291 right_col = left_col + width - 1;
293 if ((rn != r1) && (rn != r2) && (rn != r3) && rand_percent(40)) {
297 rooms[rn].is_room = R_ROOM;
299 for (i = top_row; i <= bottom_row; i++) {
300 for (j = left_col; j <= right_col; j++) {
301 if ((i == top_row) || (i == bottom_row)) {
303 } else if ( ((i != top_row) && (i != bottom_row)) &&
304 ((j == left_col) || (j == right_col))) {
313 rooms[rn].top_row = top_row;
314 rooms[rn].bottom_row = bottom_row;
315 rooms[rn].left_col = left_col;
316 rooms[rn].right_col = right_col;
320 connect_rooms(short room1, short room2)
322 short row1, col1, row2, col2, dir;
324 if ((!(rooms[room1].is_room & (R_ROOM | R_MAZE))) ||
325 (!(rooms[room2].is_room & (R_ROOM | R_MAZE)))) {
328 if (same_row(room1, room2) &&
329 (rooms[room1].left_col > rooms[room2].right_col)) {
330 put_door(&rooms[room1], LEFT, &row1, &col1);
331 put_door(&rooms[room2], RIGHT, &row2, &col2);
333 } else if (same_row(room1, room2) &&
334 (rooms[room2].left_col > rooms[room1].right_col)) {
335 put_door(&rooms[room1], RIGHT, &row1, &col1);
336 put_door(&rooms[room2], LEFT, &row2, &col2);
338 } else if (same_col(room1, room2) &&
339 (rooms[room1].top_row > rooms[room2].bottom_row)) {
340 put_door(&rooms[room1], UPWARD, &row1, &col1);
341 put_door(&rooms[room2], DOWN, &row2, &col2);
343 } else if (same_col(room1, room2) &&
344 (rooms[room2].top_row > rooms[room1].bottom_row)) {
345 put_door(&rooms[room1], DOWN, &row1, &col1);
346 put_door(&rooms[room2], UPWARD, &row2, &col2);
353 draw_simple_passage(row1, col1, row2, col2, dir);
354 } while (rand_percent(4));
356 rooms[room1].doors[dir/2].oth_room = room2;
357 rooms[room1].doors[dir/2].oth_row = row2;
358 rooms[room1].doors[dir/2].oth_col = col2;
360 rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_room = room1;
361 rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_row = row1;
362 rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_col = col1;
371 for (i = 0; i < MAXROOMS; i++) {
372 rooms[i].is_room = R_NOTHING;
373 for (j = 0; j < 4; j++) {
374 rooms[i].doors[j].oth_room = NO_ROOM;
378 for (i = 0; i < MAX_TRAPS; i++) {
379 traps[i].trap_type = NO_TRAP;
381 for (i = 0; i < DROWS; i++) {
382 for (j = 0; j < DCOLS; j++) {
383 dungeon[i][j] = NOTHING;
386 detect_monster = see_invisible = 0;
387 bear_trap = being_held = 0;
388 party_room = NO_ROOM;
389 rogue.row = rogue.col = -1;
394 put_door(room *rm, short dir, short *row, short *col)
398 wall_width = (rm->is_room & R_MAZE) ? 0 : 1;
403 *row = ((dir == UPWARD) ? rm->top_row : rm->bottom_row);
405 *col = get_rand(rm->left_col+wall_width,
406 rm->right_col-wall_width);
407 } while (!(dungeon[*row][*col] & (HORWALL | TUNNEL)));
411 *col = (dir == LEFT) ? rm->left_col : rm->right_col;
413 *row = get_rand(rm->top_row+wall_width,
414 rm->bottom_row-wall_width);
415 } while (!(dungeon[*row][*col] & (VERTWALL | TUNNEL)));
418 if (rm->is_room & R_ROOM) {
419 dungeon[*row][*col] = DOOR;
421 if ((cur_level > 2) && rand_percent(HIDE_PERCENT)) {
422 dungeon[*row][*col] |= HIDDEN;
424 rm->doors[dir/2].door_row = *row;
425 rm->doors[dir/2].door_col = *col;
429 draw_simple_passage(short row1, short col1, short row2, short col2, short dir)
433 if ((dir == LEFT) || (dir == RIGHT)) {
438 middle = get_rand(col1+1, col2-1);
439 for (i = col1+1; i != middle; i++) {
440 dungeon[row1][i] = TUNNEL;
442 for (i = row1; i != row2; i += (row1 > row2) ? -1 : 1) {
443 dungeon[i][middle] = TUNNEL;
445 for (i = middle; i != col2; i++) {
446 dungeon[row2][i] = TUNNEL;
453 middle = get_rand(row1+1, row2-1);
454 for (i = row1+1; i != middle; i++) {
455 dungeon[i][col1] = TUNNEL;
457 for (i = col1; i != col2; i += (col1 > col2) ? -1 : 1) {
458 dungeon[middle][i] = TUNNEL;
460 for (i = middle; i != row2; i++) {
461 dungeon[i][col2] = TUNNEL;
464 if (rand_percent(HIDE_PERCENT)) {
465 hide_boxed_passage(row1, col1, row2, col2, 1);
470 same_row(short room1, short room2)
472 return((room1 / 3) == (room2 / 3));
476 same_col(short room1, short room2)
478 return((room1 % 3) == (room2 % 3));
489 start = get_rand(0, (MAXROOMS-1));
490 maze_percent = (cur_level * 5) / 4;
492 if (cur_level > 15) {
493 maze_percent += cur_level;
495 for (i = 0; i < MAXROOMS; i++) {
496 j = ((start + i) % MAXROOMS);
497 if (rooms[j].is_room & R_NOTHING) {
498 if (rand_percent(maze_percent)) {
499 rooms[j].is_room = R_MAZE;
500 make_maze(get_rand(rooms[j].top_row+1, rooms[j].bottom_row-1),
501 get_rand(rooms[j].left_col+1, rooms[j].right_col-1),
502 rooms[j].top_row, rooms[j].bottom_row,
503 rooms[j].left_col, rooms[j].right_col);
504 hide_boxed_passage(rooms[j].top_row, rooms[j].left_col,
505 rooms[j].bottom_row, rooms[j].right_col,
522 for (i = 0; i < MAXROOMS; i++) {
523 rn = random_rooms[i];
524 if ((rooms[rn].is_room & R_NOTHING) ||
525 ((rooms[rn].is_room & R_CROSS) && coin_toss())) {
529 if (r_de != NO_ROOM) {
535 fill_it(int rn, boolean do_rec_de)
537 short i, tunnel_dir, door_dir, drow, dcol;
538 short target_room, rooms_found = 0;
540 static short offsets[4] = {-1, 1, 3, -3};
541 boolean did_this = 0;
543 for (i = 0; i < 10; i++) {
544 srow = get_rand(0, 3);
545 scol = get_rand(0, 3);
547 offsets[srow] = offsets[scol];
550 for (i = 0; i < 4; i++) {
552 target_room = rn + offsets[i];
554 if (((target_room < 0) || (target_room >= MAXROOMS)) ||
555 (!(same_row(rn,target_room) || same_col(rn,target_room))) ||
556 (!(rooms[target_room].is_room & (R_ROOM | R_MAZE)))) {
559 if (same_row(rn, target_room)) {
560 tunnel_dir = (rooms[rn].left_col < rooms[target_room].left_col) ?
563 tunnel_dir = (rooms[rn].top_row < rooms[target_room].top_row) ?
566 door_dir = ((tunnel_dir + 4) % DIRS);
567 if (rooms[target_room].doors[door_dir/2].oth_room != NO_ROOM) {
570 if (((!do_rec_de) || did_this) ||
571 (!mask_room(rn, &srow, &scol, TUNNEL))) {
572 srow = (rooms[rn].top_row + rooms[rn].bottom_row) / 2;
573 scol = (rooms[rn].left_col + rooms[rn].right_col) / 2;
575 put_door(&rooms[target_room], door_dir, &drow, &dcol);
577 draw_simple_passage(srow, scol, drow, dcol, tunnel_dir);
578 rooms[rn].is_room = R_DEADEND;
579 dungeon[srow][scol] = TUNNEL;
581 if ((i < 3) && (!did_this)) {
587 if ((rooms_found < 2) && do_rec_de) {
588 recursive_deadend(rn, offsets, srow, scol);
595 recursive_deadend(short rn, const short *offsets, short srow, short scol)
598 short drow, dcol, tunnel_dir;
600 rooms[rn].is_room = R_DEADEND;
601 dungeon[srow][scol] = TUNNEL;
603 for (i = 0; i < 4; i++) {
604 de = rn + offsets[i];
605 if (((de < 0) || (de >= MAXROOMS)) ||
606 (!(same_row(rn, de) || same_col(rn, de)))) {
609 if (!(rooms[de].is_room & R_NOTHING)) {
612 drow = (rooms[de].top_row + rooms[de].bottom_row) / 2;
613 dcol = (rooms[de].left_col + rooms[de].right_col) / 2;
614 if (same_row(rn, de)) {
615 tunnel_dir = (rooms[rn].left_col < rooms[de].left_col) ?
618 tunnel_dir = (rooms[rn].top_row < rooms[de].top_row) ?
621 draw_simple_passage(srow, scol, drow, dcol, tunnel_dir);
623 recursive_deadend(de, offsets, drow, dcol);
628 mask_room(short rn, short *row, short *col, unsigned short mask)
632 for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) {
633 for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) {
634 if (dungeon[i][j] & mask) {
645 make_maze(short r, short c, short tr, short br, short lc, short rc)
655 dungeon[r][c] = TUNNEL;
657 if (rand_percent(20)) {
658 for (i = 0; i < 10; i++) {
664 swap(dirs[t1], dirs[t2]);
667 for (i = 0; i < 4; i++) {
671 (dungeon[r-1][c] != TUNNEL) &&
672 (dungeon[r-1][c-1] != TUNNEL) &&
673 (dungeon[r-1][c+1] != TUNNEL) &&
674 (dungeon[r-2][c] != TUNNEL)) {
675 make_maze((r-1), c, tr, br, lc, rc);
680 (dungeon[r+1][c] != TUNNEL) &&
681 (dungeon[r+1][c-1] != TUNNEL) &&
682 (dungeon[r+1][c+1] != TUNNEL) &&
683 (dungeon[r+2][c] != TUNNEL)) {
684 make_maze((r+1), c, tr, br, lc, rc);
689 (dungeon[r][c-1] != TUNNEL) &&
690 (dungeon[r-1][c-1] != TUNNEL) &&
691 (dungeon[r+1][c-1] != TUNNEL) &&
692 (dungeon[r][c-2] != TUNNEL)) {
693 make_maze(r, (c-1), tr, br, lc, rc);
698 (dungeon[r][c+1] != TUNNEL) &&
699 (dungeon[r-1][c+1] != TUNNEL) &&
700 (dungeon[r+1][c+1] != TUNNEL) &&
701 (dungeon[r][c+2] != TUNNEL)) {
702 make_maze(r, (c+1), tr, br, lc, rc);
710 hide_boxed_passage(short row1, short col1, short row2, short col2, short n)
713 short row, col, row_cut, col_cut;
726 if ((w >= 5) || (h >= 5)) {
727 row_cut = ((h >= 2) ? 1 : 0);
728 col_cut = ((w >= 2) ? 1 : 0);
730 for (i = 0; i < n; i++) {
731 for (j = 0; j < 10; j++) {
732 row = get_rand(row1 + row_cut, row2 - row_cut);
733 col = get_rand(col1 + col_cut, col2 - col_cut);
734 if (dungeon[row][col] == TUNNEL) {
735 dungeon[row][col] |= HIDDEN;
745 put_player(short nr) /* try not to put in this room */
747 short rn = nr, misses;
750 for (misses = 0; ((misses < 2) && (rn == nr)); misses++) {
751 gr_row_col(&row, &col, (FLOOR | TUNNEL | OBJECT | STAIRS));
752 rn = get_room_number(row, col);
757 if (dungeon[rogue.row][rogue.col] & TUNNEL) {
762 if (cur_room != PASSAGE) {
763 light_up_room(cur_room);
765 light_passage(rogue.row, rogue.col);
767 rn = get_room_number(rogue.row, rogue.col);
768 wake_room(rn, 1, rogue.row, rogue.col);
769 if (new_level_message) {
770 message(new_level_message, 0);
771 new_level_message = 0;
773 mvaddch(rogue.row, rogue.col, rogue.fchar);
782 if (dungeon[rogue.row][rogue.col] & STAIRS) {
784 message("you're floating in the air!", 0);
789 message("I see no way down", 0);
797 if (!(dungeon[rogue.row][rogue.col] & STAIRS)) {
798 message("I see no way up", 0);
802 message("your way is magically blocked", 0);
806 new_level_message = "you feel a wrenching sensation in your gut";
807 if (cur_level == 1) {
817 add_exp(int e, boolean promotion)
823 rogue.exp_points += e;
825 if (rogue.exp_points >= level_points[rogue.exp-1]) {
826 new_exp = get_exp_level(rogue.exp_points);
827 if (rogue.exp_points > MAX_EXP) {
828 rogue.exp_points = MAX_EXP + 1;
830 for (i = rogue.exp+1; i <= new_exp; i++) {
831 sprintf(mbuf, "welcome to level %d", i);
835 rogue.hp_current += hp;
839 print_stats(STAT_HP | STAT_EXP);
842 print_stats(STAT_EXP);
847 get_exp_level(long e)
851 for (i = 0; i < (MAX_EXP_LEVEL - 1); i++) {
852 if (level_points[i] > e) {
864 hp = (wizard ? 10 : get_rand(3, 10));
869 show_average_hp(void)
873 float effective_average;
875 if (rogue.exp == 1) {
876 real_average = effective_average = 0.00;
878 real_average = (float)
879 ((rogue.hp_max - extra_hp - INIT_HP) + less_hp) / (rogue.exp - 1);
880 effective_average = (float) (rogue.hp_max - INIT_HP) / (rogue.exp - 1);
883 sprintf(mbuf, "R-Hp: %.2f, E-Hp: %.2f (!: %d, V: %d)", real_average,
884 effective_average, extra_hp, less_hp);
889 mix_random_rooms(void)
894 for (i = 0; i < (3 * MAXROOMS); i++) {
896 x = get_rand(0, (MAXROOMS-1));
897 y = get_rand(0, (MAXROOMS-1));
899 swap(random_rooms[x], random_rooms[y]);