Add passage links to rooms
[koperkapel.git] / koperkapel / generators / maps.py
1 """ Procedural map generation for levels """
2
3 import random
4 import math
5
6 i = random.randint(0,100)
7
8
9 class Room:
10     def __init__(self, region):
11         """
12         """
13         self.regions = [region]
14         self.max_connections = 1
15         self.passages = []
16         self.tunnels = []
17
18     def is_linked(self):
19         """
20         Check if the room is linked to another room
21         :return: Whether the room has any links or not
22         """
23         return len(self.passages) + len(self.tunnels) > 0
24
25     def add_passage(self, room_region, new_region):
26         """ Add a passage link between rooms
27         :param room_region: coordinate of region from which we are linking
28         :param new_region: coordinate of region to which we are linking
29         :return:
30         """
31         pass
32
33     def add_region(self, region):
34         """
35         Add a new region into an existing room
36         :param region: region coordinates to be added to room
37         :return:
38         """
39         self.regions.append(region)
40
41     def connect_rooms(self, other_rooms):
42         """ Find the nearest rooms to this room
43         :param other_rooms: list of Rooms objects that we are searching
44         :return:
45         """
46         distance = []
47         other_tile = []
48         this_tile = []
49         target_rooms = []
50         for local_region in self.regions:
51             for room in other_rooms:
52                 if self == room:
53                     continue
54                 for new_region in room.regions:
55                     distance.append(
56                         math.sqrt((local_region[0] - new_region[0]) ** 2 +
57                                   (local_region[1] - new_region[1]) ** 2))
58                     other_tile.append(new_region)
59                     this_tile.append(local_region)
60                     target_rooms.append(room)
61
62         sorted_indices = [i[0] for i in sorted(enumerate(distance),
63                                                key=lambda x:x[0])]
64         for index in sorted_indices:
65             if len(self.passages) + len(self.tunnels) >= self.max_connections:
66                 break
67             if not target_rooms[index].is_linked():
68                 self.link_passage(this_tile[index], other_tile[index])
69                 target_rooms[index].link_passage(
70                     other_tile[index], this_tile[index])
71
72     def link_passage(self, local_tile, foriegn_tile):
73         """ Link a passage between two rooms
74         :param local_tile: tile in this room to which we wish to link
75         :param foriegn_tile: tile in another room to which we wish to link
76         :return:
77         """
78         self.passages.append([local_tile, foriegn_tile])
79
80
81 class LevelGenerator:
82     width = 0
83     height = 0
84     no_rooms = 0
85     rooms = []
86     map = None
87     min_room_size = 0
88     max_room_size = 0
89     dist_from_edge = 0
90     dist_from_other_rooms = 0
91     region_map = None
92     regions = 0
93     region_size = 0
94
95     def __init__(self, width, height, no_rooms, min_room_size, max_room_size,
96                  dist_from_edge, dist_from_other_rooms, region_size):
97         """ Initialize the level parameters
98         """
99         self.width = width
100         self.height = height
101         self.no_rooms = no_rooms
102         self.min_room_size = min_room_size
103         self.max_room_size = max_room_size
104         self.dist_from_edge = dist_from_edge
105         self.dist_from_other_rooms = dist_from_other_rooms
106         self.region_size = region_size
107
108     def generate(self):
109         """ Generate a random level map
110         """
111         self.generate_rooms()
112         regions_selected = random.sample(range(self.regions),
113                                          min(self.regions, self.no_rooms))
114         row = ['#' for x in range(self.width * self.region_size)]
115         self.map = [row[:] for x in range(self.height * self.region_size)]
116         print('Regions: %s' % str(regions_selected))
117         for region in regions_selected:
118             self.rooms[region].connect_rooms(
119                 [self.rooms[i] for i in regions_selected])
120             self.generate_tiles(region)
121
122     def generate_rooms(self):
123         """ Generate a random level region map
124         """
125         row = [0 for x in range(self.width)]
126         self.region_map = [row[:] for x in range(self.height)]
127         for h in range(self.height):
128             for w in range(self.width):
129                 random_number = random.randint(0, 2)
130                 increment_region = False
131                 if w == h == 0:
132                     self.region_map[h][w] = self.regions
133                     increment_region = True
134                 elif h == 0:
135                     if random_number > 1:
136                         self.region_map[h][w] = self.region_map[h][w - 1]
137                     else:
138                         self.region_map[h][w] = self.regions
139                         increment_region = True
140                 elif w == 0:
141                     if random_number > 1:
142                         self.region_map[h][w] = self.region_map[h - 1][w]
143                     else:
144                         self.region_map[h][w] = self.regions
145                         increment_region = True
146                 else:
147                     if random_number > 1:
148                         self.region_map[h][w] = self.region_map[h - 1][w]
149
150                     elif random_number > 0:
151                         self.region_map[h][w] = self.region_map[h][w - 1]
152                     else:
153                         self.region_map[h][w] = self.regions
154                         increment_region = True
155                 if increment_region:
156                     self.regions += 1
157                     r = Room([h, w])
158                     self.rooms.append(r)
159                 else:
160                     self.rooms[-1].add_region([h,w])
161
162     def generate_tiles(self, region_selected):
163         """
164         :param region_selected:
165         :return:
166         """
167         for h in range(self.height):
168             for w in range(self.width):
169                 if self.region_map[h][w] == region_selected:
170                     if w == 0:
171                         w_dist = self.dist_from_other_rooms
172                     elif self.region_map[h][w-1] == region_selected:
173                         w_dist = 0
174                     else:
175                         w_dist = self.dist_from_other_rooms
176
177                     if w + 1 == self.width:
178                         e_dist = self.region_size - self.dist_from_other_rooms
179                     elif self.region_map[h][w+1] == region_selected:
180                         e_dist = self.region_size
181                     else:
182                         e_dist = self.region_size - self.dist_from_other_rooms
183
184                     if h == 0:
185                         n_dist = self.dist_from_other_rooms
186                     elif self.region_map[h-1][w] == region_selected:
187                         n_dist = 0
188                     else:
189                         n_dist = self.dist_from_other_rooms
190
191                     if h + 1 == self.height:
192                         s_dist = self.region_size - self.dist_from_other_rooms
193                     elif self.region_map[h+1][w] == region_selected:
194                         s_dist = self.region_size
195                     else:
196                         s_dist = self.region_size - self.dist_from_other_rooms
197
198                     for wt in range(w_dist, e_dist):
199                         for ht in range(n_dist, s_dist):
200                             self.map[h * self.region_size + ht]\
201                                 [w * self.region_size + wt] = ' '
202
203     def generate_passage_tiles(self):
204         """ Generate the tiles that form the passages between the rooms
205         :return:
206         """
207
208
209
210     def display(self):
211         file = open('map.txt', 'w')
212         for l in self.map:
213             print(''.join(l))
214             file.write(''.join(l))
215             file.write('\n')
216         file.close()
217         for l in self.region_map:
218             print(l)
219
220
221 if __name__ == '__main__':
222     while True:
223         level = LevelGenerator(width=4, height=3, no_rooms=4,
224                                min_room_size=5, max_room_size=20,
225                                dist_from_edge=1, dist_from_other_rooms=1,
226                                region_size=5)
227         level.generate()
228         level.display()
229         input("Press Enter to continue...")