Incomplete steps toward a new room generator.
authorNeil Moore <neil@s-z.org>
Wed, 11 Jun 2008 03:54:55 +0000 (23:54 -0400)
committerNeil Moore <neil@s-z.org>
Wed, 11 Jun 2008 03:54:55 +0000 (23:54 -0400)
New class RoomMap to generate a map with rooms.

Use a RoomMap in Level.make_room_map().

level.py

index d5c2b6e..57dfe01 100644 (file)
--- a/level.py
+++ b/level.py
@@ -62,67 +62,78 @@ class LocationProxy (object):
 
     def location(self):
         return self.level().loc(self.y, self.x)
-    
-class Room (object):
-    """A room, with a union-find structure for connected rooms."""
-    def __init__(self, y, x, h, w):
-        (self.y, self.x, self.h, self.w) = (y, x, h, w)
-        self.ymax = y + h - 1
-        self.xmax = x + w - 1
-        self.doors = { 't': False, 'b': False, 'l': False, 'r': False }
-
-    def facing(self, room):
-        possible = []
-        assert not (self & room), "Can't face intersecting room"
-
-        if self.y > room.ymax:
-            dy = room.ymax - self.y
-        elif room.y > self.ymax:
-            dy = room.y - self.ymax
-        else:
-            dy = 0
-        
-        if self.x > room.xmax:
-            dx = room.xmax - self.x
-        elif room.x > self.xmax:
-            dx = room.x - self.xmax
-        else:
-            dx = 0
 
-        if abs(dx) > abs(dy):
-            if dx > 0:
-                return 'r'
-            else:
-                return 'l'
-        else:
-            if dx > 0:
-                return 'b'
-            else:
-                return 't'
-
-    def add_door(self, face):
-        if face == 't':
-            dx = random.randint(self.x+1, self.xmax-1)
-            dy = self.y
-        elif face == 'b':
-            dx = random.randint(self.x+1, self.xmax-1)
-            dy = self.ymax
-        elif face == 'l':
-            dx = self.x
-            dy = random.randint(self.y+1, self.ymax-1)
-        elif face == 'r':
-            dx = self.xmax
-            dy = random.randint(self.y+1, self.ymax-1)
-        else:
-            raise Exception("Unknown face %s" % (face,))
-
-        return (dy, dx)
+class RoomMap (object):
+    def __init__(height, width, nrooms, nstairs):
+        self.nrooms = nrooms
+        # Tiles that are in a room
+        self.tilerooms = {}
+        # Doors we have added to a room
+        self.doors = set()
+        # Tiles that immediately adjoin a room or a 
+        self.walls = set()
+        # Floor tiles that make up a corridor
+        self.corridors = set()
+        self.tips = set()
+        st_rooms = set(random.sample(xrange(nrooms), nstairs))
 
-    def __and__(self, room):
-        yoverlap = (self.y <= room.ymax+1 and self.ymax >= room.y-1)
-        xoverlap = (self.x <= room.xmax+1 and self.xmax >= room.x-1)
-        return yoverlap and xoverlap
+        for room in xrange(nrooms):
+            rm = self.find_room()
+            if self.is_clear(rm):
+                self.place(rno, rm)
+
+    def place(self, rno, (y, x, h, w)):
+        "Place a room, updating tilerooms and walls."
+        for i in xrange(y, y + h):
+            for j in xrange(x, x + w):
+                if (i == y or i == y+h-1) and (j == x or j == x+w-1):
+                    # Corner, do nothing
+                    pass
+                elif (i == y or i == y+h-1) or (j == x or j == x+w-1):
+                    # A wall.  If there is a corridor here, place a door.
+                    if (i,j) in self.corridors:
+                        self.tips -= set([(i,j)])
+                        self.doors.add((i,j))
+                    else:
+                        self.walls.add((i,j))
+                else:
+                    # Interior; make it a room, and make it neither a wall
+                    # nor a tip.
+                    self.tilerooms[(i,j)] = rno
+                    s = set([(i,j)])
+                    self.tips -= s
+                    self.corridors -= s
+
+
+    def is_clear(self, (y, x, h, w)):
+        "Can a room (y, x, h, w) be placed without disturbing other rooms?"
+        nontip = self.corridors - self.tip
+        walls_corrs = self.walls | self.corridors
+        for i in xrange(y, y + h):
+            for j in xrange(x, x + w):
+                # Make sure the tile is not already part of a room.
+                if (i,j) in self.tilerooms:
+                    return False
+
+                # If the tile is interior to the room, make sure it is not
+                # a wall or a corridor.
+                if (i > y and i < y + h and j > x and j < x + w):
+                    if (i,j) in walls_corrs:
+                        return False
+                else:
+                    # Make sure it's not a non-tip corridor
+                    if (i,j) in nontip:
+                        return False
+        return True
+
+    def find_room(self):
+        rh = random.randint(4, self.h // (math.sqrt(self.nrooms)/1.5))
+        rw = random.randint(4, self.w // (math.sqrt(selfnrooms)/1.5))
+        ry = random.randint(0, self.h - rh)
+        rx = random.randint(0, self.w - rw)
+        return (ry, rx, rh, rw)
 
+        
 
 class Level (object):
     """Represents an area of the map comprising a contiguous rectangular
@@ -251,11 +262,10 @@ class Level (object):
                     self.grid[ty][tx] = loc.Floor(self, ty,tx)
 
     def make_room_map(self, nrooms = 10, nstairs = 2):
-        self.grid = [ [ loc.Wall(self,i,j)
-            for j in xrange(self.w) ]
-            for i in xrange(self.h) ]
-        rooms = []
+        """Make a nice map containing nrooms rooms, with two of those
+        rooms containing stairs.."""
 
+        rm = RoomMap(self.h, self.w, nrooms, nstairs)
         st_rooms = set(random.sample(xrange(nrooms), nstairs))
 
         for rno in xrange(nrooms):
@@ -287,6 +297,10 @@ class Level (object):
                 nct = 2
             for tgt in random.sample(range(0, rno), nct):
                 self.connect(rooms[rno], rooms[tgt])
+        
+        self.grid = [ [ loc.Wall(self,i,j)
+            for j in xrange(self.w) ]
+            for i in xrange(self.h) ]
 
         for row in self.grid:
             for tile in row: