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
]);