From 130f7c5738d6497e66b2b2eef213e435aaeabea0 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:53 +0000 Subject: [PATCH] *** empty log message *** --- src/sp_lev.c | 2656 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2656 insertions(+) create mode 100644 src/sp_lev.c diff --git a/src/sp_lev.c b/src/sp_lev.c new file mode 100644 index 000000000..9b1c71967 --- /dev/null +++ b/src/sp_lev.c @@ -0,0 +1,2656 @@ +/* SCCS Id: @(#)sp_lev.c 3.3 2001/09/06 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the various functions that are related to the special + * levels. + * It contains also the special level loader. + * + */ + +#include "hack.h" +#include "dlb.h" +/* #define DEBUG */ /* uncomment to enable code debugging */ + +#ifdef DEBUG +# ifdef WIZARD +#define debugpline if (wizard) pline +# else +#define debugpline pline +# endif +#endif + +#include "sp_lev.h" +#include "rect.h" + +extern void FDECL(mkmap, (lev_init *)); + +STATIC_DCL void FDECL(get_room_loc, (schar *, schar *, struct mkroom *)); +STATIC_DCL void FDECL(get_free_room_loc, (schar *, schar *, struct mkroom *)); +STATIC_DCL void FDECL(create_trap, (trap *, struct mkroom *)); +STATIC_DCL int FDECL(noncoalignment, (ALIGNTYP_P)); +STATIC_DCL void FDECL(create_monster, (monster *, struct mkroom *)); +STATIC_DCL void FDECL(create_object, (object *, struct mkroom *)); +STATIC_DCL void FDECL(create_engraving, (engraving *,struct mkroom *)); +STATIC_DCL void FDECL(create_stairs, (stair *, struct mkroom *)); +STATIC_DCL void FDECL(create_altar, (altar *, struct mkroom *)); +STATIC_DCL void FDECL(create_gold, (gold *, struct mkroom *)); +STATIC_DCL void FDECL(create_feature, (int,int,struct mkroom *,int)); +STATIC_DCL boolean FDECL(search_door, (struct mkroom *, xchar *, xchar *, + XCHAR_P, int)); +STATIC_DCL void NDECL(fix_stair_rooms); +STATIC_DCL void FDECL(create_corridor, (corridor *)); + +STATIC_DCL boolean FDECL(create_subroom, (struct mkroom *, XCHAR_P, XCHAR_P, + XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P)); + +#define LEFT 1 +#define H_LEFT 2 +#define CENTER 3 +#define H_RIGHT 4 +#define RIGHT 5 + +#define TOP 1 +#define BOTTOM 5 + +#define sq(x) ((x)*(x)) + +#define XLIM 4 +#define YLIM 3 + +#define Fread (void)dlb_fread +#define Fgetc (schar)dlb_fgetc +#define New(type) (type *) alloc(sizeof(type)) +#define NewTab(type, size) (type **) alloc(sizeof(type *) * (unsigned)size) +#define Free(ptr) if(ptr) free((genericptr_t) (ptr)) + +static NEARDATA walk walklist[50]; +extern int min_rx, max_rx, min_ry, max_ry; /* from mkmap.c */ + +static char Map[COLNO][ROWNO]; +static char robjects[10], rloc_x[10], rloc_y[10], rmonst[10]; +static aligntyp ralign[3] = { AM_CHAOTIC, AM_NEUTRAL, AM_LAWFUL }; +static NEARDATA xchar xstart, ystart; +static NEARDATA char xsize, ysize; + +STATIC_DCL void FDECL(set_wall_property, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,int)); +STATIC_DCL int NDECL(rnddoor); +STATIC_DCL int NDECL(rndtrap); +STATIC_DCL void FDECL(get_location, (schar *,schar *,int)); +STATIC_DCL void FDECL(sp_lev_shuffle, (char *,char *,int)); +STATIC_DCL void FDECL(light_region, (region *)); +STATIC_DCL void FDECL(load_common_data, (dlb *,int)); +STATIC_DCL void FDECL(load_one_monster, (dlb *,monster *)); +STATIC_DCL void FDECL(load_one_object, (dlb *,object *)); +STATIC_DCL void FDECL(load_one_engraving, (dlb *,engraving *)); +STATIC_DCL boolean FDECL(load_rooms, (dlb *)); +STATIC_DCL void FDECL(maze1xy, (coord *,int)); +STATIC_DCL boolean FDECL(load_maze, (dlb *)); +STATIC_DCL void FDECL(create_door, (room_door *, struct mkroom *)); +STATIC_DCL void FDECL(free_rooms,(room **, int)); +STATIC_DCL void FDECL(build_room, (room *, room*)); + +char *lev_message = 0; +lev_region *lregions = 0; +int num_lregions = 0; +lev_init init_lev; + +/* + * Make walls of the area (x1, y1, x2, y2) non diggable/non passwall-able + */ + +STATIC_OVL void +set_wall_property(x1,y1,x2,y2, prop) +xchar x1, y1, x2, y2; +int prop; +{ + register xchar x, y; + + for(y = y1; y <= y2; y++) + for(x = x1; x <= x2; x++) + if(IS_STWALL(levl[x][y].typ)) + levl[x][y].wall_info |= prop; +} + +/* + * Choose randomly the state (nodoor, open, closed or locked) for a door + */ +STATIC_OVL int +rnddoor() +{ + int i = 1 << rn2(5); + i >>= 1; + return i; +} + +/* + * Select a random trap + */ +STATIC_OVL int +rndtrap() +{ + int rtrap; + + do { + rtrap = rnd(TRAPNUM-1); + switch (rtrap) { + case HOLE: /* no random holes on special levels */ + case MAGIC_PORTAL: rtrap = NO_TRAP; + break; + case TRAPDOOR: if (!Can_dig_down(&u.uz)) rtrap = NO_TRAP; + break; + case LEVEL_TELEP: + case TELEP_TRAP: if (level.flags.noteleport) rtrap = NO_TRAP; + break; + case ROLLING_BOULDER_TRAP: + case ROCKTRAP: if (In_endgame(&u.uz)) rtrap = NO_TRAP; + break; + } + } while (rtrap == NO_TRAP); + return rtrap; +} + +/* + * Coordinates in special level files are handled specially: + * + * if x or y is -11, we generate a random coordinate. + * if x or y is between -1 and -10, we read one from the corresponding + * register (x0, x1, ... x9). + * if x or y is nonnegative, we convert it from relative to the local map + * to global coordinates. + * The "humidity" flag is used to insure that engravings aren't + * created underwater, or eels on dry land. + */ +#define DRY 0x1 +#define WET 0x2 + +STATIC_DCL boolean FDECL(is_ok_location, (SCHAR_P, SCHAR_P, int)); + +STATIC_OVL void +get_location(x, y, humidity) +schar *x, *y; +int humidity; +{ + int cpt = 0; + + if (*x >= 0) { /* normal locations */ + *x += xstart; + *y += ystart; + } else if (*x > -11) { /* special locations */ + *y = ystart + rloc_y[ - *y - 1]; + *x = xstart + rloc_x[ - *x - 1]; + } else { /* random location */ + do { + *x = xstart + rn2((int)xsize); + *y = ystart + rn2((int)ysize); + if (is_ok_location(*x,*y,humidity)) break; + } while (++cpt < 100); + if (cpt >= 100) { + register int xx, yy; + /* last try */ + for (xx = 0; xx < xsize; xx++) + for (yy = 0; yy < ysize; yy++) { + *x = xstart + xx; + *y = ystart + yy; + if (is_ok_location(*x,*y,humidity)) goto found_it; + } + panic("get_location: can't find a place!"); + } + } +found_it:; + + if (!isok(*x,*y)) { + impossible("get_location: (%d,%d) out of bounds", *x, *y); + *x = x_maze_max; *y = y_maze_max; + } +} + +STATIC_OVL boolean +is_ok_location(x, y, humidity) +register schar x, y; +register int humidity; +{ + register int typ; + + if (Is_waterlevel(&u.uz)) return TRUE; /* accept any spot */ + + if (humidity & DRY) { + typ = levl[x][y].typ; + if (typ == ROOM || typ == AIR || + typ == CLOUD || typ == ICE || typ == CORR) + return TRUE; + } + if (humidity & WET) { + if (is_pool(x,y) || is_lava(x,y)) + return TRUE; + } + return FALSE; +} + +/* + * Shuffle the registers for locations, objects or monsters + */ + +STATIC_OVL void +sp_lev_shuffle(list1, list2, n) +char list1[], list2[]; +int n; +{ + register int i, j; + register char k; + + for (i = n - 1; i > 0; i--) { + if ((j = rn2(i + 1)) == i) continue; + k = list1[j]; + list1[j] = list1[i]; + list1[i] = k; + if (list2) { + k = list2[j]; + list2[j] = list2[i]; + list2[i] = k; + } + } +} + +/* + * Get a relative position inside a room. + * negative values for x or y means RANDOM! + */ + +STATIC_OVL void +get_room_loc(x,y, croom) +schar *x, *y; +struct mkroom *croom; +{ + coord c; + + if (*x <0 && *y <0) { + if (somexy(croom, &c)) { + *x = c.x; + *y = c.y; + } else + panic("get_room_loc : can't find a place!"); + } else { + if (*x < 0) + *x = rn2(croom->hx - croom->lx + 1); + if (*y < 0) + *y = rn2(croom->hy - croom->ly + 1); + *x += croom->lx; + *y += croom->ly; + } +} + +/* + * Get a relative position inside a room. + * negative values for x or y means RANDOM! + */ + +STATIC_OVL void +get_free_room_loc(x,y, croom) +schar *x, *y; +struct mkroom *croom; +{ + schar try_x, try_y; + register int trycnt = 0; + + do { + try_x = *x, try_y = *y; + get_room_loc(&try_x, &try_y, croom); + } while (levl[try_x][try_y].typ != ROOM && ++trycnt <= 100); + + if (trycnt > 100) + panic("get_free_room_loc: can't find a place!"); + *x = try_x, *y = try_y; +} + +boolean +check_room(lowx, ddx, lowy, ddy, vault) +xchar *lowx, *ddx, *lowy, *ddy; +boolean vault; +{ + register int x,y,hix = *lowx + *ddx, hiy = *lowy + *ddy; + register struct rm *lev; + int xlim, ylim, ymax; + + xlim = XLIM + (vault ? 1 : 0); + ylim = YLIM + (vault ? 1 : 0); + + if (*lowx < 3) *lowx = 3; + if (*lowy < 2) *lowy = 2; + if (hix > COLNO-3) hix = COLNO-3; + if (hiy > ROWNO-3) hiy = ROWNO-3; +chk: + if (hix <= *lowx || hiy <= *lowy) return FALSE; + + /* check area around room (and make room smaller if necessary) */ + for (x = *lowx - xlim; x<= hix + xlim; x++) { + if(x <= 0 || x >= COLNO) continue; + y = *lowy - ylim; ymax = hiy + ylim; + if(y < 0) y = 0; + if(ymax >= ROWNO) ymax = (ROWNO-1); + lev = &levl[x][y]; + for (; y <= ymax; y++) { + if (lev++->typ) { +#ifdef DEBUG + if(!vault) + debugpline("strange area [%d,%d] in check_room.",x,y); +#endif + if (!rn2(3)) return FALSE; + if (x < *lowx) + *lowx = x + xlim + 1; + else + hix = x - xlim - 1; + if (y < *lowy) + *lowy = y + ylim + 1; + else + hiy = y - ylim - 1; + goto chk; + } + } + } + *ddx = hix - *lowx; + *ddy = hiy - *lowy; + return TRUE; +} + +/* + * Create a new room. + * This is still very incomplete... + */ + +boolean +create_room(x,y,w,h,xal,yal,rtype,rlit) +xchar x,y; +xchar w,h; +xchar xal,yal; +xchar rtype, rlit; +{ + xchar xabs, yabs; + int wtmp, htmp, xaltmp, yaltmp, xtmp, ytmp; + NhRect *r1 = 0, r2; + int trycnt = 0; + boolean vault = FALSE; + int xlim = XLIM, ylim = YLIM; + + if (rtype == -1) /* Is the type random ? */ + rtype = OROOM; + + if (rtype == VAULT) { + vault = TRUE; + xlim++; + ylim++; + } + + /* on low levels the room is lit (usually) */ + /* some other rooms may require lighting */ + + /* is light state random ? */ + if (rlit == -1) + rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) ? TRUE : FALSE; + + /* + * Here we will try to create a room. If some parameters are + * random we are willing to make several try before we give + * it up. + */ + do { + xchar xborder, yborder; + wtmp = w; htmp = h; + xtmp = x; ytmp = y; + xaltmp = xal; yaltmp = yal; + + /* First case : a totaly random room */ + + if((xtmp < 0 && ytmp <0 && wtmp < 0 && xaltmp < 0 && + yaltmp < 0) || vault) { + xchar hx, hy, lx, ly, dx, dy; + r1 = rnd_rect(); /* Get a random rectangle */ + + if (!r1) { /* No more free rectangles ! */ +#ifdef DEBUG + debugpline("No more rects..."); +#endif + return FALSE; + } + hx = r1->hx; + hy = r1->hy; + lx = r1->lx; + ly = r1->ly; + if (vault) + dx = dy = 1; + else { + dx = 2 + rn2((hx-lx > 28) ? 12 : 8); + dy = 2 + rn2(4); + if(dx*dy > 50) + dy = 50/dx; + } + xborder = (lx > 0 && hx < COLNO -1) ? 2*xlim : xlim+1; + yborder = (ly > 0 && hy < ROWNO -1) ? 2*ylim : ylim+1; + if(hx-lx < dx + 3 + xborder || + hy-ly < dy + 3 + yborder) { + r1 = 0; + continue; + } + xabs = lx + (lx > 0 ? xlim : 3) + + rn2(hx - (lx>0?lx : 3) - dx - xborder + 1); + yabs = ly + (ly > 0 ? ylim : 2) + + rn2(hy - (ly>0?ly : 2) - dy - yborder + 1); + if (ly == 0 && hy >= (ROWNO-1) && + (!nroom || !rn2(nroom)) && (yabs+dy > ROWNO/2)) { + yabs = rn1(3, 2); + if(nroom < 4 && dy>1) dy--; + } + if (!check_room(&xabs, &dx, &yabs, &dy, vault)) { + r1 = 0; + continue; + } + wtmp = dx+1; + htmp = dy+1; + r2.lx = xabs-1; r2.ly = yabs-1; + r2.hx = xabs + wtmp; + r2.hy = yabs + htmp; + } else { /* Only some parameters are random */ + int rndpos = 0; + if (xtmp < 0 && ytmp < 0) { /* Position is RANDOM */ + xtmp = rnd(5); + ytmp = rnd(5); + rndpos = 1; + } + if (wtmp < 0 || htmp < 0) { /* Size is RANDOM */ + wtmp = rn1(15, 3); + htmp = rn1(8, 2); + } + if (xaltmp == -1) /* Horizontal alignment is RANDOM */ + xaltmp = rnd(3); + if (yaltmp == -1) /* Vertical alignment is RANDOM */ + yaltmp = rnd(3); + + /* Try to generate real (absolute) coordinates here! */ + + xabs = (((xtmp-1) * COLNO) / 5) + 1; + yabs = (((ytmp-1) * ROWNO) / 5) + 1; + switch (xaltmp) { + case LEFT: + break; + case RIGHT: + xabs += (COLNO / 5) - wtmp; + break; + case CENTER: + xabs += ((COLNO / 5) - wtmp) / 2; + break; + } + switch (yaltmp) { + case TOP: + break; + case BOTTOM: + yabs += (ROWNO / 5) - htmp; + break; + case CENTER: + yabs += ((ROWNO / 5) - htmp) / 2; + break; + } + + if (xabs + wtmp - 1 > COLNO - 2) + xabs = COLNO - wtmp - 3; + if (xabs < 2) + xabs = 2; + if (yabs + htmp - 1> ROWNO - 2) + yabs = ROWNO - htmp - 3; + if (yabs < 2) + yabs = 2; + + /* Try to find a rectangle that fit our room ! */ + + r2.lx = xabs-1; r2.ly = yabs-1; + r2.hx = xabs + wtmp + rndpos; + r2.hy = yabs + htmp + rndpos; + r1 = get_rect(&r2); + } + } while (++trycnt <= 100 && !r1); + if (!r1) { /* creation of room failed ? */ + return FALSE; + } + split_rects(r1, &r2); + + if (!vault) { + smeq[nroom] = nroom; + add_room(xabs, yabs, xabs+wtmp-1, yabs+htmp-1, + rlit, rtype, FALSE); + } else { + rooms[nroom].lx = xabs; + rooms[nroom].ly = yabs; + } + return TRUE; +} + +/* + * Create a subroom in room proom at pos x,y with width w & height h. + * x & y are relative to the parent room. + */ + +STATIC_OVL boolean +create_subroom(proom, x, y, w, h, rtype, rlit) +struct mkroom *proom; +xchar x,y; +xchar w,h; +xchar rtype, rlit; +{ + xchar width, height; + + width = proom->hx - proom->lx + 1; + height = proom->hy - proom->ly + 1; + + /* There is a minimum size for the parent room */ + if (width < 4 || height < 4) + return FALSE; + + /* Check for random position, size, etc... */ + + if (w == -1) + w = rnd(width - 3); + if (h == -1) + h = rnd(height - 3); + if (x == -1) + x = rnd(width - w - 1) - 1; + if (y == -1) + y = rnd(height - h - 1) - 1; + if (x == 1) + x = 0; + if (y == 1) + y = 0; + if ((x + w + 1) == width) + x++; + if ((y + h + 1) == height) + y++; + if (rtype == -1) + rtype = OROOM; + if (rlit == -1) + rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) ? TRUE : FALSE; + add_subroom(proom, proom->lx + x, proom->ly + y, + proom->lx + x + w - 1, proom->ly + y + h - 1, + rlit, rtype, FALSE); + return TRUE; +} + +/* + * Create a new door in a room. + * It's placed on a wall (north, south, east or west). + */ + +STATIC_OVL void +create_door(dd, broom) +room_door *dd; +struct mkroom *broom; +{ + int x, y; + int trycnt = 0; + + if (dd->secret == -1) + dd->secret = rn2(2); + + if (dd->mask == -1) { + /* is it a locked door, closed, or a doorway? */ + if (!dd->secret) { + if(!rn2(3)) { + if(!rn2(5)) + dd->mask = D_ISOPEN; + else if(!rn2(6)) + dd->mask = D_LOCKED; + else + dd->mask = D_CLOSED; + if (dd->mask != D_ISOPEN && !rn2(25)) + dd->mask |= D_TRAPPED; + } else + dd->mask = D_NODOOR; + } else { + if(!rn2(5)) dd->mask = D_LOCKED; + else dd->mask = D_CLOSED; + + if(!rn2(20)) dd->mask |= D_TRAPPED; + } + } + + do { + register int dwall, dpos; + + dwall = dd->wall; + if (dwall == -1) /* The wall is RANDOM */ + dwall = 1 << rn2(4); + + dpos = dd->pos; + if (dpos == -1) /* The position is RANDOM */ + dpos = rn2((dwall == W_WEST || dwall == W_EAST) ? + (broom->hy - broom->ly) : (broom->hx - broom->lx)); + + /* Convert wall and pos into an absolute coordinate! */ + + switch (dwall) { + case W_NORTH: + y = broom->ly - 1; + x = broom->lx + dpos; + break; + case W_SOUTH: + y = broom->hy + 1; + x = broom->lx + dpos; + break; + case W_WEST: + x = broom->lx - 1; + y = broom->ly + dpos; + break; + case W_EAST: + x = broom->hx + 1; + y = broom->ly + dpos; + break; + default: + x = y = 0; + panic("create_door: No wall for door!"); + break; + } + if (okdoor(x,y)) + break; + } while (++trycnt <= 100); + if (trycnt > 100) { + impossible("create_door: Can't find a proper place!"); + return; + } + add_door(x,y,broom); + levl[x][y].typ = (dd->secret ? SDOOR : DOOR); + levl[x][y].doormask = dd->mask; +} + +/* + * Create a secret door in croom on any one of the specified walls. + */ +void +create_secret_door(croom, walls) + struct mkroom *croom; + xchar walls; /* any of W_NORTH | W_SOUTH | W_EAST | W_WEST (or W_ANY) */ +{ + xchar sx, sy; /* location of the secret door */ + int count; + + for(count = 0; count < 100; count++) { + sx = rn1(croom->hx - croom->lx + 1, croom->lx); + sy = rn1(croom->hy - croom->ly + 1, croom->ly); + + switch(rn2(4)) { + case 0: /* top */ + if(!(walls & W_NORTH)) continue; + sy = croom->ly-1; break; + case 1: /* bottom */ + if(!(walls & W_SOUTH)) continue; + sy = croom->hy+1; break; + case 2: /* left */ + if(!(walls & W_EAST)) continue; + sx = croom->lx-1; break; + case 3: /* right */ + if(!(walls & W_WEST)) continue; + sx = croom->hx+1; break; + } + + if(okdoor(sx,sy)) { + levl[sx][sy].typ = SDOOR; + levl[sx][sy].doormask = D_CLOSED; + add_door(sx,sy,croom); + return; + } + } + + impossible("couldn't create secret door on any walls 0x%x", walls); +} + +/* + * Create a trap in a room. + */ + +STATIC_OVL void +create_trap(t,croom) +trap *t; +struct mkroom *croom; +{ + schar x,y; + coord tm; + + if (rn2(100) < t->chance) { + x = t->x; + y = t->y; + if (croom) + get_free_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + tm.x = x; + tm.y = y; + + mktrap(t->type, 1, (struct mkroom*) 0, &tm); + } +} + +/* + * Create a monster in a room. + */ + +STATIC_OVL int +noncoalignment(alignment) +aligntyp alignment; +{ + int k; + + k = rn2(2); + if (!alignment) + return(k ? -1 : 1); + return(k ? -alignment : 0); +} + +STATIC_OVL void +create_monster(m,croom) +monster *m; +struct mkroom *croom; +{ + struct monst *mtmp; + schar x, y; + char class; + aligntyp amask; + coord cc; + struct permonst *pm; + unsigned g_mvflags; + + if (rn2(100) < m->chance) { + + if (m->class >= 0) + class = (char) def_char_to_monclass((char)m->class); + else if (m->class > -11) + class = (char) def_char_to_monclass(rmonst[- m->class - 1]); + else + class = 0; + + if (class == MAXMCLASSES) + panic("create_monster: unknown monster class '%c'", m->class); + + amask = (m->align == AM_SPLEV_CO) ? + Align2amask(u.ualignbase[A_ORIGINAL]) : + (m->align == AM_SPLEV_NONCO) ? + Align2amask(noncoalignment(u.ualignbase[A_ORIGINAL])) : + (m->align <= -11) ? induced_align(80) : + (m->align < 0 ? ralign[-m->align-1] : m->align); + + if (!class) + pm = (struct permonst *) 0; + else if (m->id != NON_PM) { + pm = &mons[m->id]; + g_mvflags = (unsigned) mvitals[monsndx(pm)].mvflags; + if ((pm->geno & G_UNIQ) && (g_mvflags & G_EXTINCT)) + goto m_done; + else if (g_mvflags & G_GONE) /* genocided or extinct */ + pm = (struct permonst *) 0; /* make random monster */ + } else { + pm = mkclass(class,G_NOGEN); + /* if we can't get a specific monster type (pm == 0) then the + class has been genocided, so settle for a random monster */ + } + if (In_mines(&u.uz) && pm && your_race(pm) && + (Race_if(PM_DWARF) || Race_if(PM_GNOME)) && rn2(3)) + pm = (struct permonst *) 0; + + x = m->x; + y = m->y; + if (croom) + get_room_loc(&x, &y, croom); + else { + if (!pm || !is_swimmer(pm)) + get_location(&x, &y, DRY); + else if (pm->mlet == S_EEL) + get_location(&x, &y, WET); + else + get_location(&x, &y, DRY|WET); + } + /* try to find a close place if someone else is already there */ + if (MON_AT(x,y) && enexto(&cc, x, y, pm)) + x = cc.x, y = cc.y; + + if(m->align != -12) + mtmp = mk_roamer(pm, Amask2align(amask), x, y, m->peaceful); + else if(PM_ARCHEOLOGIST <= m->id && m->id <= PM_WIZARD) + mtmp = mk_mplayer(pm, x, y, FALSE); + else mtmp = makemon(pm, x, y, NO_MM_FLAGS); + + if (mtmp) { + /* handle specific attributes for some special monsters */ + if (m->name.str) mtmp = christen_monst(mtmp, m->name.str); + + /* + * This is currently hardwired for mimics only. It should + * eventually be expanded. + */ + if (m->appear_as.str && mtmp->data->mlet == S_MIMIC) { + int i; + + switch (m->appear) { + case M_AP_NOTHING: + impossible( + "create_monster: mon has an appearance, \"%s\", but no type", + m->appear_as.str); + break; + + case M_AP_FURNITURE: + for (i = 0; i < MAXPCHARS; i++) + if (!strcmp(defsyms[i].explanation, + m->appear_as.str)) + break; + if (i == MAXPCHARS) { + impossible( + "create_monster: can't find feature \"%s\"", + m->appear_as.str); + } else { + mtmp->m_ap_type = M_AP_FURNITURE; + mtmp->mappearance = i; + } + break; + + case M_AP_OBJECT: + for (i = 0; i < NUM_OBJECTS; i++) + if (!strcmp(OBJ_NAME(objects[i]), + m->appear_as.str)) + break; + if (i == NUM_OBJECTS) { + impossible( + "create_monster: can't find object \"%s\"", + m->appear_as.str); + } else { + mtmp->m_ap_type = M_AP_OBJECT; + mtmp->mappearance = i; + } + break; + + case M_AP_MONSTER: + /* note: mimics don't appear as monsters! */ + /* (but chameleons can :-) */ + default: + impossible( + "create_monster: unimplemented mon appear type [%d,\"%s\"]", + m->appear, m->appear_as.str); + break; + } + if (does_block(x, y, &levl[x][y])) + block_point(x, y); + } + + if (m->peaceful >= 0) { + mtmp->mpeaceful = m->peaceful; + /* changed mpeaceful again; have to reset malign */ + set_malign(mtmp); + } + if (m->asleep >= 0) { +#ifdef UNIXPC + /* optimizer bug strikes again */ + if (m->asleep) + mtmp->msleeping = 1; + else + mtmp->msleeping = 0; +#else + mtmp->msleeping = m->asleep; +#endif + } + } + + } /* if (rn2(100) < m->chance) */ + m_done: + Free(m->name.str); + Free(m->appear_as.str); +} + +/* + * Create an object in a room. + */ + +STATIC_OVL void +create_object(o,croom) +object *o; +struct mkroom *croom; +{ + struct obj *otmp; + schar x, y; + char c; + boolean named; /* has a name been supplied in level description? */ + + if (rn2(100) < o->chance) { + named = o->name.str ? TRUE : FALSE; + + x = o->x; y = o->y; + if (croom) + get_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + if (o->class >= 0) + c = o->class; + else if (o->class > -11) + c = robjects[ -(o->class+1)]; + else + c = 0; + + if (!c) + otmp = mkobj_at(RANDOM_CLASS, x, y, !named); + else if (o->id != -1) + otmp = mksobj_at(o->id, x, y, TRUE, !named); + else { + /* + * The special levels are compiled with the default "text" object + * class characters. We must convert them to the internal format. + */ + char oclass = (char) def_char_to_objclass(c); + + if (oclass == MAXOCLASSES) + panic("create_object: unexpected object class '%c'",c); + + /* KMH -- Create piles of gold properly */ + if (oclass == GOLD_CLASS) + otmp = mkgold(0L, x, y); + else + otmp = mkobj_at(oclass, x, y, !named); + } + + if (o->spe != -127) /* That means NOT RANDOM! */ + otmp->spe = (schar)o->spe; + + switch (o->curse_state) { + case 1: bless(otmp); break; /* BLESSED */ + case 2: unbless(otmp); uncurse(otmp); break; /* uncursed */ + case 3: curse(otmp); break; /* CURSED */ + default: break; /* Otherwise it's random and we're happy + * with what mkobj gave us! */ + } + + /* corpsenm is "empty" if -1, random if -2, otherwise specific */ + if (o->corpsenm == NON_PM - 1) otmp->corpsenm = rndmonnum(); + else if (o->corpsenm != NON_PM) otmp->corpsenm = o->corpsenm; + + /* assume we wouldn't be given an egg corpsenm unless it was + hatchable */ + if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) { + if (dead_species(otmp->otyp, TRUE)) + kill_egg(otmp); /* make sure nothing hatches */ + else + attach_egg_hatch_timeout(otmp); /* attach new hatch timeout */ + } + + if (named) + otmp = oname(otmp, o->name.str); + + switch(o->containment) { + static struct obj *container = 0; + + /* contents */ + case 1: + if (!container) { + impossible("create_object: no container"); + break; + } + remove_object(otmp); + (void) add_to_container(container, otmp); + goto o_done; /* don't stack, but do other cleanup */ + /* container */ + case 2: + delete_contents(otmp); + container = otmp; + break; + /* nothing */ + case 0: break; + + default: impossible("containment type %d?", (int) o->containment); + } + + /* Medusa level special case: statues are petrified monsters, so they + * are not stone-resistant and have monster inventory. They also lack + * other contents, but that can be specified as an empty container. + */ + if (o->id == STATUE && Is_medusa_level(&u.uz) && + o->corpsenm == NON_PM) { + struct monst *was; + struct obj *obj; + int wastyp; + + /* Named random statues are of player types, and aren't stone- + * resistant (if they were, we'd have to reset the name as well as + * setting corpsenm). + */ + for (wastyp = otmp->corpsenm; ; wastyp = rndmonnum()) { + /* makemon without rndmonst() might create a group */ + was = makemon(&mons[wastyp], 0, 0, NO_MM_FLAGS); + if (!resists_ston(was)) break; + mongone(was); + } + otmp->corpsenm = wastyp; + while(was->minvent) { + obj = was->minvent; + obj->owornmask = 0; + obj_extract_self(obj); + (void) add_to_container(otmp, obj); + } + otmp->owt = weight(otmp); + mongone(was); + } + + stackobj(otmp); + + } /* if (rn2(100) < o->chance) */ + o_done: + Free(o->name.str); +} + +/* + * Randomly place a specific engraving, then release its memory. + */ +STATIC_OVL void +create_engraving(e, croom) +engraving *e; +struct mkroom *croom; +{ + xchar x, y; + + x = e->x, y = e->y; + if (croom) + get_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + make_engr_at(x, y, e->engr.str, 0L, e->etype); + free((genericptr_t) e->engr.str); +} + +/* + * Create stairs in a room. + * + */ + +STATIC_OVL void +create_stairs(s,croom) +stair *s; +struct mkroom *croom; +{ + schar x,y; + + x = s->x; y = s->y; + get_free_room_loc(&x, &y, croom); + mkstairs(x,y,(char)s->up, croom); +} + +/* + * Create an altar in a room. + */ + +STATIC_OVL void +create_altar(a, croom) + altar *a; + struct mkroom *croom; +{ + schar sproom,x,y; + aligntyp amask; + boolean croom_is_temple = TRUE; + int oldtyp; + + x = a->x; y = a->y; + + if (croom) { + get_free_room_loc(&x, &y, croom); + if (croom->rtype != TEMPLE) + croom_is_temple = FALSE; + } else { + get_location(&x, &y, DRY); + if ((sproom = (schar) *in_rooms(x, y, TEMPLE)) != 0) + croom = &rooms[sproom - ROOMOFFSET]; + else + croom_is_temple = FALSE; + } + + /* check for existing features */ + oldtyp = levl[x][y].typ; + if (oldtyp == STAIRS || oldtyp == LADDER) + return; + + a->x = x; + a->y = y; + + /* Is the alignment random ? + * If so, it's an 80% chance that the altar will be co-aligned. + * + * The alignment is encoded as amask values instead of alignment + * values to avoid conflicting with the rest of the encoding, + * shared by many other parts of the special level code. + */ + + amask = (a->align == AM_SPLEV_CO) ? + Align2amask(u.ualignbase[A_ORIGINAL]) : + (a->align == AM_SPLEV_NONCO) ? + Align2amask(noncoalignment(u.ualignbase[A_ORIGINAL])) : + (a->align == -11) ? induced_align(80) : + (a->align < 0 ? ralign[-a->align-1] : a->align); + + levl[x][y].typ = ALTAR; + levl[x][y].altarmask = amask; + + if (a->shrine == -11) a->shrine = rn2(1); /* handle random case */ + + if (oldtyp == FOUNTAIN) + level.flags.nfountains--; + else if (oldtyp == SINK) + level.flags.nsinks--; + + if (!croom_is_temple || !a->shrine) return; + + if (a->shrine) { /* Is it a shrine or sanctum? */ + priestini(&u.uz, croom, x, y, (a->shrine > 1)); + levl[x][y].altarmask |= AM_SHRINE; + level.flags.has_temple = TRUE; + } +} + +/* + * Create a gold pile in a room. + */ + +STATIC_OVL void +create_gold(g,croom) +gold *g; +struct mkroom *croom; +{ + schar x,y; + + x = g->x; y= g->y; + if (croom) + get_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + if (g->amount == -1) + g->amount = rnd(200); + (void) mkgold((long) g->amount, x, y); +} + +/* + * Create a feature (e.g a fountain) in a room. + */ + +STATIC_OVL void +create_feature(fx, fy, croom, typ) +int fx, fy; +struct mkroom *croom; +int typ; +{ + schar x,y; + int trycnt = 0; + + x = fx; y = fy; + if (croom) { + if (x < 0 && y < 0) + do { + x = -1; y = -1; + get_room_loc(&x, &y, croom); + } while (++trycnt <= 200 && occupied(x,y)); + else + get_room_loc(&x, &y, croom); + if(trycnt > 200) + return; + } else { + get_location(&x, &y, DRY); + } + /* Don't cover up an existing feature (particularly randomly + placed stairs). However, if the _same_ feature is already + here, it came from the map drawing and we still need to + update the special counters. */ + if (IS_FURNITURE(levl[x][y].typ) && levl[x][y].typ != typ) + return; + + levl[x][y].typ = typ; + if (typ == FOUNTAIN) + level.flags.nfountains++; + else if (typ == SINK) + level.flags.nsinks++; +} + +/* + * Search for a door in a room on a specified wall. + */ + +STATIC_OVL boolean +search_door(croom,x,y,wall,cnt) +struct mkroom *croom; +xchar *x, *y; +xchar wall; +int cnt; +{ + int dx, dy; + int xx,yy; + + switch(wall) { + case W_NORTH: + dy = 0; dx = 1; + xx = croom->lx; + yy = croom->hy + 1; + break; + case W_SOUTH: + dy = 0; dx = 1; + xx = croom->lx; + yy = croom->ly - 1; + break; + case W_EAST: + dy = 1; dx = 0; + xx = croom->hx + 1; + yy = croom->ly; + break; + case W_WEST: + dy = 1; dx = 0; + xx = croom->lx - 1; + yy = croom->ly; + break; + default: + dx = dy = xx = yy = 0; + panic("search_door: Bad wall!"); + break; + } + while (xx <= croom->hx+1 && yy <= croom->hy+1) { + if (IS_DOOR(levl[xx][yy].typ) || levl[xx][yy].typ == SDOOR) { + *x = xx; + *y = yy; + if (cnt-- <= 0) + return TRUE; + } + xx += dx; + yy += dy; + } + return FALSE; +} + +/* + * Dig a corridor between two points. + */ + +boolean +dig_corridor(org,dest,nxcor,ftyp,btyp) +coord *org, *dest; +boolean nxcor; +schar ftyp, btyp; +{ + register int dx=0, dy=0, dix, diy, cct; + register struct rm *crm; + register int tx, ty, xx, yy; + + xx = org->x; yy = org->y; + tx = dest->x; ty = dest->y; + if (xx <= 0 || yy <= 0 || tx <= 0 || ty <= 0 || + xx > COLNO-1 || tx > COLNO-1 || + yy > ROWNO-1 || ty > ROWNO-1) { +#ifdef DEBUG + debugpline("dig_corridor: bad coords : (%d,%d) (%d,%d).", + xx,yy,tx,ty); +#endif + return FALSE; + } + if (tx > xx) dx = 1; + else if (ty > yy) dy = 1; + else if (tx < xx) dx = -1; + else dy = -1; + + xx -= dx; + yy -= dy; + cct = 0; + while(xx != tx || yy != ty) { + /* loop: dig corridor at [xx,yy] and find new [xx,yy] */ + if(cct++ > 500 || (nxcor && !rn2(35))) + return FALSE; + + xx += dx; + yy += dy; + + if(xx >= COLNO-1 || xx <= 0 || yy <= 0 || yy >= ROWNO-1) + return FALSE; /* impossible */ + + crm = &levl[xx][yy]; + if(crm->typ == btyp) { + if(ftyp != CORR || rn2(100)) { + crm->typ = ftyp; + if(nxcor && !rn2(50)) + (void) mksobj_at(BOULDER, xx, yy, TRUE, FALSE); + } else { + crm->typ = SCORR; + } + } else + if(crm->typ != ftyp && crm->typ != SCORR) { + /* strange ... */ + return FALSE; + } + + /* find next corridor position */ + dix = abs(xx-tx); + diy = abs(yy-ty); + + /* do we have to change direction ? */ + if(dy && dix > diy) { + register int ddx = (xx > tx) ? -1 : 1; + + crm = &levl[xx+ddx][yy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) { + dx = ddx; + dy = 0; + continue; + } + } else if(dx && diy > dix) { + register int ddy = (yy > ty) ? -1 : 1; + + crm = &levl[xx][yy+ddy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) { + dy = ddy; + dx = 0; + continue; + } + } + + /* continue straight on? */ + crm = &levl[xx+dx][yy+dy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) + continue; + + /* no, what must we do now?? */ + if(dx) { + dx = 0; + dy = (ty < yy) ? -1 : 1; + } else { + dy = 0; + dx = (tx < xx) ? -1 : 1; + } + crm = &levl[xx+dx][yy+dy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) + continue; + dy = -dy; + dx = -dx; + } + return TRUE; +} + +/* + * Disgusting hack: since special levels have their rooms filled before + * sorting the rooms, we have to re-arrange the speed values upstairs_room + * and dnstairs_room after the rooms have been sorted. On normal levels, + * stairs don't get created until _after_ sorting takes place. + */ +STATIC_OVL void +fix_stair_rooms() +{ + int i; + struct mkroom *croom; + + if(xdnstair && + !((dnstairs_room->lx <= xdnstair && xdnstair <= dnstairs_room->hx) && + (dnstairs_room->ly <= ydnstair && ydnstair <= dnstairs_room->hy))) { + for(i=0; i < nroom; i++) { + croom = &rooms[i]; + if((croom->lx <= xdnstair && xdnstair <= croom->hx) && + (croom->ly <= ydnstair && ydnstair <= croom->hy)) { + dnstairs_room = croom; + break; + } + } + if(i == nroom) + panic("Couldn't find dnstair room in fix_stair_rooms!"); + } + if(xupstair && + !((upstairs_room->lx <= xupstair && xupstair <= upstairs_room->hx) && + (upstairs_room->ly <= yupstair && yupstair <= upstairs_room->hy))) { + for(i=0; i < nroom; i++) { + croom = &rooms[i]; + if((croom->lx <= xupstair && xupstair <= croom->hx) && + (croom->ly <= yupstair && yupstair <= croom->hy)) { + upstairs_room = croom; + break; + } + } + if(i == nroom) + panic("Couldn't find upstair room in fix_stair_rooms!"); + } +} + +/* + * Corridors always start from a door. But it can end anywhere... + * Basically we search for door coordinates or for endpoints coordinates + * (from a distance). + */ + +STATIC_OVL void +create_corridor(c) +corridor *c; +{ + coord org, dest; + + if (c->src.room == -1) { + sort_rooms(); + fix_stair_rooms(); + makecorridors(); + return; + } + + if( !search_door(&rooms[c->src.room], &org.x, &org.y, c->src.wall, + c->src.door)) + return; + + if (c->dest.room != -1) { + if(!search_door(&rooms[c->dest.room], &dest.x, &dest.y, + c->dest.wall, c->dest.door)) + return; + switch(c->src.wall) { + case W_NORTH: org.y--; break; + case W_SOUTH: org.y++; break; + case W_WEST: org.x--; break; + case W_EAST: org.x++; break; + } + switch(c->dest.wall) { + case W_NORTH: dest.y--; break; + case W_SOUTH: dest.y++; break; + case W_WEST: dest.x--; break; + case W_EAST: dest.x++; break; + } + (void) dig_corridor(&org, &dest, FALSE, CORR, STONE); + } +} + + +/* + * Fill a room (shop, zoo, etc...) with appropriate stuff. + */ + +void +fill_room(croom, prefilled) +struct mkroom *croom; +boolean prefilled; +{ + if (!croom || croom->rtype == OROOM) + return; + + if (!prefilled) { + int x,y; + + /* Shop ? */ + if (croom->rtype >= SHOPBASE) { + stock_room(croom->rtype - SHOPBASE, croom); + level.flags.has_shop = TRUE; + return; + } + + switch (croom->rtype) { + case VAULT: + for (x=croom->lx;x<=croom->hx;x++) + for (y=croom->ly;y<=croom->hy;y++) + (void) mkgold((long)rn1(abs(depth(&u.uz))*100, 51), x, y); + break; + case COURT: + case ZOO: + case BEEHIVE: + case MORGUE: + case BARRACKS: + fill_zoo(croom); + break; + } + } + switch (croom->rtype) { + case VAULT: + level.flags.has_vault = TRUE; + break; + case ZOO: + level.flags.has_zoo = TRUE; + break; + case COURT: + level.flags.has_court = TRUE; + break; + case MORGUE: + level.flags.has_morgue = TRUE; + break; + case BEEHIVE: + level.flags.has_beehive = TRUE; + break; + case BARRACKS: + level.flags.has_barracks = TRUE; + break; + case TEMPLE: + level.flags.has_temple = TRUE; + break; + case SWAMP: + level.flags.has_swamp = TRUE; + break; + } +} + +STATIC_OVL void +free_rooms(ro, n) +room **ro; +int n; +{ + short j; + room *r; + + while(n--) { + r = ro[n]; + Free(r->name); + Free(r->parent); + if ((j = r->ndoor) != 0) { + while(j--) + Free(r->doors[j]); + Free(r->doors); + } + if ((j = r->nstair) != 0) { + while(j--) + Free(r->stairs[j]); + Free(r->stairs); + } + if ((j = r->naltar) != 0) { + while (j--) + Free(r->altars[j]); + Free(r->altars); + } + if ((j = r->nfountain) != 0) { + while(j--) + Free(r->fountains[j]); + Free(r->fountains); + } + if ((j = r->nsink) != 0) { + while(j--) + Free(r->sinks[j]); + Free(r->sinks); + } + if ((j = r->npool) != 0) { + while(j--) + Free(r->pools[j]); + Free(r->pools); + } + if ((j = r->ntrap) != 0) { + while (j--) + Free(r->traps[j]); + Free(r->traps); + } + if ((j = r->nmonster) != 0) { + while (j--) + Free(r->monsters[j]); + Free(r->monsters); + } + if ((j = r->nobject) != 0) { + while (j--) + Free(r->objects[j]); + Free(r->objects); + } + if ((j = r->ngold) != 0) { + while(j--) + Free(r->golds[j]); + Free(r->golds); + } + if ((j = r->nengraving) != 0) { + while (j--) + Free(r->engravings[j]); + Free(r->engravings); + } + Free(r); + } + Free(ro); +} + +STATIC_OVL void +build_room(r, pr) +room *r, *pr; +{ + boolean okroom; + struct mkroom *aroom; + short i; + xchar rtype = (!r->chance || rn2(100) < r->chance) ? r->rtype : OROOM; + + if(pr) { + aroom = &subrooms[nsubroom]; + okroom = create_subroom(pr->mkr, r->x, r->y, r->w, r->h, + rtype, r->rlit); + } else { + aroom = &rooms[nroom]; + okroom = create_room(r->x, r->y, r->w, r->h, r->xalign, + r->yalign, rtype, r->rlit); + r->mkr = aroom; + } + + if (okroom) { + /* Create subrooms if necessary... */ + for(i=0; i < r->nsubroom; i++) + build_room(r->subrooms[i], r); + /* And now we can fill the room! */ + + /* Priority to the stairs */ + + for(i=0; i nstair; i++) + create_stairs(r->stairs[i], aroom); + + /* Then to the various elements (sinks, etc..) */ + for(i = 0; insink; i++) + create_feature(r->sinks[i]->x, r->sinks[i]->y, aroom, SINK); + for(i = 0; inpool; i++) + create_feature(r->pools[i]->x, r->pools[i]->y, aroom, POOL); + for(i = 0; infountain; i++) + create_feature(r->fountains[i]->x, r->fountains[i]->y, + aroom, FOUNTAIN); + for(i = 0; inaltar; i++) + create_altar(r->altars[i], aroom); + for(i = 0; indoor; i++) + create_door(r->doors[i], aroom); + + /* The traps */ + for(i = 0; intrap; i++) + create_trap(r->traps[i], aroom); + + /* The monsters */ + for(i = 0; inmonster; i++) + create_monster(r->monsters[i], aroom); + + /* The objects */ + for(i = 0; inobject; i++) + create_object(r->objects[i], aroom); + + /* The gold piles */ + for(i = 0; ingold; i++) + create_gold(r->golds[i], aroom); + + /* The engravings */ + for (i = 0; i < r->nengraving; i++) + create_engraving(r->engravings[i], aroom); + +#ifdef SPECIALIZATION + topologize(aroom,FALSE); /* set roomno */ +#else + topologize(aroom); /* set roomno */ +#endif + /* MRS - 07/04/91 - This is temporary but should result + * in proper filling of shops, etc. + * DLC - this can fail if corridors are added to this room + * at a later point. Currently no good way to fix this. + */ + if(aroom->rtype != OROOM && r->filled) fill_room(aroom, FALSE); + } +} + +/* + * set lighting in a region that will not become a room. + */ +STATIC_OVL void +light_region(tmpregion) + region *tmpregion; +{ + register boolean litstate = tmpregion->rlit ? 1 : 0; + register int hiy = tmpregion->y2; + register int x, y; + register struct rm *lev; + int lowy = tmpregion->y1; + int lowx = tmpregion->x1, hix = tmpregion->x2; + + if(litstate) { + /* adjust region size for walls, but only if lighted */ + lowx = max(lowx-1,1); + hix = min(hix+1,COLNO-1); + lowy = max(lowy-1,0); + hiy = min(hiy+1, ROWNO-1); + } + for(x = lowx; x <= hix; x++) { + lev = &levl[x][lowy]; + for(y = lowy; y <= hiy; y++) { + if (lev->typ != LAVAPOOL) /* this overrides normal lighting */ + lev->lit = litstate; + lev++; + } + } +} + +/* initialization common to all special levels */ +STATIC_OVL void +load_common_data(fd, typ) +dlb *fd; +int typ; +{ + uchar n; + long lev_flags; + int i; + + { + aligntyp atmp; + /* shuffle 3 alignments; can't use sp_lev_shuffle() on aligntyp's */ + i = rn2(3); atmp=ralign[2]; ralign[2]=ralign[i]; ralign[i]=atmp; + if (rn2(2)) { atmp=ralign[1]; ralign[1]=ralign[0]; ralign[0]=atmp; } + } + + level.flags.is_maze_lev = typ == SP_LEV_MAZE; + + /* Read the level initialization data */ + Fread((genericptr_t) &init_lev, 1, sizeof(lev_init), fd); + if(init_lev.init_present) { + if(init_lev.lit < 0) + init_lev.lit = rn2(2); + mkmap(&init_lev); + } + + /* Read the per level flags */ + Fread((genericptr_t) &lev_flags, 1, sizeof(lev_flags), fd); + if (lev_flags & NOTELEPORT) + level.flags.noteleport = 1; + if (lev_flags & HARDFLOOR) + level.flags.hardfloor = 1; + if (lev_flags & NOMMAP) + level.flags.nommap = 1; + if (lev_flags & SHORTSIGHTED) + level.flags.shortsighted = 1; + if (lev_flags & ARBOREAL) + level.flags.arboreal = 1; + + /* Read message */ + Fread((genericptr_t) &n, 1, sizeof(n), fd); + if (n) { + lev_message = (char *) alloc(n + 1); + Fread((genericptr_t) lev_message, 1, (int) n, fd); + lev_message[n] = 0; + } +} + +STATIC_OVL void +load_one_monster(fd, m) +dlb *fd; +monster *m; +{ + int size; + + Fread((genericptr_t) m, 1, sizeof *m, fd); + if ((size = m->name.len) != 0) { + m->name.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) m->name.str, 1, size, fd); + m->name.str[size] = '\0'; + } else + m->name.str = (char *) 0; + if ((size = m->appear_as.len) != 0) { + m->appear_as.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) m->appear_as.str, 1, size, fd); + m->appear_as.str[size] = '\0'; + } else + m->appear_as.str = (char *) 0; +} + +STATIC_OVL void +load_one_object(fd, o) +dlb *fd; +object *o; +{ + int size; + + Fread((genericptr_t) o, 1, sizeof *o, fd); + if ((size = o->name.len) != 0) { + o->name.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) o->name.str, 1, size, fd); + o->name.str[size] = '\0'; + } else + o->name.str = (char *) 0; +} + +STATIC_OVL void +load_one_engraving(fd, e) +dlb *fd; +engraving *e; +{ + int size; + + Fread((genericptr_t) e, 1, sizeof *e, fd); + size = e->engr.len; + e->engr.str = (char *) alloc((unsigned)size+1); + Fread((genericptr_t) e->engr.str, 1, size, fd); + e->engr.str[size] = '\0'; +} + +STATIC_OVL boolean +load_rooms(fd) +dlb *fd; +{ + xchar nrooms, ncorr; + char n; + short size; + corridor tmpcor; + room** tmproom; + int i, j; + + load_common_data(fd, SP_LEV_ROOMS); + + Fread((genericptr_t) &n, 1, sizeof(n), fd); /* nrobjects */ + if (n) { + Fread((genericptr_t)robjects, sizeof(*robjects), n, fd); + sp_lev_shuffle(robjects, (char *)0, (int)n); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); /* nrmonst */ + if (n) { + Fread((genericptr_t)rmonst, sizeof(*rmonst), n, fd); + sp_lev_shuffle(rmonst, (char *)0, (int)n); + } + + Fread((genericptr_t) &nrooms, 1, sizeof(nrooms), fd); + /* Number of rooms to read */ + tmproom = NewTab(room,nrooms); + for (i=0;i 0) { /* Yup, it does! */ + r->name = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) r->name, 1, size, fd); + r->name[size] = 0; + } else + r->name = (char *) 0; + + /* Let's see if this room has a parent */ + Fread((genericptr_t) &size, 1, sizeof(size), fd); + if (size > 0) { /* Yup, it does! */ + r->parent = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) r->parent, 1, size, fd); + r->parent[size] = 0; + } else + r->parent = (char *) 0; + + Fread((genericptr_t) &r->x, 1, sizeof(r->x), fd); + /* x pos on the grid (1-5) */ + Fread((genericptr_t) &r->y, 1, sizeof(r->y), fd); + /* y pos on the grid (1-5) */ + Fread((genericptr_t) &r->w, 1, sizeof(r->w), fd); + /* width of the room */ + Fread((genericptr_t) &r->h, 1, sizeof(r->h), fd); + /* height of the room */ + Fread((genericptr_t) &r->xalign, 1, sizeof(r->xalign), fd); + /* horizontal alignment */ + Fread((genericptr_t) &r->yalign, 1, sizeof(r->yalign), fd); + /* vertical alignment */ + Fread((genericptr_t) &r->rtype, 1, sizeof(r->rtype), fd); + /* type of room (zoo, shop, etc.) */ + Fread((genericptr_t) &r->chance, 1, sizeof(r->chance), fd); + /* chance of room being special. */ + Fread((genericptr_t) &r->rlit, 1, sizeof(r->rlit), fd); + /* lit or not ? */ + Fread((genericptr_t) &r->filled, 1, sizeof(r->filled), fd); + /* to be filled? */ + r->nsubroom= 0; + + /* read the doors */ + Fread((genericptr_t) &r->ndoor, 1, sizeof(r->ndoor), fd); + if ((n = r->ndoor) != 0) + r->doors = NewTab(room_door, n); + while(n--) { + r->doors[(int)n] = New(room_door); + Fread((genericptr_t) r->doors[(int)n], 1, + sizeof(room_door), fd); + } + + /* read the stairs */ + Fread((genericptr_t) &r->nstair, 1, sizeof(r->nstair), fd); + if ((n = r->nstair) != 0) + r->stairs = NewTab(stair, n); + while (n--) { + r->stairs[(int)n] = New(stair); + Fread((genericptr_t) r->stairs[(int)n], 1, + sizeof(stair), fd); + } + + /* read the altars */ + Fread((genericptr_t) &r->naltar, 1, sizeof(r->naltar), fd); + if ((n = r->naltar) != 0) + r->altars = NewTab(altar, n); + while (n--) { + r->altars[(int)n] = New(altar); + Fread((genericptr_t) r->altars[(int)n], 1, + sizeof(altar), fd); + } + + /* read the fountains */ + Fread((genericptr_t) &r->nfountain, 1, + sizeof(r->nfountain), fd); + if ((n = r->nfountain) != 0) + r->fountains = NewTab(fountain, n); + while (n--) { + r->fountains[(int)n] = New(fountain); + Fread((genericptr_t) r->fountains[(int)n], 1, + sizeof(fountain), fd); + } + + /* read the sinks */ + Fread((genericptr_t) &r->nsink, 1, sizeof(r->nsink), fd); + if ((n = r->nsink) != 0) + r->sinks = NewTab(sink, n); + while (n--) { + r->sinks[(int)n] = New(sink); + Fread((genericptr_t) r->sinks[(int)n], 1, sizeof(sink), fd); + } + + /* read the pools */ + Fread((genericptr_t) &r->npool, 1, sizeof(r->npool), fd); + if ((n = r->npool) != 0) + r->pools = NewTab(pool,n); + while (n--) { + r->pools[(int)n] = New(pool); + Fread((genericptr_t) r->pools[(int)n], 1, sizeof(pool), fd); + } + + /* read the traps */ + Fread((genericptr_t) &r->ntrap, 1, sizeof(r->ntrap), fd); + if ((n = r->ntrap) != 0) + r->traps = NewTab(trap, n); + while(n--) { + r->traps[(int)n] = New(trap); + Fread((genericptr_t) r->traps[(int)n], 1, sizeof(trap), fd); + } + + /* read the monsters */ + Fread((genericptr_t) &r->nmonster, 1, sizeof(r->nmonster), fd); + if ((n = r->nmonster) != 0) { + r->monsters = NewTab(monster, n); + while(n--) { + r->monsters[(int)n] = New(monster); + load_one_monster(fd, r->monsters[(int)n]); + } + } else + r->monsters = 0; + + /* read the objects */ + Fread((genericptr_t) &r->nobject, 1, sizeof(r->nobject), fd); + if ((n = r->nobject) != 0) { + r->objects = NewTab(object, n); + while (n--) { + r->objects[(int)n] = New(object); + load_one_object(fd, r->objects[(int)n]); + } + } else + r->objects = 0; + + /* read the gold piles */ + Fread((genericptr_t) &r->ngold, 1, sizeof(r->ngold), fd); + if ((n = r->ngold) != 0) + r->golds = NewTab(gold, n); + while (n--) { + r->golds[(int)n] = New(gold); + Fread((genericptr_t) r->golds[(int)n], 1, sizeof(gold), fd); + } + + /* read the engravings */ + Fread((genericptr_t) &r->nengraving, 1, + sizeof(r->nengraving), fd); + if ((n = r->nengraving) != 0) { + r->engravings = NewTab(engraving,n); + while (n--) { + r->engravings[(int)n] = New(engraving); + load_one_engraving(fd, r->engravings[(int)n]); + } + } else + r->engravings = 0; + + } + + /* Now that we have loaded all the rooms, search the + * subrooms and create the links. + */ + + for (i = 0; iparent) { + /* Search the parent room */ + for(j=0; jname && !strcmp(tmproom[j]->name, + tmproom[i]->parent)) { + n = tmproom[j]->nsubroom++; + tmproom[j]->subrooms[(int)n] = tmproom[i]; + break; + } + } + + /* + * Create the rooms now... + */ + + for (i=0; i < nrooms; i++) + if(!tmproom[i]->parent) + build_room(tmproom[i], (room *) 0); + + free_rooms(tmproom, nrooms); + + /* read the corridors */ + + Fread((genericptr_t) &ncorr, sizeof(ncorr), 1, fd); + for (i=0; ix = (xchar)x, m->y = (xchar)y; +} + +/* + * The Big Thing: special maze loader + * + * Could be cleaner, but it works. + */ + +STATIC_OVL boolean +load_maze(fd) +dlb *fd; +{ + xchar x, y, typ; + boolean prefilled, room_not_needed; + + char n, numpart = 0; + xchar nwalk = 0, nwalk_sav; + schar filling; + char halign, valign; + + int xi, dir, size; + coord mm; + int mapcount, mapcountmax, mapfact; + + lev_region tmplregion; + region tmpregion; + door tmpdoor; + trap tmptrap; + monster tmpmons; + object tmpobj; + drawbridge tmpdb; + walk tmpwalk; + digpos tmpdig; + lad tmplad; + stair tmpstair, prevstair; + altar tmpaltar; + gold tmpgold; + fountain tmpfountain; + engraving tmpengraving; + xchar mustfill[(MAXNROFROOMS+1)*2]; + struct trap *badtrap; + boolean has_bounds; + + (void) memset((genericptr_t)&Map[0][0], 0, sizeof Map); + load_common_data(fd, SP_LEV_MAZE); + + /* Initialize map */ + Fread((genericptr_t) &filling, 1, sizeof(filling), fd); + if (!init_lev.init_present) { /* don't init if mkmap() has been called */ + for(x = 2; x <= x_maze_max; x++) + for(y = 0; y <= y_maze_max; y++) + if (filling == -1) { +#ifndef WALLIFIED_MAZE + levl[x][y].typ = STONE; +#else + levl[x][y].typ = + (y < 2 || ((x % 2) && (y % 2))) ? STONE : HWALL; +#endif + } else { + levl[x][y].typ = filling; + } + } + + /* Start reading the file */ + Fread((genericptr_t) &numpart, 1, sizeof(numpart), fd); + /* Number of parts */ + if (!numpart || numpart > 9) + panic("load_maze error: numpart = %d", (int) numpart); + + while (numpart--) { + Fread((genericptr_t) &halign, 1, sizeof(halign), fd); + /* Horizontal alignment */ + Fread((genericptr_t) &valign, 1, sizeof(valign), fd); + /* Vertical alignment */ + Fread((genericptr_t) &xsize, 1, sizeof(xsize), fd); + /* size in X */ + Fread((genericptr_t) &ysize, 1, sizeof(ysize), fd); + /* size in Y */ + switch((int) halign) { + case LEFT: xstart = 3; break; + case H_LEFT: xstart = 2+((x_maze_max-2-xsize)/4); break; + case CENTER: xstart = 2+((x_maze_max-2-xsize)/2); break; + case H_RIGHT: xstart = 2+((x_maze_max-2-xsize)*3/4); break; + case RIGHT: xstart = x_maze_max-xsize-1; break; + } + switch((int) valign) { + case TOP: ystart = 3; break; + case CENTER: ystart = 2+((y_maze_max-2-ysize)/2); break; + case BOTTOM: ystart = y_maze_max-ysize-1; break; + } + if (!(xstart % 2)) xstart++; + if (!(ystart % 2)) ystart++; + if ((ystart < 0) || (ystart + ysize > ROWNO)) { + /* try to move the start a bit */ + ystart += (ystart > 0) ? -2 : 2; + if(ysize == ROWNO) ystart = 0; + if(ystart < 0 || ystart + ysize > ROWNO) + panic("reading special level with ysize too large"); + } + + /* + * If any CROSSWALLs are found, must change to ROOM after REGION's + * are laid out. CROSSWALLS are used to specify "invisible" + * boundaries where DOOR syms look bad or aren't desirable. + */ + has_bounds = FALSE; + + if(init_lev.init_present && xsize <= 1 && ysize <= 1) { + xstart = 1; + ystart = 0; + xsize = COLNO-1; + ysize = ROWNO; + } else { + /* Load the map */ + for(y = ystart; y < ystart+ysize; y++) + for(x = xstart; x < xstart+xsize; x++) { + levl[x][y].typ = Fgetc(fd); + levl[x][y].lit = FALSE; + /* + * Note: Even though levl[x][y].typ is type schar, + * lev_comp.y saves it as type char. Since schar != char + * all the time we must make this exception or hack + * through lev_comp.y to fix. + */ + + /* + * Set secret doors to closed (why not trapped too?). Set + * the horizontal bit. + */ + if (levl[x][y].typ == SDOOR || IS_DOOR(levl[x][y].typ)) { + if(levl[x][y].typ == SDOOR) + levl[x][y].doormask = D_CLOSED; + /* + * If there is a wall to the left that connects to a + * (secret) door, then it is horizontal. This does + * not allow (secret) doors to be corners of rooms. + */ + if (x != xstart && (IS_WALL(levl[x-1][y].typ) || + levl[x-1][y].horizontal)) + levl[x][y].horizontal = 1; + } else if(levl[x][y].typ == HWALL || + levl[x][y].typ == IRONBARS) + levl[x][y].horizontal = 1; + else if(levl[x][y].typ == LAVAPOOL) + levl[x][y].lit = 1; + else if(levl[x][y].typ == CROSSWALL) + has_bounds = TRUE; + Map[x][y] = 1; + } + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of level regions */ + if(n) { + if(num_lregions) { + /* realloc the lregion space to add the new ones */ + /* don't really free it up until the whole level is done */ + lev_region *newl = (lev_region *) alloc(sizeof(lev_region) * + (unsigned)(n+num_lregions)); + (void) memcpy((genericptr_t)(newl+n), (genericptr_t)lregions, + sizeof(lev_region) * num_lregions); + Free(lregions); + num_lregions += n; + lregions = newl; + } else { + num_lregions = n; + lregions = (lev_region *) + alloc(sizeof(lev_region) * (unsigned)n); + } + } + + while(n--) { + Fread((genericptr_t) &tmplregion, sizeof(tmplregion), 1, fd); + if ((size = tmplregion.rname.len) != 0) { + tmplregion.rname.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) tmplregion.rname.str, size, 1, fd); + tmplregion.rname.str[size] = '\0'; + } else + tmplregion.rname.str = (char *) 0; + if(!tmplregion.in_islev) { + get_location(&tmplregion.inarea.x1, &tmplregion.inarea.y1, + DRY|WET); + get_location(&tmplregion.inarea.x2, &tmplregion.inarea.y2, + DRY|WET); + } + if(!tmplregion.del_islev) { + get_location(&tmplregion.delarea.x1, &tmplregion.delarea.y1, + DRY|WET); + get_location(&tmplregion.delarea.x2, &tmplregion.delarea.y2, + DRY|WET); + } + lregions[(int)n] = tmplregion; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Random objects */ + if(n) { + Fread((genericptr_t)robjects, sizeof(*robjects), (int) n, fd); + sp_lev_shuffle(robjects, (char *)0, (int)n); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Random locations */ + if(n) { + Fread((genericptr_t)rloc_x, sizeof(*rloc_x), (int) n, fd); + Fread((genericptr_t)rloc_y, sizeof(*rloc_y), (int) n, fd); + sp_lev_shuffle(rloc_x, rloc_y, (int)n); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Random monsters */ + if(n) { + Fread((genericptr_t)rmonst, sizeof(*rmonst), (int) n, fd); + sp_lev_shuffle(rmonst, (char *)0, (int)n); + } + + (void) memset((genericptr_t)mustfill, 0, sizeof(mustfill)); + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of subrooms */ + while(n--) { + register struct mkroom *troom; + + Fread((genericptr_t)&tmpregion, 1, sizeof(tmpregion), fd); + + if(tmpregion.rtype > MAXRTYPE) { + tmpregion.rtype -= MAXRTYPE+1; + prefilled = TRUE; + } else + prefilled = FALSE; + + if(tmpregion.rlit < 0) + tmpregion.rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) + ? TRUE : FALSE; + + get_location(&tmpregion.x1, &tmpregion.y1, DRY|WET); + get_location(&tmpregion.x2, &tmpregion.y2, DRY|WET); + + /* for an ordinary room, `prefilled' is a flag to force + an actual room to be created (such rooms are used to + control placement of migrating monster arrivals) */ + room_not_needed = (tmpregion.rtype == OROOM && + !tmpregion.rirreg && !prefilled); + if (room_not_needed || nroom >= MAXNROFROOMS) { + if (!room_not_needed) + impossible("Too many rooms on new level!"); + light_region(&tmpregion); + continue; + } + + troom = &rooms[nroom]; + + /* mark rooms that must be filled, but do it later */ + if (tmpregion.rtype != OROOM) + mustfill[nroom] = (prefilled ? 2 : 1); + + if(tmpregion.rirreg) { + min_rx = max_rx = tmpregion.x1; + min_ry = max_ry = tmpregion.y1; + flood_fill_rm(tmpregion.x1, tmpregion.y1, + nroom+ROOMOFFSET, tmpregion.rlit, TRUE); + add_room(min_rx, min_ry, max_rx, max_ry, + FALSE, tmpregion.rtype, TRUE); + troom->rlit = tmpregion.rlit; + troom->irregular = TRUE; + } else { + add_room(tmpregion.x1, tmpregion.y1, + tmpregion.x2, tmpregion.y2, + tmpregion.rlit, tmpregion.rtype, TRUE); +#ifdef SPECIALIZATION + topologize(troom,FALSE); /* set roomno */ +#else + topologize(troom); /* set roomno */ +#endif + } + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of doors */ + while(n--) { + struct mkroom *croom = &rooms[0]; + + Fread((genericptr_t)&tmpdoor, 1, sizeof(tmpdoor), fd); + + x = tmpdoor.x; y = tmpdoor.y; + typ = tmpdoor.mask == -1 ? rnddoor() : tmpdoor.mask; + + get_location(&x, &y, DRY); + if(levl[x][y].typ != SDOOR) + levl[x][y].typ = DOOR; + else { + if(typ < D_CLOSED) + typ = D_CLOSED; /* force it to be closed */ + } + levl[x][y].doormask = typ; + + /* Now the complicated part, list it with each subroom */ + /* The dog move and mail daemon routines use this */ + while(croom->hx >= 0 && doorindex < DOORMAX) { + if(croom->hx >= x-1 && croom->lx <= x+1 && + croom->hy >= y-1 && croom->ly <= y+1) { + /* Found it */ + add_door(x, y, croom); + } + croom++; + } + } + + /* now that we have rooms _and_ associated doors, fill the rooms */ + for(n = 0; n < SIZE(mustfill); n++) + if(mustfill[(int)n]) + fill_room(&rooms[(int)n], (mustfill[(int)n] == 2)); + + /* if special boundary syms (CROSSWALL) in map, remove them now */ + if(has_bounds) { + for(x = xstart; x < xstart+xsize; x++) + for(y = ystart; y < ystart+ysize; y++) + if(levl[x][y].typ == CROSSWALL) + levl[x][y].typ = ROOM; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of drawbridges */ + while(n--) { + Fread((genericptr_t)&tmpdb, 1, sizeof(tmpdb), fd); + + x = tmpdb.x; y = tmpdb.y; + get_location(&x, &y, DRY|WET); + + if (!create_drawbridge(x, y, tmpdb.dir, tmpdb.db_open)) + impossible("Cannot create drawbridge."); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of mazewalks */ + while(n--) { + Fread((genericptr_t)&tmpwalk, 1, sizeof(tmpwalk), fd); + + get_location(&tmpwalk.x, &tmpwalk.y, DRY|WET); + + walklist[nwalk++] = tmpwalk; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of non_diggables */ + while(n--) { + Fread((genericptr_t)&tmpdig, 1, sizeof(tmpdig), fd); + + get_location(&tmpdig.x1, &tmpdig.y1, DRY|WET); + get_location(&tmpdig.x2, &tmpdig.y2, DRY|WET); + + set_wall_property(tmpdig.x1, tmpdig.y1, + tmpdig.x2, tmpdig.y2, W_NONDIGGABLE); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of non_passables */ + while(n--) { + Fread((genericptr_t)&tmpdig, 1, sizeof(tmpdig), fd); + + get_location(&tmpdig.x1, &tmpdig.y1, DRY|WET); + get_location(&tmpdig.x2, &tmpdig.y2, DRY|WET); + + set_wall_property(tmpdig.x1, tmpdig.y1, + tmpdig.x2, tmpdig.y2, W_NONPASSWALL); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of ladders */ + while(n--) { + Fread((genericptr_t)&tmplad, 1, sizeof(tmplad), fd); + + x = tmplad.x; y = tmplad.y; + get_location(&x, &y, DRY); + + levl[x][y].typ = LADDER; + if (tmplad.up == 1) { + xupladder = x; yupladder = y; + levl[x][y].ladder = LA_UP; + } else { + xdnladder = x; ydnladder = y; + levl[x][y].ladder = LA_DOWN; + } + } + + prevstair.x = prevstair.y = 0; + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of stairs */ + while(n--) { + Fread((genericptr_t)&tmpstair, 1, sizeof(tmpstair), fd); + + xi = 0; + do { + x = tmpstair.x; y = tmpstair.y; + get_location(&x, &y, DRY); + } while(prevstair.x && xi++ < 100 && + distmin(x,y,prevstair.x,prevstair.y) <= 8); + if ((badtrap = t_at(x,y)) != 0) deltrap(badtrap); + mkstairs(x, y, (char)tmpstair.up, (struct mkroom *)0); + prevstair.x = x; + prevstair.y = y; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of altars */ + while(n--) { + Fread((genericptr_t)&tmpaltar, 1, sizeof(tmpaltar), fd); + + create_altar(&tmpaltar, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of fountains */ + while (n--) { + Fread((genericptr_t)&tmpfountain, 1, sizeof(tmpfountain), fd); + + create_feature(tmpfountain.x, tmpfountain.y, + (struct mkroom *)0, FOUNTAIN); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of traps */ + while(n--) { + Fread((genericptr_t)&tmptrap, 1, sizeof(tmptrap), fd); + + create_trap(&tmptrap, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of monsters */ + while(n--) { + load_one_monster(fd, &tmpmons); + + create_monster(&tmpmons, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of objects */ + while(n--) { + load_one_object(fd, &tmpobj); + + create_object(&tmpobj, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of gold piles */ + while (n--) { + Fread((genericptr_t)&tmpgold, 1, sizeof(tmpgold), fd); + + create_gold(&tmpgold, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of engravings */ + while(n--) { + load_one_engraving(fd, &tmpengraving); + + create_engraving(&tmpengraving, (struct mkroom *)0); + } + + } /* numpart loop */ + + nwalk_sav = nwalk; + while(nwalk--) { + x = (xchar) walklist[nwalk].x; + y = (xchar) walklist[nwalk].y; + dir = walklist[nwalk].dir; + + /* don't use move() - it doesn't use W_NORTH, etc. */ + switch (dir) { + case W_NORTH: --y; break; + case W_SOUTH: y++; break; + case W_EAST: x++; break; + case W_WEST: --x; break; + default: panic("load_maze: bad MAZEWALK direction"); + } + + if(!IS_DOOR(levl[x][y].typ)) { +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + levl[x][y].flags = 0; + } + + /* + * We must be sure that the parity of the coordinates for + * walkfrom() is odd. But we must also take into account + * what direction was chosen. + */ + if(!(x % 2)) { + if (dir == W_EAST) + x++; + else + x--; + + /* no need for IS_DOOR check; out of map bounds */ +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + levl[x][y].flags = 0; + } + + if (!(y % 2)) { + if (dir == W_SOUTH) + y++; + else + y--; + } + + walkfrom(x, y); + } + wallification(1, 0, COLNO-1, ROWNO-1); + + /* + * If there's a significant portion of maze unused by the special level, + * we don't want it empty. + * + * Makes the number of traps, monsters, etc. proportional + * to the size of the maze. + */ + mapcountmax = mapcount = (x_maze_max - 2) * (y_maze_max - 2); + + for(x = 2; x < x_maze_max; x++) + for(y = 0; y < y_maze_max; y++) + if(Map[x][y]) mapcount--; + + if (nwalk_sav && (mapcount > (int) (mapcountmax / 10))) { + mapfact = (int) ((mapcount * 100L) / mapcountmax); + for(x = rnd((int) (20 * mapfact) / 100); x; x--) { + maze1xy(&mm, DRY); + (void) mkobj_at(rn2(2) ? GEM_CLASS : RANDOM_CLASS, + mm.x, mm.y, TRUE); + } + for(x = rnd((int) (12 * mapfact) / 100); x; x--) { + maze1xy(&mm, DRY); + (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE); + } + for (x = rn2(2); x; x--) { + maze1xy(&mm, DRY); + (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS); + } + for(x = rnd((int) (12 * mapfact) / 100); x; x--) { + maze1xy(&mm, WET|DRY); + (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS); + } + for(x = rn2((int) (15 * mapfact) / 100); x; x--) { + maze1xy(&mm, DRY); + (void) mkgold(0L,mm.x,mm.y); + } + for(x = rn2((int) (15 * mapfact) / 100); x; x--) { + int trytrap; + + maze1xy(&mm, DRY); + trytrap = rndtrap(); + if (sobj_at(BOULDER, mm.x, mm.y)) + while (trytrap == PIT || trytrap == SPIKED_PIT || + trytrap == TRAPDOOR || trytrap == HOLE) + trytrap = rndtrap(); + (void) maketrap(mm.x, mm.y, trytrap); + } + } + return TRUE; +} + +/* + * General loader + */ + +boolean +load_special(name) +const char *name; +{ + dlb *fd; + boolean result = FALSE; + char c; + struct version_info vers_info; + + fd = dlb_fopen(name, RDBMODE); + if (!fd) return FALSE; + + Fread((genericptr_t) &vers_info, sizeof vers_info, 1, fd); + if (!check_version(&vers_info, name, TRUE)) + goto give_up; + + Fread((genericptr_t) &c, sizeof c, 1, fd); /* c Header */ + + switch (c) { + case SP_LEV_ROOMS: + result = load_rooms(fd); + break; + case SP_LEV_MAZE: + result = load_maze(fd); + break; + default: /* ??? */ + result = FALSE; + } + give_up: + (void)dlb_fclose(fd); + return result; +} + +/*sp_lev.c*/