-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMapMaker.py
349 lines (293 loc) · 12.8 KB
/
MapMaker.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
import matplotlib.pyplot as plt
import random
"""
This program generates random maps for TTRPG based on the Nintendo property Pokemon Mystery Dungeon
TODO List
Combine rooms sometimes makes weird shapes
Add start and end stairs. TODO Grab the sprites for those before I delete them
Make Hallways that connect rooms (Maybe reuse closest point code)
Little things like dead ends need to exist to be more like the original game
Make random room locations less random
Add a hashing alg to convert string to seed (Probably something standard like SHA256 so pythonversions don't chenge the values)
Might be a bug where two walls that are exactly touching will have a wall between them
"""
#Edit these if you want to change num of rooms or the size variance
#region
minRooms = 4
maxRooms = 8
minRoomVariance = 6
maxRoomVariance = 12
#endregion
class Floor:
def __init__(self):
self.rooms = []
self.rectangles = [] #TODO decide if we need this. It can allow us to track combined rooms as two smaller rooms
self.cornerX = [0, 100, 100, 0, 0]
self.cornerY = [0, 0, 100, 100, 0]
self.numRooms = random.randint(minRooms, maxRooms)
def AddRoom(self, *rooms):
"""Adds any amount of rooms
Args:
*rooms: a single or list of rooms
"""
for room in rooms:
self.rooms.append(room)
room.floor = self
def MakeRooms(self):
"""Makes X rooms in random locations where X is a random number between minRooms & maxRooms
"""
currNumRooms = len(self.rooms)
while currNumRooms < self.numRooms:
x = random.randint(1, 100)
y = random.randint(1, 100)
# Check if the coordinate is not used then create room
if not any(room.x == x and room.y == y for room in self.rooms):
self.AddRoom(Room(x,y))
currNumRooms += 1
for i, room1 in enumerate(self.rooms):
for room2 in self.rooms[i+1:]:
room1.Combine(room2)
return True
def AddStairs(self):
#Picks random rooms for stairs
roomNumUp = random.randint(0, self.numRooms - 1)
while True:
roomNumDown = random.randint(0, self.numRooms - 1)
if roomNumUp != roomNumDown:
break
#Assignes each room a status for if it has stairs
roomUp = self.rooms[roomNumUp]
roomDown = self.rooms[roomNumDown]
roomUp.stairs = 'Up'
roomDown.stairs = 'Down'
upX = random.randint(roomUp.x+1, roomUp.x + roomUp.width-1)
upY = random.randint(roomUp.y-roomUp.height + 1, roomUp.y-1)
downX = random.randint(roomDown.x+1, roomDown.x + roomDown.width-1)
downY = random.randint(roomDown.y-roomDown.height + 1, roomDown.y-1)
roomUp.stairsXY = (upX,upY)
roomDown.stairsXY = (downX, downY)
return (roomNumUp, roomNumDown)
def Draw(self):
figure = plt.figure(figsize=(6,6))
plt.plot(self.cornerX, self.cornerY, color='black')
for room in self.rooms:
if (room.stairs == "Up"):
stairUp = room.stairsXY
plt.scatter(stairUp[0], stairUp[1], color='red', s=20)
if (room.stairs == "Down"):
stairDown = room.stairsXY
plt.scatter(stairDown[0], stairDown[1], color='black', s=20)
if (len(room.corners) > 0):
#seperate lists for x and y coordinates
x_coords = [corner[0] for corner in room.corners]
y_coords = [corner[1] for corner in room.corners]
# Add the first corner to close shape
x_coords.append(room.corners[0][0])
y_coords.append(room.corners[0][1])
plt.plot(x_coords, y_coords, color='blue')
xlim = plt.xlim(-10, 110) # Set x-axis limits
ylim = plt.ylim(-10, 110) # Set y-axis limits
plt.show
def RemoveRoom(self, room):
self.rooms.remove(room)
class Room:
"""
Updating room class to a new implementation. I do not need the mid point so we will instead track an X,Y pair for the top left corner of each room
Then just track width and height. This should simplify a ton of things
Room class allows for randomly generated rooms and combined rooms to be easily drawn
Args:
middle: list of 2 ints treated as X,Y coordinates
"""
def __init__(self, x, y):
self.x = x
self.y = y
self.width = random.randint(minRoomVariance, maxRoomVariance)
self.height = random.randint(minRoomVariance, maxRoomVariance)
self.corners = []
self.floor = None
self.stairs = None
self.stairsXY = None
self.corners.append((self.x, self.y))
self.corners.append((self.x + self.width, self.y))
self.corners.append((self.x + self.width,self.y - self.height))
self.corners.append((self.x , self.y - self.height))
def Intersect(self, otherRoom):
"""Finds if two rooms intersect. If so returns the coordinates for the intesecting rectangle
Args:
otherRoom - Second room you are checking against
"""
interiorRectangle = []
x_left = max(self.x, otherRoom.x)
x_right = min(self.x + self.width, otherRoom.x + otherRoom.width)
y_bottom = max(self.y - self.height, otherRoom.y - otherRoom.height)
y_top = min(self.y, otherRoom.y)
# Check if there is no overlap
if x_right < x_left or y_bottom > y_top:
return None
interiorRectangle.append((x_left, y_top))
interiorRectangle.append((x_right, y_top))
interiorRectangle.append((x_right, y_bottom))
interiorRectangle.append((x_left, y_bottom))
# Returns overlapping rectangle corners
return interiorRectangle
"""
Logic looks funny here but basically
if intersect combine lists check if in interior rectangle and if it is remove it from the lists
This removes the inside corners while adding the corners on the edges
"""
def Combine(self, otherRoom):
interiorRectangle = self.Intersect(otherRoom)
cornersToAdd = []
#print(f"Interior Rect is: {interiorRectangle}")
if interiorRectangle is not None:
self.corners.extend(otherRoom.corners) #Add room2 corners to room1 corners list
for intCorners in interiorRectangle: #Loops through interior rect and removes duplicates
if(intCorners in self.corners):
self.corners.remove(intCorners)
else: #If not duplicate add
cornersToAdd.append(intCorners)
for intCorner in cornersToAdd:
for i, corner in enumerate(self.corners):
if i + 1 < len(self.corners):
nextCorner = self.corners[i+1]
if((intCorner[0] == corner[0]) and (intCorner[1] == nextCorner[1])):
#Insert into this location then break?
print(f"Adding corner {intCorner} from list: {cornersToAdd} to index {i + 1} in list {self.corners}")
self.corners.insert(i+1, intCorner)
print(f"New list is: {self.corners}")
#cornersToAdd.remove(intCorner)
break
#If not in place adds at end
if (intCorner not in self.corners):
self.corners.append(intCorner)
otherRoom.Delete()
self.SortCorners()
self.floor.MakeRooms()
return True
return False
def SortCorners(self):
"""This method sorts all corners starting at the first in the corner list. Each pair is the closest to the previous pair
sort-corners-fix update: Adds a check to make sure next point is in line with current point (X or Y is same)
Args:
none
Returns:
bool: True when no errors occur
"""
tempCorners = self.corners.copy()
inOrderList = []
pointsAdded = 0
minX = min(corner[0] for corner in self.corners)
maxY = max(corner[1] for corner in self.corners)
topLeft = (minX, maxY)
#TODO Idk if this fixes anything
if topLeft not in tempCorners:
topLeft = tempCorners[0]
currPoint = topLeft
pointToAdd = topLeft
inOrderList.append(pointToAdd)
pointsAdded += 1
try:
tempCorners.remove(pointToAdd)
except:
SystemExit
directionPointToAdd = 'up'
direction = None
#Returns direction or None if directionPointToAdd is higher priority
#Also returns None if the pointToAdd is in the same direction and closer to the currPoint
def FindDirection(currPoint, nextPoint, directionPointToAdd, pointToAdd):
direction = None
sameY = currPoint[1] == nextPoint[1]
sameX = currPoint[0] == nextPoint[0]
if ((nextPoint[0] - currPoint[0] > 0) and sameY):
direction = 'right'
elif((nextPoint[0] - currPoint[0] < 0) and sameY):
direction = 'left'
elif((nextPoint[1] - currPoint[1] > 0) and sameX):
direction = 'up'
elif((nextPoint[1] - currPoint[1] < 0) and sameX):
direction = 'down'
else:
direction = None
return None
if (pointToAdd is not None and directionPointToAdd is not None):
# Check if the direction is the same but the next_point is further than point_to_add
if direction == directionPointToAdd:
if direction in ['right', 'left']:
if abs(pointToAdd[0] - currPoint[0]) < abs(nextPoint[0] - currPoint[0]):
return None
elif direction in ['up', 'down']:
if (pointToAdd[1] - currPoint[1]) < abs(nextPoint[1] - currPoint[1]):
return None
return direction
while pointsAdded < len(self.corners):
for corner in tempCorners:
direction = FindDirection(currPoint, corner, directionPointToAdd, pointToAdd)
match direction:
case None:
pointToAdd = pointToAdd
case 'right':
pointToAdd = corner
directionPointToAdd = 'right'
case 'down':
if (directionPointToAdd not in {'right'}):
pointToAdd = corner
directionPointToAdd = 'down'
case 'left':
if (directionPointToAdd not in {'right', 'down'}):
pointToAdd = corner
directionPointToAdd = 'left'
case 'up':
if (directionPointToAdd not in {'right', 'down', 'left'}):
pointToAdd = corner
directionPointToAdd = 'up'
#TODO Fix bug that writes the same number over and over here
inOrderList.append(pointToAdd)
pointsAdded += 1
try:
tempCorners.remove(pointToAdd)
except:
SystemExit
currPoint = pointToAdd
directionPointToAdd = 'temp'
self.corners = inOrderList.copy()
return 1
def RemoveWalls(self, otherRoom, interiorRectangle):
return -1
#TODO
def Delete(self):
if (self.floor and self.corners is not None):
self.floor.RemoveRoom(self)
self.corners = None
return 1
def main():
i = 0
while i < 10:
floor = Floor()
#print(f"This floor should have: {floor.numRooms} rooms")
floor.MakeRooms() #Creates randomly generated rooms
floor.AddStairs()
floor.Draw()
#print(f"Floor has: {len(floor.rooms)} Rooms")
#input(f"Press Enter...")
i+= 1
#Testin purposes:
#region
#room1 = Room(0,20)
#room2 = Room(58, 43)
#room3 = Room(2, 15)
#room4 = Room(53,28)
#floor.AddRoom(room1, room2, room3)
# Iterate through each pair of rooms
#for i, room1 in enumerate(floor.rooms):
#for room2 in floor.rooms[i+1:]:
#room1.Combine(room2)
#floor.rectangles = floor.rooms.copy()
#floor.CombineRooms()
#for rooms in overlaps:
#print(f"These rooms overlap: {rooms[0].middle}, {rooms[1].middle}")
#room1.SortCorners()
#endregion
#floor.Draw()
return 1
if __name__ == '__main__':
main()