mirror of https://github.com/nealey/rogue
455 lines
8.4 KiB
C
455 lines
8.4 KiB
C
/*
|
|
* Create the layout for the new level
|
|
*
|
|
* @(#)startup.c 4.45 (Berkeley) 02/05/99
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <curses.h>
|
|
#include "netprot.h"
|
|
|
|
typedef struct spot { /* position matrix for maze positions */
|
|
int nexits;
|
|
coord exits[4];
|
|
int used;
|
|
} SPOT;
|
|
|
|
#define GOLDGRP 1
|
|
|
|
/*
|
|
* do_rooms:
|
|
* Create rooms and corridors with a connectivity graph
|
|
*/
|
|
void
|
|
do_rooms(void)
|
|
{
|
|
int i;
|
|
struct room *rp;
|
|
THING *tp;
|
|
int left_out;
|
|
static coord top;
|
|
coord bsze; /* maximum room size */
|
|
coord mp;
|
|
|
|
bsze.x = NUMCOLS / 3;
|
|
bsze.y = NUMLINES / 3;
|
|
/*
|
|
* Clear things for a new level
|
|
*/
|
|
for (rp = Rooms; rp < &Rooms[MAXROOMS]; rp++)
|
|
{
|
|
rp->r_goldval = 0;
|
|
rp->r_nexits = 0;
|
|
rp->r_flags = 0;
|
|
}
|
|
/*
|
|
* Put the gone rooms, if any, on the level
|
|
*/
|
|
left_out = rnd(4);
|
|
for (i = 0; i < left_out; i++)
|
|
Rooms[rnd_room()].r_flags |= ISGONE;
|
|
/*
|
|
* dig and populate all the rooms on the level
|
|
*/
|
|
for (i = 0, rp = Rooms; i < MAXROOMS; rp++, i++)
|
|
{
|
|
/*
|
|
* Find upper left corner of box that this room goes in
|
|
*/
|
|
top.x = (i % 3) * bsze.x + 1;
|
|
top.y = (i / 3) * bsze.y;
|
|
if (rp->r_flags & ISGONE)
|
|
{
|
|
/*
|
|
* Place a gone room. Make certain that there is a blank line
|
|
* for passage drawing.
|
|
*/
|
|
do
|
|
{
|
|
rp->r_pos.x = top.x + rnd(bsze.x - 2) + 1;
|
|
rp->r_pos.y = top.y + rnd(bsze.y - 2) + 1;
|
|
rp->r_max.x = -NUMCOLS;
|
|
rp->r_max.y = -NUMLINES;
|
|
} until (rp->r_pos.y > 0 && rp->r_pos.y < NUMLINES-1);
|
|
continue;
|
|
}
|
|
/*
|
|
* set room type
|
|
*/
|
|
if (rnd(10) < Level - 1)
|
|
{
|
|
rp->r_flags |= ISDARK; /* dark room */
|
|
if (rnd(15) == 0)
|
|
rp->r_flags = ISMAZE; /* maze room */
|
|
}
|
|
/*
|
|
* Find a place and size for a random room
|
|
*/
|
|
if (rp->r_flags & ISMAZE)
|
|
{
|
|
rp->r_max.x = bsze.x - 1;
|
|
rp->r_max.y = bsze.y - 1;
|
|
if ((rp->r_pos.x = top.x) == 1)
|
|
rp->r_pos.x = 0;
|
|
if ((rp->r_pos.y = top.y) == 0)
|
|
{
|
|
rp->r_pos.y++;
|
|
rp->r_max.y--;
|
|
}
|
|
}
|
|
else
|
|
do
|
|
{
|
|
rp->r_max.x = rnd(bsze.x - 4) + 4;
|
|
rp->r_max.y = rnd(bsze.y - 4) + 4;
|
|
rp->r_pos.x = top.x + rnd(bsze.x - rp->r_max.x);
|
|
rp->r_pos.y = top.y + rnd(bsze.y - rp->r_max.y);
|
|
} until (rp->r_pos.y != 0);
|
|
draw_room(rp);
|
|
/*
|
|
* Put the gold in
|
|
*/
|
|
if (rnd(2) == 0 && (!Amulet || Level >= Max_level))
|
|
{
|
|
THING *gold;
|
|
|
|
gold = new_item();
|
|
gold->o_goldval = rp->r_goldval = GOLDCALC;
|
|
find_floor(rp, &rp->r_gold, FALSE, FALSE);
|
|
gold->o_pos = rp->r_gold;
|
|
chat(rp->r_gold.y, rp->r_gold.x) = GOLD;
|
|
gold->o_flags = ISMANY;
|
|
gold->o_group = GOLDGRP;
|
|
gold->o_type = GOLD;
|
|
attach(Lvl_obj, gold);
|
|
}
|
|
/*
|
|
* Put the monster in
|
|
*/
|
|
if (rnd(100) < (rp->r_goldval > 0 ? 80 : 25))
|
|
{
|
|
tp = new_item();
|
|
find_floor(rp, &mp, FALSE, TRUE);
|
|
new_monster(tp, randmonster(FALSE), &mp);
|
|
give_pack(tp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* draw_room:
|
|
* Draw a box around a room and lay down the floor for normal
|
|
* rooms; for maze rooms, draw maze.
|
|
*/
|
|
void
|
|
draw_room(struct room *rp)
|
|
{
|
|
int y, x;
|
|
|
|
if (rp->r_flags & ISMAZE)
|
|
do_maze(rp);
|
|
else
|
|
{
|
|
vert(rp, rp->r_pos.x); /* Draw left side */
|
|
vert(rp, rp->r_pos.x + rp->r_max.x - 1); /* Draw right side */
|
|
horiz(rp, rp->r_pos.y); /* Draw top */
|
|
horiz(rp, rp->r_pos.y + rp->r_max.y - 1); /* Draw bottom */
|
|
|
|
/*
|
|
* Put the floor down
|
|
*/
|
|
for (y = rp->r_pos.y + 1; y < rp->r_pos.y + rp->r_max.y - 1; y++)
|
|
for (x = rp->r_pos.x + 1; x < rp->r_pos.x + rp->r_max.x - 1; x++)
|
|
chat(y, x) = FLOOR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* vert:
|
|
* Draw a vertical line
|
|
*/
|
|
void
|
|
vert(struct room *rp, int startx)
|
|
{
|
|
int y;
|
|
|
|
for (y = rp->r_pos.y + 1; y <= rp->r_max.y + rp->r_pos.y - 1; y++)
|
|
chat(y, startx) = '|';
|
|
}
|
|
|
|
/*
|
|
* horiz:
|
|
* Draw a horizontal line
|
|
*/
|
|
void
|
|
horiz(struct room *rp, int starty)
|
|
{
|
|
int x;
|
|
|
|
for (x = rp->r_pos.x; x <= rp->r_pos.x + rp->r_max.x - 1; x++)
|
|
chat(starty, x) = '-';
|
|
}
|
|
|
|
/*
|
|
* do_maze:
|
|
* Dig a maze
|
|
*/
|
|
|
|
static int Maxy, Maxx, Starty, Startx;
|
|
|
|
static SPOT Maze[NUMLINES/3+1][NUMCOLS/3+1];
|
|
|
|
void
|
|
do_maze(struct room *rp)
|
|
{
|
|
SPOT *sp;
|
|
int starty, startx;
|
|
static coord pos;
|
|
|
|
for (sp = &Maze[0][0]; sp < &Maze[NUMLINES / 3][NUMCOLS / 3 + 1]; sp++)
|
|
{
|
|
sp->used = FALSE;
|
|
sp->nexits = 0;
|
|
}
|
|
|
|
Maxy = rp->r_max.y;
|
|
Maxx = rp->r_max.x;
|
|
Starty = rp->r_pos.y;
|
|
Startx = rp->r_pos.x;
|
|
starty = (rnd(rp->r_max.y) / 2) * 2;
|
|
startx = (rnd(rp->r_max.x) / 2) * 2;
|
|
pos.y = starty + Starty;
|
|
pos.x = startx + Startx;
|
|
putpass(&pos);
|
|
dig(starty, startx);
|
|
}
|
|
|
|
/*
|
|
* dig:
|
|
* Dig out from around where we are now, if possible
|
|
*/
|
|
void
|
|
dig(int y, int x)
|
|
{
|
|
coord *cp;
|
|
int cnt, newy, newx, nexty, nextx;
|
|
static coord pos;
|
|
static coord del[4] = {
|
|
{2, 0}, {-2, 0}, {0, 2}, {0, -2}
|
|
};
|
|
|
|
for (;;)
|
|
{
|
|
cnt = 0;
|
|
for (cp = del; cp < &del[4]; cp++)
|
|
{
|
|
newy = y + cp->y;
|
|
newx = x + cp->x;
|
|
if (newy < 0 || newy > Maxy || newx < 0 || newx > Maxx)
|
|
continue;
|
|
if (flat(newy + Starty, newx + Startx) & F_PASS)
|
|
continue;
|
|
if (rnd(++cnt) == 0)
|
|
{
|
|
nexty = newy;
|
|
nextx = newx;
|
|
}
|
|
}
|
|
if (cnt == 0)
|
|
return;
|
|
accnt_maze(y, x, nexty, nextx);
|
|
accnt_maze(nexty, nextx, y, x);
|
|
if (nexty == y)
|
|
{
|
|
pos.y = y + Starty;
|
|
if (nextx - x < 0)
|
|
pos.x = nextx + Startx + 1;
|
|
else
|
|
pos.x = nextx + Startx - 1;
|
|
}
|
|
else
|
|
{
|
|
pos.x = x + Startx;
|
|
if (nexty - y < 0)
|
|
pos.y = nexty + Starty + 1;
|
|
else
|
|
pos.y = nexty + Starty - 1;
|
|
}
|
|
putpass(&pos);
|
|
pos.y = nexty + Starty;
|
|
pos.x = nextx + Startx;
|
|
putpass(&pos);
|
|
dig(nexty, nextx);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* accnt_maze:
|
|
* Account for maze exits
|
|
*/
|
|
void
|
|
accnt_maze(int y, int x, int ny, int nx)
|
|
{
|
|
SPOT *sp;
|
|
coord *cp;
|
|
|
|
sp = &Maze[y][x];
|
|
for (cp = sp->exits; cp < &sp->exits[sp->nexits]; cp++)
|
|
if (cp->y == ny && cp->x == nx)
|
|
return;
|
|
cp->y = ny;
|
|
cp->x = nx;
|
|
}
|
|
|
|
/*
|
|
* rnd_pos:
|
|
* Pick a random spot in a room
|
|
*/
|
|
void
|
|
rnd_pos(struct room *rp, coord *cp)
|
|
{
|
|
cp->x = rp->r_pos.x + rnd(rp->r_max.x - 2) + 1;
|
|
cp->y = rp->r_pos.y + rnd(rp->r_max.y - 2) + 1;
|
|
}
|
|
|
|
/*
|
|
* find_floor:
|
|
* Find a valid floor spot in this room. If rp is NULL, then
|
|
* pick a new room each time around the loop.
|
|
*/
|
|
bool
|
|
find_floor(struct room *rp, coord *cp, int limit, bool monst)
|
|
{
|
|
PLACE *pp;
|
|
int cnt;
|
|
char compchar;
|
|
bool pickroom;
|
|
|
|
if (!(pickroom = (rp == NULL)))
|
|
compchar = ((rp->r_flags & ISMAZE) ? PASSAGE : FLOOR);
|
|
cnt = limit;
|
|
for (;;)
|
|
{
|
|
if (limit && cnt-- == 0)
|
|
return FALSE;
|
|
if (pickroom)
|
|
{
|
|
rp = &Rooms[rnd_room()];
|
|
compchar = ((rp->r_flags & ISMAZE) ? PASSAGE : FLOOR);
|
|
}
|
|
rnd_pos(rp, cp);
|
|
pp = INDEX(cp->y, cp->x);
|
|
if (monst)
|
|
{
|
|
if (pp->p_monst == NULL && step_ok(pp->p_ch))
|
|
return TRUE;
|
|
}
|
|
else if (pp->p_ch == compchar)
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* enter_room:
|
|
* Code that is executed whenver you appear in a room
|
|
*/
|
|
void
|
|
enter_room(coord *cp)
|
|
{
|
|
struct room *rp;
|
|
THING *tp;
|
|
int y, x;
|
|
char ch;
|
|
|
|
rp = Proom = roomin(cp);
|
|
door_open(rp);
|
|
if (!(rp->r_flags & ISDARK) && !on(Player, ISBLIND))
|
|
for (y = rp->r_pos.y; y < rp->r_max.y + rp->r_pos.y; y++)
|
|
{
|
|
move(y, rp->r_pos.x);
|
|
for (x = rp->r_pos.x; x < rp->r_max.x + rp->r_pos.x; x++)
|
|
{
|
|
tp = moat(y, x);
|
|
ch = chat(y, x);
|
|
if (tp == NULL)
|
|
if (inch() != ch)
|
|
addch(ch);
|
|
else
|
|
move(y, x + 1);
|
|
else
|
|
{
|
|
tp->t_oldch = ch;
|
|
if (!see_monst(tp))
|
|
if (on(Player, SEEMONST))
|
|
{
|
|
standout();
|
|
addch(tp->t_disguise);
|
|
standend();
|
|
}
|
|
else
|
|
addch(ch);
|
|
else
|
|
addch(tp->t_disguise);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* leave_room:
|
|
* Code for when we exit a room
|
|
*/
|
|
void
|
|
leave_room(coord *cp)
|
|
{
|
|
PLACE *pp;
|
|
struct room *rp;
|
|
int y, x;
|
|
char floor;
|
|
char ch;
|
|
|
|
rp = Proom;
|
|
|
|
if (rp->r_flags & ISMAZE)
|
|
return;
|
|
|
|
if (rp->r_flags & ISGONE)
|
|
floor = PASSAGE;
|
|
else if (!(rp->r_flags & ISDARK) || on(Player, ISBLIND))
|
|
floor = FLOOR;
|
|
else
|
|
floor = ' ';
|
|
|
|
Proom = &Passages[flat(cp->y, cp->x) & F_PNUM];
|
|
for (y = rp->r_pos.y; y < rp->r_max.y + rp->r_pos.y; y++)
|
|
for (x = rp->r_pos.x; x < rp->r_max.x + rp->r_pos.x; x++)
|
|
{
|
|
move(y, x);
|
|
switch (ch = inch())
|
|
{
|
|
case FLOOR:
|
|
if (floor == ' ' && ch != ' ')
|
|
addch(' ');
|
|
break;
|
|
default:
|
|
/*
|
|
* to check for monster, we have to strip out
|
|
* standout bit
|
|
*/
|
|
if (isupper(toascii(ch)))
|
|
{
|
|
if (on(Player, SEEMONST))
|
|
{
|
|
standout();
|
|
addch(ch);
|
|
standend();
|
|
break;
|
|
}
|
|
pp = INDEX(cp->y, cp->x);
|
|
addch(pp->p_ch == DOOR ? DOOR : floor);
|
|
}
|
|
}
|
|
}
|
|
door_open(rp);
|
|
}
|