2016-11-10 20:01:50 +01:00
|
|
|
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 """
|
2016-11-10 20:09:08 +01:00
|
|
|
if self.left_leaf or self.right_leaf:
|
2016-11-10 20:01:50 +01:00
|
|
|
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
|
|
|
|
|
2017-07-19 03:46:17 +02:00
|
|
|
def center(self):
|
|
|
|
return round(self.x + self.width/2), round(self.y + self.height/2)
|
|
|
|
|
2016-11-10 20:01:50 +01:00
|
|
|
|
|
|
|
class Level:
|
|
|
|
TILE_WALL = 0
|
|
|
|
TILE_ROAD = 1
|
|
|
|
TILE_GROUND = 2
|
2017-07-19 03:46:17 +02:00
|
|
|
TILE_HALLWAY = 3
|
2016-11-10 20:01:50 +01:00
|
|
|
|
|
|
|
def __init__(self, sizex, sizey, tile_file=None):
|
|
|
|
self.sizex, self.sizey = sizex, sizey
|
|
|
|
self.tileset = []
|
2016-12-07 01:38:58 +01:00
|
|
|
self.tilemap = []
|
2016-11-10 20:01:50 +01:00
|
|
|
if tile_file:
|
|
|
|
self.load_tileset(tile_file)
|
2016-12-07 03:08:49 +01:00
|
|
|
|
|
|
|
self.tree = None
|
|
|
|
self.rooms = []
|
|
|
|
self.regenerate()
|
2016-11-10 20:01:50 +01:00
|
|
|
|
|
|
|
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
|
2016-11-10 20:09:08 +01:00
|
|
|
tree = [Leaf(0, 0, 30, 30, 0)]
|
2016-11-10 20:01:50 +01:00
|
|
|
|
|
|
|
# split leaves until none succeed
|
|
|
|
# Lists are ordered. Tree will be created and travel from left to right
|
2016-11-10 20:09:08 +01:00
|
|
|
for l in tree:
|
2016-11-10 20:01:50 +01:00
|
|
|
if l.split():
|
2016-11-10 20:09:08 +01:00
|
|
|
tree.append(l.left_leaf)
|
|
|
|
tree.append(l.right_leaf)
|
2016-11-10 20:01:50 +01:00
|
|
|
|
2016-12-07 03:08:49 +01:00
|
|
|
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()
|
|
|
|
|
2016-12-07 02:39:09 +01:00
|
|
|
# initialize tilemap with road/ground
|
2017-07-19 03:46:17 +02:00
|
|
|
self.tilemap = [[self.TILE_GROUND for y in range(0, self.sizey)] for x in range(0, self.sizex)]
|
2016-12-07 01:38:58 +01:00
|
|
|
|
2016-11-10 20:01:50 +01:00
|
|
|
# create rooms from partitions
|
2016-12-07 03:08:49 +01:00
|
|
|
self.rooms = self.generate_rooms(self.tree[0], self.tilemap, [])
|
2016-11-10 20:01:50 +01:00
|
|
|
|
2017-07-19 03:46:17 +02:00
|
|
|
self.generate_hallways()
|
|
|
|
|
2016-11-10 20:01:50 +01:00
|
|
|
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("")
|
|
|
|
|
2017-07-19 03:46:17 +02:00
|
|
|
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
|
|
|
|
|
2016-12-07 03:08:49 +01:00
|
|
|
|
2016-11-10 20:01:50 +01:00
|
|
|
##
|
|
|
|
# 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')
|
2016-12-07 03:08:49 +01:00
|
|
|
level.regenerate()
|
2016-11-10 20:01:50 +01:00
|
|
|
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()
|
2016-12-07 01:38:58 +01:00
|
|
|
level.draw_map()
|
2016-11-10 20:01:50 +01:00
|
|
|
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()
|