/* make and stock a room of a given type Note: must not use the level creation RNG if this is of a room type that's affected by genocide/extinction (LEPREHALL, BEEHIVE, BARRACKS, ANTHOLE, COCKNEST), or genocide/extinction would change the layout of the rest of the level. */ void mkroom(struct level *lev, int roomtype) { if (roomtype >= SHOPBASE) mkshop(lev); /* someday, we should be able to specify shop type */ else switch (roomtype) { case COURT: mkzoo(lev, COURT, rng_for_level(&lev->z)); break; case ZOO: mkzoo(lev, ZOO, rng_for_level(&lev->z)); break; case BEEHIVE: mkzoo(lev, BEEHIVE, rng_main); break; case MORGUE: mkzoo(lev, MORGUE, rng_for_level(&lev->z)); break; case BARRACKS: mkzoo(lev, BARRACKS, rng_main); break; case SWAMP: mkswamp(lev); break; case TEMPLE: mktemple(lev); break; case LEPREHALL: mkzoo(lev, LEPREHALL, rng_main); break; case COCKNEST: mkzoo(lev, COCKNEST, rng_main); break; case ANTHOLE: mkzoo(lev, ANTHOLE, rng_main); break; default: impossible("Tried to make a room of type %d.", roomtype); } }
static const struct permonst * squadmon(const d_level *dlev) { /* return soldier types. */ int sel_prob, i, cpro, mndx; sel_prob = 1 + rn2_on_rng(80 + level_difficulty(dlev), rng_for_level(dlev)); 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_on_rng(NSTYPES, rng_for_level(dlev))].pm; gotone: if (!(mvitals[mndx].mvflags & G_GONE)) return &mons[mndx]; else return NULL; }
static void mktemple(struct level *lev) { struct mkroom *sroom; coord *shrine_spot; struct rm *loc; if (!(sroom = pick_room(lev, TRUE, rng_for_level(&lev->z)))) 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(lev, (sroom - lev->rooms) + ROOMOFFSET); loc = &lev->locations[shrine_spot->x][shrine_spot->y]; loc->typ = ALTAR; loc->altarmask = induced_align(&lev->z, 80, rng_for_level(&lev->z)); priestini(lev, sroom, shrine_spot->x, shrine_spot->y, FALSE); loc->altarmask |= AM_SHRINE; }
/* Make an object of the appropriate type for a shop square. Uses the level generation RNG. */ static void mkshobj_at(const struct shclass *shp, struct level *lev, int sx, int sy) { struct monst *mtmp; int atype; const struct permonst *ptr; enum rng rng = rng_for_level(&lev->z); if (rn2_on_rng(100, rng) < depth(&lev->z) && !MON_AT(lev, sx, sy) && (ptr = mkclass(&lev->z, S_MIMIC, 0, rng)) && (mtmp = makemon(ptr, lev, sx, sy, MM_ALLLEVRNG)) != 0) { /* note: makemon will set the mimic symbol to a shop item */ if (rn2_on_rng(10, rng) >= depth(&lev->z)) { mtmp->m_ap_type = M_AP_OBJECT; mtmp->mappearance = STRANGE_OBJECT; } } else { atype = get_shop_item(shp - shtypes, rng); if (atype < 0) mksobj_at(-atype, lev, sx, sy, TRUE, TRUE, rng); else mkobj_at(atype, lev, sx, sy, TRUE, rng); } }
static void mkswamp(struct level *lev) { struct mkroom *sroom; int sx, sy, i, eelct = 0; enum rng rng = rng_for_level(&lev->z); for (i = 0; i < 5; i++) { /* turn up to 5 rooms swampy */ sroom = &lev->rooms[rn2_on_rng(lev->nroom, rng)]; if (sroom->hx < 0 || sroom->rtype != OROOM || has_upstairs(lev, sroom) || has_dnstairs(lev, 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_LEV(lev, sx, sy) && !MON_AT(lev, sx, sy) && !t_at(lev, sx, sy) && !nexttodoor(lev, sx, sy)) { if ((sx + sy) % 2) { lev->locations[sx][sy].typ = POOL; if (!eelct || !rn2_on_rng(4, rng)) { /* mkclass() won't do, as we might get kraken */ makemon(rn2_on_rng(5, rng) ? &mons[PM_GIANT_EEL] : rn2_on_rng(2, rng) ? &mons[PM_PIRANHA] : &mons[PM_ELECTRIC_EEL], lev, sx, sy, MM_ALLLEVRNG); eelct++; } } else if (!rn2_on_rng(4, rng)) /* swamps tend to be moldy */ makemon(mkclass(&lev->z, S_FUNGUS, 0, rng), lev, sx, sy, MM_ALLLEVRNG); } } }
/* create a new shopkeeper in the given room; uses level creation RNG */ static int shkinit(const struct shclass *shp, struct level *lev, struct mkroom *sroom) { int sh, sx, sy; struct monst *shk; /* place the shopkeeper in the given room */ sh = sroom->fdoor; sx = lev->doors[sh].x; sy = lev->doors[sh].y; /* check that the shopkeeper placement is sane */ if (sroom->irregular) { int rmno = (sroom - lev->rooms) + ROOMOFFSET; if (isok(sx - 1, sy) && !lev->locations[sx - 1][sy].edge && (int)lev->locations[sx - 1][sy].roomno == rmno) sx--; else if (isok(sx + 1, sy) && !lev->locations[sx + 1][sy].edge && (int)lev->locations[sx + 1][sy].roomno == rmno) sx++; else if (isok(sx, sy - 1) && !lev->locations[sx][sy - 1].edge && (int)lev->locations[sx][sy - 1].roomno == rmno) sy--; else if (isok(sx, sy + 1) && !lev->locations[sx][sy + 1].edge && (int)lev->locations[sx][sy + 1].roomno == rmno) sx++; else goto shk_failed; } else if (sx == sroom->lx - 1) sx++; else if (sx == sroom->hx + 1) sx--; else if (sy == sroom->ly - 1) sy++; else if (sy == sroom->hy + 1) sy--; else { shk_failed: return -1; } if (MON_AT(lev, sx, sy)) rloc(m_at(lev, sx, sy), FALSE); /* insurance */ /* now initialize the shopkeeper monster structure */ if (!(shk = makemon(&mons[PM_SHOPKEEPER], lev, sx, sy, MM_ALLLEVRNG))) return -1; shk->isshk = 1; msethostility(shk, FALSE, TRUE); shk->msleeping = 0; shk->mtrapseen = ~0; /* we know all the traps already */ ESHK(shk)->shoproom = (sroom - lev->rooms) + ROOMOFFSET; sroom->resident = shk; ESHK(shk)->shoptype = sroom->rtype; assign_level(&(ESHK(shk)->shoplevel), &lev->z); ESHK(shk)->shd = lev->doors[sh]; ESHK(shk)->shk.x = sx; ESHK(shk)->shk.y = sy; ESHK(shk)->robbed = 0L; ESHK(shk)->credit = 0L; ESHK(shk)->debit = 0L; ESHK(shk)->loan = 0L; ESHK(shk)->visitct = 0; ESHK(shk)->following = 0; ESHK(shk)->billct = 0; ESHK(shk)->bill_inactive = FALSE; /* initial capital */ mkmonmoney(shk, 1030L + 30L * mklev_rn2(100, lev), rng_for_level(&lev->z)); if (shp->shknms == shkrings) mongets(shk, TOUCHSTONE, rng_for_level(&lev->z)); nameshk(shk, shp->shknms, lev); return sh; }
static void join_map(struct level *lev, schar bg_typ, schar fg_typ) { struct mkroom *croom, *croom2; int i, j; int sx, sy; coord sm, em; enum rng rng = rng_for_level(&lev->z); /* first, use flood filling to find all of the regions that need joining */ for (i = 1; i <= WIDTH; i++) for (j = 1; j < HEIGHT; j++) { if (lev->locations[i][j].typ == fg_typ && lev->locations[i][j].roomno == NO_ROOM) { min_rx = max_rx = i; min_ry = max_ry = j; n_loc_filled = 0; flood_fill_rm(lev, i, j, lev->nroom + ROOMOFFSET, FALSE, FALSE); if (n_loc_filled > 3) { add_room(lev, min_rx, min_ry, max_rx, max_ry, FALSE, OROOM, TRUE); lev->rooms[lev->nroom - 1].irregular = TRUE; if (lev->nroom >= (MAXNROFROOMS * 2)) goto joinm; } else { /* * it's a tiny hole; erase it from the map to avoid * having the player end up here with no way out. */ for (sx = min_rx; sx <= max_rx; sx++) for (sy = min_ry; sy <= max_ry; sy++) if ((int)lev->locations[sx][sy].roomno == lev->nroom + ROOMOFFSET) { lev->locations[sx][sy].typ = bg_typ; lev->locations[sx][sy].roomno = NO_ROOM; } } } } joinm: /* * Ok, now we can actually join the regions with fg_typ's. * The rooms are already sorted due to the previous loop, * so don't call sort_rooms(), which can screw up the roomno's * validity in the level->locations structure. */ for (croom = &lev->rooms[0], croom2 = croom + 1; croom2 < &lev->rooms[lev->nroom];) { /* pick random starting and end locations for "corridor" */ if (!somexy(lev, croom, &sm, rng) || !somexy(lev, croom2, &em, rng)) { /* ack! -- the level is going to be busted */ /* arbitrarily pick centers of both rooms and hope for the best */ impossible("No start/end room loc in join_map."); sm.x = croom->lx + ((croom->hx - croom->lx) / 2); sm.y = croom->ly + ((croom->hy - croom->ly) / 2); em.x = croom2->lx + ((croom2->hx - croom2->lx) / 2); em.y = croom2->ly + ((croom2->hy - croom2->ly) / 2); } dig_corridor(lev, &sm, &em, FALSE, fg_typ, bg_typ); /* choose next region to join */ /* only increment croom if croom and croom2 are non-overlapping */ if (croom2->lx > croom->hx || ((croom2->ly > croom->hy || croom2->hy < croom->ly) && mklev_rn2(3, lev))) { croom = croom2; } croom2++; /* always increment the next room */ } }
/* exclusively for mktemple(); uses level creation RNG */ void priestini(struct level *lev, struct mkroom *sroom, int sx, int sy, boolean sanctum) { /* is it the seat of the high priest? */ struct monst *priest = NULL; struct obj *otmp; int cnt; coord *priest_pos, pos_array[] = { { sx + 1, sy }, { sx - 1, sy }, { sx, sy + 1 }, { sx, sy - 1 }, { sx, sy }, { COLNO, ROWNO }, }; /* Search for a good position for the priest. The -1 in the array bound is * to ensure that we stop on the { COLNO, ROWNO } entry which is not ok. Do * not pass a monster to goodpos(), because we will move any monster later. */ for (priest_pos = pos_array; !goodpos(lev, priest_pos->x, priest_pos->y, NULL, 0) && (priest_pos < pos_array + ARRAY_SIZE(pos_array) - 1); ++priest_pos) {} if (!isok(priest_pos->x, priest_pos->y)) { impossible("Unable to find location for priest in shrine"); } else { if (MON_AT(lev, priest_pos->x, priest_pos->y)) rloc(m_at(lev, priest_pos->x, priest_pos->y), FALSE); priest = makemon(&mons[sanctum ? PM_HIGH_PRIEST : PM_ALIGNED_PRIEST], lev, priest_pos->x, priest_pos->y, MM_ALLLEVRNG); } if (priest) { EPRI(priest)->shroom = (sroom - lev->rooms) + ROOMOFFSET; EPRI(priest)->shralign = Amask2align(lev->locations[sx][sy].altarmask); EPRI(priest)->shrpos.x = sx; EPRI(priest)->shrpos.y = sy; assign_level(&(EPRI(priest)->shrlevel), &lev->z); priest->mtrapseen = ~0; /* traps are known */ priest->mpeaceful = 1; priest->ispriest = 1; priest->msleeping = 0; set_malign(priest); /* mpeaceful may have changed */ /* now his/her goodies... */ if (sanctum && CONST_EPRI(priest)->shralign == A_NONE && on_level(&sanctum_level, &lev->z)) { mongets(priest, AMULET_OF_YENDOR, rng_for_level(&lev->z)); } /* 2 to 4 spellbooks */ for (cnt = rn1(3, 2); cnt > 0; --cnt) { mpickobj(priest, mkobj(level, SPBOOK_CLASS, FALSE, rng_for_level(&lev->z))); } /* robe [via makemon()] */ if (mklev_rn2(2, lev) && (otmp = which_armor(priest, os_armc)) != 0) { if (p_coaligned(priest)) uncurse(otmp); else curse(otmp); } } }