035ea0e80b27014676a4ca62e1ac0bf61a6d0750
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
, HitStrategy
18 from pmachines
.items
.basketball
import Basketball
19 from pmachines
.items
.domino
import Domino
, DownStrategy
20 from pmachines
.items
.shelf
import Shelf
21 from pmachines
.items
.teetertooter
import TeeterTooter
22 from pmachines
.editor
.scene
import SceneEditor
23 from ya2
.utils
.cursor
import MouseCursor
24 from ya2
.p3d
.gfx
import P3dGfxMgr
25 from ya2
.p3d
.p3d
import LibP3d
28 class Scene(DirectObject
):
33 def __init__(self
, world
, exit_cb
, auto_close_instr
, dbg_items
, reload_cb
, scenes
, pos_mgr
, testing
, mouse_coords
, persistent
, json_name
, editor
, auto_start_editor
):
36 self
._exit
_cb
= exit_cb
37 self
._testing
= testing
38 self
._mouse
_coords
= mouse_coords
39 self
._dbg
_items
= dbg_items
40 self
._reload
_cb
= reload_cb
41 self
._pos
_mgr
= pos_mgr
44 self
._start
_evt
_time
= None
45 self
._enforce
_res
= ''
46 self
.__persistent
= persistent
47 self
.__json
_name
= json_name
48 self
.__editor
= editor
49 self
.__scene
_editor
= None
51 self
.accept('enforce_res', self
.enforce_res
)
53 self
._cursor
= MouseCursor(
54 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
59 self
._set
_mouse
_plane
()
65 self
._item
_active
= None
68 self
.__restore
_state
()
70 self
._set
_instructions
()
71 self
._bg
= Background()
72 self
._side
_panel
= SidePanel(world
, self
._mouse
_plane
_node
, (-5, 4), (-3, 1), 1, self
.items
)
73 self
._scene
_tsk
= taskMgr
.add(self
.on_frame
, 'on_frame')
78 def filename(cls
, scene_name
):
79 return f
'assets/scenes/{scene_name}.json'
82 def name(cls
, scene_name
):
83 if not scene_name
in cls
.json_files
:
84 with
open(cls
.filename(scene_name
)) as f
:
85 cls
.json_files
[scene_name
] = loads(f
.read())
86 return _(cls
.json_files
[scene_name
]['name'])
89 def version(cls
, scene_name
):
90 if not scene_name
in cls
.json_files
:
91 with
open(cls
.filename(scene_name
)) as f
:
92 cls
.json_files
[scene_name
] = loads(f
.read())
93 return cls
.json_files
[scene_name
]['version']
96 def is_done(cls
, scene_name
):
97 if not cls
.scenes_done
or len(cls
.scenes_done
) == 1 and not cls
.scenes_done
[0]:
99 return bool([(name
, version
) for name
, version
in cls
.scenes_done
if scene_name
== name
and cls
.version(scene_name
) == version
])
101 def _instr_txt(self
):
102 txt
= _('Scene: ') + self
.name(self
.__json
_name
) + '\n\n'
103 txt
+= _(self
.__process
_json
_escape
(self
.__class
__.json_files
[self
.__json
_name
]['instructions']))
106 def __process_json_escape(self
, string
):
107 return bytes(string
, 'utf-8').decode('unicode-escape')
109 def _set_items(self
):
111 self
._test
_items
= []
113 with
open(f
'assets/scenes/{self.__json_name}.json') as f
:
114 self
.json
= loads(f
.read())
115 for item
in self
.json
['start_items']:
117 'world': self
._world
,
118 'plane_node': self
._mouse
_plane
_node
,
119 'cb_inst': self
.cb_inst
,
120 'curr_bottom': self
.current_bottom
,
122 'count': item
['count'],
125 args
['mass'] = item
['mass']
126 if 'friction' in item
:
127 args
['friction'] = item
['friction']
128 self
.items
+= [self
.__code
2class
(item
['class'])(**args
)]
129 for item
in self
.json
['fixed_items']:
131 'world': self
._world
,
132 'plane_node': self
._mouse
_plane
_node
,
133 'cb_inst': self
.cb_inst
,
134 'curr_bottom': self
.current_bottom
,
138 args
['pos'] = tuple(item
['position'])
140 args
['r'] = item
['roll']
141 if 'restitution' in item
:
142 args
['restitution'] = item
['restitution']
143 if 'friction' in item
:
144 args
['friction'] = item
['friction']
145 self
.items
+= [self
.__code
2class
(item
['class'])(**args
)]
146 for item
in self
.json
['scene_items']:
148 'world': self
._world
,
149 'plane_node': self
._mouse
_plane
_node
,
150 'cb_inst': self
.cb_inst
,
151 'curr_bottom': self
.current_bottom
,
154 args
['pos'] = tuple(item
['position'])
156 args
['mass'] = item
['mass']
157 if 'friction' in item
:
158 args
['friction'] = item
['friction']
160 args
['r'] = item
['roll']
161 if 'model_scale' in item
:
162 args
['model_scale'] = item
['model_scale']
163 self
.items
+= [self
.__code
2class
(item
['class'])(**args
)]
164 if 'strategy' in item
:
165 match item
['strategy']:
167 self
.items
[-1].set_strategy(self
.__code
2class
(item
['strategy'])(self
.items
[-1]._np
, *item
['strategy_args']))
169 self
.items
[-1].set_strategy(self
.__code
2class
(item
['strategy'])(self
.__item
_with
_id
(item
['strategy_args'][0]), self
.items
[-1].node
, self
.items
[-1]._world
))
171 def __code2class(self
, code
):
174 'Basketball': Basketball
,
177 'TeeterTooter': TeeterTooter
,
178 'DownStrategy': DownStrategy
,
179 'HitStrategy': HitStrategy
182 def __item_with_id(self
, id):
183 for item
in self
.items
:
184 if 'id' in item
.json
and item
.json
['id'] == id:
187 def screenshot(self
, task
=None):
188 tex
= Texture('screenshot')
189 buffer = base
.win
.make_texture_buffer('screenshot', 512, 512, tex
, True )
190 cam
= base
.make_camera(buffer)
191 cam
.reparent_to(render
)
192 cam
.node().get_lens().set_fov(base
.camLens
.get_fov())
193 cam
.set_pos(0, -20, 0)
199 use_normal_maps
=True,
200 use_emission_maps
=False,
201 use_occlusion_maps
=True,
204 base
.graphicsEngine
.renderFrame()
205 base
.graphicsEngine
.renderFrame()
206 fname
= self
.__json
_name
207 if not exists('assets/images/scenes'):
208 makedirs('assets/images/scenes')
209 buffer.save_screenshot('assets/images/scenes/%s.png' % fname
)
210 # img = DirectButton(
211 # frameTexture=buffer.get_texture(), relief=FLAT,
212 # frameSize=(-.2, .2, -.2, .2))
213 return buffer.get_texture()
215 def current_bottom(self
):
217 for item
in self
.items
:
219 curr_bottom
= min(curr_bottom
, item
.get_bottom())
223 [itm
.destroy() for itm
in self
.items
]
224 [itm
.remove_node() for itm
in self
._test
_items
]
226 self
._test
_items
= []
228 self
._set
_test
_items
()
231 self
._command
_idx
= 0
232 self
._start
_evt
_time
= None
233 if hasattr(self
, '_success_txt'):
234 self
._success
_txt
.destroy()
235 del self
._success
_txt
236 self
.__right
_btn
['state'] = NORMAL
238 def enforce_res(self
, val
):
239 self
._enforce
_res
= val
240 info('enforce res: ' + val
)
243 self
.__intro
_sequence
.finish()
244 self
.ignore('enforce_res')
248 self
._unset
_mouse
_plane
()
249 [itm
.destroy() for itm
in self
.items
]
250 [itm
.remove_node() for itm
in self
._test
_items
]
252 self
._side
_panel
.destroy()
253 self
._cursor
.destroy()
254 taskMgr
.remove(self
._scene
_tsk
)
255 if hasattr(self
, '_success_txt'):
256 self
._success
_txt
.destroy()
258 def _set_camera(self
):
259 base
.camera
.set_pos(0, -20, 0)
260 base
.camera
.look_at(0, 0, 0)
265 start_v
[0] + (end_v
[0] - start_v
[0]) * t
,
266 start_v
[1] + (end_v
[1] - start_v
[1]) * t
,
267 start_v
[2] + (end_v
[2] - start_v
[2]) * t
)
268 base
.camera
.set_pos(*curr_pos
)
270 camera_interval
= LerpFunctionInterval(
275 blendType
='easeInOut')
276 self
.__intro
_sequence
= Sequence(
279 self
.__intro
_sequence
.start()
281 def __load_img_btn(self
, path
, col
):
282 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
283 img
.set_transparency(True)
289 def load_images_btn(path
, col
):
292 (.6, .6, .6, 1), # ready
293 (1, 1, 1, 1), # press
294 (.8, .8, .8, 1), # rollover
300 (.4, .1, .1, .4)]}[col
]
301 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
302 abl
, abr
= base
.a2dBottomLeft
, base
.a2dBottomRight
304 ('home', self
.on_home
, NORMAL
, abl
, 'gray'),
305 ('information', self
._set
_instructions
, NORMAL
, abl
, 'gray'),
306 ('right', self
.on_play
, NORMAL
, abr
, 'green'),
307 #('next', self.on_next, DISABLED, abr, 'gray'),
308 #('previous', self.on_prev, DISABLED, abr, 'gray'),
309 #('rewind', self.reset, NORMAL, abr, 'gray')
312 btn_info
.insert(2, ('wrench', self
._set
_editor
, NORMAL
, abl
, 'gray'))
315 for binfo
in btn_info
:
316 imgs
= load_images_btn(binfo
[0], binfo
[4])
317 if binfo
[3] == base
.a2dBottomLeft
:
321 sign
, num
= -1, num_r
323 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
325 image
=imgs
, scale
=.05, pos
=(sign
* (.06 + .11 * num
), 1, .06),
326 parent
=binfo
[3], command
=binfo
[1], state
=binfo
[2], relief
=FLAT
,
327 frameColor
=fcols
[0] if binfo
[2] == NORMAL
else fcols
[1],
328 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
329 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
330 btn
.set_transparency(True)
331 self
._pos
_mgr
.register(binfo
[0], LibP3d
.wdg_pos(btn
))
334 self
.__home
_btn
, self
.__info
_btn
, self
.__editor
_btn
, self
.__right
_btn
= btns
336 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
= btns
337 # , self.__next_btn, self.__prev_btn, self.__rewind_btn
339 self
._info
_txt
= OnscreenText(
340 '', parent
=base
.a2dTopRight
, scale
=0.04,
341 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
342 if self
._mouse
_coords
:
343 self
._coords
_txt
= OnscreenText(
344 '', parent
=base
.a2dTopRight
, scale
=0.04,
345 pos
=(-.03, -.12), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
346 def update_coords(task
):
348 for hit
in self
._get
_hits
():
349 if hit
.get_node() == self
._mouse
_plane
_node
:
350 pos
= hit
.get_hit_pos()
352 txt
= '%s %s' % (round(pos
.x
, 3),
354 self
._coords
_txt
['text'] = txt
356 self
._coords
_tsk
= taskMgr
.add(update_coords
, 'update_coords')
358 def _unset_gui(self
):
360 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
361 #self.__next_btn, self.__prev_btn, self.__rewind_btn
363 [btn
.destroy() for btn
in btns
]
365 self
._info
_txt
.destroy()
366 if self
._mouse
_coords
:
367 taskMgr
.remove(self
._coords
_tsk
)
368 self
._coords
_txt
.destroy()
370 def _set_spotlight(self
, name
, pos
, look_at
, color
, shadows
=False):
371 light
= Spotlight(name
)
373 light
.setLens(PerspectiveLens())
374 light_np
= render
.attach_new_node(light
)
375 light_np
.set_pos(pos
)
376 light_np
.look_at(look_at
)
377 light
.set_color(color
)
378 render
.set_light(light_np
)
381 def _set_lights(self
):
382 alight
= AmbientLight('alight') # for ao
383 alight
.set_color((.15, .15, .15, 1))
384 self
._alnp
= render
.attach_new_node(alight
)
385 render
.set_light(self
._alnp
)
386 self
._key
_light
= self
._set
_spotlight
(
387 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
388 self
._shadow
_light
= self
._set
_spotlight
(
389 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
390 self
._shadow
_light
.node().set_shadow_caster(True, 2048, 2048)
391 self
._shadow
_light
.node().get_lens().set_film_size(2048, 2048)
392 self
._shadow
_light
.node().get_lens().set_near_far(1, 256)
393 self
._shadow
_light
.node().set_camera_mask(BitMask32(0x01))
395 def _unset_lights(self
):
396 for light
in [self
._alnp
, self
._key
_light
, self
._shadow
_light
]:
397 render
.clear_light(light
)
400 def _set_input(self
):
401 self
.accept('mouse1', self
.on_click_l
)
402 self
.accept('mouse1-up', self
.on_release
)
403 self
.accept('mouse3', self
.on_click_r
)
404 self
.accept('mouse3-up', self
.on_release
)
406 def _unset_input(self
):
407 for evt
in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
410 def _set_mouse_plane(self
):
411 shape
= BulletPlaneShape((0, -1, 0), 0)
412 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
413 self
._mouse
_plane
_node
= BulletGhostNode('mouse plane')
414 self
._mouse
_plane
_node
.addShape(shape
)
415 #np = render.attachNewNode(self._mouse_plane_node)
416 #self._world.attachRigidBody(self._mouse_plane_node)
417 self
._world
.attach_ghost(self
._mouse
_plane
_node
)
419 def _unset_mouse_plane(self
):
420 self
._world
.remove_ghost(self
._mouse
_plane
_node
)
423 if not base
.mouseWatcherNode
.has_mouse(): return []
424 p_from
, p_to
= P3dGfxMgr
.world_from_to(base
.mouseWatcherNode
.get_mouse())
425 return self
._world
.ray_test_all(p_from
, p_to
).get_hits()
427 def _update_info(self
, item
):
430 txt
= '%.3f %.3f\n%.3f°' % (
431 item
._np
.get_x(), item
._np
.get_z(), item
._np
.get_r())
432 self
._info
_txt
['text'] = txt
434 def _on_click(self
, method
):
437 for hit
in self
._get
_hits
():
438 if hit
.get_node() == self
._mouse
_plane
_node
:
439 pos
= hit
.get_hit_pos()
440 for hit
in self
._get
_hits
():
441 for item
in [i
for i
in self
.items
if hit
.get_node() == i
.node
and i
.interactable
]:
442 if not self
._item
_active
:
443 self
._item
_active
= item
444 getattr(item
, method
)(pos
)
445 img
= 'move' if method
== 'on_click_l' else 'rotate'
446 if not (img
== 'rotate' and not item
._instantiated
):
447 self
._cursor
.set_image('assets/images/buttons/%s.dds' % img
)
449 def on_click_l(self
):
450 self
._on
_click
('on_click_l')
452 def on_click_r(self
):
453 self
._on
_click
('on_click_r')
455 def on_release(self
):
456 if self
._item
_active
and not self
._item
_active
._first
_command
:
457 self
._commands
= self
._commands
[:self
._command
_idx
]
458 self
._commands
+= [self
._item
_active
]
459 self
._command
_idx
+= 1
460 #self.__prev_btn['state'] = NORMAL
461 #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
462 #self.__prev_btn['frameColor'] = fcols[0]
463 #if self._item_active._command_idx == len(self._item_active._commands) - 1:
464 # self.__next_btn['state'] = DISABLED
465 # self.__next_btn['frameColor'] = fcols[1]
466 self
._item
_active
= None
467 [item
.on_release() for item
in self
.items
]
468 self
._cursor
.set_image('assets/images/buttons/arrowUpLeft.dds')
471 for item
in self
.items
:
472 item
.repos_done
= False
473 self
.items
= sorted(self
.items
, key
=lambda itm
: itm
.__class
__.__name
__)
474 [item
.on_aspect_ratio_changed() for item
in self
.items
]
475 self
._side
_panel
.update(self
.items
)
476 max_x
= -float('inf')
477 for item
in self
.items
:
478 if not item
._instantiated
:
479 max_x
= max(item
._np
.get_x(), max_x
)
480 for item
in self
.items
:
481 if not item
._instantiated
:
484 def on_aspect_ratio_changed(self
):
487 def _win_condition(self
):
488 return all(itm
.strategy
.win_condition() for itm
in self
.items
) and not self
._paused
490 def _fail_condition(self
):
491 return all(itm
.fail_condition() for itm
in self
.items
) and not self
._paused
and self
._state
== 'playing'
493 def on_frame(self
, task
):
494 hits
= self
._get
_hits
()
496 for hit
in self
._get
_hits
():
497 if hit
.get_node() == self
._mouse
_plane
_node
:
498 pos
= hit
.get_hit_pos()
499 hit_nodes
= [hit
.get_node() for hit
in hits
]
500 if self
._item
_active
:
501 items_hit
= [self
._item
_active
]
503 items_hit
= [itm
for itm
in self
.items
if itm
.node
in hit_nodes
]
504 items_no_hit
= [itm
for itm
in self
.items
if itm
not in items_hit
]
505 [itm
.on_mouse_on() for itm
in items_hit
]
506 [itm
.on_mouse_off() for itm
in items_no_hit
]
507 if pos
and self
._item
_active
:
508 self
._item
_active
.on_mouse_move(pos
)
510 self
._update
_info
(items_hit
[0] if items_hit
else None)
511 if self
._win
_condition
():
512 self
._start
_evt
_time
= None
513 self
._set
_fail
() if self
._enforce
_res
== 'fail' else self
._set
_win
()
514 elif self
._state
== 'playing' and self
._fail
_condition
():
515 self
._start
_evt
_time
= None
516 self
._set
_win
() if self
._enforce
_res
== 'win' else self
._set
_fail
()
517 elif self
._testing
and self
._start
_evt
_time
and globalClock
.getFrameTime() - self
._start
_evt
_time
> 5.0:
518 self
._start
_evt
_time
= None
519 self
._set
_win
() if self
._enforce
_res
== 'win' else self
._set
_fail
()
520 if any(itm
._overlapping
for itm
in self
.items
):
521 self
._cursor
.cursor_img
.img
.set_color(.9, .1, .1, 1)
523 self
._cursor
.cursor_img
.img
.set_color(.9, .9, .9, 1)
526 def cb_inst(self
, item
):
530 self
._state
= 'playing'
531 #self.__prev_btn['state'] = DISABLED
532 #self.__next_btn['state'] = DISABLED
533 self
.__right
_btn
['state'] = DISABLED
534 [itm
.play() for itm
in self
.items
]
535 self
._start
_evt
_time
= globalClock
.getFrameTime()
538 self
._commands
[self
._command
_idx
].redo()
539 self
._command
_idx
+= 1
540 #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
541 #self.__prev_btn['state'] = NORMAL
542 #self.__prev_btn['frameColor'] = fcols[0]
543 #more_commands = self._command_idx < len(self._commands)
544 #self.__next_btn['state'] = NORMAL if more_commands else DISABLED
545 #self.__next_btn['frameColor'] = fcols[0] if more_commands else fcols[1]
548 self
._command
_idx
-= 1
549 self
._commands
[self
._command
_idx
].undo()
550 #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
551 #self.__next_btn['state'] = NORMAL
552 #self.__next_btn['frameColor'] = fcols[0]
553 #self.__prev_btn['state'] = NORMAL if self._command_idx else DISABLED
554 #self.__prev_btn['frameColor'] = fcols[0] if self._command_idx else fcols[1]
559 def _set_instructions(self
):
562 mgr
= TextPropertiesManager
.get_global_ptr()
563 for name
in ['mouse_l', 'mouse_r']:
564 graphic
= OnscreenImage('assets/images/buttons/%s.dds' % name
)
565 graphic
.set_scale(.5)
566 graphic
.get_texture().set_minfilter(Texture
.FTLinearMipmapLinear
)
567 graphic
.get_texture().set_anisotropic_degree(2)
568 mgr
.set_graphic(name
, graphic
)
570 graphic
.set_transparency(True)
571 graphic
.detach_node()
572 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
573 frameSize
=(-.6, .6, -.3, .3))
574 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
576 font
.set_pixels_per_unit(60)
577 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
578 font
.set_outline((0, 0, 0, 1), .8, .2)
579 self
._txt
= OnscreenText(
580 self
._instr
_txt
(), parent
=frm
, font
=font
, scale
=0.06,
581 fg
=(.9, .9, .9, 1), align
=TextNode
.A_left
)
582 u_l
= self
._txt
.textNode
.get_upper_left_3d()
583 l_r
= self
._txt
.textNode
.get_lower_right_3d()
584 w
, h
= l_r
[0] - u_l
[0], u_l
[2] - l_r
[2]
587 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
588 z
+= (btn_scale
+ 2 * mar
) / 2
589 self
._txt
['pos'] = -w
/ 2, z
590 u_l
= self
._txt
.textNode
.get_upper_left_3d()
591 l_r
= self
._txt
.textNode
.get_lower_right_3d()
592 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
593 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
594 frm
['frameSize'] = fsz
596 (.6, .6, .6, 1), # ready
597 (1, 1, 1, 1), # press
598 (.8, .8, .8, 1), # rollover
600 imgs
= [self
.__load
_img
_btn
('exitRight', col
) for col
in colors
]
602 image
=imgs
, scale
=btn_scale
,
603 pos
=(l_r
[0] - btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
604 parent
=frm
, command
=self
.__on
_close
_instructions
, extraArgs
=[frm
],
605 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
606 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
607 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
608 btn
.set_transparency(True)
609 self
._pos
_mgr
.register('close_instructions', LibP3d
.wdg_pos(btn
))
612 self
.__persistent
.save_scene(self
.__json
_name
, self
.version(self
.__json
_name
))
613 loader
.load_sfx('assets/audio/sfx/success.ogg').play()
616 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
617 frameSize
=(-.6, .6, -.3, .3))
618 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
620 font
.set_pixels_per_unit(60)
621 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
622 font
.set_outline((0, 0, 0, 1), .8, .2)
623 self
._txt
= OnscreenText(
626 font
=font
, scale
=0.2,
628 u_l
= self
._txt
.textNode
.get_upper_left_3d()
629 l_r
= self
._txt
.textNode
.get_lower_right_3d()
630 #w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
634 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
635 z
+= (btn_scale
+ 2 * mar
) / 2
636 self
._txt
['pos'] = 0, z
637 u_l
= self
._txt
.textNode
.get_upper_left_3d()
638 l_r
= self
._txt
.textNode
.get_lower_right_3d()
639 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
640 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
641 frm
['frameSize'] = fsz
643 (.6, .6, .6, 1), # ready
644 (1, 1, 1, 1), # press
645 (.8, .8, .8, 1), # rollover
647 imgs
= [self
.__load
_img
_btn
('home', col
) for col
in colors
]
649 image
=imgs
, scale
=btn_scale
,
650 pos
=(-2.8 * btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
651 parent
=frm
, command
=self
._on
_end
_home
, extraArgs
=[frm
],
652 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
653 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
654 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
655 btn
.set_transparency(True)
656 self
._pos
_mgr
.register('home_win', LibP3d
.wdg_pos(btn
))
657 imgs
= [self
.__load
_img
_btn
('rewind', col
) for col
in colors
]
659 image
=imgs
, scale
=btn_scale
,
660 pos
=(0, 1, l_r
[2] - mar
- btn_scale
),
661 parent
=frm
, command
=self
._on
_restart
, extraArgs
=[frm
],
662 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
663 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
664 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
665 self
._pos
_mgr
.register('replay', LibP3d
.wdg_pos(btn
))
666 btn
.set_transparency(True)
667 enabled
= self
._scenes
.index(self
.__json
_name
) < len(self
._scenes
) - 1
669 next_scene
= self
._scenes
[self
._scenes
.index(self
.__json
_name
) + 1]
672 imgs
= [self
.__load
_img
_btn
('right', col
) for col
in colors
]
674 image
=imgs
, scale
=btn_scale
,
675 pos
=(2.8 * btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
676 parent
=frm
, command
=self
._on
_next
_scene
,
677 extraArgs
=[frm
, next_scene
], relief
=FLAT
,
678 frameColor
=(.6, .6, .6, .08),
679 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
680 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
681 btn
['state'] = NORMAL
if enabled
else DISABLED
682 self
._pos
_mgr
.register('next', LibP3d
.wdg_pos(btn
))
683 btn
.set_transparency(True)
686 loader
.load_sfx('assets/audio/sfx/success.ogg').play()
689 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
690 frameSize
=(-.6, .6, -.3, .3))
691 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
693 font
.set_pixels_per_unit(60)
694 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
695 font
.set_outline((0, 0, 0, 1), .8, .2)
696 self
._txt
= OnscreenText(
697 _('You have failed!'),
699 font
=font
, scale
=0.2,
701 u_l
= self
._txt
.textNode
.get_upper_left_3d()
702 l_r
= self
._txt
.textNode
.get_lower_right_3d()
703 #w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
707 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
708 z
+= (btn_scale
+ 2 * mar
) / 2
709 self
._txt
['pos'] = 0, z
710 u_l
= self
._txt
.textNode
.get_upper_left_3d()
711 l_r
= self
._txt
.textNode
.get_lower_right_3d()
712 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
713 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
714 frm
['frameSize'] = fsz
716 (.6, .6, .6, 1), # ready
717 (1, 1, 1, 1), # press
718 (.8, .8, .8, 1), # rollover
720 imgs
= [self
.__load
_img
_btn
('home', col
) for col
in colors
]
722 image
=imgs
, scale
=btn_scale
,
723 pos
=(-2.8 * btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
724 parent
=frm
, command
=self
._on
_end
_home
, extraArgs
=[frm
],
725 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
726 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
727 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
728 self
._pos
_mgr
.register('home_win', LibP3d
.wdg_pos(btn
))
729 btn
.set_transparency(True)
730 imgs
= [self
.__load
_img
_btn
('rewind', col
) for col
in colors
]
732 image
=imgs
, scale
=btn_scale
,
733 pos
=(0, 1, l_r
[2] - mar
- btn_scale
),
734 parent
=frm
, command
=self
._on
_restart
, extraArgs
=[frm
],
735 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
736 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
737 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
738 self
._pos
_mgr
.register('replay', LibP3d
.wdg_pos(btn
))
739 btn
.set_transparency(True)
741 def _on_restart(self
, frm
):
742 self
.__on
_close
_instructions
(frm
)
745 def _on_end_home(self
, frm
):
746 self
.__on
_close
_instructions
(frm
)
749 def _on_next_scene(self
, frm
, scene
):
750 self
.__on
_close
_instructions
(frm
)
751 self
._reload
_cb
(scene
)
753 def __store_state(self
):
755 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
756 #self.__next_btn, self.__prev_btn, self.__rewind_btn
758 self
.__btn
_state
= [btn
['state'] for btn
in btns
]
760 btn
['state'] = DISABLED
761 [itm
.store_state() for itm
in self
.items
]
763 def __restore_state(self
):
765 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
766 #self.__next_btn, self.__prev_btn, self.__rewind_btn
768 for btn
, state
in zip(btns
, self
.__btn
_state
):
770 [itm
.restore_state() for itm
in self
.items
]
773 def __on_close_instructions(self
, frm
):
775 self
.__restore
_state
()
777 def _set_test_items(self
):
778 def frame_after(task
):
779 self
._define
_test
_items
()
780 for itm
in self
._test
_items
:
781 self
._pos
_mgr
.register(itm
.name
, P3dGfxMgr
.pos2d_p2d(itm
))
782 taskMgr
.doMethodLater(1.4, frame_after
, 'frame after') # after the intro sequence
784 def _define_test_items(self
):
785 if not self
.__json
_name
in self
.__class
__.json_files
:
786 with
open(self
.__class
__.filename(self
.__json
_name
)) as f
:
787 self
.__class
__.json_files
[self
.__json
_name
] = loads(f
.read())
788 for item
in self
.__class
__.json_files
[self
.__json
_name
]['test_items']['pixel_space']:
789 self
._pos
_mgr
.register(item
['id'], tuple(item
['position']))
790 for item
in self
.__class
__.json_files
[self
.__json
_name
]['test_items']['world_space']:
791 self
._set
_test
_item
(item
['id'], tuple(item
['position']))
793 def _set_test_item(self
, name
, pos
):
794 self
._test
_items
+= [NodePath(name
)]
795 self
._test
_items
[-1].set_pos(pos
[0], 0, pos
[1])
797 def _set_editor(self
):
798 self
.__scene
_editor
= SceneEditor(self
.json
, self
.__json
_name
)