source: tools/area_editor.py@ 545:15f066cc5bf1

Last change on this file since 545:15f066cc5bf1 was 545:15f066cc5bf1, checked in by Neil Muller <drnlmuller@…>, 8 years ago

pyflakes cleanup

  • Property exe set to *
File size: 48.1 KB
Line 
1#!/usr/bin/env python
2
3# The basic area editor
4#
5# To edit an existing level, use
6# editor levelname
7#
8# To create a new level:
9#
10# editor levelname <xsize> <ysize>
11# (size specified in pixels
12#
13
14import os
15import sys
16
17import pygame
18import pygame.locals as pgl
19
20sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
21
22import pymunk
23
24from albow.root import RootWidget
25from albow.widget import Widget
26from albow.controls import Button, Label, CheckBox
27from albow.dialogs import alert, Dialog, ask
28from albow.layout import Row
29from albow.fields import TextField
30from albow.table_view import TableView, TableColumn
31
32from nagslang.options import parse_args
33from nagslang.constants import SCREEN
34from nagslang.level import Level, POLY_COLORS, LINE_COLOR
35from nagslang.yamlish import load_s
36import nagslang.enemies as ne
37import nagslang.game_object as ngo
38import nagslang.collectable as collectable
39import nagslang.puzzle as np
40
41# layout constants
42MENU_BUTTON_HEIGHT = 35
43MENU_PAD = 4
44MENU_HALF_PAD = MENU_PAD // 2
45MENU_LEFT = SCREEN[0] + MENU_HALF_PAD
46MENU_WIDTH = 200 - MENU_PAD
47MENU_HALF_WIDTH = MENU_WIDTH // 2 - MENU_HALF_PAD
48
49BUTTON_RECT = pygame.rect.Rect(0, 0, MENU_WIDTH, MENU_BUTTON_HEIGHT)
50HALF_BUTTON_RECT = pygame.rect.Rect(0, 0, MENU_HALF_WIDTH, MENU_BUTTON_HEIGHT)
51CHECK_RECT = pygame.rect.Rect(0, 0, MENU_BUTTON_HEIGHT // 2,
52 MENU_BUTTON_HEIGHT // 2)
53
54
55class TestWorld(object):
56
57 def __init__(self):
58 self.level_state = {}
59 self.inventory = {}
60
61
62def distance(tup1, tup2):
63 return (tup1[0] - tup2[0]) ** 2 + (tup1[1] - tup2[1]) ** 2
64
65
66class EditorLevel(Level):
67
68 def __init__(self, name, x=800, y=600):
69 world = TestWorld()
70 super(EditorLevel, self).__init__(name, world)
71 self.x = x
72 self.y = y
73 # Lookup initiliasition info from the objects
74 self.lookup = {}
75 self._move_poly = None
76
77 def _in_bounds(self, pos):
78 if pos[0] < 0 or pos[0] >= self.x:
79 return False
80 if pos[1] < 0 or pos[1] >= self.y:
81 return False
82 return True
83
84 def load(self, space):
85 super(EditorLevel, self).load(space)
86 # Needed to fill in the lookup dict
87 self.reset_objs()
88
89 def point_to_pygame(self, pos):
90 # inverse of point_to_pymunk
91 # (this is also the same as point_to_pymunk, but an additional
92 # function for sanity later in pyweek).
93 return (pos[0], self.y - pos[1])
94
95 def point_to_pymunk(self, pos):
96 # inverse of point_to_pygame
97 # (this is also the same as point_to_pygame, but an additional
98 # function for sanity later in pyweek).
99 return (pos[0], self.y - pos[1])
100
101 def add_point(self, poly_index, pos):
102 self.polygons.setdefault(poly_index, [])
103 if not self._in_bounds(pos):
104 return False
105 if not self.polygons[poly_index]:
106 point = self.point_to_pymunk(pos)
107 self.polygons[poly_index].append(point)
108 else:
109 add_pos = self.point_to_pymunk(pos)
110 self.polygons[poly_index].append(add_pos)
111 return True
112
113 def delete_point(self, index):
114 if index in self.polygons and len(self.polygons[index]) > 0:
115 self.polygons[index].pop()
116
117 def close_poly(self, index):
118 """Attempts to close the current polygon.
119
120 We allow a small additional step to close the polygon, but
121 it's limited as it's a magic point addition"""
122 if len(self.polygons[index]) < 2:
123 # Too small
124 return False
125 first = self.polygons[index][0]
126 if self.add_point(index, self.point_to_pygame(first)):
127 return True
128 return False
129
130 def add_line(self, start_pos, end_pos):
131 startpoint = self.point_to_pymunk(start_pos)
132 endpoint = self.point_to_pymunk(end_pos)
133 self.lines.append([startpoint, endpoint])
134
135 def draw(self, mouse_pos, mouse_poly, filled, draw_cand_line, start_pos,
136 move_point_mode, move_poly_mode, move_point, zoom_factor,
137 level_widget):
138 self._draw_background(True)
139 # Draw polygons as needed for the editor
140 line_width = int(2 * zoom_factor)
141 if filled:
142 self._draw_exterior(True)
143 for index, polygon in self.polygons.items():
144 color = POLY_COLORS.get(index, pygame.color.THECOLORS['black'])
145 if move_point_mode and index == self._move_poly and move_point:
146 pointlist = [p for p in polygon]
147 pointlist = [self.point_to_pygame(p) if p != move_point else
148 mouse_pos for p in pointlist]
149 pygame.draw.lines(self._surface, color, False, pointlist,
150 line_width)
151 break
152 if move_poly_mode and index == self._move_poly and move_point:
153 new_point = self.point_to_pymunk(mouse_pos)
154 pointlist = [self.point_to_pygame(p)
155 for p in self.translate_poly(
156 polygon, move_point, new_point)]
157 pygame.draw.lines(self._surface, color, False, pointlist,
158 line_width)
159 #break
160 if len(polygon) > 1:
161 pointlist = [self.point_to_pygame(p) for p in polygon]
162 pygame.draw.lines(self._surface, color, False, pointlist,
163 line_width)
164 if index == mouse_poly and mouse_pos and len(polygon) > 0:
165 endpoint = self.point_to_pymunk(mouse_pos)
166 pygame.draw.line(self._surface, color,
167 self.point_to_pygame(polygon[-1]),
168 self.point_to_pygame(endpoint),
169 line_width // 2)
170 line_found = False # Hack for sane behaviour if lines overlap
171 for line in self.lines:
172 pointlist = [self.point_to_pygame(p) for p in line]
173 if move_point_mode and not self._move_poly and not line_found:
174 if move_point in line:
175 line_found = True
176 pointlist.remove(self.point_to_pygame(move_point))
177 pointlist.append(mouse_pos)
178 pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist,
179 line_width)
180 if draw_cand_line and start_pos and mouse_pos:
181 endpoint = level_widget.snap_to_grid(mouse_pos)
182 endpoint = self.point_to_pymunk(endpoint)
183 pointlist = [start_pos,
184 self.point_to_pygame(endpoint)]
185 pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 1)
186 return self._surface
187
188 def reset_objs(self):
189 # Reset the object state - needed when changing stuff
190 self.drawables = []
191 self.overlay_drawables = []
192 self._glue = np.PuzzleGlue()
193 for game_object_dict in self._game_objects:
194 obj = self._create_game_object(pymunk.Space(), **game_object_dict)
195 self.lookup[obj] = game_object_dict
196 for enemy_dict in self._enemies:
197 obj = self._create_enemy(pymunk.Space(), **enemy_dict)
198 self.lookup[obj] = enemy_dict
199
200 def get_class(self, classname, mod=None):
201 # Get the class given the classname
202 modules = {
203 'game_object': ngo,
204 'collectable': collectable,
205 'enemies': ne,
206 'puzzle': np,
207 }
208 if '.' in classname:
209 modname, classname = classname.split('.')
210 mod = modules[modname]
211 if mod is None:
212 mod = ngo
213 return getattr(mod, classname)
214
215 def try_new_object(self, classname, target, new, old=None):
216 if old in target:
217 target.remove(old)
218 try:
219 target.append(new)
220 self.reset_objs()
221 test_surface = pygame.surface.Surface((self.x, self.y))
222 # Check for initialisation stuff that happens in render
223 for thing in self.drawables:
224 if not isinstance(thing, ne.Enemy):
225 thing.render(test_surface)
226 return True
227 except Exception as e:
228 target.remove(new)
229 if old is not None:
230 target.append(old)
231 self.reset_objs()
232 alert("Failed to update object %s: %s" % (classname, e))
233 return False
234
235 def find_obj_at_pos(self, mouse_pos):
236 pymunk_pos = self.point_to_pymunk(mouse_pos)
237 # Search visible objects
238 for obj in self.drawables:
239 if obj.get_shape().point_query(pymunk_pos):
240 return obj
241 return None
242
243 def find_vertex(self, mouse_pos):
244 # search for vertexes closest to where we've killed
245 mindist = 1000
246 move_point = None
247 search_point = self.point_to_pymunk(mouse_pos)
248 for index, polygon in self.polygons.items():
249 for point in polygon:
250 dist = distance(point, search_point)
251 if dist < mindist:
252 mindist = dist
253 move_point = point
254 self._move_poly = index
255 # Also check lines
256 for line in self.lines:
257 for point in line:
258 dist = distance(point, search_point)
259 if dist < mindist:
260 mindist = dist
261 move_point = point
262 self._move_poly = None
263 return move_point
264
265 def translate_poly(self, poly, move_point, new_point):
266 dx = new_point[0] - move_point[0]
267 dy = new_point[1] - move_point[1]
268 new_poly = [(p[0] + dx, p[1] + dy) for p in poly]
269 return new_poly
270
271 def replace_poly(self, move_point, new_point):
272 new_point = self.point_to_pymunk(new_point)
273 if self._move_poly:
274 self.polygons[self._move_poly] = self.translate_poly(
275 self.polygons[self._move_poly], move_point, new_point)
276
277 def replace_vertex(self, old_point, new_point):
278 new_point = self.point_to_pymunk(new_point)
279 if self._move_poly:
280 new_polygon = [p if p != old_point else new_point for p in
281 self.polygons[self._move_poly]]
282 self.polygons[self._move_poly] = new_polygon
283 else:
284 for line in self.lines:
285 if old_point in line:
286 line.remove(old_point)
287 line.append(new_point)
288 break
289
290
291class ObjectTable(TableView):
292
293 columns = [TableColumn("Object", 690, 'l', '%r')]
294
295 def __init__(self, data):
296 super(ObjectTable, self).__init__(height=450)
297 self.data = sorted(data,
298 key=lambda d: (d.get('classname'), d.get('name')))
299 self.selected_row = -1
300
301 def num_rows(self):
302 return len(self.data)
303
304 def row_data(self, i):
305 data = self.data[i]
306 if 'name' in data:
307 return ('%s (%s)' % (data['classname'], data['name']), )
308 return (data['classname'], )
309
310 def row_is_selected(self, i):
311 return self.selected_row == i
312
313 def click_row(self, i, ev):
314 self.selected_row = i
315
316 def get_selection(self):
317 if self.selected_row >= 0:
318 return self.data[self.selected_row]
319 return None
320
321
322class EditClassDialog(Dialog):
323
324 def __init__(self, classname, cls, data, level_widget,
325 delete=False):
326 super(EditClassDialog, self).__init__()
327 self.level_widget = level_widget
328 self.classname = classname
329 self.rect = pygame.rect.Rect(0, 0, 900, 550)
330 title = Label("Editing %s" % classname)
331 title.rect = pygame.rect.Rect(100, 10, 600, 25)
332 self.add(title)
333 self.requires = cls.requires()
334 y = 40
335 self.fields = {}
336 index = 0
337 self.poly_field = None
338 self.needs_cleanup = False
339 for requirement, hint in self.requires:
340 label = Label(requirement)
341 label.rect = pygame.rect.Rect(40, y, 200, 25)
342 self.add(label)
343 field = TextField()
344 field.rect = pygame.rect.Rect(220, y, 400, 25)
345 self.add(field)
346 if data is not None:
347 if requirement in data:
348 field.set_text('%s' % data[requirement])
349 elif 'args' in data and requirement != 'name':
350 # NB: The ordering assumptions in requires should make
351 # this safe, but it's really, really, really fragile
352 try:
353 field.set_text('%s' % data['args'][index])
354 index += 1
355 except IndexError:
356 # Assumed to be arguments with the default value
357 pass
358 if hint.startswith('polygon'):
359 self.poly_field = field
360 self.fields[requirement] = field
361 hintlabel = Label(hint)
362 hintlabel.rect = pygame.rect.Rect(640, y, 250, 25)
363 self.add(hintlabel)
364 y += 30
365 if self.poly_field:
366 y += 40
367 label = Label("Polygon to use:")
368 label.rect = pygame.rect.Rect(40, y, 200, 25)
369 self.add(label)
370 self.poly_choice = TextField()
371 self.poly_choice.rect = pygame.Rect(280, y, 400, 25)
372 self.add(self.poly_choice)
373 y += 30
374 button = Button('Use Polygon X', action=self.get_poly)
375 button.rect = pygame.rect.Rect(350, y, 250, 30)
376 self.add(button)
377 buttons = []
378 if delete:
379 labels = ['OK', 'Delete', 'Cancel']
380 else:
381 labels = ['OK', 'Cancel']
382 for text in labels:
383 but = Button(text, action=lambda x=text: self.dismiss(x))
384 buttons.append(but)
385 row = Row(buttons)
386 row.rect = pygame.rect.Rect(250, 500, 700, 50)
387 self.add(row)
388
389 def get_poly(self):
390 try:
391 try:
392 index = int(self.poly_choice.get_text())
393 except ValueError:
394 index = 0
395 data = self.level_widget.level.polygons[index][:]
396 except KeyError:
397 data = []
398 if data:
399 # We unclose the polygon, because that's what pymunk
400 # wants
401 if data[0] == data[-1] and len(data) > 1:
402 data.pop()
403 data = [list(x) for x in data]
404 self.needs_cleanup = True
405 self.poly_field.set_text('%s' % data)
406
407 def cleanup(self):
408 if self.needs_cleanup:
409 self.level_widget.level.polygons[6] = []
410 self.level_widget.invalidate()
411
412 def get_data(self):
413 result = {}
414 result['classname'] = self.classname
415 args = []
416 # We arrange to bounce this through yaml'ish to convert
417 # stuff to the expected type
418 for val, _ in self.requires:
419 text = self.fields[val].get_text()
420 if not text:
421 # skip empty fields
422 continue
423 if val == 'name':
424 result['name'] = text
425 elif self.fields[val] == self.poly_field and text:
426 # Evil, but faster than good
427 try:
428 l = eval(text)
429 except Exception:
430 alert("Invalid polygon %s" % text)
431 self.needs_cleanup = False
432 return None
433 # Check for convexity
434 if not pymunk.util.is_convex(l):
435 alert("Invalid polygon %s - not convex" % text)
436 return None
437 if not pymunk.util.is_clockwise(l):
438 l.reverse()
439 if not pymunk.util.is_clockwise(l):
440 alert("Invalid polygon %s - unable to make clockwise"
441 % text)
442 return None
443 args.append(' - - %s' % l[0])
444 for coord in l[1:]:
445 args.append(' - %s' % coord)
446 else:
447 args.append(' - ' + text)
448 data = "args:\n" + '\n'.join(args)
449 result['args'] = load_s(data)['args']
450 return result
451
452
453class LevelWidget(Widget):
454
455 def __init__(self, level, parent):
456 super(LevelWidget, self).__init__(pygame.rect.Rect(0, 0,
457 SCREEN[0], SCREEN[1]))
458 self.level = level
459 self.pos = (0, 0)
460 self.filled_mode = False
461 self.mouse_pos = None
462 self.cur_poly = None
463 self._mouse_drag = False
464 self._draw_objects = False
465 self._draw_enemies = False
466 self._draw_lines = False
467 self.grid_size = 1
468 self.sel_mode = False
469 self.move_obj_mode = False
470 self.move_obj = None
471 self._start_pos = None
472 self._parent = parent
473 self._move_point_mode = False
474 self._move_poly_mode = False
475 self._move_point = False
476 self._zoom_factor = 1.0
477
478 def _level_coordinates(self, pos):
479 # Move positions to level values
480 if not pos:
481 return (0, 0)
482 # Apply zoom_factor
483 pos = pos[0] + self.pos[0], pos[1] + self.pos[1]
484 zoomed = (pos[0] * self._zoom_factor, pos[1] * self._zoom_factor)
485 return int(zoomed[0]), int(zoomed[1])
486
487 def _move_view(self, offset):
488 new_pos = [self.pos[0] + offset[0], self.pos[1] + offset[1]]
489 if new_pos[0] < 0:
490 new_pos[0] = self.pos[0]
491 elif new_pos[0] > self.level.x - SCREEN[0]:
492 new_pos[0] = self.pos[0]
493 if new_pos[1] < 0:
494 new_pos[1] = self.pos[1]
495 elif new_pos[1] > self.level.y - SCREEN[1]:
496 new_pos[1] = self.pos[1]
497 self.pos = tuple(new_pos)
498
499 def inc_grid_size(self, amount):
500 self.grid_size = max(1, self.grid_size + amount)
501 if self.grid_size > 1:
502 self.grid_size = self.grid_size - (self.grid_size % 5)
503
504 def snap_to_grid(self, pos):
505 x = pos[0] - (pos[0] % self.grid_size)
506 y = pos[1] - (pos[1] % self.grid_size)
507 return (x, y)
508
509 def set_objects(self, value):
510 if self._draw_objects != value:
511 self._draw_objects = value
512 self.invalidate()
513
514 def set_enemies(self, value):
515 if self._draw_enemies != value:
516 self._draw_enemies = value
517 self.invalidate()
518
519 def zoom_out(self):
520 self._zoom_factor = self._zoom_factor * 2.0
521 if self._zoom_factor > 8:
522 self._zoom_factor = 8
523
524 def zoom_in(self):
525 self._zoom_factor = self._zoom_factor // 2.0
526 if self._zoom_factor < 1:
527 self._zoom_factor = 1
528
529 def draw(self, surface):
530 if (self.cur_poly is not None and self.cur_poly in self.level.polygons
531 and len(self.level.polygons[self.cur_poly])):
532 # We have an active polygon
533 mouse_pos = self._level_coordinates(self.mouse_pos)
534 mouse_pos = self.snap_to_grid(mouse_pos)
535 elif self._draw_lines:
536 # Interior wall mode
537 mouse_pos = self._level_coordinates(self.mouse_pos)
538 mouse_pos = self.snap_to_grid(mouse_pos)
539 elif self._move_point_mode or self._move_poly_mode:
540 mouse_pos = self._level_coordinates(self.mouse_pos)
541 mouse_pos = self.snap_to_grid(mouse_pos)
542 else:
543 mouse_pos = None
544 level_surface = level.draw(mouse_pos, self.cur_poly, self.filled_mode,
545 self._draw_lines, self._start_pos,
546 self._move_point_mode, self._move_poly_mode,
547 self._move_point, self._zoom_factor, self)
548 if self._draw_objects:
549 for thing in self.level.drawables:
550 if not isinstance(thing, ne.Enemy):
551 thing.render(level_surface)
552 if self._draw_enemies:
553 for thing in self.level.drawables:
554 if isinstance(thing, ne.Enemy):
555 thing.render(level_surface)
556 surface_area = pygame.rect.Rect(self.pos, SCREEN)
557 zoomed_surface = level_surface.copy()
558 if self._zoom_factor != 1:
559 zoomed_surface = pygame.transform.scale(
560 level_surface,
561 (int(level_surface.get_width() / self._zoom_factor),
562 int(level_surface.get_height() / self._zoom_factor)))
563 surface.blit(zoomed_surface, (0, 0), surface_area)
564
565 def change_poly(self, new_poly):
566 self.cur_poly = new_poly
567 self._draw_lines = False
568 self._move_point_mode = False
569 if self.cur_poly is not None:
570 self._parent.reset_lit_buttons()
571 self.filled_mode = False
572
573 def line_mode(self):
574 self.cur_poly = None
575 self._parent.reset_lit_buttons()
576 self._draw_lines = True
577 self.filled_mode = False
578 self._start_pos = None
579 self._move_point_mode = False
580 self._move_poly_mode = False
581
582 def key_down(self, ev):
583 if ev.key == pgl.K_LEFT:
584 self._move_view((-10, 0))
585 elif ev.key == pgl.K_RIGHT:
586 self._move_view((10, 0))
587 elif ev.key == pgl.K_UP:
588 self._move_view((0, -10))
589 elif ev.key == pgl.K_DOWN:
590 self._move_view((0, 10))
591 elif ev.key in (pgl.K_1, pgl.K_2, pgl.K_3, pgl.K_4, pgl.K_5, pgl.K_6):
592 self.change_poly(ev.key - pgl.K_0)
593 elif ev.key == pgl.K_0:
594 self.change_poly(None)
595 elif ev.key == pgl.K_d and self.cur_poly:
596 self.level.delete_point(self.cur_poly)
597 elif ev.key == pgl.K_f:
598 self.set_filled()
599 elif ev.key == pgl.K_c:
600 self.close_poly()
601
602 def set_move_poly_mode(self):
603 self._draw_lines = False
604 self._move_point_mode = False
605 self._move_poly_mode = True
606 self.filled_mode = False
607 self._parent.reset_lit_buttons()
608 self._move_point = None
609
610 def set_move_mode(self):
611 self._draw_lines = False
612 self._move_point_mode = True
613 self._move_poly_mode = False
614 self.filled_mode = False
615 self._parent.reset_lit_buttons()
616 self._move_point = None
617
618 def set_filled(self):
619 closed, _ = self.level.all_closed()
620 if closed:
621 self.cur_poly = None
622 self._parent.reset_lit_buttons()
623 self.filled_mode = True
624 self._draw_lines = False
625 self._move_point_mode = False
626 self._move_poly_mode = False
627 else:
628 alert('Not all polygons closed, so not filling')
629
630 def mouse_move(self, ev):
631 old_pos = self.mouse_pos
632 self.mouse_pos = ev.pos
633 if self.move_obj:
634 corrected_pos = self._level_coordinates(ev.pos)
635 snapped_pos = self.snap_to_grid(corrected_pos)
636 self._move_obj(self.move_obj, snapped_pos)
637 if old_pos != self.mouse_pos and (self.cur_poly or self._draw_lines
638 or self._move_point_mode
639 or self._move_poly_mode):
640 self.invalidate()
641
642 def mouse_drag(self, ev):
643 if self._mouse_drag:
644 old_pos = self.mouse_pos
645 self.mouse_pos = ev.pos
646 diff = (-self.mouse_pos[0] + old_pos[0],
647 -self.mouse_pos[1] + old_pos[1])
648 self._move_view(diff)
649 self.invalidate()
650
651 def mouse_down(self, ev):
652 corrected_pos = self._level_coordinates(ev.pos)
653 snapped_pos = self.snap_to_grid(corrected_pos)
654 if self.sel_mode and ev.button == 1:
655 obj = self.level.find_obj_at_pos(corrected_pos)
656 if obj is not None:
657 self._edit_selected(obj)
658 elif self.move_obj_mode and ev.button == 1 and not self.move_obj:
659 obj = self.level.find_obj_at_pos(corrected_pos)
660 if obj is not None:
661 if obj.movable():
662 self.move_obj = obj
663 elif self.move_obj_mode and ev.button == 1 and self.move_obj:
664 self._update_pos(self.move_obj, snapped_pos)
665 self.move_obj = None
666 elif self._move_poly_mode and ev.button == 1:
667 if self._move_point:
668 # Place the current point
669 self.level.replace_poly(self._move_point, snapped_pos)
670 self._move_point = None
671 self.invalidate()
672 else:
673 # find the current point
674 self._move_point = self.level.find_vertex(corrected_pos)
675 elif self._move_point_mode and ev.button == 1:
676 if self._move_point:
677 # Place the current point
678 self.level.replace_vertex(self._move_point, snapped_pos)
679 self._move_point = None
680 self.invalidate()
681 else:
682 # find the current point
683 self._move_point = self.level.find_vertex(corrected_pos)
684 elif ev.button == 1:
685 if self._draw_lines:
686 if self._start_pos is None:
687 self._start_pos = snapped_pos
688 else:
689 self.level.add_line(self._start_pos, snapped_pos)
690 self._start_pos = None
691 else:
692 print "Click: %r" % (
693 self.level.point_to_pymunk(corrected_pos),)
694 if ev.button == 4: # Scroll up
695 self._move_view((0, -10))
696 elif ev.button == 5: # Scroll down
697 self._move_view((0, 10))
698 elif ev.button == 6: # Scroll left
699 self._move_view((-10, 0))
700 elif ev.button == 7: # Scroll right
701 self._move_view((10, 0))
702 elif self.cur_poly and ev.button == 1:
703 # Add a point
704 if not self.level.add_point(self.cur_poly, snapped_pos):
705 alert("Failed to place point")
706 elif ev.button == 3:
707 self._mouse_drag = True
708
709 def mouse_up(self, ev):
710 if ev.button == 3:
711 self._mouse_drag = False
712
713 def close_poly(self):
714 if self.cur_poly is None:
715 return
716 if self.level.close_poly(self.cur_poly):
717 alert("Successfully closed the polygon")
718 self.change_poly(None)
719 else:
720 alert("Failed to close the polygon")
721
722 def _edit_class(self, classname, cls, data):
723 # Dialog for class properties
724 dialog = EditClassDialog(classname, cls, data, self)
725 if dialog.present() == 'OK':
726 return dialog
727 return None
728
729 def _edit_selected(self, obj):
730 data = self.level.lookup[obj]
731 cls = obj.__class__
732 classname = obj.__class__.__name__
733 dialog = EditClassDialog(classname, cls, data, self, True)
734 res = dialog.present()
735 if res == 'OK':
736 edited = dialog.get_data()
737 if edited is not None:
738 for target in [self.level._game_objects, self.level._enemies]:
739 if data in target:
740 if self.level.try_new_object(classname, target,
741 edited, data):
742 dialog.cleanup()
743 break
744 elif res == 'Delete':
745 for target in [self.level._game_objects, self.level._enemies]:
746 if data in target:
747 target.remove(data)
748 self.level.reset_objs()
749 break
750
751 def _make_edit_dialog(self, entries):
752 # Dialog to hold the editor
753 edit_box = Dialog()
754 edit_box.rect = pygame.rect.Rect(0, 0, 700, 500)
755 table = ObjectTable(entries)
756 edit_box.add(table)
757 buttons = []
758 for text in ['OK', 'Delete', 'Cancel']:
759 but = Button(text, action=lambda x=text: edit_box.dismiss(x))
760 buttons.append(but)
761 row = Row(buttons)
762 row.rect = pygame.rect.Rect(250, 450, 700, 50)
763 edit_box.add(row)
764 edit_box.get_selection = lambda: table.get_selection()
765 return edit_box
766
767 def edit_objects(self):
768 edit_box = self._make_edit_dialog(self.level._game_objects)
769 res = edit_box.present()
770 choice = edit_box.get_selection()
771 if choice is None:
772 return
773 if res == 'OK':
774 cls = self.level.get_class(choice['classname'])
775 edit_dlg = self._edit_class(choice['classname'], cls, choice)
776 if edit_dlg is not None:
777 edited = edit_dlg.get_data()
778 if self.level.try_new_object(choice["classname"],
779 self.level._game_objects,
780 edited, choice):
781 edit_dlg.cleanup()
782 elif res == 'Delete':
783 self.level._game_objects.remove(choice)
784 self.level.reset_objs()
785
786 def edit_enemies(self):
787 edit_box = self._make_edit_dialog(self.level._enemies)
788 res = edit_box.present()
789 choice = edit_box.get_selection()
790 if choice is None:
791 return
792 if res == 'OK':
793 cls = self.level.get_class(choice['classname'], ne)
794 edit_dlg = self._edit_class(choice['classname'], cls, choice)
795 if edit_dlg is not None:
796 edited = edit_dlg.get_data()
797 if self.level.try_new_object(choice["classname"],
798 self.level._enemies,
799 edited, choice):
800 edit_dlg.cleanup()
801 elif res == 'Delete':
802 self.level._enemies.remove(choice)
803 self.level.reset_objs()
804
805 def _make_choice_dialog(self, classes):
806 # Dialog to hold the editor
807 data = []
808 for cls_name, cls in classes:
809 data.append({"classname": cls_name, "class": cls})
810 choice_box = Dialog()
811 choice_box.rect = pygame.rect.Rect(0, 0, 700, 500)
812 table = ObjectTable(data)
813 choice_box.add(table)
814 buttons = []
815 for text in ['OK', 'Cancel']:
816 but = Button(text, action=lambda x=text: choice_box.dismiss(x))
817 buttons.append(but)
818 row = Row(buttons)
819 row.rect = pygame.rect.Rect(250, 450, 700, 50)
820 choice_box.add(row)
821 choice_box.get_selection = lambda: table.get_selection()
822 return choice_box
823
824 def add_game_object(self):
825 classes = ngo.get_editable_game_objects()
826 classes.extend(("collectable.%s" % cls_name, cls)
827 for cls_name, cls
828 in collectable.get_editable_game_objects())
829 choose = self._make_choice_dialog(classes)
830 res = choose.present()
831 choice = choose.get_selection()
832 if res == 'OK' and choice is not None:
833 classname = choice['classname']
834 cls = choice['class']
835 edit_dlg = self._edit_class(classname, cls, None)
836 if edit_dlg is not None:
837 new_cls = edit_dlg.get_data()
838 if self.level.try_new_object(classname,
839 self.level._game_objects,
840 new_cls, None):
841 edit_dlg.cleanup()
842
843 def add_enemy(self):
844 classes = ne.get_editable_enemies()
845 choose = self._make_choice_dialog(classes)
846 res = choose.present()
847 choice = choose.get_selection()
848 if res == 'OK' and choice is not None:
849 classname = choice['classname']
850 cls = choice['class']
851 edit_dlg = self._edit_class(classname, cls, None)
852 if edit_dlg is not None:
853 new_cls = edit_dlg.get_data()
854 if self.level.try_new_object(classname, self.level._enemies,
855 new_cls, None):
856 edit_dlg.cleanup()
857
858 def add_puzzler(self):
859 classes = np.get_editable_puzzlers()
860 choose = self._make_choice_dialog(classes)
861 res = choose.present()
862 choice = choose.get_selection()
863 if res == 'OK' and choice is not None:
864 classname = choice['classname']
865 cls = choice['class']
866 edit_dlg = self._edit_class(classname, cls, None)
867 if edit_dlg is not None:
868 new_cls = edit_dlg.get_data()
869 if self.level.try_new_object(classname,
870 self.level._game_objects,
871 new_cls, None):
872 edit_dlg.cleanup()
873
874 def _move_obj(self, obj, new_pos):
875 new_coords = self.level.point_to_pymunk(new_pos)
876 shape = obj.get_shape()
877 shape.body.position = (new_coords[0], new_coords[1])
878 self.invalidate()
879
880 def _update_pos(self, obj, new_pos, final=False):
881 data = self.level.lookup[obj]
882 new_coords = self.level.point_to_pymunk(new_pos)
883 args = data['args']
884 old_coords = list(args[0])
885 args[0][0] = new_coords[0]
886 args[0][1] = new_coords[1]
887 param_defs = obj.requires()[1:] # chop off name
888 for i, (_key, key_type) in enumerate(param_defs):
889 if i > len(args):
890 break
891 if key_type == "polygon (convex)":
892 args[i] = self.level.translate_poly(
893 args[i], old_coords, new_coords)
894 self.level.reset_objs()
895 self.invalidate()
896
897
898class HighLightButton(Button):
899 """Button with highlight support"""
900 def __init__(self, text, parent, **kwds):
901 super(HighLightButton, self).__init__(text, **kwds)
902 self._parent = parent
903
904 def highlight(self):
905 self.border_color = pygame.color.Color('red')
906
907 def reset(self):
908 self.border_color = self.fg_color
909
910
911class PolyButton(HighLightButton):
912 """Button for coosing the correct polygon"""
913
914 def __init__(self, index, level_widget, parent):
915 if index is not None:
916 text = "P %s" % index
917 else:
918 text = 'Exit Draw Mode'
919 super(PolyButton, self).__init__(text, parent)
920 self.index = index
921 self.level_widget = level_widget
922
923 def action(self):
924 self.level_widget.change_poly(self.index)
925 self._parent.reset_lit_buttons()
926 if self.index is not None:
927 self.highlight()
928
929
930class GridSizeLabel(Label):
931 """Label and setter for grid size."""
932
933 def __init__(self, level_widget, **kwds):
934 self.level_widget = level_widget
935 super(GridSizeLabel, self).__init__(self.grid_text(), **kwds)
936
937 def grid_text(self):
938 return "Grid size: %d" % self.level_widget.grid_size
939
940 def inc_grid_size(self, amount):
941 self.level_widget.inc_grid_size(amount)
942 self.set_text(self.grid_text())
943
944
945class SnapButton(Button):
946 """Button for increasing or decreasing snap-to-grid size."""
947
948 def __init__(self, grid_size_label, parent, inc_amount):
949 self.grid_size_label = grid_size_label
950 self.inc_amount = inc_amount
951 text = "Grid %s%d" % (
952 '-' if inc_amount < 0 else '+',
953 abs(inc_amount))
954 self._parent = parent
955 super(SnapButton, self).__init__(text)
956
957 def action(self):
958 self.grid_size_label.inc_grid_size(self.inc_amount)
959
960
961class EditorApp(RootWidget):
962
963 def __init__(self, level, surface):
964 super(EditorApp, self).__init__(surface)
965 self.level = level
966 self.level_widget = LevelWidget(self.level, self)
967 self.add(self.level_widget)
968
969 self._dMenus = {}
970
971 self._light_buttons = []
972
973 self._make_draw_menu()
974 self._make_objects_menu()
975
976 self._menu_mode = 'drawing'
977 self._populate_menu()
978
979 self._zoom = 1
980
981 def _make_draw_menu(self):
982 widgets = []
983
984 white = pygame.color.Color("white")
985
986 # Add poly buttons
987 y = 5
988 for poly in range(1, 10):
989 but = PolyButton(poly, self.level_widget, self)
990 but.rect = pygame.rect.Rect(0, 0, MENU_WIDTH // 3 - MENU_PAD,
991 MENU_BUTTON_HEIGHT)
992 index = poly % 3
993 if index == 1:
994 but.rect.move_ip(MENU_LEFT, y)
995 elif index == 2:
996 but.rect.move_ip(MENU_LEFT + MENU_WIDTH // 3 - MENU_HALF_PAD,
997 y)
998 else:
999 left = MENU_LEFT + 2 * MENU_WIDTH // 3 - MENU_HALF_PAD
1000 but.rect.move_ip(left, y)
1001 y += MENU_BUTTON_HEIGHT + MENU_PAD
1002 self._light_buttons.append(but)
1003 widgets.append(but)
1004
1005 end_poly_but = PolyButton(None, self.level_widget, self)
1006 end_poly_but.rect = BUTTON_RECT.copy()
1007 end_poly_but.rect.move_ip(MENU_LEFT, y)
1008 widgets.append(end_poly_but)
1009 y += MENU_BUTTON_HEIGHT + MENU_PAD
1010
1011 self.move_point_but = HighLightButton("Mv Point", self,
1012 action=self.move_point)
1013 self.move_point_but.rect = HALF_BUTTON_RECT.copy()
1014 self.move_point_but.rect.move_ip(MENU_LEFT, y)
1015 widgets.append(self.move_point_but)
1016 self._light_buttons.append(self.move_point_but)
1017
1018 self.move_poly_but = HighLightButton("Mv Poly", self,
1019 action=self.move_poly)
1020 self.move_poly_but.rect = HALF_BUTTON_RECT.copy()
1021 self.move_poly_but.rect.move_ip(MENU_LEFT + MENU_HALF_WIDTH, y)
1022 widgets.append(self.move_poly_but)
1023 self._light_buttons.append(self.move_poly_but)
1024
1025 y += MENU_BUTTON_HEIGHT + MENU_PAD
1026
1027 # grid size widgets
1028 grid_size_label = GridSizeLabel(
1029 self.level_widget, width=BUTTON_RECT.width,
1030 align="c", fg_color=white)
1031 grid_size_label.rect.move_ip(MENU_LEFT, y)
1032 widgets.append(grid_size_label)
1033 y += grid_size_label.rect.height + MENU_PAD
1034 inc_snap_but = SnapButton(grid_size_label, self, 5)
1035 inc_snap_but.rect = HALF_BUTTON_RECT.copy()
1036 inc_snap_but.rect.move_ip(MENU_LEFT, y)
1037 widgets.append(inc_snap_but)
1038 dec_snap_but = SnapButton(grid_size_label, self, -5)
1039 dec_snap_but.rect = HALF_BUTTON_RECT.copy()
1040 dec_snap_but.rect.move_ip(
1041 MENU_LEFT + MENU_HALF_WIDTH, y)
1042 widgets.append(dec_snap_but)
1043 y += MENU_BUTTON_HEIGHT + MENU_PAD
1044
1045 self.draw_line_but = HighLightButton("Draw interior wall", self,
1046 action=self.set_line_mode)
1047 self.draw_line_but.rect = BUTTON_RECT.copy()
1048 self.draw_line_but.rect.move_ip(MENU_LEFT, y)
1049 widgets.append(self.draw_line_but)
1050 self._light_buttons.append(self.draw_line_but)
1051 y += MENU_BUTTON_HEIGHT + MENU_PAD
1052
1053 fill_but = Button('Fill exterior', action=self.level_widget.set_filled)
1054 fill_but.rect = BUTTON_RECT.copy()
1055 fill_but.rect.move_ip(MENU_LEFT, y)
1056 widgets.append(fill_but)
1057 y += MENU_BUTTON_HEIGHT + MENU_PAD
1058
1059 close_poly_but = Button('Close Polygon',
1060 action=self.level_widget.close_poly)
1061 close_poly_but.rect = BUTTON_RECT.copy()
1062 close_poly_but.rect.move_ip(MENU_LEFT, y)
1063 widgets.append(close_poly_but)
1064 y += MENU_BUTTON_HEIGHT + MENU_PAD
1065
1066 self.show_objs = CheckBox(fg_color=white)
1067 self.show_objs.rect = CHECK_RECT.copy()
1068 self.show_objs.rect.move_ip(MENU_LEFT, y)
1069 label = Label("Show Objects", fg_color=white)
1070 label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
1071 widgets.append(self.show_objs)
1072 widgets.append(label)
1073 y += label.rect.height + MENU_PAD
1074
1075 self.show_enemies = CheckBox(fg_color=white)
1076 self.show_enemies.rect = CHECK_RECT.copy()
1077 self.show_enemies.rect.move_ip(MENU_LEFT, y)
1078 label = Label("Show enemy start pos", fg_color=white)
1079 label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
1080 widgets.append(self.show_enemies)
1081 widgets.append(label)
1082 y += label.rect.height + MENU_PAD
1083
1084 y += MENU_PAD
1085 switch_but = Button('Switch to Objects', action=self.switch_to_objects)
1086 switch_but.rect = BUTTON_RECT.copy()
1087 switch_but.rect.move_ip(MENU_LEFT, y)
1088 widgets.append(switch_but)
1089 y += switch_but.rect.height + MENU_PAD
1090
1091 save_but = Button('Save Level', action=self.save)
1092 save_but.rect = BUTTON_RECT.copy()
1093 save_but.rect.move_ip(MENU_LEFT, y)
1094 widgets.append(save_but)
1095 y += MENU_BUTTON_HEIGHT + MENU_PAD
1096
1097 zoom_out = Button('Zoom out', action=self.level_widget.zoom_out)
1098 zoom_out.rect = BUTTON_RECT.copy()
1099 zoom_out.rect.width = zoom_out.rect.width // 2
1100 zoom_out.rect.move_ip(MENU_LEFT, y)
1101 widgets.append(zoom_out)
1102
1103 zoom_in = Button('Zoom in', action=self.level_widget.zoom_in)
1104 zoom_in.rect = BUTTON_RECT.copy()
1105 zoom_in.width = zoom_in.width // 2
1106 zoom_in.rect.move_ip(MENU_LEFT + zoom_out.width, y)
1107 widgets.append(zoom_in)
1108
1109 y = SCREEN[1] - MENU_BUTTON_HEIGHT - MENU_PAD
1110 quit_but = Button('Quit', action=self.do_quit)
1111 quit_but.rect = BUTTON_RECT.copy()
1112 quit_but.rect.move_ip(MENU_LEFT, y)
1113 widgets.append(quit_but)
1114
1115 self._dMenus['drawing'] = widgets
1116
1117 def _make_objects_menu(self):
1118 widgets = []
1119
1120 # Add poly buttons
1121 y = 15
1122
1123 edit_objs_but = Button('Edit Objects',
1124 action=self.level_widget.edit_objects)
1125 edit_objs_but.rect = BUTTON_RECT.copy()
1126 edit_objs_but.rect.move_ip(MENU_LEFT, y)
1127 widgets.append(edit_objs_but)
1128 y += MENU_BUTTON_HEIGHT + MENU_PAD
1129
1130 edir_enemies_but = Button('Edit Enemies',
1131 action=self.level_widget.edit_enemies)
1132 edir_enemies_but.rect = BUTTON_RECT.copy()
1133 edir_enemies_but.rect.move_ip(MENU_LEFT, y)
1134 widgets.append(edir_enemies_but)
1135 y += MENU_BUTTON_HEIGHT + MENU_PAD
1136
1137 add_obj_but = Button('Add Game Object',
1138 action=self.level_widget.add_game_object)
1139 add_obj_but.rect = BUTTON_RECT.copy()
1140 add_obj_but.rect.move_ip(MENU_LEFT, y)
1141 widgets.append(add_obj_but)
1142 y += MENU_BUTTON_HEIGHT + MENU_PAD
1143
1144 add_puzzle_but = Button('Add Puzzler',
1145 action=self.level_widget.add_puzzler)
1146 add_puzzle_but.rect = BUTTON_RECT.copy()
1147 add_puzzle_but.rect.move_ip(MENU_LEFT, y)
1148 widgets.append(add_puzzle_but)
1149 y += MENU_BUTTON_HEIGHT + MENU_PAD
1150
1151 add_enemy_but = Button('Add Enemy',
1152 action=self.level_widget.add_enemy)
1153 add_enemy_but.rect = BUTTON_RECT.copy()
1154 add_enemy_but.rect.move_ip(MENU_LEFT, y)
1155 widgets.append(add_enemy_but)
1156 y += MENU_BUTTON_HEIGHT + MENU_PAD
1157
1158 y += MENU_PAD
1159 self.sel_mode_but = HighLightButton('Select Object', self,
1160 action=self.sel_mode)
1161 self.sel_mode_but.rect = BUTTON_RECT.copy()
1162 self.sel_mode_but.rect.move_ip(MENU_LEFT, y)
1163 widgets.append(self.sel_mode_but)
1164 self._light_buttons.append(self.sel_mode_but)
1165 y += MENU_BUTTON_HEIGHT + MENU_PAD
1166
1167 y += MENU_PAD
1168 self.move_obj_mode_but = HighLightButton('Move Object', self,
1169 action=self.move_obj_mode)
1170 self.move_obj_mode_but.rect = BUTTON_RECT.copy()
1171 self.move_obj_mode_but.rect.move_ip(MENU_LEFT, y)
1172 widgets.append(self.move_obj_mode_but)
1173 self._light_buttons.append(self.move_obj_mode_but)
1174 y += MENU_BUTTON_HEIGHT + MENU_PAD
1175
1176 y += MENU_PAD
1177 switch_but = Button('Switch to Drawing', action=self.switch_to_draw)
1178 switch_but.rect = BUTTON_RECT.copy()
1179 switch_but.rect.move_ip(MENU_LEFT, y)
1180 widgets.append(switch_but)
1181 y += switch_but.rect.height + MENU_PAD
1182
1183 save_but = Button('Save Level', action=self.save)
1184 save_but.rect = BUTTON_RECT.copy()
1185 save_but.rect.move_ip(MENU_LEFT, y)
1186 widgets.append(save_but)
1187 y += MENU_BUTTON_HEIGHT + MENU_PAD
1188
1189 zoom_out = Button('Zoom out', action=self.level_widget.zoom_out)
1190 zoom_out.rect = BUTTON_RECT.copy()
1191 zoom_out.rect.width = zoom_out.rect.width // 2
1192 zoom_out.rect.move_ip(MENU_LEFT, y)
1193 widgets.append(zoom_out)
1194
1195 zoom_in = Button('Zoom in', action=self.level_widget.zoom_in)
1196 zoom_in.rect = BUTTON_RECT.copy()
1197 zoom_in.width = zoom_in.width // 2
1198 zoom_in.rect.move_ip(MENU_LEFT + zoom_out.width, y)
1199 widgets.append(zoom_in)
1200 y += MENU_BUTTON_HEIGHT + MENU_PAD
1201
1202 y = SCREEN[1] - MENU_BUTTON_HEIGHT - MENU_PAD
1203 quit_but = Button('Quit', action=self.do_quit)
1204 quit_but.rect = BUTTON_RECT.copy()
1205 quit_but.rect.move_ip(MENU_LEFT, y)
1206 widgets.append(quit_but)
1207
1208 self._dMenus['objects'] = widgets
1209
1210 def key_down(self, ev):
1211 if ev.key == pgl.K_ESCAPE:
1212 self.do_quit()
1213 elif ev.key == pgl.K_s:
1214 self.save()
1215 else:
1216 self.level_widget.key_down(ev)
1217
1218 def do_quit(self):
1219 res = ask("Really Quit?")
1220 if res == "OK":
1221 self.quit()
1222
1223 def save(self):
1224 closed, messages = self.level.all_closed()
1225 if closed:
1226 self.level.save()
1227 # display success
1228 alert("Level %s saved successfully." % self.level.name)
1229 else:
1230 # display errors
1231 alert("Failed to save level.\n\n%s" % '\n'.join(messages))
1232
1233 def switch_to_draw(self):
1234 if self._menu_mode != 'drawing':
1235 self._clear_menu()
1236 self._menu_mode = 'drawing'
1237 self._populate_menu()
1238
1239 def switch_to_objects(self):
1240 if self._menu_mode != 'objects':
1241 self._clear_menu()
1242 self._menu_mode = 'objects'
1243 self._populate_menu()
1244
1245 def _clear_menu(self):
1246 for widget in self._dMenus[self._menu_mode]:
1247 self.remove(widget)
1248
1249 def reset_lit_buttons(self):
1250 for but in self._light_buttons:
1251 but.reset()
1252
1253 def _populate_menu(self):
1254 self.level_widget.change_poly(None)
1255 self.level_widget.sel_mode = False
1256 self.level_widget.move_obj_mode = False
1257 self.level_widget.move_obj = None
1258 for widget in self._dMenus[self._menu_mode]:
1259 self.add(widget)
1260 self.invalidate()
1261
1262 def set_line_mode(self):
1263 self.level_widget.line_mode()
1264 self.draw_line_but.highlight()
1265
1266 def sel_mode(self):
1267 self.level_widget.sel_mode = not self.level_widget.sel_mode
1268 if self.level_widget.sel_mode:
1269 self.move_obj_mode_but.reset()
1270 self.sel_mode_but.highlight()
1271 self.level_widget.move_obj_mode = False
1272 self.level_widget.move_obj = None
1273 else:
1274 self.sel_mode_but.reset()
1275
1276 def move_obj_mode(self):
1277 self.level_widget.move_obj_mode = not self.level_widget.move_obj_mode
1278 if self.level_widget.move_obj_mode:
1279 self.sel_mode_but.reset()
1280 self.move_obj_mode_but.highlight()
1281 self.level_widget.sel_mode = False
1282 else:
1283 self.move_obj_mode_but.reset()
1284
1285 def mouse_move(self, ev):
1286 self.level_widget.mouse_move(ev)
1287
1288 def move_point(self):
1289 self.level_widget.set_move_mode()
1290 self.move_point_but.highlight()
1291
1292 def move_poly(self):
1293 self.level_widget.set_move_poly_mode()
1294 self.move_poly_but.highlight()
1295
1296 def draw(self, surface):
1297 # Update checkbox state
1298 if self._menu_mode == 'drawing':
1299 self.level_widget.set_objects(self.show_objs.value)
1300 self.level_widget.set_enemies(self.show_enemies.value)
1301 else:
1302 self.level_widget.set_objects(True)
1303 self.level_widget.set_enemies(True)
1304 super(EditorApp, self).draw(surface)
1305
1306
1307if __name__ == "__main__":
1308 if len(sys.argv) not in [2, 4]:
1309 print 'Please supply a levelname or levelname and level size'
1310 sys.exit()
1311 # Need to ensure we have defaults for rendering
1312 parse_args([])
1313 pygame.display.init()
1314 pygame.font.init()
1315 pygame.display.set_mode((SCREEN[0] + MENU_WIDTH, SCREEN[1]),
1316 pgl.SWSURFACE)
1317 if len(sys.argv) == 2:
1318 level = EditorLevel(sys.argv[1])
1319 level.load(pymunk.Space())
1320 elif len(sys.argv) == 4:
1321 level = EditorLevel(sys.argv[1], int(sys.argv[2]), int(sys.argv[3]))
1322 pygame.display.set_caption('Nagslang Area Editor')
1323 pygame.key.set_repeat(200, 100)
1324 app = EditorApp(level, pygame.display.get_surface())
1325 app.run()
Note: See TracBrowser for help on using the repository browser.