ya2 · news · projects · code · about

changed do_physics
[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(
e67dbe53 163 _('Special thanks to:\n \1scale\1rdb\2\n \1scale\1Luisa Tenuta\2\n \1scale\1Damiana Ercolani\2'),
2612dfc8
FC
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):
9914cfc9 172 scenes = []
8c9bf90e
FC
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:
9914cfc9
FC
178 scenes += [member]
179 scenes = sorted(scenes, key=lambda elm: elm.sorting)
0d5a5427 180 self.destroy()
638deddf
FC
181 self._cursor = MouseCursor(
182 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
183 (.01, .01))
0d5a5427 184 self._widgets = []
8c9bf90e
FC
185 cmn = self._common_btn.copy() | {
186 'frameSize': (-2.4, 2.4, -2.4, 2.4),
187 'frameColor': (1, 1, 1, .8)}
9914cfc9
FC
188 left = - (dx := .8) * (len(scenes) - 1) / 2
189 for i, cls in enumerate(scenes):
28acea3a
FC
190 self._widgets += [DirectButton(
191 text=cls.name(), pos=(left + dx * i, 1, .1), command=self.start,
3211f3f3 192 extraArgs=[cls], text_wordwrap=4,
28acea3a
FC
193 frameTexture='assets/images/scenes/%s.png' % cls.__name__,
194 **cmn)]
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()
638deddf
FC
204 self._cursor = MouseCursor(
205 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
206 (.01, .01))
4071c6d8
FC
207 self._set_options()
208
2612dfc8
FC
209 def on_credits(self):
210 self.destroy()
638deddf
FC
211 self._cursor = MouseCursor(
212 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
213 (.01, .01))
2612dfc8
FC
214 self._set_credits()
215
75b89b83
FC
216 def _rearrange_width(self):
217 max_width = 0
218 for wdg in self._widgets:
219 t_n = wdg.component('text0')
220 u_l = t_n.textNode.get_upper_left_3d()
221 l_r = t_n.textNode.get_lower_right_3d()
222 max_width = max(l_r[0] - u_l[0], max_width)
223 for wdg in self._widgets:
224 m_w = max_width / 2 + .8
225 wdg['frameSize'] = -m_w, m_w, wdg['frameSize'][2], wdg['frameSize'][3]
226
4071c6d8
FC
227 def on_language(self, arg):
228 lang_code = {
229 _('English'): 'en_EN',
230 _('Italian'): 'it_IT'}[arg]
231 self._lang_mgr.set_lang(lang_code)
232 self._opt_file['settings']['language'] = lang_code[:2]
233 self._opt_file.store()
234 self.on_options()
235
e1e44d5c
FC
236 def on_volume(self):
237 self._opt_file['settings']['volume'] = self._slider['value']
238 self._music.set_volume(self._slider['value'])
239
6fff1464
FC
240 def on_fullscreen(self, arg):
241 props = WindowProperties()
242 props.set_fullscreen(arg)
243 base.win.request_properties(props)
244 self._opt_file['settings']['fullscreen'] = int(arg)
245 self._opt_file.store()
246
247 def on_resolution(self, arg):
248 props = WindowProperties()
249 props.set_size(LVector2i(*[int(_res) for _res in arg.split('x')]))
250 base.win.request_properties(props)
251 self._opt_file['settings']['resolution'] = arg
252 self._opt_file.store()
253
a9aba267
FC
254 def on_aa(self, arg):
255 self._pipeline.msaa_samples = 4 if arg else 1
94a18c21 256 debug(f'msaa: {self._pipeline.msaa_samples}')
a9aba267
FC
257 self._opt_file['settings']['antialiasing'] = int(arg)
258 self._opt_file.store()
259
5fdf77d0
FC
260 def on_shadows(self, arg):
261 self._pipeline.enable_shadows = int(arg)
94a18c21 262 debug(f'shadows: {self._pipeline.enable_shadows}')
5fdf77d0
FC
263 self._opt_file['settings']['shadows'] = int(arg)
264 self._opt_file.store()
265
2612dfc8
FC
266 def on_website(self):
267 if platform.startswith('linux'):
268 environ['LD_LIBRARY_PATH'] = ''
269 system('xdg-open https://www.ya2.it')
270 else:
271 open_new_tab('https://www.ya2.it')
272
4071c6d8 273 def on_back(self):
e1e44d5c 274 self._opt_file.store()
4071c6d8 275 self.destroy()
638deddf
FC
276 self._cursor = MouseCursor(
277 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
278 (.01, .01))
4071c6d8
FC
279 self._set_main()
280
281 def destroy(self):
e1e44d5c 282 [wdg.destroy() for wdg in self._widgets]
407412a5 283 self._cursor.destroy()