ya2 · news · projects · code · about

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