ya2 · news · projects · code · about

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