ya2 · news · projects · code · about

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