4fef839f33df48e058bba825a3ff4f0c724273d2
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
, DirectFrame
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
.utils
.gfx
import GfxTools
, DirectGuiMixin
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) # it does not work with mixins
25 DirectOptionMenu
.showPopupMenu(self
, event
)
26 self
._show
_cb
([self
.component(cmp) for cmp in self
.components()])
30 class Menu(DirectObject
):
32 def __init__(self
, fsm
, lang_mgr
, opt_file
, music
, pipeline
, scenes
,
36 self
._lang
_mgr
= lang_mgr
37 self
._opt
_file
= opt_file
39 self
._pipeline
= pipeline
41 self
._fun
_test
= fun_test
42 self
._pos
_mgr
= pos_mgr
43 self
._enforced
_res
= ''
44 self
._cursor
= MouseCursor(
45 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04),
46 (.5, .5, .5, 1), (.01, .01), fun_test
)
47 self
._font
= base
.loader
.load_font(
48 'assets/fonts/Hanken-Book.ttf')
50 self
._font
.set_pixels_per_unit(60)
51 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
52 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
56 'text_font': self
._font
,
57 'text_fg': (.9, .9, .9, 1),
59 'frameColor': (.4, .4, .4, .14),
60 'rolloverSound': loader
.load_sfx(
61 'assets/audio/sfx/rollover.ogg'),
62 'clickSound': loader
.load_sfx(
63 'assets/audio/sfx/click.ogg')}
64 self
._common
_btn
= {'frameSize': (-4.8, 4.8, -.6, 1.2)} | self
._common
65 hlc
= self
._common
_btn
['frameColor']
66 hlc
= (hlc
[0] + .2, hlc
[1] + .2, hlc
[2] + .2, hlc
[3] + .2)
68 'item_frameColor': self
._common
_btn
['frameColor'],
69 'popupMarker_frameColor': self
._common
_btn
['frameColor'],
70 'item_relief': self
._common
_btn
['relief'],
71 'item_text_font': self
._common
_btn
['text_font'],
72 'item_text_fg': self
._common
_btn
['text_fg'],
74 'highlightColor': hlc
,
75 'text_align': TextNode
.A_center
,
77 f_s
= self
._common
_opt
['frameSize']
78 self
._common
_opt
['frameSize'] = f_s
[0], f_s
[1] - .56, f_s
[2], f_s
[3]
79 self
._common
_slider
= self
._common |
{
81 'thumb_frameColor': (.4, .4, .4, .4),
84 del self
._common
_slider
['rolloverSound']
85 del self
._common
_slider
['clickSound']
88 def enforce_res(self
, val
):
89 self
._enforced
_res
= val
90 info('enforced resolution: ' + val
)
95 self
._widgets
+= [DirectButton(
96 text
=_('Play'), pos
=(0, 1, .6), command
=self
.on_play
,
98 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
99 self
._pos
_mgr
['play'] = self
._widgets
[-1].pos_pixel()
100 self
._widgets
+= [DirectButton(
101 text
=_('Options'), pos
=(0, 1, .2), command
=self
.on_options
,
103 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
104 self
._pos
_mgr
['options'] = self
._widgets
[-1].pos_pixel()
105 self
._widgets
+= [DirectButton(
106 text
=_('Credits'), pos
=(0, 1, -.2), command
=self
.on_credits
,
108 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
109 self
._pos
_mgr
['credits'] = self
._widgets
[-1].pos_pixel()
113 ServerProxy('http://localhost:7000').destroy()
115 self
._widgets
+= [DirectButton(
116 text
=_('Exit'), pos
=(0, 1, -.6), command
=lambda: btn_exit(),
118 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
119 self
._pos
_mgr
['exit'] = self
._widgets
[-1].pos_pixel()
120 self
._rearrange
_width
()
121 self
.accept('enforce_resolution', self
.enforce_res
)
123 def _set_options(self
):
126 self
._lang
_funcs
= [lambda: _('English'), lambda: _('Italian')]
127 items
= [fnc() for fnc
in self
._lang
_funcs
]
131 }[self
._opt
_file
['settings']['language']]
134 for element
in ['english', 'italian']:
135 if element
in self
._pos
_mgr
:
136 del self
._pos
_mgr
[element
]
137 for lng
, btn
in zip(['english', 'italian'], comps
):
138 btn
.__class
__ = type('DirectFrameMixed', (DirectFrame
, DirectGuiMixin
), {})
139 pos
= btn
.pos_pixel()
140 self
._pos
_mgr
[lng
] = (pos
[0] + 5, pos
[1])
141 btn
= DirectOptionMenuTest(
142 text
=_('Language'), items
=items
, initialitem
=inititem
,
143 pos
=(0, 1, .8), command
=self
.on_language
, **self
._common
_opt
)
144 btn
.__class
__ = type('DirectOptionMenuMixed', (DirectOptionMenu
, DirectGuiMixin
), {})
145 btn
._show
_cb
= lang_cb
146 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
147 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
148 self
._widgets
+= [btn
]
149 pos_lang
= self
._widgets
[-1].pos_pixel()
150 self
._pos
_mgr
['languages'] = pos_lang
151 self
._widgets
+= [OnscreenText(
152 _('Volume'), pos
=(-.1, .55), font
=self
._common
['text_font'],
153 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
154 align
=TextNode
.A_right
)]
155 self
._widgets
+= [DirectSlider(
157 value
=self
._opt
_file
['settings']['volume'],
158 command
=self
.on_volume
,
159 **self
._common
_slider
)]
160 self
._widgets
[-1].__class
__ = type('DirectSliderMixed', (DirectSlider
, DirectGuiMixin
), {})
161 vol_pos
= self
._widgets
[-1].pos_pixel()
162 self
._pos
_mgr
['volume'] = vol_pos
163 np_left
= GfxTools
.build_empty_node('left_slider')
164 np_left
.set_pos(self
._widgets
[-1].get_net_transform().get_pos())
165 np_left
.set_x(np_left
.get_x() - self
._widgets
[-1].get_scale()[0])
166 lpos
= np_left
.pos_as_widget() # try with pos2d_pixel and remove pos_as_widget if ok
167 self
._pos
_mgr
['volume_0'] = lpos
168 self
._slider
= self
._widgets
[-1]
169 self
._widgets
+= [DirectCheckButton(
170 text
=_('Fullscreen'), pos
=(0, 1, .3), command
=self
.on_fullscreen
,
171 indicator_frameColor
=self
._common
_opt
['highlightColor'],
172 indicator_relief
=self
._common
_btn
['relief'],
173 indicatorValue
=self
._opt
_file
['settings']['fullscreen'],
175 self
._widgets
[-1].__class
__ = type('DirectCheckButtonMixed', (DirectCheckButton
, DirectGuiMixin
), {})
176 self
._pos
_mgr
['fullscreen'] = self
._widgets
[-1].pos_pixel()
177 res
= self
._opt
_file
['settings']['resolution']
178 d_i
= base
.pipe
.get_display_information()
180 return d_i
.get_display_mode_width(idx
), \
181 d_i
.get_display_mode_height(idx
)
183 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
184 resolutions
= list(set(resolutions
))
185 resolutions
= sorted(resolutions
)
186 resolutions
= [(str(_res
[0]), str(_res
[1])) for _res
in resolutions
]
187 resolutions
= ['x'.join(_res
) for _res
in resolutions
]
189 res
= resolutions
[-1]
192 for element
in ['res_1440x900', 'res_1360x768']:
193 if element
in self
._pos
_mgr
:
194 del self
._pos
_mgr
[element
]
195 for tgt_res
in ['1440x900', '1360x768']:
197 if btn
['text'] == tgt_res
:
198 pos
= btn
.pos_pixel()
199 self
._pos
_mgr
['res_' + tgt_res
] = (pos
[0] + 5, pos
[1])
200 btn
= DirectOptionMenuTest(
201 text
=_('Resolution'), items
=resolutions
, initialitem
=res
,
202 pos
=(0, 1, .05), command
=self
.on_resolution
, **self
._common
_opt
)
203 btn
.__class
__ = type('DirectOptionMenuMixed', (DirectOptionMenu
, DirectGuiMixin
), {})
204 btn
._show
_cb
= res_cb
205 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
206 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
207 self
._widgets
+= [btn
]
208 pos_res
= self
._widgets
[-1].pos_pixel()
209 self
._pos
_mgr
['resolutions'] = pos_res
# 680 365
210 self
._pos
_mgr
['res_1440x900'] = [pos_res
[0] + 320, pos_res
[1] + 75]
211 self
._pos
_mgr
['res_1360x768'] = [pos_res
[0] + 430, pos_res
[1] -285]
212 self
._widgets
+= [DirectCheckButton(
213 text
=_('Antialiasing'), pos
=(0, 1, -.2), command
=self
.on_aa
,
214 indicator_frameColor
=self
._common
_opt
['highlightColor'],
215 indicator_relief
=self
._common
_btn
['relief'],
216 indicatorValue
=self
._opt
_file
['settings']['antialiasing'],
218 self
._widgets
[-1].__class
__ = type('DirectCheckButtonMixed', (DirectCheckButton
, DirectGuiMixin
), {})
219 self
._pos
_mgr
['aa'] = self
._widgets
[-1].pos_pixel()
220 self
._widgets
+= [DirectCheckButton(
221 text
=_('Shadows'), pos
=(0, 1, -.45), command
=self
.on_shadows
,
222 indicator_frameColor
=self
._common
_opt
['highlightColor'],
223 indicator_relief
=self
._common
_btn
['relief'],
224 indicatorValue
=self
._opt
_file
['settings']['shadows'],
226 self
._widgets
[-1].__class
__ = type('DirectCheckButtonMixed', (DirectCheckButton
, DirectGuiMixin
), {})
227 self
._pos
_mgr
['shadows'] = self
._widgets
[-1].pos_pixel()
228 self
._widgets
+= [DirectButton(
229 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
231 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
232 self
._pos
_mgr
['back'] = self
._widgets
[-1].pos_pixel()
233 self
.accept('enforce_resolution', self
.enforce_res
)
235 def _set_credits(self
):
238 tp_scale
= TextProperties()
239 tp_scale
.set_text_scale(.64)
240 TextPropertiesManager
.getGlobalPtr().setProperties('scale', tp_scale
)
241 self
._widgets
+= [OnscreenText(
242 _('Code and gfx\n \1scale\1Flavio Calva\2\n\n\nMusic\n \1scale\1Stefan Grossmann\2'),
243 pos
=(-.9, .55), font
=self
._common
['text_font'],
244 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
245 align
=TextNode
.A_left
)]
246 self
._widgets
+= [DirectButton(
247 text
=_('Website'), pos
=(-.6, 1, .29), command
=self
.on_website
,
248 **self
._common
_btn |
{'scale': .08})]
249 self
._widgets
+= [OnscreenText(
250 _('Special thanks to:\n \1scale\1rdb\2\n \1scale\1Luisa Tenuta\2\n \1scale\1Damiana Ercolani\2'),
251 pos
=(.1, .55), font
=self
._common
['text_font'],
252 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
253 align
=TextNode
.A_left
)]
254 self
._widgets
+= [DirectButton(
255 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
257 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
258 self
._pos
_mgr
['back'] = self
._widgets
[-1].pos_pixel()
259 self
.accept('enforce_resolution', self
.enforce_res
)
264 self
._cursor
= MouseCursor(
265 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
266 (.01, .01), self
._fun
_test
)
268 cmn
= self
._common
_btn
.copy() |
{
269 'frameSize': (-2.4, 2.4, -2.4, 2.4),
270 'frameColor': (1, 1, 1, .8),
272 left
= - (dx
:= .8) * (min(4, len(self
._scenes
)) - 1) / 2
273 from pmachines
.scene
.scene
import Scene
274 for i
, scene_name
in enumerate(self
._scenes
):
275 top
= .1 if len(self
._scenes
) < 5 else .6
276 row
= 0 if i
< 4 else 1
278 if Scene
.is_done(scene_name
):
279 new_cmn
['frameColor'] = (1, 1, 1, .4)
280 new_cmn
['text_fg'] = (.9, .9, .9, .4)
281 self
._widgets
+= [DirectButton(
282 text
=Scene
.name(scene_name
), pos
=(left
+ dx
* (i
% 4), 1, top
- dx
* row
),
283 command
=self
.start
, extraArgs
=[scene_name
], text_wordwrap
=6,
284 frameTexture
='assets/images/scenes/%s.dds' % scene_name
,
286 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
287 name
= scene_name
.lower()
288 self
._pos
_mgr
[name
] = self
._widgets
[-1].pos_pixel()
290 tnode
= self
._widgets
[-1].component('text%s' % j
).textNode
291 height
= - tnode
.getLineHeight() / 2
292 height
+= (tnode
.get_height() - tnode
.get_line_height()) / 2
293 self
._widgets
[-1].component('text%s' % j
).set_pos(0, 0, height
)
294 self
._widgets
+= [DirectButton(
295 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
297 self
._widgets
[-1].__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
298 self
._pos
_mgr
['back'] = self
._widgets
[-1].pos_pixel()
300 def start(self
, cls
):
301 self
._fsm
.demand('Scene', cls
)
303 def on_options(self
):
305 self
._cursor
= MouseCursor(
306 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
307 (.01, .01), self
._fun
_test
)
310 def on_credits(self
):
312 self
._cursor
= MouseCursor(
313 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
314 (.01, .01), self
._fun
_test
)
317 def _rearrange_width(self
):
319 for wdg
in self
._widgets
:
320 t_n
= wdg
.component('text0')
321 u_l
= t_n
.textNode
.get_upper_left_3d()
322 l_r
= t_n
.textNode
.get_lower_right_3d()
323 max_width
= max(l_r
[0] - u_l
[0], max_width
)
324 for wdg
in self
._widgets
:
325 m_w
= max_width
/ 2 + .8
326 wdg
['frameSize'] = -m_w
, m_w
, wdg
['frameSize'][2], wdg
['frameSize'][3]
328 def on_language(self
, arg
):
330 _('English'): 'en_EN',
331 _('Italian'): 'it_IT'}[arg
]
332 self
._lang
_mgr
.set_language(lang_code
)
333 self
._opt
_file
['settings']['language'] = lang_code
[:2]
334 self
._opt
_file
.store()
338 self
._opt
_file
['settings']['volume'] = self
._slider
['value']
339 self
._music
.set_volume(self
._slider
['value'])
341 def on_fullscreen(self
, arg
):
342 props
= WindowProperties()
343 props
.set_fullscreen(arg
)
344 if not self
._fun
_test
:
345 base
.win
.request_properties(props
)
346 # if we actually switch to fullscreen during the tests then
347 # exwm inside qemu can't restore the correct resolution
348 # i may re-enable this if/when i run the tests onto a
350 self
._opt
_file
['settings']['fullscreen'] = int(arg
)
351 self
._opt
_file
.store()
353 def on_resolution(self
, arg
):
354 info('on resolution: %s (%s)' % (arg
, self
._enforced
_res
))
355 arg
= self
._enforced
_res
or arg
356 info('set resolution: %s' % arg
)
357 props
= WindowProperties()
358 props
.set_size(LVector2i(*[int(_res
) for _res
in arg
.split('x')]))
359 base
.win
.request_properties(props
)
360 self
._opt
_file
['settings']['resolution'] = arg
361 self
._opt
_file
.store()
363 def on_aa(self
, arg
):
364 self
._pipeline
.msaa_samples
= 4 if arg
else 1
365 debug(f
'msaa: {self._pipeline.msaa_samples}')
366 self
._opt
_file
['settings']['antialiasing'] = int(arg
)
367 self
._opt
_file
.store()
369 def on_shadows(self
, arg
):
370 self
._pipeline
.enable_shadows
= int(arg
)
371 debug(f
'shadows: {self._pipeline.enable_shadows}')
372 self
._opt
_file
['settings']['shadows'] = int(arg
)
373 self
._opt
_file
.store()
375 def on_website(self
):
376 if platform
.startswith('linux'):
377 environ
['LD_LIBRARY_PATH'] = ''
378 system('xdg-open https://www.ya2.it')
380 open_new_tab('https://www.ya2.it')
383 self
._opt
_file
.store()
385 self
._cursor
= MouseCursor(
386 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
387 (.01, .01), self
._fun
_test
)
391 [wdg
.destroy() for wdg
in self
._widgets
]
392 self
._cursor
.destroy()
393 self
.ignore('enforce_resolution')