From c9de3aadb7a75ddd3de7b007c9dd0a29f8a0daab Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:49 +0000 Subject: [PATCH] *** empty log message *** --- src/mklev.c | 1590 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1590 insertions(+) create mode 100644 src/mklev.c diff --git a/src/mklev.c b/src/mklev.c new file mode 100644 index 000000000..3f2b3105f --- /dev/null +++ b/src/mklev.c @@ -0,0 +1,1590 @@ +/* SCCS Id: @(#)mklev.c 3.3 2001/11/29 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +/* #define DEBUG */ /* uncomment to enable code debugging */ + +#ifdef DEBUG +# ifdef WIZARD +#define debugpline if (wizard) pline +# else +#define debugpline pline +# endif +#endif + +/* for UNIX, Rand #def'd to (long)lrand48() or (long)random() */ +/* croom->lx etc are schar (width <= int), so % arith ensures that */ +/* conversion of result to int is reasonable */ + + +STATIC_DCL void FDECL(mkfount,(int,struct mkroom *)); +#ifdef SINKS +STATIC_DCL void FDECL(mksink,(struct mkroom *)); +#endif +STATIC_DCL void FDECL(mkaltar,(struct mkroom *)); +STATIC_DCL void FDECL(mkgrave,(struct mkroom *)); +STATIC_DCL void NDECL(makevtele); +STATIC_DCL void NDECL(clear_level_structures); +STATIC_DCL void NDECL(makelevel); +STATIC_DCL void NDECL(mineralize); +STATIC_DCL boolean FDECL(bydoor,(XCHAR_P,XCHAR_P)); +STATIC_DCL struct mkroom *FDECL(find_branch_room, (coord *)); +STATIC_DCL struct mkroom *FDECL(pos_to_room, (XCHAR_P, XCHAR_P)); +STATIC_DCL boolean FDECL(place_niche,(struct mkroom *,int*,int*,int*)); +STATIC_DCL void FDECL(makeniche,(int)); +STATIC_DCL void NDECL(make_niches); +STATIC_PTR int FDECL(do_comp,(const genericptr,const genericptr)); +STATIC_DCL void FDECL(dosdoor,(XCHAR_P,XCHAR_P,struct mkroom *,int)); +STATIC_DCL void FDECL(join,(int,int,BOOLEAN_P)); +STATIC_DCL void FDECL(do_room_or_subroom, (struct mkroom *,int,int,int,int, + BOOLEAN_P,SCHAR_P,BOOLEAN_P,BOOLEAN_P)); +STATIC_DCL void NDECL(makerooms); +STATIC_DCL void FDECL(finddpos,(coord *,XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +STATIC_DCL void FDECL(mkinvpos, (XCHAR_P,XCHAR_P,int)); +STATIC_DCL void FDECL(mk_knox_portal, (XCHAR_P,XCHAR_P)); + +#define create_vault() create_room(-1, -1, 2, 2, -1, -1, VAULT, TRUE) +#define init_vault() vault_x = -1 +#define do_vault() (vault_x != -1) +static xchar vault_x, vault_y; +boolean goldseen; +static boolean made_branch; /* used only during level creation */ + +/* Args must be (const genericptr) so that qsort will always be happy. */ + +STATIC_PTR int +do_comp(vx,vy) +const genericptr vx; +const genericptr vy; +{ +#ifdef LINT +/* lint complains about possible pointer alignment problems, but we know + that vx and vy are always properly aligned. Hence, the following + bogus definition: +*/ + return (vx == vy) ? 0 : -1; +#else + register const struct mkroom *x, *y; + + x = (const struct mkroom *)vx; + y = (const struct mkroom *)vy; + if(x->lx < y->lx) return(-1); + return(x->lx > y->lx); +#endif /* LINT */ +} + +STATIC_OVL void +finddpos(cc, xl,yl,xh,yh) +coord *cc; +xchar xl,yl,xh,yh; +{ + register xchar x, y; + + x = (xl == xh) ? xl : (xl + rn2(xh-xl+1)); + y = (yl == yh) ? yl : (yl + rn2(yh-yl+1)); + if(okdoor(x, y)) + goto gotit; + + for(x = xl; x <= xh; x++) for(y = yl; y <= yh; y++) + if(okdoor(x, y)) + goto gotit; + + for(x = xl; x <= xh; x++) for(y = yl; y <= yh; y++) + if(IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) + goto gotit; + /* cannot find something reasonable -- strange */ + x = xl; + y = yh; +gotit: + cc->x = x; + cc->y = y; + return; +} + +void +sort_rooms() +{ +#if defined(SYSV) || defined(DGUX) + qsort((genericptr_t) rooms, (unsigned)nroom, sizeof(struct mkroom), do_comp); +#else + qsort((genericptr_t) rooms, nroom, sizeof(struct mkroom), do_comp); +#endif +} + +STATIC_OVL void +do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, is_room) + register struct mkroom *croom; + int lowx, lowy; + register int hix, hiy; + boolean lit; + schar rtype; + boolean special; + boolean is_room; +{ + register int x, y; + struct rm *lev; + + /* locations might bump level edges in wall-less rooms */ + /* add/subtract 1 to allow for edge locations */ + if(!lowx) lowx++; + if(!lowy) lowy++; + if(hix >= COLNO-1) hix = COLNO-2; + if(hiy >= ROWNO-1) hiy = ROWNO-2; + + if(lit) { + for(x = lowx-1; x <= hix+1; x++) { + lev = &levl[x][max(lowy-1,0)]; + for(y = lowy-1; y <= hiy+1; y++) + lev++->lit = 1; + } + croom->rlit = 1; + } else + croom->rlit = 0; + + croom->lx = lowx; + croom->hx = hix; + croom->ly = lowy; + croom->hy = hiy; + croom->rtype = rtype; + croom->doorct = 0; + /* if we're not making a vault, doorindex will still be 0 + * if we are, we'll have problems adding niches to the previous room + * unless fdoor is at least doorindex + */ + croom->fdoor = doorindex; + croom->irregular = FALSE; + + croom->nsubrooms = 0; + croom->sbrooms[0] = (struct mkroom *) 0; + if (!special) { + for(x = lowx-1; x <= hix+1; x++) + for(y = lowy-1; y <= hiy+1; y += (hiy-lowy+2)) { + levl[x][y].typ = HWALL; + levl[x][y].horizontal = 1; /* For open/secret doors. */ + } + for(x = lowx-1; x <= hix+1; x += (hix-lowx+2)) + for(y = lowy; y <= hiy; y++) { + levl[x][y].typ = VWALL; + levl[x][y].horizontal = 0; /* For open/secret doors. */ + } + for(x = lowx; x <= hix; x++) { + lev = &levl[x][lowy]; + for(y = lowy; y <= hiy; y++) + lev++->typ = ROOM; + } + if (is_room) { + levl[lowx-1][lowy-1].typ = TLCORNER; + levl[hix+1][lowy-1].typ = TRCORNER; + levl[lowx-1][hiy+1].typ = BLCORNER; + levl[hix+1][hiy+1].typ = BRCORNER; + } else { /* a subroom */ + wallification(lowx-1, lowy-1, hix+1, hiy+1); + } + } +} + + +void +add_room(lowx, lowy, hix, hiy, lit, rtype, special) +register int lowx, lowy, hix, hiy; +boolean lit; +schar rtype; +boolean special; +{ + register struct mkroom *croom; + + croom = &rooms[nroom]; + do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, + rtype, special, (boolean) TRUE); + croom++; + croom->hx = -1; + nroom++; +} + +void +add_subroom(proom, lowx, lowy, hix, hiy, lit, rtype, special) +struct mkroom *proom; +register int lowx, lowy, hix, hiy; +boolean lit; +schar rtype; +boolean special; +{ + register struct mkroom *croom; + + croom = &subrooms[nsubroom]; + do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, + rtype, special, (boolean) FALSE); + proom->sbrooms[proom->nsubrooms++] = croom; + croom++; + croom->hx = -1; + nsubroom++; +} + +STATIC_OVL void +makerooms() +{ + boolean tried_vault = FALSE; + + /* make rooms until satisfied */ + /* rnd_rect() will returns 0 if no more rects are available... */ + while(nroom < MAXNROFROOMS && rnd_rect()) { + if(nroom >= (MAXNROFROOMS/6) && rn2(2) && !tried_vault) { + tried_vault = TRUE; + if (create_vault()) { + vault_x = rooms[nroom].lx; + vault_y = rooms[nroom].ly; + rooms[nroom].hx = -1; + } + } else + if (!create_room(-1, -1, -1, -1, -1, -1, OROOM, -1)) + return; + } + return; +} + +STATIC_OVL void +join(a,b,nxcor) +register int a, b; +boolean nxcor; +{ + coord cc,tt, org, dest; + register xchar tx, ty, xx, yy; + register struct mkroom *croom, *troom; + register int dx, dy; + + croom = &rooms[a]; + troom = &rooms[b]; + + /* find positions cc and tt for doors in croom and troom + and direction for a corridor between them */ + + if(troom->hx < 0 || croom->hx < 0 || doorindex >= DOORMAX) return; + if(troom->lx > croom->hx) { + dx = 1; + dy = 0; + xx = croom->hx+1; + tx = troom->lx-1; + finddpos(&cc, xx, croom->ly, xx, croom->hy); + finddpos(&tt, tx, troom->ly, tx, troom->hy); + } else if(troom->hy < croom->ly) { + dy = -1; + dx = 0; + yy = croom->ly-1; + finddpos(&cc, croom->lx, yy, croom->hx, yy); + ty = troom->hy+1; + finddpos(&tt, troom->lx, ty, troom->hx, ty); + } else if(troom->hx < croom->lx) { + dx = -1; + dy = 0; + xx = croom->lx-1; + tx = troom->hx+1; + finddpos(&cc, xx, croom->ly, xx, croom->hy); + finddpos(&tt, tx, troom->ly, tx, troom->hy); + } else { + dy = 1; + dx = 0; + yy = croom->hy+1; + ty = troom->ly-1; + finddpos(&cc, croom->lx, yy, croom->hx, yy); + finddpos(&tt, troom->lx, ty, troom->hx, ty); + } + xx = cc.x; + yy = cc.y; + tx = tt.x - dx; + ty = tt.y - dy; + if(nxcor && levl[xx+dx][yy+dy].typ) + return; + if (okdoor(xx,yy) || !nxcor) + dodoor(xx,yy,croom); + + org.x = xx+dx; org.y = yy+dy; + dest.x = tx; dest.y = ty; + + if (!dig_corridor(&org, &dest, nxcor, + level.flags.arboreal ? ROOM : CORR, STONE)) + return; + + /* we succeeded in digging the corridor */ + if (okdoor(tt.x, tt.y) || !nxcor) + dodoor(tt.x, tt.y, troom); + + if(smeq[a] < smeq[b]) + smeq[b] = smeq[a]; + else + smeq[a] = smeq[b]; +} + +void +makecorridors() +{ + int a, b, i; + boolean any = TRUE; + + for(a = 0; a < nroom-1; a++) { + join(a, a+1, FALSE); + if(!rn2(50)) break; /* allow some randomness */ + } + for(a = 0; a < nroom-2; a++) + if(smeq[a] != smeq[a+2]) + join(a, a+2, FALSE); + for(a = 0; any && a < nroom; a++) { + any = FALSE; + for(b = 0; b < nroom; b++) + if(smeq[a] != smeq[b]) { + join(a, b, FALSE); + any = TRUE; + } + } + if(nroom > 2) + for(i = rn2(nroom) + 4; i; i--) { + a = rn2(nroom); + b = rn2(nroom-2); + if(b >= a) b += 2; + join(a, b, TRUE); + } +} + +void +add_door(x,y,aroom) +register int x, y; +register struct mkroom *aroom; +{ + register struct mkroom *broom; + register int tmp; + + aroom->doorct++; + broom = aroom+1; + if(broom->hx < 0) + tmp = doorindex; + else + for(tmp = doorindex; tmp > broom->fdoor; tmp--) + doors[tmp] = doors[tmp-1]; + doorindex++; + doors[tmp].x = x; + doors[tmp].y = y; + for( ; broom->hx >= 0; broom++) broom->fdoor++; +} + +STATIC_OVL void +dosdoor(x,y,aroom,type) +register xchar x, y; +register struct mkroom *aroom; +register int type; +{ + boolean shdoor = ((*in_rooms(x, y, SHOPBASE))? TRUE : FALSE); + + if(!IS_WALL(levl[x][y].typ)) /* avoid SDOORs on already made doors */ + type = DOOR; + levl[x][y].typ = type; + if(type == DOOR) { + if(!rn2(3)) { /* is it a locked door, closed, or a doorway? */ + if(!rn2(5)) + levl[x][y].doormask = D_ISOPEN; + else if(!rn2(6)) + levl[x][y].doormask = D_LOCKED; + else + levl[x][y].doormask = D_CLOSED; + + if (levl[x][y].doormask != D_ISOPEN && !shdoor && + level_difficulty() >= 5 && !rn2(25)) + levl[x][y].doormask |= D_TRAPPED; + } else +#ifdef STUPID + if (shdoor) + levl[x][y].doormask = D_ISOPEN; + else + levl[x][y].doormask = D_NODOOR; +#else + levl[x][y].doormask = (shdoor ? D_ISOPEN : D_NODOOR); +#endif + if(levl[x][y].doormask & D_TRAPPED) { + struct monst *mtmp; + + if (level_difficulty() >= 9 && !rn2(5) && + !((mvitals[PM_SMALL_MIMIC].mvflags & G_GONE) && + (mvitals[PM_LARGE_MIMIC].mvflags & G_GONE) && + (mvitals[PM_GIANT_MIMIC].mvflags & G_GONE))) { + /* make a mimic instead */ + levl[x][y].doormask = D_NODOOR; + mtmp = makemon(mkclass(S_MIMIC,0), x, y, NO_MM_FLAGS); + if (mtmp) + set_mimic_sym(mtmp); + } + } + /* newsym(x,y); */ + } else { /* SDOOR */ + if(shdoor || !rn2(5)) levl[x][y].doormask = D_LOCKED; + else levl[x][y].doormask = D_CLOSED; + + if(!shdoor && level_difficulty() >= 4 && !rn2(20)) + levl[x][y].doormask |= D_TRAPPED; + } + + add_door(x,y,aroom); +} + +STATIC_OVL boolean +place_niche(aroom,dy,xx,yy) +register struct mkroom *aroom; +int *dy, *xx, *yy; +{ + coord dd; + + if(rn2(2)) { + *dy = 1; + finddpos(&dd, aroom->lx, aroom->hy+1, aroom->hx, aroom->hy+1); + } else { + *dy = -1; + finddpos(&dd, aroom->lx, aroom->ly-1, aroom->hx, aroom->ly-1); + } + *xx = dd.x; + *yy = dd.y; + return((boolean)((isok(*xx,*yy+*dy) && levl[*xx][*yy+*dy].typ == STONE) + && (isok(*xx,*yy-*dy) && !IS_POOL(levl[*xx][*yy-*dy].typ) + && !IS_FURNITURE(levl[*xx][*yy-*dy].typ)))); +} + +/* there should be one of these per trap, in the same order as trap.h */ +static NEARDATA const char *trap_engravings[TRAPNUM] = { + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0, (char *)0, + /* 14..16: trap door, teleport, level-teleport */ + "Vlad was here", "ad aerarium", "ad aerarium", + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, +}; + +STATIC_OVL void +makeniche(trap_type) +int trap_type; +{ + register struct mkroom *aroom; + register struct rm *rm; + register int vct = 8; + int dy, xx, yy; + register struct trap *ttmp; + + if(doorindex < DOORMAX) + while(vct--) { + aroom = &rooms[rn2(nroom)]; + if(aroom->rtype != OROOM) continue; /* not an ordinary room */ + if(aroom->doorct == 1 && rn2(5)) continue; + if(!place_niche(aroom,&dy,&xx,&yy)) continue; + + rm = &levl[xx][yy+dy]; + if(trap_type || !rn2(4)) { + + rm->typ = SCORR; + if(trap_type) { + if((trap_type == HOLE || trap_type == TRAPDOOR) + && !Can_fall_thru(&u.uz)) + trap_type = ROCKTRAP; + ttmp = maketrap(xx, yy+dy, trap_type); + if (ttmp) { + ttmp->once = 1; + if (trap_engravings[trap_type]) { + make_engr_at(xx, yy-dy, + trap_engravings[trap_type], 0L, DUST); + wipe_engr_at(xx, yy-dy, 5); /* age it a little */ + } + } + } + dosdoor(xx, yy, aroom, SDOOR); + } else { + rm->typ = CORR; + if(rn2(7)) + dosdoor(xx, yy, aroom, rn2(5) ? SDOOR : DOOR); + else { + if (!level.flags.noteleport) + (void) mksobj_at(SCR_TELEPORTATION, + xx, yy+dy, TRUE, FALSE); + if (!rn2(3)) (void) mkobj_at(0, xx, yy+dy, TRUE); + } + } + return; + } +} + +STATIC_OVL void +make_niches() +{ + register int ct = rnd((nroom>>1) + 1), dep = depth(&u.uz); + + boolean ltptr = (!level.flags.noteleport && dep > 15), + vamp = (dep > 5 && dep < 25); + + while(ct--) { + if (ltptr && !rn2(6)) { + ltptr = FALSE; + makeniche(LEVEL_TELEP); + } else if (vamp && !rn2(6)) { + vamp = FALSE; + makeniche(TRAPDOOR); + } else makeniche(NO_TRAP); + } +} + +STATIC_OVL void +makevtele() +{ + makeniche(TELEP_TRAP); +} + +/* clear out various globals that keep information on the current level. + * some of this is only necessary for some types of levels (maze, normal, + * special) but it's easier to put it all in one place than make sure + * each type initializes what it needs to separately. + */ +STATIC_OVL void +clear_level_structures() +{ + static struct rm zerorm = { cmap_to_glyph(S_stone), + 0, 0, 0, 0, 0, 0, 0, 0 }; + register int x,y; + register struct rm *lev; + + for(x=0; xproto); + return; + } else if (dungeons[u.uz.dnum].proto[0]) { + makemaz(""); + return; + } else if (In_mines(&u.uz)) { + makemaz("minefill"); + return; + } else if (In_quest(&u.uz)) { + char fillname[9]; + s_level *loc_lev; + + Sprintf(fillname, "%s-loca", urole.filecode); + loc_lev = find_level(fillname); + + Sprintf(fillname, "%s-fil", urole.filecode); + Strcat(fillname, + (u.uz.dlevel < loc_lev->dlevel.dlevel) ? "a" : "b"); + makemaz(fillname); + return; + } else if(In_hell(&u.uz) || + (rn2(5) && u.uz.dnum == medusa_level.dnum + && depth(&u.uz) > depth(&medusa_level))) { + makemaz(""); + return; + } + } + + /* otherwise, fall through - it's a "regular" level. */ + +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { + makeroguerooms(); + makerogueghost(); + } else +#endif + makerooms(); + sort_rooms(); + + /* construct stairs (up and down in different rooms if possible) */ + croom = &rooms[rn2(nroom)]; + if (!Is_botlevel(&u.uz)) + mkstairs(somex(croom), somey(croom), 0, croom); /* down */ + if (nroom > 1) { + troom = croom; + croom = &rooms[rn2(nroom-1)]; + if (croom == troom) croom++; + } + + if (u.uz.dlevel != 1) { + xchar sx, sy; + do { + sx = somex(croom); + sy = somey(croom); + } while(occupied(sx, sy)); + mkstairs(sx, sy, 1, croom); /* up */ + } + + branchp = Is_branchlev(&u.uz); /* possible dungeon branch */ + room_threshold = branchp ? 4 : 3; /* minimum number of rooms needed + to allow a random special room */ +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) goto skip0; +#endif + makecorridors(); + make_niches(); + + /* make a secret treasure vault, not connected to the rest */ + if(do_vault()) { + xchar w,h; +#ifdef DEBUG + debugpline("trying to make a vault..."); +#endif + w = 1; + h = 1; + if (check_room(&vault_x, &w, &vault_y, &h, TRUE)) { + fill_vault: + add_room(vault_x, vault_y, vault_x+w, + vault_y+h, TRUE, VAULT, FALSE); + level.flags.has_vault = 1; + ++room_threshold; + fill_room(&rooms[nroom - 1], FALSE); + mk_knox_portal(vault_x+w, vault_y+h); + if(!level.flags.noteleport && !rn2(3)) makevtele(); + } else if(rnd_rect() && create_vault()) { + vault_x = rooms[nroom].lx; + vault_y = rooms[nroom].ly; + if (check_room(&vault_x, &w, &vault_y, &h, TRUE)) + goto fill_vault; + else + rooms[nroom].hx = -1; + } + } + + { + register int u_depth = depth(&u.uz); + +#ifdef WIZARD + if(wizard && nh_getenv("SHOPTYPE")) mkroom(SHOPBASE); else +#endif + if (u_depth > 1 && + u_depth < depth(&medusa_level) && + nroom >= room_threshold && + rn2(u_depth) < 3) mkroom(SHOPBASE); + else if (u_depth > 4 && !rn2(6)) mkroom(COURT); + else if (u_depth > 5 && !rn2(8) && + !(mvitals[PM_LEPRECHAUN].mvflags & G_GONE)) mkroom(LEPREHALL); + else if (u_depth > 6 && !rn2(7)) mkroom(ZOO); + else if (u_depth > 8 && !rn2(5)) mkroom(TEMPLE); + else if (u_depth > 9 && !rn2(5) && + !(mvitals[PM_KILLER_BEE].mvflags & G_GONE)) mkroom(BEEHIVE); + else if (u_depth > 11 && !rn2(6)) mkroom(MORGUE); + else if (u_depth > 12 && !rn2(8)) mkroom(ANTHOLE); + else if (u_depth > 14 && !rn2(4) && + !(mvitals[PM_SOLDIER].mvflags & G_GONE)) mkroom(BARRACKS); + else if (u_depth > 15 && !rn2(6)) mkroom(SWAMP); + else if (u_depth > 16 && !rn2(8) && + !(mvitals[PM_COCKATRICE].mvflags & G_GONE)) mkroom(COCKNEST); + } + +#ifdef REINCARNATION +skip0: +#endif + /* Place multi-dungeon branch. */ + place_branch(branchp, 0, 0); + + /* for each room: put things inside */ + for(croom = rooms; croom->hx > 0; croom++) { + if(croom->rtype != OROOM) continue; + + /* put a sleeping monster inside */ + /* Note: monster may be on the stairs. This cannot be + avoided: maybe the player fell through a trap door + while a monster was on the stairs. Conclusion: + we have to check for monsters on the stairs anyway. */ + + if(u.uhave.amulet || !rn2(3)) { + x = somex(croom); y = somey(croom); + tmonst = makemon((struct permonst *) 0, x,y,NO_MM_FLAGS); + if (tmonst && tmonst->data == &mons[PM_GIANT_SPIDER] && + !occupied(x, y)) + (void) maketrap(x, y, WEB); + } + /* put traps and mimics inside */ + goldseen = FALSE; + x = 8 - (level_difficulty()/6); + if (x <= 1) x = 2; + while (!rn2(x)) + mktrap(0,0,croom,(coord*)0); + if (!goldseen && !rn2(3)) + (void) mkgold(0L, somex(croom), somey(croom)); +#ifdef REINCARNATION + if(Is_rogue_level(&u.uz)) goto skip_nonrogue; +#endif + if(!rn2(10)) mkfount(0,croom); +#ifdef SINKS + if(!rn2(60)) mksink(croom); +#endif + if(!rn2(60)) mkaltar(croom); + x = 80 - (depth(&u.uz) * 2); + if (x < 2) x = 2; + if(!rn2(x)) mkgrave(croom); + + /* put statues inside */ + if(!rn2(20)) + (void) mkcorpstat(STATUE, (struct monst *)0, + (struct permonst *)0, + somex(croom), somey(croom), TRUE); + /* put box/chest inside; + * 40% chance for at least 1 box, regardless of number + * of rooms; about 5 - 7.5% for 2 boxes, least likely + * when few rooms; chance for 3 or more is neglible. + */ + if(!rn2(nroom * 5 / 2)) + (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST, + somex(croom), somey(croom), TRUE, FALSE); + + /* maybe make some graffiti */ + if(!rn2(27 + 3 * abs(depth(&u.uz)))) { + char buf[BUFSZ]; + const char *mesg = random_engraving(buf); + if (mesg) { + do { + x = somex(croom); y = somey(croom); + } while(levl[x][y].typ != ROOM && !rn2(40)); + if (!(IS_POOL(levl[x][y].typ) || + IS_FURNITURE(levl[x][y].typ))) + make_engr_at(x, y, mesg, 0L, MARK); + } + } + +#ifdef REINCARNATION + skip_nonrogue: +#endif + if(!rn2(3)) { + (void) mkobj_at(0, somex(croom), somey(croom), TRUE); + tryct = 0; + while(!rn2(5)) { + if(++tryct > 100) { + impossible("tryct overflow4"); + break; + } + (void) mkobj_at(0, somex(croom), somey(croom), TRUE); + } + } + } +} + +/* + * Place deposits of minerals (gold and misc gems) in the stone + * surrounding the rooms on the map. + * Also place kelp in water. + */ +STATIC_OVL void +mineralize() +{ + s_level *sp; + struct obj *otmp; + int goldprob, gemprob, x, y, cnt; + + + /* Place kelp, except on the plane of water */ + if (In_endgame(&u.uz)) return; + for (x = 2; x < (COLNO - 2); x++) + for (y = 1; y < (ROWNO - 1); y++) + if ((levl[x][y].typ == POOL && !rn2(10)) || + (levl[x][y].typ == MOAT && !rn2(30))) + (void) mksobj_at(KELP_FROND, x, y, TRUE, FALSE); + + /* determine if it is even allowed; + almost all special levels are excluded */ + if (In_hell(&u.uz) || In_V_tower(&u.uz) || +#ifdef REINCARNATION + Is_rogue_level(&u.uz) || +#endif + level.flags.arboreal || + ((sp = Is_special(&u.uz)) != 0 && !Is_oracle_level(&u.uz) + && (!In_mines(&u.uz) || sp->flags.town) + )) return; + + /* basic level-related probabilities */ + goldprob = 20 + depth(&u.uz) / 3; + gemprob = goldprob / 4; + + /* mines have ***MORE*** goodies - otherwise why mine? */ + if (In_mines(&u.uz)) { + goldprob *= 2; + gemprob *= 3; + } else if (In_quest(&u.uz)) { + goldprob /= 4; + gemprob /= 6; + } + + /* + * Seed rock areas with gold and/or gems. + * We use fairly low level object handling to avoid unnecessary + * overhead from placing things in the floor chain prior to burial. + */ + for (x = 2; x < (COLNO - 2); x++) + for (y = 1; y < (ROWNO - 1); y++) + if (levl[x][y+1].typ != STONE) { /* spot not eligible */ + y += 2; /* next two spots aren't eligible either */ + } else if (levl[x][y].typ != STONE) { /* this spot not eligible */ + y += 1; /* next spot isn't eligible either */ + } else if (!(levl[x][y].wall_info & W_NONDIGGABLE) && + levl[x][y-1].typ == STONE && + levl[x+1][y-1].typ == STONE && levl[x-1][y-1].typ == STONE && + levl[x+1][y].typ == STONE && levl[x-1][y].typ == STONE && + levl[x+1][y+1].typ == STONE && levl[x-1][y+1].typ == STONE) { + if (rn2(1000) < goldprob) { + if ((otmp = mksobj(GOLD_PIECE, FALSE, FALSE)) != 0) { + otmp->ox = x, otmp->oy = y; + otmp->quan = 1L + rnd(goldprob * 3); + otmp->owt = weight(otmp); + if (!rn2(3)) add_to_buried(otmp); + else place_object(otmp, x, y); + } + } + if (rn2(1000) < gemprob) { + for (cnt = rnd(2 + dunlev(&u.uz) / 3); cnt > 0; cnt--) + if ((otmp = mkobj(GEM_CLASS, FALSE)) != 0) { + if (otmp->otyp == ROCK) { + dealloc_obj(otmp); /* discard it */ + } else { + otmp->ox = x, otmp->oy = y; + if (!rn2(3)) add_to_buried(otmp); + else place_object(otmp, x, y); + } + } + } + } +} + +void +mklev() +{ + struct mkroom *croom; + + if(getbones()) return; + in_mklev = TRUE; + makelevel(); + bound_digging(); + mineralize(); + in_mklev = FALSE; + /* has_morgue gets cleared once morgue is entered; graveyard stays + set (graveyard might already be set even when has_morgue is clear + [see fixup_special()], so don't update it unconditionally) */ + if (level.flags.has_morgue) + level.flags.graveyard = 1; + if (!level.flags.is_maze_lev) { + for (croom = &rooms[0]; croom != &rooms[nroom]; croom++) +#ifdef SPECIALIZATION + topologize(croom, FALSE); +#else + topologize(croom); +#endif + } + set_wall_state(); +} + +void +#ifdef SPECIALIZATION +topologize(croom, do_ordinary) +register struct mkroom *croom; +boolean do_ordinary; +#else +topologize(croom) +register struct mkroom *croom; +#endif +{ + register int x, y, roomno = (croom - rooms) + ROOMOFFSET; + register int lowx = croom->lx, lowy = croom->ly; + register int hix = croom->hx, hiy = croom->hy; +#ifdef SPECIALIZATION + register schar rtype = croom->rtype; +#endif + register int subindex, nsubrooms = croom->nsubrooms; + + /* skip the room if already done; i.e. a shop handled out of order */ + /* also skip if this is non-rectangular (it _must_ be done already) */ + if ((int) levl[lowx][lowy].roomno == roomno || croom->irregular) + return; +#ifdef SPECIALIZATION +# ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) + do_ordinary = TRUE; /* vision routine helper */ +# endif + if ((rtype != OROOM) || do_ordinary) +#endif + { + /* do innards first */ + for(x = lowx; x <= hix; x++) + for(y = lowy; y <= hiy; y++) +#ifdef SPECIALIZATION + if (rtype == OROOM) + levl[x][y].roomno = NO_ROOM; + else +#endif + levl[x][y].roomno = roomno; + /* top and bottom edges */ + for(x = lowx-1; x <= hix+1; x++) + for(y = lowy-1; y <= hiy+1; y += (hiy-lowy+2)) { + levl[x][y].edge = 1; + if (levl[x][y].roomno) + levl[x][y].roomno = SHARED; + else + levl[x][y].roomno = roomno; + } + /* sides */ + for(x = lowx-1; x <= hix+1; x += (hix-lowx+2)) + for(y = lowy; y <= hiy; y++) { + levl[x][y].edge = 1; + if (levl[x][y].roomno) + levl[x][y].roomno = SHARED; + else + levl[x][y].roomno = roomno; + } + } + /* subrooms */ + for (subindex = 0; subindex < nsubrooms; subindex++) +#ifdef SPECIALIZATION + topologize(croom->sbrooms[subindex], (rtype != OROOM)); +#else + topologize(croom->sbrooms[subindex]); +#endif +} + +/* Find an unused room for a branch location. */ +STATIC_OVL struct mkroom * +find_branch_room(mp) + coord *mp; +{ + struct mkroom *croom = 0; + + if (nroom == 0) { + mazexy(mp); /* already verifies location */ + } else { + /* not perfect - there may be only one stairway */ + if(nroom > 2) { + int tryct = 0; + + do + croom = &rooms[rn2(nroom)]; + while((croom == dnstairs_room || croom == upstairs_room || + croom->rtype != OROOM) && (++tryct < 100)); + } else + croom = &rooms[rn2(nroom)]; + + do { + if (!somexy(croom, mp)) + impossible("Can't place branch!"); + } while(occupied(mp->x, mp->y) || + (levl[mp->x][mp->y].typ != CORR && levl[mp->x][mp->y].typ != ROOM)); + } + return croom; +} + +/* Find the room for (x,y). Return null if not in a room. */ +STATIC_OVL struct mkroom * +pos_to_room(x, y) + xchar x, y; +{ + int i; + struct mkroom *curr; + + for (curr = rooms, i = 0; i < nroom; curr++, i++) + if (inside_room(curr, x, y)) return curr;; + return (struct mkroom *) 0; +} + + +/* If given a branch, randomly place a special stair or portal. */ +void +place_branch(br, x, y) +branch *br; /* branch to place */ +xchar x, y; /* location */ +{ + coord m; + d_level *dest; + boolean make_stairs; + struct mkroom *br_room; + + /* + * Return immediately if there is no branch to make or we have + * already made one. This routine can be called twice when + * a special level is loaded that specifies an SSTAIR location + * as a favored spot for a branch. + */ + if (!br || made_branch) return; + + if (!x) { /* find random coordinates for branch */ + br_room = find_branch_room(&m); + x = m.x; + y = m.y; + } else { + br_room = pos_to_room(x, y); + } + + if (on_level(&br->end1, &u.uz)) { + /* we're on end1 */ + make_stairs = br->type != BR_NO_END1; + dest = &br->end2; + } else { + /* we're on end2 */ + make_stairs = br->type != BR_NO_END2; + dest = &br->end1; + } + + if (br->type == BR_PORTAL) { + mkportal(x, y, dest->dnum, dest->dlevel); + } else if (make_stairs) { + sstairs.sx = x; + sstairs.sy = y; + sstairs.up = (char) on_level(&br->end1, &u.uz) ? + br->end1_up : !br->end1_up; + assign_level(&sstairs.tolev, dest); + sstairs_room = br_room; + + levl[x][y].ladder = sstairs.up ? LA_UP : LA_DOWN; + levl[x][y].typ = STAIRS; + } + /* + * Set made_branch to TRUE even if we didn't make a stairwell (i.e. + * make_stairs is false) since there is currently only one branch + * per level, if we failed once, we're going to fail again on the + * next call. + */ + made_branch = TRUE; +} + +STATIC_OVL boolean +bydoor(x, y) +register xchar x, y; +{ + register int typ; + + if (isok(x+1, y)) { + typ = levl[x+1][y].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + if (isok(x-1, y)) { + typ = levl[x-1][y].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + if (isok(x, y+1)) { + typ = levl[x][y+1].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + if (isok(x, y-1)) { + typ = levl[x][y-1].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + return FALSE; +} + +/* see whether it is allowable to create a door at [x,y] */ +int +okdoor(x,y) +register xchar x, y; +{ + register boolean near_door = bydoor(x, y); + + return((levl[x][y].typ == HWALL || levl[x][y].typ == VWALL) && + doorindex < DOORMAX && !near_door); +} + +void +dodoor(x,y,aroom) +register int x, y; +register struct mkroom *aroom; +{ + if(doorindex >= DOORMAX) { + impossible("DOORMAX exceeded?"); + return; + } + + dosdoor(x,y,aroom,rn2(8) ? DOOR : SDOOR); +} + +boolean +occupied(x, y) +register xchar x, y; +{ + return((boolean)(t_at(x, y) + || IS_FURNITURE(levl[x][y].typ) + || is_lava(x,y) + || is_pool(x,y) + || invocation_pos(x,y) + )); +} + +/* make a trap somewhere (in croom if mazeflag = 0 && !tm) */ +/* if tm != null, make trap at that location */ +void +mktrap(num, mazeflag, croom, tm) +register int num, mazeflag; +register struct mkroom *croom; +coord *tm; +{ + register int kind; + coord m; + + /* no traps in pools */ + if (tm && is_pool(tm->x,tm->y)) return; + + if (num > 0 && num < TRAPNUM) { + kind = num; +#ifdef REINCARNATION + } else if (Is_rogue_level(&u.uz)) { + switch (rn2(7)) { + default: kind = BEAR_TRAP; break; /* 0 */ + case 1: kind = ARROW_TRAP; break; + case 2: kind = DART_TRAP; break; + case 3: kind = TRAPDOOR; break; + case 4: kind = PIT; break; + case 5: kind = SLP_GAS_TRAP; break; + case 6: kind = RUST_TRAP; break; + } +#endif + } else if (Inhell && !rn2(5)) { + /* bias the frequency of fire traps in Gehennom */ + kind = FIRE_TRAP; + } else { + unsigned lvl = level_difficulty(); + + do { + kind = rnd(TRAPNUM-1); + /* reject "too hard" traps */ + switch (kind) { + case MAGIC_PORTAL: + kind = NO_TRAP; break; + case ROLLING_BOULDER_TRAP: + case SLP_GAS_TRAP: + if (lvl < 2) kind = NO_TRAP; break; + case LEVEL_TELEP: + if (lvl < 5 || level.flags.noteleport) + kind = NO_TRAP; break; + case SPIKED_PIT: + if (lvl < 5) kind = NO_TRAP; break; + case LANDMINE: + if (lvl < 6) kind = NO_TRAP; break; + case WEB: + if (lvl < 7) kind = NO_TRAP; break; + case STATUE_TRAP: + case POLY_TRAP: + if (lvl < 8) kind = NO_TRAP; break; + case FIRE_TRAP: + if (!Inhell) kind = NO_TRAP; break; + case TELEP_TRAP: + if (level.flags.noteleport) kind = NO_TRAP; break; + case HOLE: + /* make these much less often than other traps */ + if (rn2(7)) kind = NO_TRAP; break; + } + } while (kind == NO_TRAP); + } + + if ((kind == TRAPDOOR || kind == HOLE) && !Can_fall_thru(&u.uz)) + kind = ROCKTRAP; + + if (tm) + m = *tm; + else { + register int tryct = 0; + boolean avoid_boulder = (kind == PIT || kind == SPIKED_PIT || + kind == TRAPDOOR || kind == HOLE); + + do { + if (++tryct > 200) + return; + if (mazeflag) + mazexy(&m); + else if (!somexy(croom,&m)) + return; + } while (occupied(m.x, m.y) || + (avoid_boulder && sobj_at(BOULDER, m.x, m.y))); + } + + (void) maketrap(m.x, m.y, kind); + if (kind == WEB) (void) makemon(&mons[PM_GIANT_SPIDER], + m.x, m.y, NO_MM_FLAGS); +} + +void +mkstairs(x, y, up, croom) +xchar x, y; +char up; +struct mkroom *croom; +{ + if (!x) { + impossible("mkstairs: bogus stair attempt at <%d,%d>", x, y); + return; + } + + /* + * We can't make a regular stair off an end of the dungeon. This + * attempt can happen when a special level is placed at an end and + * has an up or down stair specified in its description file. + */ + if ((dunlev(&u.uz) == 1 && up) || + (dunlev(&u.uz) == dunlevs_in_dungeon(&u.uz) && !up)) + return; + + if(up) { + xupstair = x; + yupstair = y; + upstairs_room = croom; + } else { + xdnstair = x; + ydnstair = y; + dnstairs_room = croom; + } + + levl[x][y].typ = STAIRS; + levl[x][y].ladder = up ? LA_UP : LA_DOWN; +} + +STATIC_OVL +void +mkfount(mazeflag,croom) +register int mazeflag; +register struct mkroom *croom; +{ + coord m; + register int tryct = 0; + + do { + if(++tryct > 200) return; + if(mazeflag) + mazexy(&m); + else + if (!somexy(croom, &m)) + return; + } while(occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put a fountain at m.x, m.y */ + levl[m.x][m.y].typ = FOUNTAIN; + /* Is it a "blessed" fountain? (affects drinking from fountain) */ + if(!rn2(7)) levl[m.x][m.y].blessedftn = 1; + + level.flags.nfountains++; +} + +#ifdef SINKS +STATIC_OVL void +mksink(croom) +register struct mkroom *croom; +{ + coord m; + register int tryct = 0; + + do { + if(++tryct > 200) return; + if (!somexy(croom, &m)) + return; + } while(occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put a sink at m.x, m.y */ + levl[m.x][m.y].typ = SINK; + + level.flags.nsinks++; +} +#endif /* SINKS */ + + +STATIC_OVL void +mkaltar(croom) +register struct mkroom *croom; +{ + coord m; + register int tryct = 0; + aligntyp al; + + if (croom->rtype != OROOM) return; + + do { + if(++tryct > 200) return; + if (!somexy(croom, &m)) + return; + } while (occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put an altar at m.x, m.y */ + levl[m.x][m.y].typ = ALTAR; + + /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */ + al = rn2((int)A_LAWFUL+2) - 1; + levl[m.x][m.y].altarmask = Align2amask( al ); +} + +static void +mkgrave(croom) +struct mkroom *croom; +{ + coord m; + register int tryct = 0; + register struct obj *otmp; + boolean dobell = !rn2(10); + + + if(croom->rtype != OROOM) return; + + do { + if(++tryct > 200) return; + if (!somexy(croom, &m)) + return; + } while (occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put a grave at m.x, m.y */ + make_grave(m.x, m.y, dobell ? "Saved by the bell!" : (char *) 0); + + /* Possibly fill it with objects */ + if (!rn2(3)) (void) mkgold(0L, m.x, m.y); + for (tryct = rn2(5); tryct; tryct--) { + otmp = mkobj(RANDOM_CLASS, TRUE); + if (!otmp) return; + curse(otmp); + otmp->ox = m.x; + otmp->oy = m.y; + add_to_buried(otmp); + } + + /* Leave a bell, in case we accidentally buried someone alive */ + if (dobell) (void) mksobj_at(BELL, m.x, m.y, TRUE, FALSE); + return; +} + + +/* maze levels have slightly different constraints from normal levels */ +#define x_maze_min 2 +#define y_maze_min 2 +/* + * Major level transmutation: add a set of stairs (to the Sanctum) after + * an earthquake that leaves behind a a new topology, centered at inv_pos. + * Assumes there are no rooms within the invocation area and that inv_pos + * is not too close to the edge of the map. Also assume the hero can see, + * which is guaranteed for normal play due to the fact that sight is needed + * to read the Book of the Dead. + */ +void +mkinvokearea() +{ + int dist; + xchar xmin = inv_pos.x, xmax = inv_pos.x; + xchar ymin = inv_pos.y, ymax = inv_pos.y; + register xchar i; + + pline_The("floor shakes violently under you!"); + pline_The("walls around you begin to bend and crumble!"); + display_nhwindow(WIN_MESSAGE, TRUE); + + mkinvpos(xmin, ymin, 0); /* middle, before placing stairs */ + + for(dist = 1; dist < 7; dist++) { + xmin--; xmax++; + + /* top and bottom */ + if(dist != 3) { /* the area is wider that it is high */ + ymin--; ymax++; + for(i = xmin+1; i < xmax; i++) { + mkinvpos(i, ymin, dist); + mkinvpos(i, ymax, dist); + } + } + + /* left and right */ + for(i = ymin; i <= ymax; i++) { + mkinvpos(xmin, i, dist); + mkinvpos(xmax, i, dist); + } + + flush_screen(1); /* make sure the new glyphs shows up */ + delay_output(); + } + + You("are standing at the top of a stairwell leading down!"); + mkstairs(u.ux, u.uy, 0, (struct mkroom *)0); /* down */ + newsym(u.ux, u.uy); + vision_full_recalc = 1; /* everything changed */ +} + +/* Change level topology. Boulders in the vicinity are eliminated. + * Temporarily overrides vision in the name of a nice effect. + */ +STATIC_OVL void +mkinvpos(x,y,dist) +xchar x,y; +int dist; +{ + struct trap *ttmp; + struct obj *otmp; + boolean make_rocks; + register struct rm *lev = &levl[x][y]; + + /* clip at existing map borders if necessary */ + if (!within_bounded_area(x, y, x_maze_min + 1, y_maze_min + 1, + x_maze_max - 1, y_maze_max - 1)) { + /* only outermost 2 columns and/or rows may be truncated due to edge */ + if (dist < (7 - 2)) + panic("mkinvpos: <%d,%d> (%d) off map edge!", x, y, dist); + return; + } + + /* clear traps */ + if ((ttmp = t_at(x,y)) != 0) deltrap(ttmp); + + /* clear boulders; leave some rocks for non-{moat|trap} locations */ + make_rocks = (dist != 1 && dist != 4 && dist != 5) ? TRUE : FALSE; + while ((otmp = sobj_at(BOULDER, x, y)) != 0) { + if (make_rocks) { + fracture_rock(otmp); + make_rocks = FALSE; /* don't bother with more rocks */ + } else { + obj_extract_self(otmp); + obfree(otmp, (struct obj *)0); + } + } + unblock_point(x,y); /* make sure vision knows this location is open */ + + /* fake out saved state */ + lev->seenv = 0; + lev->doormask = 0; + if(dist < 6) lev->lit = TRUE; + lev->waslit = TRUE; + lev->horizontal = FALSE; + viz_array[y][x] = (dist < 6 ) ? + (IN_SIGHT|COULD_SEE) : /* short-circuit vision recalc */ + COULD_SEE; + + switch(dist) { + case 1: /* fire traps */ + if (is_pool(x,y)) break; + lev->typ = ROOM; + ttmp = maketrap(x, y, FIRE_TRAP); + if (ttmp) ttmp->tseen = TRUE; + break; + case 0: /* lit room locations */ + case 2: + case 3: + case 6: /* unlit room locations */ + lev->typ = ROOM; + break; + case 4: /* pools (aka a wide moat) */ + case 5: + lev->typ = MOAT; + /* No kelp! */ + break; + default: + impossible("mkinvpos called with dist %d", dist); + break; + } + + /* display new value of position; could have a monster/object on it */ + newsym(x,y); +} + +/* + * The portal to Ludios is special. The entrance can only occur within a + * vault in the main dungeon at a depth greater than 10. The Ludios branch + * structure reflects this by having a bogus "source" dungeon: the value + * of n_dgns (thus, Is_branchlev() will never find it). + * + * Ludios will remain isolated until the branch is corrected by this function. + */ +STATIC_OVL void +mk_knox_portal(x, y) +xchar x, y; +{ + extern int n_dgns; /* from dungeon.c */ + d_level *source; + branch *br; + schar u_depth; + + br = dungeon_branch("Fort Ludios"); + if (on_level(&knox_level, &br->end1)) { + source = &br->end2; + } else { + /* disallow Knox branch on a level with one branch already */ + if(Is_branchlev(&u.uz)) + return; + source = &br->end1; + } + + /* Already set or 2/3 chance of deferring until a later level. */ + if (source->dnum < n_dgns || (rn2(3) +#ifdef WIZARD + && !wizard +#endif + )) return; + + if (! (u.uz.dnum == oracle_level.dnum /* in main dungeon */ + && !at_dgn_entrance("The Quest") /* but not Quest's entry */ + && (u_depth = depth(&u.uz)) > 10 /* beneath 10 */ + && u_depth < depth(&medusa_level))) /* and above Medusa */ + return; + + /* Adjust source to be current level and re-insert branch. */ + *source = u.uz; + insert_branch(br, TRUE); + +#ifdef DEBUG + pline("Made knox portal."); +#endif + place_branch(br, x, y); +} + +/*mklev.c*/