ya2 · news · projects · code · about

editor: start items
authorFlavio Calva <f.calva@gmail.com>
Sat, 12 Nov 2022 07:46:20 +0000 (09:46 +0200)
committerFlavio Calva <f.calva@gmail.com>
Fri, 11 Nov 2022 16:12:54 +0000 (17:12 +0100)
assets/images/buttons/start_items.png [new file with mode: 0644]
pmachines/editor/scene.py
pmachines/editor/start_items.py [new file with mode: 0644]
prj.org

diff --git a/assets/images/buttons/start_items.png b/assets/images/buttons/start_items.png
new file mode 100644 (file)
index 0000000..3db9e99
Binary files /dev/null and b/assets/images/buttons/start_items.png differ
index e0a6a67c28ebf4bb6f326858770345c601927978..b2d3795e2677073bfd3dd92dd26e58aa135f0cb5 100644 (file)
@@ -16,6 +16,7 @@ from direct.showbase.DirectObject import DirectObject
 from pmachines.items.item import Item
 from pmachines.editor.scene_list import SceneList
 from pmachines.editor.inspector import Inspector
+from pmachines.editor.start_items import StartItems
 
 
 class SceneEditor(DirectObject):
@@ -176,8 +177,15 @@ class SceneEditor(DirectObject):
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
         DirectButton(
-            image=load_images_btn('plus', 'gray'), scale=.05,
+            image=load_images_btn('start_items', 'gray'), scale=.05,
             pos=(.06, 1, .58),
+            parent=self._frm, command=self.__on_start_items, state=NORMAL, relief=FLAT,
+            frameColor=fcols[0],
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        DirectButton(
+            image=load_images_btn('plus', 'gray'), scale=.05,
+            pos=(.06, 1, .7),
             parent=self._frm, command=self.__on_new_scene, state=NORMAL, relief=FLAT,
             frameColor=fcols[0],
             rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
@@ -185,6 +193,8 @@ class SceneEditor(DirectObject):
         messenger.send('editor-start')
         self.accept('editor-item-click', self.__on_item_click)
         self.accept('editor-inspector-destroy', self.__on_inspector_destroy)
+        self.accept('editor-start-items-save', self.__on_start_items_save)
+        self.accept('editor-start-items-destroy', self.__on_start_items_destroy)
 
     def __on_close(self):
         self.__json['name'] = self.__name_entry.get()
@@ -270,9 +280,21 @@ class SceneEditor(DirectObject):
         messenger.send('editor-stop')
         messenger.send('new_scene')
 
+    def __on_start_items(self):
+        self.__start_items = StartItems(self.__json['start_items'])
+
+    def __on_start_items_save(self, start_items):
+        self.__json['start_items'] = start_items
+        self.__on_save()
+
+    def __on_start_items_destroy(self):
+        self.__start_items = None
+
     def destroy(self):
         self._frm.destroy()
         if self.__inspector:
             self.__inspector.destroy()
         self.ignore('editor-item-click')
         self.ignore('editor-inspector-destroy')
+        self.ignore('editor-start-items-save')
+        self.ignore('editor-start-items-destroy')
diff --git a/pmachines/editor/start_items.py b/pmachines/editor/start_items.py
new file mode 100644 (file)
index 0000000..02fa3ca
--- /dev/null
@@ -0,0 +1,312 @@
+from collections import namedtuple
+from glob import glob
+from importlib import import_module
+from os.path import basename
+from inspect import isclass
+from panda3d.core import Texture, TextNode
+from direct.gui.OnscreenImage import OnscreenImage
+from direct.gui.DirectGui import DirectButton, DirectFrame, DirectEntry, DirectOptionMenu, OkDialog
+from direct.gui.DirectGuiGlobals import FLAT, NORMAL
+from direct.gui.OnscreenText import OnscreenText
+from direct.showbase.DirectObject import DirectObject
+from pmachines.items.item import Item, ItemStrategy, FixedStrategy, StillStrategy
+from pmachines.items.box import HitStrategy
+from pmachines.items.domino import DownStrategy, UpStrategy
+
+
+class StartItems(DirectObject):
+
+    def __init__(self, json_items):
+        super().__init__()
+        self.__items = json_items
+        self.__json = self.__items[0]
+        self._font = base.loader.load_font(
+            'assets/fonts/Hanken-Book.ttf')
+        self._font.clear()
+        self._font.set_pixels_per_unit(60)
+        self._font.set_minfilter(Texture.FTLinearMipmapLinear)
+        self._font.set_outline((0, 0, 0, 1), .8, .2)
+        self._common = {
+            'scale': .046,
+            'text_font': self._font,
+            'text_fg': (.9, .9, .9, 1),
+            'relief': FLAT,
+            'frameColor': (.4, .4, .4, .14),
+            'rolloverSound': loader.load_sfx(
+                'assets/audio/sfx/rollover.ogg'),
+            'clickSound': loader.load_sfx(
+                'assets/audio/sfx/click.ogg')}
+        w, h = .8, 1.04
+        self._frm = DirectFrame(frameColor=(.4, .4, .4, .06),
+                                frameSize=(0, w, -h, 0),
+                                parent=base.a2dTopRight,
+                                pos=(-w, 0, 0))
+        self.__z = -.08
+        item_modules = glob('pmachines/items/*.py')
+        item_modules = [basename(i)[:-3] for i in item_modules]
+        new_items = ['']
+        for item_module in item_modules:
+            mod_name = 'pmachines.items.' + item_module
+            for member in import_module(mod_name).__dict__.values():
+                if isclass(member) and issubclass(member, Item) and \
+                        member != Item:
+                    new_items = list(set(new_items + [member.__name__]))
+        t, item_class_entry = self.__add_row_option(_('class'), '', new_items, self.on_edit_class)
+        t, count_entry = self.__add_row(_('count'), '', self.on_edit_count)
+        t, scale_entry = self.__add_row(_('scale'), '', self.on_edit_scale)
+        t, mass_entry = self.__add_row(_('mass'), '', self.on_edit_mass)
+        t, restitution_entry = self.__add_row(_('restitution'), '', self.on_edit_restitution)
+        t, friction_entry = self.__add_row(_('friction'), '', self.on_edit_friction)
+        t, id_entry = self.__add_row(_('id'), '', self.on_edit_id)
+        new_items = ['']
+        for item_module in item_modules:
+            mod_name = 'pmachines.items.' + item_module
+            for member in import_module(mod_name).__dict__.values():
+                if isclass(member) and issubclass(member, ItemStrategy) and \
+                        member != ItemStrategy:
+                    new_items = list(set(new_items + [member.__name__]))
+        t, strategy_entry = self.__add_row_option(_('strategy'), '', new_items, self.on_edit_strategy)
+        t, strategy_args_entry = self.__add_row(_('strategy_args'), '', self.on_edit_strategy_args)
+        fields = ['scale', 'mass', 'restitution', 'friction', 'id', 'strategy', 'strategy_args', 'item_class', 'count']
+        Entries = namedtuple('Entries', fields)
+        self.__entries = Entries(scale_entry, mass_entry, restitution_entry, friction_entry, id_entry, strategy_entry, strategy_args_entry, item_class_entry, count_entry)
+        self.__set(self.__json)
+        def load_images_btn(path, col):
+            colors = {
+                'gray': [
+                    (.6, .6, .6, 1),  # ready
+                    (1, 1, 1, 1), # press
+                    (.8, .8, .8, 1), # rollover
+                    (.4, .4, .4, .4)],
+                'green': [
+                    (.1, .68, .1, 1),
+                    (.1, 1, .1, 1),
+                    (.1, .84, .1, 1),
+                    (.4, .1, .1, .4)]}[col]
+            return [self.__load_img_btn(path, col) for col in colors]
+        fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
+        DirectButton(
+            image=load_images_btn('exitRight', 'gray'), scale=.05,
+            pos=(.54, 1, -h + .06),
+            parent=self._frm, command=self.destroy, state=NORMAL, relief=FLAT,
+            frameColor=fcols[0],
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        DirectButton(
+            image=load_images_btn('save', 'gray'), scale=.05,
+            pos=(.42, 1, -h + .06),
+            parent=self._frm, command=self.__save, state=NORMAL, relief=FLAT,
+            frameColor=fcols[0],
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        DirectButton(
+            image=load_images_btn('trashcan', 'gray'), scale=.05,
+            pos=(.3, 1, -h + .06),
+            parent=self._frm, command=self.__delete_item, state=NORMAL, relief=FLAT,
+            frameColor=fcols[0],
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        DirectButton(
+            image=load_images_btn('plus', 'gray'), scale=.05,
+            pos=(.06, 1, -h + .06),
+            parent=self._frm, command=self.__new_item, state=NORMAL, relief=FLAT,
+            frameColor=fcols[0],
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        DirectButton(
+            image=load_images_btn('right', 'gray'), scale=.05,
+            pos=(.18, 1, -h + .06),
+            parent=self._frm, command=self.__next_item, state=NORMAL, relief=FLAT,
+            frameColor=fcols[0],
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+
+    def __add_row(self, label, text, callback):
+        tw = 10
+        t = OnscreenText(
+            label,
+            pos=(.03, self.__z), parent=self._frm,
+            font=self._common['text_font'],
+            scale=self._common['scale'],
+            fg=self._common['text_fg'],
+            wordwrap=20, align=TextNode.ALeft)
+        e = DirectEntry(
+            scale=self._common['scale'],
+            pos=(.30, 1, self.__z),
+            entryFont=self._font,
+            width=tw,
+            cursorKeys=True,
+            frameColor=self._common['frameColor'],
+            initialText=text,
+            parent=self._frm,
+            text_fg=self._common['text_fg'],
+            command=callback)
+        self.__z -= .1
+        return t, e
+
+    def __add_row_option(self, label, text, items, callback):
+        item_modules = glob('pmachines/items/*.py')
+        item_modules = [basename(i)[:-3] for i in item_modules]
+        t = OnscreenText(
+            label,
+            pos=(.03, self.__z), parent=self._frm,
+            font=self._common['text_font'],
+            scale=self._common['scale'],
+            fg=self._common['text_fg'],
+            wordwrap=20, align=TextNode.ALeft)
+        e = DirectOptionMenu(
+            scale=self._common['scale'],
+            initialitem=text,
+            pos=(.30, 1, self.__z),
+            items=items,
+            parent=self._frm,
+            command=callback,
+            state=NORMAL,
+            relief=FLAT,
+            item_relief=FLAT,
+            frameColor=self._common['frameColor'],
+            item_frameColor=self._common['frameColor'],
+            popupMenu_frameColor=self._common['frameColor'],
+            popupMarker_frameColor=self._common['frameColor'],
+            text_font=self._font,
+            text_fg=self._common['text_fg'],
+            highlightColor=(.9, .9, .9, .9),
+            item_text_font=self._font,
+            item_text_fg=self._common['text_fg'],
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        self.__z -= .1
+        return t, e
+
+    def __load_img_btn(self, path, col):
+        img = OnscreenImage('assets/images/buttons/%s.dds' % path)
+        img.set_transparency(True)
+        img.set_color(col)
+        img.detach_node()
+        return img
+
+    def on_edit_scale(self, txt):
+        self.__json['model_scale'] = float(txt)
+
+    def on_edit_mass(self, txt):
+        self.__json['mass'] = float(txt)
+
+    def on_edit_restitution(self, txt):
+        self.__json['restitution'] = float(txt)
+
+    def on_edit_friction(self, txt):
+        self.__json['friction'] = float(txt)
+
+    def on_edit_id(self, txt):
+        self.__json['id'] = txt
+
+    def on_edit_strategy(self, txt):
+        if txt:
+            self.__json['strategy'] = txt
+            self.__entries.strategy_args.set('')
+
+    def on_edit_strategy_args(self, txt):
+        self.__json['strategy_args'] = txt
+
+    def on_edit_class(self, txt):
+        if txt:
+            self.__json['class'] = txt
+
+    def on_edit_count(self, txt):
+        self.__json['count'] = int(txt)
+
+    def __new_item(self):
+        curr_index = self.__items.index(self.__json)
+        self.__json = {}
+        curr_index = (curr_index + 1) % len(self.__items)
+        self.__items.insert(curr_index, self.__json)
+        self.__set(self.__json)
+        import pprint
+        pprint.pprint(self.__items)
+
+    def __next_item(self):
+        curr_index = self.__items.index(self.__json)
+        self.__json = self.__items[(curr_index + 1) % len(self.__items)]
+        self.__set(self.__json)
+        import pprint
+        pprint.pprint(self.__items)
+
+    def __delete_item(self):
+        curr_index = self.__items.index(self.__json)
+        if curr_index == len(self.__items): curr_index = 0
+        self.__items.remove(self.__json)
+        if not self.__items:
+            self.__items = [{}]
+        self.__json = self.__items[curr_index]
+        self.__set(self.__json)
+        import pprint
+        pprint.pprint(self.__items)
+
+    def __set(self, json):
+        if 'model_scale' in json:
+            self.__entries.scale.set(str(json['model_scale']))
+        else:
+            self.__entries.scale.set('')
+        if 'mass' in json:
+            self.__entries.mass.set(str(json['mass']))
+        else:
+            self.__entries.mass.set('')
+        if 'restitution' in json:
+            self.__entries.restitution.set(str(json['restitution']))
+        else:
+            self.__entries.restitution.set('')
+        if 'friction' in json:
+            self.__entries.friction.set(str(json['friction']))
+        else:
+            self.__entries.friction.set('')
+        if 'id' in json:
+            self.__entries.id.set(str(json['id']))
+        else:
+            self.__entries.id.set('')
+        if 'strategy' in json:
+            self.__entries.strategy.set(str(json['strategy']))
+        else:
+            self.__entries.strategy.set('')
+        if 'strategy_args' in json:
+            self.__entries.strategy_args.set(str(json['strategy_args']))
+        else:
+            self.__entries.strategy_args.set('')
+        if 'class' in json:
+            self.__entries.item_class.set(str(json['class']))
+        else:
+            self.__entries.item_class.set('')
+        if 'count' in json:
+            self.__entries.count.set(str(json['count']))
+        else:
+            self.__entries.count.set('')
+        import pprint
+        pprint.pprint(self.__items)
+
+    def __save(self):
+        messenger.send('editor-start-items-save', [self.__items])
+
+    def __show_error_popup(self):
+        self.__dialog = OkDialog(dialogName='Strategy args errors',
+                                    text=_('There are errors in the strategy args.'),
+                                    command=self.__actually_close)
+        self.__dialog['frameColor'] = (.4, .4, .4, .14)
+        self.__dialog['relief'] = FLAT
+        self.__dialog.component('text0')['fg'] = (.9, .9, .9, 1)
+        self.__dialog.component('text0')['font'] = self._font
+        for b in self.__dialog.buttonList:
+            b['frameColor'] = (.4, .4, .4, .14)
+            b.component('text0')['fg'] = (.9, .9, .9, 1)
+            b.component('text0')['font'] = self._font
+            b.component('text1')['fg'] = (.9, .1, .1, 1)
+            b.component('text1')['font'] = self._font
+            b.component('text2')['fg'] = (.9, .9, .1, 1)
+            b.component('text2')['font'] = self._font
+
+    def __actually_close(self, arg):
+        self.__entries.strategy.set('')
+        self.__entries.strategy_args.set('')
+        self.__dialog.cleanup()
+
+    def destroy(self):
+        self._frm.destroy()
+        messenger.send('editor-start-items-destroy')
diff --git a/prj.org b/prj.org
index fece1dbb85137278f55ade250b614df7a5af0193..3471bba456605ef60b9d38b5f6703e7ad2e62d4d 100644 (file)
--- a/prj.org
+++ b/prj.org
   - [X] strategy and strategy_args
   - [X] pop up if errors with arguments in strategy_args
 - [X] new scene
-- [ ] start items
+- [X] start items
+  - [X] inspector without position and roll
+  - [X] optionmenu for class
+  - [X] field for quantity
+  - [X] new item
+  - [X] left/right arrow
+  - [X] json
 - [ ] editing of test_items in the editor for functional tests
 - [ ] define functional tests
 * BACKLOG tooltips for buttons