junglejourney

view tools/maps/make_maps.py @ 231:e24e7d33b5e7

Added license headers. Scaled each room image horizontally to create more authentic graphics.
author David Boddie <david@boddie.org.uk>
date Sun Feb 05 02:09:50 2012 +0100
parents f7b16fb00dda
children 26066c4a9925
line source
1 #!/usr/bin/env python
3 """
4 Copyright (C) 2011 David Boddie <david@boddie.org.uk>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """
20 import os, sys
21 import Image
22 import series
23 from tileimages import blank, tile_size, leaf1, leaf2, visited, flowers, \
24 flowers2, leaf4, leaf6, flowers3, leaf5, leaf3, \
25 exit, final_exit1, final_exit2, item_size, player, \
26 treasure_images
28 image_sets = {
29 100: [blank, flowers, leaf1, leaf2, exit],
30 239: [blank, flowers2, leaf6, leaf4, exit],
31 183: [blank, flowers2, leaf6, leaf4, exit],
32 144: [blank, flowers3, leaf5, leaf3, exit, final_exit1, final_exit2]
33 }
35 default_image_set = [blank, flowers, leaf1, leaf2]
37 tile_values_map = [0, 1, 0, 0, 0, 0, 2, 3, 4, 5, 6]
39 class Mapper:
41 start_rooms = {100: (5, 5), 36: (0, 0), 44: (9, 7), 4: (7, 0), 5: (5, 10),
42 8: (0, 8), 10: (0, 10), 17: (7, 10), 26: (0, 9),
43 33: (10, 0), 127: (0, 0), 144: (10, 8), 183: (5, 1),
44 239: (3, 8)}
45 exit_rooms = {100: (7, 0), 36: (7, 0), 4: (5, 10), 5: (9, 6),
46 8: (3, 0), 10: (10, 2), 17: (9, 0), 26: (10, 4),
47 33: (2, 10), 144: (0, 10), 183: (3, 9), 239: (9, 0)}
48 key_rooms = {100: (1, 0), 17: (0, 0), 26: (9, 0), 33: (10, 6), 144: (1, 4),
49 183: (10, 6), 239: (5, 2)}
50 extra_life_rooms = {17: (2, 0), 26: (9, 4)}
52 levels = {100: 0, 239: 1, 183: 2, 144: 3}
54 treasure_table = [6, 5, 7, 1, 1, 5, 2, 7, 6, 2, 1, 7, 1, 7, 8, 7,
55 0, 7, 6, 7, 7, 7, 5, 0, 6, 3, 7, 7, 5, 7, 5, 0]
57 treasure_x = [3, 2, 4, 8, 2, 5, 4, 1, 3, 8, 6, 5, 7, 1, 7, 6]
58 treasure_y = [1, 3, 7, 7, 2, 3, 6, 1, 4, 6, 8, 5, 5, 4, 8, 2]
60 exit_room_offsets = [35, 66, 63, 56, 34, 44, 64, 33, 36, 55, 65, 53,
61 45, 46, 54, 43]
63 def __init__(self, map_width, map_height, room_width, room_height, seed,
64 image, images, wall_tile, floor_tiles):
66 self.map_width = map_width
67 self.map_height = map_height
68 self.room_width = room_width
69 self.room_height = room_height
70 self.rooms = {}
71 self.exits = {}
72 self.visited = set()
73 self.seed = seed
74 self.image = image
75 self.images = images
76 self.wall_tile = wall_tile
77 self.floor_tiles = floor_tiles
79 self.add_objects()
81 def add_objects(self):
83 self.objects = []
85 first = (self.seed + 1) & 31
86 second = (self.seed + 2) & 31
87 gen = series.unlimited_values(first, second)
89 # Find the level associated with the seed used for the map.
90 level = self.levels.get(self.seed, 0)
92 k = 0
93 for i in range(self.map_height):
95 for j in range(self.map_width):
97 if self.key_rooms.get(self.seed, ()) == (j, i):
99 self.objects.append(5)
101 else:
103 item = gen.next() & 15
104 if item == 0:
105 self.objects.append(0)
106 else:
107 treasure = self.treasure_table[(item + k) & 31]
108 if 0 <= treasure <= 3:
109 if treasure <= level + 1:
110 self.objects.append(treasure + 1)
111 else:
112 self.objects.append(0)
113 else:
114 self.objects.append(treasure + 1)
116 k += 1
118 def make_room(self, i, j):
120 first = j
121 second = self.seed - i
123 gen = series.unlimited_values(first, second)
124 for k in range(10):
125 gen.next()
127 special_rooms = {self.start_rooms.get(self.seed, ()): 7,
128 self.exit_rooms.get(self.seed, ()): 7,
129 self.key_rooms.get(self.seed, ()): 1}
131 if (j, i) in special_rooms:
133 x = y = 1
134 while True:
135 if (x, y) in [(1, 1), (1, 8), (8, 1), (8, 8)]:
136 yield special_rooms[(j, i)]
138 elif (j, i) == self.exit_rooms.get(self.seed, ()):
139 position = self.exit_room_offsets[(i ^ j) & 15]
140 if x == position % 10 and y == position / 10:
141 yield 8
142 else:
143 yield 0
145 else:
146 yield 0
148 x += 1
149 if x == 9:
150 x = 1
151 y += 1
153 else:
154 while True:
155 yield (gen.next() % 9) & 7
157 def top_exit(self, i, j):
159 if i == 0:
160 return True
161 elif i & 7 == j & 7:
162 return False
164 return ((i ^ j) + i) == j
166 def right_exit(self, i, j):
168 if j == self.map_width - 1:
169 return True
171 return self.left_exit(i, j + 1)
173 def bottom_exit(self, i, j):
175 if i == self.map_height - 1:
176 return True
178 return self.top_exit(i + 1, j)
180 def left_exit(self, i, j):
182 if j == 0:
183 return True
184 elif i & 3 == j & 3:
185 return False
187 return ((i | j) ^ j) == i
189 def make_room_image(self, i, j):
191 im = Image.new("P", (self.room_width * tile_size[0],
192 self.room_height * tile_size[1]), 0)
194 rows, exits = self.read_room((j, i))
196 for y in range(0, self.room_height):
197 for x in range(0, self.room_width):
199 value = rows[y][x]
200 im.paste(self.images[value], (x * tile_size[0], y * tile_size[1]))
202 item, x, y = self.item_for_room(rows, i, j)
203 if item is not None:
205 im.paste(treasure_images[item],
206 (x * tile_size[0],
207 y * tile_size[1] + (tile_size[1] - item_size[1])/2))
209 return im
211 def item_for_room(self, rows, i, j):
213 item = self.objects[i * self.map_height + j]
214 if item != 0:
216 item -= 1
217 k = ((i ^ j) + item) & 15
218 a = 15
219 while a >= 0:
221 x, y = self.treasure_x[k], self.treasure_y[k]
223 if rows[y][x] & 0x7f == 0:
225 return item, x, y
226 break
228 if k > 0:
229 k -= 1
230 else:
231 k = 15
232 a -= 1
234 return None, 0, 0
236 def read_room(self, room):
238 gen = self.make_room(room[1], room[0])
240 if self.rooms.has_key(room):
241 rows = self.rooms[room]
242 exits = self.exits[room]
243 else:
244 exits = []
245 exits.append(self.top_exit(room[1], room[0]))
246 exits.append(self.right_exit(room[1], room[0]))
247 exits.append(self.bottom_exit(room[1], room[0]))
248 exits.append(self.left_exit(room[1], room[0]))
250 rows = []
251 cx = self.room_width/2 - 2
252 cy = self.room_height/2 - 2
254 if exits[0]:
255 rows.append([self.wall_tile]*self.room_width)
256 else:
257 rows.append([self.wall_tile]*cx + [0]*(self.room_width - 2*cx) + [self.wall_tile]*cx)
259 if self.levels.get(self.seed) == 3 and room == (2, 0):
261 rows[-1][4] = 5
262 rows[-1][5] = 6
264 for ry in range(1, self.room_height - 1):
265 row = []
266 if exits[3] or ry < cy or ry > self.room_height - cy - 1:
267 row.append(self.wall_tile)
268 else:
269 row.append(0)
271 for rx in range(1, self.room_width - 1):
272 row.append(tile_values_map[gen.next()])
274 if exits[1] or ry < cy or ry > self.room_height - cy - 1:
275 row.append(self.wall_tile)
276 else:
277 row.append(0)
278 rows.append(row)
280 if exits[2]:
281 rows.append([self.wall_tile]*self.room_width)
282 else:
283 rows.append([self.wall_tile]*cx + [0]*(self.room_width - 2*cx) + [self.wall_tile]*cx)
285 self.rooms[room] = rows
286 self.exits[room] = exits
288 return rows, exits
290 def find_map_extent(self, room, x = None, y = None):
292 rows, exits = self.read_room(room)
294 if x is None:
295 x = self.room_width/2 - 1
296 y = self.room_height/2 - 1
298 places = [(x, y)]
300 # Use a recursive algorithm to explore the room, but don't recursively
301 # call this function until we leave the room.
303 while places:
305 x, y = places.pop()
307 if rows[y][x] not in self.floor_tiles:
308 # The square has already been visited, so backtrack.
309 continue
310 else:
311 # Mark this room as visited.
312 self.visited.add(room)
313 #self.image.paste(visited,
314 # ((room[0] * self.room_width + x) * tile_size[0] + room[0] + 3 * tile_size[0]/8,
315 # (room[1] * self.room_height + y) * tile_size[1] + room[1] + 3 * tile_size[1]/8))
317 # Mark this square as visited.
318 rows[y][x] = rows[y][x] | 0x80
320 # Try to move to adjacent squares.
321 if x > 0:
322 if rows[y][x-1] in self.floor_tiles:
323 places.append((x - 1, y))
324 elif room[0] > 0:
325 self.find_map_extent((room[0] - 1, room[1]),
326 self.room_width - 1, y)
328 if x < self.room_width - 1:
329 if rows[y][x+1] in self.floor_tiles:
330 places.append((x + 1, y))
331 elif room[0] < self.map_width - 1:
332 self.find_map_extent((room[0] + 1, room[1]), 0, y)
334 if y > 0:
335 if rows[y-1][x] in self.floor_tiles:
336 places.append((x, y - 1))
337 elif room[1] > 0:
338 self.find_map_extent((room[0], room[1] - 1),
339 x, self.room_height - 1)
341 if y < self.room_height - 1:
342 if rows[y+1][x] in self.floor_tiles:
343 places.append((x, y + 1))
344 elif room[1] < self.map_height - 1:
345 self.find_map_extent((room[0], room[1] + 1), x, 0)
347 def fade(image, x0, y0, w, h):
349 for i in range(h):
351 y = y0 + i
352 x = x0 + y % 2
353 while x < x0 + w:
354 image.putpixel((x, y), 0)
355 x += 2
357 def select_images(seed):
359 images = image_sets.get(seed, default_image_set)
360 wall_tile = 2
361 floor_tiles = []
363 for i in range(len(images)):
364 if images[i] in (blank,):
365 floor_tiles.append(i)
367 return images, wall_tile, floor_tiles
369 def make_map(name, width, height, room_width, room_height, seed):
371 images, wall_tile, floor_tiles = select_images(seed)
373 xf = 2
374 yf = 1
376 scaled_tile_size = (int(tile_size[0] * xf), int(tile_size[1] * yf))
378 scaled_room_size = (int(room_width * scaled_tile_size[0]),
379 int(room_height * scaled_tile_size[1]))
381 im = Image.new("P", (width * scaled_room_size[0] + (width - 1),
382 height * scaled_room_size[1] + (height - 1)), 0xffffff)
383 black = (0,0,0)
384 red = (255,0,0)
385 green = (0,255,0)
386 yellow = (255,255,0)
387 blue = (0,0,255)
388 magenta = (255,0,255)
389 cyan = (0,255,255)
390 white = (255,255,255)
391 im.putpalette(black + red + green + yellow + blue + magenta + cyan + white)
393 room_palettes = [1, 6, 5, 7]
395 mapper = Mapper(width, height, room_width, room_height, seed, im, images,
396 wall_tile, floor_tiles)
398 start_room = mapper.start_rooms.get(seed)
400 for i in range(height):
401 for j in range(width):
403 room_image = mapper.make_room_image(i, j)
405 # Change the palette for this room.
406 room_string = room_image.tostring()
408 # Replace logical colour 1 with 1, 3, 5 or 7.
409 new_colour = room_palettes[(i ^ j) & 3]
410 if new_colour != 1:
411 room_string = room_string.replace("\x01", chr(new_colour))
413 room_image = Image.fromstring("P", (room_width * tile_size[0],
414 room_height * tile_size[1]),
415 room_string)
416 room_image.putpalette(black + red + green + yellow + blue + magenta + cyan + white)
418 if (j, i) == start_room:
419 room_image.paste(player,
420 ((tile_size[0] * room_width/2) - tile_size[0]/4,
421 (tile_size[1] * room_height/2) - tile_size[1]/2))
423 room_image = room_image.resize(scaled_room_size, Image.BILINEAR)
425 im.paste(room_image, (j * scaled_room_size[0] + j,
426 i * scaled_room_size[1] + i))
428 if start_room:
430 mapper.find_map_extent(start_room)
432 for i in range(height):
433 for j in range(width):
435 if (j,i) not in mapper.visited:
437 fade(im, j * scaled_room_size[0] + j,
438 i * scaled_room_size[1] + i,
439 scaled_room_size[0],
440 scaled_room_size[1])
442 im.save(name)
445 if __name__ == "__main__":
447 if len(sys.argv) != 2:
449 sys.stderr.write("Usage: %s <file name template>\n\n" % sys.argv[0])
450 sys.stderr.write("Creates images for the four maps used in the release version of Jungle Journey.\n"
451 "The file name of each image is derived from the template.\n"
452 "For example, specifying a template called map.png will result in files being\n"
453 "created called map1.png, map2.png, map3.png and map4.png.\n\n")
454 sys.exit(1)
456 width = height = 11
457 name = sys.argv[1]
458 room_width = 10
459 room_height = 10
461 start_room = (width/2, height/2)
462 stem, suffix = os.path.splitext(name)
463 level = 1
465 for seed in 100, 239, 183, 144:
467 file_name = "%s%i%s" % (stem, level, suffix)
468 make_map(file_name, width, height, room_width, room_height, seed)
470 print "Created %s" % file_name
471 level += 1
473 sys.exit()