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 panda3d
.core
import Texture
, TextNode
, WindowProperties
, LVector2i
, \
9 TextProperties
, TextPropertiesManager
10 from direct
.gui
.DirectGui
import DirectButton
, DirectCheckButton
, \
11 DirectOptionMenu
, DirectSlider
, DirectCheckButton
12 from direct
.gui
.DirectGuiGlobals
import FLAT
13 from direct
.gui
.OnscreenText
import OnscreenText
14 from lib
.engine
.gui
.cursor
import MouseCursor
15 from pmachines
.scene
import Scene
16 from panda3d
.bullet
import BulletWorld
21 def __init__(self
, fsm
, lang_mgr
, opt_file
, music
, pipeline
):
23 self
._lang
_mgr
= lang_mgr
24 self
._opt
_file
= opt_file
26 self
._pipeline
= pipeline
27 self
._cursor
= MouseCursor(
28 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
30 self
._font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
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)
38 'text_font': self
._font
,
39 'text_fg': (.9, .9, .9, 1),
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')}
44 self
._common
_btn
= {'frameSize': (-4.8, 4.8, -.6, 1.2)} | self
._common
45 hlc
= self
._common
_btn
['frameColor']
46 hlc
= (hlc
[0] + .2, hlc
[1] + .2, hlc
[2] + .2, hlc
[3] + .2)
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'],
54 'highlightColor': hlc
,
55 'text_align': TextNode
.A_center
,
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]
59 self
._common
_slider
= self
._common |
{
61 'thumb_frameColor': (.4, .4, .4, .4),
64 del self
._common
_slider
['rolloverSound']
65 del self
._common
_slider
['clickSound']
70 self
._widgets
+= [DirectButton(
71 text
=_('Play'), pos
=(0, 1, .6), command
=self
.on_play
,
73 self
._widgets
+= [DirectButton(
74 text
=_('Options'), pos
=(0, 1, .2), command
=self
.on_options
,
76 self
._widgets
+= [DirectButton(
77 text
=_('Credits'), pos
=(0, 1, -.2), command
=self
.on_credits
,
79 self
._widgets
+= [DirectButton(
80 text
=_('Exit'), pos
=(0, 1, -.6), command
=lambda: exit(),
82 self
._rearrange
_width
()
84 def _set_options(self
):
86 self
._lang
_funcs
= [lambda: _('English'), lambda: _('Italian')]
87 items
= [fnc() for fnc
in self
._lang
_funcs
]
91 }[self
._opt
_file
['settings']['language']]
92 btn
= DirectOptionMenu(
93 text
=_('Language'), items
=items
, initialitem
=inititem
,
94 pos
=(0, 1, .8), command
=self
.on_language
, **self
._common
_opt
)
95 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
96 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
97 self
._widgets
+= [btn
]
98 self
._widgets
+= [OnscreenText(
99 _('Volume'), pos
=(-.1, .55), font
=self
._common
['text_font'],
100 scale
=self
._common
['scale'], fg
=self
._common
['text_fg'],
101 align
=TextNode
.A_right
)]
102 self
._widgets
+= [DirectSlider(
104 value
=self
._opt
_file
['settings']['volume'],
105 command
=self
.on_volume
,
106 **self
._common
_slider
)]
107 self
._slider
= self
._widgets
[-1]
108 self
._widgets
+= [DirectCheckButton(
109 text
=_('Fullscreen'), pos
=(0, 1, .3), command
=self
.on_fullscreen
,
110 indicator_frameColor
=self
._common
_opt
['highlightColor'],
111 indicator_relief
=self
._common
_btn
['relief'],
112 indicatorValue
=self
._opt
_file
['settings']['fullscreen'],
114 res
= self
._opt
_file
['settings']['resolution']
115 d_i
= base
.pipe
.get_display_information()
117 return d_i
.get_display_mode_width(idx
), \
118 d_i
.get_display_mode_height(idx
)
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
]
126 res
= resolutions
[-1]
127 btn
= DirectOptionMenu(
128 text
=_('Resolution'), items
=resolutions
, initialitem
=res
,
129 pos
=(0, 1, .05), command
=self
.on_resolution
, **self
._common
_opt
)
130 btn
.popupMenu
['frameColor'] = self
._common
_btn
['frameColor']
131 btn
.popupMenu
['relief'] = self
._common
_btn
['relief']
132 self
._widgets
+= [btn
]
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'],
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'],
145 self
._widgets
+= [DirectButton(
146 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
149 def _set_credits(self
):
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 _('Special thanks to:\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
,
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 \
179 scenes
= sorted(scenes
, key
=lambda elm
: elm
.sorting
)
181 self
._cursor
= MouseCursor(
182 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
185 cmn
= self
._common
_btn
.copy() |
{
186 'frameSize': (-2.4, 2.4, -2.4, 2.4),
187 'frameColor': (1, 1, 1, .8),
189 left
= - (dx
:= .8) * (min(4, len(scenes
)) - 1) / 2
190 for i
, cls
in enumerate(scenes
):
191 top
= .1 if len(scenes
) < 5 else .6
192 row
= 0 if i
< 4 else 1
193 self
._widgets
+= [DirectButton(
194 text
=cls
.name(), pos
=(left
+ dx
* (i
% 4), 1, top
- dx
* row
),
195 command
=self
.start
, extraArgs
=[cls
], text_wordwrap
=6,
196 frameTexture
='assets/images/scenes/%s.dds' % cls
.__name
__,
199 tnode
= self
._widgets
[-1].component('text%s' % j
).textNode
200 height
= - tnode
.getLineHeight() / 2
201 height
+= (tnode
.get_height() - tnode
.get_line_height()) / 2
202 self
._widgets
[-1].component('text%s' % j
).set_pos(0, 0, height
)
203 self
._widgets
+= [DirectButton(
204 text
=_('Back'), pos
=(0, 1, -.8), command
=self
.on_back
,
207 def start(self
, cls
):
208 self
._fsm
.demand('Scene', cls
)
210 def on_options(self
):
212 self
._cursor
= MouseCursor(
213 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
217 def on_credits(self
):
219 self
._cursor
= MouseCursor(
220 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
224 def _rearrange_width(self
):
226 for wdg
in self
._widgets
:
227 t_n
= wdg
.component('text0')
228 u_l
= t_n
.textNode
.get_upper_left_3d()
229 l_r
= t_n
.textNode
.get_lower_right_3d()
230 max_width
= max(l_r
[0] - u_l
[0], max_width
)
231 for wdg
in self
._widgets
:
232 m_w
= max_width
/ 2 + .8
233 wdg
['frameSize'] = -m_w
, m_w
, wdg
['frameSize'][2], wdg
['frameSize'][3]
235 def on_language(self
, arg
):
237 _('English'): 'en_EN',
238 _('Italian'): 'it_IT'}[arg
]
239 self
._lang
_mgr
.set_lang(lang_code
)
240 self
._opt
_file
['settings']['language'] = lang_code
[:2]
241 self
._opt
_file
.store()
245 self
._opt
_file
['settings']['volume'] = self
._slider
['value']
246 self
._music
.set_volume(self
._slider
['value'])
248 def on_fullscreen(self
, arg
):
249 props
= WindowProperties()
250 props
.set_fullscreen(arg
)
251 base
.win
.request_properties(props
)
252 self
._opt
_file
['settings']['fullscreen'] = int(arg
)
253 self
._opt
_file
.store()
255 def on_resolution(self
, arg
):
256 props
= WindowProperties()
257 props
.set_size(LVector2i(*[int(_res
) for _res
in arg
.split('x')]))
258 base
.win
.request_properties(props
)
259 self
._opt
_file
['settings']['resolution'] = arg
260 self
._opt
_file
.store()
262 def on_aa(self
, arg
):
263 self
._pipeline
.msaa_samples
= 4 if arg
else 1
264 debug(f
'msaa: {self._pipeline.msaa_samples}')
265 self
._opt
_file
['settings']['antialiasing'] = int(arg
)
266 self
._opt
_file
.store()
268 def on_shadows(self
, arg
):
269 self
._pipeline
.enable_shadows
= int(arg
)
270 debug(f
'shadows: {self._pipeline.enable_shadows}')
271 self
._opt
_file
['settings']['shadows'] = int(arg
)
272 self
._opt
_file
.store()
274 def on_website(self
):
275 if platform
.startswith('linux'):
276 environ
['LD_LIBRARY_PATH'] = ''
277 system('xdg-open https://www.ya2.it')
279 open_new_tab('https://www.ya2.it')
282 self
._opt
_file
.store()
284 self
._cursor
= MouseCursor(
285 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
290 [wdg
.destroy() for wdg
in self
._widgets
]
291 self
._cursor
.destroy()