1 from logging
import info
, debug
2 from sys
import platform
, exit
3 from os
import environ
, system
4 from webbrowser
import open_new_tab
5 from xmlrpc
.client
import ServerProxy
6 from panda3d
.core
import Texture
, TextNode
, WindowProperties
, LVector2i
, \
7 TextProperties
, TextPropertiesManager
8 from direct
.gui
.DirectGui
import DirectButton
, DirectCheckButton
, \
9 DirectOptionMenu
, DirectSlider
10 from direct
.gui
.DirectGuiGlobals
import FLAT
11 from direct
.gui
.OnscreenText
import OnscreenText
12 from direct
.showbase
.DirectObject
import DirectObject
13 from ya2
.utils
.cursor
import MouseCursor
14 from ya2
.p3d
.p3d
import LibP3d
17 class DirectOptionMenuTest(DirectOptionMenu
):
19 def __init__(self
, parent
=None, **kw
):
20 DirectOptionMenu
.__init
__(self
, parent
, **kw
)
21 self
.initialiseoptions(DirectOptionMenuTest
)
23 def showPopupMenu(self
, event
=None):
24 super().showPopupMenu(event
)
25 self
._show
_cb
([self
.component(cmp) for cmp in self
.components()])
29 class Menu(DirectObject
):
31 def __init__(self
, fsm
, lang_mgr
, opt_file
, music
, pipeline
, scenes
,
35 self
._lang
_mgr
= lang_mgr
36 self
._opt
_file
= opt_file
38 self
._pipeline
= pipeline
40 self
._fun
_test
= fun_test
41 self
._pos
_mgr
= pos_mgr
42 self
._enforced
_res
= ''
43 self
._cursor
= MouseCursor(
44 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04),
45 (.5, .5, .5, 1), (.01, .01))
46 self
._font
= base
.loader
.load_font(
47 'assets/fonts/Hanken-Book.ttf')
49 self
._font
.set_pixels_per_unit(60)
50 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
51 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
55 'text_font': self
._font
,
56 'text_fg': (.9, .9, .9, 1),
58 'frameColor': (.4, .4, .4, .14),
59 'rolloverSound': loader
.load_sfx(
60 'assets/audio/sfx/rollover.ogg'),
61 'clickSound': loader
.load_sfx(
62 'assets/audio/sfx/click.ogg')}
63 self
._common
_btn
= {'frameSize': (-4.8, 4.8, -.6, 1.2)} | self
._common
64 hlc
= self
._common
_btn
['frameColor']
65 hlc
= (hlc
[0] + .2, hlc
[1] + .2, hlc
[2] + .2, hlc
[3] + .2)
67 'item_frameColor': self
._common
_btn
['frameColor'],
68 'popupMarker_frameColor': self
._common
_btn
['frameColor'],
69 'item_relief': self
._common
_btn
['relief'],
70 'item_text_font': self
._common
_btn
['text_font'],
71 'item_text_fg': self
._common
_btn
['text_fg'],
73 'highlightColor': hlc
,
74 'text_align': TextNode
.A_center
,
76 f_s
= self
._common
_opt
['frameSize']
77 self
._common
_opt
['frameSize'] = f_s
[0], f_s
[1] - .56, f_s
[2], f_s
[3]
78 self
._common
_slider
= self
._common |
{
80 'thumb_frameColor': (.4, .4, .4, .4),
83 del self
._common
_slider
['rolloverSound']
84 del self
._common
_slider
['clickSound']
87 def enforce_res(self
, val
):
88 self
._enforced
_res
= val
89 info('enforced resolution: ' + val
)
94 self
._widgets
+= [DirectButton(
95 text
=_('Play'), pos
=(0, 1, .6), command
=self
.on_play
,
97 self
._pos
_mgr
.register('play', LibP3d
.wdg_pos(self
._widgets
[-1]))
98 self
._widgets
+= [DirectButton(
99 text
=_('Options'), pos
=(0, 1, .2), command
=self
.on_options
,
101 self
._pos
_mgr
.register('options', LibP3d
.wdg_pos(self
._widgets
[-1]))
102 self
._widgets
+= [DirectButton(
103 text
=_('Credits'), pos
=(0, 1, -.2), command
=self
.on_credits
,
105 self
._pos
_mgr
.register('credits', LibP3d
.wdg_pos(self
._widgets
[-1]))
109 ServerProxy('http://localhost:6000').destroy()
111 self
._widgets
+= [DirectButton(
112 text
=_('Exit'), pos
=(0, 1, -.6), command
=lambda: btn_exit(),
114 self
._pos
_mgr
.register('exit', LibP3d
.wdg_pos(self
._widgets
[-1]))
115 self
._rearrange
_width
()
116 self
.accept('enforce_resolution', self
.enforce_res
)
118 def _set_options(self
):
119 self
._pos
_mgr
.reset()
121 self
._lang
_funcs
= [lambda: _('English'), lambda: _('Italian')]
122 items
= [fnc() for fnc
in self
._lang
_funcs
]
126 }[self
._opt
_file
['settings']['language']]
129 self
._pos
_mgr
.remove(['english', 'italian'])
130 for lng
, btn
in zip(['english', 'italian'], comps
):
131 pos
= LibP3d
.wdg_pos(btn
)
132 self
._pos
_mgr
.register(lng
, (pos
[0] + 5, pos
[1]))
133 btn
= DirectOptionMenuTest(
134 text
=_('Language'), items
=items
, initialitem
=inititem
,
135 pos
=(0, 1, .8), command
=self
.on_language
, **self
._common
_opt
)
136 btn
._show
_cb
= lang_cb
137 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
138 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
139 self
._widgets
+= [btn
]
140 pos_lang
= LibP3d
.wdg_pos(self
._widgets
[-1])
141 self
._pos
_mgr
.register('languages', pos_lang
)
142 self
._widgets
+= [OnscreenText(
143 _('Volume'), pos
=(-.1, .55), font
=self
._common
['text_font'],
144 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
145 align
=TextNode
.A_right
)]
146 self
._widgets
+= [DirectSlider(
148 value
=self
._opt
_file
['settings']['volume'],
149 command
=self
.on_volume
,
150 **self
._common
_slider
)]
151 vol_pos
= LibP3d
.wdg_pos(self
._widgets
[-1])
152 self
._pos
_mgr
.register('volume', vol_pos
)
153 self
._pos
_mgr
.register('volume_0', [vol_pos
[0] - 153, vol_pos
[1]])
154 self
._slider
= self
._widgets
[-1]
155 self
._widgets
+= [DirectCheckButton(
156 text
=_('Fullscreen'), pos
=(0, 1, .3), command
=self
.on_fullscreen
,
157 indicator_frameColor
=self
._common
_opt
['highlightColor'],
158 indicator_relief
=self
._common
_btn
['relief'],
159 indicatorValue
=self
._opt
_file
['settings']['fullscreen'],
161 self
._pos
_mgr
.register('fullscreen', LibP3d
.wdg_pos(self
._widgets
[-1]))
162 res
= self
._opt
_file
['settings']['resolution']
163 d_i
= base
.pipe
.get_display_information()
165 return d_i
.get_display_mode_width(idx
), \
166 d_i
.get_display_mode_height(idx
)
168 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
169 resolutions
= list(set(resolutions
))
170 resolutions
= sorted(resolutions
)
171 resolutions
= [(str(_res
[0]), str(_res
[1])) for _res
in resolutions
]
172 resolutions
= ['x'.join(_res
) for _res
in resolutions
]
174 res
= resolutions
[-1]
177 self
._pos
_mgr
.remove(['res_1440x900', 'res_1360x768'])
178 for tgt_res
in ['1440x900', '1360x768']:
180 if btn
['text'] == tgt_res
:
181 pos
= LibP3d
.wdg_pos(btn
)
182 self
._pos
_mgr
.register('res_' + tgt_res
, (pos
[0] + 5, pos
[1]))
183 btn
= DirectOptionMenuTest(
184 text
=_('Resolution'), items
=resolutions
, initialitem
=res
,
185 pos
=(0, 1, .05), command
=self
.on_resolution
, **self
._common
_opt
)
186 btn
._show
_cb
= res_cb
187 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
188 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
189 self
._widgets
+= [btn
]
190 pos_res
= LibP3d
.wdg_pos(self
._widgets
[-1])
191 self
._pos
_mgr
.register('resolutions', pos_res
) # 680 365
192 self
._pos
_mgr
.register('res_1440x900', [pos_res
[0] + 320, pos_res
[1] + 75])
193 self
._pos
_mgr
.register('res_1360x768', [pos_res
[0] + 430, pos_res
[1] -285])
194 self
._widgets
+= [DirectCheckButton(
195 text
=_('Antialiasing'), pos
=(0, 1, -.2), command
=self
.on_aa
,
196 indicator_frameColor
=self
._common
_opt
['highlightColor'],
197 indicator_relief
=self
._common
_btn
['relief'],
198 indicatorValue
=self
._opt
_file
['settings']['antialiasing'],
200 self
._pos
_mgr
.register('aa', LibP3d
.wdg_pos(self
._widgets
[-1]))
201 self
._widgets
+= [DirectCheckButton(
202 text
=_('Shadows'), pos
=(0, 1, -.45), command
=self
.on_shadows
,
203 indicator_frameColor
=self
._common
_opt
['highlightColor'],
204 indicator_relief
=self
._common
_btn
['relief'],
205 indicatorValue
=self
._opt
_file
['settings']['shadows'],
207 self
._pos
_mgr
.register('shadows', LibP3d
.wdg_pos(self
._widgets
[-1]))
208 self
._widgets
+= [DirectButton(
209 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
211 self
._pos
_mgr
.register('back', LibP3d
.wdg_pos(self
._widgets
[-1]))
212 self
.accept('enforce_resolution', self
.enforce_res
)
214 def _set_credits(self
):
215 self
._pos
_mgr
.reset()
217 tp_scale
= TextProperties()
218 tp_scale
.set_text_scale(.64)
219 TextPropertiesManager
.getGlobalPtr().setProperties('scale', tp_scale
)
220 self
._widgets
+= [OnscreenText(
221 _('Code and gfx\n \1scale\1Flavio Calva\2\n\n\nMusic\n \1scale\1Stefan Grossmann\2'),
222 pos
=(-.9, .55), font
=self
._common
['text_font'],
223 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
224 align
=TextNode
.A_left
)]
225 self
._widgets
+= [DirectButton(
226 text
=_('Website'), pos
=(-.6, 1, .29), command
=self
.on_website
,
227 **self
._common
_btn |
{'scale': .08})]
228 self
._widgets
+= [OnscreenText(
229 _('Special thanks to:\n \1scale\1rdb\2\n \1scale\1Luisa Tenuta\2\n \1scale\1Damiana Ercolani\2'),
230 pos
=(.1, .55), font
=self
._common
['text_font'],
231 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
232 align
=TextNode
.A_left
)]
233 self
._widgets
+= [DirectButton(
234 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
236 self
._pos
_mgr
.register('back', LibP3d
.wdg_pos(self
._widgets
[-1]))
237 self
.accept('enforce_resolution', self
.enforce_res
)
240 self
._pos
_mgr
.reset()
242 self
._cursor
= MouseCursor(
243 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
246 cmn
= self
._common
_btn
.copy() |
{
247 'frameSize': (-2.4, 2.4, -2.4, 2.4),
248 'frameColor': (1, 1, 1, .8),
250 left
= - (dx
:= .8) * (min(4, len(self
._scenes
)) - 1) / 2
251 for i
, cls
in enumerate(self
._scenes
):
252 top
= .1 if len(self
._scenes
) < 5 else .6
253 row
= 0 if i
< 4 else 1
254 self
._widgets
+= [DirectButton(
255 text
=cls
.name(), pos
=(left
+ dx
* (i
% 4), 1, top
- dx
* row
),
256 command
=self
.start
, extraArgs
=[cls
], text_wordwrap
=6,
257 frameTexture
='assets/images/scenes/%s.dds' % cls
.__name
__,
259 name
= cls
.__name
__[5:].lower()
260 self
._pos
_mgr
.register(name
, LibP3d
.wdg_pos(self
._widgets
[-1]))
262 tnode
= self
._widgets
[-1].component('text%s' % j
).textNode
263 height
= - tnode
.getLineHeight() / 2
264 height
+= (tnode
.get_height() - tnode
.get_line_height()) / 2
265 self
._widgets
[-1].component('text%s' % j
).set_pos(0, 0, height
)
266 self
._widgets
+= [DirectButton(
267 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
269 self
._pos
_mgr
.register('back', LibP3d
.wdg_pos(self
._widgets
[-1]))
271 def start(self
, cls
):
272 self
._fsm
.demand('Scene', cls
)
274 def on_options(self
):
276 self
._cursor
= MouseCursor(
277 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
281 def on_credits(self
):
283 self
._cursor
= MouseCursor(
284 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
288 def _rearrange_width(self
):
290 for wdg
in self
._widgets
:
291 t_n
= wdg
.component('text0')
292 u_l
= t_n
.textNode
.get_upper_left_3d()
293 l_r
= t_n
.textNode
.get_lower_right_3d()
294 max_width
= max(l_r
[0] - u_l
[0], max_width
)
295 for wdg
in self
._widgets
:
296 m_w
= max_width
/ 2 + .8
297 wdg
['frameSize'] = -m_w
, m_w
, wdg
['frameSize'][2], wdg
['frameSize'][3]
299 def on_language(self
, arg
):
301 _('English'): 'en_EN',
302 _('Italian'): 'it_IT'}[arg
]
303 self
._lang
_mgr
.set_lang(lang_code
)
304 self
._opt
_file
['settings']['language'] = lang_code
[:2]
305 self
._opt
_file
.store()
309 self
._opt
_file
['settings']['volume'] = self
._slider
['value']
310 self
._music
.set_volume(self
._slider
['value'])
312 def on_fullscreen(self
, arg
):
313 props
= WindowProperties()
314 props
.set_fullscreen(arg
)
315 if not self
._fun
_test
:
316 base
.win
.request_properties(props
)
317 # if we actually switch to fullscreen during the tests then
318 # exwm inside qemu can't restore the correct resolution
319 # i may re-enable this if/when i run the tests onto a
321 self
._opt
_file
['settings']['fullscreen'] = int(arg
)
322 self
._opt
_file
.store()
324 def on_resolution(self
, arg
):
325 info('on resolution: %s (%s)' % (arg
, self
._enforced
_res
))
326 arg
= self
._enforced
_res
or arg
327 info('set resolution: %s' % arg
)
328 props
= WindowProperties()
329 props
.set_size(LVector2i(*[int(_res
) for _res
in arg
.split('x')]))
330 base
.win
.request_properties(props
)
331 self
._opt
_file
['settings']['resolution'] = arg
332 self
._opt
_file
.store()
334 def on_aa(self
, arg
):
335 self
._pipeline
.msaa_samples
= 4 if arg
else 1
336 debug(f
'msaa: {self._pipeline.msaa_samples}')
337 self
._opt
_file
['settings']['antialiasing'] = int(arg
)
338 self
._opt
_file
.store()
340 def on_shadows(self
, arg
):
341 self
._pipeline
.enable_shadows
= int(arg
)
342 debug(f
'shadows: {self._pipeline.enable_shadows}')
343 self
._opt
_file
['settings']['shadows'] = int(arg
)
344 self
._opt
_file
.store()
346 def on_website(self
):
347 if platform
.startswith('linux'):
348 environ
['LD_LIBRARY_PATH'] = ''
349 system('xdg-open https://www.ya2.it')
351 open_new_tab('https://www.ya2.it')
354 self
._opt
_file
.store()
356 self
._cursor
= MouseCursor(
357 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
362 [wdg
.destroy() for wdg
in self
._widgets
]
363 self
._cursor
.destroy()
364 self
.ignore('enforce_resolution')