ya2 · news · projects · code · about

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