/*      $NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $      */

/*
* Copyright (c) 1988, 1993
*      The Regents of the University of California.  All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Timothy C. Stoehr.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/

#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)level.c     8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $");
#endif
#endif /* not lint */

/*
* level.c
*
* This source herein may be modified and/or distributed by anybody who
* so desires, with the following restrictions:
*    1.)  No portion of this notice shall be removed.
*    2.)  Credit shall not be taken for the creation of this source.
*    3.)  This code is not to be traded, sold, or used for personal
*         gain or profit.
*
*/

#include "rogue.h"

#define SWAP(x,y) (t = (x), (x) = (y), (y) = t)

static void     add_mazes(void);
static int      connect_rooms(short, short);
static void     draw_simple_passage(short, short, short, short, short);
static void     fill_it(int, boolean);
static void     fill_out_level(void);
static int      get_exp_level(long);
static void     hide_boxed_passage(short, short, short, short, short);
static void     make_maze(short, short, short, short, short, short);
static void     make_room(short, short, short, short);
static boolean  mask_room(short, short *, short *, unsigned short);
static void     mix_random_rooms(void);
static void     put_door(room *, short, short *, short *);
static void     recursive_deadend(short, const short *, short, short);
static int      same_col(int, int);
static int      same_row(int, int);

short cur_level = 0;
short max_level = 1;
short cur_room;
const char *new_level_message = NULL;
short party_room = NO_ROOM;

static short r_de;

const long level_points[MAX_EXP_LEVEL] = {
                 10L,
                 20L,
                 40L,
                 80L,
                160L,
                320L,
                640L,
               1300L,
               2600L,
               5200L,
          10000L,
          20000L,
          40000L,
          80000L,
         160000L,
         320000L,
        1000000L,
        3333333L,
        6666666L,
         MAX_EXP,
       99900000L
};

static short random_rooms[MAXROOMS] = {3, 7, 5, 2, 0, 6, 1, 4, 8};

void
make_level(void)
{
       short i, j;
       short must_1, must_2, must_3;
       boolean big_room;

       must_2 = must_3 = 0;
       if (cur_level < LAST_DUNGEON) {
               cur_level++;
       }
       if (cur_level > max_level) {
               max_level = cur_level;
       }
       must_1 = get_rand(0, 5);

       switch(must_1) {
       case 0:
               must_1 = 0;
               must_2 = 1;
               must_3 = 2;
               break;
       case 1:
               must_1 = 3;
               must_2 = 4;
               must_3 = 5;
               break;
       case 2:
               must_1 = 6;
               must_2 = 7;
               must_3 = 8;
               break;
       case 3:
               must_1 = 0;
               must_2 = 3;
               must_3 = 6;
               break;
       case 4:
               must_1 = 1;
               must_2 = 4;
               must_3 = 7;
               break;
       case 5:
               must_1 = 2;
               must_2 = 5;
               must_3 = 8;
               break;
       }
       if (rand_percent(8)) {
               party_room = 0;
       }
       big_room = ((party_room != NO_ROOM) && rand_percent(1));
       if (big_room) {
               make_room(BIG_ROOM, 0, 0, 0);
       } else {
               for (i = 0; i < MAXROOMS; i++) {
                       make_room(i, must_1, must_2, must_3);
               }
       }
       if (!big_room) {
               add_mazes();

               mix_random_rooms();

               for (j = 0; j < MAXROOMS; j++) {

                       i = random_rooms[j];

                       if (i < (MAXROOMS-1)) {
                               (void)connect_rooms(i, i+1);
                       }
                       if (i < (MAXROOMS-3)) {
                               (void)connect_rooms(i, i+3);
                       }
                       if (i < (MAXROOMS-2)) {
                               if (rooms[i+1].is_room & R_NOTHING) {
                                       if (connect_rooms(i, i+2)) {
                                               rooms[i+1].is_room = R_CROSS;
                                       }
                               }
                       }
                       if (i < (MAXROOMS-6)) {
                               if (rooms[i+3].is_room & R_NOTHING) {
                                       if (connect_rooms(i, i+6)) {
                                               rooms[i+3].is_room = R_CROSS;
                                       }
                               }
                       }
                       if (is_all_connected()) {
                               break;
                       }
               }
               fill_out_level();
       }
       if (!has_amulet() && (cur_level >= AMULET_LEVEL)) {
               put_amulet();
       }
}

static void
make_room(short rn, short r1, short r2, short r3)
{
       short left_col, right_col, top_row, bottom_row;
       short width, height;
       short row_offset, col_offset;
       short i, j, ch;

       left_col = right_col = top_row = bottom_row = 0;
       switch(rn) {
       case 0:
               left_col = 0;
               right_col = COL1-1;
               top_row = MIN_ROW;
               bottom_row = ROW1-1;
               break;
       case 1:
               left_col = COL1+1;
               right_col = COL2-1;
               top_row = MIN_ROW;
               bottom_row = ROW1-1;
               break;
       case 2:
               left_col = COL2+1;
               right_col = DCOLS-1;
               top_row = MIN_ROW;
               bottom_row = ROW1-1;
               break;
       case 3:
               left_col = 0;
               right_col = COL1-1;
               top_row = ROW1+1;
               bottom_row = ROW2-1;
               break;
       case 4:
               left_col = COL1+1;
               right_col = COL2-1;
               top_row = ROW1+1;
               bottom_row = ROW2-1;
               break;
       case 5:
               left_col = COL2+1;
               right_col = DCOLS-1;
               top_row = ROW1+1;
               bottom_row = ROW2-1;
               break;
       case 6:
               left_col = 0;
               right_col = COL1-1;
               top_row = ROW2+1;
               bottom_row = DROWS - 2;
               break;
       case 7:
               left_col = COL1+1;
               right_col = COL2-1;
               top_row = ROW2+1;
               bottom_row = DROWS - 2;
               break;
       case 8:
               left_col = COL2+1;
               right_col = DCOLS-1;
               top_row = ROW2+1;
               bottom_row = DROWS - 2;
               break;
       case BIG_ROOM:
               top_row = get_rand(MIN_ROW, MIN_ROW+5);
               bottom_row = get_rand(DROWS-7, DROWS-2);
               left_col = get_rand(0, 10);
               right_col = get_rand(DCOLS-11, DCOLS-1);
               rn = 0;
               goto B;
       }
       height = get_rand(4, (bottom_row - top_row + 1));
       width = get_rand(7, (right_col - left_col - 2));

       row_offset = get_rand(0, ((bottom_row - top_row) - height + 1));
       col_offset = get_rand(0, ((right_col - left_col) - width + 1));

       top_row += row_offset;
       bottom_row = top_row + height - 1;

       left_col += col_offset;
       right_col = left_col + width - 1;

       if ((rn != r1) && (rn != r2) && (rn != r3) && rand_percent(40)) {
               goto END;
       }
B:
       rooms[rn].is_room = R_ROOM;

       for (i = top_row; i <= bottom_row; i++) {
               for (j = left_col; j <= right_col; j++) {
                       if ((i == top_row) || (i == bottom_row)) {
                               ch = HORWALL;
                       } else if (     ((i != top_row) && (i != bottom_row)) &&
                                               ((j == left_col) || (j == right_col))) {
                               ch = VERTWALL;
                       } else {
                               ch = FLOOR;
                       }
                       dungeon[i][j] = ch;
               }
       }
END:
       rooms[rn].top_row = top_row;
       rooms[rn].bottom_row = bottom_row;
       rooms[rn].left_col = left_col;
       rooms[rn].right_col = right_col;
}

static int
connect_rooms(short room1, short room2)
{
       short row1, col1, row2, col2, dir;

       if ((!(rooms[room1].is_room & (R_ROOM | R_MAZE))) ||
               (!(rooms[room2].is_room & (R_ROOM | R_MAZE)))) {
               return(0);
       }
       if (same_row(room1, room2) &&
               (rooms[room1].left_col > rooms[room2].right_col)) {
               put_door(&rooms[room1], LEFT, &row1, &col1);
               put_door(&rooms[room2], RIGHT, &row2, &col2);
               dir = LEFT;
       } else if (same_row(room1, room2) &&
               (rooms[room2].left_col > rooms[room1].right_col)) {
               put_door(&rooms[room1], RIGHT, &row1, &col1);
               put_door(&rooms[room2], LEFT, &row2, &col2);
               dir = RIGHT;
       } else if (same_col(room1, room2) &&
               (rooms[room1].top_row > rooms[room2].bottom_row)) {
               put_door(&rooms[room1], UPWARD, &row1, &col1);
               put_door(&rooms[room2], DOWN, &row2, &col2);
               dir = UPWARD;
       } else if (same_col(room1, room2) &&
               (rooms[room2].top_row > rooms[room1].bottom_row)) {
               put_door(&rooms[room1], DOWN, &row1, &col1);
               put_door(&rooms[room2], UPWARD, &row2, &col2);
               dir = DOWN;
       } else {
               return(0);
       }

       do {
               draw_simple_passage(row1, col1, row2, col2, dir);
       } while (rand_percent(4));

       rooms[room1].doors[dir/2].oth_room = room2;
       rooms[room1].doors[dir/2].oth_row = row2;
       rooms[room1].doors[dir/2].oth_col = col2;

       rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_room = room1;
       rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_row = row1;
       rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_col = col1;
       return(1);
}

void
clear_level(void)
{
       short i, j;

       for (i = 0; i < MAXROOMS; i++) {
               rooms[i].is_room = R_NOTHING;
               for (j = 0; j < 4; j++) {
                       rooms[i].doors[j].oth_room = NO_ROOM;
               }
       }

       for (i = 0; i < MAX_TRAPS; i++) {
               traps[i].trap_type = NO_TRAP;
       }
       for (i = 0; i < DROWS; i++) {
               for (j = 0; j < DCOLS; j++) {
                       dungeon[i][j] = NOTHING;
               }
       }
       detect_monster = see_invisible = 0;
       being_held = bear_trap = 0;
       party_room = NO_ROOM;
       rogue.row = rogue.col = -1;
       clear();
}

static void
put_door(room *rm, short dir, short *row, short *col)
{
       short wall_width;

       wall_width = (rm->is_room & R_MAZE) ? 0 : 1;

       switch(dir) {
       case UPWARD:
       case DOWN:
               *row = ((dir == UPWARD) ? rm->top_row : rm->bottom_row);
               do {
                       *col = get_rand(rm->left_col+wall_width,
                               rm->right_col-wall_width);
               } while (!(dungeon[*row][*col] & (HORWALL | TUNNEL)));
               break;
       case RIGHT:
       case LEFT:
               *col = (dir == LEFT) ? rm->left_col : rm->right_col;
               do {
                       *row = get_rand(rm->top_row+wall_width,
                               rm->bottom_row-wall_width);
               } while (!(dungeon[*row][*col] & (VERTWALL | TUNNEL)));
               break;
       }
       if (rm->is_room & R_ROOM) {
               dungeon[*row][*col] = DOOR;
       }
       if ((cur_level > 2) && rand_percent(HIDE_PERCENT)) {
               dungeon[*row][*col] |= HIDDEN;
       }
       rm->doors[dir/2].door_row = *row;
       rm->doors[dir/2].door_col = *col;
}

static void
draw_simple_passage(short row1, short col1, short row2, short col2, short dir)
{
       short i, middle, t;

       if ((dir == LEFT) || (dir == RIGHT)) {
               if (col1 > col2) {
                       SWAP(row1, row2);
                       SWAP(col1, col2);
               }
               middle = get_rand(col1+1, col2-1);
               for (i = col1+1; i != middle; i++) {
                       dungeon[row1][i] = TUNNEL;
               }
               for (i = row1; i != row2; i += (row1 > row2) ? -1 : 1) {
                       dungeon[i][middle] = TUNNEL;
               }
               for (i = middle; i != col2; i++) {
                       dungeon[row2][i] = TUNNEL;
               }
       } else {
               if (row1 > row2) {
                       SWAP(row1, row2);
                       SWAP(col1, col2);
               }
               middle = get_rand(row1+1, row2-1);
               for (i = row1+1; i != middle; i++) {
                       dungeon[i][col1] = TUNNEL;
               }
               for (i = col1; i != col2; i += (col1 > col2) ? -1 : 1) {
                       dungeon[middle][i] = TUNNEL;
               }
               for (i = middle; i != row2; i++) {
                       dungeon[i][col2] = TUNNEL;
               }
       }
       if (rand_percent(HIDE_PERCENT)) {
               hide_boxed_passage(row1, col1, row2, col2, 1);
       }
}

static int
same_row(int room1, int room2)
{
       return((room1 / 3) == (room2 / 3));
}

static int
same_col(int room1, int room2)
{
       return((room1 % 3) == (room2 % 3));
}

static void
add_mazes(void)
{
       short i, j;
       short start;
       short maze_percent;

       if (cur_level > 1) {
               start = get_rand(0, (MAXROOMS-1));
               maze_percent = (cur_level * 5) / 4;

               if (cur_level > 15) {
                       maze_percent += cur_level;
               }
               for (i = 0; i < MAXROOMS; i++) {
                       j = ((start + i) % MAXROOMS);
                       if (rooms[j].is_room & R_NOTHING) {
                               if (rand_percent(maze_percent)) {
                               rooms[j].is_room = R_MAZE;
                               make_maze(get_rand(rooms[j].top_row+1, rooms[j].bottom_row-1),
                                       get_rand(rooms[j].left_col+1, rooms[j].right_col-1),
                                       rooms[j].top_row, rooms[j].bottom_row,
                                       rooms[j].left_col, rooms[j].right_col);
                               hide_boxed_passage(rooms[j].top_row, rooms[j].left_col,
                                       rooms[j].bottom_row, rooms[j].right_col,
                                       get_rand(0, 2));
                               }
                       }
               }
       }
}

static void
fill_out_level(void)
{
       short i, rn;

       mix_random_rooms();

       r_de = NO_ROOM;

       for (i = 0; i < MAXROOMS; i++) {
               rn = random_rooms[i];
               if ((rooms[rn].is_room & R_NOTHING) ||
                       ((rooms[rn].is_room & R_CROSS) && coin_toss())) {
                       fill_it(rn, 1);
               }
       }
       if (r_de != NO_ROOM) {
               fill_it(r_de, 0);
       }
}

static void
fill_it(int rn, boolean do_rec_de)
{
       short i, tunnel_dir, door_dir, drow, dcol;
       short target_room, rooms_found = 0;
       short srow, scol, t;
       static short offsets[4] = {-1, 1, 3, -3};
       boolean did_this = 0;

       for (i = 0; i < 10; i++) {
               srow = get_rand(0, 3);
               scol = get_rand(0, 3);
               t = offsets[srow];
               offsets[srow] = offsets[scol];
               offsets[scol] = t;
       }
       for (i = 0; i < 4; i++) {

               target_room = rn + offsets[i];

               if (((target_room < 0) || (target_room >= MAXROOMS)) ||
                       (!(same_row(rn,target_room) || same_col(rn,target_room))) ||
                       (!(rooms[target_room].is_room & (R_ROOM | R_MAZE)))) {
                       continue;
               }
               if (same_row(rn, target_room)) {
                       tunnel_dir = (rooms[rn].left_col < rooms[target_room].left_col) ?
                               RIGHT : LEFT;
               } else {
                       tunnel_dir = (rooms[rn].top_row < rooms[target_room].top_row) ?
                               DOWN : UPWARD;
               }
               door_dir = ((tunnel_dir + 4) % DIRS);
               if (rooms[target_room].doors[door_dir/2].oth_room != NO_ROOM) {
                       continue;
               }
               if (((!do_rec_de) || did_this) ||
                       (!mask_room(rn, &srow, &scol, TUNNEL))) {
                       srow = (rooms[rn].top_row + rooms[rn].bottom_row) / 2;
                       scol = (rooms[rn].left_col + rooms[rn].right_col) / 2;
               }
               put_door(&rooms[target_room], door_dir, &drow, &dcol);
               rooms_found++;
               draw_simple_passage(srow, scol, drow, dcol, tunnel_dir);
               rooms[rn].is_room = R_DEADEND;
               dungeon[srow][scol] = TUNNEL;

               if ((i < 3) && (!did_this)) {
                       did_this = 1;
                       if (coin_toss()) {
                               continue;
                       }
               }
               if ((rooms_found < 2) && do_rec_de) {
                       recursive_deadend(rn, offsets, srow, scol);
               }
               break;
       }
}

static void
recursive_deadend(short rn, const short *offsets, short srow, short scol)
{
       short i, de;
       short drow, dcol, tunnel_dir;

       rooms[rn].is_room = R_DEADEND;
       dungeon[srow][scol] = TUNNEL;

       for (i = 0; i < 4; i++) {
               de = rn + offsets[i];
               if (((de < 0) || (de >= MAXROOMS)) ||
                       (!(same_row(rn, de) || same_col(rn, de)))) {
                       continue;
               }
               if (!(rooms[de].is_room & R_NOTHING)) {
                       continue;
               }
               drow = (rooms[de].top_row + rooms[de].bottom_row) / 2;
               dcol = (rooms[de].left_col + rooms[de].right_col) / 2;
               if (same_row(rn, de)) {
                       tunnel_dir = (rooms[rn].left_col < rooms[de].left_col) ?
                               RIGHT : LEFT;
               } else {
                       tunnel_dir = (rooms[rn].top_row < rooms[de].top_row) ?
                               DOWN : UPWARD;
               }
               draw_simple_passage(srow, scol, drow, dcol, tunnel_dir);
               r_de = de;
               recursive_deadend(de, offsets, drow, dcol);
       }
}

static boolean
mask_room(short rn, short *row, short *col, unsigned short mask)
{
       short i, j;

       for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) {
               for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) {
                       if (dungeon[i][j] & mask) {
                               *row = i;
                               *col = j;
                               return(1);
                       }
               }
       }
       return(0);
}

static void
make_maze(short r, short c, short tr, short br, short lc, short rc)
{
       char dirs[4];
       short i, t;

       dirs[0] = UPWARD;
       dirs[1] = DOWN;
       dirs[2] = LEFT;
       dirs[3] = RIGHT;

       dungeon[r][c] = TUNNEL;

       if (rand_percent(20)) {
               for (i = 0; i < 10; i++) {
                       short t1, t2;

                       t1 = get_rand(0, 3);
                       t2 = get_rand(0, 3);

                       SWAP(dirs[t1], dirs[t2]);
               }
       }
       for (i = 0; i < 4; i++) {
               switch(dirs[i]) {
               case UPWARD:
                       if (((r-1) >= tr) &&
                               (dungeon[r-1][c] != TUNNEL) &&
                               (dungeon[r-1][c-1] != TUNNEL) &&
                               (dungeon[r-1][c+1] != TUNNEL) &&
                               (dungeon[r-2][c] != TUNNEL)) {
                               make_maze((r-1), c, tr, br, lc, rc);
                       }
                       break;
               case DOWN:
                       if (((r+1) <= br) &&
                               (dungeon[r+1][c] != TUNNEL) &&
                               (dungeon[r+1][c-1] != TUNNEL) &&
                               (dungeon[r+1][c+1] != TUNNEL) &&
                               (dungeon[r+2][c] != TUNNEL)) {
                               make_maze((r+1), c, tr, br, lc, rc);
                       }
                       break;
               case LEFT:
                       if (((c-1) >= lc) &&
                               (dungeon[r][c-1] != TUNNEL) &&
                               (dungeon[r-1][c-1] != TUNNEL) &&
                               (dungeon[r+1][c-1] != TUNNEL) &&
                               (dungeon[r][c-2] != TUNNEL)) {
                               make_maze(r, (c-1), tr, br, lc, rc);
                       }
                       break;
               case RIGHT:
                       if (((c+1) <= rc) &&
                               (dungeon[r][c+1] != TUNNEL) &&
                               (dungeon[r-1][c+1] != TUNNEL) &&
                               (dungeon[r+1][c+1] != TUNNEL) &&
                               (dungeon[r][c+2] != TUNNEL)) {
                               make_maze(r, (c+1), tr, br, lc, rc);
                       }
                       break;
               }
       }
}

static void
hide_boxed_passage(short row1, short col1, short row2, short col2, short n)
{
       short i, j, t;
       short row, col, row_cut, col_cut;
       short h, w;

       if (cur_level > 2) {
               if (row1 > row2) {
                       SWAP(row1, row2);
               }
               if (col1 > col2) {
                       SWAP(col1, col2);
               }
               h = row2 - row1;
               w = col2 - col1;

               if ((w >= 5) || (h >= 5)) {
                       row_cut = ((h >= 2) ? 1 : 0);
                       col_cut = ((w >= 2) ? 1 : 0);

                       for (i = 0; i < n; i++) {
                               for (j = 0; j < 10; j++) {
                                       row = get_rand(row1 + row_cut, row2 - row_cut);
                                       col = get_rand(col1 + col_cut, col2 - col_cut);
                                       if (dungeon[row][col] == TUNNEL) {
                                               dungeon[row][col] |= HIDDEN;
                                               break;
                                       }
                               }
                       }
               }
       }
}

/*
* try not to put in room NR
*/
void
put_player(short nr)
{
       short rn = nr, misses;
       short row, col;

       for (misses = 0; ((misses < 2) && (rn == nr)); misses++) {
               gr_row_col(&row, &col, (FLOOR | TUNNEL | OBJECT | STAIRS));
               rn = get_room_number(row, col);
       }
       rogue.row = row;
       rogue.col = col;

       if (dungeon[rogue.row][rogue.col] & TUNNEL) {
               cur_room = PASSAGE;
       } else {
               cur_room = rn;
       }
       if (cur_room != PASSAGE) {
               light_up_room(cur_room);
       } else {
               light_passage(rogue.row, rogue.col);
       }
       rn = get_room_number(rogue.row, rogue.col);
       wake_room(rn, 1, rogue.row, rogue.col);
       if (new_level_message) {
               messagef(0, "%s", new_level_message);
               new_level_message = NULL;
       }
       mvaddch(rogue.row, rogue.col, rogue.fchar);
}

int
drop_check(void)
{
       if (wizard) {
               return(1);
       }
       if (dungeon[rogue.row][rogue.col] & STAIRS) {
               if (levitate) {
                       messagef(0, "you're floating in the air!");
                       return(0);
               }
               return(1);
       }
       messagef(0, "I see no way down");
       return(0);
}

int
check_up(void)
{
       if (!wizard) {
               if (!(dungeon[rogue.row][rogue.col] & STAIRS)) {
                       messagef(0, "I see no way up");
                       return(0);
               }
               if (!has_amulet()) {
                       messagef(0, "your way is magically blocked");
                       return(0);
               }
       }
       new_level_message = "you feel a wrenching sensation in your gut";
       if (cur_level == 1) {
               win();
       } else {
               cur_level -= 2;
               return(1);
       }
       return(0);
}

void
add_exp(int e, boolean promotion)
{
       short new_exp;
       short i, hp;

       rogue.exp_points += e;

       if (rogue.exp_points >= level_points[rogue.exp-1]) {
               new_exp = get_exp_level(rogue.exp_points);
               if (rogue.exp_points > MAX_EXP) {
                       rogue.exp_points = MAX_EXP + 1;
               }
               for (i = rogue.exp+1; i <= new_exp; i++) {
                       messagef(0, "welcome to level %d", i);
                       if (promotion) {
                               hp = hp_raise();
                               rogue.hp_current += hp;
                               rogue.hp_max += hp;
                       }
                       rogue.exp = i;
                       print_stats(STAT_HP | STAT_EXP);
               }
       } else {
               print_stats(STAT_EXP);
       }
}

static int
get_exp_level(long e)
{
       short i;

       for (i = 0; i < (MAX_EXP_LEVEL - 1); i++) {
               if (level_points[i] > e) {
                       break;
               }
       }
       return(i+1);
}

int
hp_raise(void)
{
       int hp;

       hp = (wizard ? 10 : get_rand(3, 10));
       return(hp);
}

void
show_average_hp(void)
{
       float real_average;
       float effective_average;

       if (rogue.exp == 1) {
               real_average = effective_average = 0.00;
       } else {
               real_average = (float)
                       ((rogue.hp_max - extra_hp - INIT_HP) + less_hp) / (rogue.exp - 1);
               effective_average = (float)(rogue.hp_max - INIT_HP) / (rogue.exp - 1);

       }
       messagef(0, "R-Hp: %.2f, E-Hp: %.2f (!: %d, V: %d)", real_average,
               effective_average, extra_hp, less_hp);
}

static void
mix_random_rooms(void)
{
       short i, t;
       short x, y;

       for (i = 0; i < (3 * MAXROOMS); i++) {
               do {
                       x = get_rand(0, (MAXROOMS-1));
                       y = get_rand(0, (MAXROOMS-1));
               } while (x == y);
               SWAP(random_rooms[x], random_rooms[y]);
       }
}