changeset 508:6c61d5862310

Massive speical sprite editor rewrite
author Neil Muller <drnlmuller@gmail.com>
date Sun, 18 Sep 2011 00:11:30 +0200
parents f4f883418ac2
children a332f8b3619c
files mamba/habitats/editor.py mamba/level.py mamba/sprites.py mamba/widgets/editsprite.py
diffstat 4 files changed, 160 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/mamba/habitats/editor.py	Sun Sep 18 00:04:42 2011 +0200
+++ b/mamba/habitats/editor.py	Sun Sep 18 00:11:30 2011 +0200
@@ -9,6 +9,7 @@
 import urllib2
 
 from mamba.engine import Habitat, NewHabitatEvent
+from mamba.sprites import find_special_sprites
 from mamba.widgets.level import EditLevelWidget
 from mamba.widgets.text import TextWidget, TextButton
 from mamba.widgets.imagebutton import ImageButtonWidget
@@ -20,7 +21,7 @@
 from mamba.widgets.editlevel import EditLevelBox
 from mamba.level import Level, Tileset, TILE_MAP, THING_MAP, InvalidMapError
 from mamba.data import (check_level_exists, get_level_list, load_file,
-                        load_image)
+                        load_image, load_tile_image)
 from mamba.constants import (SCREEN, EDIT_SCREEN, NAME, ESCAPE_KEYS,
         RESERVED_NAMES, WINDOW_ICON, LEVEL_SERVER)
 
@@ -41,6 +42,7 @@
         self.container.add_callback(MOUSEBUTTONDOWN, self.mouse_event)
         self.mode = 'Tiles'
         self.sprite_mode = 'Add'
+        self.sprite_cls = None
 
     def on_enter(self):
         # We need to juggle the display to the correct size
@@ -139,10 +141,19 @@
                         tile_char, text)
                 tool_list.append(tile_button)
             if self.mode == "Sprites":
-                for name in ['Add', 'Edit', 'Delete']:
+                for cls_name, sprite_cls in find_special_sprites():
+                    image = load_tile_image(sprite_cls.image_name,
+                            self.level.tileset.name)
+                    name = sprite_cls.name
+                    tile_button = ImageButtonWidget((0, 0), image, name,
+                            color='white')
+                    tile_button.add_callback('clicked', self.sprite_tool,
+                            'Add', cls_name, sprite_cls)
+                    tool_list.append(tile_button)
+                for name in ['Edit', 'Delete']:
                     tile_button = TextButton((0, 0), '%s Sprite' % name)
                     tile_button.add_callback('clicked', self.sprite_tool,
-                            name)
+                            name, None, None)
                     tool_list.append(tile_button)
             self.tool_widget = ToolListWidget((button_left, button_height),
                     tool_list, MAX_TOOLS, start_key=K_2)
@@ -362,9 +373,11 @@
         self.level.level_name = new_name
         return None
 
-    def sprite_tool(self, ev, widget, sprite_mode):
+    def sprite_tool(self, ev, widget, sprite_mode, cls_name, sprite_cls):
         """Handle sprite stuff"""
         self.sprite_mode = sprite_mode
+        self.sprite_cls_name = cls_name
+        self.sprite_cls = sprite_cls
         self.level.update_tiles_ascii()  # commit any changes
         self.clear_toolbar()
         self.setup_toolbar()
@@ -377,18 +390,19 @@
         if self.container.paused:
             return False
         tile_pos = self.edit_widget.convert_pos(ev.pos)
-        sprite = self.level.get_sprite_at(tile_pos)
+        sprite, sprite_ascii = self.level.get_sprite_at(tile_pos)
         if self.sprite_mode == 'Delete' and sprite:
-            self.level.remove_sprite(sprite)
+            self.level.remove_sprite(sprite_ascii)
             self.level.restart()
         elif self.sprite_mode == 'Edit' and sprite:
             self.edit_sprite(tile_pos, sprite)
         elif self.sprite_mode == 'Add' and sprite is None:
-            self.edit_sprite(tile_pos, sprite)
+            self.edit_sprite(tile_pos,
+                    (self.sprite_cls_name, self.sprite_cls, None, []))
         return True
 
-    def edit_sprite(self, tile_pos, sprite):
-        sprite_editor = EditSpriteBox((200, 100), tile_pos, sprite,
+    def edit_sprite(self, tile_pos, sprite_info):
+        sprite_editor = EditSpriteBox((200, 100), tile_pos, sprite_info,
                 post_callback=self.commit_line)
         self.display_dialog(sprite_editor)
 
--- a/mamba/level.py	Sun Sep 18 00:04:42 2011 +0200
+++ b/mamba/level.py	Sun Sep 18 00:11:30 2011 +0200
@@ -336,8 +336,13 @@
             except ValueError:
                 continue
             if pos[0] == sprite_pos[0] and pos[1] == sprite_pos[1]:
-                return sprite_ascii
-        return None
+                pos, _sep, rest = sprite_ascii.partition(':')
+                class_name, rest = rest.split(None, 1)
+                args = rest.split()
+                sprite_cls = sprites.find_sprite(class_name)
+                sprite_id, args = args[0], args[1:]
+                return (class_name, sprite_cls, sprite_id, args), sprite_ascii
+        return None, None
 
     def remove_sprite(self, sprite):
         """Remove the given sprite line from the list of sprites"""
@@ -366,10 +371,10 @@
     def add_sprite(self, sprite):
         self.sprites_ascii.append(sprite)
 
-    def replace_sprite(self, sprite):
+    def replace_sprite(self, new_ascii):
         # Need to find the sprite at the same psoition
-        pos, _sep, rest = sprite.partition(':')
+        pos, _sep, rest = new_ascii.partition(':')
         pos = [int(x.strip()) for x in pos.split(',')]
-        old_sprite = self.get_sprite_at(pos)
-        self.sprites_ascii.remove(old_sprite)
-        self.sprites_ascii.append(sprite)
+        old_sprite, old_ascii = self.get_sprite_at(pos)
+        self.sprites_ascii.remove(old_ascii)
+        self.sprites_ascii.append(new_ascii)
--- a/mamba/sprites.py	Sun Sep 18 00:04:42 2011 +0200
+++ b/mamba/sprites.py	Sun Sep 18 00:11:30 2011 +0200
@@ -36,6 +36,15 @@
     return cls
 
 
+def find_special_sprites():
+    results = []
+    for cls_name, cls in globals().iteritems():
+        if isinstance(cls, type) and issubclass(cls, Sprite):
+            if hasattr(cls, 'get_sprite_args'):
+                results.append((cls_name, cls))
+    return results
+
+
 class SpriteImageVariants(object):
     VARIANTS = {
         '....': ('-0', ()),
@@ -250,6 +259,12 @@
             other = world.get_sprite(self.other_id)
             head.shift_tile_and_pixels((other.tile_pos, self.direction))
 
+    @classmethod
+    def get_sprite_args(cls):
+        """Arguments for the tile constructor"""
+        return [('Other ID:', None),
+                ('Direction', ('north', 'east', 'south', 'west'))]
+
 
 class ButtonSprite(SingleImageTileSprite):
     image_name = 'button'
@@ -266,9 +281,15 @@
             other = world.get_sprite(self.other_id)
             other.button_pushed()
 
+    @classmethod
+    def get_sprite_args(cls):
+        """Arguments for the tile constructor"""
+        return [('Other ID:', None)]
+
 
 class GateSprite(SingleImageTileSprite):
     closed_name = 'closed_gate'
+    image_name = 'closed_gate'
     open_image = 'floor'
     name = 'gate'
     tileset = 'lab'
@@ -287,6 +308,11 @@
         self.image_name = self.open_image
         self.image = self.load_image(self.image_name, mutators=())
 
+    @classmethod
+    def get_sprite_args(cls):
+        """Arguments for the tile constructor"""
+        return []
+
 
 class PuddleSprite(SingleImageTileSprite):
     image_name = 'puddle'
--- a/mamba/widgets/editsprite.py	Sun Sep 18 00:04:42 2011 +0200
+++ b/mamba/widgets/editsprite.py	Sun Sep 18 00:11:30 2011 +0200
@@ -5,20 +5,20 @@
 class EditSpriteBox(Box):
     """Edit details for a special sprite on the level map"""
 
-    def __init__(self, rect, sprite_pos, sprite, post_callback=None):
+    def __init__(self, rect, sprite_pos, sprite_info, post_callback=None):
         super(EditSpriteBox, self).__init__(rect)
         self.sprite_pos = sprite_pos
-        if sprite:
-            _, _, rest = sprite.partition(':')
-            self.sprite_name, rest = rest.split(None, 1)
-            args = rest.split()
-            self.sprite_id = args[0]
-            self.sprite_parameters = " ".join(args[1:])
+        sprite_cls_name, sprite_cls, sprite_id, args = sprite_info
+        self.sprite_cls_name = sprite_cls_name
+        self.sprite_cls = sprite_cls
+        if sprite_id:
+            self.sprite_id = sprite_id
         else:
-            self.sprite_name = ''
             self.sprite_id = ''
-            self.sprite_parameters = ""
+        self.sprite_parameters = args
+        self.new_sprite_parameters = []
         self.post_callback = post_callback
+        self.parameter_widgets = []
         self.prepare()
         self.modal = True
 
@@ -26,19 +26,75 @@
         title = TextWidget(self.rect, "Specify Sprite Details")
         self.add(title)
         height = self.rect.top + title.rect.height + 2
-        self.edit_sprite_name = EntryTextWidget((self.rect.left, height),
-                self.sprite_name, prompt='Sprite Class:')
+        self.edit_sprite_name = TextWidget((self.rect.left, height),
+                'Sprite Class: %s' % self.sprite_cls.name)
         self.add(self.edit_sprite_name)
         height += self.edit_sprite_name.rect.height + 2
-        self.edit_sprite_id = EntryTextWidget((self.rect.left, height),
-                self.sprite_id, prompt='Sprite Id:')
+        self.edit_sprite_id = EntryTextWidget((self.rect.left + 20, height),
+                self.sprite_id, prompt='Sprite Id (required):')
         self.add(self.edit_sprite_id)
         height += self.edit_sprite_id.rect.height + 2
-        self.edit_sprite_param = EntryTextWidget((self.rect.left, height),
-                self.sprite_parameters, prompt='Sprite Parameters:')
-        self.add(self.edit_sprite_param)
-        height += self.edit_sprite_param.rect.height + 2
-
+        poss_params = self.sprite_cls.get_sprite_args()
+        if not poss_params:
+            self.sprite_param = TextWidget((self.rect.left, height),
+                    'No Parameters')
+            self.add(self.sprite_param)
+            height += self.sprite_param.rect.height + 2
+        else:
+            self.sprite_param = TextWidget((self.rect.left, height),
+                    'Parameters')
+            self.add(self.sprite_param)
+            height += self.sprite_param.rect.height + 2
+            for i, param_tuple in enumerate(poss_params):
+                if len(self.sprite_parameters) > i:
+                    value = self.sprite_parameters[i]
+                else:
+                    value = None
+                if param_tuple[1] is None:
+                    # Text Entry Parameter
+                    if value is None:
+                        value = ''
+                    edit_widget = EntryTextWidget(
+                            (self.rect.left + 20, height),
+                            value, prompt=param_tuple[0])
+                    self.parameter_widgets.append(edit_widget)
+                    self.add(edit_widget)
+                    height += edit_widget.rect.height
+                elif isinstance(param_tuple[1], tuple):
+                    # We have a list of possible values
+                    if value is None:
+                        value = param_tuple[1][0]  # Take the first
+                    mylist = []
+                    list_width = 0
+                    list_height = 0
+                    for choice in param_tuple[1]:
+                        list_parameter = TextWidget(
+                                (self.rect.left + 20, height),
+                                '%s: %s' % (param_tuple[0], choice))
+                        # So we can pull it out of this later
+                        list_parameter.choice = choice
+                        list_width = max(list_width, list_parameter.rect.width)
+                        change_list = TextButton(
+                                (list_parameter.rect.right + 5, height),
+                                'Next Option')
+                        change_list.add_callback('clicked', self.change_list,
+                                choice, param_tuple[1], mylist)
+                        mylist.append((list_parameter, change_list))
+                        list_height = max(list_height, change_list.rect.height,
+                                list_parameter.rect.height)
+                        if choice == value:
+                            self.add(list_parameter)
+                            self.add(change_list)
+                    for x in mylist:
+                        x[1].rect.left = self.rect.left + list_width + 25
+                        if x[0].rect.height < list_height:
+                            x[0].rect.top += (list_height -
+                                    x[0].rect.height) / 2
+                    height += max(list_parameter.rect.height,
+                            change_list.rect.height)
+                    self.parameter_widgets.append(mylist)
+                # FIXME: Other cases
+        height += 20
         self.ok_button = TextButton((self.rect.left + 10, height), 'OK')
         self.ok_button.add_callback('clicked', self.close, True)
         self.add(self.ok_button)
@@ -49,10 +105,30 @@
         self.rect.width = max(self.rect.width, 400)
         self.rect.height += 5
 
+    def change_list(self, ev, widget, cur_choice, all_choices, widget_list):
+        pos = all_choices.index(cur_choice)
+        if pos == len(all_choices) - 1:
+            next_pos = 0
+        else:
+            next_pos = pos + 1
+        self.remove(widget_list[pos][0])
+        self.remove(widget_list[pos][1])
+        self.add(widget_list[next_pos][0])
+        self.add(widget_list[next_pos][1])
+
     def close(self, ev, widget, do_update):
         if do_update:
-            self.sprite_parameters = self.edit_sprite_param.value
-            self.sprite_name = self.edit_sprite_name.value
+            self.new_sprite_parameters = []
+            for param in self.parameter_widgets:
+                if hasattr(param, 'value'):
+                    self.new_sprite_parameters.append(param.value)
+                elif isinstance(param, list):
+                    # Find the selected one
+                    for choice, _ in param:
+                        if choice in self.children:
+                            # Is selected, so we grab this choice
+                            self.new_sprite_parameters.append(choice.choice)
+                            break
             self.sprite_id = self.edit_sprite_id.value
             sprite = self.make_sprite()
             if not self.post_callback(sprite):
@@ -64,8 +140,9 @@
     def make_sprite(self):
         """Convert values to a sprite line"""
         pos = "%s, %s" % self.sprite_pos
-        return "%s: %s %s %s" % (pos, self.sprite_name, self.sprite_id,
-                self.sprite_parameters)
+        sprite_string = "%s: %s %s %s" % (pos, self.sprite_cls_name,
+                self.sprite_id, " ".join(self.new_sprite_parameters))
+        return sprite_string
 
     def grab_focus(self):
         return self.ok_button.grab_focus()