source: tools/area_editor.py@ 524:011a087c4370

Last change on this file since 524:011a087c4370 was 524:011a087c4370, checked in by Neil Muller <drnlmuller@…>, 8 years ago

Fix error catch thinko

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