source: tools/area_editor.py@ 546:c13f81f8d48c

Last change on this file since 546:c13f81f8d48c was 546:c13f81f8d48c, checked in by Neil Muller <drnlmuller@…>, 9 years ago

Hackily unbreak terrian movement.

  • Property exe set to *
File size: 48.4 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 data = self.level.lookup[obj]
879 args = data['args']
880 param_defs = obj.requires()[1:] # chop off name
881 for i, (_key, key_type) in enumerate(param_defs):
882 if i > len(args):
883 break
884 if key_type == "polygon (convex)":
885 self._update_pos(obj, new_pos)
886 self.invalidate()
887
888 def _update_pos(self, obj, new_pos):
889 data = self.level.lookup[obj]
890 new_coords = self.level.point_to_pymunk(new_pos)
891 args = data['args']
892 old_coords = list(args[0])
893 args[0][0] = new_coords[0]
894 args[0][1] = new_coords[1]
895 param_defs = obj.requires()[1:] # chop off name
896 for i, (_key, key_type) in enumerate(param_defs):
897 if i > len(args):
898 break
899 if key_type == "polygon (convex)":
900 args[i] = self.level.translate_poly(
901 args[i], old_coords, new_coords)
902 self.level.reset_objs()
903 self.invalidate()
904
905
906class HighLightButton(Button):
907 """Button with highlight support"""
908 def __init__(self, text, parent, **kwds):
909 super(HighLightButton, self).__init__(text, **kwds)
910 self._parent = parent
911
912 def highlight(self):
913 self.border_color = pygame.color.Color('red')
914
915 def reset(self):
916 self.border_color = self.fg_color
917
918
919class PolyButton(HighLightButton):
920 """Button for coosing the correct polygon"""
921
922 def __init__(self, index, level_widget, parent):
923 if index is not None:
924 text = "P %s" % index
925 else:
926 text = 'Exit Draw Mode'
927 super(PolyButton, self).__init__(text, parent)
928 self.index = index
929 self.level_widget = level_widget
930
931 def action(self):
932 self.level_widget.change_poly(self.index)
933 self._parent.reset_lit_buttons()
934 if self.index is not None:
935 self.highlight()
936
937
938class GridSizeLabel(Label):
939 """Label and setter for grid size."""
940
941 def __init__(self, level_widget, **kwds):
942 self.level_widget = level_widget
943 super(GridSizeLabel, self).__init__(self.grid_text(), **kwds)
944
945 def grid_text(self):
946 return "Grid size: %d" % self.level_widget.grid_size
947
948 def inc_grid_size(self, amount):
949 self.level_widget.inc_grid_size(amount)
950 self.set_text(self.grid_text())
951
952
953class SnapButton(Button):
954 """Button for increasing or decreasing snap-to-grid size."""
955
956 def __init__(self, grid_size_label, parent, inc_amount):
957 self.grid_size_label = grid_size_label
958 self.inc_amount = inc_amount
959 text = "Grid %s%d" % (
960 '-' if inc_amount < 0 else '+',
961 abs(inc_amount))
962 self._parent = parent
963 super(SnapButton, self).__init__(text)
964
965 def action(self):
966 self.grid_size_label.inc_grid_size(self.inc_amount)
967
968
969class EditorApp(RootWidget):
970
971 def __init__(self, level, surface):
972 super(EditorApp, self).__init__(surface)
973 self.level = level
974 self.level_widget = LevelWidget(self.level, self)
975 self.add(self.level_widget)
976
977 self._dMenus = {}
978
979 self._light_buttons = []
980
981 self._make_draw_menu()
982 self._make_objects_menu()
983
984 self._menu_mode = 'drawing'
985 self._populate_menu()
986
987 self._zoom = 1
988
989 def _make_draw_menu(self):
990 widgets = []
991
992 white = pygame.color.Color("white")
993
994 # Add poly buttons
995 y = 5
996 for poly in range(1, 10):
997 but = PolyButton(poly, self.level_widget, self)
998 but.rect = pygame.rect.Rect(0, 0, MENU_WIDTH // 3 - MENU_PAD,
999 MENU_BUTTON_HEIGHT)
1000 index = poly % 3
1001 if index == 1:
1002 but.rect.move_ip(MENU_LEFT, y)
1003 elif index == 2:
1004 but.rect.move_ip(MENU_LEFT + MENU_WIDTH // 3 - MENU_HALF_PAD,
1005 y)
1006 else:
1007 left = MENU_LEFT + 2 * MENU_WIDTH // 3 - MENU_HALF_PAD
1008 but.rect.move_ip(left, y)
1009 y += MENU_BUTTON_HEIGHT + MENU_PAD
1010 self._light_buttons.append(but)
1011 widgets.append(but)
1012
1013 end_poly_but = PolyButton(None, self.level_widget, self)
1014 end_poly_but.rect = BUTTON_RECT.copy()
1015 end_poly_but.rect.move_ip(MENU_LEFT, y)
1016 widgets.append(end_poly_but)
1017 y += MENU_BUTTON_HEIGHT + MENU_PAD
1018
1019 self.move_point_but = HighLightButton("Mv Point", self,
1020 action=self.move_point)
1021 self.move_point_but.rect = HALF_BUTTON_RECT.copy()
1022 self.move_point_but.rect.move_ip(MENU_LEFT, y)
1023 widgets.append(self.move_point_but)
1024 self._light_buttons.append(self.move_point_but)
1025
1026 self.move_poly_but = HighLightButton("Mv Poly", self,
1027 action=self.move_poly)
1028 self.move_poly_but.rect = HALF_BUTTON_RECT.copy()
1029 self.move_poly_but.rect.move_ip(MENU_LEFT + MENU_HALF_WIDTH, y)
1030 widgets.append(self.move_poly_but)
1031 self._light_buttons.append(self.move_poly_but)
1032
1033 y += MENU_BUTTON_HEIGHT + MENU_PAD
1034
1035 # grid size widgets
1036 grid_size_label = GridSizeLabel(
1037 self.level_widget, width=BUTTON_RECT.width,
1038 align="c", fg_color=white)
1039 grid_size_label.rect.move_ip(MENU_LEFT, y)
1040 widgets.append(grid_size_label)
1041 y += grid_size_label.rect.height + MENU_PAD
1042 inc_snap_but = SnapButton(grid_size_label, self, 5)
1043 inc_snap_but.rect = HALF_BUTTON_RECT.copy()
1044 inc_snap_but.rect.move_ip(MENU_LEFT, y)
1045 widgets.append(inc_snap_but)
1046 dec_snap_but = SnapButton(grid_size_label, self, -5)
1047 dec_snap_but.rect = HALF_BUTTON_RECT.copy()
1048 dec_snap_but.rect.move_ip(
1049 MENU_LEFT + MENU_HALF_WIDTH, y)
1050 widgets.append(dec_snap_but)
1051 y += MENU_BUTTON_HEIGHT + MENU_PAD
1052
1053 self.draw_line_but = HighLightButton("Draw interior wall", self,
1054 action=self.set_line_mode)
1055 self.draw_line_but.rect = BUTTON_RECT.copy()
1056 self.draw_line_but.rect.move_ip(MENU_LEFT, y)
1057 widgets.append(self.draw_line_but)
1058 self._light_buttons.append(self.draw_line_but)
1059 y += MENU_BUTTON_HEIGHT + MENU_PAD
1060
1061 fill_but = Button('Fill exterior', action=self.level_widget.set_filled)
1062 fill_but.rect = BUTTON_RECT.copy()
1063 fill_but.rect.move_ip(MENU_LEFT, y)
1064 widgets.append(fill_but)
1065 y += MENU_BUTTON_HEIGHT + MENU_PAD
1066
1067 close_poly_but = Button('Close Polygon',
1068 action=self.level_widget.close_poly)
1069 close_poly_but.rect = BUTTON_RECT.copy()
1070 close_poly_but.rect.move_ip(MENU_LEFT, y)
1071 widgets.append(close_poly_but)
1072 y += MENU_BUTTON_HEIGHT + MENU_PAD
1073
1074 self.show_objs = CheckBox(fg_color=white)
1075 self.show_objs.rect = CHECK_RECT.copy()
1076 self.show_objs.rect.move_ip(MENU_LEFT, y)
1077 label = Label("Show Objects", fg_color=white)
1078 label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
1079 widgets.append(self.show_objs)
1080 widgets.append(label)
1081 y += label.rect.height + MENU_PAD
1082
1083 self.show_enemies = CheckBox(fg_color=white)
1084 self.show_enemies.rect = CHECK_RECT.copy()
1085 self.show_enemies.rect.move_ip(MENU_LEFT, y)
1086 label = Label("Show enemy start pos", fg_color=white)
1087 label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
1088 widgets.append(self.show_enemies)
1089 widgets.append(label)
1090 y += label.rect.height + MENU_PAD
1091
1092 y += MENU_PAD
1093 switch_but = Button('Switch to Objects', action=self.switch_to_objects)
1094 switch_but.rect = BUTTON_RECT.copy()
1095 switch_but.rect.move_ip(MENU_LEFT, y)
1096 widgets.append(switch_but)
1097 y += switch_but.rect.height + MENU_PAD
1098
1099 save_but = Button('Save Level', action=self.save)
1100 save_but.rect = BUTTON_RECT.copy()
1101 save_but.rect.move_ip(MENU_LEFT, y)
1102 widgets.append(save_but)
1103 y += MENU_BUTTON_HEIGHT + MENU_PAD
1104
1105 zoom_out = Button('Zoom out', action=self.level_widget.zoom_out)
1106 zoom_out.rect = BUTTON_RECT.copy()
1107 zoom_out.rect.width = zoom_out.rect.width // 2
1108 zoom_out.rect.move_ip(MENU_LEFT, y)
1109 widgets.append(zoom_out)
1110
1111 zoom_in = Button('Zoom in', action=self.level_widget.zoom_in)
1112 zoom_in.rect = BUTTON_RECT.copy()
1113 zoom_in.width = zoom_in.width // 2
1114 zoom_in.rect.move_ip(MENU_LEFT + zoom_out.width, y)
1115 widgets.append(zoom_in)
1116
1117 y = SCREEN[1] - MENU_BUTTON_HEIGHT - MENU_PAD
1118 quit_but = Button('Quit', action=self.do_quit)
1119 quit_but.rect = BUTTON_RECT.copy()
1120 quit_but.rect.move_ip(MENU_LEFT, y)
1121 widgets.append(quit_but)
1122
1123 self._dMenus['drawing'] = widgets
1124
1125 def _make_objects_menu(self):
1126 widgets = []
1127
1128 # Add poly buttons
1129 y = 15
1130
1131 edit_objs_but = Button('Edit Objects',
1132 action=self.level_widget.edit_objects)
1133 edit_objs_but.rect = BUTTON_RECT.copy()
1134 edit_objs_but.rect.move_ip(MENU_LEFT, y)
1135 widgets.append(edit_objs_but)
1136 y += MENU_BUTTON_HEIGHT + MENU_PAD
1137
1138 edir_enemies_but = Button('Edit Enemies',
1139 action=self.level_widget.edit_enemies)
1140 edir_enemies_but.rect = BUTTON_RECT.copy()
1141 edir_enemies_but.rect.move_ip(MENU_LEFT, y)
1142 widgets.append(edir_enemies_but)
1143 y += MENU_BUTTON_HEIGHT + MENU_PAD
1144
1145 add_obj_but = Button('Add Game Object',
1146 action=self.level_widget.add_game_object)
1147 add_obj_but.rect = BUTTON_RECT.copy()
1148 add_obj_but.rect.move_ip(MENU_LEFT, y)
1149 widgets.append(add_obj_but)
1150 y += MENU_BUTTON_HEIGHT + MENU_PAD
1151
1152 add_puzzle_but = Button('Add Puzzler',
1153 action=self.level_widget.add_puzzler)
1154 add_puzzle_but.rect = BUTTON_RECT.copy()
1155 add_puzzle_but.rect.move_ip(MENU_LEFT, y)
1156 widgets.append(add_puzzle_but)
1157 y += MENU_BUTTON_HEIGHT + MENU_PAD
1158
1159 add_enemy_but = Button('Add Enemy',
1160 action=self.level_widget.add_enemy)
1161 add_enemy_but.rect = BUTTON_RECT.copy()
1162 add_enemy_but.rect.move_ip(MENU_LEFT, y)
1163 widgets.append(add_enemy_but)
1164 y += MENU_BUTTON_HEIGHT + MENU_PAD
1165
1166 y += MENU_PAD
1167 self.sel_mode_but = HighLightButton('Select Object', self,
1168 action=self.sel_mode)
1169 self.sel_mode_but.rect = BUTTON_RECT.copy()
1170 self.sel_mode_but.rect.move_ip(MENU_LEFT, y)
1171 widgets.append(self.sel_mode_but)
1172 self._light_buttons.append(self.sel_mode_but)
1173 y += MENU_BUTTON_HEIGHT + MENU_PAD
1174
1175 y += MENU_PAD
1176 self.move_obj_mode_but = HighLightButton('Move Object', self,
1177 action=self.move_obj_mode)
1178 self.move_obj_mode_but.rect = BUTTON_RECT.copy()
1179 self.move_obj_mode_but.rect.move_ip(MENU_LEFT, y)
1180 widgets.append(self.move_obj_mode_but)
1181 self._light_buttons.append(self.move_obj_mode_but)
1182 y += MENU_BUTTON_HEIGHT + MENU_PAD
1183
1184 y += MENU_PAD
1185 switch_but = Button('Switch to Drawing', action=self.switch_to_draw)
1186 switch_but.rect = BUTTON_RECT.copy()
1187 switch_but.rect.move_ip(MENU_LEFT, y)
1188 widgets.append(switch_but)
1189 y += switch_but.rect.height + MENU_PAD
1190
1191 save_but = Button('Save Level', action=self.save)
1192 save_but.rect = BUTTON_RECT.copy()
1193 save_but.rect.move_ip(MENU_LEFT, y)
1194 widgets.append(save_but)
1195 y += MENU_BUTTON_HEIGHT + MENU_PAD
1196
1197 zoom_out = Button('Zoom out', action=self.level_widget.zoom_out)
1198 zoom_out.rect = BUTTON_RECT.copy()
1199 zoom_out.rect.width = zoom_out.rect.width // 2
1200 zoom_out.rect.move_ip(MENU_LEFT, y)
1201 widgets.append(zoom_out)
1202
1203 zoom_in = Button('Zoom in', action=self.level_widget.zoom_in)
1204 zoom_in.rect = BUTTON_RECT.copy()
1205 zoom_in.width = zoom_in.width // 2
1206 zoom_in.rect.move_ip(MENU_LEFT + zoom_out.width, y)
1207 widgets.append(zoom_in)
1208 y += MENU_BUTTON_HEIGHT + MENU_PAD
1209
1210 y = SCREEN[1] - MENU_BUTTON_HEIGHT - MENU_PAD
1211 quit_but = Button('Quit', action=self.do_quit)
1212 quit_but.rect = BUTTON_RECT.copy()
1213 quit_but.rect.move_ip(MENU_LEFT, y)
1214 widgets.append(quit_but)
1215
1216 self._dMenus['objects'] = widgets
1217
1218 def key_down(self, ev):
1219 if ev.key == pgl.K_ESCAPE:
1220 self.do_quit()
1221 elif ev.key == pgl.K_s:
1222 self.save()
1223 else:
1224 self.level_widget.key_down(ev)
1225
1226 def do_quit(self):
1227 res = ask("Really Quit?")
1228 if res == "OK":
1229 self.quit()
1230
1231 def save(self):
1232 closed, messages = self.level.all_closed()
1233 if closed:
1234 self.level.save()
1235 # display success
1236 alert("Level %s saved successfully." % self.level.name)
1237 else:
1238 # display errors
1239 alert("Failed to save level.\n\n%s" % '\n'.join(messages))
1240
1241 def switch_to_draw(self):
1242 if self._menu_mode != 'drawing':
1243 self._clear_menu()
1244 self._menu_mode = 'drawing'
1245 self._populate_menu()
1246
1247 def switch_to_objects(self):
1248 if self._menu_mode != 'objects':
1249 self._clear_menu()
1250 self._menu_mode = 'objects'
1251 self._populate_menu()
1252
1253 def _clear_menu(self):
1254 for widget in self._dMenus[self._menu_mode]:
1255 self.remove(widget)
1256
1257 def reset_lit_buttons(self):
1258 for but in self._light_buttons:
1259 but.reset()
1260
1261 def _populate_menu(self):
1262 self.level_widget.change_poly(None)
1263 self.level_widget.sel_mode = False
1264 self.level_widget.move_obj_mode = False
1265 self.level_widget.move_obj = None
1266 for widget in self._dMenus[self._menu_mode]:
1267 self.add(widget)
1268 self.invalidate()
1269
1270 def set_line_mode(self):
1271 self.level_widget.line_mode()
1272 self.draw_line_but.highlight()
1273
1274 def sel_mode(self):
1275 self.level_widget.sel_mode = not self.level_widget.sel_mode
1276 if self.level_widget.sel_mode:
1277 self.move_obj_mode_but.reset()
1278 self.sel_mode_but.highlight()
1279 self.level_widget.move_obj_mode = False
1280 self.level_widget.move_obj = None
1281 else:
1282 self.sel_mode_but.reset()
1283
1284 def move_obj_mode(self):
1285 self.level_widget.move_obj_mode = not self.level_widget.move_obj_mode
1286 if self.level_widget.move_obj_mode:
1287 self.sel_mode_but.reset()
1288 self.move_obj_mode_but.highlight()
1289 self.level_widget.sel_mode = False
1290 else:
1291 self.move_obj_mode_but.reset()
1292
1293 def mouse_move(self, ev):
1294 self.level_widget.mouse_move(ev)
1295
1296 def move_point(self):
1297 self.level_widget.set_move_mode()
1298 self.move_point_but.highlight()
1299
1300 def move_poly(self):
1301 self.level_widget.set_move_poly_mode()
1302 self.move_poly_but.highlight()
1303
1304 def draw(self, surface):
1305 # Update checkbox state
1306 if self._menu_mode == 'drawing':
1307 self.level_widget.set_objects(self.show_objs.value)
1308 self.level_widget.set_enemies(self.show_enemies.value)
1309 else:
1310 self.level_widget.set_objects(True)
1311 self.level_widget.set_enemies(True)
1312 super(EditorApp, self).draw(surface)
1313
1314
1315if __name__ == "__main__":
1316 if len(sys.argv) not in [2, 4]:
1317 print 'Please supply a levelname or levelname and level size'
1318 sys.exit()
1319 # Need to ensure we have defaults for rendering
1320 parse_args([])
1321 pygame.display.init()
1322 pygame.font.init()
1323 pygame.display.set_mode((SCREEN[0] + MENU_WIDTH, SCREEN[1]),
1324 pgl.SWSURFACE)
1325 if len(sys.argv) == 2:
1326 level = EditorLevel(sys.argv[1])
1327 level.load(pymunk.Space())
1328 elif len(sys.argv) == 4:
1329 level = EditorLevel(sys.argv[1], int(sys.argv[2]), int(sys.argv[3]))
1330 pygame.display.set_caption('Nagslang Area Editor')
1331 pygame.key.set_repeat(200, 100)
1332 app = EditorApp(level, pygame.display.get_surface())
1333 app.run()
Note: See TracBrowser for help on using the repository browser.