import pyglet from pyglet.window import key import re import sys import random # constants TILE_SIZE_X = 16 TILE_SIZE_Y = 16 MIN_LEAF_SIZE = 8 class Leaf: """ Leaf of the BSP """ def __init__(self, x, y, w, h, id): self.id = id self.x = x self.y = y self.width = w self.height = h self.left_leaf = None self.right_leaf = None def print_leaf(self): print('[Leaf]' + str(self.id) + ': (' + str(self.x) + '), (' + str(self.y) + ')' + ' - ' + str(self.width) + 'x' + str(self.height)) def is_terminal(self): """ Returns True if the leave has no children """ if self.left_leaf or self.right_leaf: return False return True def split(self): """ :return: True if the leaf has been split """ # self.print_leaf() # choose direction # TODO: if leaf is not so square, split along the long side # if width > XX% height (25, 33 ?) -> split vertical if random.random() < 0.5: # x if self.width < MIN_LEAF_SIZE * 2: # leaf is too small return False split_point = random.randint(MIN_LEAF_SIZE, self.width - MIN_LEAF_SIZE) self.left_leaf = Leaf(self.x, self.y, split_point, self.height, self.id+1) self.right_leaf = Leaf(self.x + split_point, self.y, self.width - split_point, self.height, self.id+2) else: # y if self.height < MIN_LEAF_SIZE * 2: # leaf is too small return False split_point = random.randint(MIN_LEAF_SIZE, self.height - MIN_LEAF_SIZE) self.left_leaf = Leaf(self.x, self.y, self.width, split_point, self.id+1) self.right_leaf = Leaf(self.x, self.y + split_point, self.width, self.height - split_point, self.id+2) return True class Room: """ room """ def __init__(self, x, y, w, h, id): self.x = x self.y = y self.width = w self.height = h self.id = id self.print_room() def print_room(self): print('[room]' + str(self.id) + ': (' + str(self.x) + '), (' + str(self.y) + ')' + ' - ' + str(self.width) + 'x' + str(self.height)) def draw_map(self, tilemap): # first wall for x in range(0, self.width): tilemap[self.x + x][self.y] = Level.TILE_WALL # middle for y in range(1, self.height - 1): tilemap[self.x][self.y + y] = Level.TILE_WALL for x in range(1, self.width - 1): tilemap[self.x + x][self.y + y] = Level.TILE_GROUND tilemap[self.x ++ self.width - 1][self.y + y] = Level.TILE_WALL # end for x in range(0, self.width): tilemap[self.x + x][self.y + self.height - 1] = Level.TILE_WALL def center(self): return round(self.x + self.width/2), round(self.y + self.height/2) class Level: TILE_WALL = 0 TILE_ROAD = 1 TILE_GROUND = 2 TILE_HALLWAY = 3 def __init__(self, sizex, sizey, tile_file=None): self.sizex, self.sizey = sizex, sizey self.tileset = [] self.tilemap = [] if tile_file: self.load_tileset(tile_file) self.tree = None self.rooms = [] self.regenerate() def load_tileset(self, tile_file): resource_file = open(tile_file) line = resource_file.readline().strip() while line: image_file = re.compile('\d: ').split(line)[1] print('loading tile: ' + str(image_file)) self.tileset.append(pyglet.resource.image(image_file)) line = resource_file.readline().strip() resource_file.close() def generate_tree(self): # init tree tree = [Leaf(0, 0, 30, 30, 0)] # split leaves until none succeed # Lists are ordered. Tree will be created and travel from left to right for l in tree: if l.split(): tree.append(l.left_leaf) tree.append(l.right_leaf) return tree def generate_rooms(self, leaf, tilemap, room_list): """ generate rooms inside partitioned space :return: a list of generated rooms """ if leaf.left_leaf: self.generate_rooms(leaf.left_leaf, tilemap, room_list) if leaf.right_leaf: self.generate_rooms(leaf.right_leaf, tilemap, room_list) if not leaf.left_leaf and not leaf.right_leaf: room = self.generate_room(leaf, tilemap) room_list.append(room) return room_list def generate_room(self, leaf, tilemap): # Leave space for walls x = random.randint(leaf.x + 1, leaf.x + 2) y = random.randint(leaf.y + 1, leaf.y + 2) w = random.randint(MIN_LEAF_SIZE / 2, leaf.width - 2) h = random.randint(MIN_LEAF_SIZE / 2, leaf.height - 2) r = Room(x, y, w, h, leaf.id) r.draw_map(tilemap) return r def regenerate(self): """" generate a new level """ self.tree = self.generate_tree() # initialize tilemap with road/ground self.tilemap = [[self.TILE_GROUND for y in range(0, self.sizey)] for x in range(0, self.sizex)] # create rooms from partitions self.rooms = self.generate_rooms(self.tree[0], self.tilemap, []) self.generate_hallways() def draw_map(self): for y in range(0, self.sizey): for x in range(0, self.sizex): self.tileset[self.tilemap[x][y]].blit(x*TILE_SIZE_X, y*TILE_SIZE_Y) def dump_tilemap(self): for y in range(0, self.sizey): for x in range(0, self.sizex): print(self.tilemap[x][y], end="") print("") def generate_hallways(self): """ """ if len(self.rooms) < 2: # out of luck ? return room_a = self.rooms[0] for r in self.rooms[1:]: room_b = r self.generate_hallway(room_a, room_b) room_a = room_b def generate_hallway(self, room_a, room_b): """ """ print("hallway between room" + str(room_a.id) + " and room " + str(room_b.id)) (xa, ya) = room_a.center() (xb, yb) = room_b.center() # horizontal part if xa < xb: start_x = xa end_x = xb else: start_x = xb end_x = xa print("h_hw form " + str(start_x) + " and " + str(end_x)) for x in range(start_x, end_x): self.tilemap[x][ya] = self.TILE_ROAD # vertical part if ya < yb: start_y = ya end_y = yb else: start_y = yb end_y = ya print("v_hw form " + str(start_y) + " and " + str(end_y)) for y in range(start_y, end_y): self.tilemap[end_x][y] = self.TILE_ROAD ## # main ## # main window window = pyglet.window.Window() @window.event def on_key_press(symbol, modifiers): # TODO: ugly hack global level if symbol == key.Q: print('Will quit') if symbol == key.R: print('Regeneration') level.regenerate() window.invalid = True if symbol == key.D: level.dump_tilemap() elif symbol == key.LEFT: print('Left arrow') elif symbol == key.ENTER: print('Enter !') @window.event def on_draw(): window.clear() level.draw_map() label.draw() # init random seed = random.randint(0, sys.maxsize) print("Using seed: " + str(seed)) random.seed(seed) label_txt = 'Plop World seed: ' + str(seed) label = pyglet.text.Label(label_txt, x=window.width*5/6, y=window.height*5/6, anchor_x='center', anchor_y='center', multiline=True, width=window.width - window.width*5/6) level = Level(30, 30, 'tiles.txt') pyglet.app.run()