ya2 · news · projects · code · about

end frame
authorFlavio Calva <f.calva@gmail.com>
Tue, 8 Feb 2022 17:41:15 +0000 (18:41 +0100)
committerFlavio Calva <f.calva@gmail.com>
Tue, 8 Feb 2022 17:41:15 +0000 (18:41 +0100)
pmachines/app.py
pmachines/menu.py
pmachines/scene.py
pmachines/scenes/scene_box.py
pmachines/scenes/scene_domino.py
prj.org

index f87bee384159904d193e4a82d8551250886db4e4..82208b2d351a9f384585ed5ae6387a2e013f8d39 100755 (executable)
@@ -100,17 +100,22 @@ class PmachinesApp:
         self._scene = cls(
             self.world, self.on_home,
             self._options['development']['auto_close_instructions'],
-            self._options['development']['debug_items'])
+            self._options['development']['debug_items'],
+            self.reload)
 
     def on_scene_exit(self):
         self._unset_physics()
         self._scene.destroy()
 
+    def reload(self, cls):
+        self._fsm.demand('Scene', cls)
+
     def _configure(self, args):
         load_prc_file_data('', 'window-title pmachines')
         load_prc_file_data('', 'framebuffer-srgb true')
         load_prc_file_data('', 'sync-video true')
-        load_prc_file_data('', 'threading-model Cull/Draw')
+        # load_prc_file_data('', 'threading-model Cull/Draw')
+        # it freezes when you go to the next scene
         if args.screenshots:
             load_prc_file_data('', 'window-type offscreen')
             load_prc_file_data('', 'audio-library-name null')
index 103516fdbf4582d12f565e4fbed11e7b3c9860bd..96cb99ee9a22cd45abbe0e510ac553bbc8127dc9 100644 (file)
@@ -169,13 +169,14 @@ class Menu:
             **self._common_btn)]
 
     def on_play(self):
-        scene_classes = []
+        scenes = []
         for _file in glob('pmachines/scenes/*.py'):
             _fn = _file.replace('.py', '').replace('/', '.')
             for member in import_module(_fn).__dict__.values():
                 if isclass(member) and issubclass(member, Scene) and \
                         member != Scene:
-                    scene_classes += [member]
+                    scenes += [member]
+        scenes = sorted(scenes, key=lambda elm: elm.sorting)
         self.destroy()
         self._cursor = MouseCursor(
             'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
@@ -184,8 +185,8 @@ class Menu:
         cmn = self._common_btn.copy() | {
             'frameSize': (-2.4, 2.4, -2.4, 2.4),
             'frameColor': (1, 1, 1, .8)}
-        left = - (dx := .8) * (len(scene_classes) - 1) / 2
-        for i, cls in enumerate(scene_classes):
+        left = - (dx := .8) * (len(scenes) - 1) / 2
+        for i, cls in enumerate(scenes):
             self._widgets += [DirectButton(
                 text=cls.name(), pos=(left + dx * i, 1, .1), command=self.start,
                 extraArgs=[cls],
index 04424f85d47b1cdb5acdea946fb5ba20db161084..4d20f3376a1606edefce45564d81747166924fff 100644 (file)
@@ -1,4 +1,7 @@
 from os.path import exists
+from glob import glob
+from importlib import import_module
+from inspect import isclass
 from panda3d.core import AmbientLight, DirectionalLight, Point3, Texture, \
     TextPropertiesManager, TextNode, Spotlight, PerspectiveLens, BitMask32
 from panda3d.bullet import BulletPlaneShape, BulletGhostNode
@@ -15,11 +18,12 @@ from lib.lib.p3d.gfx import P3dGfxMgr
 
 class Scene(DirectObject):
 
-    def __init__(self, world, exit_cb, auto_close_instr, dbg_items):
+    def __init__(self, world, exit_cb, auto_close_instr, dbg_items, reload_cb):
         super().__init__()
         self._world = world
         self._exit_cb = exit_cb
         self._dbg_items = dbg_items
+        self._reload_cb = reload_cb
         self._set_camera()
         self._cursor = MouseCursor(
             'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
@@ -309,15 +313,8 @@ class Scene(DirectObject):
             self._item_active.on_mouse_move(pos)
         if self._dbg_items:
             self._update_info(items_hit[0] if items_hit else None)
-        if all(itm.end_condition() for itm in self.items) and not hasattr(self, '_success_txt'):
-            loader.load_sfx('assets/audio/sfx/success.ogg').play()
-            font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
-            font.clear()
-            font.set_pixels_per_unit(60)
-            font.set_minfilter(Texture.FTLinearMipmapLinear)
-            font.set_outline((0, 0, 0, 1), .8, .2)
-            self._success_txt = OnscreenText(
-                _('You win!'), font=font, scale=0.2, fg=(.9, .9, .9, 1))
+        if all(itm.end_condition() for itm in self.items) and not self._paused:
+            self._set_end()
         return task.cont
 
     def cb_inst(self, item):
@@ -402,6 +399,95 @@ class Scene(DirectObject):
             clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
         btn.set_transparency(True)
 
+    def _set_end(self):
+        loader.load_sfx('assets/audio/sfx/success.ogg').play()
+        self._paused = True
+        self.__store_state()
+        frm = DirectFrame(frameColor=(.4, .4, .4, .06),
+                          frameSize=(-.6, .6, -.3, .3))
+        font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
+        font.clear()
+        font.set_pixels_per_unit(60)
+        font.set_minfilter(Texture.FTLinearMipmapLinear)
+        font.set_outline((0, 0, 0, 1), .8, .2)
+        self._txt = OnscreenText(
+            _('You win!'),
+            parent=frm,
+            font=font, scale=0.2,
+            fg=(.9, .9, .9, 1))
+        u_l = self._txt.textNode.get_upper_left_3d()
+        l_r = self._txt.textNode.get_lower_right_3d()
+        w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
+        btn_scale = .05
+        mar = .06  # margin
+        z = h / 2 - font.get_line_height() * self._txt['scale'][1]
+        z += (btn_scale + 2 * mar) / 2
+        self._txt['pos'] = 0, z
+        u_l = self._txt.textNode.get_upper_left_3d()
+        l_r = self._txt.textNode.get_lower_right_3d()
+        c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
+        fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
+        frm['frameSize'] = fsz
+        colors = [
+            (.6, .6, .6, 1),  # ready
+            (1, 1, 1, 1), # press
+            (.8, .8, .8, 1), # rollover
+            (.4, .4, .4, .4)]
+        imgs = [self.__load_img_btn('home', col) for col in colors]
+        btn = DirectButton(
+            image=imgs, scale=btn_scale,
+            pos=(-2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
+            parent=frm, command=self._on_end_home, extraArgs=[frm],
+            relief=FLAT, frameColor=(.6, .6, .6, .08),
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        btn.set_transparency(True)
+        imgs = [self.__load_img_btn('rewind', col) for col in colors]
+        btn = DirectButton(
+            image=imgs, scale=btn_scale,
+            pos=(0, 1, l_r[2] - mar - btn_scale),
+            parent=frm, command=self._on_restart, extraArgs=[frm],
+            relief=FLAT, frameColor=(.6, .6, .6, .08),
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        btn.set_transparency(True)
+        scenes = []
+        for _file in glob('pmachines/scenes/*.py'):
+            _fn = _file.replace('.py', '').replace('/', '.')
+            for member in import_module(_fn).__dict__.values():
+                if isclass(member) and issubclass(member, Scene) and \
+                        member != Scene:
+                    scenes += [member]
+        scenes = sorted(scenes, key=lambda elm: elm.sorting)
+        enabled = scenes.index(self.__class__) < len(scenes) - 1
+        if enabled:
+            next_scene = scenes[scenes.index(self.__class__) + 1]
+        else:
+            next_scene = None
+        imgs = [self.__load_img_btn('right', col) for col in colors]
+        btn = DirectButton(
+            image=imgs, scale=btn_scale,
+            pos=(2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
+            parent=frm, command=self._on_next_scene,
+            extraArgs=[frm, next_scene], relief=FLAT,
+            frameColor=(.6, .6, .6, .08),
+            rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
+            clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
+        btn['state'] = NORMAL if enabled else DISABLED
+        btn.set_transparency(True)
+
+    def _on_restart(self, frm):
+        self.__on_close_instructions(frm)
+        self.reset()
+
+    def _on_end_home(self, frm):
+        self.__on_close_instructions(frm)
+        self.on_home()
+
+    def _on_next_scene(self, frm, scene):
+        self.__on_close_instructions(frm)
+        self._reload_cb(scene)
+
     def __store_state(self):
         btns = [
             self.__home_btn, self.__info_btn, self.__right_btn,
index aa34237b981455bde69885d6ffe6d162abce682c..781fb1622db1161becf4280cc71d132ba8b19d5f 100644 (file)
@@ -8,6 +8,8 @@ from pmachines.items.teetertooter import TeeterTooter
 
 class SceneBox(Scene):
 
+    sorting = 1
+
     @staticmethod
     def name():
         return _('Box')
index 6148636845608ffa55324e07951727a9f6d2dd65..c659a7db3f9792cbd9e647c9b309252bdc94628e 100644 (file)
@@ -8,6 +8,8 @@ from pmachines.items.teetertooter import TeeterTooter
 
 class SceneDomino(Scene):
 
+    sorting = 0
+
     @staticmethod
     def name():
         return _('Domino')
diff --git a/prj.org b/prj.org
index 70ebb524306b540803ca853dbc49d3a6c07c0811..c7b3649e8606f57c633874f5e3890e7a4f3f3995 100644 (file)
--- a/prj.org
+++ b/prj.org
@@ -1,8 +1,10 @@
 * issues
 * todo
-** frame for the end of the level (back, replay, next level)
-** create one level per item, then levels with more items
-*** e.g. item1, item2, item1+2, item3, item1+2+3, ...
+** level domino + box
+** level basketball
+** level domino + box + basketball
+** level teetertooter
+** level domino + box + basketball + teetertooter
 ** intro animation (from target item to start position)
 ** use dds files in place of png/jpg
 ** refactoring