junglejourney
view tools/maps/make_maps.py @ 233:26066c4a9925
Added a banner to each level map.
| author | David Boddie <david@boddie.org.uk> |
|---|---|
| date | Fri Aug 17 00:12:23 2012 +0200 |
| parents | e24e7d33b5e7 |
| children |
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, read_xpm
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 xf = 2
40 yf = 1
42 scaled_tile_size = (int(tile_size[0] * xf), int(tile_size[1] * yf))
44 class Mapper:
46 start_rooms = {100: (5, 5), 36: (0, 0), 44: (9, 7), 4: (7, 0), 5: (5, 10),
47 8: (0, 8), 10: (0, 10), 17: (7, 10), 26: (0, 9),
48 33: (10, 0), 127: (0, 0), 144: (10, 8), 183: (5, 1),
49 239: (3, 8)}
50 exit_rooms = {100: (7, 0), 36: (7, 0), 4: (5, 10), 5: (9, 6),
51 8: (3, 0), 10: (10, 2), 17: (9, 0), 26: (10, 4),
52 33: (2, 10), 144: (0, 10), 183: (3, 9), 239: (9, 0)}
53 key_rooms = {100: (1, 0), 17: (0, 0), 26: (9, 0), 33: (10, 6), 144: (1, 4),
54 183: (10, 6), 239: (5, 2)}
55 extra_life_rooms = {17: (2, 0), 26: (9, 4)}
57 levels = {100: 0, 239: 1, 183: 2, 144: 3}
59 treasure_table = [6, 5, 7, 1, 1, 5, 2, 7, 6, 2, 1, 7, 1, 7, 8, 7,
60 0, 7, 6, 7, 7, 7, 5, 0, 6, 3, 7, 7, 5, 7, 5, 0]
62 treasure_x = [3, 2, 4, 8, 2, 5, 4, 1, 3, 8, 6, 5, 7, 1, 7, 6]
63 treasure_y = [1, 3, 7, 7, 2, 3, 6, 1, 4, 6, 8, 5, 5, 4, 8, 2]
65 exit_room_offsets = [35, 66, 63, 56, 34, 44, 64, 33, 36, 55, 65, 53,
66 45, 46, 54, 43]
68 def __init__(self, map_width, map_height, room_width, room_height, seed,
69 image, images, wall_tile, floor_tiles):
71 self.map_width = map_width
72 self.map_height = map_height
73 self.room_width = room_width
74 self.room_height = room_height
75 self.rooms = {}
76 self.exits = {}
77 self.visited = set()
78 self.seed = seed
79 self.image = image
80 self.images = images
81 self.wall_tile = wall_tile
82 self.floor_tiles = floor_tiles
84 self.add_objects()
86 def add_objects(self):
88 self.objects = []
90 first = (self.seed + 1) & 31
91 second = (self.seed + 2) & 31
92 gen = series.unlimited_values(first, second)
94 # Find the level associated with the seed used for the map.
95 level = self.levels.get(self.seed, 0)
97 k = 0
98 for i in range(self.map_height):
100 for j in range(self.map_width):
102 if self.key_rooms.get(self.seed, ()) == (j, i):
104 self.objects.append(5)
106 else:
108 item = gen.next() & 15
109 if item == 0:
110 self.objects.append(0)
111 else:
112 treasure = self.treasure_table[(item + k) & 31]
113 if 0 <= treasure <= 3:
114 if treasure <= level + 1:
115 self.objects.append(treasure + 1)
116 else:
117 self.objects.append(0)
118 else:
119 self.objects.append(treasure + 1)
121 k += 1
123 def make_room(self, i, j):
125 first = j
126 second = self.seed - i
128 gen = series.unlimited_values(first, second)
129 for k in range(10):
130 gen.next()
132 special_rooms = {self.start_rooms.get(self.seed, ()): 7,
133 self.exit_rooms.get(self.seed, ()): 7,
134 self.key_rooms.get(self.seed, ()): 1}
136 if (j, i) in special_rooms:
138 x = y = 1
139 while True:
140 if (x, y) in [(1, 1), (1, 8), (8, 1), (8, 8)]:
141 yield special_rooms[(j, i)]
143 elif (j, i) == self.exit_rooms.get(self.seed, ()):
144 position = self.exit_room_offsets[(i ^ j) & 15]
145 if x == position % 10 and y == position / 10:
146 yield 8
147 else:
148 yield 0
150 else:
151 yield 0
153 x += 1
154 if x == 9:
155 x = 1
156 y += 1
158 else:
159 while True:
160 yield (gen.next() % 9) & 7
162 def top_exit(self, i, j):
164 if i == 0:
165 return True
166 elif i & 7 == j & 7:
167 return False
169 return ((i ^ j) + i) == j
171 def right_exit(self, i, j):
173 if j == self.map_width - 1:
174 return True
176 return self.left_exit(i, j + 1)
178 def bottom_exit(self, i, j):
180 if i == self.map_height - 1:
181 return True
183 return self.top_exit(i + 1, j)
185 def left_exit(self, i, j):
187 if j == 0:
188 return True
189 elif i & 3 == j & 3:
190 return False
192 return ((i | j) ^ j) == i
194 def make_room_image(self, i, j):
196 im = Image.new("P", (self.room_width * tile_size[0],
197 self.room_height * tile_size[1]), 0)
199 rows, exits = self.read_room((j, i))
201 for y in range(0, self.room_height):
202 for x in range(0, self.room_width):
204 value = rows[y][x]
205 im.paste(self.images[value], (x * tile_size[0], y * tile_size[1]))
207 item, x, y = self.item_for_room(rows, i, j)
208 if item is not None:
210 im.paste(treasure_images[item],
211 (x * tile_size[0],
212 y * tile_size[1] + (tile_size[1] - item_size[1])/2))
214 return im
216 def item_for_room(self, rows, i, j):
218 item = self.objects[i * self.map_height + j]
219 if item != 0:
221 item -= 1
222 k = ((i ^ j) + item) & 15
223 a = 15
224 while a >= 0:
226 x, y = self.treasure_x[k], self.treasure_y[k]
228 if rows[y][x] & 0x7f == 0:
230 return item, x, y
231 break
233 if k > 0:
234 k -= 1
235 else:
236 k = 15
237 a -= 1
239 return None, 0, 0
241 def read_room(self, room):
243 gen = self.make_room(room[1], room[0])
245 if self.rooms.has_key(room):
246 rows = self.rooms[room]
247 exits = self.exits[room]
248 else:
249 exits = []
250 exits.append(self.top_exit(room[1], room[0]))
251 exits.append(self.right_exit(room[1], room[0]))
252 exits.append(self.bottom_exit(room[1], room[0]))
253 exits.append(self.left_exit(room[1], room[0]))
255 rows = []
256 cx = self.room_width/2 - 2
257 cy = self.room_height/2 - 2
259 if exits[0]:
260 rows.append([self.wall_tile]*self.room_width)
261 else:
262 rows.append([self.wall_tile]*cx + [0]*(self.room_width - 2*cx) + [self.wall_tile]*cx)
264 if self.levels.get(self.seed) == 3 and room == (2, 0):
266 rows[-1][4] = 5
267 rows[-1][5] = 6
269 for ry in range(1, self.room_height - 1):
270 row = []
271 if exits[3] or ry < cy or ry > self.room_height - cy - 1:
272 row.append(self.wall_tile)
273 else:
274 row.append(0)
276 for rx in range(1, self.room_width - 1):
277 row.append(tile_values_map[gen.next()])
279 if exits[1] or ry < cy or ry > self.room_height - cy - 1:
280 row.append(self.wall_tile)
281 else:
282 row.append(0)
283 rows.append(row)
285 if exits[2]:
286 rows.append([self.wall_tile]*self.room_width)
287 else:
288 rows.append([self.wall_tile]*cx + [0]*(self.room_width - 2*cx) + [self.wall_tile]*cx)
290 self.rooms[room] = rows
291 self.exits[room] = exits
293 return rows, exits
295 def find_map_extent(self, room, x = None, y = None):
297 rows, exits = self.read_room(room)
299 if x is None:
300 x = self.room_width/2 - 1
301 y = self.room_height/2 - 1
303 places = [(x, y)]
305 # Use a recursive algorithm to explore the room, but don't recursively
306 # call this function until we leave the room.
308 while places:
310 x, y = places.pop()
312 if rows[y][x] not in self.floor_tiles:
313 # The square has already been visited, so backtrack.
314 continue
315 else:
316 # Mark this room as visited.
317 self.visited.add(room)
318 #self.image.paste(visited,
319 # ((room[0] * self.room_width + x) * tile_size[0] + room[0] + 3 * tile_size[0]/8,
320 # (room[1] * self.room_height + y) * tile_size[1] + room[1] + 3 * tile_size[1]/8))
322 # Mark this square as visited.
323 rows[y][x] = rows[y][x] | 0x80
325 # Try to move to adjacent squares.
326 if x > 0:
327 if rows[y][x-1] in self.floor_tiles:
328 places.append((x - 1, y))
329 elif room[0] > 0:
330 self.find_map_extent((room[0] - 1, room[1]),
331 self.room_width - 1, y)
333 if x < self.room_width - 1:
334 if rows[y][x+1] in self.floor_tiles:
335 places.append((x + 1, y))
336 elif room[0] < self.map_width - 1:
337 self.find_map_extent((room[0] + 1, room[1]), 0, y)
339 if y > 0:
340 if rows[y-1][x] in self.floor_tiles:
341 places.append((x, y - 1))
342 elif room[1] > 0:
343 self.find_map_extent((room[0], room[1] - 1),
344 x, self.room_height - 1)
346 if y < self.room_height - 1:
347 if rows[y+1][x] in self.floor_tiles:
348 places.append((x, y + 1))
349 elif room[1] < self.map_height - 1:
350 self.find_map_extent((room[0], room[1] + 1), x, 0)
352 def fade(image, x0, y0, w, h):
354 for i in range(h):
356 y = y0 + i
357 x = x0 + y % 2
358 while x < x0 + w:
359 image.putpixel((x, y), 0)
360 x += 2
362 def select_images(seed):
364 images = image_sets.get(seed, default_image_set)
365 wall_tile = 2
366 floor_tiles = []
368 for i in range(len(images)):
369 if images[i] in (blank,):
370 floor_tiles.append(i)
372 return images, wall_tile, floor_tiles
374 def make_map(name, width, height, room_width, room_height, seed):
376 images, wall_tile, floor_tiles = select_images(seed)
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 title_image = Image.fromstring("P", (128, 48), "".join(
466 read_xpm("../../images/title-screen.xpm", [(".", "\x00"), ("#", "\x01"), ("+", "\x02"), ("@", "\x03")])))
467 title_image = title_image.resize((title_image.size[0]*4, title_image.size[1]*4),
468 Image.LINEAR)
470 overlay_image = Image.fromstring("P", (128, 115), "".join(
471 read_xpm("../../images/overlay.xpm", [(".", "\x00"), ("#", "\x01"), ("@", "\x02"), ("+", "\x03")])))
472 overlay_image = overlay_image.resize((overlay_image.size[0]*4, overlay_image.size[1]*4),
473 Image.LINEAR)
475 border = Image.new("P", (scaled_tile_size[0] * 18 + 1,
476 title_image.size[1] + overlay_image.size[1] + scaled_tile_size[1]), 0)
478 banner_positions = [(-scaled_tile_size[0] - border.size[0], -scaled_tile_size[1] - border.size[1]),
479 (scaled_tile_size[0], -scaled_tile_size[1] - border.size[1]),
480 (scaled_tile_size[0], scaled_tile_size[1]),
481 (-scaled_tile_size[0] - border.size[0], scaled_tile_size[1])]
483 for seed in 100, 239, 183, 144:
485 file_name = "%s%i%s" % (stem, level, suffix)
486 make_map(file_name, width, height, room_width, room_height, seed)
488 im = Image.open(file_name)
489 bx, by = banner_positions[level - 1]
490 if bx < 0:
491 bx = im.size[0] + bx
492 if by < 0:
493 by = im.size[1] + by
495 im.paste(border, (bx, by))
496 im.paste(title_image, (bx + scaled_tile_size[0], by + int(scaled_tile_size[1] * 0.75)))
497 im.paste(overlay_image, (bx + scaled_tile_size[0], by + int(scaled_tile_size[1] * 0.75) + title_image.size[1]))
498 im.save(file_name)
500 print "Created %s" % file_name
501 level += 1
503 sys.exit()
