ya2 · news · projects · code · about

dds and bam
[pmachines.git] / pmachines / scene.py
CommitLineData
31237524 1from os.path import exists
9914cfc9
FC
2from glob import glob
3from importlib import import_module
4from inspect import isclass
2aaa10d3 5from panda3d.core import AmbientLight, DirectionalLight, Point3, Texture, \
bdfa6dda 6 TextPropertiesManager, TextNode, Spotlight, PerspectiveLens, BitMask32
36099535
FC
7from panda3d.bullet import BulletPlaneShape, BulletGhostNode
8from direct.gui.OnscreenImage import OnscreenImage
2aaa10d3
FC
9from direct.gui.OnscreenText import OnscreenText
10from direct.gui.DirectGui import DirectButton, DirectFrame
36099535 11from direct.gui.DirectGuiGlobals import FLAT, DISABLED, NORMAL
1be87278
FC
12from direct.showbase.DirectObject import DirectObject
13from pmachines.items.background import Background
a5dc83f4 14from pmachines.sidepanel import SidePanel
79f81d48 15from lib.engine.gui.cursor import MouseCursor
13263131 16from lib.lib.p3d.gfx import P3dGfxMgr
1be87278
FC
17
18
19class Scene(DirectObject):
20
9914cfc9 21 def __init__(self, world, exit_cb, auto_close_instr, dbg_items, reload_cb):
1be87278
FC
22 super().__init__()
23 self._world = world
5964572b 24 self._exit_cb = exit_cb
31237524 25 self._dbg_items = dbg_items
9914cfc9 26 self._reload_cb = reload_cb
1be87278 27 self._set_camera()
79f81d48 28 self._cursor = MouseCursor(
420ce99a 29 'assets/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
79f81d48 30 (.01, .01))
36099535 31 self._set_gui()
1be87278
FC
32 self._set_lights()
33 self._set_input()
c8d8653f 34 self._set_mouse_plane()
d5932612
FC
35 self.items = []
36 self.reset()
0a0994e4 37 self._state = 'init'
9830561d 38 self._paused = False
7c0a81ae 39 self._item_active = None
e669403e 40 if auto_close_instr:
32aa4dae 41 self.__store_state()
e669403e
FC
42 self.__restore_state()
43 else:
44 self._set_instructions()
5964572b
FC
45 self._bg = Background()
46 self._side_panel = SidePanel(world, self._mouse_plane_node, (-5, 4), (-3, 1), 1, self.items)
47 self._scene_tsk = taskMgr.add(self.on_frame, 'on_frame')
48
8c9bf90e
FC
49 @staticmethod
50 def name():
51 return ''
52
0eff64a3
FC
53 def _instr_txt(self):
54 return ''
55
56 def _set_items(self):
57 self.items = []
58
0d5a5427 59 def screenshot(self, task=None):
ecdf8933
FC
60 tex = Texture('screenshot')
61 buffer = base.win.make_texture_buffer('screenshot', 512, 512, tex, True )
62 cam = base.make_camera(buffer)
63 cam.reparent_to(render)
64 cam.node().get_lens().set_fov(base.camLens.get_fov())
65 cam.set_pos(0, -20, 0)
66 cam.look_at(0, 0, 0)
67 import simplepbr
68 simplepbr.init(
69 window=buffer,
70 camera_node=cam,
71 use_normal_maps=True,
72 use_emission_maps=False,
73 use_occlusion_maps=True,
74 msaa_samples=4,
75 enable_shadows=True)
76 base.graphicsEngine.renderFrame()
77 base.graphicsEngine.renderFrame()
63e7aeb2
FC
78 fname = self.__class__.__name__
79 buffer.save_screenshot('assets/images/scenes/%s.png' % fname)
ecdf8933
FC
80 # img = DirectButton(
81 # frameTexture=buffer.get_texture(), relief=FLAT,
82 # frameSize=(-.2, .2, -.2, .2))
0d5a5427 83 return buffer.get_texture()
ecdf8933 84
d5932612
FC
85 def current_bottom(self):
86 curr_bottom = 1
87 for item in self.items:
88 if item.repos_done:
89 curr_bottom = min(curr_bottom, item.get_bottom())
90 return curr_bottom
91
05fd23ec 92 def reset(self):
d5932612 93 [itm.destroy() for itm in self.items]
0eff64a3 94 self._set_items()
0a0994e4 95 self._state = 'init'
32aa4dae
FC
96 self._commands = []
97 self._command_idx = 0
b41381b2
FC
98 if hasattr(self, '_success_txt'):
99 self._success_txt.destroy()
100 del self._success_txt
101 self.__right_btn['state'] = NORMAL
05fd23ec 102
5964572b
FC
103 def destroy(self):
104 self._unset_gui()
105 self._unset_lights()
106 self._unset_input()
107 self._unset_mouse_plane()
108 [itm.destroy() for itm in self.items]
109 self._bg.destroy()
110 self._side_panel.destroy()
407412a5 111 self._cursor.destroy()
5964572b 112 taskMgr.remove(self._scene_tsk)
9fc7f6fb
FC
113 if hasattr(self, '_success_txt'):
114 self._success_txt.destroy()
1be87278
FC
115
116 def _set_camera(self):
117 base.camera.set_pos(0, -20, 0)
118 base.camera.look_at(0, 0, 0)
119
a0acba9a 120 def __load_img_btn(self, path, col):
420ce99a 121 img = OnscreenImage('assets/buttons/%s.dds' % path)
a0acba9a
FC
122 img.set_transparency(True)
123 img.set_color(col)
124 img.detach_node()
125 return img
126
36099535 127 def _set_gui(self):
01b221a6
FC
128 def load_images_btn(path, col):
129 colors = {
130 'gray': [
131 (.6, .6, .6, 1), # ready
132 (1, 1, 1, 1), # press
133 (.8, .8, .8, 1), # rollover
134 (.4, .4, .4, .4)],
135 'green': [
136 (.1, .68, .1, 1),
137 (.1, 1, .1, 1),
138 (.1, .84, .1, 1),
139 (.4, .1, .1, .4)]}[col]
a0acba9a 140 return [self.__load_img_btn(path, col) for col in colors]
36099535
FC
141 abl, abr = base.a2dBottomLeft, base.a2dBottomRight
142 btn_info = [
5964572b 143 ('home', self.on_home, NORMAL, abl, 'gray'),
9830561d 144 ('information', self._set_instructions, NORMAL, abl, 'gray'),
01b221a6
FC
145 ('right', self.on_play, NORMAL, abr, 'green'),
146 ('next', self.on_next, DISABLED, abr, 'gray'),
147 ('previous', self.on_prev, DISABLED, abr, 'gray'),
05fd23ec 148 ('rewind', self.reset, NORMAL, abr, 'gray')]
36099535 149 num_l = num_r = 0
a0acba9a 150 btns = []
36099535 151 for binfo in btn_info:
01b221a6 152 imgs = load_images_btn(binfo[0], binfo[4])
36099535
FC
153 if binfo[3] == base.a2dBottomLeft:
154 sign, num = 1, num_l
155 num_l += 1
156 else:
157 sign, num = -1, num_r
158 num_r += 1
159 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
160 btn = DirectButton(
161 image=imgs, scale=.05, pos=(sign * (.06 + .11 * num), 1, .06),
162 parent=binfo[3], command=binfo[1], state=binfo[2], relief=FLAT,
54a1397e
FC
163 frameColor=fcols[0] if binfo[2] == NORMAL else fcols[1],
164 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
165 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
36099535 166 btn.set_transparency(True)
a0acba9a
FC
167 btns += [btn]
168 self.__home_btn, self.__info_btn, self.__right_btn, self.__next_btn, \
169 self.__prev_btn, self.__rewind_btn = btns
31237524
FC
170 if self._dbg_items:
171 self._info_txt = OnscreenText(
172 '', parent=base.a2dTopRight, scale=0.04,
173 pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right)
36099535 174
5964572b
FC
175 def _unset_gui(self):
176 btns = [
177 self.__home_btn, self.__info_btn, self.__right_btn,
178 self.__next_btn, self.__prev_btn, self.__rewind_btn]
179 [btn.destroy() for btn in btns]
31237524
FC
180 if self._dbg_items:
181 self._info_txt.destroy()
5964572b 182
64eae9c7
FC
183 def _set_spotlight(self, name, pos, look_at, color, shadows=False):
184 light = Spotlight(name)
185 if shadows:
186 light.setLens(PerspectiveLens())
1be87278 187 light_np = render.attach_new_node(light)
64eae9c7
FC
188 light_np.set_pos(pos)
189 light_np.look_at(look_at)
1be87278
FC
190 light.set_color(color)
191 render.set_light(light_np)
5964572b 192 return light_np
1be87278
FC
193
194 def _set_lights(self):
195 alight = AmbientLight('alight') # for ao
64eae9c7
FC
196 alight.set_color((.15, .15, .15, 1))
197 self._alnp = render.attach_new_node(alight)
198 render.set_light(self._alnp)
199 self._key_light = self._set_spotlight(
200 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
201 self._shadow_light = self._set_spotlight(
202 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
203 self._shadow_light.node().set_shadow_caster(True, 2048, 2048)
204 self._shadow_light.node().get_lens().set_film_size(2048, 2048)
205 self._shadow_light.node().get_lens().set_near_far(1, 256)
206 self._shadow_light.node().set_camera_mask(BitMask32(0x01))
5964572b
FC
207
208 def _unset_lights(self):
64eae9c7 209 for light in [self._alnp, self._key_light, self._shadow_light]:
5964572b
FC
210 render.clear_light(light)
211 light.remove_node()
1be87278
FC
212
213 def _set_input(self):
49c79300 214 self.accept('mouse1', self.on_click_l)
c8d8653f 215 self.accept('mouse1-up', self.on_release)
49c79300
FC
216 self.accept('mouse3', self.on_click_r)
217 self.accept('mouse3-up', self.on_release)
c8d8653f 218
5964572b
FC
219 def _unset_input(self):
220 for evt in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
221 self.ignore(evt)
222
c8d8653f 223 def _set_mouse_plane(self):
651713a9 224 shape = BulletPlaneShape((0, -1, 0), 0)
c8d8653f
FC
225 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
226 self._mouse_plane_node = BulletGhostNode('mouse plane')
227 self._mouse_plane_node.addShape(shape)
228 #np = render.attachNewNode(self._mouse_plane_node)
229 #self._world.attachRigidBody(self._mouse_plane_node)
06af3aa9 230 self._world.attach_ghost(self._mouse_plane_node)
1be87278 231
5964572b
FC
232 def _unset_mouse_plane(self):
233 self._world.remove_ghost(self._mouse_plane_node)
234
37ba71d8
FC
235 def _get_hits(self):
236 if not base.mouseWatcherNode.has_mouse(): return []
13263131 237 p_from, p_to = P3dGfxMgr.world_from_to(base.mouseWatcherNode.get_mouse())
37ba71d8
FC
238 return self._world.ray_test_all(p_from, p_to).get_hits()
239
31237524
FC
240 def _update_info(self, item):
241 txt = ''
242 if item:
243 txt = '%.3f %.3f\n%.3f°' % (
244 item._np.get_x(), item._np.get_z(), item._np.get_r())
245 self._info_txt['text'] = txt
246
49c79300 247 def _on_click(self, method):
a0acba9a
FC
248 if self._paused:
249 return
c8d8653f
FC
250 for hit in self._get_hits():
251 if hit.get_node() == self._mouse_plane_node:
252 pos = hit.get_hit_pos()
37ba71d8 253 for hit in self._get_hits():
ef3c36bf 254 for item in [i for i in self.items if hit.get_node() == i.node and i.interactable]:
7c0a81ae
FC
255 if not self._item_active:
256 self._item_active = item
49c79300 257 getattr(item, method)(pos)
79f81d48 258 img = 'move' if method == 'on_click_l' else 'rotate'
a6843832 259 if not (img == 'rotate' and not item._instantiated):
420ce99a 260 self._cursor.set_image('assets/buttons/%s.dds' % img)
49c79300
FC
261
262 def on_click_l(self):
263 self._on_click('on_click_l')
264
265 def on_click_r(self):
266 self._on_click('on_click_r')
c8d8653f
FC
267
268 def on_release(self):
32aa4dae
FC
269 if self._item_active and not self._item_active._first_command:
270 self._commands = self._commands[:self._command_idx]
271 self._commands += [self._item_active]
272 self._command_idx += 1
273 self.__prev_btn['state'] = NORMAL
274 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
275 self.__prev_btn['frameColor'] = fcols[0]
276 if self._item_active._command_idx == len(self._item_active._commands) - 1:
277 self.__next_btn['state'] = DISABLED
278 self.__next_btn['frameColor'] = fcols[1]
7c0a81ae 279 self._item_active = None
49c79300 280 [item.on_release() for item in self.items]
420ce99a 281 self._cursor.set_image('assets/buttons/arrowUpLeft.dds')
37ba71d8 282
e1438449 283 def repos(self):
d5932612
FC
284 for item in self.items:
285 item.repos_done = False
e1438449 286 self.items = sorted(self.items, key=lambda itm: itm.__class__.__name__)
651713a9 287 [item.on_aspect_ratio_changed() for item in self.items]
a5dc83f4 288 self._side_panel.update(self.items)
7790323d 289 max_x = -float('inf')
e1438449
FC
290 for item in self.items:
291 if not item._instantiated:
292 max_x = max(item._np.get_x(), max_x)
293 for item in self.items:
294 if not item._instantiated:
295 item.repos_x(max_x)
296
297 def on_aspect_ratio_changed(self):
298 self.repos()
651713a9 299
0a0994e4 300 def _win_condition(self):
0e86689f
FC
301 pass
302
0a0994e4
FC
303 def _fail_condition(self):
304 return all(itm.fail_condition() for itm in self.items) and not self._paused and self._state == 'playing'
305
37ba71d8 306 def on_frame(self, task):
c8d8653f
FC
307 hits = self._get_hits()
308 pos = None
309 for hit in self._get_hits():
310 if hit.get_node() == self._mouse_plane_node:
311 pos = hit.get_hit_pos()
312 hit_nodes = [hit.get_node() for hit in hits]
7c0a81ae
FC
313 if self._item_active:
314 items_hit = [self._item_active]
315 else:
316 items_hit = [itm for itm in self.items if itm.node in hit_nodes]
37ba71d8
FC
317 items_no_hit = [itm for itm in self.items if itm not in items_hit]
318 [itm.on_mouse_on() for itm in items_hit]
319 [itm.on_mouse_off() for itm in items_no_hit]
7c0a81ae
FC
320 if pos and self._item_active:
321 self._item_active.on_mouse_move(pos)
31237524
FC
322 if self._dbg_items:
323 self._update_info(items_hit[0] if items_hit else None)
0a0994e4
FC
324 if self._win_condition():
325 self._set_win()
326 elif self._state == 'playing' and self._fail_condition():
327 self._set_fail()
93cf86b2
FC
328 if any(itm._overlapping for itm in self.items):
329 self._cursor.cursor_img.img.set_color(.9, .1, .1, 1)
330 else:
331 self._cursor.cursor_img.img.set_color(.9, .9, .9, 1)
37ba71d8 332 return task.cont
c8d8653f 333
7850ccc4
FC
334 def cb_inst(self, item):
335 self.items += [item]
336
c8d8653f 337 def on_play(self):
0a0994e4 338 self._state = 'playing'
b41381b2
FC
339 self.__prev_btn['state'] = DISABLED
340 self.__next_btn['state'] = DISABLED
341 self.__right_btn['state'] = DISABLED
c8d8653f 342 [itm.play() for itm in self.items]
36099535
FC
343
344 def on_next(self):
32aa4dae
FC
345 self._commands[self._command_idx].redo()
346 self._command_idx += 1
347 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
348 self.__prev_btn['state'] = NORMAL
349 self.__prev_btn['frameColor'] = fcols[0]
350 more_commands = self._command_idx < len(self._commands)
351 self.__next_btn['state'] = NORMAL if more_commands else DISABLED
352 self.__next_btn['frameColor'] = fcols[0] if more_commands else fcols[1]
36099535
FC
353
354 def on_prev(self):
32aa4dae
FC
355 self._command_idx -= 1
356 self._commands[self._command_idx].undo()
357 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
358 self.__next_btn['state'] = NORMAL
359 self.__next_btn['frameColor'] = fcols[0]
360 self.__prev_btn['state'] = NORMAL if self._command_idx else DISABLED
361 self.__prev_btn['frameColor'] = fcols[0] if self._command_idx else fcols[1]
36099535 362
36099535 363 def on_home(self):
5964572b 364 self._exit_cb()
36099535 365
2aaa10d3 366 def _set_instructions(self):
9830561d
FC
367 self._paused = True
368 self.__store_state()
2aaa10d3
FC
369 mgr = TextPropertiesManager.get_global_ptr()
370 for name in ['mouse_l', 'mouse_r']:
420ce99a 371 graphic = OnscreenImage('assets/buttons/%s.dds' % name)
2aaa10d3
FC
372 graphic.set_scale(.5)
373 graphic.get_texture().set_minfilter(Texture.FTLinearMipmapLinear)
374 graphic.get_texture().set_anisotropic_degree(2)
375 mgr.set_graphic(name, graphic)
376 graphic.set_z(-.2)
377 graphic.set_transparency(True)
378 graphic.detach_node()
379 frm = DirectFrame(frameColor=(.4, .4, .4, .06),
380 frameSize=(-.6, .6, -.3, .3))
381 font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
382 font.clear()
383 font.set_pixels_per_unit(60)
384 font.set_minfilter(Texture.FTLinearMipmapLinear)
385 font.set_outline((0, 0, 0, 1), .8, .2)
2aaa10d3 386 self._txt = OnscreenText(
0eff64a3
FC
387 self._instr_txt(), parent=frm, font=font, scale=0.06,
388 fg=(.9, .9, .9, 1), align=TextNode.A_left)
2aaa10d3
FC
389 u_l = self._txt.textNode.get_upper_left_3d()
390 l_r = self._txt.textNode.get_lower_right_3d()
391 w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
a0acba9a
FC
392 btn_scale = .05
393 mar = .06 # margin
2aaa10d3 394 z = h / 2 - font.get_line_height() * self._txt['scale'][1]
a0acba9a 395 z += (btn_scale + 2 * mar) / 2
2aaa10d3
FC
396 self._txt['pos'] = -w / 2, z
397 u_l = self._txt.textNode.get_upper_left_3d()
398 l_r = self._txt.textNode.get_lower_right_3d()
a0acba9a
FC
399 c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
400 fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
2aaa10d3 401 frm['frameSize'] = fsz
a0acba9a
FC
402 colors = [
403 (.6, .6, .6, 1), # ready
404 (1, 1, 1, 1), # press
405 (.8, .8, .8, 1), # rollover
406 (.4, .4, .4, .4)]
407 imgs = [self.__load_img_btn('exitRight', col) for col in colors]
408 btn = DirectButton(
409 image=imgs, scale=btn_scale,
410 pos=(l_r[0] - btn_scale, 1, l_r[2] - mar - btn_scale),
411 parent=frm, command=self.__on_close_instructions, extraArgs=[frm],
412 relief=FLAT, frameColor=(.6, .6, .6, .08),
413 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
414 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
415 btn.set_transparency(True)
416
0a0994e4 417 def _set_win(self):
9914cfc9
FC
418 loader.load_sfx('assets/audio/sfx/success.ogg').play()
419 self._paused = True
420 self.__store_state()
421 frm = DirectFrame(frameColor=(.4, .4, .4, .06),
422 frameSize=(-.6, .6, -.3, .3))
423 font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
424 font.clear()
425 font.set_pixels_per_unit(60)
426 font.set_minfilter(Texture.FTLinearMipmapLinear)
427 font.set_outline((0, 0, 0, 1), .8, .2)
428 self._txt = OnscreenText(
429 _('You win!'),
430 parent=frm,
431 font=font, scale=0.2,
432 fg=(.9, .9, .9, 1))
433 u_l = self._txt.textNode.get_upper_left_3d()
434 l_r = self._txt.textNode.get_lower_right_3d()
435 w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
436 btn_scale = .05
437 mar = .06 # margin
438 z = h / 2 - font.get_line_height() * self._txt['scale'][1]
439 z += (btn_scale + 2 * mar) / 2
440 self._txt['pos'] = 0, z
441 u_l = self._txt.textNode.get_upper_left_3d()
442 l_r = self._txt.textNode.get_lower_right_3d()
443 c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
444 fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
445 frm['frameSize'] = fsz
446 colors = [
447 (.6, .6, .6, 1), # ready
448 (1, 1, 1, 1), # press
449 (.8, .8, .8, 1), # rollover
450 (.4, .4, .4, .4)]
451 imgs = [self.__load_img_btn('home', col) for col in colors]
452 btn = DirectButton(
453 image=imgs, scale=btn_scale,
454 pos=(-2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
455 parent=frm, command=self._on_end_home, extraArgs=[frm],
456 relief=FLAT, frameColor=(.6, .6, .6, .08),
457 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
458 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
459 btn.set_transparency(True)
460 imgs = [self.__load_img_btn('rewind', col) for col in colors]
461 btn = DirectButton(
462 image=imgs, scale=btn_scale,
463 pos=(0, 1, l_r[2] - mar - btn_scale),
464 parent=frm, command=self._on_restart, extraArgs=[frm],
465 relief=FLAT, frameColor=(.6, .6, .6, .08),
466 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
467 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
468 btn.set_transparency(True)
469 scenes = []
470 for _file in glob('pmachines/scenes/*.py'):
471 _fn = _file.replace('.py', '').replace('/', '.')
472 for member in import_module(_fn).__dict__.values():
473 if isclass(member) and issubclass(member, Scene) and \
474 member != Scene:
475 scenes += [member]
476 scenes = sorted(scenes, key=lambda elm: elm.sorting)
477 enabled = scenes.index(self.__class__) < len(scenes) - 1
478 if enabled:
479 next_scene = scenes[scenes.index(self.__class__) + 1]
480 else:
481 next_scene = None
482 imgs = [self.__load_img_btn('right', col) for col in colors]
483 btn = DirectButton(
484 image=imgs, scale=btn_scale,
485 pos=(2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
486 parent=frm, command=self._on_next_scene,
487 extraArgs=[frm, next_scene], relief=FLAT,
488 frameColor=(.6, .6, .6, .08),
489 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
490 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
491 btn['state'] = NORMAL if enabled else DISABLED
492 btn.set_transparency(True)
493
0a0994e4
FC
494 def _set_fail(self):
495 loader.load_sfx('assets/audio/sfx/success.ogg').play()
496 self._paused = True
497 self.__store_state()
498 frm = DirectFrame(frameColor=(.4, .4, .4, .06),
499 frameSize=(-.6, .6, -.3, .3))
500 font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
501 font.clear()
502 font.set_pixels_per_unit(60)
503 font.set_minfilter(Texture.FTLinearMipmapLinear)
504 font.set_outline((0, 0, 0, 1), .8, .2)
505 self._txt = OnscreenText(
506 _('You have failed!'),
507 parent=frm,
508 font=font, scale=0.2,
509 fg=(.9, .9, .9, 1))
510 u_l = self._txt.textNode.get_upper_left_3d()
511 l_r = self._txt.textNode.get_lower_right_3d()
512 w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
513 btn_scale = .05
514 mar = .06 # margin
515 z = h / 2 - font.get_line_height() * self._txt['scale'][1]
516 z += (btn_scale + 2 * mar) / 2
517 self._txt['pos'] = 0, z
518 u_l = self._txt.textNode.get_upper_left_3d()
519 l_r = self._txt.textNode.get_lower_right_3d()
520 c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
521 fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
522 frm['frameSize'] = fsz
523 colors = [
524 (.6, .6, .6, 1), # ready
525 (1, 1, 1, 1), # press
526 (.8, .8, .8, 1), # rollover
527 (.4, .4, .4, .4)]
528 imgs = [self.__load_img_btn('home', col) for col in colors]
529 btn = DirectButton(
530 image=imgs, scale=btn_scale,
531 pos=(-2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
532 parent=frm, command=self._on_end_home, extraArgs=[frm],
533 relief=FLAT, frameColor=(.6, .6, .6, .08),
534 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
535 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
536 btn.set_transparency(True)
537 imgs = [self.__load_img_btn('rewind', col) for col in colors]
538 btn = DirectButton(
539 image=imgs, scale=btn_scale,
540 pos=(0, 1, l_r[2] - mar - btn_scale),
541 parent=frm, command=self._on_restart, extraArgs=[frm],
542 relief=FLAT, frameColor=(.6, .6, .6, .08),
543 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
544 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
545 btn.set_transparency(True)
546
9914cfc9
FC
547 def _on_restart(self, frm):
548 self.__on_close_instructions(frm)
549 self.reset()
550
551 def _on_end_home(self, frm):
552 self.__on_close_instructions(frm)
553 self.on_home()
554
555 def _on_next_scene(self, frm, scene):
556 self.__on_close_instructions(frm)
557 self._reload_cb(scene)
558
a0acba9a
FC
559 def __store_state(self):
560 btns = [
561 self.__home_btn, self.__info_btn, self.__right_btn,
562 self.__next_btn, self.__prev_btn, self.__rewind_btn]
563 self.__btn_state = [btn['state'] for btn in btns]
564 for btn in btns:
565 btn['state'] = DISABLED
566 [itm.store_state() for itm in self.items]
567
568 def __restore_state(self):
569 btns = [
570 self.__home_btn, self.__info_btn, self.__right_btn,
571 self.__next_btn, self.__prev_btn, self.__rewind_btn]
572 for btn, state in zip(btns, self.__btn_state):
573 btn['state'] = state
574 [itm.restore_state() for itm in self.items]
575 self._paused = False
576
577 def __on_close_instructions(self, frm):
578 frm.remove_node()
579 self.__restore_state()