New file containing room-based level algorithms
authorMartin Read <mpread@chiark.greenend.org.uk>
Thu, 13 Mar 2014 23:23:26 +0000 (23:23 +0000)
committerMartin Read <mpread@chiark.greenend.org.uk>
Thu, 13 Mar 2014 23:23:26 +0000 (23:23 +0000)
* Not complete or even usable yet, and probably too time-demanding.

dungeon.cc [new file with mode: 0644]

diff --git a/dungeon.cc b/dungeon.cc
new file mode 100644 (file)
index 0000000..ac8514e
--- /dev/null
@@ -0,0 +1,269 @@
+/*! \file dungeon.cc
+ *  \brief "dungeon"-like level generators i.e. made of rooms
+ */
+
+/* Copyright 2014 Martin Read
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "obumbrata.hh"
+#include "objects.hh"
+#include "monsters.hh"
+#include "mapgen.hh"
+
+#define DBASH_ROOMS_WIDE (3)
+#define DBASH_ROOMS_HIGH (3)
+/*! \brief Number of rooms on a "dungeonbash"-style level */
+#define DBASH_ROOMS (DBASH_ROOMS_WIDE * DBASH_ROOMS_HIGH)
+/*! \brief Maximum number of corridors on a dungeonbash-style level
+ *
+ * This value corresponds to the situation where every adjacent pair of
+ * rooms is connected. */
+#define DBASH_CORRIDORS ((DBASH_ROOMS_WIDE - 1) * DBASH_ROOMS_HIGH + \
+                         (DBASH_ROOMS_HIGH - 1) * DBASH_ROOMS_WIDE)
+/*! \brief Room generated first */
+#define DBASH_ORIGIN_ROOM (4)
+
+struct Dbash_room
+{
+    Coord inner_tl;
+    Coord inner_br;
+    Coord exit_positions[4];
+};
+
+struct Dbash_corridor
+{
+    Coord endpoints[2];
+};
+
+struct Dbash_layout_data
+{
+    Level_layout id;
+    int32_t exit_room;
+    int32_t entry_room;
+    uint16_t connectivity[DBASH_ROOMS];
+    Dbash_room rooms[DBASH_ROOMS];
+    Dbash_corridor corridors[DBASH_CORRIDORS];
+    void *next_layout_data;
+};
+
+static void excavate_normal_room(Level *l, Coord inner_tl, Coord inner_br, uint32_t region)
+{
+    Coord pos;
+    // Initial pass: make the room interior all floor.
+    for (pos.y = inner_tl.y; pos.y <= inner_br.y; ++pos.y)
+    {
+        for (pos.x = inner_tl.x; pos.x <= inner_br.x; ++pos.x)
+        {
+            l->set_terrain_at(pos, FLOOR);
+            l->set_region_at(pos, region);
+        }
+    }
+}
+
+static void draw_pillar_row(Level *l, Coord end1, Coord end2, Terrain t)
+{
+    Offset delta = end2 - end1;
+    Offset step = mysign(delta) * 2;
+}
+
+#define PILLARS_WEST  0x00000001u
+#define PILLARS_EAST  0x00000002u
+#define PILLARS_NORTH 0x00000004u
+#define PILLARS_SOUTH 0x00000008u
+#define PILLARS_INNER_MASK 0x000000f0u
+#define PILLARS_EMPTY 0u
+#define PILLARS_ALL 1u
+#define PILLARS_EW_AVENUE 2u
+#define PILLARS_NS_AVENUE 3u
+#define PILLARS_CROSSED_AVENUE 4u
+#define PILLARS_INNER_SHIFT 4u
+
+static void excavate_pillar_room(Level *l, Coord inner_tl, Coord inner_br, uint32_t region, uint32_t pillar_pattern)
+{
+    Offset delta = inner_br - inner_tl;
+    if ((delta.y | delta.x) & 1)
+    {
+        throw Obumb_levgen_excep("Awkward dimensions for pillar room");
+    }
+    excavate_normal_room(l, inner_tl, inner_br, region);
+    /* Second pass: What pattern of pillars?
+     *
+     * Low 4 bits: Do we have the "ring" pillars?
+     * Next 4 bits: Choice of interior pattern from:
+     *      0 = no interior pillars
+     *      1 = all interior pillars
+     *      2 = east-west avenue of pillars
+     *      3 = north-south avenue of pillars
+     *      4 = crossed avenues of pillars
+     */
+    Coord end1;
+    Coord end2;
+    if (pillar_pattern & PILLARS_EAST)
+    {
+        end1.y = inner_tl.y + 1;
+        end1.x = end2.x = inner_br.x - 1;
+        end2.y = inner_br.y - 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+    }
+    if (pillar_pattern & PILLARS_WEST)
+    {
+        end1.y = inner_tl.y + 1;
+        end1.x = end2.x = inner_tl.x + 1;
+        end2.y = inner_br.y - 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+    }
+    if (pillar_pattern & PILLARS_NORTH)
+    {
+        end1.y = end2.y = inner_tl.y + 1;
+        end2.x = inner_tl.x + 1;
+        end2.x = inner_br.x - 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+    }
+    if (pillar_pattern & PILLARS_SOUTH)
+    {
+        end1.y = end2.y = inner_tl.y + 1;
+        end2.x = inner_tl.x + 1;
+        end2.x = inner_br.x - 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+    }
+    switch ((pillar_pattern >> 4) & 0xf)
+    {
+    default:
+        // TODO emit debug
+    case PILLARS_EMPTY:
+        break;
+    case PILLARS_ALL:
+        end1.x = inner_tl.x;
+        end2.x = inner_tl.x;
+        for (end1.y = end2.y = inner_tl.y + 1;
+             end1.y < inner_br.y; (end1.y += 2), (end2.y += 2))
+        {
+            draw_pillar_row(l, end1, end2, MASONRY_WALL);
+        }
+        break;
+    case PILLARS_EW_AVENUE:
+        if (delta.y > 2)
+        {
+            end1.y = end2.y = (inner_tl.y + inner_br.y) / 2 - 1;
+            end1.x = inner_tl.x + 1;
+            end2.x = inner_br.x - 1;
+            draw_pillar_row(l, end1, end2, MASONRY_WALL);
+            end1.y = end2.y = (inner_tl.y + inner_br.y) / 2 + 1;
+            draw_pillar_row(l, end1, end2, MASONRY_WALL);
+        }
+        break;
+    case PILLARS_NS_AVENUE:
+        if (delta.x > 2)
+        {
+
+            end1.x = end2.x = (inner_tl.x + inner_br.x) / 2 - 1;
+            end1.y = inner_tl.y + 1;
+            end2.y = inner_br.y - 1;
+            draw_pillar_row(l, end1, end2, MASONRY_WALL);
+            end1.y = end2.y = (inner_tl.y + inner_br.y) / 2 + 1;
+            draw_pillar_row(l, end1, end2, MASONRY_WALL);
+        }
+        break;
+    case PILLARS_CROSSED_AVENUE:
+        end1.y = end2.y = (inner_tl.y + inner_br.y) / 2 - 1;
+        end1.x = inner_tl.x + 1;
+        end2.x = inner_br.x - 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+        end1.y = end2.y = (inner_tl.y + inner_br.y) / 2 + 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+        end1.x = end2.x = (inner_tl.x + inner_br.x) / 2 - 1;
+        end1.y = inner_tl.y + 1;
+        end2.y = inner_br.y - 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+        end1.y = end2.y = (inner_tl.y + inner_br.y) / 2 + 1;
+        draw_pillar_row(l, end1, end2, MASONRY_WALL);
+        break;
+    }
+}
+
+/*! \brief Excavate a dungeonbash-style level
+ *
+ * Levels in Martin's Dungeon Bash had nine rooms in a 3x3 grid. This function
+ * reproduces that structure.
+ */
+void build_level_dungeonbash(Level *l)
+{
+    Coord tl;
+    Coord br;
+    int ysize; // internal y-size of room we're about to build
+    int xsize; // internal x-size of room we're about to build
+    Dbash_layout_data *layout_data = new Dbash_layout_data;
+    Dbash_room *rptr;
+    layout_data->id = LAYOUT_DUNGEONBASH;
+    l->layout_data = layout_data;
+    layout_data->entry_room = zero_die(9);
+    do
+    {
+        layout_data->exit_room = zero_die(9);
+    } while (layout_data->exit_room == layout_data->entry_room);
+    /* Go with the standard 3x3 chunk set. */
+    initialize_chunks(l, GUIDE_EDGE_CHUNKS, GUIDE_EDGE_CHUNKS, true);
+    /* Build the centre room. */
+    ysize = 3 + zero_die(6);
+    xsize = 3 + zero_die(6);
+    tl.y = (GUIDE_EDGE_CHUNKS / 2) - (ysize / 2);
+    br.y = tl.y + (ysize - 1);
+    tl.x = (GUIDE_EDGE_CHUNKS / 2) - (xsize / 2);
+    br.x = tl.x + (xsize - 1);
+    rptr = layout_data->rooms + DBASH_ORIGIN_ROOM;
+    rptr->inner_tl = tl;
+    rptr->inner_br = br;
+    /* Centre room is either plain, or ring pillars with crossed avenues. */
+    if (zero_die(2))
+    {
+        excavate_pillar_room(l, rptr->inner_tl, rptr->inner_br,
+                             DBASH_ORIGIN_ROOM, 0x4f);
+    }
+    else
+    {
+        excavate_normal_room(l, rptr->inner_tl, rptr->inner_br,
+                             DBASH_ORIGIN_ROOM);
+    }
+    /* Other rooms are plain for now. */
+    for (int i = 0; i < DBASH_ROOMS; ++i)
+    {
+        if (i == DBASH_ORIGIN_ROOM)
+        {
+            continue;
+        }
+    }
+}
+
+/*! \brief Excavate a "claustrophobia" level
+ *
+ * "Claustrophobia" levels
+ *
+ * \todo Implement.
+ */
+void build_level_claustrophobia(Level *l)
+{
+}
+
+/* dungeon.cc */
+// vim:cindent:ts=8:sw=4:expandtab