]> granicus.if.org Git - nethack/commitdiff
Allow themed room subrooms to be filled
authorcopperwater <aosdict@gmail.com>
Mon, 18 May 2020 01:21:10 +0000 (21:21 -0400)
committerPasi Kallinen <paxed@alt.org>
Sun, 27 Sep 2020 15:54:14 +0000 (18:54 +0300)
I noticed that any subrooms created within a themed room were bare -
they never had any monsters, objects, traps, or anything really,
regardless of whether filled = 1 was set on them. As a result, they're
pretty boring.

It turns out that the code in makelevel() responsible for stocking
ordinary rooms with stuff only looped through g.rooms, and completely
ignored subrooms. (Subrooms would not get stocked with items by virtue
of being part of the larger room; I tested this by dialing the item
generation in rooms way up, and none of those items ever got placed in a
subroom.)

To fix this, I've extracted the code that populates an ordinary room
into its own function, fill_ordinary_room, and made it recurse into its
own subrooms. (I also renamed fill_rooms and fill_room to include the
word "special" in their names, because they only deal with special
rooms.) Note that since special rooms follow a separate codepath, an
ordinary subroom of a special room won't get stocked; perhaps these
functions should be unified in the future.

The fill_ordinary_room code is pretty much a verbatim cut and paste from
makelevel, so there is not currently any consideration for the size of
the subroom or the fact that it is a subroom with respect to how many
monsters, traps, objects, etc get placed.

I'm not sure whether other things such as stair selection will ever
select themed room subrooms, or whether they too only look at g.rooms.

include/extern.h
src/mklev.c
src/sp_lev.c

index 1366ec92ec3d1f026d9fcf99c5799cc580e74584..79708ebfea3c4e607b6c58931250f9d871251ad1 100644 (file)
@@ -2467,7 +2467,7 @@ E boolean FDECL(create_room, (XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P,
                               XCHAR_P, XCHAR_P, XCHAR_P));
 E void FDECL(create_secret_door, (struct mkroom *, XCHAR_P));
 E boolean FDECL(dig_corridor, (coord *, coord *, BOOLEAN_P, SCHAR_P, SCHAR_P));
-E void FDECL(fill_room, (struct mkroom *, BOOLEAN_P));
+E void FDECL(fill_special_room, (struct mkroom *, BOOLEAN_P));
 E boolean FDECL(load_special, (const char *));
 E xchar FDECL(selection_getpoint, (int, int, struct selectionvar *));
 E struct selectionvar *NDECL(selection_new);
index 7f316b8db929a5567fa3bf2d70a428ae1635799f..b4d7efe0737bed724637e4846cedd1d14c7f85a5 100644 (file)
@@ -19,6 +19,7 @@ static void FDECL(mkaltar, (struct mkroom *));
 static void FDECL(mkgrave, (struct mkroom *));
 static void NDECL(makevtele);
 void NDECL(clear_level_structures);
+static void FDECL(fill_ordinary_room, (struct mkroom *));
 static void NDECL(makelevel);
 static boolean FDECL(bydoor, (XCHAR_P, XCHAR_P));
 static struct mkroom *FDECL(find_branch_room, (coord *));
@@ -734,13 +735,114 @@ clear_level_structures()
     }
 }
 
+/* Fill a "random" room (i.e. a typical non-special room in the Dungeons of
+ * Doom) with random monsters, objects, and dungeon features.
+ */
+static void
+fill_ordinary_room(croom)
+struct mkroom *croom;
+{
+    int trycnt = 0;
+    coord pos;
+    struct monst *tmonst; /* always put a web with a spider */
+    int x, y;
+
+    if (croom->rtype != OROOM && croom->rtype != THEMEROOM)
+        return;
+
+    /* If there are subrooms, fill them now - we don't want an outer room
+     * that's specified to be unfilled to block an inner subroom that's
+     * specified to be filled. */
+    for (x = 0; x < croom->nsubrooms; ++x) {
+        fill_ordinary_room(croom->sbrooms[x]);
+    }
+
+    if (!croom->needfill)
+        return;
+
+    /* 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)) && somexyspace(croom, &pos)) {
+        tmonst = makemon((struct permonst *) 0, pos.x, pos.y, MM_NOGRP);
+        if (tmonst && tmonst->data == &mons[PM_GIANT_SPIDER]
+            && !occupied(pos.x, pos.y))
+            (void) maketrap(pos.x, pos.y, WEB);
+    }
+    /* put traps and mimics inside */
+    x = 8 - (level_difficulty() / 6);
+    if (x <= 1)
+        x = 2;
+    while (!rn2(x) && (++trycnt < 1000))
+        mktrap(0, 0, croom, (coord *) 0);
+    if (!rn2(3) && somexyspace(croom, &pos))
+        (void) mkgold(0L, pos.x, pos.y);
+    if (Is_rogue_level(&u.uz))
+        goto skip_nonrogue;
+    if (!rn2(10))
+        mkfount(0, croom);
+    if (!rn2(60))
+        mksink(croom);
+    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) && somexyspace(croom, &pos))
+        (void) mkcorpstat(STATUE, (struct monst *) 0,
+                            (struct permonst *) 0, pos.x,
+                            pos.y, CORPSTAT_INIT);
+    /* 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 negligible.
+     */
+    if (!rn2(g.nroom * 5 / 2) && somexyspace(croom, &pos))
+        (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST,
+                            pos.x, pos.y, 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 {
+                somexyspace(croom, &pos);
+                x = pos.x;
+                y = pos.y;
+            } 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);
+        }
+    }
+
+ skip_nonrogue:
+    if (!rn2(3) && somexyspace(croom, &pos)) {
+        (void) mkobj_at(0, pos.x, pos.y, TRUE);
+        trycnt = 0;
+        while (!rn2(5)) {
+            if (++trycnt > 100) {
+                impossible("trycnt overflow4");
+                break;
+            }
+            (void) mkobj_at(0, pos.x, pos.y, TRUE);
+        }
+    }
+}
+
 static void
 makelevel()
 {
     register struct mkroom *croom;
-    register int tryct;
-    register int x, y;
-    struct monst *tmonst; /* always put a web with a spider */
     branch *branchp;
     int room_threshold;
 
@@ -814,7 +916,7 @@ makelevel()
                      TRUE, VAULT, FALSE);
             g.level.flags.has_vault = 1;
             ++room_threshold;
-            fill_room(&g.rooms[g.nroom - 1], FALSE);
+            fill_special_room(&g.rooms[g.nroom - 1], FALSE);
             mk_knox_portal(g.vault_x + w, g.vault_y + h);
             if (!g.level.flags.noteleport && !rn2(3))
                 makevtele();
@@ -868,90 +970,7 @@ makelevel()
 
     /* for each room: put things inside */
     for (croom = g.rooms; croom->hx > 0; croom++) {
-        int trycnt = 0;
-        coord pos;
-        if (croom->rtype != OROOM && croom->rtype != THEMEROOM)
-            continue;
-        if (!croom->needfill)
-            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)) && somexyspace(croom, &pos)) {
-            tmonst = makemon((struct permonst *) 0, pos.x, pos.y, MM_NOGRP);
-            if (tmonst && tmonst->data == &mons[PM_GIANT_SPIDER]
-                && !occupied(pos.x, pos.y))
-                (void) maketrap(pos.x, pos.y, WEB);
-        }
-        /* put traps and mimics inside */
-        x = 8 - (level_difficulty() / 6);
-        if (x <= 1)
-            x = 2;
-        while (!rn2(x) && (++trycnt < 1000))
-            mktrap(0, 0, croom, (coord *) 0);
-        if (!rn2(3) && somexyspace(croom, &pos))
-            (void) mkgold(0L, pos.x, pos.y);
-        if (Is_rogue_level(&u.uz))
-            goto skip_nonrogue;
-        if (!rn2(10))
-            mkfount(0, croom);
-        if (!rn2(60))
-            mksink(croom);
-        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) && somexyspace(croom, &pos))
-            (void) mkcorpstat(STATUE, (struct monst *) 0,
-                              (struct permonst *) 0, pos.x,
-                              pos.y, CORPSTAT_INIT);
-        /* 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 negligible.
-         */
-        if (!rn2(g.nroom * 5 / 2) && somexyspace(croom, &pos))
-            (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST,
-                             pos.x, pos.y, 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 {
-                    somexyspace(croom, &pos);
-                    x = pos.x;
-                    y = pos.y;
-                } 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);
-            }
-        }
-
- skip_nonrogue:
-        if (!rn2(3) && somexyspace(croom, &pos)) {
-            (void) mkobj_at(0, pos.x, pos.y, TRUE);
-            tryct = 0;
-            while (!rn2(5)) {
-                if (++tryct > 100) {
-                    impossible("tryct overflow4");
-                    break;
-                }
-                (void) mkobj_at(0, pos.x, pos.y, TRUE);
-            }
-        }
+        fill_ordinary_room(croom);
     }
 }
 
index 5d63476c2f6818e08725a7e322b55ebde76046d0..bf6096e327335abfc23f12d9371afcbe6c6a72d7 100755 (executable)
@@ -40,7 +40,7 @@ static void NDECL(remove_boundary_syms);
 static void FDECL(set_door_orientation, (int, int));
 static void FDECL(maybe_add_door, (int, int, struct mkroom *));
 static void NDECL(link_doors_rooms);
-static void NDECL(fill_rooms);
+static void NDECL(fill_special_rooms);
 static int NDECL(rnddoor);
 static int NDECL(rndtrap);
 static void FDECL(get_location, (schar *, schar *, int, struct mkroom *));
@@ -1036,16 +1036,16 @@ link_doors_rooms()
 }
 
 static void
-fill_rooms()
+fill_special_rooms()
 {
     int tmpi, m;
 
     for (tmpi = 0; tmpi < g.nroom; tmpi++) {
         if (g.rooms[tmpi].needfill)
-            fill_room(&g.rooms[tmpi], (g.rooms[tmpi].needfill == 2));
+            fill_special_room(&g.rooms[tmpi], (g.rooms[tmpi].needfill == 2));
         for (m = 0; m < g.rooms[tmpi].nsubrooms; m++)
             if (g.rooms[tmpi].sbrooms[m]->needfill)
-                fill_room(g.rooms[tmpi].sbrooms[m], FALSE);
+                fill_special_room(g.rooms[tmpi].sbrooms[m], FALSE);
     }
 }
 
@@ -2682,7 +2682,7 @@ corridor *c;
  * Fill a room (shop, zoo, etc...) with appropriate stuff.
  */
 void
-fill_room(croom, prefilled)
+fill_special_room(croom, prefilled)
 struct mkroom *croom;
 boolean prefilled;
 {
@@ -6419,7 +6419,7 @@ const char *name;
         goto give_up;
 
     link_doors_rooms();
-    fill_rooms();
+    fill_special_rooms();
     remove_boundary_syms();
 
     /* TODO: ensure_way_out() needs rewrite */