"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"PO-Revision-Date: 2022-10-06 16:30+0200\n"
+"PO-Revision-Date: 2022-10-20 15:23+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: pmachines/scene.py:101
+#: pmachines/scene.py:102
msgid "Scene: "
msgstr "Scena: "
-#: pmachines/scene.py:623
+#: pmachines/scene.py:624
msgid "You win!"
msgstr "Hai vinto!"
-#: pmachines/scene.py:696
+#: pmachines/scene.py:697
msgid "You have failed!"
msgstr "Hai perso!"
" \ 1scale\ 1Luisa Tenuta\ 2\n"
" \ 1scale\ 1Damiana Ercolani\ 2"
-#: pmachines/editor/scene.py:39
+#: pmachines/editor/scene_list.py:34
+msgid "Write the file names (without the extension), one file for each line"
+msgstr "Scrivi i nomi dei file (senza estensione), uno per riga."
+
+#: pmachines/editor/scene_list.py:96 pmachines/editor/scene.py:127
+msgid "You have unsaved changes. Really quit?"
+msgstr "Hai modifiche non salvate, vuoi veramente uscire?"
+
+#: pmachines/editor/scene.py:43
msgid "Name"
msgstr "Nome"
-#: pmachines/editor/scene.py:54
+#: pmachines/editor/scene.py:58
msgid "Description"
msgstr "Descrizione"
-#: pmachines/editor/scene.py:120 pmachines/editor/scene_list.py:96
-msgid "You have unsaved changes. Really quit?"
-msgstr "Hai modifiche non salvate, vuoi veramente uscire?"
+#: pmachines/editor/inspector.py:43
+msgid "position"
+msgstr "posizione"
-#: pmachines/editor/scene_list.py:34
-msgid "Write the file names (without the extension), one file for each line"
-msgstr "Scrivi i nomi dei file (senza estensione), uno per riga."
+#: pmachines/editor/inspector.py:44
+msgid "roll"
+msgstr "roll"
+
+#: pmachines/editor/inspector.py:45
+msgid "scale"
+msgstr "scala"
+
+#: pmachines/editor/inspector.py:46
+msgid "mass"
+msgstr "massa"
+
+#: pmachines/editor/inspector.py:47
+msgid "restitution"
+msgstr "rimbalzo"
+
+#: pmachines/editor/inspector.py:48
+msgid "friction"
+msgstr "frizione"
#~ msgid ""
#~ "Goal: the left box must hit the right box\n"
--- /dev/null
+from collections import namedtuple
+from panda3d.core import Texture, TextNode
+from direct.gui.OnscreenImage import OnscreenImage
+from direct.gui.DirectGui import DirectButton, DirectFrame, DirectEntry
+from direct.gui.DirectGuiGlobals import FLAT, NORMAL
+from direct.gui.OnscreenText import OnscreenText
+from direct.showbase.DirectObject import DirectObject
+
+
+class Inspector(DirectObject):
+
+ def __init__(self, item):
+ super().__init__()
+ self.__item = item
+ 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, .76
+ self._frm = DirectFrame(frameColor=(.4, .4, .4, .06),
+ frameSize=(0, w, -h, 0),
+ parent=base.a2dTopRight,
+ pos=(-w, 0, 0))
+ self.__z = -.08
+ p = self.__item._np.get_pos()
+ r = self.__item._np.get_r()
+ s = self.__item._np.get_scale()[0]
+ m = self.__item._mass
+ restitution = self.__item._restitution
+ f = self.__item._friction
+ t, pos_entry = self.__add_row(_('position'), f'{round(p.x, 3)}, {round(p.z, 3)}', self.on_edit_position)
+ t, rot_entry = self.__add_row(_('roll'), f'{round(r, 3)}', self.on_edit_roll)
+ t, scale_entry = self.__add_row(_('scale'), f'{round(s, 3)}', self.on_edit_scale)
+ t, mass_entry = self.__add_row(_('mass'), f'{round(m, 3)}', self.on_edit_mass)
+ t, restitution_entry = self.__add_row(_('restitution'), f'{round(restitution, 3)}', self.on_edit_restitution)
+ t, friction_entry = self.__add_row(_('friction'), f'{round(f, 3)}', self.on_edit_friction)
+ fields = ['position', 'roll', 'scale', 'mass', 'restitution', 'friction']
+ Entries = namedtuple('Entries', fields)
+ self.__entries = Entries(pos_entry, rot_entry, scale_entry, mass_entry, restitution_entry, friction_entry)
+ 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=(.06, 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'))
+ self.accept('item-rototranslated', self.__on_item_rototranslated)
+
+ 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 __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_item_rototranslated(self, np):
+ pos = np.get_pos()
+ r = np.get_r()
+ self.__entries.position.set('%s %s' % (str(round(pos.x, 3)), str(round(pos.z, 3))))
+ self.__entries.roll.set('%s' % str(round(r, 3)))
+
+ @property
+ def item(self):
+ return self.__item
+
+ def on_edit_position(self, txt):
+ x, z = map(float, txt.split())
+ self.__item.position = [x, 0, z]
+
+ def on_edit_roll(self, txt):
+ self.__item.roll = float(txt)
+
+ def on_edit_scale(self, txt):
+ self.__item.scale = float(txt)
+
+ def on_edit_mass(self, txt):
+ self.__item.mass = float(txt)
+
+ def on_edit_restitution(self, txt):
+ self.__item.restitution = float(txt)
+
+ def on_edit_friction(self, txt):
+ self.__item.friction = float(txt)
+
+ def destroy(self):
+ self._frm.destroy()
+ self.ignore('item-rototranslated')
+ messenger.send('editor-inspector-destroy')
from direct.gui.DirectGui import DirectButton, DirectFrame, DirectEntry, YesNoDialog
from direct.gui.DirectGuiGlobals import FLAT, NORMAL
from direct.gui.OnscreenText import OnscreenText
+from direct.showbase.DirectObject import DirectObject
from pmachines.editor.scene_list import SceneList
+from pmachines.editor.inspector import Inspector
-class SceneEditor:
+class SceneEditor(DirectObject):
def __init__(self, json, json_name):
+ super().__init__()
self.__json = json
self.__json_name = json_name
+ self.__inspector = None
self._font = base.loader.load_font(
'assets/fonts/Hanken-Book.ttf')
self._font.clear()
frameColor=fcols[0],
rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+ messenger.send('editor-start')
+ self.accept('editor-item-click-l', self.__on_item_click_l)
+ self.accept('editor-inspector-destroy', self.__on_inspector_destroy)
def __on_close(self):
self.__json['name'] = self.__name_entry.get()
def __actually_close(self, arg):
if arg:
self._frm.destroy()
+ messenger.send('editor-stop')
self.__dialog.cleanup()
def __on_save(self):
h = hashlib.new('sha256')
h.update(new_dict_str.encode())
return h.hexdigest()[:12]
+
+ def __on_item_click_l(self, item):
+ if self.__inspector and self.__inspector.item == item: return
+ if self.__inspector:
+ self.__inspector.destroy()
+ self.__inspector = Inspector(item)
+
+ def __on_inspector_destroy(self):
+ self.__inspector = None
Plane, Vec3, BitMask32
from panda3d.bullet import BulletRigidBodyNode, BulletGhostNode
from direct.gui.OnscreenText import OnscreenText
+from direct.showbase.DirectObject import DirectObject
from ya2.p3d.gfx import P3dGfxMgr, set_srgb
all((rot - avg_rot).length() < 1 for rot in self._rotations)
-class Item:
+class Item(DirectObject):
def __init__(self, world, plane_node, cb_inst, curr_bottom, scene_repos, model_path, json, model_scale=1, exp_num_contacts=1, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.5):
+ super().__init__()
self._world = world
self._plane_node = plane_node
self._count = count
self._model.reparent_to(self._np)
self._np.set_scale(model_scale)
self._np.flatten_strong()
- if count:
- self._set_outline_model()
- set_srgb(self._outline_model)
- self._model.hide(BitMask32(0x01))
- self._outline_model.hide(BitMask32(0x01))
+ self._set_outline_model()
+ set_srgb(self._outline_model)
+ self._model.hide(BitMask32(0x01))
+ self._outline_model.hide(BitMask32(0x01))
self._start_drag_pos = None
self._prev_rot_info = None
self._last_nonoverlapping_pos = None
self._last_nonoverlapping_rot = None
self._instantiated = not count
- self.interactable = count
self._box_tsk = taskMgr.add(self.on_frame, 'on_frame')
if count:
self._repos()
else:
self._np.set_pos(pos)
self._np.set_r(r)
+ self.__editing = False
+ self.accept('editor-start', self.__editor_start)
+ self.accept('editor-stop', self.__editor_stop)
def _set_shape(self, apply_scale=True):
pass
+ def __editor_start(self):
+ self.__editing = True
+
+ def __editor_stop(self):
+ self.__editing = False
+
+ @property
+ def interactable(self):
+ return self.__editing or self.count
+
def set_strategy(self, strategy):
self.strategy = strategy
def on_click_l(self, pos):
if self._paused: return
+ if self.__editing:
+ messenger.send('editor-item-click-l', [self])
self._start_drag_pos = pos, self._np.get_pos()
loader.load_sfx('assets/audio/sfx/grab.ogg').play()
if not self._instantiated:
if not self._overlapping:
self._last_nonoverlapping_pos = self._np.get_pos()
self._last_nonoverlapping_rot = self._np.get_hpr()
+ messenger.send('item-rototranslated', [self._np])
def on_aspect_ratio_changed(self):
if not self._instantiated:
return all((pos - avg_pos).length() < .1 for pos in self._positions) and \
all((rot - avg_rot).length() < 1 for rot in self._rotations)
+ @property
+ def mass(self):
+ return self._mass
+
+ @mass.setter
+ def mass(self, val):
+ self._mass = val
+ self.json['mass'] = val
+
+ @property
+ def restitution(self):
+ return self._restitution
+
+ @restitution.setter
+ def restitution(self, val):
+ self._restitution = val
+ self.json['restitution'] = val
+
+ @property
+ def friction(self):
+ return self._friction
+
+ @friction.setter
+ def friction(self, val):
+ self._friction = val
+ self.json['friction'] = val
+
+ @property
+ def position(self):
+ return self._
+
+ @position.setter
+ def position(self, val):
+ self._np.set_pos(*val)
+ self.json['position'] = val
+
+ @property
+ def roll(self):
+ return self._np.get_r()
+
+ @roll.setter
+ def roll(self, val):
+ self._np.set_r(val)
+ self.json['roll'] = val
+
+ @property
+ def scale(self):
+ return self._np.get_scale()[0]
+
+ @scale.setter
+ def scale(self, val):
+ self._np.set_scale(val)
+ self.json['model_scale'] = val
+
def destroy(self):
self._np.remove_node()
taskMgr.remove(self._box_tsk)
self.__persistent = persistent
self.__json_name = json_name
self.__editor = editor
+ self.__scene_editor = None
self.json = {}
self.accept('enforce_res', self.enforce_res)
self._set_camera()
- [X] close editor
- [X] popup if exit with an unsaved scene
- [X] list of scenes
-- [ ] item editor
+- [X] item editor
+ - [X] position (default: 0, 0, 0)
+ - [X] roll (default: 0)
+ - [X] scale (default: 1)
+ - [X] mass (default: 1)
+ - [X] restitution (default: .5)
+ - [X] friction (default: .5)
+ - [X] expected number of contacts (default: 1)
+ - [X] save modifications in the json file
- [ ] new item
- [ ] new scene
+- [ ] start items
- [ ] editing of test_items in the editor for functional tests
- [ ] define functional tests
* BACKLOG tooltips for buttons