ya2 · news · projects · code · about

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