diff --git a/src/mkroom.c b/src/mkroom.c new file mode 100644 index 000000000..180db8ea9 --- /dev/null +++ b/src/mkroom.c @@ -0,0 +1,779 @@ +/* SCCS Id: @(#)mkroom.c 3.3 2001/09/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Entry points: + * mkroom() -- make and stock a room of a given type + * nexttodoor() -- return TRUE if adjacent to a door + * has_dnstairs() -- return TRUE if given room has a down staircase + * has_upstairs() -- return TRUE if given room has an up staircase + * courtmon() -- generate a court monster + * save_rooms() -- save rooms into file fd + * rest_rooms() -- restore rooms from file fd + */ + +#include "hack.h" + +#ifdef OVLB +STATIC_DCL boolean FDECL(isbig, (struct mkroom *)); +STATIC_DCL struct mkroom * FDECL(pick_room,(BOOLEAN_P)); +STATIC_DCL void NDECL(mkshop), FDECL(mkzoo,(int)), NDECL(mkswamp); +STATIC_DCL void NDECL(mktemple); +STATIC_DCL coord * FDECL(shrine_pos, (int)); +STATIC_DCL struct permonst * NDECL(morguemon); +STATIC_DCL struct permonst * NDECL(antholemon); +STATIC_DCL struct permonst * NDECL(squadmon); +STATIC_DCL void FDECL(save_room, (int,struct mkroom *)); +STATIC_DCL void FDECL(rest_room, (int,struct mkroom *)); +#endif /* OVLB */ + +#define sq(x) ((x)*(x)) + +extern const struct shclass shtypes[]; /* defined in shknam.c */ + +#ifdef OVLB + +STATIC_OVL boolean +isbig(sroom) +register struct mkroom *sroom; +{ + register int area = (sroom->hx - sroom->lx + 1) + * (sroom->hy - sroom->ly + 1); + return((boolean)( area > 20 )); +} + +void +mkroom(roomtype) +/* make and stock a room of a given type */ +int roomtype; +{ + if (roomtype >= SHOPBASE) + mkshop(); /* someday, we should be able to specify shop type */ + else switch(roomtype) { + case COURT: mkzoo(COURT); break; + case ZOO: mkzoo(ZOO); break; + case BEEHIVE: mkzoo(BEEHIVE); break; + case MORGUE: mkzoo(MORGUE); break; + case BARRACKS: mkzoo(BARRACKS); break; + case SWAMP: mkswamp(); break; + case TEMPLE: mktemple(); break; + case LEPREHALL: mkzoo(LEPREHALL); break; + case COCKNEST: mkzoo(COCKNEST); break; + case ANTHOLE: mkzoo(ANTHOLE); break; + default: impossible("Tried to make a room of type %d.", roomtype); + } +} + +STATIC_OVL void +mkshop() +{ + register struct mkroom *sroom; + int i = -1; +#ifdef WIZARD + char *ep = (char *)0; /* (init == lint suppression) */ + + /* first determine shoptype */ + if(wizard){ +#ifndef MAC + ep = nh_getenv("SHOPTYPE"); + if(ep){ + if(*ep == 'z' || *ep == 'Z'){ + mkzoo(ZOO); + return; + } + if(*ep == 'm' || *ep == 'M'){ + mkzoo(MORGUE); + return; + } + if(*ep == 'b' || *ep == 'B'){ + mkzoo(BEEHIVE); + return; + } + if(*ep == 't' || *ep == 'T' || *ep == '\\'){ + mkzoo(COURT); + return; + } + if(*ep == 's' || *ep == 'S'){ + mkzoo(BARRACKS); + return; + } + if(*ep == 'a' || *ep == 'A'){ + mkzoo(ANTHOLE); + return; + } + if(*ep == 'c' || *ep == 'C'){ + mkzoo(COCKNEST); + return; + } + if(*ep == 'l' || *ep == 'L'){ + mkzoo(LEPREHALL); + return; + } + if(*ep == '_'){ + mktemple(); + return; + } + if(*ep == '}'){ + mkswamp(); + return; + } + for(i=0; shtypes[i].name; i++) + if(*ep == def_oc_syms[(int)shtypes[i].symb]) + goto gottype; + if(*ep == 'g' || *ep == 'G') + i = 0; + else + i = -1; + } +#endif + } +gottype: +#endif + for(sroom = &rooms[0]; ; sroom++){ + if(sroom->hx < 0) return; + if(sroom - rooms >= nroom) { + pline("rooms not closed by -1?"); + return; + } + if(sroom->rtype != OROOM) continue; + if(has_dnstairs(sroom) || has_upstairs(sroom)) + continue; + if( +#ifdef WIZARD + (wizard && ep && sroom->doorct != 0) || +#endif + sroom->doorct == 1) break; + } + if (!sroom->rlit) { + int x, y; + + for(x = sroom->lx - 1; x <= sroom->hx + 1; x++) + for(y = sroom->ly - 1; y <= sroom->hy + 1; y++) + levl[x][y].lit = 1; + sroom->rlit = 1; + } + + if(i < 0) { /* shoptype not yet determined */ + register int j; + + /* pick a shop type at random */ + for (j = rnd(100), i = 0; (j -= shtypes[i].prob) > 0; i++) + continue; + + /* big rooms cannot be wand or book shops, + * - so make them general stores + */ + if(isbig(sroom) && (shtypes[i].symb == WAND_CLASS + || shtypes[i].symb == SPBOOK_CLASS)) i = 0; + } + sroom->rtype = SHOPBASE + i; + + /* set room bits before stocking the shop */ +#ifdef SPECIALIZATION + topologize(sroom, FALSE); /* doesn't matter - this is a special room */ +#else + topologize(sroom); +#endif + + /* stock the room with a shopkeeper and artifacts */ + stock_room(i, sroom); +} + +STATIC_OVL struct mkroom * +pick_room(strict) +register boolean strict; +/* pick an unused room, preferably with only one door */ +{ + register struct mkroom *sroom; + register int i = nroom; + + for(sroom = &rooms[rn2(nroom)]; i--; sroom++) { + if(sroom == &rooms[nroom]) + sroom = &rooms[0]; + if(sroom->hx < 0) + return (struct mkroom *)0; + if(sroom->rtype != OROOM) continue; + if(!strict) { + if(has_upstairs(sroom) || (has_dnstairs(sroom) && rn2(3))) + continue; + } else if(has_upstairs(sroom) || has_dnstairs(sroom)) + continue; + if(sroom->doorct == 1 || !rn2(5) +#ifdef WIZARD + || wizard +#endif + ) + return sroom; + } + return (struct mkroom *)0; +} + +STATIC_OVL void +mkzoo(type) +int type; +{ + register struct mkroom *sroom; + + if ((sroom = pick_room(FALSE)) != 0) { + sroom->rtype = type; + fill_zoo(sroom); + } +} + +void +fill_zoo(sroom) +struct mkroom *sroom; +{ + struct monst *mon; + register int sx,sy,i; + int sh, tx, ty, goldlim, type = sroom->rtype; + int rmno = (sroom - rooms) + ROOMOFFSET; + coord mm; + +#ifdef GCC_WARN + tx = ty = goldlim = 0; +#endif + + sh = sroom->fdoor; + switch(type) { + case COURT: + if(level.flags.is_maze_lev) { + for(tx = sroom->lx; tx <= sroom->hx; tx++) + for(ty = sroom->ly; ty <= sroom->hy; ty++) + if(IS_THRONE(levl[tx][ty].typ)) + goto throne_placed; + } + i = 100; + do { /* don't place throne on top of stairs */ + (void) somexy(sroom, &mm); + tx = mm.x; ty = mm.y; + } while (occupied((xchar)tx, (xchar)ty) && --i > 0); + throne_placed: + /* TODO: try to ensure the enthroned monster is an M2_PRINCE */ + break; + case BEEHIVE: + tx = sroom->lx + (sroom->hx - sroom->lx + 1)/2; + ty = sroom->ly + (sroom->hy - sroom->ly + 1)/2; + if(sroom->irregular) { + /* center might not be valid, so put queen elsewhere */ + if ((int) levl[tx][ty].roomno != rmno || + levl[tx][ty].edge) { + (void) somexy(sroom, &mm); + tx = mm.x; ty = mm.y; + } + } + break; + case ZOO: + case LEPREHALL: + goldlim = 500 * level_difficulty(); + break; + } + for(sx = sroom->lx; sx <= sroom->hx; sx++) + for(sy = sroom->ly; sy <= sroom->hy; sy++) { + if(sroom->irregular) { + if ((int) levl[sx][sy].roomno != rmno || + levl[sx][sy].edge || + (sroom->doorct && + distmin(sx, sy, doors[sh].x, doors[sh].y) <= 1)) + continue; + } else if(!SPACE_POS(levl[sx][sy].typ) || + (sroom->doorct && + ((sx == sroom->lx && doors[sh].x == sx-1) || + (sx == sroom->hx && doors[sh].x == sx+1) || + (sy == sroom->ly && doors[sh].y == sy-1) || + (sy == sroom->hy && doors[sh].y == sy+1)))) + continue; + /* don't place monster on explicitly placed throne */ + if(type == COURT && IS_THRONE(levl[sx][sy].typ)) + continue; + mon = makemon( + (type == COURT) ? courtmon() : + (type == BARRACKS) ? squadmon() : + (type == MORGUE) ? morguemon() : + (type == BEEHIVE) ? + (sx == tx && sy == ty ? &mons[PM_QUEEN_BEE] : + &mons[PM_KILLER_BEE]) : + (type == LEPREHALL) ? &mons[PM_LEPRECHAUN] : + (type == COCKNEST) ? &mons[PM_COCKATRICE] : + (type == ANTHOLE) ? antholemon() : + (struct permonst *) 0, + sx, sy, NO_MM_FLAGS); + if(mon) { + mon->msleeping = 1; + if (type==COURT && mon->mpeaceful) { + mon->mpeaceful = 0; + set_malign(mon); + } + } + switch(type) { + case ZOO: + case LEPREHALL: + if(sroom->doorct) + { + int distval = dist2(sx,sy,doors[sh].x,doors[sh].y); + i = sq(distval); + } + else + i = goldlim; + if(i >= goldlim) i = 5*level_difficulty(); + goldlim -= i; + (void) mkgold((long) rn1(i, 10), sx, sy); + break; + case MORGUE: + if(!rn2(5)) + (void) mk_tt_object(CORPSE, sx, sy); + if(!rn2(10)) /* lots of treasure buried with dead */ + (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST, + sx, sy, TRUE, FALSE); + if (!rn2(5)) + make_grave(sx, sy, (char *)0); + break; + case BEEHIVE: + if(!rn2(3)) + (void) mksobj_at(LUMP_OF_ROYAL_JELLY, + sx, sy, TRUE, FALSE); + break; + case BARRACKS: + if(!rn2(20)) /* the payroll and some loot */ + (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST, + sx, sy, TRUE, FALSE); + break; + case COCKNEST: + if(!rn2(3)) { + struct obj *sobj = mk_tt_object(STATUE, sx, sy); + + if (sobj) { + for (i = rn2(5); i; i--) + (void) add_to_container(sobj, + mkobj(RANDOM_CLASS, FALSE)); + sobj->owt = weight(sobj); + } + } + break; + case ANTHOLE: + if(!rn2(3)) + (void) mkobj_at(FOOD_CLASS, sx, sy, FALSE); + break; + } + } + switch (type) { + case COURT: + { + struct obj *chest; + levl[tx][ty].typ = THRONE; + (void) somexy(sroom, &mm); + (void) mkgold((long) rn1(50 * level_difficulty(),10), mm.x, mm.y); + /* the royal coffers */ + chest = mksobj_at(CHEST, mm.x, mm.y, TRUE, FALSE); + chest->spe = 2; /* so it can be found later */ + level.flags.has_court = 1; + break; + } + case BARRACKS: + level.flags.has_barracks = 1; + break; + case ZOO: + level.flags.has_zoo = 1; + break; + case MORGUE: + level.flags.has_morgue = 1; + break; + case SWAMP: + level.flags.has_swamp = 1; + break; + case BEEHIVE: + level.flags.has_beehive = 1; + break; + } +} + +/* make a swarm of undead around mm */ +void +mkundead(mm, revive_corpses, mm_flags) +coord *mm; +boolean revive_corpses; +int mm_flags; +{ + int cnt = (level_difficulty() + 1)/10 + rnd(5); + struct permonst *mdat; + struct obj *otmp; + coord cc; + + while (cnt--) { + mdat = morguemon(); + if (enexto(&cc, mm->x, mm->y, mdat) && + (!revive_corpses || + !(otmp = sobj_at(CORPSE, cc.x, cc.y)) || + !revive(otmp))) + (void) makemon(mdat, cc.x, cc.y, mm_flags); + } + level.flags.graveyard = TRUE; /* reduced chance for undead corpse */ +} + +STATIC_OVL struct permonst * +morguemon() +{ + register int i = rn2(100), hd = rn2(level_difficulty()); + + if(hd > 10 && i < 10) + return((Inhell || In_endgame(&u.uz)) ? mkclass(S_DEMON,0) : + &mons[ndemon(A_NONE)]); + if(hd > 8 && i > 85) + return(mkclass(S_VAMPIRE,0)); + + return((i < 20) ? &mons[PM_GHOST] + : (i < 40) ? &mons[PM_WRAITH] : mkclass(S_ZOMBIE,0)); +} + +STATIC_OVL struct permonst * +antholemon() +{ + int mtyp; + + /* Same monsters within a level, different ones between levels */ + switch ((level_difficulty() + ((long)u.ubirthday)) % 3) { + default: mtyp = PM_GIANT_ANT; break; + case 0: mtyp = PM_SOLDIER_ANT; break; + case 1: mtyp = PM_FIRE_ANT; break; + } + return ((mvitals[mtyp].mvflags & G_GONE) ? + (struct permonst *)0 : &mons[mtyp]); +} + +STATIC_OVL void +mkswamp() /* Michiel Huisjes & Fred de Wilde */ +{ + register struct mkroom *sroom; + register int sx,sy,i,eelct = 0; + + for(i=0; i<5; i++) { /* turn up to 5 rooms swampy */ + sroom = &rooms[rn2(nroom)]; + if(sroom->hx < 0 || sroom->rtype != OROOM || + has_upstairs(sroom) || has_dnstairs(sroom)) + continue; + + /* satisfied; make a swamp */ + sroom->rtype = SWAMP; + for(sx = sroom->lx; sx <= sroom->hx; sx++) + for(sy = sroom->ly; sy <= sroom->hy; sy++) + if(!OBJ_AT(sx, sy) && + !MON_AT(sx, sy) && !t_at(sx,sy) && !nexttodoor(sx,sy)) { + if((sx+sy)%2) { + levl[sx][sy].typ = POOL; + if(!eelct || !rn2(4)) { + /* mkclass() won't do, as we might get kraken */ + (void) makemon(rn2(5) ? &mons[PM_GIANT_EEL] + : rn2(2) ? &mons[PM_PIRANHA] + : &mons[PM_ELECTRIC_EEL], + sx, sy, NO_MM_FLAGS); + eelct++; + } + } else + if(!rn2(4)) /* swamps tend to be moldy */ + (void) makemon(mkclass(S_FUNGUS,0), + sx, sy, NO_MM_FLAGS); + } + level.flags.has_swamp = 1; + } +} + +STATIC_OVL coord * +shrine_pos(roomno) +int roomno; +{ + static coord buf; + struct mkroom *troom = &rooms[roomno - ROOMOFFSET]; + + buf.x = troom->lx + ((troom->hx - troom->lx) / 2); + buf.y = troom->ly + ((troom->hy - troom->ly) / 2); + return(&buf); +} + +STATIC_OVL void +mktemple() +{ + register struct mkroom *sroom; + coord *shrine_spot; + register struct rm *lev; + + if(!(sroom = pick_room(TRUE))) return; + + /* set up Priest and shrine */ + sroom->rtype = TEMPLE; + /* + * In temples, shrines are blessed altars + * located in the center of the room + */ + shrine_spot = shrine_pos((sroom - rooms) + ROOMOFFSET); + lev = &levl[shrine_spot->x][shrine_spot->y]; + lev->typ = ALTAR; + lev->altarmask = induced_align(80); + priestini(&u.uz, sroom, shrine_spot->x, shrine_spot->y, FALSE); + lev->altarmask |= AM_SHRINE; + level.flags.has_temple = 1; +} + +boolean +nexttodoor(sx,sy) +register int sx, sy; +{ + register int dx, dy; + register struct rm *lev; + for(dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++) { + if(!isok(sx+dx, sy+dy)) continue; + if(IS_DOOR((lev = &levl[sx+dx][sy+dy])->typ) || + lev->typ == SDOOR) + return(TRUE); + } + return(FALSE); +} + +boolean +has_dnstairs(sroom) +register struct mkroom *sroom; +{ + if (sroom == dnstairs_room) + return TRUE; + if (sstairs.sx && !sstairs.up) + return((boolean)(sroom == sstairs_room)); + return FALSE; +} + +boolean +has_upstairs(sroom) +register struct mkroom *sroom; +{ + if (sroom == upstairs_room) + return TRUE; + if (sstairs.sx && sstairs.up) + return((boolean)(sroom == sstairs_room)); + return FALSE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +int +somex(croom) +register struct mkroom *croom; +{ + return rn2(croom->hx-croom->lx+1) + croom->lx; +} + +int +somey(croom) +register struct mkroom *croom; +{ + return rn2(croom->hy-croom->ly+1) + croom->ly; +} + +boolean +inside_room(croom, x, y) +struct mkroom *croom; +xchar x, y; +{ + return((boolean)(x >= croom->lx-1 && x <= croom->hx+1 && + y >= croom->ly-1 && y <= croom->hy+1)); +} + +boolean +somexy(croom, c) +struct mkroom *croom; +coord *c; +{ + int try_cnt = 0; + int i; + + if (croom->irregular) { + i = (croom - rooms) + ROOMOFFSET; + + while(try_cnt++ < 100) { + c->x = somex(croom); + c->y = somey(croom); + if (!levl[c->x][c->y].edge && + (int) levl[c->x][c->y].roomno == i) + return TRUE; + } + /* try harder; exhaustively search until one is found */ + for(c->x = croom->lx; c->x <= croom->hx; c->x++) + for(c->y = croom->ly; c->y <= croom->hy; c->y++) + if (!levl[c->x][c->y].edge && + (int) levl[c->x][c->y].roomno == i) + return TRUE; + return FALSE; + } + + if (!croom->nsubrooms) { + c->x = somex(croom); + c->y = somey(croom); + return TRUE; + } + + /* Check that coords doesn't fall into a subroom or into a wall */ + + while(try_cnt++ < 100) { + c->x = somex(croom); + c->y = somey(croom); + if (IS_WALL(levl[c->x][c->y].typ)) + continue; + for(i=0 ; insubrooms;i++) + if(inside_room(croom->sbrooms[i], c->x, c->y)) + goto you_lose; + break; +you_lose: ; + } + if (try_cnt >= 100) + return FALSE; + return TRUE; +} + +/* + * Search for a special room given its type (zoo, court, etc...) + * Special values : + * - ANY_SHOP + * - ANY_TYPE + */ + +struct mkroom * +search_special(type) +schar type; +{ + register struct mkroom *croom; + + for(croom = &rooms[0]; croom->hx >= 0; croom++) + if((type == ANY_TYPE && croom->rtype != OROOM) || + (type == ANY_SHOP && croom->rtype >= SHOPBASE) || + croom->rtype == type) + return croom; + for(croom = &subrooms[0]; croom->hx >= 0; croom++) + if((type == ANY_TYPE && croom->rtype != OROOM) || + (type == ANY_SHOP && croom->rtype >= SHOPBASE) || + croom->rtype == type) + return croom; + return (struct mkroom *) 0; +} + +#endif /* OVL0 */ +#ifdef OVLB + +struct permonst * +courtmon() +{ + int i = rn2(60) + rn2(3*level_difficulty()); + if (i > 100) return(mkclass(S_DRAGON,0)); + else if (i > 95) return(mkclass(S_GIANT,0)); + else if (i > 85) return(mkclass(S_TROLL,0)); + else if (i > 75) return(mkclass(S_CENTAUR,0)); + else if (i > 60) return(mkclass(S_ORC,0)); + else if (i > 45) return(&mons[PM_BUGBEAR]); + else if (i > 30) return(&mons[PM_HOBGOBLIN]); + else if (i > 15) return(mkclass(S_GNOME,0)); + else return(mkclass(S_KOBOLD,0)); +} + +#define NSTYPES (PM_CAPTAIN - PM_SOLDIER + 1) + +static struct { + unsigned pm; + unsigned prob; +} squadprob[NSTYPES] = { + {PM_SOLDIER, 80}, {PM_SERGEANT, 15}, {PM_LIEUTENANT, 4}, {PM_CAPTAIN, 1} +}; + +STATIC_OVL struct permonst * +squadmon() /* return soldier types. */ +{ + int sel_prob, i, cpro, mndx; + + sel_prob = rnd(80+level_difficulty()); + + cpro = 0; + for (i = 0; i < NSTYPES; i++) { + cpro += squadprob[i].prob; + if (cpro > sel_prob) { + mndx = squadprob[i].pm; + goto gotone; + } + } + mndx = squadprob[rn2(NSTYPES)].pm; +gotone: + if (!(mvitals[mndx].mvflags & G_GONE)) return(&mons[mndx]); + else return((struct permonst *) 0); +} + +/* + * save_room : A recursive function that saves a room and its subrooms + * (if any). + */ + +STATIC_OVL void +save_room(fd, r) +int fd; +struct mkroom *r; +{ + short i; + /* + * Well, I really should write only useful information instead + * of writing the whole structure. That is I should not write + * the subrooms pointers, but who cares ? + */ + bwrite(fd, (genericptr_t) r, sizeof(struct mkroom)); + for(i=0; insubrooms; i++) + save_room(fd, r->sbrooms[i]); +} + +/* + * save_rooms : Save all the rooms on disk! + */ + +void +save_rooms(fd) +int fd; +{ + short i; + + /* First, write the number of rooms */ + bwrite(fd, (genericptr_t) &nroom, sizeof(nroom)); + for(i=0; insubrooms; i++) { + r->sbrooms[i] = &subrooms[nsubroom]; + rest_room(fd, &subrooms[nsubroom]); + subrooms[nsubroom++].resident = (struct monst *)0; + } +} + +/* + * rest_rooms : That's for restoring rooms. Read the rooms structure from + * the disk. + */ + +void +rest_rooms(fd) +int fd; +{ + short i; + + mread(fd, (genericptr_t) &nroom, sizeof(nroom)); + nsubroom = 0; + for(i = 0; i