939742431466b4959345461a7f921c47c8e63a49
1 from collections
import namedtuple
3 from importlib
import import_module
4 from os
.path
import basename
5 from inspect
import isclass
6 from panda3d
.core
import Texture
, TextNode
, LPoint3f
7 from direct
.gui
.OnscreenImage
import OnscreenImage
8 from direct
.gui
.DirectGui
import DirectButton
, DirectFrame
, DirectEntry
, DirectOptionMenu
, OkDialog
9 from direct
.gui
.DirectGuiGlobals
import FLAT
, NORMAL
10 from direct
.gui
.OnscreenText
import OnscreenText
11 from direct
.showbase
.DirectObject
import DirectObject
12 from pmachines
.items
.item
import ItemStrategy
, FixedStrategy
, StillStrategy
13 from pmachines
.items
.box
import HitStrategy
14 from pmachines
.items
.domino
import DownStrategy
, UpStrategy
15 from pmachines
.editor
.augmented_frame
import AugmentedDirectFrame
16 from ya2
.utils
.gfx
import DirectGuiMixin
19 class Inspector(DirectObject
):
21 def __init__(self
, item
, all_items
):
24 self
.__all
_items
= all_items
25 self
._font
= base
.loader
.load_font(
26 'assets/fonts/Hanken-Book.ttf')
28 self
._font
.set_pixels_per_unit(60)
29 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
30 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
33 'text_font': self
._font
,
34 'text_fg': (.9, .9, .9, 1),
36 'frameColor': (.4, .4, .4, .14),
37 'rolloverSound': loader
.load_sfx(
38 'assets/audio/sfx/rollover.ogg'),
39 'clickSound': loader
.load_sfx(
40 'assets/audio/sfx/click.ogg')}
41 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
43 self
._frm
= AugmentedDirectFrame(frameColor
=(.4, .4, .4, .06),
44 frameSize
=(0, w
, -h
, 0),
45 parent
=base
.a2dTopRight
,
47 delta_drag
=LPoint3f(-w
, 0, -h
),
48 collapse_pos
=(w
- .06, 1, -h
+ .06))
50 p
= self
.__item
._np
.get_pos()
51 r
= self
.__item
._np
.get_r()
52 s
= self
.__item
._np
.get_scale()[0]
54 restitution
= self
.__item
._restitution
55 f
= self
.__item
._friction
57 if 'id' in self
.__item
.json
:
58 _id
= self
.__item
.json
['id']
60 if 'strategy' in self
.__item
.json
:
61 _strategy
= self
.__item
.json
['strategy']
63 if 'strategy_args' in self
.__item
.json
:
64 _strategy_args
= ' '.join(map(str, self
.__item
.json
['strategy_args']))
65 t
, pos_entry
= self
.__add
_row
(_('position'), f
'{round(p.x, 3)}, {round(p.z, 3)}', self
.on_edit_position
, _('position (e.g. 0.1 2.3 4.5)'))
66 t
, rot_entry
= self
.__add
_row
(_('roll'), f
'{round(r, 3)}', self
.on_edit_roll
, _('roll (e.g. 90)'))
67 t
, scale_entry
= self
.__add
_row
(_('scale'), f
'{round(s, 3)}', self
.on_edit_scale
, _('scale (e.g. 1.2)'))
68 t
, mass_entry
= self
.__add
_row
(_('mass'), f
'{round(m, 3)}', self
.on_edit_mass
, _('mass (default 1; 0 if fixed)'))
69 t
, restitution_entry
= self
.__add
_row
(_('restitution'), f
'{round(restitution, 3)}', self
.on_edit_restitution
, _('restitution (default 0.5)'))
70 t
, friction_entry
= self
.__add
_row
(_('friction'), f
'{round(f, 3)}', self
.on_edit_friction
, _('friction (default 0.5)'))
71 t
, id_entry
= self
.__add
_row
(_('id'), _id
, self
.on_edit_id
, _('id of the item (for the strategies)'))
72 t
, strategy_entry
= self
.__add
_row
_option
(_('strategy'), _strategy
, self
.on_edit_strategy
, _('the strategy of the item'))
73 t
, strategy_args_entry
= self
.__add
_row
(_('strategy_args'), _strategy_args
, self
.on_edit_strategy_args
, _('the arguments of the strategy'))
74 fields
= ['position', 'roll', 'scale', 'mass', 'restitution', 'friction', 'id', 'strategy', 'strategy_args']
75 Entries
= namedtuple('Entries', fields
)
76 self
.__entries
= Entries(pos_entry
, rot_entry
, scale_entry
, mass_entry
, restitution_entry
, friction_entry
, id_entry
, strategy_entry
, strategy_args_entry
)
77 def load_images_btn(path
, col
):
80 (.6, .6, .6, 1), # ready
82 (.8, .8, .8, 1), # rollover
88 (.4, .1, .1, .4)]}[col
]
89 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
90 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
92 image
=load_images_btn('exitRight', 'gray'), scale
=.05,
93 pos
=(.06, 1, -h
+ .06),
94 parent
=self
._frm
, command
=self
.destroy
, state
=NORMAL
, relief
=FLAT
,
96 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
97 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
98 b
.__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
99 b
.set_tooltip(_('Close'), *tooltip_args
)
100 self
.accept('item-rototranslated', self
.__on
_item
_rototranslated
)
102 image
=load_images_btn('trashcan', 'gray'), scale
=.05,
103 pos
=(.18, 1, -h
+ .06),
104 parent
=self
._frm
, command
=self
.__delete
_item
, state
=NORMAL
, relief
=FLAT
,
106 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
107 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
108 b
.__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
109 b
.set_tooltip(_('Delete the item'), *tooltip_args
)
111 def __add_row(self
, label
, text
, callback
, tooltip
):
113 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
116 pos
=(.03, self
.__z
), parent
=self
._frm
,
117 font
=self
._common
['text_font'],
118 scale
=self
._common
['scale'],
119 fg
=self
._common
['text_fg'],
120 wordwrap
=20, align
=TextNode
.ALeft
)
122 scale
=self
._common
['scale'],
123 pos
=(.30, 1, self
.__z
),
124 entryFont
=self
._font
,
127 frameColor
=self
._common
['frameColor'],
130 text_fg
=self
._common
['text_fg'],
132 e
.__class
__ = type('DirectEntryMixed', (DirectEntry
, DirectGuiMixin
), {})
133 e
.set_tooltip(tooltip
, *tooltip_args
)
137 def __add_row_option(self
, label
, text
, callback
, tooltip
):
138 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
139 item_modules
= glob('pmachines/items/*.py')
140 item_modules
= [basename(i
)[:-3] for i
in item_modules
]
142 for item_module
in item_modules
:
143 mod_name
= 'pmachines.items.' + item_module
144 for member
in import_module(mod_name
).__dict
__.values():
145 if isclass(member
) and issubclass(member
, ItemStrategy
) and \
146 member
!= ItemStrategy
:
147 new_items
= list(set(new_items
+ [member
.__name
__]))
150 pos
=(.03, self
.__z
), parent
=self
._frm
,
151 font
=self
._common
['text_font'],
152 scale
=self
._common
['scale'],
153 fg
=self
._common
['text_fg'],
154 wordwrap
=20, align
=TextNode
.ALeft
)
155 e
= DirectOptionMenu(
156 scale
=self
._common
['scale'],
158 pos
=(.30, 1, self
.__z
),
165 frameColor
=self
._common
['frameColor'],
166 item_frameColor
=self
._common
['frameColor'],
167 popupMenu_frameColor
=self
._common
['frameColor'],
168 popupMarker_frameColor
=self
._common
['frameColor'],
169 text_font
=self
._font
,
170 text_fg
=self
._common
['text_fg'],
171 highlightColor
=(.9, .9, .9, .9),
172 item_text_font
=self
._font
,
173 item_text_fg
=self
._common
['text_fg'],
174 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
175 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
176 e
.__class
__ = type('DirectOptionMenuMixed', (DirectOptionMenu
, DirectGuiMixin
), {})
177 e
.set_tooltip(tooltip
, *tooltip_args
)
181 def __load_img_btn(self
, path
, col
):
182 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
183 img
.set_transparency(True)
188 def __on_item_rototranslated(self
, np
):
191 self
.__entries
.position
.set('%s %s' % (str(round(pos
.x
, 3)), str(round(pos
.z
, 3))))
192 self
.__entries
.roll
.set('%s' % str(round(r
, 3)))
193 self
.__item
.json
['position'] = list(pos
)
194 self
.__item
.json
['roll'] = round(r
, 3)
196 def __delete_item(self
):
197 messenger
.send('editor-inspector-delete', [self
.__item
])
204 def on_edit_position(self
, txt
):
205 x
, z
= map(float, txt
.split())
206 self
.__item
.position
= [x
, 0, z
]
208 def on_edit_roll(self
, txt
):
209 self
.__item
.roll
= float(txt
)
211 def on_edit_scale(self
, txt
):
212 self
.__item
.scale
= float(txt
)
214 def on_edit_mass(self
, txt
):
215 self
.__item
.mass
= float(txt
)
217 def on_edit_restitution(self
, txt
):
218 self
.__item
.restitution
= float(txt
)
220 def on_edit_friction(self
, txt
):
221 self
.__item
.friction
= float(txt
)
223 def on_edit_id(self
, txt
):
226 def on_edit_strategy(self
, txt
):
228 self
.__entries
.strategy_args
.set('')
231 'StillStrategy': StillStrategy
,
232 'UpStrategy': UpStrategy
,
233 'HitStrategy': HitStrategy
,
234 'DownStrategy': DownStrategy
,
235 'FixedStrategy': FixedStrategy
}
236 class_
= name2class
[txt
]
239 if txt
== 'StillStrategy':
240 args
+= [self
.__item
._np
]
241 if txt
in ['UpStrategy', 'DownStrategy']:
242 args
+= [self
.__item
._np
]
244 args
+= [float(self
.__entries
.strategy_args
.get())]
247 if txt
== 'HitStrategy':
248 for item
in self
.__all
_items
:
249 if item
.id == self
.__entries
.strategy_args
.get():
251 args
+= [self
.__item
.node
]
252 args
+= [self
.__item
._world
]
254 self
.__item
.strategy
= class_(*args
)
255 self
.__item
.strategy_json
= txt
257 self
.__show
_error
_popup
()
259 def on_edit_strategy_args(self
, txt
):
260 self
.__item
.strategy_args_json
= txt
262 def __show_error_popup(self
):
263 self
.__dialog
= OkDialog(dialogName
='Strategy args errors',
264 text
=_('There are errors in the strategy args.'),
265 command
=self
.__actually
_close
)
266 self
.__dialog
['frameColor'] = (.4, .4, .4, .14)
267 self
.__dialog
['relief'] = FLAT
268 self
.__dialog
.component('text0')['fg'] = (.9, .9, .9, 1)
269 self
.__dialog
.component('text0')['font'] = self
._font
270 for b
in self
.__dialog
.buttonList
:
271 b
['frameColor'] = (.4, .4, .4, .14)
272 b
.component('text0')['fg'] = (.9, .9, .9, 1)
273 b
.component('text0')['font'] = self
._font
274 b
.component('text1')['fg'] = (.9, .1, .1, 1)
275 b
.component('text1')['font'] = self
._font
276 b
.component('text2')['fg'] = (.9, .9, .1, 1)
277 b
.component('text2')['font'] = self
._font
279 def __actually_close(self
, arg
):
280 self
.__entries
.strategy
.set('')
281 self
.__entries
.strategy_args
.set('')
282 self
.__dialog
.cleanup()
286 self
.ignore('item-rototranslated')
287 messenger
.send('editor-inspector-destroy')
290 class PixelSpaceInspector(DirectObject
):
292 def __init__(self
, item
, all_items
):
295 self
.__all
_items
= all_items
296 self
._font
= base
.loader
.load_font(
297 'assets/fonts/Hanken-Book.ttf')
299 self
._font
.set_pixels_per_unit(60)
300 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
301 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
304 'text_font': self
._font
,
305 'text_fg': (.9, .9, .9, 1),
307 'frameColor': (.4, .4, .4, .14),
308 'rolloverSound': loader
.load_sfx(
309 'assets/audio/sfx/rollover.ogg'),
310 'clickSound': loader
.load_sfx(
311 'assets/audio/sfx/click.ogg')}
313 self
._frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
314 frameSize
=(0, w
, -h
, 0),
315 parent
=base
.a2dTopRight
,
318 p
= self
.__item
._np
.get_pos()
320 if 'id' in self
.__item
.json
:
321 _id
= self
.__item
.json
['id']
322 t
, pos_entry
= self
.__add
_row
(_('position'), f
'{round(p.x, 3)}, {round(p.z, 3)}', self
.on_edit_position
)
323 t
, id_entry
= self
.__add
_row
(_('id'), _id
, self
.on_edit_id
)
324 fields
= ['position', 'id']
325 Entries
= namedtuple('Entries', fields
)
326 self
.__entries
= Entries(pos_entry
, id_entry
)
327 def load_images_btn(path
, col
):
330 (.6, .6, .6, 1), # ready
331 (1, 1, 1, 1), # press
332 (.8, .8, .8, 1), # rollover
338 (.4, .1, .1, .4)]}[col
]
339 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
340 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
342 image
=load_images_btn('exitRight', 'gray'), scale
=.05,
343 pos
=(.06, 1, -h
+ .06),
344 parent
=self
._frm
, command
=self
.destroy
, state
=NORMAL
, relief
=FLAT
,
346 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
347 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
348 self
.accept('item-rototranslated', self
.__on
_item
_rototranslated
)
350 image
=load_images_btn('trashcan', 'gray'), scale
=.05,
351 pos
=(.18, 1, -h
+ .06),
352 parent
=self
._frm
, command
=self
.__delete
_item
, state
=NORMAL
, relief
=FLAT
,
354 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
355 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
357 def __add_row(self
, label
, text
, callback
):
361 pos
=(.03, self
.__z
), parent
=self
._frm
,
362 font
=self
._common
['text_font'],
363 scale
=self
._common
['scale'],
364 fg
=self
._common
['text_fg'],
365 wordwrap
=20, align
=TextNode
.ALeft
)
367 scale
=self
._common
['scale'],
368 pos
=(.30, 1, self
.__z
),
369 entryFont
=self
._font
,
372 frameColor
=self
._common
['frameColor'],
375 text_fg
=self
._common
['text_fg'],
380 def __load_img_btn(self
, path
, col
):
381 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
382 img
.set_transparency(True)
387 def __on_item_rototranslated(self
, np
):
388 pos
= np
.pos2d_pixel()
389 self
.__entries
.position
.set('%s %s' % (str(round(pos
[0], 3)), str(round(pos
[1], 3))))
390 self
.__item
.json
['position'] = list(pos
)
392 def __delete_item(self
):
393 messenger
.send('editor-inspector-delete', [self
.__item
])
400 def on_edit_position(self
, txt
):
401 x
, z
= map(float, txt
.split())
402 self
.__item
.position
= [x
, 0, z
]
404 def on_edit_id(self
, txt
):
407 def __actually_close(self
, arg
):
408 self
.__entries
.strategy
.set('')
409 self
.__entries
.strategy_args
.set('')
410 self
.__dialog
.cleanup()
414 self
.ignore('item-rototranslated')
415 messenger
.send('editor-inspector-destroy')
418 class WorldSpaceInspector(DirectObject
):
420 def __init__(self
, item
, all_items
):
423 self
.__all
_items
= all_items
424 self
._font
= base
.loader
.load_font(
425 'assets/fonts/Hanken-Book.ttf')
427 self
._font
.set_pixels_per_unit(60)
428 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
429 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
432 'text_font': self
._font
,
433 'text_fg': (.9, .9, .9, 1),
435 'frameColor': (.4, .4, .4, .14),
436 'rolloverSound': loader
.load_sfx(
437 'assets/audio/sfx/rollover.ogg'),
438 'clickSound': loader
.load_sfx(
439 'assets/audio/sfx/click.ogg')}
441 self
._frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
442 frameSize
=(0, w
, -h
, 0),
443 parent
=base
.a2dTopRight
,
446 p
= self
.__item
._np
.get_pos()
448 if 'id' in self
.__item
.json
:
449 _id
= self
.__item
.json
['id']
450 t
, pos_entry
= self
.__add
_row
(_('position'), f
'{round(p.x, 3)}, {round(p.z, 3)}', self
.on_edit_position
)
451 t
, id_entry
= self
.__add
_row
(_('id'), _id
, self
.on_edit_id
)
452 fields
= ['position', 'id']
453 Entries
= namedtuple('Entries', fields
)
454 self
.__entries
= Entries(pos_entry
, id_entry
)
455 def load_images_btn(path
, col
):
458 (.6, .6, .6, 1), # ready
459 (1, 1, 1, 1), # press
460 (.8, .8, .8, 1), # rollover
466 (.4, .1, .1, .4)]}[col
]
467 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
468 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
470 image
=load_images_btn('exitRight', 'gray'), scale
=.05,
471 pos
=(.06, 1, -h
+ .06),
472 parent
=self
._frm
, command
=self
.destroy
, state
=NORMAL
, relief
=FLAT
,
474 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
475 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
476 self
.accept('item-rototranslated', self
.__on
_item
_rototranslated
)
478 image
=load_images_btn('trashcan', 'gray'), scale
=.05,
479 pos
=(.18, 1, -h
+ .06),
480 parent
=self
._frm
, command
=self
.__delete
_item
, state
=NORMAL
, relief
=FLAT
,
482 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
483 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
485 def __add_row(self
, label
, text
, callback
):
489 pos
=(.03, self
.__z
), parent
=self
._frm
,
490 font
=self
._common
['text_font'],
491 scale
=self
._common
['scale'],
492 fg
=self
._common
['text_fg'],
493 wordwrap
=20, align
=TextNode
.ALeft
)
495 scale
=self
._common
['scale'],
496 pos
=(.30, 1, self
.__z
),
497 entryFont
=self
._font
,
500 frameColor
=self
._common
['frameColor'],
503 text_fg
=self
._common
['text_fg'],
508 def __load_img_btn(self
, path
, col
):
509 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
510 img
.set_transparency(True)
515 def __on_item_rototranslated(self
, np
):
517 self
.__entries
.position
.set('%s %s' % (str(round(pos
.x
, 3)), str(round(pos
.z
, 3))))
518 self
.__item
.json
['position'] = list(pos
)
520 def __delete_item(self
):
521 messenger
.send('editor-inspector-delete', [self
.__item
])
528 def on_edit_position(self
, txt
):
529 x
, z
= map(float, txt
.split())
530 self
.__item
.position
= [x
, 0, z
]
532 def on_edit_id(self
, txt
):
535 def __actually_close(self
, arg
):
536 self
.__entries
.strategy
.set('')
537 self
.__entries
.strategy_args
.set('')
538 self
.__dialog
.cleanup()
542 self
.ignore('item-rototranslated')
543 messenger
.send('editor-inspector-destroy')