1 from os
.path
import exists
2 from os
import makedirs
3 from logging
import info
5 from panda3d
.core
import AmbientLight
, Texture
, TextPropertiesManager
, \
6 TextNode
, Spotlight
, PerspectiveLens
, BitMask32
, NodePath
7 from panda3d
.bullet
import BulletPlaneShape
, BulletGhostNode
8 from direct
.gui
.OnscreenImage
import OnscreenImage
9 from direct
.gui
.OnscreenText
import OnscreenText
10 from direct
.gui
.DirectGui
import DirectButton
, DirectFrame
11 from direct
.gui
.DirectGuiGlobals
import FLAT
, DISABLED
, NORMAL
12 from direct
.showbase
.DirectObject
import DirectObject
13 from direct
.interval
.IntervalGlobal
import Sequence
, Func
14 from direct
.interval
.LerpInterval
import LerpFunctionInterval
15 from pmachines
.items
.background
import Background
16 from pmachines
.gui
.sidepanel
import SidePanel
17 from pmachines
.items
.box
import Box
18 from pmachines
.items
.basketball
import Basketball
19 from pmachines
.items
.domino
import Domino
20 from ya2
.utils
.cursor
import MouseCursor
21 from ya2
.p3d
.gfx
import P3dGfxMgr
22 from ya2
.p3d
.p3d
import LibP3d
25 class Scene(DirectObject
):
30 def __init__(self
, world
, exit_cb
, auto_close_instr
, dbg_items
, reload_cb
, scenes
, pos_mgr
, testing
, mouse_coords
, persistent
):
33 self
._exit
_cb
= exit_cb
34 self
._testing
= testing
35 self
._mouse
_coords
= mouse_coords
36 self
._dbg
_items
= dbg_items
37 self
._reload
_cb
= reload_cb
38 self
._pos
_mgr
= pos_mgr
41 self
._start
_evt
_time
= None
42 self
._enforce
_res
= ''
43 self
.__persistent
= persistent
44 self
.accept('enforce_res', self
.enforce_res
)
46 self
._cursor
= MouseCursor(
47 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
52 self
._set
_mouse
_plane
()
58 self
._item
_active
= None
61 self
.__restore
_state
()
63 self
._set
_instructions
()
64 self
._bg
= Background()
65 self
._side
_panel
= SidePanel(world
, self
._mouse
_plane
_node
, (-5, 4), (-3, 1), 1, self
.items
)
66 self
._scene
_tsk
= taskMgr
.add(self
.on_frame
, 'on_frame')
75 with
open(cls
.filename
) as f
:
76 cls
.json
= loads(f
.read())
77 return cls
.json
['version']
81 return bool([(name
, version
) for name
, version
in cls
.scenes_done
if cls
.__name
__ == name
and cls
.version() == version
])
89 if not self
.__class
__.json
:
90 with
open(self
.__class
__.filename
) as f
:
91 self
.__class
__.json
= loads(f
.read())
92 for item
in self
.__class
__.json
['start_items']:
95 'plane_node': self
._mouse
_plane
_node
,
96 'cb_inst': self
.cb_inst
,
97 'curr_bottom': self
.current_bottom
,
99 'count': item
['count']}
101 args
['mass'] = item
['mass']
102 if 'friction' in item
:
103 args
['friction'] = item
['friction']
104 self
.items
+= [self
.__code
2class
(item
['class'])(**args
)]
106 def __code2class(self
, code
):
109 'Basketball': Basketball
,
113 def screenshot(self
, task
=None):
114 tex
= Texture('screenshot')
115 buffer = base
.win
.make_texture_buffer('screenshot', 512, 512, tex
, True )
116 cam
= base
.make_camera(buffer)
117 cam
.reparent_to(render
)
118 cam
.node().get_lens().set_fov(base
.camLens
.get_fov())
119 cam
.set_pos(0, -20, 0)
125 use_normal_maps
=True,
126 use_emission_maps
=False,
127 use_occlusion_maps
=True,
130 base
.graphicsEngine
.renderFrame()
131 base
.graphicsEngine
.renderFrame()
132 fname
= self
.__class
__.__name
__
133 if not exists('assets/images/scenes'):
134 makedirs('assets/images/scenes')
135 buffer.save_screenshot('assets/images/scenes/%s.png' % fname
)
136 # img = DirectButton(
137 # frameTexture=buffer.get_texture(), relief=FLAT,
138 # frameSize=(-.2, .2, -.2, .2))
139 return buffer.get_texture()
141 def current_bottom(self
):
143 for item
in self
.items
:
145 curr_bottom
= min(curr_bottom
, item
.get_bottom())
149 [itm
.destroy() for itm
in self
.items
]
150 [itm
.remove_node() for itm
in self
._test
_items
]
152 self
._test
_items
= []
154 self
._set
_test
_items
()
157 self
._command
_idx
= 0
158 self
._start
_evt
_time
= None
159 if hasattr(self
, '_success_txt'):
160 self
._success
_txt
.destroy()
161 del self
._success
_txt
162 self
.__right
_btn
['state'] = NORMAL
164 def enforce_res(self
, val
):
165 self
._enforce
_res
= val
166 info('enforce res: ' + val
)
169 self
.__intro
_sequence
.finish()
170 self
.ignore('enforce_res')
174 self
._unset
_mouse
_plane
()
175 [itm
.destroy() for itm
in self
.items
]
176 [itm
.remove_node() for itm
in self
._test
_items
]
178 self
._side
_panel
.destroy()
179 self
._cursor
.destroy()
180 taskMgr
.remove(self
._scene
_tsk
)
181 if hasattr(self
, '_success_txt'):
182 self
._success
_txt
.destroy()
184 def _set_camera(self
):
185 base
.camera
.set_pos(0, -20, 0)
186 base
.camera
.look_at(0, 0, 0)
191 start_v
[0] + (end_v
[0] - start_v
[0]) * t
,
192 start_v
[1] + (end_v
[1] - start_v
[1]) * t
,
193 start_v
[2] + (end_v
[2] - start_v
[2]) * t
)
194 base
.camera
.set_pos(*curr_pos
)
196 camera_interval
= LerpFunctionInterval(
201 blendType
='easeInOut')
202 self
.__intro
_sequence
= Sequence(
205 self
.__intro
_sequence
.start()
207 def __load_img_btn(self
, path
, col
):
208 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
209 img
.set_transparency(True)
215 def load_images_btn(path
, col
):
218 (.6, .6, .6, 1), # ready
219 (1, 1, 1, 1), # press
220 (.8, .8, .8, 1), # rollover
226 (.4, .1, .1, .4)]}[col
]
227 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
228 abl
, abr
= base
.a2dBottomLeft
, base
.a2dBottomRight
230 ('home', self
.on_home
, NORMAL
, abl
, 'gray'),
231 ('information', self
._set
_instructions
, NORMAL
, abl
, 'gray'),
232 ('right', self
.on_play
, NORMAL
, abr
, 'green'),
233 #('next', self.on_next, DISABLED, abr, 'gray'),
234 #('previous', self.on_prev, DISABLED, abr, 'gray'),
235 #('rewind', self.reset, NORMAL, abr, 'gray')
239 for binfo
in btn_info
:
240 imgs
= load_images_btn(binfo
[0], binfo
[4])
241 if binfo
[3] == base
.a2dBottomLeft
:
245 sign
, num
= -1, num_r
247 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
249 image
=imgs
, scale
=.05, pos
=(sign
* (.06 + .11 * num
), 1, .06),
250 parent
=binfo
[3], command
=binfo
[1], state
=binfo
[2], relief
=FLAT
,
251 frameColor
=fcols
[0] if binfo
[2] == NORMAL
else fcols
[1],
252 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
253 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
254 btn
.set_transparency(True)
255 self
._pos
_mgr
.register(binfo
[0], LibP3d
.wdg_pos(btn
))
257 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
= btns
258 # , self.__next_btn, self.__prev_btn, self.__rewind_btn
260 self
._info
_txt
= OnscreenText(
261 '', parent
=base
.a2dTopRight
, scale
=0.04,
262 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
263 if self
._mouse
_coords
:
264 self
._coords
_txt
= OnscreenText(
265 '', parent
=base
.a2dTopRight
, scale
=0.04,
266 pos
=(-.03, -.12), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
267 def update_coords(task
):
269 for hit
in self
._get
_hits
():
270 if hit
.get_node() == self
._mouse
_plane
_node
:
271 pos
= hit
.get_hit_pos()
273 txt
= '%s %s' % (round(pos
.x
, 3),
275 self
._coords
_txt
['text'] = txt
277 self
._coords
_tsk
= taskMgr
.add(update_coords
, 'update_coords')
279 def _unset_gui(self
):
281 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
282 #self.__next_btn, self.__prev_btn, self.__rewind_btn
284 [btn
.destroy() for btn
in btns
]
286 self
._info
_txt
.destroy()
287 if self
._mouse
_coords
:
288 taskMgr
.remove(self
._coords
_tsk
)
289 self
._coords
_txt
.destroy()
291 def _set_spotlight(self
, name
, pos
, look_at
, color
, shadows
=False):
292 light
= Spotlight(name
)
294 light
.setLens(PerspectiveLens())
295 light_np
= render
.attach_new_node(light
)
296 light_np
.set_pos(pos
)
297 light_np
.look_at(look_at
)
298 light
.set_color(color
)
299 render
.set_light(light_np
)
302 def _set_lights(self
):
303 alight
= AmbientLight('alight') # for ao
304 alight
.set_color((.15, .15, .15, 1))
305 self
._alnp
= render
.attach_new_node(alight
)
306 render
.set_light(self
._alnp
)
307 self
._key
_light
= self
._set
_spotlight
(
308 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
309 self
._shadow
_light
= self
._set
_spotlight
(
310 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
311 self
._shadow
_light
.node().set_shadow_caster(True, 2048, 2048)
312 self
._shadow
_light
.node().get_lens().set_film_size(2048, 2048)
313 self
._shadow
_light
.node().get_lens().set_near_far(1, 256)
314 self
._shadow
_light
.node().set_camera_mask(BitMask32(0x01))
316 def _unset_lights(self
):
317 for light
in [self
._alnp
, self
._key
_light
, self
._shadow
_light
]:
318 render
.clear_light(light
)
321 def _set_input(self
):
322 self
.accept('mouse1', self
.on_click_l
)
323 self
.accept('mouse1-up', self
.on_release
)
324 self
.accept('mouse3', self
.on_click_r
)
325 self
.accept('mouse3-up', self
.on_release
)
327 def _unset_input(self
):
328 for evt
in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
331 def _set_mouse_plane(self
):
332 shape
= BulletPlaneShape((0, -1, 0), 0)
333 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
334 self
._mouse
_plane
_node
= BulletGhostNode('mouse plane')
335 self
._mouse
_plane
_node
.addShape(shape
)
336 #np = render.attachNewNode(self._mouse_plane_node)
337 #self._world.attachRigidBody(self._mouse_plane_node)
338 self
._world
.attach_ghost(self
._mouse
_plane
_node
)
340 def _unset_mouse_plane(self
):
341 self
._world
.remove_ghost(self
._mouse
_plane
_node
)
344 if not base
.mouseWatcherNode
.has_mouse(): return []
345 p_from
, p_to
= P3dGfxMgr
.world_from_to(base
.mouseWatcherNode
.get_mouse())
346 return self
._world
.ray_test_all(p_from
, p_to
).get_hits()
348 def _update_info(self
, item
):
351 txt
= '%.3f %.3f\n%.3f°' % (
352 item
._np
.get_x(), item
._np
.get_z(), item
._np
.get_r())
353 self
._info
_txt
['text'] = txt
355 def _on_click(self
, method
):
358 for hit
in self
._get
_hits
():
359 if hit
.get_node() == self
._mouse
_plane
_node
:
360 pos
= hit
.get_hit_pos()
361 for hit
in self
._get
_hits
():
362 for item
in [i
for i
in self
.items
if hit
.get_node() == i
.node
and i
.interactable
]:
363 if not self
._item
_active
:
364 self
._item
_active
= item
365 getattr(item
, method
)(pos
)
366 img
= 'move' if method
== 'on_click_l' else 'rotate'
367 if not (img
== 'rotate' and not item
._instantiated
):
368 self
._cursor
.set_image('assets/images/buttons/%s.dds' % img
)
370 def on_click_l(self
):
371 self
._on
_click
('on_click_l')
373 def on_click_r(self
):
374 self
._on
_click
('on_click_r')
376 def on_release(self
):
377 if self
._item
_active
and not self
._item
_active
._first
_command
:
378 self
._commands
= self
._commands
[:self
._command
_idx
]
379 self
._commands
+= [self
._item
_active
]
380 self
._command
_idx
+= 1
381 #self.__prev_btn['state'] = NORMAL
382 #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
383 #self.__prev_btn['frameColor'] = fcols[0]
384 #if self._item_active._command_idx == len(self._item_active._commands) - 1:
385 # self.__next_btn['state'] = DISABLED
386 # self.__next_btn['frameColor'] = fcols[1]
387 self
._item
_active
= None
388 [item
.on_release() for item
in self
.items
]
389 self
._cursor
.set_image('assets/images/buttons/arrowUpLeft.dds')
392 for item
in self
.items
:
393 item
.repos_done
= False
394 self
.items
= sorted(self
.items
, key
=lambda itm
: itm
.__class
__.__name
__)
395 [item
.on_aspect_ratio_changed() for item
in self
.items
]
396 self
._side
_panel
.update(self
.items
)
397 max_x
= -float('inf')
398 for item
in self
.items
:
399 if not item
._instantiated
:
400 max_x
= max(item
._np
.get_x(), max_x
)
401 for item
in self
.items
:
402 if not item
._instantiated
:
405 def on_aspect_ratio_changed(self
):
408 def _win_condition(self
):
411 def _fail_condition(self
):
412 return all(itm
.fail_condition() for itm
in self
.items
) and not self
._paused
and self
._state
== 'playing'
414 def on_frame(self
, task
):
415 hits
= self
._get
_hits
()
417 for hit
in self
._get
_hits
():
418 if hit
.get_node() == self
._mouse
_plane
_node
:
419 pos
= hit
.get_hit_pos()
420 hit_nodes
= [hit
.get_node() for hit
in hits
]
421 if self
._item
_active
:
422 items_hit
= [self
._item
_active
]
424 items_hit
= [itm
for itm
in self
.items
if itm
.node
in hit_nodes
]
425 items_no_hit
= [itm
for itm
in self
.items
if itm
not in items_hit
]
426 [itm
.on_mouse_on() for itm
in items_hit
]
427 [itm
.on_mouse_off() for itm
in items_no_hit
]
428 if pos
and self
._item
_active
:
429 self
._item
_active
.on_mouse_move(pos
)
431 self
._update
_info
(items_hit
[0] if items_hit
else None)
432 if self
._win
_condition
():
433 self
._start
_evt
_time
= None
434 self
._set
_fail
() if self
._enforce
_res
== 'fail' else self
._set
_win
()
435 elif self
._state
== 'playing' and self
._fail
_condition
():
436 self
._start
_evt
_time
= None
437 self
._set
_win
() if self
._enforce
_res
== 'win' else self
._set
_fail
()
438 elif self
._testing
and self
._start
_evt
_time
and globalClock
.getFrameTime() - self
._start
_evt
_time
> 5.0:
439 self
._start
_evt
_time
= None
440 self
._set
_win
() if self
._enforce
_res
== 'win' else self
._set
_fail
()
441 if any(itm
._overlapping
for itm
in self
.items
):
442 self
._cursor
.cursor_img
.img
.set_color(.9, .1, .1, 1)
444 self
._cursor
.cursor_img
.img
.set_color(.9, .9, .9, 1)
447 def cb_inst(self
, item
):
451 self
._state
= 'playing'
452 #self.__prev_btn['state'] = DISABLED
453 #self.__next_btn['state'] = DISABLED
454 self
.__right
_btn
['state'] = DISABLED
455 [itm
.play() for itm
in self
.items
]
456 self
._start
_evt
_time
= globalClock
.getFrameTime()
459 self
._commands
[self
._command
_idx
].redo()
460 self
._command
_idx
+= 1
461 #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
462 #self.__prev_btn['state'] = NORMAL
463 #self.__prev_btn['frameColor'] = fcols[0]
464 #more_commands = self._command_idx < len(self._commands)
465 #self.__next_btn['state'] = NORMAL if more_commands else DISABLED
466 #self.__next_btn['frameColor'] = fcols[0] if more_commands else fcols[1]
469 self
._command
_idx
-= 1
470 self
._commands
[self
._command
_idx
].undo()
471 #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
472 #self.__next_btn['state'] = NORMAL
473 #self.__next_btn['frameColor'] = fcols[0]
474 #self.__prev_btn['state'] = NORMAL if self._command_idx else DISABLED
475 #self.__prev_btn['frameColor'] = fcols[0] if self._command_idx else fcols[1]
480 def _set_instructions(self
):
483 mgr
= TextPropertiesManager
.get_global_ptr()
484 for name
in ['mouse_l', 'mouse_r']:
485 graphic
= OnscreenImage('assets/images/buttons/%s.dds' % name
)
486 graphic
.set_scale(.5)
487 graphic
.get_texture().set_minfilter(Texture
.FTLinearMipmapLinear
)
488 graphic
.get_texture().set_anisotropic_degree(2)
489 mgr
.set_graphic(name
, graphic
)
491 graphic
.set_transparency(True)
492 graphic
.detach_node()
493 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
494 frameSize
=(-.6, .6, -.3, .3))
495 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
497 font
.set_pixels_per_unit(60)
498 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
499 font
.set_outline((0, 0, 0, 1), .8, .2)
500 self
._txt
= OnscreenText(
501 self
._instr
_txt
(), parent
=frm
, font
=font
, scale
=0.06,
502 fg
=(.9, .9, .9, 1), align
=TextNode
.A_left
)
503 u_l
= self
._txt
.textNode
.get_upper_left_3d()
504 l_r
= self
._txt
.textNode
.get_lower_right_3d()
505 w
, h
= l_r
[0] - u_l
[0], u_l
[2] - l_r
[2]
508 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
509 z
+= (btn_scale
+ 2 * mar
) / 2
510 self
._txt
['pos'] = -w
/ 2, z
511 u_l
= self
._txt
.textNode
.get_upper_left_3d()
512 l_r
= self
._txt
.textNode
.get_lower_right_3d()
513 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
514 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
515 frm
['frameSize'] = fsz
517 (.6, .6, .6, 1), # ready
518 (1, 1, 1, 1), # press
519 (.8, .8, .8, 1), # rollover
521 imgs
= [self
.__load
_img
_btn
('exitRight', col
) for col
in colors
]
523 image
=imgs
, scale
=btn_scale
,
524 pos
=(l_r
[0] - btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
525 parent
=frm
, command
=self
.__on
_close
_instructions
, extraArgs
=[frm
],
526 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
527 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
528 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
529 btn
.set_transparency(True)
530 self
._pos
_mgr
.register('close_instructions', LibP3d
.wdg_pos(btn
))
533 self
.__persistent
.save_scene(self
.__class
__.__name
__, self
.version())
534 loader
.load_sfx('assets/audio/sfx/success.ogg').play()
537 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
538 frameSize
=(-.6, .6, -.3, .3))
539 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
541 font
.set_pixels_per_unit(60)
542 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
543 font
.set_outline((0, 0, 0, 1), .8, .2)
544 self
._txt
= OnscreenText(
547 font
=font
, scale
=0.2,
549 u_l
= self
._txt
.textNode
.get_upper_left_3d()
550 l_r
= self
._txt
.textNode
.get_lower_right_3d()
551 #w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
555 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
556 z
+= (btn_scale
+ 2 * mar
) / 2
557 self
._txt
['pos'] = 0, z
558 u_l
= self
._txt
.textNode
.get_upper_left_3d()
559 l_r
= self
._txt
.textNode
.get_lower_right_3d()
560 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
561 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
562 frm
['frameSize'] = fsz
564 (.6, .6, .6, 1), # ready
565 (1, 1, 1, 1), # press
566 (.8, .8, .8, 1), # rollover
568 imgs
= [self
.__load
_img
_btn
('home', col
) for col
in colors
]
570 image
=imgs
, scale
=btn_scale
,
571 pos
=(-2.8 * btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
572 parent
=frm
, command
=self
._on
_end
_home
, extraArgs
=[frm
],
573 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
574 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
575 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
576 btn
.set_transparency(True)
577 self
._pos
_mgr
.register('home_win', LibP3d
.wdg_pos(btn
))
578 imgs
= [self
.__load
_img
_btn
('rewind', col
) for col
in colors
]
580 image
=imgs
, scale
=btn_scale
,
581 pos
=(0, 1, l_r
[2] - mar
- btn_scale
),
582 parent
=frm
, command
=self
._on
_restart
, extraArgs
=[frm
],
583 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
584 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
585 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
586 self
._pos
_mgr
.register('replay', LibP3d
.wdg_pos(btn
))
587 btn
.set_transparency(True)
588 enabled
= self
._scenes
.index(self
.__class
__) < len(self
._scenes
) - 1
590 next_scene
= self
._scenes
[self
._scenes
.index(self
.__class
__) + 1]
593 imgs
= [self
.__load
_img
_btn
('right', col
) for col
in colors
]
595 image
=imgs
, scale
=btn_scale
,
596 pos
=(2.8 * btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
597 parent
=frm
, command
=self
._on
_next
_scene
,
598 extraArgs
=[frm
, next_scene
], relief
=FLAT
,
599 frameColor
=(.6, .6, .6, .08),
600 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
601 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
602 btn
['state'] = NORMAL
if enabled
else DISABLED
603 self
._pos
_mgr
.register('next', LibP3d
.wdg_pos(btn
))
604 btn
.set_transparency(True)
607 loader
.load_sfx('assets/audio/sfx/success.ogg').play()
610 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
611 frameSize
=(-.6, .6, -.3, .3))
612 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
614 font
.set_pixels_per_unit(60)
615 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
616 font
.set_outline((0, 0, 0, 1), .8, .2)
617 self
._txt
= OnscreenText(
618 _('You have failed!'),
620 font
=font
, scale
=0.2,
622 u_l
= self
._txt
.textNode
.get_upper_left_3d()
623 l_r
= self
._txt
.textNode
.get_lower_right_3d()
624 #w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
628 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
629 z
+= (btn_scale
+ 2 * mar
) / 2
630 self
._txt
['pos'] = 0, z
631 u_l
= self
._txt
.textNode
.get_upper_left_3d()
632 l_r
= self
._txt
.textNode
.get_lower_right_3d()
633 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
634 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
635 frm
['frameSize'] = fsz
637 (.6, .6, .6, 1), # ready
638 (1, 1, 1, 1), # press
639 (.8, .8, .8, 1), # rollover
641 imgs
= [self
.__load
_img
_btn
('home', col
) for col
in colors
]
643 image
=imgs
, scale
=btn_scale
,
644 pos
=(-2.8 * btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
645 parent
=frm
, command
=self
._on
_end
_home
, extraArgs
=[frm
],
646 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
647 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
648 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
649 self
._pos
_mgr
.register('home_win', LibP3d
.wdg_pos(btn
))
650 btn
.set_transparency(True)
651 imgs
= [self
.__load
_img
_btn
('rewind', col
) for col
in colors
]
653 image
=imgs
, scale
=btn_scale
,
654 pos
=(0, 1, l_r
[2] - mar
- btn_scale
),
655 parent
=frm
, command
=self
._on
_restart
, extraArgs
=[frm
],
656 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
657 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
658 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
659 self
._pos
_mgr
.register('replay', LibP3d
.wdg_pos(btn
))
660 btn
.set_transparency(True)
662 def _on_restart(self
, frm
):
663 self
.__on
_close
_instructions
(frm
)
666 def _on_end_home(self
, frm
):
667 self
.__on
_close
_instructions
(frm
)
670 def _on_next_scene(self
, frm
, scene
):
671 self
.__on
_close
_instructions
(frm
)
672 self
._reload
_cb
(scene
)
674 def __store_state(self
):
676 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
677 #self.__next_btn, self.__prev_btn, self.__rewind_btn
679 self
.__btn
_state
= [btn
['state'] for btn
in btns
]
681 btn
['state'] = DISABLED
682 [itm
.store_state() for itm
in self
.items
]
684 def __restore_state(self
):
686 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
687 #self.__next_btn, self.__prev_btn, self.__rewind_btn
689 for btn
, state
in zip(btns
, self
.__btn
_state
):
691 [itm
.restore_state() for itm
in self
.items
]
694 def __on_close_instructions(self
, frm
):
696 self
.__restore
_state
()
698 def _set_test_items(self
):
699 def frame_after(task
):
700 self
._define
_test
_items
()
701 for itm
in self
._test
_items
:
702 self
._pos
_mgr
.register(itm
.name
, P3dGfxMgr
.pos2d_p2d(itm
))
703 taskMgr
.doMethodLater(.01, frame_after
, 'frame after')
705 def _define_test_items(self
):
706 if not self
.__class
__.json
:
707 with
open(self
.__class
__.filename
) as f
:
708 self
.__class
__.json
= loads(f
.read())
709 for item
in self
.__class
__.json
['test_items']['pixel_space']:
710 self
._pos
_mgr
.register(item
['id'], tuple(item
['position']))
711 for item
in self
.__class
__.json
['test_items']['world_space']:
712 self
._set
_test
_item
(item
['id'], tuple(item
['position']))
714 def _set_test_item(self
, name
, pos
):
715 self
._test
_items
+= [NodePath(name
)]
716 self
._test
_items
[-1].set_pos(pos
[0], 0, pos
[1])