1 from logging
import info
, debug
2 from sys
import platform
, exit
3 from os
import environ
, system
5 from importlib
import import_module
6 from inspect
import isclass
7 from webbrowser
import open_new_tab
8 from xmlrpc
.client
import ServerProxy
9 from panda3d
.core
import Texture
, TextNode
, WindowProperties
, LVector2i
, \
10 TextProperties
, TextPropertiesManager
11 from direct
.gui
.DirectGui
import DirectButton
, DirectCheckButton
, \
12 DirectOptionMenu
, DirectSlider
, DirectCheckButton
13 from direct
.gui
.DirectGuiGlobals
import FLAT
14 from direct
.gui
.OnscreenText
import OnscreenText
15 from direct
.showbase
.DirectObject
import DirectObject
16 from ya2
.utils
.cursor
import MouseCursor
17 from logics
.scene
import Scene
18 from panda3d
.bullet
import BulletWorld
21 class Menu(DirectObject
):
23 def __init__(self
, fsm
, lang_mgr
, opt_file
, music
, pipeline
, scenes
, fun_test
):
26 self
._lang
_mgr
= lang_mgr
27 self
._opt
_file
= opt_file
29 self
._pipeline
= pipeline
31 self
._fun
_test
= fun_test
32 self
._enforced
_res
= ''
33 self
._cursor
= MouseCursor(
34 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
36 self
._font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
38 self
._font
.set_pixels_per_unit(60)
39 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
40 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
44 'text_font': self
._font
,
45 'text_fg': (.9, .9, .9, 1),
47 'frameColor': (.4, .4, .4, .14),
48 'rolloverSound': loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
49 'clickSound': loader
.load_sfx('assets/audio/sfx/click.ogg')}
50 self
._common
_btn
= {'frameSize': (-4.8, 4.8, -.6, 1.2)} | self
._common
51 hlc
= self
._common
_btn
['frameColor']
52 hlc
= (hlc
[0] + .2, hlc
[1] + .2, hlc
[2] + .2, hlc
[3] + .2)
54 'item_frameColor': self
._common
_btn
['frameColor'],
55 'popupMarker_frameColor': self
._common
_btn
['frameColor'],
56 'item_relief': self
._common
_btn
['relief'],
57 'item_text_font': self
._common
_btn
['text_font'],
58 'item_text_fg': self
._common
_btn
['text_fg'],
60 'highlightColor': hlc
,
61 'text_align': TextNode
.A_center
,
63 f_s
= self
._common
_opt
['frameSize']
64 self
._common
_opt
['frameSize'] = f_s
[0], f_s
[1] - .56, f_s
[2], f_s
[3]
65 self
._common
_slider
= self
._common |
{
67 'thumb_frameColor': (.4, .4, .4, .4),
70 del self
._common
_slider
['rolloverSound']
71 del self
._common
_slider
['clickSound']
74 def enforce_res(self
, val
):
75 self
._enforced
_res
= val
76 info('enforced resolution: ' + val
)
80 self
._widgets
+= [DirectButton(
81 text
=_('Play'), pos
=(0, 1, .6), command
=self
.on_play
,
83 self
._widgets
+= [DirectButton(
84 text
=_('Options'), pos
=(0, 1, .2), command
=self
.on_options
,
86 self
._widgets
+= [DirectButton(
87 text
=_('Credits'), pos
=(0, 1, -.2), command
=self
.on_credits
,
91 ServerProxy('http://localhost:6000').destroy()
93 self
._widgets
+= [DirectButton(
94 text
=_('Exit'), pos
=(0, 1, -.6), command
=lambda: btn_exit(),
96 self
._rearrange
_width
()
97 self
.accept('enforce_resolution', self
.enforce_res
)
99 def _set_options(self
):
101 self
._lang
_funcs
= [lambda: _('English'), lambda: _('Italian')]
102 items
= [fnc() for fnc
in self
._lang
_funcs
]
106 }[self
._opt
_file
['settings']['language']]
107 btn
= DirectOptionMenu(
108 text
=_('Language'), items
=items
, initialitem
=inititem
,
109 pos
=(0, 1, .8), command
=self
.on_language
, **self
._common
_opt
)
110 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
111 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
112 self
._widgets
+= [btn
]
113 self
._widgets
+= [OnscreenText(
114 _('Volume'), pos
=(-.1, .55), font
=self
._common
['text_font'],
115 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
116 align
=TextNode
.A_right
)]
117 self
._widgets
+= [DirectSlider(
119 value
=self
._opt
_file
['settings']['volume'],
120 command
=self
.on_volume
,
121 **self
._common
_slider
)]
122 self
._slider
= self
._widgets
[-1]
123 self
._widgets
+= [DirectCheckButton(
124 text
=_('Fullscreen'), pos
=(0, 1, .3), command
=self
.on_fullscreen
,
125 indicator_frameColor
=self
._common
_opt
['highlightColor'],
126 indicator_relief
=self
._common
_btn
['relief'],
127 indicatorValue
=self
._opt
_file
['settings']['fullscreen'],
129 res
= self
._opt
_file
['settings']['resolution']
130 d_i
= base
.pipe
.get_display_information()
132 return d_i
.get_display_mode_width(idx
), \
133 d_i
.get_display_mode_height(idx
)
135 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
136 resolutions
= list(set(resolutions
))
137 resolutions
= sorted(resolutions
)
138 resolutions
= [(str(_res
[0]), str(_res
[1])) for _res
in resolutions
]
139 resolutions
= ['x'.join(_res
) for _res
in resolutions
]
141 res
= resolutions
[-1]
142 btn
= DirectOptionMenu(
143 text
=_('Resolution'), items
=resolutions
, initialitem
=res
,
144 pos
=(0, 1, .05), command
=self
.on_resolution
, **self
._common
_opt
)
145 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
146 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
147 self
._widgets
+= [btn
]
148 self
._widgets
+= [DirectCheckButton(
149 text
=_('Antialiasing'), pos
=(0, 1, -.2), command
=self
.on_aa
,
150 indicator_frameColor
=self
._common
_opt
['highlightColor'],
151 indicator_relief
=self
._common
_btn
['relief'],
152 indicatorValue
=self
._opt
_file
['settings']['antialiasing'],
154 self
._widgets
+= [DirectCheckButton(
155 text
=_('Shadows'), pos
=(0, 1, -.45), command
=self
.on_shadows
,
156 indicator_frameColor
=self
._common
_opt
['highlightColor'],
157 indicator_relief
=self
._common
_btn
['relief'],
158 indicatorValue
=self
._opt
_file
['settings']['shadows'],
160 self
._widgets
+= [DirectButton(
161 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
163 self
.accept('enforce_resolution', self
.enforce_res
)
165 def _set_credits(self
):
167 tp_scale
= TextProperties()
168 tp_scale
.set_text_scale(.64)
169 TextPropertiesManager
.getGlobalPtr().setProperties('scale', tp_scale
)
170 self
._widgets
+= [OnscreenText(
171 _('Code and gfx\n \1scale\1Flavio Calva\2\n\n\nMusic\n \1scale\1Stefan Grossmann\2'),
172 pos
=(-.9, .55), font
=self
._common
['text_font'],
173 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
174 align
=TextNode
.A_left
)]
175 self
._widgets
+= [DirectButton(
176 text
=_('Website'), pos
=(-.6, 1, .29), command
=self
.on_website
,
177 **self
._common
_btn |
{'scale': .08})]
178 self
._widgets
+= [OnscreenText(
179 _('Special thanks to:\n \1scale\1rdb\2\n \1scale\1Luisa Tenuta\2\n \1scale\1Damiana Ercolani\2'),
180 pos
=(.1, .55), font
=self
._common
['text_font'],
181 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
182 align
=TextNode
.A_left
)]
183 self
._widgets
+= [DirectButton(
184 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
186 self
.accept('enforce_resolution', self
.enforce_res
)
190 self
._cursor
= MouseCursor(
191 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
194 cmn
= self
._common
_btn
.copy() |
{
195 'frameSize': (-2.4, 2.4, -2.4, 2.4),
196 'frameColor': (1, 1, 1, .8),
198 left
= - (dx
:= .8) * (min(4, len(self
._scenes
)) - 1) / 2
199 for i
, cls
in enumerate(self
._scenes
):
200 top
= .1 if len(self
._scenes
) < 5 else .6
201 row
= 0 if i
< 4 else 1
202 self
._widgets
+= [DirectButton(
203 text
=cls
.name(), pos
=(left
+ dx
* (i
% 4), 1, top
- dx
* row
),
204 command
=self
.start
, extraArgs
=[cls
], text_wordwrap
=6,
205 frameTexture
='assets/images/scenes/%s.dds' % cls
.__name
__,
208 tnode
= self
._widgets
[-1].component('text%s' % j
).textNode
209 height
= - tnode
.getLineHeight() / 2
210 height
+= (tnode
.get_height() - tnode
.get_line_height()) / 2
211 self
._widgets
[-1].component('text%s' % j
).set_pos(0, 0, height
)
212 self
._widgets
+= [DirectButton(
213 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
216 def start(self
, cls
):
217 self
._fsm
.demand('Scene', cls
)
219 def on_options(self
):
221 self
._cursor
= MouseCursor(
222 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
226 def on_credits(self
):
228 self
._cursor
= MouseCursor(
229 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
233 def _rearrange_width(self
):
235 for wdg
in self
._widgets
:
236 t_n
= wdg
.component('text0')
237 u_l
= t_n
.textNode
.get_upper_left_3d()
238 l_r
= t_n
.textNode
.get_lower_right_3d()
239 max_width
= max(l_r
[0] - u_l
[0], max_width
)
240 for wdg
in self
._widgets
:
241 m_w
= max_width
/ 2 + .8
242 wdg
['frameSize'] = -m_w
, m_w
, wdg
['frameSize'][2], wdg
['frameSize'][3]
244 def on_language(self
, arg
):
246 _('English'): 'en_EN',
247 _('Italian'): 'it_IT'}[arg
]
248 self
._lang
_mgr
.set_lang(lang_code
)
249 self
._opt
_file
['settings']['language'] = lang_code
[:2]
250 self
._opt
_file
.store()
254 self
._opt
_file
['settings']['volume'] = self
._slider
['value']
255 self
._music
.set_volume(self
._slider
['value'])
257 def on_fullscreen(self
, arg
):
258 props
= WindowProperties()
259 props
.set_fullscreen(arg
)
260 if not self
._fun
_test
:
261 base
.win
.request_properties(props
)
262 # if we actually switch to fullscreen during the tests then
263 # exwm inside qemu can't restore the correct resolution
264 # i may re-enable this if/when i run the tests onto a
266 self
._opt
_file
['settings']['fullscreen'] = int(arg
)
267 self
._opt
_file
.store()
269 def on_resolution(self
, arg
):
270 info('on resolution: %s (%s)' % (arg
, self
._enforced
_res
))
271 arg
= self
._enforced
_res
or arg
272 info('set resolution: %s' % arg
)
273 props
= WindowProperties()
274 props
.set_size(LVector2i(*[int(_res
) for _res
in arg
.split('x')]))
275 base
.win
.request_properties(props
)
276 self
._opt
_file
['settings']['resolution'] = arg
277 self
._opt
_file
.store()
279 def on_aa(self
, arg
):
280 self
._pipeline
.msaa_samples
= 4 if arg
else 1
281 debug(f
'msaa: {self._pipeline.msaa_samples}')
282 self
._opt
_file
['settings']['antialiasing'] = int(arg
)
283 self
._opt
_file
.store()
285 def on_shadows(self
, arg
):
286 self
._pipeline
.enable_shadows
= int(arg
)
287 debug(f
'shadows: {self._pipeline.enable_shadows}')
288 self
._opt
_file
['settings']['shadows'] = int(arg
)
289 self
._opt
_file
.store()
291 def on_website(self
):
292 if platform
.startswith('linux'):
293 environ
['LD_LIBRARY_PATH'] = ''
294 system('xdg-open https://www.ya2.it')
296 open_new_tab('https://www.ya2.it')
299 self
._opt
_file
.store()
301 self
._cursor
= MouseCursor(
302 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
307 [wdg
.destroy() for wdg
in self
._widgets
]
308 self
._cursor
.destroy()
309 self
.ignore('enforce_resolution')