ya2 · news · projects · code · about

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