From 0412f517fe297cd9e67b0b0c9852de4475c102d9 Mon Sep 17 00:00:00 2001 From: Martin Read Date: Thu, 13 Mar 2014 23:23:26 +0000 Subject: [PATCH] New file containing room-based level algorithms * Not complete or even usable yet, and probably too time-demanding. --- dungeon.cc | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 dungeon.cc diff --git a/dungeon.cc b/dungeon.cc new file mode 100644 index 0000000..ac8514e --- /dev/null +++ b/dungeon.cc @@ -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 -- 2.11.0