diff 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
line wrap: on
line diff
--- a/tools/area_editor.py	Wed Sep 04 23:42:09 2013 +0200
+++ b/tools/area_editor.py	Wed Sep 04 23:46:06 2013 +0200
@@ -26,14 +26,15 @@
 from albow.controls import Button, Label, CheckBox
 from albow.dialogs import alert, Dialog
 from albow.layout import Row
+from albow.fields import TextField
 from albow.table_view import TableView, TableColumn
 
 from nagslang.options import parse_args
 from nagslang.constants import SCREEN
 from nagslang.level import Level, POLY_COLORS, LINE_COLOR
-from nagslang.enemies import Enemy, get_editable_enemies
-from nagslang.game_object import get_editable_game_objects
-from nagslang.puzzle import get_editable_puzzlers, PuzzleGlue
+import nagslang.enemies as ne
+import nagslang.game_object as ngo
+import nagslang.puzzle as np
 
 # layout constants
 MENU_BUTTON_HEIGHT = 35
@@ -175,12 +176,40 @@
         # Reset the object state - needed when changing stuff
         self.drawables = []
         self.overlay_drawables = []
-        self._glue = PuzzleGlue()
+        self._glue = np.PuzzleGlue()
         for game_object_dict in self._game_objects:
             self._create_game_object(pymunk.Space(), **game_object_dict)
         for enemy_dict in self._enemies:
             self._create_enemy(pymunk.Space(), **enemy_dict)
 
+    def get_class(self, classname, mod=None):
+        # Get the class given the classname
+        modules = {
+            'game_object': ngo,
+            'enemies': ne,
+            'puzzle': np,
+            }
+        if '.' in classname:
+            modname, classname = classname.split('.')
+            mod = modules[modname]
+        if mod is None:
+            mod = ngo
+        return getattr(mod, classname)
+
+    def try_new_object(self, target, new, old=None):
+        if old in target:
+            target.remove(old)
+        try:
+            target.append(new)
+            self.reset_objs()
+            return True
+        except Exception:
+            target.remove(new)
+            if old is not None:
+                target.append(old)
+            self.reset_objs()
+        return False
+
 
 class ObjectTable(TableView):
 
@@ -212,6 +241,50 @@
         return None
 
 
+class EditClassDialog(Dialog):
+
+    def __init__(self, classname, cls, data):
+        super(EditClassDialog, self).__init__()
+        self.rect = pygame.rect.Rect(0, 0, 800, 550)
+        title = Label("Editing %s" % classname)
+        title.rect = pygame.rect.Rect(100, 10, 600, 25)
+        self.add(title)
+        requires = cls.requires()
+        y = 40
+        self.fields = {}
+        index = 0
+        for requirement, hint in requires:
+            label = Label(requirement)
+            label.rect = pygame.rect.Rect(40, y, 200, 25)
+            self.add(label)
+            field = TextField()
+            field.rect = pygame.rect.Rect(220, y, 400, 25)
+            self.add(field)
+            if data is not None:
+                if requirement in data:
+                    field.set_text('%s' % data[requirement])
+                elif 'args' in data:
+                    # NB: The ordering assumptions in requires should make
+                    # this safe, but it's really, really, really fragile
+                    field.set_text('%s' % data['args'][index])
+                    index += 1
+            self.fields[requirement] = field
+            hintlabel = Label(hint)
+            hintlabel.rect = pygame.rect.Rect(640, y, 100, 25)
+            self.add(hintlabel)
+            y += 30
+        buttons = []
+        for text in ['OK', 'Cancel']:
+            but = Button(text, action=lambda x=text: self.dismiss(x))
+            buttons.append(but)
+        row = Row(buttons)
+        row.rect = pygame.rect.Rect(250, 500, 700, 50)
+        self.add(row)
+
+    def get_data(self):
+        return ''
+
+
 class LevelWidget(Widget):
 
     def __init__(self, level):
@@ -270,11 +343,11 @@
                                    self._draw_lines, self._start_pos)
         if self._draw_objects:
             for thing in self.level.drawables:
-                if not isinstance(thing, Enemy):
+                if not isinstance(thing, ne.Enemy):
                     thing.render(level_surface)
         if self._draw_enemies:
             for thing in self.level.drawables:
-                if isinstance(thing, Enemy):
+                if isinstance(thing, ne.Enemy):
                     thing.render(level_surface)
         surface_area = pygame.rect.Rect(self.pos, SCREEN)
         surface.blit(level_surface, (0, 0), surface_area)
@@ -375,6 +448,13 @@
         else:
             alert("Failed to close the polygon")
 
+    def _edit_class(self, classname, cls, data):
+        # Dialog for class properties
+        dialog = EditClassDialog(classname, cls, data)
+        if dialog.present() == 'OK':
+            return dialog.get_data()
+        return None
+
     def _make_edit_dialog(self, entries):
         # Dialog to hold the editor
         edit_box = Dialog()
@@ -386,7 +466,7 @@
             but = Button(text, action=lambda x=text: edit_box.dismiss(x))
             buttons.append(but)
         row = Row(buttons)
-        row.rect = pygame.rect.Rect(0, 450, 700, 50)
+        row.rect = pygame.rect.Rect(250, 450, 700, 50)
         edit_box.add(row)
         edit_box.get_selection = lambda: table.get_selection()
         return edit_box
@@ -398,8 +478,13 @@
         if choice is None:
             return
         if res == 'OK':
-            # Edit object stuff goes here
-            pass
+            cls = self.level.get_class(choice['classname'])
+            edited = self._edit_class(choice['classname'], cls, choice)
+            if edited is not None:
+                if not self.level.try_new_object(self.level._game_objects,
+                                                 edited, choice):
+                    alert('Failed to update GameObject %s'
+                          % choice['classname'])
         elif res == 'Delete':
             self.level._game_objects.remove(choice)
             self.level.reset_objs()
@@ -411,8 +496,13 @@
         if choice is None:
             return
         if res == 'OK':
-            # Edit object stuff goes here
-            pass
+            cls = self.level.get_class(choice['classname'], ne)
+            edited = self._edit_class(choice['classname'], cls, choice)
+            if edited is not None:
+                if not self.level.try_new_object(self.level._enemies,
+                                                 edited, choice):
+                    alert('Failed to update Enemy %s'
+                          % choice['classname'])
         elif res == 'Delete':
             self.level._enemies.remove(choice)
             self.level.reset_objs()
@@ -431,34 +521,52 @@
             but = Button(text, action=lambda x=text: choice_box.dismiss(x))
             buttons.append(but)
         row = Row(buttons)
-        row.rect = pygame.rect.Rect(0, 450, 700, 50)
+        row.rect = pygame.rect.Rect(250, 450, 700, 50)
         choice_box.add(row)
         choice_box.get_selection = lambda: table.get_selection()
         return choice_box
 
     def add_game_object(self):
-        classes = get_editable_game_objects()
+        classes = ngo.get_editable_game_objects()
+        choose = self._make_choice_dialog(classes)
+        res = choose.present()
+        choice = choose.get_selection()
+        if res == 'OK' and choice is not None:
+            classname = choice['classname']
+            cls = choice['class']
+            new_cls = self._edit_class(classname, cls, None)
+            if new_cls is not None:
+                if not self.level.try_new_object(self.level._game_objects,
+                                                 new_cls, None):
+                    alert('Failed to add GameObject %s' % classname)
+
+    def add_enemy(self):
+        classes = ne.get_editable_enemies()
         choose = self._make_choice_dialog(classes)
         res = choose.present()
         choice = choose.get_selection()
         if res == 'OK' and choice is not None:
-            pass
+            classname = choice['classname']
+            cls = choice['class']
+            new_cls = self._edit_class(classname, cls, None)
+            if new_cls is not None:
+                if not self.level.try_new_object(self.level._enemies,
+                                                 new_cls, None):
+                    alert('Failed to add Enemy %s' % classname)
 
-    def add_enemy(self):
-        classes = get_editable_enemies()
+    def add_puzzler(self):
+        classes = np.get_editable_puzzlers()
         choose = self._make_choice_dialog(classes)
         res = choose.present()
         choice = choose.get_selection()
         if res == 'OK' and choice is not None:
-            pass
-
-    def add_puzzler(self):
-        classes = get_editable_puzzlers()
-        choose = self._make_choice_dialog(classes)
-        res = choose.present()
-        choice = choose.get_selection()
-        if res == 'OK' and choice is not None:
-            pass
+            classname = choice['classname']
+            cls = choice['class']
+            new_cls = self._edit_class(classname, cls, None)
+            if new_cls is not None:
+                if not self.level.try_new_object(self.level._game_objects,
+                                                 new_cls, None):
+                    alert('Failed to add Puzzler %s' % classname)
 
 
 class PolyButton(Button):