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