forked from orphu/mcdungeon
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcave_factory.py
312 lines (273 loc) · 10.4 KB
/
cave_factory.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
# Cave factory produces a cave-like structure with no disconnected
# rooms. Caves typically have a smooth, twisty appearance with lots of
# alcoves. This is based largely on the cellular automata examples at:
#
# http://roguebasin.roguelikedevelopment.org
#
# It also borrows code for joining disconnected cells from Dana Larose's
# example:
# http://pixelenvy.ca/wa/ca_cave.html
#
# I've tweaked the CA generations a bit to smooth out the cell joins, and added
# support for building connecting edges. I use this to build connected tiles of
# caves and hallways joining to other parts of the dungeon.
import sys
from random import randrange, random, choice
from disjoint_set import DisjointSet
FLOOR = 1
WALL = 2
TUNNEL = 3
class new:
def __init__(self, length, width, walls=0.40):
self.__length = length
self.__width = width
self.__exits = []
self.__map = []
self.__buf_map = []
self.__gen_initial_map(walls)
self.__ds = DisjointSet()
self.__cpt = (int(self.__length / 2), int(self.__width / 2))
def resize_map(self, new_length, new_width, center=True):
new_map = [[WALL for i in xrange(new_width)]
for j in xrange(new_length)]
ox = int(new_width / 2.0 - self.__width / 2.0 + 0.5)
oy = int(new_length / 2.0 - self.__length / 2.0 + 0.5)
for i in xrange(self.__width):
for j in xrange(self.__length):
x2 = ox + i
y2 = oy + j
if (
x2 >= 0 and
y2 >= 0 and
x2 < new_width and
y2 < new_width
):
new_map[x2][y2] = self.__map[i][j]
self.__map = new_map
self.__length = new_length
self.__width = new_width
self.__exits = []
self.__cpt = (int(self.__length / 2), int(self.__width / 2))
def print_map(self):
for c in xrange(0, self.__width):
for r in xrange(0, self.__length):
if self.__map[r][c] == WALL:
sys.stdout.write('#')
elif self.__map[r][c] == TUNNEL:
sys.stdout.write('+')
else:
sys.stdout.write(' ')
print
print
def iterate_walls(self):
for c in xrange(0, self.__width):
for r in xrange(0, self.__length):
if self.__map[r][c] == WALL:
if (self.__adj_flr_count(r, c) > 0):
yield (c, r)
def iterate_map(self, cell_type):
for c in xrange(0, self.__width):
for r in xrange(0, self.__length):
if self.__map[r][c] == cell_type:
yield (c, r)
def add_exit(self, pt1, pt2):
while (pt1 != pt2):
if (
pt1[0] < 0 or
pt1[0] >= self.__width or
pt1[1] < 0 or
pt1[1] >= self.__length
):
sys.exit('WARN: Exit out of range', pt1)
else:
self.__exits.append(pt1)
pt1 = (pt1[0] + cmp(pt2[0], pt1[0]),
pt1[1] + cmp(pt2[1], pt1[1]))
def purge_exits(self):
self.__exits = []
for c in xrange(0, self.__width):
for r in xrange(0, self.__length):
if (
c == 0 or c == self.__width - 1 or
r == 0 or r == self.__length - 1
):
self.__map[r][c] == WALL
def grow_map(self):
self.__generation(1, 2, -1)
def reduce_map(self):
self.__generation(1, 7, -1)
def gen_map(self, mode='default'):
if mode == 'room':
# One large cavern room
self.__generation(4, 5, -1)
self.__join_rooms()
self.__generation(1, 5, -1)
else:
# Windey passages.
# Repeat 4: W?(p) = R1(p) ? 5 || R2(p) ? 2
# Repeat 3: W?(p) = R1(p) ? 5
# We do the above, with a cave join pass right before the final
# iteration. This helps smooth out any sharp edges after the join
# pass.
self.__generation(4, 5, 2)
self.__generation(2, 5, -1)
self.__join_rooms()
self.__generation(1, 5, -1)
def __generation(self, count, r1_cutoff, r2_cutoff):
while (count > 0):
self.__buf_map = [[WALL for i in xrange(self.__width)]
for j in xrange(self.__length)]
self.__gen_walls(self.__buf_map)
self.__gen_walls(self.__map)
for r in xrange(1, self.__length - 1):
for c in xrange(1, self.__width - 1):
adjcount_r1 = self.__adj_wall_count(r, c, 1)
adjcount_r2 = self.__adj_wall_count(r, c, 2)
if(adjcount_r1 >= r1_cutoff or
adjcount_r2 <= r2_cutoff):
self.__buf_map[r][c] = WALL
else:
self.__buf_map[r][c] = FLOOR
self.__map = list(self.__buf_map)
count -= 1
def __gen_initial_map(self, fillprob):
def rwall(fillprob):
if (random() < fillprob):
return WALL
return FLOOR
self.__map = [[rwall(fillprob) for i in xrange(self.__width)]
for j in xrange(self.__length)]
self.__gen_walls(self.__map)
def __gen_walls(self, a_map):
for j in range(0, self.__length):
a_map[j][0] = WALL
a_map[j][self.__width - 1] = WALL
for j in range(0, self.__width):
a_map[0][j] = WALL
a_map[self.__length - 1][j] = WALL
# Force the exits to be floor. We grow them out from the edge a bit to
# make sure they don't get sealed off.
for pos in self.__exits:
a_map[pos[0]][pos[1]] = FLOOR
for pos2 in ((-1, 0), (1, 0), (0, -1), (0, 1),
(-2, 0), (2, 0), (0, -2), (0, 2)):
p = (pos[0] + pos2[0], pos[1] + pos2[1])
if (p[0] < 1 or p[1] < 1):
continue
if (
p[0] >= self.__width - 1 or
p[1] >= self.__length - 1
):
continue
a_map[p[0]][p[1]] = FLOOR
def __adj_flr_count(self, sr, sc):
count = 0
for pos in ((-1, 0), (1, 0), (0, -1), (0, 1)):
p = (sr + pos[0], sc + pos[1])
if (p[0] < 0 or p[1] < 0):
continue
if (
p[0] > self.__width - 1 or
p[1] > self.__length - 1
):
continue
if (self.__map[p[0]][p[1]] == FLOOR):
count += 1
return count
def __adj_wall_count(self, sr, sc, rng=1):
count = 0
for r in xrange(-rng, rng + 1):
for c in xrange(-rng, rng + 1):
# if (r == 0 and c == 0):
# continue
if (abs(r) == 2 and abs(c) == 2):
continue
if (sr + r < 0 or sc + c < 0):
continue
if (sr + r >= self.__length or sc + c >= self.__width):
continue
if self.__map[sr + r][sc + c] == WALL:
count += 1
return count
def __join_rooms(self):
# Divide all cells into joined sets
for r in xrange(0, self.__length):
for c in xrange(0, self.__width):
if self.__map[r][c] != WALL:
self.__union_adj_sqr(r, c)
all_caves = self.__ds.split_sets()
while len(all_caves) > 1:
self.__join_points(all_caves[choice(all_caves.keys())][0])
all_caves = self.__ds.split_sets()
def __union_adj_sqr(self, sr, sc):
loc = (sr, sc)
root1 = self.__ds.find(loc)
# A cell is connected to other cells only in cardinal directions.
# (diagonals don't count for movement).
for pos in ((-1, 0), (1, 0), (0, -1), (0, 1)):
if (sr + pos[0] < 0 or sc + pos[1] < 0):
continue
if (
sr + pos[0] >= self.__length or
sc + pos[1] >= self.__width
):
continue
nloc = (sr + pos[0], sc + pos[1])
if self.__map[nloc[0]][nloc[1]] == FLOOR:
root2 = self.__ds.find(nloc)
if root1 != root2:
self.__ds.union(root1, root2)
def __join_points(self, pt1):
next_pt = pt1
while True:
dir = self.__get_tunnel_dir(pt1, self.__cpt)
move = randrange(0, 3)
if move == 0:
next_pt = (pt1[0] + dir[0], pt1[1])
elif move == 1:
next_pt = (pt1[0], pt1[1] + dir[1])
else:
next_pt = (pt1[0] + dir[0], pt1[1] + dir[1])
root1 = self.__ds.find(next_pt)
root2 = self.__ds.find(pt1)
if root1 != root2:
self.__ds.union(root1, root2)
for pos in ((0, 0), (-1, 0), (1, 0), (0, -1), (0, 1)):
if (
next_pt[0] + pos[0] < 0 or next_pt[1] + pos[1] < 0 or
next_pt[0] + pos[0] >= self.__length or
next_pt[1] + pos[1] >= self.__width
):
continue
if (self.__map[next_pt[0] + pos[0]][next_pt[1] + pos[1]] == WALL):
self.__map[
next_pt[0] +
pos[0]][
next_pt[1] +
pos[1]] = TUNNEL
if self.__stop_drawing(pt1, next_pt, self.__cpt):
return
pt1 = next_pt
def __stop_drawing(self, pt, npt, cpt):
if self.__ds.find(npt) == self.__ds.find(cpt):
return 1
if (
self.__ds.find(pt) != self.__ds.find(npt) and
self.__map[npt[0]][npt[1]] != WALL
):
return 1
return 0
def __get_tunnel_dir(self, pt1, pt2):
if pt1[0] < pt2[0]:
h_dir = +1
elif pt1[0] > pt2[0]:
h_dir = -1
else:
h_dir = 0
if pt1[1] < pt2[1]:
v_dir = +1
elif pt1[1] > pt2[1]:
v_dir = -1
else:
v_dir = 0
return (h_dir, v_dir)