"""Dungeon Generator, by Kris Schnee. Public domain. Creates and (using Pygame) draws "dungeon" adventuring areas in the style of "The Legend of Zelda". The different room colors represent security levels. You start at room S, then have to get the bronze Key (K) to reach the bronze rooms, to get the silver key, and so on to reach tbe Boss (B). No, it's not a game, just a demonstration of a tool that could be used in one. Known bug: The display doesn't center, so you'll often see part of the image cut off. The program will save a copy of the image. The keys don't have to literally be keys. They could be tools or abilities needed to pass certain spots, as seen in the Zelda and Metroid game series. Ideas for things to add would be warp points, ideas for specific room layouts or puzzles, the notion of vertical levels (just add a chance to put new rooms above or below existing ones, rarely), and the idea of building an overworld of multiple dungeons. See squidi.net, particularly "Three Hundred Game Mechanics" #4 and #93 and some of the "Blind Mapmaker" articles. """ import random NEIGHBORS = ((1,0),(-1,0),(0,-1),(0,1)) KEY_LEVELS = ["none","bronze","silver","gold"] MIN_ROOMS_PER_LEVEL = 3 MAX_ROOMS_PER_LEVEL = 12 CONNECTION_CHANCE = 20 CLOSET_ROOM_CHANCE = 50 SUPPLY_CLOSET_CHANCE = 20 class Dungeon: def __init__(self,**options): self.key_levels = options.get("key_levels",1) self.x_bounds = (0,10) self.y_bounds = (0,10) self.start_coords = (random.randint(3,5),0) self.rooms = {} def MakeDungeon(self): self.rooms = {} self.AddRoom(self.start_coords,0,{"start":True}) for level in range(self.key_levels): rooms_this_level = random.randint(MIN_ROOMS_PER_LEVEL,MAX_ROOMS_PER_LEVEL) for n in range(rooms_this_level): spot, parent_coords = self.PickFreeSpot(level) if spot: room = self.AddRoom(spot,level) neighbors = self.GetNeighborRooms(spot) for x in neighbors: if random.randint(0,99) < CONNECTION_CHANCE: room["exits"].append(x["coords"]) if not parent_coords in room["exits"]: room["exits"].append(parent_coords) for e in room["exits"]: if not spot in self.rooms[e]["exits"]: self.rooms[e]["exits"].append(spot) ## Add the first room of the next level (or boss). spot, parent_coords = self.PickFreeSpot(level) if level == self.key_levels - 1: ## This is actually the boss room. room = self.AddRoom(spot,level+1,{"boss":True}) else: room = self.AddRoom(spot,level+1) room["exits"] = [parent_coords] self.rooms[parent_coords]["exits"].append(spot) ## Add the key to the next level. key_rooms = [r["coords"] for r in self.rooms.values() if (r["level"] == level) and not r.get("start") and not spot in r["exits"]] room_coords = key_rooms[ random.randint(0,len(key_rooms)-1) ] self.rooms[room_coords]["key"] = level + 1 ## Add "supply closets". closets = [r for r in self.rooms.values() if (len(r["exits"]) < 2) and not r.get("boss") and not r.get("start") and not r.get("key")] for c in closets: if random.randint(0,99) < SUPPLY_CLOSET_CHANCE: c["treasure"] = True def PickFreeSpot(self,level=0): parent_rooms = [c for c in self.rooms.keys() if (self.rooms[c].get("level") == level) or (random.randint(0,99) 10: return None ## No eligible spots. spot = openings[ random.randint(0,len(openings)-1) ] return spot, parent_coords def GetNeighborRooms(self,coords): neighbors = [] for n in NEIGHBORS: c = (n[0]+coords[0],n[1]+coords[1]) if c in self.rooms.keys(): neighbors.append(self.rooms[c]) return neighbors def GetNeighborNonRooms(self,coords): neighbors = [] for n in NEIGHBORS: c = (n[0]+coords[0],n[1]+coords[1]) if not c in self.rooms.keys(): neighbors.append(c) return neighbors def AddRoom(self,coords,level=0,tags={}): r = {"coords":coords,"level":level,"exits":[]} r.update(tags) self.rooms[coords] = r return r def ShowDungeon(self): dungeon_map.fill((200,200,200)) ROOM_COLORS = [(70,70,120),(205,127,50),(150,150,150),(255,215,10),(220,0,0)] for coords in self.rooms.keys(): room_data = self.rooms[coords] for room_exit in room_data["exits"]: center_coords = (coords[0]*50) + 300 + 25, (coords[1]*50) + 220 + 25 center_coords_2 = (room_exit[0]*50) + 300 + 25, (room_exit[1]*50) + 220 + 25 pygame.draw.line(dungeon_map,(255,255,255),center_coords,center_coords_2,5) for coords in self.rooms.keys(): room_data = self.rooms[coords] screen_x = (coords[0]*50) + 300 + 5 screen_y = (coords[1]*50) + 220 + 5 color = ROOM_COLORS[ room_data["level"] ] pygame.draw.rect(dungeon_map,color,(screen_x,screen_y,40,40)) if room_data.get("start"): pygame.draw.rect(dungeon_map,(255,255,255),(screen_x,screen_y,40,40),2) dungeon_map.blit( Write("S"), (screen_x+10,screen_y+10) ) elif room_data.get("boss"): dungeon_map.blit( Write("B"), (screen_x+10,screen_y+10) ) elif room_data.get("key"): level = room_data["key"] dungeon_map.blit( Write("K",ROOM_COLORS[level]), (screen_x+10,screen_y+10) ) elif room_data.get("treasure"): dungeon_map.blit( Write("$",(0,255,0)), (screen_x+10,screen_y+10) ) screen.blit(dungeon_map,(0,0)) pygame.display.update() ## Autorun Code ## d = Dungeon(key_levels=4) d.MakeDungeon() import pygame ## See pygame.org for this module. pygame.font.init() screen = pygame.display.set_mode((1024,768)) dungeon_map = pygame.surface.Surface((1024,768)) font = pygame.font.Font(None,32) def Write(text,color=(255,255,255)): return font.render(text,1,color) d.ShowDungeon() pygame.image.save(dungeon_map,"dungeon_map.jpg")