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