ya2 · news · projects · code · about

second level
[pmachines.git] / pmachines / menu.py
CommitLineData
94a18c21 1from logging import info, debug
2612dfc8
FC
2from sys import platform, exit
3from os import environ, system
8c9bf90e
FC
4from glob import glob
5from importlib import import_module
6from inspect import isclass
2612dfc8
FC
7from webbrowser import open_new_tab
8from panda3d.core import Texture, TextNode, WindowProperties, LVector2i, \
9 TextProperties, TextPropertiesManager
4071c6d8 10from direct.gui.DirectGui import DirectButton, DirectCheckButton, \
6fff1464 11 DirectOptionMenu, DirectSlider, DirectCheckButton
4071c6d8 12from direct.gui.DirectGuiGlobals import FLAT
e1e44d5c 13from direct.gui.OnscreenText import OnscreenText
407412a5 14from lib.engine.gui.cursor import MouseCursor
0d5a5427
FC
15from pmachines.scene import Scene
16from panda3d.bullet import BulletWorld
4071c6d8
FC
17
18
19class Menu:
20
a9aba267 21 def __init__(self, fsm, lang_mgr, opt_file, music, pipeline):
4071c6d8
FC
22 self._fsm = fsm
23 self._lang_mgr = lang_mgr
24 self._opt_file = opt_file
e1e44d5c 25 self._music = music
a9aba267 26 self._pipeline = pipeline
407412a5
FC
27 self._cursor = MouseCursor(
28 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
29 (.01, .01))
4071c6d8
FC
30 self._font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
31 self._font.clear()
32 self._font.set_pixels_per_unit(60)
33 self._font.set_minfilter(Texture.FTLinearMipmapLinear)
34 self._font.set_outline((0, 0, 0, 1), .8, .2)
e1e44d5c 35 self._widgets = []
4071c6d8
FC
36 self._common = {
37 'scale': .12,
38 'text_font': self._font,
39 'text_fg': (.9, .9, .9, 1),
40 'relief': FLAT,
41 'frameColor': (.4, .4, .4, .14),
42 'rolloverSound': loader.load_sfx('assets/audio/sfx/rollover.ogg'),
43 'clickSound': loader.load_sfx('assets/audio/sfx/click.ogg')}
75b89b83 44 self._common_btn = {'frameSize': (-4.8, 4.8, -.6, 1.2)} | self._common
6fff1464
FC
45 hlc = self._common_btn['frameColor']
46 hlc = (hlc[0] + .2, hlc[1] + .2, hlc[2] + .2, hlc[3] + .2)
47 self._common_opt = {
48 'item_frameColor': self._common_btn['frameColor'],
49 'popupMarker_frameColor': self._common_btn['frameColor'],
50 'item_relief': self._common_btn['relief'],
51 'item_text_font': self._common_btn['text_font'],
52 'item_text_fg': self._common_btn['text_fg'],
53 'textMayChange': 1,
54 'highlightColor': hlc,
55 'text_align': TextNode.A_center,
56 } | self._common_btn
57 f_s = self._common_opt['frameSize']
58 self._common_opt['frameSize'] = f_s[0], f_s[1] - .56, f_s[2], f_s[3]
e1e44d5c
FC
59 self._common_slider = self._common | {
60 'range': (0, 1),
61 'thumb_frameColor': (.4, .4, .4, .4),
62 'thumb_scale': 1.6,
63 'scale': .4}
64 del self._common_slider['rolloverSound']
65 del self._common_slider['clickSound']
4071c6d8
FC
66 self._set_main()
67
68 def _set_main(self):
e1e44d5c
FC
69 self._widgets = []
70 self._widgets += [DirectButton(
4071c6d8
FC
71 text=_('Play'), pos=(0, 1, .6), command=self.on_play,
72 **self._common_btn)]
e1e44d5c 73 self._widgets += [DirectButton(
2612dfc8 74 text=_('Options'), pos=(0, 1, .2), command=self.on_options,
4071c6d8 75 **self._common_btn)]
e1e44d5c 76 self._widgets += [DirectButton(
2612dfc8 77 text=_('Credits'), pos=(0, 1, -.2), command=self.on_credits,
4071c6d8 78 **self._common_btn)]
e1e44d5c 79 self._widgets += [DirectButton(
2612dfc8 80 text=_('Exit'), pos=(0, 1, -.6), command=lambda: exit(),
4071c6d8 81 **self._common_btn)]
75b89b83 82 self._rearrange_width()
4071c6d8
FC
83
84 def _set_options(self):
e1e44d5c 85 self._widgets = []
4071c6d8
FC
86 self._lang_funcs = [lambda: _('English'), lambda: _('Italian')]
87 items = [fnc() for fnc in self._lang_funcs]
88 inititem = {
89 'en': _('English'),
90 'it': _('Italian')
91 }[self._opt_file['settings']['language']]
4071c6d8
FC
92 btn = DirectOptionMenu(
93 text=_('Language'), items=items, initialitem=inititem,
a9aba267 94 pos=(0, 1, .8), command=self.on_language, **self._common_opt)
4071c6d8
FC
95 btn.popupMenu['frameColor'] = self._common_btn['frameColor']
96 btn.popupMenu['relief'] = self._common_btn['relief']
e1e44d5c
FC
97 self._widgets += [btn]
98 self._widgets += [OnscreenText(
a9aba267 99 _('Volume'), pos=(-.1, .55), font=self._common['text_font'],
e1e44d5c
FC
100 scale=self._common['scale'], fg=self._common['text_fg'],
101 align=TextNode.A_right)]
102 self._widgets += [DirectSlider(
a9aba267 103 pos=(.5, 1, .57),
e1e44d5c
FC
104 value=self._opt_file['settings']['volume'],
105 command=self.on_volume,
106 **self._common_slider)]
107 self._slider = self._widgets[-1]
6fff1464 108 self._widgets += [DirectCheckButton(
a9aba267 109 text=_('Fullscreen'), pos=(0, 1, .3), command=self.on_fullscreen,
6fff1464
FC
110 indicator_frameColor=self._common_opt['highlightColor'],
111 indicator_relief=self._common_btn['relief'],
112 indicatorValue=self._opt_file['settings']['fullscreen'],
113 **self._common_btn)]
114 res = self._opt_file['settings']['resolution']
115 d_i = base.pipe.get_display_information()
116 def _res(idx):
117 return d_i.get_display_mode_width(idx), \
118 d_i.get_display_mode_height(idx)
119 resolutions = [
120 _res(idx) for idx in range(d_i.get_total_display_modes())]
121 resolutions = list(set(resolutions))
122 resolutions = sorted(resolutions)
123 resolutions = [(str(_res[0]), str(_res[1])) for _res in resolutions]
124 resolutions = ['x'.join(_res) for _res in resolutions]
125 if not res:
126 res = resolutions[-1]
127 btn = DirectOptionMenu(
128 text=_('Resolution'), items=resolutions, initialitem=res,
a9aba267 129 pos=(0, 1, .05), command=self.on_resolution, **self._common_opt)
6fff1464
FC
130 btn.popupMenu['frameColor'] = self._common_btn['frameColor']
131 btn.popupMenu['relief'] = self._common_btn['relief']
132 self._widgets += [btn]
a9aba267
FC
133 self._widgets += [DirectCheckButton(
134 text=_('Antialiasing'), pos=(0, 1, -.2), command=self.on_aa,
135 indicator_frameColor=self._common_opt['highlightColor'],
136 indicator_relief=self._common_btn['relief'],
137 indicatorValue=self._opt_file['settings']['antialiasing'],
138 **self._common_btn)]
5fdf77d0
FC
139 self._widgets += [DirectCheckButton(
140 text=_('Shadows'), pos=(0, 1, -.45), command=self.on_shadows,
141 indicator_frameColor=self._common_opt['highlightColor'],
142 indicator_relief=self._common_btn['relief'],
143 indicatorValue=self._opt_file['settings']['shadows'],
144 **self._common_btn)]
e1e44d5c 145 self._widgets += [DirectButton(
a9aba267 146 text=_('Back'), pos=(0, 1, -.8), command=self.on_back,
4071c6d8
FC
147 **self._common_btn)]
148
2612dfc8
FC
149 def _set_credits(self):
150 self._widgets = []
151 tp_scale = TextProperties()
152 tp_scale.set_text_scale(.64)
153 TextPropertiesManager.getGlobalPtr().setProperties('scale', tp_scale)
154 self._widgets += [OnscreenText(
155 _('Code and gfx\n \1scale\1Flavio Calva\2\n\n\nMusic\n \1scale\1Stefan Grossmann\2'),
156 pos=(-.9, .55), font=self._common['text_font'],
157 scale=self._common['scale'], fg=self._common['text_fg'],
158 align=TextNode.A_left)]
159 self._widgets += [DirectButton(
160 text=_('Website'), pos=(-.6, 1, .29), command=self.on_website,
161 **self._common_btn | {'scale': .08})]
162 self._widgets += [OnscreenText(
163 _('Supporters\n \1scale\1rdb\2\n \1scale\1Luisa Tenuta\2\n \1scale\1Damiana Ercolani\2'),
164 pos=(.1, .55), font=self._common['text_font'],
165 scale=self._common['scale'], fg=self._common['text_fg'],
166 align=TextNode.A_left)]
167 self._widgets += [DirectButton(
168 text=_('Back'), pos=(0, 1, -.8), command=self.on_back,
169 **self._common_btn)]
170
4071c6d8 171 def on_play(self):
8c9bf90e
FC
172 scene_classes = []
173 for _file in glob('pmachines/scenes/*.py'):
174 _fn = _file.replace('.py', '').replace('/', '.')
175 for member in import_module(_fn).__dict__.values():
176 if isclass(member) and issubclass(member, Scene) and \
177 member != Scene:
178 scene_classes += [member]
0d5a5427
FC
179 self.destroy()
180 self._widgets = []
8c9bf90e
FC
181 cmn = self._common_btn.copy() | {
182 'frameSize': (-2.4, 2.4, -2.4, 2.4),
183 'frameColor': (1, 1, 1, .8)}
0d5a5427 184 self._widgets += [DirectButton(
8c9bf90e
FC
185 text=scene_classes[0].name(), pos=(-.4, 1, .1), command=self.start,
186 extraArgs=[scene_classes[0]], **cmn)]
187 self._widgets += [DirectButton(
188 text=scene_classes[1].name(), pos=(.4, 1, .1), command=self.start,
189 extraArgs=[scene_classes[1]], **cmn)]
190 #s = Scene(BulletWorld(), None, True, None)
191 #self._widgets[-1]['frameTexture'] = s.screenshot().make_copy()
192 #base.graphicsEngine.renderFrame()
193 #base.graphicsEngine.renderFrame()
194 #s.destroy()
0d5a5427
FC
195 self._widgets += [DirectButton(
196 text=_('Back'), pos=(0, 1, -.8), command=self.on_back,
197 **self._common_btn)]
198
8c9bf90e
FC
199 def start(self, cls):
200 self._fsm.demand('Scene', cls)
4071c6d8
FC
201
202 def on_options(self):
203 self.destroy()
204 self._set_options()
205
2612dfc8
FC
206 def on_credits(self):
207 self.destroy()
208 self._set_credits()
209
75b89b83
FC
210 def _rearrange_width(self):
211 max_width = 0
212 for wdg in self._widgets:
213 t_n = wdg.component('text0')
214 u_l = t_n.textNode.get_upper_left_3d()
215 l_r = t_n.textNode.get_lower_right_3d()
216 max_width = max(l_r[0] - u_l[0], max_width)
217 for wdg in self._widgets:
218 m_w = max_width / 2 + .8
219 wdg['frameSize'] = -m_w, m_w, wdg['frameSize'][2], wdg['frameSize'][3]
220
4071c6d8
FC
221 def on_language(self, arg):
222 lang_code = {
223 _('English'): 'en_EN',
224 _('Italian'): 'it_IT'}[arg]
225 self._lang_mgr.set_lang(lang_code)
226 self._opt_file['settings']['language'] = lang_code[:2]
227 self._opt_file.store()
228 self.on_options()
229
e1e44d5c
FC
230 def on_volume(self):
231 self._opt_file['settings']['volume'] = self._slider['value']
232 self._music.set_volume(self._slider['value'])
233
6fff1464
FC
234 def on_fullscreen(self, arg):
235 props = WindowProperties()
236 props.set_fullscreen(arg)
237 base.win.request_properties(props)
238 self._opt_file['settings']['fullscreen'] = int(arg)
239 self._opt_file.store()
240
241 def on_resolution(self, arg):
242 props = WindowProperties()
243 props.set_size(LVector2i(*[int(_res) for _res in arg.split('x')]))
244 base.win.request_properties(props)
245 self._opt_file['settings']['resolution'] = arg
246 self._opt_file.store()
247
a9aba267
FC
248 def on_aa(self, arg):
249 self._pipeline.msaa_samples = 4 if arg else 1
94a18c21 250 debug(f'msaa: {self._pipeline.msaa_samples}')
a9aba267
FC
251 self._opt_file['settings']['antialiasing'] = int(arg)
252 self._opt_file.store()
253
5fdf77d0
FC
254 def on_shadows(self, arg):
255 self._pipeline.enable_shadows = int(arg)
94a18c21 256 debug(f'shadows: {self._pipeline.enable_shadows}')
5fdf77d0
FC
257 self._opt_file['settings']['shadows'] = int(arg)
258 self._opt_file.store()
259
2612dfc8
FC
260 def on_website(self):
261 if platform.startswith('linux'):
262 environ['LD_LIBRARY_PATH'] = ''
263 system('xdg-open https://www.ya2.it')
264 else:
265 open_new_tab('https://www.ya2.it')
266
4071c6d8 267 def on_back(self):
e1e44d5c 268 self._opt_file.store()
4071c6d8
FC
269 self.destroy()
270 self._set_main()
271
272 def destroy(self):
e1e44d5c 273 [wdg.destroy() for wdg in self._widgets]
407412a5 274 self._cursor.destroy()