Mercurial > nagslang
comparison tools/area_editor.py @ 251:611370331bd1
Add framework of edit object dialog
author | Neil Muller <drnlmuller@gmail.com> |
---|---|
date | Wed, 04 Sep 2013 23:46:06 +0200 |
parents | 30137dc83a72 |
children | d4928d4a661a |
comparison
equal
deleted
inserted
replaced
250:d9a54f152ac3 | 251:611370331bd1 |
---|---|
24 from albow.root import RootWidget | 24 from albow.root import RootWidget |
25 from albow.widget import Widget | 25 from albow.widget import Widget |
26 from albow.controls import Button, Label, CheckBox | 26 from albow.controls import Button, Label, CheckBox |
27 from albow.dialogs import alert, Dialog | 27 from albow.dialogs import alert, Dialog |
28 from albow.layout import Row | 28 from albow.layout import Row |
29 from albow.fields import TextField | |
29 from albow.table_view import TableView, TableColumn | 30 from albow.table_view import TableView, TableColumn |
30 | 31 |
31 from nagslang.options import parse_args | 32 from nagslang.options import parse_args |
32 from nagslang.constants import SCREEN | 33 from nagslang.constants import SCREEN |
33 from nagslang.level import Level, POLY_COLORS, LINE_COLOR | 34 from nagslang.level import Level, POLY_COLORS, LINE_COLOR |
34 from nagslang.enemies import Enemy, get_editable_enemies | 35 import nagslang.enemies as ne |
35 from nagslang.game_object import get_editable_game_objects | 36 import nagslang.game_object as ngo |
36 from nagslang.puzzle import get_editable_puzzlers, PuzzleGlue | 37 import nagslang.puzzle as np |
37 | 38 |
38 # layout constants | 39 # layout constants |
39 MENU_BUTTON_HEIGHT = 35 | 40 MENU_BUTTON_HEIGHT = 35 |
40 MENU_PAD = 6 | 41 MENU_PAD = 6 |
41 MENU_HALF_PAD = MENU_PAD // 2 | 42 MENU_HALF_PAD = MENU_PAD // 2 |
173 | 174 |
174 def reset_objs(self): | 175 def reset_objs(self): |
175 # Reset the object state - needed when changing stuff | 176 # Reset the object state - needed when changing stuff |
176 self.drawables = [] | 177 self.drawables = [] |
177 self.overlay_drawables = [] | 178 self.overlay_drawables = [] |
178 self._glue = PuzzleGlue() | 179 self._glue = np.PuzzleGlue() |
179 for game_object_dict in self._game_objects: | 180 for game_object_dict in self._game_objects: |
180 self._create_game_object(pymunk.Space(), **game_object_dict) | 181 self._create_game_object(pymunk.Space(), **game_object_dict) |
181 for enemy_dict in self._enemies: | 182 for enemy_dict in self._enemies: |
182 self._create_enemy(pymunk.Space(), **enemy_dict) | 183 self._create_enemy(pymunk.Space(), **enemy_dict) |
184 | |
185 def get_class(self, classname, mod=None): | |
186 # Get the class given the classname | |
187 modules = { | |
188 'game_object': ngo, | |
189 'enemies': ne, | |
190 'puzzle': np, | |
191 } | |
192 if '.' in classname: | |
193 modname, classname = classname.split('.') | |
194 mod = modules[modname] | |
195 if mod is None: | |
196 mod = ngo | |
197 return getattr(mod, classname) | |
198 | |
199 def try_new_object(self, target, new, old=None): | |
200 if old in target: | |
201 target.remove(old) | |
202 try: | |
203 target.append(new) | |
204 self.reset_objs() | |
205 return True | |
206 except Exception: | |
207 target.remove(new) | |
208 if old is not None: | |
209 target.append(old) | |
210 self.reset_objs() | |
211 return False | |
183 | 212 |
184 | 213 |
185 class ObjectTable(TableView): | 214 class ObjectTable(TableView): |
186 | 215 |
187 columns = [TableColumn("Object", 690, 'l', '%r')] | 216 columns = [TableColumn("Object", 690, 'l', '%r')] |
208 | 237 |
209 def get_selection(self): | 238 def get_selection(self): |
210 if self.selected_row >= 0: | 239 if self.selected_row >= 0: |
211 return self.data[self.selected_row] | 240 return self.data[self.selected_row] |
212 return None | 241 return None |
242 | |
243 | |
244 class EditClassDialog(Dialog): | |
245 | |
246 def __init__(self, classname, cls, data): | |
247 super(EditClassDialog, self).__init__() | |
248 self.rect = pygame.rect.Rect(0, 0, 800, 550) | |
249 title = Label("Editing %s" % classname) | |
250 title.rect = pygame.rect.Rect(100, 10, 600, 25) | |
251 self.add(title) | |
252 requires = cls.requires() | |
253 y = 40 | |
254 self.fields = {} | |
255 index = 0 | |
256 for requirement, hint in requires: | |
257 label = Label(requirement) | |
258 label.rect = pygame.rect.Rect(40, y, 200, 25) | |
259 self.add(label) | |
260 field = TextField() | |
261 field.rect = pygame.rect.Rect(220, y, 400, 25) | |
262 self.add(field) | |
263 if data is not None: | |
264 if requirement in data: | |
265 field.set_text('%s' % data[requirement]) | |
266 elif 'args' in data: | |
267 # NB: The ordering assumptions in requires should make | |
268 # this safe, but it's really, really, really fragile | |
269 field.set_text('%s' % data['args'][index]) | |
270 index += 1 | |
271 self.fields[requirement] = field | |
272 hintlabel = Label(hint) | |
273 hintlabel.rect = pygame.rect.Rect(640, y, 100, 25) | |
274 self.add(hintlabel) | |
275 y += 30 | |
276 buttons = [] | |
277 for text in ['OK', 'Cancel']: | |
278 but = Button(text, action=lambda x=text: self.dismiss(x)) | |
279 buttons.append(but) | |
280 row = Row(buttons) | |
281 row.rect = pygame.rect.Rect(250, 500, 700, 50) | |
282 self.add(row) | |
283 | |
284 def get_data(self): | |
285 return '' | |
213 | 286 |
214 | 287 |
215 class LevelWidget(Widget): | 288 class LevelWidget(Widget): |
216 | 289 |
217 def __init__(self, level): | 290 def __init__(self, level): |
268 mouse_pos = None | 341 mouse_pos = None |
269 level_surface = level.draw(mouse_pos, self.cur_poly, self.filled_mode, | 342 level_surface = level.draw(mouse_pos, self.cur_poly, self.filled_mode, |
270 self._draw_lines, self._start_pos) | 343 self._draw_lines, self._start_pos) |
271 if self._draw_objects: | 344 if self._draw_objects: |
272 for thing in self.level.drawables: | 345 for thing in self.level.drawables: |
273 if not isinstance(thing, Enemy): | 346 if not isinstance(thing, ne.Enemy): |
274 thing.render(level_surface) | 347 thing.render(level_surface) |
275 if self._draw_enemies: | 348 if self._draw_enemies: |
276 for thing in self.level.drawables: | 349 for thing in self.level.drawables: |
277 if isinstance(thing, Enemy): | 350 if isinstance(thing, ne.Enemy): |
278 thing.render(level_surface) | 351 thing.render(level_surface) |
279 surface_area = pygame.rect.Rect(self.pos, SCREEN) | 352 surface_area = pygame.rect.Rect(self.pos, SCREEN) |
280 surface.blit(level_surface, (0, 0), surface_area) | 353 surface.blit(level_surface, (0, 0), surface_area) |
281 | 354 |
282 def change_poly(self, new_poly): | 355 def change_poly(self, new_poly): |
373 alert("Successfully closed the polygon") | 446 alert("Successfully closed the polygon") |
374 self.change_poly(None) | 447 self.change_poly(None) |
375 else: | 448 else: |
376 alert("Failed to close the polygon") | 449 alert("Failed to close the polygon") |
377 | 450 |
451 def _edit_class(self, classname, cls, data): | |
452 # Dialog for class properties | |
453 dialog = EditClassDialog(classname, cls, data) | |
454 if dialog.present() == 'OK': | |
455 return dialog.get_data() | |
456 return None | |
457 | |
378 def _make_edit_dialog(self, entries): | 458 def _make_edit_dialog(self, entries): |
379 # Dialog to hold the editor | 459 # Dialog to hold the editor |
380 edit_box = Dialog() | 460 edit_box = Dialog() |
381 edit_box.rect = pygame.rect.Rect(0, 0, 700, 500) | 461 edit_box.rect = pygame.rect.Rect(0, 0, 700, 500) |
382 table = ObjectTable(entries) | 462 table = ObjectTable(entries) |
384 buttons = [] | 464 buttons = [] |
385 for text in ['OK', 'Delete', 'Cancel']: | 465 for text in ['OK', 'Delete', 'Cancel']: |
386 but = Button(text, action=lambda x=text: edit_box.dismiss(x)) | 466 but = Button(text, action=lambda x=text: edit_box.dismiss(x)) |
387 buttons.append(but) | 467 buttons.append(but) |
388 row = Row(buttons) | 468 row = Row(buttons) |
389 row.rect = pygame.rect.Rect(0, 450, 700, 50) | 469 row.rect = pygame.rect.Rect(250, 450, 700, 50) |
390 edit_box.add(row) | 470 edit_box.add(row) |
391 edit_box.get_selection = lambda: table.get_selection() | 471 edit_box.get_selection = lambda: table.get_selection() |
392 return edit_box | 472 return edit_box |
393 | 473 |
394 def edit_objects(self): | 474 def edit_objects(self): |
396 res = edit_box.present() | 476 res = edit_box.present() |
397 choice = edit_box.get_selection() | 477 choice = edit_box.get_selection() |
398 if choice is None: | 478 if choice is None: |
399 return | 479 return |
400 if res == 'OK': | 480 if res == 'OK': |
401 # Edit object stuff goes here | 481 cls = self.level.get_class(choice['classname']) |
402 pass | 482 edited = self._edit_class(choice['classname'], cls, choice) |
483 if edited is not None: | |
484 if not self.level.try_new_object(self.level._game_objects, | |
485 edited, choice): | |
486 alert('Failed to update GameObject %s' | |
487 % choice['classname']) | |
403 elif res == 'Delete': | 488 elif res == 'Delete': |
404 self.level._game_objects.remove(choice) | 489 self.level._game_objects.remove(choice) |
405 self.level.reset_objs() | 490 self.level.reset_objs() |
406 | 491 |
407 def edit_enemies(self): | 492 def edit_enemies(self): |
409 res = edit_box.present() | 494 res = edit_box.present() |
410 choice = edit_box.get_selection() | 495 choice = edit_box.get_selection() |
411 if choice is None: | 496 if choice is None: |
412 return | 497 return |
413 if res == 'OK': | 498 if res == 'OK': |
414 # Edit object stuff goes here | 499 cls = self.level.get_class(choice['classname'], ne) |
415 pass | 500 edited = self._edit_class(choice['classname'], cls, choice) |
501 if edited is not None: | |
502 if not self.level.try_new_object(self.level._enemies, | |
503 edited, choice): | |
504 alert('Failed to update Enemy %s' | |
505 % choice['classname']) | |
416 elif res == 'Delete': | 506 elif res == 'Delete': |
417 self.level._enemies.remove(choice) | 507 self.level._enemies.remove(choice) |
418 self.level.reset_objs() | 508 self.level.reset_objs() |
419 | 509 |
420 def _make_choice_dialog(self, classes): | 510 def _make_choice_dialog(self, classes): |
429 buttons = [] | 519 buttons = [] |
430 for text in ['OK', 'Cancel']: | 520 for text in ['OK', 'Cancel']: |
431 but = Button(text, action=lambda x=text: choice_box.dismiss(x)) | 521 but = Button(text, action=lambda x=text: choice_box.dismiss(x)) |
432 buttons.append(but) | 522 buttons.append(but) |
433 row = Row(buttons) | 523 row = Row(buttons) |
434 row.rect = pygame.rect.Rect(0, 450, 700, 50) | 524 row.rect = pygame.rect.Rect(250, 450, 700, 50) |
435 choice_box.add(row) | 525 choice_box.add(row) |
436 choice_box.get_selection = lambda: table.get_selection() | 526 choice_box.get_selection = lambda: table.get_selection() |
437 return choice_box | 527 return choice_box |
438 | 528 |
439 def add_game_object(self): | 529 def add_game_object(self): |
440 classes = get_editable_game_objects() | 530 classes = ngo.get_editable_game_objects() |
441 choose = self._make_choice_dialog(classes) | 531 choose = self._make_choice_dialog(classes) |
442 res = choose.present() | 532 res = choose.present() |
443 choice = choose.get_selection() | 533 choice = choose.get_selection() |
444 if res == 'OK' and choice is not None: | 534 if res == 'OK' and choice is not None: |
445 pass | 535 classname = choice['classname'] |
536 cls = choice['class'] | |
537 new_cls = self._edit_class(classname, cls, None) | |
538 if new_cls is not None: | |
539 if not self.level.try_new_object(self.level._game_objects, | |
540 new_cls, None): | |
541 alert('Failed to add GameObject %s' % classname) | |
446 | 542 |
447 def add_enemy(self): | 543 def add_enemy(self): |
448 classes = get_editable_enemies() | 544 classes = ne.get_editable_enemies() |
449 choose = self._make_choice_dialog(classes) | 545 choose = self._make_choice_dialog(classes) |
450 res = choose.present() | 546 res = choose.present() |
451 choice = choose.get_selection() | 547 choice = choose.get_selection() |
452 if res == 'OK' and choice is not None: | 548 if res == 'OK' and choice is not None: |
453 pass | 549 classname = choice['classname'] |
550 cls = choice['class'] | |
551 new_cls = self._edit_class(classname, cls, None) | |
552 if new_cls is not None: | |
553 if not self.level.try_new_object(self.level._enemies, | |
554 new_cls, None): | |
555 alert('Failed to add Enemy %s' % classname) | |
454 | 556 |
455 def add_puzzler(self): | 557 def add_puzzler(self): |
456 classes = get_editable_puzzlers() | 558 classes = np.get_editable_puzzlers() |
457 choose = self._make_choice_dialog(classes) | 559 choose = self._make_choice_dialog(classes) |
458 res = choose.present() | 560 res = choose.present() |
459 choice = choose.get_selection() | 561 choice = choose.get_selection() |
460 if res == 'OK' and choice is not None: | 562 if res == 'OK' and choice is not None: |
461 pass | 563 classname = choice['classname'] |
564 cls = choice['class'] | |
565 new_cls = self._edit_class(classname, cls, None) | |
566 if new_cls is not None: | |
567 if not self.level.try_new_object(self.level._game_objects, | |
568 new_cls, None): | |
569 alert('Failed to add Puzzler %s' % classname) | |
462 | 570 |
463 | 571 |
464 class PolyButton(Button): | 572 class PolyButton(Button): |
465 """Button for coosing the correct polygon""" | 573 """Button for coosing the correct polygon""" |
466 | 574 |