From: Flavio Calva Date: Thu, 9 Jun 2022 07:01:44 +0000 (+0200) Subject: Revert "entities component" X-Git-Url: http://git.ya2.it/?p=pmachines.git;a=commitdiff_plain;h=404a11ea75bad23b2fbc047beaf6747eaa1cf494 Revert "entities component" This reverts commit 78589fb12fc2650dcb4f1b3c5094e5d263d61574. --- diff --git a/entities/__init__.py b/entities/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/entities/app.py b/entities/app.py deleted file mode 100755 index a01e558..0000000 --- a/entities/app.py +++ /dev/null @@ -1,277 +0,0 @@ -import argparse -import simplepbr -#import gltf -from glob import glob -from importlib import import_module -from inspect import isclass -from sys import platform, argv, exit -from logging import info, debug -from os.path import exists -from os import makedirs -from panda3d.core import Filename, load_prc_file_data, AntialiasAttrib, \ - Texture, WindowProperties, LVector2i, TextNode -from panda3d.bullet import BulletWorld, BulletDebugNode -from direct.showbase.ShowBase import ShowBase -from direct.gui.OnscreenText import OnscreenText -from direct.fsm.FSM import FSM -from entities.music import MusicMgr -from entities.items.background import Background -from entities.menu import Menu -from entities.scene import Scene -from entities.scenes.scene_basketball import SceneBasketBall -from entities.scenes.scene_box import SceneBox -from entities.scenes.scene_domino_box_basketball import SceneDominoBoxBasketball -from entities.scenes.scene_domino_box import SceneDominoBox -from entities.scenes.scene_domino import SceneDomino -from entities.scenes.scene_teeter_domino_box_basketball import SceneTeeterDominoBoxBasketball -from entities.scenes.scene_teeter_tooter import SceneTeeterTooter -from ya2.dictfile import DctFile -from ya2.lib.p3d.p3d import LibP3d -from ya2.engine.lang import LangMgr -from ya2.engine.log import LogMgr -from ya2.engine.functional import FunctionalTest - - -class MainFsm(FSM): - - def __init__(self, pmachines): - super().__init__('Main FSM') - self._pmachines = pmachines - - def enterMenu(self): - self._pmachines.on_menu_enter() - - def exitMenu(self): - self._pmachines.on_menu_exit() - - def enterScene(self, cls): - self._pmachines.on_scene_enter(cls) - - def exitScene(self): - self._pmachines.on_scene_exit() - - -class PmachinesApp: - - scenes = [ - SceneDomino, - SceneBox, - SceneDominoBox, - SceneBasketBall, - SceneDominoBoxBasketball, - SceneTeeterTooter, - SceneTeeterDominoBoxBasketball] - - def __init__(self): - info('platform: %s' % platform) - info('exists main.py: %s' % exists('main.py')) - self._args = args = self._parse_args() - self._configure(args) - self.base = ShowBase() - self._pipeline = None - self.updating = args.update - self.version = args.version - self.log_mgr = LogMgr.init_cls()(self) - self._prepare_window(args) - if args.update: - return - if args.functional_test: - self._options['settings']['volume'] = 0 - self._music = MusicMgr(self._options['settings']['volume']) - self.lang_mgr = LangMgr(self._options['settings']['language'], - 'pmachines', - 'assets/locale/') - self._fsm = MainFsm(self) - if args.screenshot: - cls = [cls for cls in self.scenes if cls.__name__ == args.screenshot][0] - scene = cls(BulletWorld(), None, True, False, lambda: None, self.scenes) - scene.screenshot() - scene.destroy() - exit() - elif self._options['development']['auto_start']: - mod_name = 'entities.scenes.scene_' + self._options['development']['auto_start'] - for member in import_module(mod_name).__dict__.values(): - if isclass(member) and issubclass(member, Scene) and \ - member != Scene: - cls = member - self._fsm.demand('Scene', cls) - else: - self._fsm.demand('Menu') - if args.functional_test or args.functional_ref: - FunctionalTest(args.functional_ref) - - def on_menu_enter(self): - self._menu_bg = Background() - self._menu = Menu( - self._fsm, self.lang_mgr, self._options, self._music, - self._pipeline, self.scenes, self._args.functional_test or self._args.functional_ref) - - def on_home(self): - self._fsm.demand('Menu') - - def on_menu_exit(self): - self._menu_bg.destroy() - self._menu.destroy() - - def on_scene_enter(self, cls): - self._set_physics() - self._scene = cls( - self.world, self.on_home, - self._options['development']['auto_close_instructions'], - self._options['development']['debug_items'], - self.reload, - self.scenes) - - 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') - if args.functional_test or args.functional_ref: - load_prc_file_data('', 'win-size 1360 768') - # otherwise it is not centered in exwm - # load_prc_file_data('', 'threading-model Cull/Draw') - # it freezes when you go to the next scene - if args.screenshot: - load_prc_file_data('', 'window-type offscreen') - load_prc_file_data('', 'audio-library-name null') - - def _parse_args(self): - parser = argparse.ArgumentParser() - parser.add_argument('--update', action='store_true') - parser.add_argument('--version', action='store_true') - parser.add_argument('--optfile') - parser.add_argument('--screenshot') - parser.add_argument('--functional-test', action='store_true') - parser.add_argument('--functional-ref', action='store_true') - cmd_line = [arg for arg in iter(argv[1:]) if not arg.startswith('-psn_')] - args = parser.parse_args(cmd_line) - return args - - def _prepare_window(self, args): - data_path = '' - if (platform.startswith('win') or platform.startswith('linux')) and ( - not exists('main.py') or __file__.startswith('/app/bin/')): - # it is the deployed version for windows - data_path = str(Filename.get_user_appdata_directory()) + '/pmachines' - home = '/home/flavio' # we must force this for wine - if data_path.startswith('/c/users/') and exists(home + '/.wine/'): - data_path = home + '/.wine/drive_' + data_path[1:] - info('creating dirs: %s' % data_path) - makedirs(data_path, exist_ok=True) - optfile = args.optfile if args.optfile else 'options.ini' - info('data path: %s' % data_path) - info('option file: %s' % optfile) - info('fixed path: %s' % LibP3d.fixpath(data_path + '/' + optfile)) - default_opt = { - 'settings': { - 'volume': 1, - 'language': 'en', - 'fullscreen': 1, - 'resolution': '', - 'antialiasing': 1, - 'shadows': 1}, - 'development': { - 'simplepbr': 1, - 'verbose_log': 0, - 'physics_debug': 0, - 'auto_start': 0, - 'auto_close_instructions': 0, - 'show_buffers': 0, - 'debug_items': 0, - 'mouse_coords': 0, - 'fps': 0}} - opt_path = LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile - opt_exists = exists(opt_path) - self._options = DctFile( - LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile, - default_opt) - if not opt_exists: - self._options.store() - res = self._options['settings']['resolution'] - if res: - res = LVector2i(*[int(_res) for _res in res.split('x')]) - else: - resolutions = [] - if not self.version: - d_i = base.pipe.get_display_information() - def _res(idx): - return d_i.get_display_mode_width(idx), \ - d_i.get_display_mode_height(idx) - resolutions = [ - _res(idx) for idx in range(d_i.get_total_display_modes())] - res = sorted(resolutions)[-1] - fullscreen = self._options['settings']['fullscreen'] - props = WindowProperties() - if args.functional_test or args.functional_ref: - fullscreen = False - else: - props.set_size(res) - props.set_fullscreen(fullscreen) - props.set_icon_filename('assets/images/icon/pmachines.ico') - if not args.screenshot and not self.version: - base.win.request_properties(props) - #gltf.patch_loader(base.loader) - if self._options['development']['simplepbr'] and not self.version: - self._pipeline = simplepbr.init( - use_normal_maps=True, - use_emission_maps=False, - use_occlusion_maps=True, - msaa_samples=4 if self._options['settings']['antialiasing'] else 1, - enable_shadows=int(self._options['settings']['shadows'])) - debug(f'msaa: {self._pipeline.msaa_samples}') - debug(f'shadows: {self._pipeline.enable_shadows}') - render.setAntialias(AntialiasAttrib.MAuto) - self.base.set_background_color(0, 0, 0, 1) - self.base.disable_mouse() - if self._options['development']['show_buffers']: - base.bufferViewer.toggleEnable() - if self._options['development']['fps']: - base.set_frame_rate_meter(True) - #self.base.accept('window-event', self._on_win_evt) - self.base.accept('aspectRatioChanged', self._on_aspect_ratio_changed) - if self._options['development']['mouse_coords']: - coords_txt = OnscreenText( - '', parent=base.a2dTopRight, scale=0.04, - pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right) - def update_coords(task): - txt = '%s %s' % (int(base.win.get_pointer(0).x), - int(base.win.get_pointer(0).y)) - coords_txt['text'] = txt - return task.cont - taskMgr.add(update_coords, 'update_coords') - - def _set_physics(self): - if self._options['development']['physics_debug']: - debug_node = BulletDebugNode('Debug') - debug_node.show_wireframe(True) - debug_node.show_constraints(True) - debug_node.show_bounding_boxes(True) - debug_node.show_normals(True) - self._debug_np = render.attach_new_node(debug_node) - self._debug_np.show() - self.world = BulletWorld() - self.world.set_gravity((0, 0, -9.81)) - if self._options['development']['physics_debug']: - self.world.set_debug_node(self._debug_np.node()) - def update(task): - dt = globalClock.get_dt() - self.world.do_physics(dt, 10, 1/180) - return task.cont - self._phys_tsk = taskMgr.add(update, 'update') - - def _unset_physics(self): - if self._options['development']['physics_debug']: - self._debug_np.remove_node() - self.world = None - taskMgr.remove(self._phys_tsk) - - def _on_aspect_ratio_changed(self): - if self._fsm.state == 'Scene': - self._scene.on_aspect_ratio_changed() diff --git a/entities/items/__init__.py b/entities/items/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/entities/items/background.py b/entities/items/background.py deleted file mode 100644 index 2d83fc0..0000000 --- a/entities/items/background.py +++ /dev/null @@ -1,26 +0,0 @@ -from itertools import product -from panda3d.core import NodePath -from ya2.lib.p3d.gfx import set_srgb - - -class Background: - - def __init__(self): - self._root = NodePath('background_root') - self._root.reparent_to(render) - ncols, nrows = 16, 8 - start_size, end_size = 5, 2.5 - offset = 5 - for col, row in product(range(ncols), range(nrows)): - model = loader.load_model('assets/models/bam/background/background.bam') - model.set_scale(end_size / start_size) - model.reparent_to(self._root) - total_width, total_height = end_size * ncols, end_size * nrows - left, bottom = -total_width/2, -total_height/2 - model.set_pos(left + end_size * col, offset, bottom + end_size * row) - self._root.clear_model_nodes() - self._root.flatten_strong() - set_srgb(self._root) - - def destroy(self): - self._root.remove_node() diff --git a/entities/items/basketball.py b/entities/items/basketball.py deleted file mode 100644 index 81e5586..0000000 --- a/entities/items/basketball.py +++ /dev/null @@ -1,11 +0,0 @@ -from panda3d.bullet import BulletSphereShape, BulletRigidBodyNode, BulletGhostNode -from entities.items.item import Item - - -class Basketball(Item): - - def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.92, friction=.6): - super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/basketball/basketball.bam', .4, mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction) - - def _set_shape(self, apply_scale=True): - self.node.add_shape(BulletSphereShape(1)) diff --git a/entities/items/box.py b/entities/items/box.py deleted file mode 100644 index 832ed2f..0000000 --- a/entities/items/box.py +++ /dev/null @@ -1,25 +0,0 @@ -from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode -from entities.items.item import Item - - -class Box(Item): - - def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.8, model_scale=1): - super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/box/box.bam', mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction, model_scale=model_scale) - - def _set_shape(self, apply_scale=True): - self.node.add_shape(BulletBoxShape((.5, .5, .5))) - - -class HitStrategy: - - def __init__(self, hit_by, node, world): - self._hit_by = hit_by - self._node = node - self._world = world - - def win_condition(self): - for contact in self._world.contact_test(self._node).get_contacts(): - other = contact.get_node1() if contact.get_node0() == self._node else contact.get_node0() - if other == self._hit_by.node: - return True diff --git a/entities/items/domino.py b/entities/items/domino.py deleted file mode 100644 index c698dc8..0000000 --- a/entities/items/domino.py +++ /dev/null @@ -1,33 +0,0 @@ -from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode -from entities.items.item import Item, StillStrategy - - -class Domino(Item): - - def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.6): - super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/domino/domino.bam', mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction) - - def _set_shape(self, apply_scale=True): - self.node.add_shape(BulletBoxShape((.1, .25, .5))) - - -class UpStrategy(StillStrategy): - - def __init__(self, np, tgt_degrees): - super().__init__(np) - self._tgt_degrees = tgt_degrees - self._np = np - - def win_condition(self): - return super().win_condition() and abs(self._np.get_r()) <= self._tgt_degrees - - -class DownStrategy(StillStrategy): - - def __init__(self, np, tgt_degrees): - super().__init__(np) - self._tgt_degrees = tgt_degrees - self._np = np - - def win_condition(self): - return self._np.get_z() < -6 or super().win_condition() and abs(self._np.get_r()) >= self._tgt_degrees diff --git a/entities/items/item.py b/entities/items/item.py deleted file mode 100644 index 9eb5afb..0000000 --- a/entities/items/item.py +++ /dev/null @@ -1,339 +0,0 @@ -from panda3d.core import CullFaceAttrib, Point3, NodePath, Point2, Texture, \ - Plane, Vec3, BitMask32 -from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode -from direct.gui.OnscreenText import OnscreenText -from ya2.lib.p3d.gfx import P3dGfxMgr, set_srgb - - -class Command: - - def __init__(self, pos, rot): - self.pos = pos - self.rot = rot - - -class FixedStrategy: - - def win_condition(self): - return True - - -class StillStrategy: - - def __init__(self, np): - self._np = np - self._positions = [] - self._rotations = [] - - def win_condition(self): - self._positions += [self._np.get_pos()] - self._rotations += [self._np.get_hpr()] - if len(self._positions) > 30: - self._positions.pop(0) - if len(self._rotations) > 30: - self._rotations.pop(0) - if len(self._positions) < 28: - return - avg_x = sum(pos.x for pos in self._positions) / len(self._positions) - avg_y = sum(pos.y for pos in self._positions) / len(self._positions) - avg_z = sum(pos.z for pos in self._positions) / len(self._positions) - avg_h = sum(rot.x for rot in self._rotations) / len(self._rotations) - avg_p = sum(rot.y for rot in self._rotations) / len(self._rotations) - avg_r = sum(rot.z for rot in self._rotations) / len(self._rotations) - avg_pos = Point3(avg_x, avg_y, avg_z) - avg_rot = Point3(avg_h, avg_p, avg_r) - return all((pos - avg_pos).length() < .1 for pos in self._positions) and \ - all((rot - avg_rot).length() < 1 for rot in self._rotations) - - -class Item: - - def __init__(self, world, plane_node, cb_inst, curr_bottom, scene_repos, model_path, model_scale=1, exp_num_contacts=1, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.5): - self._world = world - self._plane_node = plane_node - self._count = count - self._cb_inst = cb_inst - self._paused = False - self._overlapping = False - self._first_command = True - self.repos_done = False - self._mass = mass - self._pos = pos - self._r = r - self.strategy = FixedStrategy() - self._exp_num_contacts = exp_num_contacts - self._curr_bottom = curr_bottom - self._scene_repos = scene_repos - self._model_scale = model_scale - self._model_path = model_path - self._commands = [] - self._command_idx = -1 - self._restitution = restitution - self._friction = friction - self._positions = [] - self._rotations = [] - if count: - self.node = BulletGhostNode(self.__class__.__name__) - else: - self.node = BulletRigidBodyNode(self.__class__.__name__) - self._set_shape(count) - self._np = render.attach_new_node(self.node) - if count: - world.attach_ghost(self.node) - else: - world.attach_rigid_body(self.node) - self._model = loader.load_model(model_path) - set_srgb(self._model) - self._model.flatten_light() - self._model.reparent_to(self._np) - self._np.set_scale(model_scale) - self._np.flatten_strong() - if count: - self._set_outline_model() - set_srgb(self._outline_model) - self._model.hide(BitMask32(0x01)) - self._outline_model.hide(BitMask32(0x01)) - self._start_drag_pos = None - self._prev_rot_info = None - self._last_nonoverlapping_pos = None - self._last_nonoverlapping_rot = None - self._instantiated = not count - self.interactable = count - self._box_tsk = taskMgr.add(self.on_frame, 'on_frame') - if count: - self._repos() - else: - self._np.set_pos(pos) - self._np.set_r(r) - - def _set_shape(self, apply_scale=True): - pass - - def set_strategy(self, strategy): - self.strategy = strategy - - def _repos(self): - p_from, p_to = P3dGfxMgr.world_from_to((-1, 1)) - for hit in self._world.ray_test_all(p_from, p_to).get_hits(): - if hit.get_node() == self._plane_node: - pos = hit.get_hit_pos() - corner = P3dGfxMgr.screen_coord(pos) - bounds = self._np.get_tight_bounds() - bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos() - self._np.set_pos(pos) - plane = Plane(Vec3(0, 1, 0), bounds[0][1]) - pos3d, near_pt, far_pt = Point3(), Point3(), Point3() - margin, ar = .04, base.get_aspect_ratio() - margin_x = margin / ar if ar >= 1 else margin - margin_z = margin * ar if ar < 1 else margin - top = self._curr_bottom() - base.camLens.extrude((-1 + margin_x, top - margin_z), near_pt, far_pt) - plane.intersects_line( - pos3d, render.get_relative_point(base.camera, near_pt), - render.get_relative_point(base.camera, far_pt)) - delta = Vec3(bounds[1][0], bounds[1][1], bounds[0][2]) - self._np.set_pos(pos3d + delta) - if not hasattr(self, '_txt'): - 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( - str(self._count), font=font, scale=0.06, fg=(.9, .9, .9, 1)) - pos = self._np.get_pos() + (bounds[1][0], bounds[0][1], bounds[0][2]) - p2d = P3dGfxMgr.screen_coord(pos) - self._txt['pos'] = p2d - self.repos_done = True - - def repos_x(self, x): - self._np.set_x(x) - bounds = self._np.get_tight_bounds() - bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos() - pos = self._np.get_pos() + (bounds[1][0], bounds[0][1], bounds[0][2]) - p2d = P3dGfxMgr.screen_coord(pos) - self._txt['pos'] = p2d - - def get_bottom(self): - bounds = self._np.get_tight_bounds() - bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos() - pos = self._np.get_pos() + (bounds[1][0], bounds[1][1], bounds[0][2]) - p2d = P3dGfxMgr.screen_coord(pos) - ar = base.get_aspect_ratio() - return p2d[1] if ar >= 1 else (p2d[1] * ar) - - def get_corner(self): - bounds = self._np.get_tight_bounds() - return bounds[1][0], bounds[1][1], bounds[0][2] - - def _set_outline_model(self): - self._outline_model = loader.load_model(self._model_path) - #clockw = CullFaceAttrib.MCullClockwise - #self._outline_model.set_attrib(CullFaceAttrib.make(clockw)) - self._outline_model.set_attrib(CullFaceAttrib.make_reverse()) - self._outline_model.reparent_to(self._np) - self._outline_model.set_scale(1.08) - self._outline_model.set_light_off() - self._outline_model.set_color(.4, .4, .4, 1) - self._outline_model.set_color_scale(.4, .4, .4, 1) - self._outline_model.hide() - - def on_frame(self, task): - self._np.set_y(0) - return task.cont - - def undo(self): - self._command_idx -= 1 - self._np.set_pos(self._commands[self._command_idx].pos) - self._np.set_hpr(self._commands[self._command_idx].rot) - - def redo(self): - self._command_idx += 1 - self._np.set_pos(self._commands[self._command_idx].pos) - self._np.set_hpr(self._commands[self._command_idx].rot) - - def play(self): - if not self._instantiated: - return - self._world.remove_rigid_body(self.node) - self.node.set_mass(self._mass) - self._world.attach_rigid_body(self.node) - self.node.set_restitution(self._restitution) - self.node.set_friction(self._friction) - - def on_click_l(self, pos): - if self._paused: return - self._start_drag_pos = pos, self._np.get_pos() - loader.load_sfx('assets/audio/sfx/grab.ogg').play() - if not self._instantiated: - self._world.remove_ghost(self.node) - pos = self._np.get_pos() - self._np.remove_node() - self.node = BulletRigidBodyNode('box') - self._set_shape() - self._np = render.attach_new_node(self.node) - self._world.attach_rigid_body(self.node) - self._model.reparent_to(self._np) - self._np.set_pos(pos) - self._set_outline_model() - self._np.set_scale(self._model_scale) - self._model.show(BitMask32(0x01)) - self._outline_model.hide(BitMask32(0x01)) - self._instantiated = True - self._txt.destroy() - self._count -= 1 - if self._count: - item = self.__class__(self._world, self._plane_node, self._cb_inst, self._curr_bottom, self._scene_repos, count=self._count, mass=self._mass, pos=self._pos, r=self._r) # pylint: disable=no-value-for-parameter - self._cb_inst(item) - self._scene_repos() - - def on_click_r(self, pos): - if self._paused or not self._instantiated: return - self._prev_rot_info = pos, self._np.get_pos(), self._np.get_r() - loader.load_sfx('assets/audio/sfx/grab.ogg').play() - - def on_release(self): - if self._start_drag_pos or self._prev_rot_info: - loader.load_sfx('assets/audio/sfx/release.ogg').play() - self._command_idx += 1 - self._commands = self._commands[:self._command_idx] - self._commands += [Command(self._np.get_pos(), self._np.get_hpr())] - self._first_command = False - self._start_drag_pos = self._prev_rot_info = None - if self._overlapping: - self._np.set_pos(self._last_nonoverlapping_pos) - self._np.set_hpr(self._last_nonoverlapping_rot) - self._outline_model.set_color(.4, .4, .4, 1) - self._outline_model.set_color_scale(.4, .4, .4, 1) - self._overlapping = False - - def on_mouse_on(self): - if not self._paused and self.interactable: - self._outline_model.show() - - def on_mouse_off(self): - if self._start_drag_pos or self._prev_rot_info: return - if self.interactable: - self._outline_model.hide() - - def on_mouse_move(self, pos): - if self._start_drag_pos: - d_pos = pos - self._start_drag_pos[0] - self._np.set_pos(self._start_drag_pos[1] + d_pos) - if self._prev_rot_info: - start_vec = self._prev_rot_info[0] - self._prev_rot_info[1] - curr_vec = pos - self._prev_rot_info[1] - d_angle = curr_vec.signed_angle_deg(start_vec, (0, -1, 0)) - self._np.set_r(self._prev_rot_info[2] + d_angle) - self._prev_rot_info = pos, self._np.get_pos(), self._np.get_r() - if self._start_drag_pos or self._prev_rot_info: - res = self._world.contact_test(self.node) - nres = res.get_num_contacts() - if nres <= self._exp_num_contacts: - self._overlapping = False - self._outline_model.set_color(.4, .4, .4, 1) - self._outline_model.set_color_scale(.4, .4, .4, 1) - if nres > self._exp_num_contacts and not self._overlapping: - actual_nres = 0 - for contact in res.get_contacts(): - for node in [contact.get_node0(), contact.get_node1()]: - if isinstance(node, BulletRigidBodyNode) and \ - node != self.node: - actual_nres += 1 - if actual_nres >= 1: - self._overlapping = True - loader.load_sfx('assets/audio/sfx/overlap.ogg').play() - self._outline_model.set_color(.9, .1, .1, 1) - self._outline_model.set_color_scale(.9, .1, .1, 1) - if not self._overlapping: - self._last_nonoverlapping_pos = self._np.get_pos() - self._last_nonoverlapping_rot = self._np.get_hpr() - - def on_aspect_ratio_changed(self): - if not self._instantiated: - self._repos() - - def store_state(self): - self._paused = True - self._model.set_transparency(True) - self._model.set_alpha_scale(.3) - if hasattr(self, '_txt') and not self._txt.is_empty(): - self._txt.set_alpha_scale(.3) - - def restore_state(self): - self._paused = False - self._model.set_alpha_scale(1) - if hasattr(self, '_txt') and not self._txt.is_empty(): - self._txt.set_alpha_scale(1) - - def fail_condition(self): - if self._np.get_z() < -6: - return True - self._positions += [self._np.get_pos()] - self._rotations += [self._np.get_hpr()] - if len(self._positions) > 30: - self._positions.pop(0) - if len(self._rotations) > 30: - self._rotations.pop(0) - if len(self._positions) < 28: - return - avg_x = sum(pos.x for pos in self._positions) / len(self._positions) - avg_y = sum(pos.y for pos in self._positions) / len(self._positions) - avg_z = sum(pos.z for pos in self._positions) / len(self._positions) - avg_h = sum(rot.x for rot in self._rotations) / len(self._rotations) - avg_p = sum(rot.y for rot in self._rotations) / len(self._rotations) - avg_r = sum(rot.z for rot in self._rotations) / len(self._rotations) - avg_pos = Point3(avg_x, avg_y, avg_z) - avg_rot = Point3(avg_h, avg_p, avg_r) - return all((pos - avg_pos).length() < .1 for pos in self._positions) and \ - all((rot - avg_rot).length() < 1 for rot in self._rotations) - - def destroy(self): - self._np.remove_node() - taskMgr.remove(self._box_tsk) - if hasattr(self, '_txt'): - self._txt.destroy() - if not self._instantiated: - self._world.remove_ghost(self.node) - else: - self._world.remove_rigid_body(self.node) diff --git a/entities/items/shelf.py b/entities/items/shelf.py deleted file mode 100644 index 0e0fb8a..0000000 --- a/entities/items/shelf.py +++ /dev/null @@ -1,11 +0,0 @@ -from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode -from entities.items.item import Item - - -class Shelf(Item): - - def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.6): - super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/shelf/shelf.bam', mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction) - - def _set_shape(self, apply_scale=True): - self.node.add_shape(BulletBoxShape((1, .5, .05))) diff --git a/entities/items/teetertooter.py b/entities/items/teetertooter.py deleted file mode 100644 index 2e3a068..0000000 --- a/entities/items/teetertooter.py +++ /dev/null @@ -1,21 +0,0 @@ -from panda3d.core import TransformState -from panda3d.bullet import BulletCylinderShape, BulletRigidBodyNode, BulletGhostNode, YUp, ZUp -from entities.items.item import Item - - -class TeeterTooter(Item): - - def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.5): - super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/teeter_tooter/teeter_tooter.bam', exp_num_contacts=2, mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction, model_scale=.5) - - def _set_shape(self, apply_scale=True): - scale = self._model_scale if apply_scale else 1 - self.node.add_shape( - BulletCylinderShape(.1, 1.6, YUp), - TransformState.makePos((0, 0, scale * .36))) - self.node.add_shape( - BulletCylinderShape(.1, .7, ZUp), - TransformState.makePos((0, scale * .8, scale * -.1))) - self.node.add_shape( - BulletCylinderShape(.1, .7, ZUp), - TransformState.makePos((0, scale * -.8, scale * -.1))) diff --git a/entities/menu.py b/entities/menu.py deleted file mode 100644 index 384e5ae..0000000 --- a/entities/menu.py +++ /dev/null @@ -1,304 +0,0 @@ -from logging import info, debug -from sys import platform, exit -from os import environ, system -from glob import glob -from importlib import import_module -from inspect import isclass -from webbrowser import open_new_tab -from panda3d.core import Texture, TextNode, WindowProperties, LVector2i, \ - TextProperties, TextPropertiesManager -from direct.gui.DirectGui import DirectButton, DirectCheckButton, \ - DirectOptionMenu, DirectSlider, DirectCheckButton -from direct.gui.DirectGuiGlobals import FLAT -from direct.gui.OnscreenText import OnscreenText -from direct.showbase.DirectObject import DirectObject -from ya2.engine.gui.cursor import MouseCursor -from entities.scene import Scene -from panda3d.bullet import BulletWorld - - -class Menu(DirectObject): - - def __init__(self, fsm, lang_mgr, opt_file, music, pipeline, scenes, fun_test): - super().__init__() - self._fsm = fsm - self._lang_mgr = lang_mgr - self._opt_file = opt_file - self._music = music - self._pipeline = pipeline - self._scenes = scenes - self._fun_test = fun_test - self._enforced_res = '' - self._cursor = MouseCursor( - 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), - (.01, .01)) - self._font = base.loader.load_font('assets/fonts/Hanken-Book.ttf') - self._font.clear() - self._font.set_pixels_per_unit(60) - self._font.set_minfilter(Texture.FTLinearMipmapLinear) - self._font.set_outline((0, 0, 0, 1), .8, .2) - self._widgets = [] - self._common = { - 'scale': .12, - 'text_font': self._font, - 'text_fg': (.9, .9, .9, 1), - 'relief': FLAT, - 'frameColor': (.4, .4, .4, .14), - 'rolloverSound': loader.load_sfx('assets/audio/sfx/rollover.ogg'), - 'clickSound': loader.load_sfx('assets/audio/sfx/click.ogg')} - self._common_btn = {'frameSize': (-4.8, 4.8, -.6, 1.2)} | self._common - hlc = self._common_btn['frameColor'] - hlc = (hlc[0] + .2, hlc[1] + .2, hlc[2] + .2, hlc[3] + .2) - self._common_opt = { - 'item_frameColor': self._common_btn['frameColor'], - 'popupMarker_frameColor': self._common_btn['frameColor'], - 'item_relief': self._common_btn['relief'], - 'item_text_font': self._common_btn['text_font'], - 'item_text_fg': self._common_btn['text_fg'], - 'textMayChange': 1, - 'highlightColor': hlc, - 'text_align': TextNode.A_center, - } | self._common_btn - f_s = self._common_opt['frameSize'] - self._common_opt['frameSize'] = f_s[0], f_s[1] - .56, f_s[2], f_s[3] - self._common_slider = self._common | { - 'range': (0, 1), - 'thumb_frameColor': (.4, .4, .4, .4), - 'thumb_scale': 1.6, - 'scale': .4} - del self._common_slider['rolloverSound'] - del self._common_slider['clickSound'] - self._set_main() - - def enforce_res(self, val): - self._enforced_res = val - info('enforced resolution: ' + val) - - def _set_main(self): - self._widgets = [] - self._widgets += [DirectButton( - text=_('Play'), pos=(0, 1, .6), command=self.on_play, - **self._common_btn)] - self._widgets += [DirectButton( - text=_('Options'), pos=(0, 1, .2), command=self.on_options, - **self._common_btn)] - self._widgets += [DirectButton( - text=_('Credits'), pos=(0, 1, -.2), command=self.on_credits, - **self._common_btn)] - self._widgets += [DirectButton( - text=_('Exit'), pos=(0, 1, -.6), command=lambda: exit(), - **self._common_btn)] - self._rearrange_width() - self.accept('enforce_resolution', self.enforce_res) - - def _set_options(self): - self._widgets = [] - self._lang_funcs = [lambda: _('English'), lambda: _('Italian')] - items = [fnc() for fnc in self._lang_funcs] - inititem = { - 'en': _('English'), - 'it': _('Italian') - }[self._opt_file['settings']['language']] - btn = DirectOptionMenu( - text=_('Language'), items=items, initialitem=inititem, - pos=(0, 1, .8), command=self.on_language, **self._common_opt) - btn.popupMenu['frameColor'] = self._common_btn['frameColor'] - btn.popupMenu['relief'] = self._common_btn['relief'] - self._widgets += [btn] - self._widgets += [OnscreenText( - _('Volume'), pos=(-.1, .55), font=self._common['text_font'], - scale=self._common['scale'], fg=self._common['text_fg'], - align=TextNode.A_right)] - self._widgets += [DirectSlider( - pos=(.5, 1, .57), - value=self._opt_file['settings']['volume'], - command=self.on_volume, - **self._common_slider)] - self._slider = self._widgets[-1] - self._widgets += [DirectCheckButton( - text=_('Fullscreen'), pos=(0, 1, .3), command=self.on_fullscreen, - indicator_frameColor=self._common_opt['highlightColor'], - indicator_relief=self._common_btn['relief'], - indicatorValue=self._opt_file['settings']['fullscreen'], - **self._common_btn)] - res = self._opt_file['settings']['resolution'] - d_i = base.pipe.get_display_information() - def _res(idx): - return d_i.get_display_mode_width(idx), \ - d_i.get_display_mode_height(idx) - resolutions = [ - _res(idx) for idx in range(d_i.get_total_display_modes())] - resolutions = list(set(resolutions)) - resolutions = sorted(resolutions) - resolutions = [(str(_res[0]), str(_res[1])) for _res in resolutions] - resolutions = ['x'.join(_res) for _res in resolutions] - if not res: - res = resolutions[-1] - btn = DirectOptionMenu( - text=_('Resolution'), items=resolutions, initialitem=res, - pos=(0, 1, .05), command=self.on_resolution, **self._common_opt) - btn.popupMenu['frameColor'] = self._common_btn['frameColor'] - btn.popupMenu['relief'] = self._common_btn['relief'] - self._widgets += [btn] - self._widgets += [DirectCheckButton( - text=_('Antialiasing'), pos=(0, 1, -.2), command=self.on_aa, - indicator_frameColor=self._common_opt['highlightColor'], - indicator_relief=self._common_btn['relief'], - indicatorValue=self._opt_file['settings']['antialiasing'], - **self._common_btn)] - self._widgets += [DirectCheckButton( - text=_('Shadows'), pos=(0, 1, -.45), command=self.on_shadows, - indicator_frameColor=self._common_opt['highlightColor'], - indicator_relief=self._common_btn['relief'], - indicatorValue=self._opt_file['settings']['shadows'], - **self._common_btn)] - self._widgets += [DirectButton( - text=_('Back'), pos=(0, 1, -.8), command=self.on_back, - **self._common_btn)] - self.accept('enforce_resolution', self.enforce_res) - - def _set_credits(self): - self._widgets = [] - tp_scale = TextProperties() - tp_scale.set_text_scale(.64) - TextPropertiesManager.getGlobalPtr().setProperties('scale', tp_scale) - self._widgets += [OnscreenText( - _('Code and gfx\n \1scale\1Flavio Calva\2\n\n\nMusic\n \1scale\1Stefan Grossmann\2'), - pos=(-.9, .55), font=self._common['text_font'], - scale=self._common['scale'], fg=self._common['text_fg'], - align=TextNode.A_left)] - self._widgets += [DirectButton( - text=_('Website'), pos=(-.6, 1, .29), command=self.on_website, - **self._common_btn | {'scale': .08})] - self._widgets += [OnscreenText( - _('Special thanks to:\n \1scale\1rdb\2\n \1scale\1Luisa Tenuta\2\n \1scale\1Damiana Ercolani\2'), - pos=(.1, .55), font=self._common['text_font'], - scale=self._common['scale'], fg=self._common['text_fg'], - align=TextNode.A_left)] - self._widgets += [DirectButton( - text=_('Back'), pos=(0, 1, -.8), command=self.on_back, - **self._common_btn)] - self.accept('enforce_resolution', self.enforce_res) - - def on_play(self): - self.destroy() - self._cursor = MouseCursor( - 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), - (.01, .01)) - self._widgets = [] - cmn = self._common_btn.copy() | { - 'frameSize': (-2.4, 2.4, -2.4, 2.4), - 'frameColor': (1, 1, 1, .8), - 'text_scale': .64} - left = - (dx := .8) * (min(4, len(self._scenes)) - 1) / 2 - for i, cls in enumerate(self._scenes): - top = .1 if len(self._scenes) < 5 else .6 - row = 0 if i < 4 else 1 - self._widgets += [DirectButton( - text=cls.name(), pos=(left + dx * (i % 4), 1, top - dx * row), - command=self.start, extraArgs=[cls], text_wordwrap=6, - frameTexture='assets/images/scenes/%s.dds' % cls.__name__, - **cmn)] - for j in range(4): - tnode = self._widgets[-1].component('text%s' % j).textNode - height = - tnode.getLineHeight() / 2 - height += (tnode.get_height() - tnode.get_line_height()) / 2 - self._widgets[-1].component('text%s' % j).set_pos(0, 0, height) - self._widgets += [DirectButton( - text=_('Back'), pos=(0, 1, -.8), command=self.on_back, - **self._common_btn)] - - def start(self, cls): - self._fsm.demand('Scene', cls) - - def on_options(self): - self.destroy() - self._cursor = MouseCursor( - 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), - (.01, .01)) - self._set_options() - - def on_credits(self): - self.destroy() - self._cursor = MouseCursor( - 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), - (.01, .01)) - self._set_credits() - - def _rearrange_width(self): - max_width = 0 - for wdg in self._widgets: - t_n = wdg.component('text0') - u_l = t_n.textNode.get_upper_left_3d() - l_r = t_n.textNode.get_lower_right_3d() - max_width = max(l_r[0] - u_l[0], max_width) - for wdg in self._widgets: - m_w = max_width / 2 + .8 - wdg['frameSize'] = -m_w, m_w, wdg['frameSize'][2], wdg['frameSize'][3] - - def on_language(self, arg): - lang_code = { - _('English'): 'en_EN', - _('Italian'): 'it_IT'}[arg] - self._lang_mgr.set_lang(lang_code) - self._opt_file['settings']['language'] = lang_code[:2] - self._opt_file.store() - self.on_options() - - def on_volume(self): - self._opt_file['settings']['volume'] = self._slider['value'] - self._music.set_volume(self._slider['value']) - - def on_fullscreen(self, arg): - props = WindowProperties() - props.set_fullscreen(arg) - if not self._fun_test: - base.win.request_properties(props) - # if we actually switch to fullscreen during the tests then - # exwm inside qemu can't restore the correct resolution - # i may re-enable this if/when i run the tests onto a - # physical machine - self._opt_file['settings']['fullscreen'] = int(arg) - self._opt_file.store() - - def on_resolution(self, arg): - info('on resolution: %s (%s)' % (arg, self._enforced_res)) - arg = self._enforced_res or arg - info('set resolution: %s' % arg) - props = WindowProperties() - props.set_size(LVector2i(*[int(_res) for _res in arg.split('x')])) - base.win.request_properties(props) - self._opt_file['settings']['resolution'] = arg - self._opt_file.store() - - def on_aa(self, arg): - self._pipeline.msaa_samples = 4 if arg else 1 - debug(f'msaa: {self._pipeline.msaa_samples}') - self._opt_file['settings']['antialiasing'] = int(arg) - self._opt_file.store() - - def on_shadows(self, arg): - self._pipeline.enable_shadows = int(arg) - debug(f'shadows: {self._pipeline.enable_shadows}') - self._opt_file['settings']['shadows'] = int(arg) - self._opt_file.store() - - def on_website(self): - if platform.startswith('linux'): - environ['LD_LIBRARY_PATH'] = '' - system('xdg-open https://www.ya2.it') - else: - open_new_tab('https://www.ya2.it') - - def on_back(self): - self._opt_file.store() - self.destroy() - self._cursor = MouseCursor( - 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), - (.01, .01)) - self._set_main() - - def destroy(self): - [wdg.destroy() for wdg in self._widgets] - self._cursor.destroy() - self.ignore('enforce_resolution') diff --git a/entities/music.py b/entities/music.py deleted file mode 100644 index cf6c0c9..0000000 --- a/entities/music.py +++ /dev/null @@ -1,55 +0,0 @@ -from os.path import dirname, exists, basename -from platform import system -from glob import glob -from pathlib import Path -from random import choice -from logging import info -from panda3d.core import AudioSound, Filename - - -class MusicMgr: - - def __init__(self, volume): - files = self.curr_path + 'assets/audio/music/*.ogg' - self._start_music(glob(files)) - base.musicManager.setVolume(.8 * volume) - base.sfxManagerList[0].setVolume(volume) - taskMgr.add(self._on_frame, 'on frame music') - - @property - def is_appimage(self): - par_path = str(Path(__file__).parent.absolute()) - is_appimage = par_path.startswith('/tmp/.mount_Pmachi') - return is_appimage and par_path.endswith('/usr/bin') - - @property - def curr_path(self): - if system() == 'Windows': - return '' - if exists('main.py'): - return '' - else: - par_path = str(Path(__file__).parent.absolute()) - if self.is_appimage: - par_path = str(Path(par_path).absolute()) - par_path += '/' - return par_path - - def _start_music(self, files): - self._music = loader.load_music(choice(files)) - info('playing music ' + self._music.get_name()) - self._music.play() - - def set_volume(self, volume): - base.musicManager.setVolume(.8 * volume) - base.sfxManagerList[0].setVolume(volume) - - def _on_frame(self, task): - if self._music.status() == AudioSound.READY: - oggs = Filename(self.curr_path + 'assets/audio/music/*.ogg').to_os_specific() - files = glob(oggs) - rm_music = Filename(self.curr_path + 'assets/audio/music/' + basename(self._music.get_name())).to_os_specific() - # basename is needed in windows - files.remove(rm_music) - self._start_music(files) - return task.cont diff --git a/entities/scene.py b/entities/scene.py deleted file mode 100644 index 36c9226..0000000 --- a/entities/scene.py +++ /dev/null @@ -1,587 +0,0 @@ -from os.path import exists -from os import makedirs -from glob import glob -from logging import debug, info -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 -from direct.gui.OnscreenImage import OnscreenImage -from direct.gui.OnscreenText import OnscreenText -from direct.gui.DirectGui import DirectButton, DirectFrame -from direct.gui.DirectGuiGlobals import FLAT, DISABLED, NORMAL -from direct.showbase.DirectObject import DirectObject -from entities.items.background import Background -from entities.sidepanel import SidePanel -from ya2.engine.gui.cursor import MouseCursor -from ya2.lib.p3d.gfx import P3dGfxMgr - - -class Scene(DirectObject): - - def __init__(self, world, exit_cb, auto_close_instr, dbg_items, reload_cb, scenes): - super().__init__() - self._world = world - self._exit_cb = exit_cb - self._dbg_items = dbg_items - self._reload_cb = reload_cb - self._scenes = scenes - self._enforce_res = '' - self.accept('enforce_res', self.enforce_res) - self._set_camera() - self._cursor = MouseCursor( - 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), - (.01, .01)) - self._set_gui() - self._set_lights() - self._set_input() - self._set_mouse_plane() - self.items = [] - self.reset() - self._state = 'init' - self._paused = False - self._item_active = None - if auto_close_instr: - self.__store_state() - self.__restore_state() - else: - self._set_instructions() - self._bg = Background() - self._side_panel = SidePanel(world, self._mouse_plane_node, (-5, 4), (-3, 1), 1, self.items) - self._scene_tsk = taskMgr.add(self.on_frame, 'on_frame') - - @staticmethod - def name(): - return '' - - def _instr_txt(self): - return '' - - def _set_items(self): - self.items = [] - - def screenshot(self, task=None): - tex = Texture('screenshot') - buffer = base.win.make_texture_buffer('screenshot', 512, 512, tex, True ) - cam = base.make_camera(buffer) - cam.reparent_to(render) - cam.node().get_lens().set_fov(base.camLens.get_fov()) - cam.set_pos(0, -20, 0) - cam.look_at(0, 0, 0) - import simplepbr - simplepbr.init( - window=buffer, - camera_node=cam, - use_normal_maps=True, - use_emission_maps=False, - use_occlusion_maps=True, - msaa_samples=4, - enable_shadows=True) - base.graphicsEngine.renderFrame() - base.graphicsEngine.renderFrame() - fname = self.__class__.__name__ - if not exists('assets/images/scenes'): - makedirs('assets/images/scenes') - buffer.save_screenshot('assets/images/scenes/%s.png' % fname) - # img = DirectButton( - # frameTexture=buffer.get_texture(), relief=FLAT, - # frameSize=(-.2, .2, -.2, .2)) - return buffer.get_texture() - - def current_bottom(self): - curr_bottom = 1 - for item in self.items: - if item.repos_done: - curr_bottom = min(curr_bottom, item.get_bottom()) - return curr_bottom - - def reset(self): - [itm.destroy() for itm in self.items] - self._set_items() - self._state = 'init' - self._commands = [] - self._command_idx = 0 - if hasattr(self, '_success_txt'): - self._success_txt.destroy() - del self._success_txt - self.__right_btn['state'] = NORMAL - - def enforce_res(self, val): - self._enforce_res = val - info('enforce res: ' + val) - - def destroy(self): - self.ignore('enforce_res') - self._unset_gui() - self._unset_lights() - self._unset_input() - self._unset_mouse_plane() - [itm.destroy() for itm in self.items] - self._bg.destroy() - self._side_panel.destroy() - self._cursor.destroy() - taskMgr.remove(self._scene_tsk) - if hasattr(self, '_success_txt'): - self._success_txt.destroy() - - def _set_camera(self): - base.camera.set_pos(0, -20, 0) - base.camera.look_at(0, 0, 0) - - def __load_img_btn(self, path, col): - img = OnscreenImage('assets/images/buttons/%s.dds' % path) - img.set_transparency(True) - img.set_color(col) - img.detach_node() - return img - - def _set_gui(self): - def load_images_btn(path, col): - colors = { - 'gray': [ - (.6, .6, .6, 1), # ready - (1, 1, 1, 1), # press - (.8, .8, .8, 1), # rollover - (.4, .4, .4, .4)], - 'green': [ - (.1, .68, .1, 1), - (.1, 1, .1, 1), - (.1, .84, .1, 1), - (.4, .1, .1, .4)]}[col] - return [self.__load_img_btn(path, col) for col in colors] - abl, abr = base.a2dBottomLeft, base.a2dBottomRight - btn_info = [ - ('home', self.on_home, NORMAL, abl, 'gray'), - ('information', self._set_instructions, NORMAL, abl, 'gray'), - ('right', self.on_play, NORMAL, abr, 'green'), - #('next', self.on_next, DISABLED, abr, 'gray'), - #('previous', self.on_prev, DISABLED, abr, 'gray'), - #('rewind', self.reset, NORMAL, abr, 'gray') - ] - num_l = num_r = 0 - btns = [] - for binfo in btn_info: - imgs = load_images_btn(binfo[0], binfo[4]) - if binfo[3] == base.a2dBottomLeft: - sign, num = 1, num_l - num_l += 1 - else: - sign, num = -1, num_r - num_r += 1 - fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) - btn = DirectButton( - image=imgs, scale=.05, pos=(sign * (.06 + .11 * num), 1, .06), - parent=binfo[3], command=binfo[1], state=binfo[2], relief=FLAT, - frameColor=fcols[0] if binfo[2] == NORMAL else fcols[1], - rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'), - clickSound=loader.load_sfx('assets/audio/sfx/click.ogg')) - btn.set_transparency(True) - btns += [btn] - self.__home_btn, self.__info_btn, self.__right_btn = btns - # , self.__next_btn, self.__prev_btn, self.__rewind_btn - if self._dbg_items: - self._info_txt = OnscreenText( - '', parent=base.a2dTopRight, scale=0.04, - pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right) - - def _unset_gui(self): - btns = [ - self.__home_btn, self.__info_btn, self.__right_btn, - #self.__next_btn, self.__prev_btn, self.__rewind_btn - ] - [btn.destroy() for btn in btns] - if self._dbg_items: - self._info_txt.destroy() - - def _set_spotlight(self, name, pos, look_at, color, shadows=False): - light = Spotlight(name) - if shadows: - light.setLens(PerspectiveLens()) - light_np = render.attach_new_node(light) - light_np.set_pos(pos) - light_np.look_at(look_at) - light.set_color(color) - render.set_light(light_np) - return light_np - - def _set_lights(self): - alight = AmbientLight('alight') # for ao - alight.set_color((.15, .15, .15, 1)) - self._alnp = render.attach_new_node(alight) - render.set_light(self._alnp) - self._key_light = self._set_spotlight( - 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1)) - self._shadow_light = self._set_spotlight( - 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True) - self._shadow_light.node().set_shadow_caster(True, 2048, 2048) - self._shadow_light.node().get_lens().set_film_size(2048, 2048) - self._shadow_light.node().get_lens().set_near_far(1, 256) - self._shadow_light.node().set_camera_mask(BitMask32(0x01)) - - def _unset_lights(self): - for light in [self._alnp, self._key_light, self._shadow_light]: - render.clear_light(light) - light.remove_node() - - def _set_input(self): - self.accept('mouse1', self.on_click_l) - self.accept('mouse1-up', self.on_release) - self.accept('mouse3', self.on_click_r) - self.accept('mouse3-up', self.on_release) - - def _unset_input(self): - for evt in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']: - self.ignore(evt) - - def _set_mouse_plane(self): - shape = BulletPlaneShape((0, -1, 0), 0) - #self._mouse_plane_node = BulletRigidBodyNode('mouse plane') - self._mouse_plane_node = BulletGhostNode('mouse plane') - self._mouse_plane_node.addShape(shape) - #np = render.attachNewNode(self._mouse_plane_node) - #self._world.attachRigidBody(self._mouse_plane_node) - self._world.attach_ghost(self._mouse_plane_node) - - def _unset_mouse_plane(self): - self._world.remove_ghost(self._mouse_plane_node) - - def _get_hits(self): - if not base.mouseWatcherNode.has_mouse(): return [] - p_from, p_to = P3dGfxMgr.world_from_to(base.mouseWatcherNode.get_mouse()) - return self._world.ray_test_all(p_from, p_to).get_hits() - - def _update_info(self, item): - txt = '' - if item: - txt = '%.3f %.3f\n%.3f°' % ( - item._np.get_x(), item._np.get_z(), item._np.get_r()) - self._info_txt['text'] = txt - - def _on_click(self, method): - if self._paused: - return - for hit in self._get_hits(): - if hit.get_node() == self._mouse_plane_node: - pos = hit.get_hit_pos() - for hit in self._get_hits(): - for item in [i for i in self.items if hit.get_node() == i.node and i.interactable]: - if not self._item_active: - self._item_active = item - getattr(item, method)(pos) - img = 'move' if method == 'on_click_l' else 'rotate' - if not (img == 'rotate' and not item._instantiated): - self._cursor.set_image('assets/images/buttons/%s.dds' % img) - - def on_click_l(self): - self._on_click('on_click_l') - - def on_click_r(self): - self._on_click('on_click_r') - - def on_release(self): - if self._item_active and not self._item_active._first_command: - self._commands = self._commands[:self._command_idx] - self._commands += [self._item_active] - self._command_idx += 1 - #self.__prev_btn['state'] = NORMAL - #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) - #self.__prev_btn['frameColor'] = fcols[0] - #if self._item_active._command_idx == len(self._item_active._commands) - 1: - # self.__next_btn['state'] = DISABLED - # self.__next_btn['frameColor'] = fcols[1] - self._item_active = None - [item.on_release() for item in self.items] - self._cursor.set_image('assets/images/buttons/arrowUpLeft.dds') - - def repos(self): - for item in self.items: - item.repos_done = False - self.items = sorted(self.items, key=lambda itm: itm.__class__.__name__) - [item.on_aspect_ratio_changed() for item in self.items] - self._side_panel.update(self.items) - max_x = -float('inf') - for item in self.items: - if not item._instantiated: - max_x = max(item._np.get_x(), max_x) - for item in self.items: - if not item._instantiated: - item.repos_x(max_x) - - def on_aspect_ratio_changed(self): - self.repos() - - def _win_condition(self): - pass - - def _fail_condition(self): - return all(itm.fail_condition() for itm in self.items) and not self._paused and self._state == 'playing' - - def on_frame(self, task): - hits = self._get_hits() - pos = None - for hit in self._get_hits(): - if hit.get_node() == self._mouse_plane_node: - pos = hit.get_hit_pos() - hit_nodes = [hit.get_node() for hit in hits] - if self._item_active: - items_hit = [self._item_active] - else: - items_hit = [itm for itm in self.items if itm.node in hit_nodes] - items_no_hit = [itm for itm in self.items if itm not in items_hit] - [itm.on_mouse_on() for itm in items_hit] - [itm.on_mouse_off() for itm in items_no_hit] - if pos and self._item_active: - self._item_active.on_mouse_move(pos) - if self._dbg_items: - self._update_info(items_hit[0] if items_hit else None) - if self._win_condition(): - self._set_fail() if self._enforce_res == 'fail' else self._set_win() - elif self._state == 'playing' and self._fail_condition(): - self._set_win() if self._enforce_res == 'win' else self._set_fail() - if any(itm._overlapping for itm in self.items): - self._cursor.cursor_img.img.set_color(.9, .1, .1, 1) - else: - self._cursor.cursor_img.img.set_color(.9, .9, .9, 1) - return task.cont - - def cb_inst(self, item): - self.items += [item] - - def on_play(self): - self._state = 'playing' - #self.__prev_btn['state'] = DISABLED - #self.__next_btn['state'] = DISABLED - self.__right_btn['state'] = DISABLED - [itm.play() for itm in self.items] - - def on_next(self): - self._commands[self._command_idx].redo() - self._command_idx += 1 - fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) - #self.__prev_btn['state'] = NORMAL - #self.__prev_btn['frameColor'] = fcols[0] - more_commands = self._command_idx < len(self._commands) - #self.__next_btn['state'] = NORMAL if more_commands else DISABLED - #self.__next_btn['frameColor'] = fcols[0] if more_commands else fcols[1] - - def on_prev(self): - self._command_idx -= 1 - self._commands[self._command_idx].undo() - fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) - #self.__next_btn['state'] = NORMAL - #self.__next_btn['frameColor'] = fcols[0] - #self.__prev_btn['state'] = NORMAL if self._command_idx else DISABLED - #self.__prev_btn['frameColor'] = fcols[0] if self._command_idx else fcols[1] - - def on_home(self): - self._exit_cb() - - def _set_instructions(self): - self._paused = True - self.__store_state() - mgr = TextPropertiesManager.get_global_ptr() - for name in ['mouse_l', 'mouse_r']: - graphic = OnscreenImage('assets/images/buttons/%s.dds' % name) - graphic.set_scale(.5) - graphic.get_texture().set_minfilter(Texture.FTLinearMipmapLinear) - graphic.get_texture().set_anisotropic_degree(2) - mgr.set_graphic(name, graphic) - graphic.set_z(-.2) - graphic.set_transparency(True) - graphic.detach_node() - 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( - self._instr_txt(), parent=frm, font=font, scale=0.06, - fg=(.9, .9, .9, 1), align=TextNode.A_left) - 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'] = -w / 2, 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('exitRight', col) for col in colors] - btn = DirectButton( - image=imgs, scale=btn_scale, - pos=(l_r[0] - btn_scale, 1, l_r[2] - mar - btn_scale), - parent=frm, command=self.__on_close_instructions, 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) - - def _set_win(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) - enabled = self._scenes.index(self.__class__) < len(self._scenes) - 1 - if enabled: - next_scene = self._scenes[self._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 _set_fail(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 have failed!'), - 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) - - 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, - #self.__next_btn, self.__prev_btn, self.__rewind_btn - ] - self.__btn_state = [btn['state'] for btn in btns] - for btn in btns: - btn['state'] = DISABLED - [itm.store_state() for itm in self.items] - - def __restore_state(self): - btns = [ - self.__home_btn, self.__info_btn, self.__right_btn, - #self.__next_btn, self.__prev_btn, self.__rewind_btn - ] - for btn, state in zip(btns, self.__btn_state): - btn['state'] = state - [itm.restore_state() for itm in self.items] - self._paused = False - - def __on_close_instructions(self, frm): - frm.remove_node() - self.__restore_state() diff --git a/entities/scenes/__init__.py b/entities/scenes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/entities/scenes/scene_basketball.py b/entities/scenes/scene_basketball.py deleted file mode 100644 index cba2972..0000000 --- a/entities/scenes/scene_basketball.py +++ /dev/null @@ -1,59 +0,0 @@ -from entities.scene import Scene -from entities.items.box import Box -from entities.items.shelf import Shelf -from entities.items.domino import Domino, UpStrategy, DownStrategy -from entities.items.basketball import Basketball -from entities.items.teetertooter import TeeterTooter - - -class SceneBasketBall(Scene): - - @staticmethod - def name(): - return _('Basket ball') - - def _set_items(self): - self.items = [] - #self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=5, count=2)] - #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] - self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=1)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, .21))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, .21))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-4.45, 0, -3.18), r=27, restitution=1)] - #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-5.45, 0, -3.18), restitution=1)] - #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 30)) - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, .73), r=37)] - #self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, .78))] - #self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, .78))] - #self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, .78))] - #self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, .78))] - #self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) - #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - - def _instr_txt(self): - txt = _('Scene: ') + self.name() + '\n\n' - txt += _('Goal: you must hit every domino piece\n\n') - txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' - 'keep \5mouse_r\5 pressed to rotate an item') - return txt - - def _win_condition(self): - return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/entities/scenes/scene_box.py b/entities/scenes/scene_box.py deleted file mode 100644 index 0617659..0000000 --- a/entities/scenes/scene_box.py +++ /dev/null @@ -1,43 +0,0 @@ -from entities.scene import Scene -from entities.items.box import Box, HitStrategy -from entities.items.shelf import Shelf -from entities.items.domino import Domino -from entities.items.basketball import Basketball -from entities.items.teetertooter import TeeterTooter - - -class SceneBox(Scene): - - @staticmethod - def name(): - return _('Box') - - def _set_items(self): - self.items = [] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.46, 0, -3.95))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(4.43, 0, -3.95))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.29, 0, .26), r=28.45)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(2.15, 0, -1.49), r=28.45)] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-1.55, 0, 1.23), friction=.4)] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(4.38, 0, -3.35))] - self.items[-1].set_strategy(HitStrategy(self.items[-2], self.items[-1].node, self.items[-1]._world)) - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=2)] - #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-1.14, 0, -.04), tgt_degrees=60)] - #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.49, 0, -.04), tgt_degrees=60)] - #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.94, 0, -.04), tgt_degrees=60)] - #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.55, 0, -.04), tgt_degrees=60)] - #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.09, 0, -.04), tgt_degrees=88)] - #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - - def _instr_txt(self): - txt = _('Scene: ') + self.name() + '\n\n' - txt += _('Goal: the left box must hit the right box\n\n') - txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' - 'keep \5mouse_r\5 pressed to rotate an item') - return txt - - def _win_condition(self): - return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/entities/scenes/scene_domino.py b/entities/scenes/scene_domino.py deleted file mode 100644 index 1d5dec9..0000000 --- a/entities/scenes/scene_domino.py +++ /dev/null @@ -1,43 +0,0 @@ -from entities.scene import Scene -from entities.items.box import Box -from entities.items.shelf import Shelf -from entities.items.domino import Domino, DownStrategy -from entities.items.basketball import Basketball -from entities.items.teetertooter import TeeterTooter - - -class SceneDomino(Scene): - - @staticmethod - def name(): - return _('Domino') - - def _set_items(self): - self.items = [] - #self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.2, 0, -.6))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.2, 0, -.6))] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=2)] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-1.14, 0, -.04))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.49, 0, -.04))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.94, 0, -.04))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.55, 0, -.04))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.09, 0, -.04))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 88)) - #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - - def _instr_txt(self): - txt = _('Scene: ') + self.name() + '\n\n' - txt += _('Goal: every domino piece must fall\n\n') - txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' - 'keep \5mouse_r\5 pressed to rotate an item') - return txt - - def _win_condition(self): - return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/entities/scenes/scene_domino_box.py b/entities/scenes/scene_domino_box.py deleted file mode 100644 index 915a309..0000000 --- a/entities/scenes/scene_domino_box.py +++ /dev/null @@ -1,56 +0,0 @@ -from entities.scene import Scene -from entities.items.box import Box -from entities.items.shelf import Shelf -from entities.items.domino import Domino, UpStrategy, DownStrategy -from entities.items.basketball import Basketball -from entities.items.teetertooter import TeeterTooter - - -class SceneDominoBox(Scene): - - @staticmethod - def name(): - return _('Domino and box') - - def _set_items(self): - self.items = [] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=5, count=2)] - #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, .21))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, .21))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, -.94), r=37)] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, -.89))] - self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, .73), r=37)] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, .78))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, .78))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, .78))] - self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, .78))] - self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) - #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - - def _instr_txt(self): - txt = _('Scene: ') + self.name() + '\n\n' - txt += _('Goal: only the last piece of each row must be up\n\n') - txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' - 'keep \5mouse_r\5 pressed to rotate an item') - return txt - - def _win_condition(self): - return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/entities/scenes/scene_domino_box_basketball.py b/entities/scenes/scene_domino_box_basketball.py deleted file mode 100644 index ccd0e46..0000000 --- a/entities/scenes/scene_domino_box_basketball.py +++ /dev/null @@ -1,45 +0,0 @@ -from entities.scene import Scene -from entities.items.box import Box -from entities.items.shelf import Shelf -from entities.items.domino import Domino, UpStrategy, DownStrategy -from entities.items.basketball import Basketball -from entities.items.teetertooter import TeeterTooter - - -class SceneDominoBoxBasketball(Scene): - - @staticmethod - def name(): - return _('Domino, box and basket ball') - - def _set_items(self): - self.items = [] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=1, mass=5)] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=1)] - self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.3, 1, 2.5))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] - #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.68, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.35, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(3.08, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(3.78, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(4.53, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - - def _instr_txt(self): - txt = _('Scene: ') + self.name() + '\n\n' - txt += _('Goal: every domino piece must be hit\n\n') - txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' - 'keep \5mouse_r\5 pressed to rotate an item') - return txt - - def _win_condition(self): - return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/entities/scenes/scene_teeter_domino_box_basketball.py b/entities/scenes/scene_teeter_domino_box_basketball.py deleted file mode 100644 index 34194af..0000000 --- a/entities/scenes/scene_teeter_domino_box_basketball.py +++ /dev/null @@ -1,55 +0,0 @@ -from entities.scene import Scene -from entities.items.box import Box -from entities.items.shelf import Shelf -from entities.items.domino import Domino, UpStrategy, DownStrategy -from entities.items.basketball import Basketball -from entities.items.teetertooter import TeeterTooter - - -class SceneTeeterDominoBoxBasketball(Scene): - - @staticmethod - def name(): - return _('Teeter tooter, domino, box and basket ball') - - def _set_items(self): - self.items = [] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=3, count=2, friction=1)] - self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(.98, 1, 1.02))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-6.24, 0, -1.45))] - self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-6.24, 0, -1.20))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, r=24.60, friction=1, pos=(-6.15, 0, -.93))] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.3, friction=1, model_scale=.5, pos=(-5.38, 0, -.93), r=24.60)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(5.37, 0, -.78))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(7.48, 0, -.78))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(4.74, 0, -1.95))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(6.88, 0, -1.95))] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, pos=(4.83, 0, -1.39))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, pos=(5.67, 0, -1.39))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, pos=(6.59, 0, -1.39))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.53, 0, -1.95), restitution=.95)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(2.63, 0, -1.95), restitution=.95)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-3.65, 0, 1.05), r=28, friction=0)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.27, 0, 1.72), restitution=.95)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.88, 0, 1.72), restitution=.95)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.67, 0, .55), restitution=.95)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.52, 0, .55), restitution=.95)] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.5, pos=(-1.73, 0, 1.11))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.5, pos=(-.97, 0, 1.11))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.5, pos=(-.1, 0, 1.11))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - - def _instr_txt(self): - txt = _('Scene: ') + self.name() + '\n\n' - txt += _('Goal: every domino piece must be hit\n\n') - txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' - 'keep \5mouse_r\5 pressed to rotate an item') - return txt - - def _win_condition(self): - return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/entities/scenes/scene_teeter_tooter.py b/entities/scenes/scene_teeter_tooter.py deleted file mode 100644 index 0784be2..0000000 --- a/entities/scenes/scene_teeter_tooter.py +++ /dev/null @@ -1,46 +0,0 @@ -from entities.scene import Scene -from entities.items.box import Box -from entities.items.shelf import Shelf -from entities.items.domino import Domino, UpStrategy, DownStrategy -from entities.items.basketball import Basketball -from entities.items.teetertooter import TeeterTooter - - -class SceneTeeterTooter(Scene): - - @staticmethod - def name(): - return _('Teeter tooter') - - def _set_items(self): - self.items = [] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=3, count=1, friction=1)] - #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=5, count=2)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-2.76, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(2.27, 0, -.28))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(4.38, 0, -.28))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] - self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-2.74, 0, -1.20))] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, r=-25.30, friction=1, pos=(-2.78, 0, -.93))] - self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.2, friction=1, model_scale=.5, pos=(-3.61, 0, -.99), r=-25.30)] - self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.25, 0, -.57), r=52)] - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(3.50, 0, -.89))] - self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) - #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] - - def _instr_txt(self): - txt = _('Scene: ') + self.name() + '\n\n' - txt += _('Goal: you must hit every domino piece\n\n') - txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' - 'keep \5mouse_r\5 pressed to rotate an item') - return txt - - def _win_condition(self): - return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/entities/sidepanel.py b/entities/sidepanel.py deleted file mode 100644 index ee59d7d..0000000 --- a/entities/sidepanel.py +++ /dev/null @@ -1,132 +0,0 @@ -from textwrap import dedent -from panda3d.core import GeomVertexData, GeomVertexFormat, Geom, \ - GeomVertexWriter, GeomTriangles, GeomNode, Shader, Point3, Plane, Vec3 -from ya2.lib.p3d.gfx import P3dGfxMgr - - -class SidePanel: - - def __init__(self, world, plane_node, top_l, bottom_r, y, items): - self._world = world - self._plane_node = plane_node - self._set((-1, 1), y) - self.update(items) - - def update(self, items): - p_from, p_to = P3dGfxMgr.world_from_to((-1, 1)) - for hit in self._world.ray_test_all(p_from, p_to).get_hits(): - if hit.get_node() == self._plane_node: - pos = hit.get_hit_pos() - y = 0 - corner = -20, 20 - for item in items: - if not item._instantiated: - bounds = item._np.get_tight_bounds() - if bounds[1][1] > y: - y = bounds[1][1] - icorner = item.get_corner() - icorner = P3dGfxMgr.screen_coord(icorner) - if icorner[0] > corner[0]: - corner = icorner[0], corner[1] - if icorner[1] < corner[1]: - corner = corner[0], icorner[1] - self._set((pos[0], pos[2]), y) - bounds = self._np.get_tight_bounds() - corner3d = bounds[1][0], bounds[1][1], bounds[0][2] - corner2d = P3dGfxMgr.screen_coord(corner3d) - plane = Plane(Vec3(0, 1, 0), y) - pos3d, near_pt, far_pt = Point3(), Point3(), Point3() - ar, margin = base.get_aspect_ratio(), .04 - x = corner[0] / ar if ar >= 1 else corner[0] - z = corner[1] * ar if ar < 1 else corner[1] - x += margin / ar if ar >= 1 else margin - z -= margin * ar if ar < 1 else margin - base.camLens.extrude((x, z), near_pt, far_pt) - plane.intersects_line( - pos3d, render.get_relative_point(base.camera, near_pt), - render.get_relative_point(base.camera, far_pt)) - corner_pos3d, near_pt, far_pt = Point3(), Point3(), Point3() - base.camLens.extrude((-1, 1), near_pt, far_pt) - plane.intersects_line( - corner_pos3d, render.get_relative_point(base.camera, near_pt), - render.get_relative_point(base.camera, far_pt)) - self._np.set_pos((pos3d + corner_pos3d) / 2) - scale = Vec3(pos3d[0] - corner_pos3d[0], 1, corner_pos3d[2] - pos3d[2]) - self._np.set_scale(scale) - - def _set(self, pos, y): - if hasattr(self, '_np'): - self._np.remove_node() - vdata = GeomVertexData('quad', GeomVertexFormat.get_v3(), Geom.UHStatic) - vdata.setNumRows(2) - vertex = GeomVertexWriter(vdata, 'vertex') - vertex.add_data3(.5, 0, -.5) - vertex.add_data3(.5, 0, .5) - vertex.add_data3(-.5, 0, .5) - vertex.add_data3(-.5, 0, -.5) - prim = GeomTriangles(Geom.UHStatic) - prim.add_vertices(0, 1, 2) - prim.add_vertices(0, 2, 3) - prim.close_primitive() - geom = Geom(vdata) - geom.add_primitive(prim) - node = GeomNode('gnode') - node.add_geom(geom) - self._np = render.attach_new_node(node) - self._np.setTransparency(True) - self._np.set_pos(pos[0], y, pos[1]) - vert = '''\ - #version 130 - uniform mat4 p3d_ModelViewProjectionMatrix; - in vec4 p3d_Vertex; - void main() { - gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; }''' - frag = '''\ - #version 130 - out vec4 p3d_FragColor; - void main() { - p3d_FragColor = vec4(.04, .04, .04, .08); }''' - self._np.set_shader(Shader.make(Shader.SL_GLSL, dedent(vert), dedent(frag))) - # mat = Material() - # mat.set_base_color((1, 1, 1, 1)) - # mat.set_emission((1, 1, 1, 1)) - # mat.set_metallic(.5) - # mat.set_roughness(.5) - # np.set_material(mat) - # texture_sz = 64 - # base_color_pnm = PNMImage(texture_sz, texture_sz) - # base_color_pnm.fill(.1, .1, .1) - # base_color_pnm.add_alpha() - # base_color_pnm.alpha_fill(.04) - # base_color_tex = Texture('base color') - # base_color_tex.load(base_color_pnm) - # ts = TextureStage('base color') - # ts.set_mode(TextureStage.M_modulate) - # np.set_texture(ts, base_color_tex) - # emission_pnm = PNMImage(texture_sz, texture_sz) - # emission_pnm.fill(0, 0, 0) - # emission_tex = Texture('emission') - # emission_tex.load(emission_pnm) - # ts = TextureStage('emission') - # ts.set_mode(TextureStage.M_emission) - # np.set_texture(ts, emission_tex) - # metal_rough_pnm = PNMImage(texture_sz, texture_sz) - # ambient_occlusion = 1 - # roughness = .5 - # metallicity = .5 - # metal_rough_pnm.fill(ambient_occlusion, roughness, metallicity) - # metal_rough_tex = Texture('ao metal roughness') - # metal_rough_tex.load(metal_rough_pnm) - # ts = TextureStage('ao metal roughness') - # ts.set_mode(TextureStage.M_selector) - # np.set_texture(ts, metal_rough_tex) - # normal_pnm = PNMImage(texture_sz, texture_sz) - # normal_pnm.fill(.5, .5, .1) - # normal_tex = Texture('normals') - # normal_tex.load(normal_pnm) - # ts = TextureStage('normals') - # ts.set_mode(TextureStage.M_normal) - # np.set_texture(ts, normal_tex) - - def destroy(self): - self._np.remove_node() diff --git a/game/__init__.py b/game/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/game/app.py b/game/app.py new file mode 100755 index 0000000..ac75b5c --- /dev/null +++ b/game/app.py @@ -0,0 +1,277 @@ +import argparse +import simplepbr +#import gltf +from glob import glob +from importlib import import_module +from inspect import isclass +from sys import platform, argv, exit +from logging import info, debug +from os.path import exists +from os import makedirs +from panda3d.core import Filename, load_prc_file_data, AntialiasAttrib, \ + Texture, WindowProperties, LVector2i, TextNode +from panda3d.bullet import BulletWorld, BulletDebugNode +from direct.showbase.ShowBase import ShowBase +from direct.gui.OnscreenText import OnscreenText +from direct.fsm.FSM import FSM +from game.music import MusicMgr +from game.items.background import Background +from game.menu import Menu +from game.scene import Scene +from game.scenes.scene_basketball import SceneBasketBall +from game.scenes.scene_box import SceneBox +from game.scenes.scene_domino_box_basketball import SceneDominoBoxBasketball +from game.scenes.scene_domino_box import SceneDominoBox +from game.scenes.scene_domino import SceneDomino +from game.scenes.scene_teeter_domino_box_basketball import SceneTeeterDominoBoxBasketball +from game.scenes.scene_teeter_tooter import SceneTeeterTooter +from ya2.dictfile import DctFile +from ya2.lib.p3d.p3d import LibP3d +from ya2.engine.lang import LangMgr +from ya2.engine.log import LogMgr +from ya2.engine.functional import FunctionalTest + + +class MainFsm(FSM): + + def __init__(self, pmachines): + super().__init__('Main FSM') + self._pmachines = pmachines + + def enterMenu(self): + self._pmachines.on_menu_enter() + + def exitMenu(self): + self._pmachines.on_menu_exit() + + def enterScene(self, cls): + self._pmachines.on_scene_enter(cls) + + def exitScene(self): + self._pmachines.on_scene_exit() + + +class PmachinesApp: + + scenes = [ + SceneDomino, + SceneBox, + SceneDominoBox, + SceneBasketBall, + SceneDominoBoxBasketball, + SceneTeeterTooter, + SceneTeeterDominoBoxBasketball] + + def __init__(self): + info('platform: %s' % platform) + info('exists main.py: %s' % exists('main.py')) + self._args = args = self._parse_args() + self._configure(args) + self.base = ShowBase() + self._pipeline = None + self.updating = args.update + self.version = args.version + self.log_mgr = LogMgr.init_cls()(self) + self._prepare_window(args) + if args.update: + return + if args.functional_test: + self._options['settings']['volume'] = 0 + self._music = MusicMgr(self._options['settings']['volume']) + self.lang_mgr = LangMgr(self._options['settings']['language'], + 'pmachines', + 'assets/locale/') + self._fsm = MainFsm(self) + if args.screenshot: + cls = [cls for cls in self.scenes if cls.__name__ == args.screenshot][0] + scene = cls(BulletWorld(), None, True, False, lambda: None, self.scenes) + scene.screenshot() + scene.destroy() + exit() + elif self._options['development']['auto_start']: + mod_name = 'game.scenes.scene_' + self._options['development']['auto_start'] + for member in import_module(mod_name).__dict__.values(): + if isclass(member) and issubclass(member, Scene) and \ + member != Scene: + cls = member + self._fsm.demand('Scene', cls) + else: + self._fsm.demand('Menu') + if args.functional_test or args.functional_ref: + FunctionalTest(args.functional_ref) + + def on_menu_enter(self): + self._menu_bg = Background() + self._menu = Menu( + self._fsm, self.lang_mgr, self._options, self._music, + self._pipeline, self.scenes, self._args.functional_test or self._args.functional_ref) + + def on_home(self): + self._fsm.demand('Menu') + + def on_menu_exit(self): + self._menu_bg.destroy() + self._menu.destroy() + + def on_scene_enter(self, cls): + self._set_physics() + self._scene = cls( + self.world, self.on_home, + self._options['development']['auto_close_instructions'], + self._options['development']['debug_items'], + self.reload, + self.scenes) + + 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') + if args.functional_test or args.functional_ref: + load_prc_file_data('', 'win-size 1360 768') + # otherwise it is not centered in exwm + # load_prc_file_data('', 'threading-model Cull/Draw') + # it freezes when you go to the next scene + if args.screenshot: + load_prc_file_data('', 'window-type offscreen') + load_prc_file_data('', 'audio-library-name null') + + def _parse_args(self): + parser = argparse.ArgumentParser() + parser.add_argument('--update', action='store_true') + parser.add_argument('--version', action='store_true') + parser.add_argument('--optfile') + parser.add_argument('--screenshot') + parser.add_argument('--functional-test', action='store_true') + parser.add_argument('--functional-ref', action='store_true') + cmd_line = [arg for arg in iter(argv[1:]) if not arg.startswith('-psn_')] + args = parser.parse_args(cmd_line) + return args + + def _prepare_window(self, args): + data_path = '' + if (platform.startswith('win') or platform.startswith('linux')) and ( + not exists('main.py') or __file__.startswith('/app/bin/')): + # it is the deployed version for windows + data_path = str(Filename.get_user_appdata_directory()) + '/pmachines' + home = '/home/flavio' # we must force this for wine + if data_path.startswith('/c/users/') and exists(home + '/.wine/'): + data_path = home + '/.wine/drive_' + data_path[1:] + info('creating dirs: %s' % data_path) + makedirs(data_path, exist_ok=True) + optfile = args.optfile if args.optfile else 'options.ini' + info('data path: %s' % data_path) + info('option file: %s' % optfile) + info('fixed path: %s' % LibP3d.fixpath(data_path + '/' + optfile)) + default_opt = { + 'settings': { + 'volume': 1, + 'language': 'en', + 'fullscreen': 1, + 'resolution': '', + 'antialiasing': 1, + 'shadows': 1}, + 'development': { + 'simplepbr': 1, + 'verbose_log': 0, + 'physics_debug': 0, + 'auto_start': 0, + 'auto_close_instructions': 0, + 'show_buffers': 0, + 'debug_items': 0, + 'mouse_coords': 0, + 'fps': 0}} + opt_path = LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile + opt_exists = exists(opt_path) + self._options = DctFile( + LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile, + default_opt) + if not opt_exists: + self._options.store() + res = self._options['settings']['resolution'] + if res: + res = LVector2i(*[int(_res) for _res in res.split('x')]) + else: + resolutions = [] + if not self.version: + d_i = base.pipe.get_display_information() + def _res(idx): + return d_i.get_display_mode_width(idx), \ + d_i.get_display_mode_height(idx) + resolutions = [ + _res(idx) for idx in range(d_i.get_total_display_modes())] + res = sorted(resolutions)[-1] + fullscreen = self._options['settings']['fullscreen'] + props = WindowProperties() + if args.functional_test or args.functional_ref: + fullscreen = False + else: + props.set_size(res) + props.set_fullscreen(fullscreen) + props.set_icon_filename('assets/images/icon/pmachines.ico') + if not args.screenshot and not self.version: + base.win.request_properties(props) + #gltf.patch_loader(base.loader) + if self._options['development']['simplepbr'] and not self.version: + self._pipeline = simplepbr.init( + use_normal_maps=True, + use_emission_maps=False, + use_occlusion_maps=True, + msaa_samples=4 if self._options['settings']['antialiasing'] else 1, + enable_shadows=int(self._options['settings']['shadows'])) + debug(f'msaa: {self._pipeline.msaa_samples}') + debug(f'shadows: {self._pipeline.enable_shadows}') + render.setAntialias(AntialiasAttrib.MAuto) + self.base.set_background_color(0, 0, 0, 1) + self.base.disable_mouse() + if self._options['development']['show_buffers']: + base.bufferViewer.toggleEnable() + if self._options['development']['fps']: + base.set_frame_rate_meter(True) + #self.base.accept('window-event', self._on_win_evt) + self.base.accept('aspectRatioChanged', self._on_aspect_ratio_changed) + if self._options['development']['mouse_coords']: + coords_txt = OnscreenText( + '', parent=base.a2dTopRight, scale=0.04, + pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right) + def update_coords(task): + txt = '%s %s' % (int(base.win.get_pointer(0).x), + int(base.win.get_pointer(0).y)) + coords_txt['text'] = txt + return task.cont + taskMgr.add(update_coords, 'update_coords') + + def _set_physics(self): + if self._options['development']['physics_debug']: + debug_node = BulletDebugNode('Debug') + debug_node.show_wireframe(True) + debug_node.show_constraints(True) + debug_node.show_bounding_boxes(True) + debug_node.show_normals(True) + self._debug_np = render.attach_new_node(debug_node) + self._debug_np.show() + self.world = BulletWorld() + self.world.set_gravity((0, 0, -9.81)) + if self._options['development']['physics_debug']: + self.world.set_debug_node(self._debug_np.node()) + def update(task): + dt = globalClock.get_dt() + self.world.do_physics(dt, 10, 1/180) + return task.cont + self._phys_tsk = taskMgr.add(update, 'update') + + def _unset_physics(self): + if self._options['development']['physics_debug']: + self._debug_np.remove_node() + self.world = None + taskMgr.remove(self._phys_tsk) + + def _on_aspect_ratio_changed(self): + if self._fsm.state == 'Scene': + self._scene.on_aspect_ratio_changed() diff --git a/game/items/__init__.py b/game/items/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/game/items/background.py b/game/items/background.py new file mode 100644 index 0000000..2d83fc0 --- /dev/null +++ b/game/items/background.py @@ -0,0 +1,26 @@ +from itertools import product +from panda3d.core import NodePath +from ya2.lib.p3d.gfx import set_srgb + + +class Background: + + def __init__(self): + self._root = NodePath('background_root') + self._root.reparent_to(render) + ncols, nrows = 16, 8 + start_size, end_size = 5, 2.5 + offset = 5 + for col, row in product(range(ncols), range(nrows)): + model = loader.load_model('assets/models/bam/background/background.bam') + model.set_scale(end_size / start_size) + model.reparent_to(self._root) + total_width, total_height = end_size * ncols, end_size * nrows + left, bottom = -total_width/2, -total_height/2 + model.set_pos(left + end_size * col, offset, bottom + end_size * row) + self._root.clear_model_nodes() + self._root.flatten_strong() + set_srgb(self._root) + + def destroy(self): + self._root.remove_node() diff --git a/game/items/basketball.py b/game/items/basketball.py new file mode 100644 index 0000000..b27130a --- /dev/null +++ b/game/items/basketball.py @@ -0,0 +1,11 @@ +from panda3d.bullet import BulletSphereShape, BulletRigidBodyNode, BulletGhostNode +from game.items.item import Item + + +class Basketball(Item): + + def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.92, friction=.6): + super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/basketball/basketball.bam', .4, mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction) + + def _set_shape(self, apply_scale=True): + self.node.add_shape(BulletSphereShape(1)) diff --git a/game/items/box.py b/game/items/box.py new file mode 100644 index 0000000..0687b56 --- /dev/null +++ b/game/items/box.py @@ -0,0 +1,25 @@ +from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode +from game.items.item import Item + + +class Box(Item): + + def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.8, model_scale=1): + super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/box/box.bam', mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction, model_scale=model_scale) + + def _set_shape(self, apply_scale=True): + self.node.add_shape(BulletBoxShape((.5, .5, .5))) + + +class HitStrategy: + + def __init__(self, hit_by, node, world): + self._hit_by = hit_by + self._node = node + self._world = world + + def win_condition(self): + for contact in self._world.contact_test(self._node).get_contacts(): + other = contact.get_node1() if contact.get_node0() == self._node else contact.get_node0() + if other == self._hit_by.node: + return True diff --git a/game/items/domino.py b/game/items/domino.py new file mode 100644 index 0000000..7586953 --- /dev/null +++ b/game/items/domino.py @@ -0,0 +1,33 @@ +from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode +from game.items.item import Item, StillStrategy + + +class Domino(Item): + + def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.6): + super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/domino/domino.bam', mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction) + + def _set_shape(self, apply_scale=True): + self.node.add_shape(BulletBoxShape((.1, .25, .5))) + + +class UpStrategy(StillStrategy): + + def __init__(self, np, tgt_degrees): + super().__init__(np) + self._tgt_degrees = tgt_degrees + self._np = np + + def win_condition(self): + return super().win_condition() and abs(self._np.get_r()) <= self._tgt_degrees + + +class DownStrategy(StillStrategy): + + def __init__(self, np, tgt_degrees): + super().__init__(np) + self._tgt_degrees = tgt_degrees + self._np = np + + def win_condition(self): + return self._np.get_z() < -6 or super().win_condition() and abs(self._np.get_r()) >= self._tgt_degrees diff --git a/game/items/item.py b/game/items/item.py new file mode 100644 index 0000000..9eb5afb --- /dev/null +++ b/game/items/item.py @@ -0,0 +1,339 @@ +from panda3d.core import CullFaceAttrib, Point3, NodePath, Point2, Texture, \ + Plane, Vec3, BitMask32 +from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode +from direct.gui.OnscreenText import OnscreenText +from ya2.lib.p3d.gfx import P3dGfxMgr, set_srgb + + +class Command: + + def __init__(self, pos, rot): + self.pos = pos + self.rot = rot + + +class FixedStrategy: + + def win_condition(self): + return True + + +class StillStrategy: + + def __init__(self, np): + self._np = np + self._positions = [] + self._rotations = [] + + def win_condition(self): + self._positions += [self._np.get_pos()] + self._rotations += [self._np.get_hpr()] + if len(self._positions) > 30: + self._positions.pop(0) + if len(self._rotations) > 30: + self._rotations.pop(0) + if len(self._positions) < 28: + return + avg_x = sum(pos.x for pos in self._positions) / len(self._positions) + avg_y = sum(pos.y for pos in self._positions) / len(self._positions) + avg_z = sum(pos.z for pos in self._positions) / len(self._positions) + avg_h = sum(rot.x for rot in self._rotations) / len(self._rotations) + avg_p = sum(rot.y for rot in self._rotations) / len(self._rotations) + avg_r = sum(rot.z for rot in self._rotations) / len(self._rotations) + avg_pos = Point3(avg_x, avg_y, avg_z) + avg_rot = Point3(avg_h, avg_p, avg_r) + return all((pos - avg_pos).length() < .1 for pos in self._positions) and \ + all((rot - avg_rot).length() < 1 for rot in self._rotations) + + +class Item: + + def __init__(self, world, plane_node, cb_inst, curr_bottom, scene_repos, model_path, model_scale=1, exp_num_contacts=1, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.5): + self._world = world + self._plane_node = plane_node + self._count = count + self._cb_inst = cb_inst + self._paused = False + self._overlapping = False + self._first_command = True + self.repos_done = False + self._mass = mass + self._pos = pos + self._r = r + self.strategy = FixedStrategy() + self._exp_num_contacts = exp_num_contacts + self._curr_bottom = curr_bottom + self._scene_repos = scene_repos + self._model_scale = model_scale + self._model_path = model_path + self._commands = [] + self._command_idx = -1 + self._restitution = restitution + self._friction = friction + self._positions = [] + self._rotations = [] + if count: + self.node = BulletGhostNode(self.__class__.__name__) + else: + self.node = BulletRigidBodyNode(self.__class__.__name__) + self._set_shape(count) + self._np = render.attach_new_node(self.node) + if count: + world.attach_ghost(self.node) + else: + world.attach_rigid_body(self.node) + self._model = loader.load_model(model_path) + set_srgb(self._model) + self._model.flatten_light() + self._model.reparent_to(self._np) + self._np.set_scale(model_scale) + self._np.flatten_strong() + if count: + self._set_outline_model() + set_srgb(self._outline_model) + self._model.hide(BitMask32(0x01)) + self._outline_model.hide(BitMask32(0x01)) + self._start_drag_pos = None + self._prev_rot_info = None + self._last_nonoverlapping_pos = None + self._last_nonoverlapping_rot = None + self._instantiated = not count + self.interactable = count + self._box_tsk = taskMgr.add(self.on_frame, 'on_frame') + if count: + self._repos() + else: + self._np.set_pos(pos) + self._np.set_r(r) + + def _set_shape(self, apply_scale=True): + pass + + def set_strategy(self, strategy): + self.strategy = strategy + + def _repos(self): + p_from, p_to = P3dGfxMgr.world_from_to((-1, 1)) + for hit in self._world.ray_test_all(p_from, p_to).get_hits(): + if hit.get_node() == self._plane_node: + pos = hit.get_hit_pos() + corner = P3dGfxMgr.screen_coord(pos) + bounds = self._np.get_tight_bounds() + bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos() + self._np.set_pos(pos) + plane = Plane(Vec3(0, 1, 0), bounds[0][1]) + pos3d, near_pt, far_pt = Point3(), Point3(), Point3() + margin, ar = .04, base.get_aspect_ratio() + margin_x = margin / ar if ar >= 1 else margin + margin_z = margin * ar if ar < 1 else margin + top = self._curr_bottom() + base.camLens.extrude((-1 + margin_x, top - margin_z), near_pt, far_pt) + plane.intersects_line( + pos3d, render.get_relative_point(base.camera, near_pt), + render.get_relative_point(base.camera, far_pt)) + delta = Vec3(bounds[1][0], bounds[1][1], bounds[0][2]) + self._np.set_pos(pos3d + delta) + if not hasattr(self, '_txt'): + 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( + str(self._count), font=font, scale=0.06, fg=(.9, .9, .9, 1)) + pos = self._np.get_pos() + (bounds[1][0], bounds[0][1], bounds[0][2]) + p2d = P3dGfxMgr.screen_coord(pos) + self._txt['pos'] = p2d + self.repos_done = True + + def repos_x(self, x): + self._np.set_x(x) + bounds = self._np.get_tight_bounds() + bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos() + pos = self._np.get_pos() + (bounds[1][0], bounds[0][1], bounds[0][2]) + p2d = P3dGfxMgr.screen_coord(pos) + self._txt['pos'] = p2d + + def get_bottom(self): + bounds = self._np.get_tight_bounds() + bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos() + pos = self._np.get_pos() + (bounds[1][0], bounds[1][1], bounds[0][2]) + p2d = P3dGfxMgr.screen_coord(pos) + ar = base.get_aspect_ratio() + return p2d[1] if ar >= 1 else (p2d[1] * ar) + + def get_corner(self): + bounds = self._np.get_tight_bounds() + return bounds[1][0], bounds[1][1], bounds[0][2] + + def _set_outline_model(self): + self._outline_model = loader.load_model(self._model_path) + #clockw = CullFaceAttrib.MCullClockwise + #self._outline_model.set_attrib(CullFaceAttrib.make(clockw)) + self._outline_model.set_attrib(CullFaceAttrib.make_reverse()) + self._outline_model.reparent_to(self._np) + self._outline_model.set_scale(1.08) + self._outline_model.set_light_off() + self._outline_model.set_color(.4, .4, .4, 1) + self._outline_model.set_color_scale(.4, .4, .4, 1) + self._outline_model.hide() + + def on_frame(self, task): + self._np.set_y(0) + return task.cont + + def undo(self): + self._command_idx -= 1 + self._np.set_pos(self._commands[self._command_idx].pos) + self._np.set_hpr(self._commands[self._command_idx].rot) + + def redo(self): + self._command_idx += 1 + self._np.set_pos(self._commands[self._command_idx].pos) + self._np.set_hpr(self._commands[self._command_idx].rot) + + def play(self): + if not self._instantiated: + return + self._world.remove_rigid_body(self.node) + self.node.set_mass(self._mass) + self._world.attach_rigid_body(self.node) + self.node.set_restitution(self._restitution) + self.node.set_friction(self._friction) + + def on_click_l(self, pos): + if self._paused: return + self._start_drag_pos = pos, self._np.get_pos() + loader.load_sfx('assets/audio/sfx/grab.ogg').play() + if not self._instantiated: + self._world.remove_ghost(self.node) + pos = self._np.get_pos() + self._np.remove_node() + self.node = BulletRigidBodyNode('box') + self._set_shape() + self._np = render.attach_new_node(self.node) + self._world.attach_rigid_body(self.node) + self._model.reparent_to(self._np) + self._np.set_pos(pos) + self._set_outline_model() + self._np.set_scale(self._model_scale) + self._model.show(BitMask32(0x01)) + self._outline_model.hide(BitMask32(0x01)) + self._instantiated = True + self._txt.destroy() + self._count -= 1 + if self._count: + item = self.__class__(self._world, self._plane_node, self._cb_inst, self._curr_bottom, self._scene_repos, count=self._count, mass=self._mass, pos=self._pos, r=self._r) # pylint: disable=no-value-for-parameter + self._cb_inst(item) + self._scene_repos() + + def on_click_r(self, pos): + if self._paused or not self._instantiated: return + self._prev_rot_info = pos, self._np.get_pos(), self._np.get_r() + loader.load_sfx('assets/audio/sfx/grab.ogg').play() + + def on_release(self): + if self._start_drag_pos or self._prev_rot_info: + loader.load_sfx('assets/audio/sfx/release.ogg').play() + self._command_idx += 1 + self._commands = self._commands[:self._command_idx] + self._commands += [Command(self._np.get_pos(), self._np.get_hpr())] + self._first_command = False + self._start_drag_pos = self._prev_rot_info = None + if self._overlapping: + self._np.set_pos(self._last_nonoverlapping_pos) + self._np.set_hpr(self._last_nonoverlapping_rot) + self._outline_model.set_color(.4, .4, .4, 1) + self._outline_model.set_color_scale(.4, .4, .4, 1) + self._overlapping = False + + def on_mouse_on(self): + if not self._paused and self.interactable: + self._outline_model.show() + + def on_mouse_off(self): + if self._start_drag_pos or self._prev_rot_info: return + if self.interactable: + self._outline_model.hide() + + def on_mouse_move(self, pos): + if self._start_drag_pos: + d_pos = pos - self._start_drag_pos[0] + self._np.set_pos(self._start_drag_pos[1] + d_pos) + if self._prev_rot_info: + start_vec = self._prev_rot_info[0] - self._prev_rot_info[1] + curr_vec = pos - self._prev_rot_info[1] + d_angle = curr_vec.signed_angle_deg(start_vec, (0, -1, 0)) + self._np.set_r(self._prev_rot_info[2] + d_angle) + self._prev_rot_info = pos, self._np.get_pos(), self._np.get_r() + if self._start_drag_pos or self._prev_rot_info: + res = self._world.contact_test(self.node) + nres = res.get_num_contacts() + if nres <= self._exp_num_contacts: + self._overlapping = False + self._outline_model.set_color(.4, .4, .4, 1) + self._outline_model.set_color_scale(.4, .4, .4, 1) + if nres > self._exp_num_contacts and not self._overlapping: + actual_nres = 0 + for contact in res.get_contacts(): + for node in [contact.get_node0(), contact.get_node1()]: + if isinstance(node, BulletRigidBodyNode) and \ + node != self.node: + actual_nres += 1 + if actual_nres >= 1: + self._overlapping = True + loader.load_sfx('assets/audio/sfx/overlap.ogg').play() + self._outline_model.set_color(.9, .1, .1, 1) + self._outline_model.set_color_scale(.9, .1, .1, 1) + if not self._overlapping: + self._last_nonoverlapping_pos = self._np.get_pos() + self._last_nonoverlapping_rot = self._np.get_hpr() + + def on_aspect_ratio_changed(self): + if not self._instantiated: + self._repos() + + def store_state(self): + self._paused = True + self._model.set_transparency(True) + self._model.set_alpha_scale(.3) + if hasattr(self, '_txt') and not self._txt.is_empty(): + self._txt.set_alpha_scale(.3) + + def restore_state(self): + self._paused = False + self._model.set_alpha_scale(1) + if hasattr(self, '_txt') and not self._txt.is_empty(): + self._txt.set_alpha_scale(1) + + def fail_condition(self): + if self._np.get_z() < -6: + return True + self._positions += [self._np.get_pos()] + self._rotations += [self._np.get_hpr()] + if len(self._positions) > 30: + self._positions.pop(0) + if len(self._rotations) > 30: + self._rotations.pop(0) + if len(self._positions) < 28: + return + avg_x = sum(pos.x for pos in self._positions) / len(self._positions) + avg_y = sum(pos.y for pos in self._positions) / len(self._positions) + avg_z = sum(pos.z for pos in self._positions) / len(self._positions) + avg_h = sum(rot.x for rot in self._rotations) / len(self._rotations) + avg_p = sum(rot.y for rot in self._rotations) / len(self._rotations) + avg_r = sum(rot.z for rot in self._rotations) / len(self._rotations) + avg_pos = Point3(avg_x, avg_y, avg_z) + avg_rot = Point3(avg_h, avg_p, avg_r) + return all((pos - avg_pos).length() < .1 for pos in self._positions) and \ + all((rot - avg_rot).length() < 1 for rot in self._rotations) + + def destroy(self): + self._np.remove_node() + taskMgr.remove(self._box_tsk) + if hasattr(self, '_txt'): + self._txt.destroy() + if not self._instantiated: + self._world.remove_ghost(self.node) + else: + self._world.remove_rigid_body(self.node) diff --git a/game/items/shelf.py b/game/items/shelf.py new file mode 100644 index 0000000..dd39e83 --- /dev/null +++ b/game/items/shelf.py @@ -0,0 +1,11 @@ +from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode, BulletGhostNode +from game.items.item import Item + + +class Shelf(Item): + + def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.6): + super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/shelf/shelf.bam', mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction) + + def _set_shape(self, apply_scale=True): + self.node.add_shape(BulletBoxShape((1, .5, .05))) diff --git a/game/items/teetertooter.py b/game/items/teetertooter.py new file mode 100644 index 0000000..6fd01f2 --- /dev/null +++ b/game/items/teetertooter.py @@ -0,0 +1,21 @@ +from panda3d.core import TransformState +from panda3d.bullet import BulletCylinderShape, BulletRigidBodyNode, BulletGhostNode, YUp, ZUp +from game.items.item import Item + + +class TeeterTooter(Item): + + def __init__(self, world, plane_node, cb_inst, curr_bottom, repos, mass=1, pos=(0, 0, 0), r=0, count=0, restitution=.5, friction=.5): + super().__init__(world, plane_node, cb_inst, curr_bottom, repos, 'assets/models/bam/teeter_tooter/teeter_tooter.bam', exp_num_contacts=2, mass=mass, pos=pos, r=r, count=count, restitution=restitution, friction=friction, model_scale=.5) + + def _set_shape(self, apply_scale=True): + scale = self._model_scale if apply_scale else 1 + self.node.add_shape( + BulletCylinderShape(.1, 1.6, YUp), + TransformState.makePos((0, 0, scale * .36))) + self.node.add_shape( + BulletCylinderShape(.1, .7, ZUp), + TransformState.makePos((0, scale * .8, scale * -.1))) + self.node.add_shape( + BulletCylinderShape(.1, .7, ZUp), + TransformState.makePos((0, scale * -.8, scale * -.1))) diff --git a/game/menu.py b/game/menu.py new file mode 100644 index 0000000..b20889a --- /dev/null +++ b/game/menu.py @@ -0,0 +1,304 @@ +from logging import info, debug +from sys import platform, exit +from os import environ, system +from glob import glob +from importlib import import_module +from inspect import isclass +from webbrowser import open_new_tab +from panda3d.core import Texture, TextNode, WindowProperties, LVector2i, \ + TextProperties, TextPropertiesManager +from direct.gui.DirectGui import DirectButton, DirectCheckButton, \ + DirectOptionMenu, DirectSlider, DirectCheckButton +from direct.gui.DirectGuiGlobals import FLAT +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from ya2.engine.gui.cursor import MouseCursor +from game.scene import Scene +from panda3d.bullet import BulletWorld + + +class Menu(DirectObject): + + def __init__(self, fsm, lang_mgr, opt_file, music, pipeline, scenes, fun_test): + super().__init__() + self._fsm = fsm + self._lang_mgr = lang_mgr + self._opt_file = opt_file + self._music = music + self._pipeline = pipeline + self._scenes = scenes + self._fun_test = fun_test + self._enforced_res = '' + self._cursor = MouseCursor( + 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), + (.01, .01)) + self._font = base.loader.load_font('assets/fonts/Hanken-Book.ttf') + self._font.clear() + self._font.set_pixels_per_unit(60) + self._font.set_minfilter(Texture.FTLinearMipmapLinear) + self._font.set_outline((0, 0, 0, 1), .8, .2) + self._widgets = [] + self._common = { + 'scale': .12, + 'text_font': self._font, + 'text_fg': (.9, .9, .9, 1), + 'relief': FLAT, + 'frameColor': (.4, .4, .4, .14), + 'rolloverSound': loader.load_sfx('assets/audio/sfx/rollover.ogg'), + 'clickSound': loader.load_sfx('assets/audio/sfx/click.ogg')} + self._common_btn = {'frameSize': (-4.8, 4.8, -.6, 1.2)} | self._common + hlc = self._common_btn['frameColor'] + hlc = (hlc[0] + .2, hlc[1] + .2, hlc[2] + .2, hlc[3] + .2) + self._common_opt = { + 'item_frameColor': self._common_btn['frameColor'], + 'popupMarker_frameColor': self._common_btn['frameColor'], + 'item_relief': self._common_btn['relief'], + 'item_text_font': self._common_btn['text_font'], + 'item_text_fg': self._common_btn['text_fg'], + 'textMayChange': 1, + 'highlightColor': hlc, + 'text_align': TextNode.A_center, + } | self._common_btn + f_s = self._common_opt['frameSize'] + self._common_opt['frameSize'] = f_s[0], f_s[1] - .56, f_s[2], f_s[3] + self._common_slider = self._common | { + 'range': (0, 1), + 'thumb_frameColor': (.4, .4, .4, .4), + 'thumb_scale': 1.6, + 'scale': .4} + del self._common_slider['rolloverSound'] + del self._common_slider['clickSound'] + self._set_main() + + def enforce_res(self, val): + self._enforced_res = val + info('enforced resolution: ' + val) + + def _set_main(self): + self._widgets = [] + self._widgets += [DirectButton( + text=_('Play'), pos=(0, 1, .6), command=self.on_play, + **self._common_btn)] + self._widgets += [DirectButton( + text=_('Options'), pos=(0, 1, .2), command=self.on_options, + **self._common_btn)] + self._widgets += [DirectButton( + text=_('Credits'), pos=(0, 1, -.2), command=self.on_credits, + **self._common_btn)] + self._widgets += [DirectButton( + text=_('Exit'), pos=(0, 1, -.6), command=lambda: exit(), + **self._common_btn)] + self._rearrange_width() + self.accept('enforce_resolution', self.enforce_res) + + def _set_options(self): + self._widgets = [] + self._lang_funcs = [lambda: _('English'), lambda: _('Italian')] + items = [fnc() for fnc in self._lang_funcs] + inititem = { + 'en': _('English'), + 'it': _('Italian') + }[self._opt_file['settings']['language']] + btn = DirectOptionMenu( + text=_('Language'), items=items, initialitem=inititem, + pos=(0, 1, .8), command=self.on_language, **self._common_opt) + btn.popupMenu['frameColor'] = self._common_btn['frameColor'] + btn.popupMenu['relief'] = self._common_btn['relief'] + self._widgets += [btn] + self._widgets += [OnscreenText( + _('Volume'), pos=(-.1, .55), font=self._common['text_font'], + scale=self._common['scale'], fg=self._common['text_fg'], + align=TextNode.A_right)] + self._widgets += [DirectSlider( + pos=(.5, 1, .57), + value=self._opt_file['settings']['volume'], + command=self.on_volume, + **self._common_slider)] + self._slider = self._widgets[-1] + self._widgets += [DirectCheckButton( + text=_('Fullscreen'), pos=(0, 1, .3), command=self.on_fullscreen, + indicator_frameColor=self._common_opt['highlightColor'], + indicator_relief=self._common_btn['relief'], + indicatorValue=self._opt_file['settings']['fullscreen'], + **self._common_btn)] + res = self._opt_file['settings']['resolution'] + d_i = base.pipe.get_display_information() + def _res(idx): + return d_i.get_display_mode_width(idx), \ + d_i.get_display_mode_height(idx) + resolutions = [ + _res(idx) for idx in range(d_i.get_total_display_modes())] + resolutions = list(set(resolutions)) + resolutions = sorted(resolutions) + resolutions = [(str(_res[0]), str(_res[1])) for _res in resolutions] + resolutions = ['x'.join(_res) for _res in resolutions] + if not res: + res = resolutions[-1] + btn = DirectOptionMenu( + text=_('Resolution'), items=resolutions, initialitem=res, + pos=(0, 1, .05), command=self.on_resolution, **self._common_opt) + btn.popupMenu['frameColor'] = self._common_btn['frameColor'] + btn.popupMenu['relief'] = self._common_btn['relief'] + self._widgets += [btn] + self._widgets += [DirectCheckButton( + text=_('Antialiasing'), pos=(0, 1, -.2), command=self.on_aa, + indicator_frameColor=self._common_opt['highlightColor'], + indicator_relief=self._common_btn['relief'], + indicatorValue=self._opt_file['settings']['antialiasing'], + **self._common_btn)] + self._widgets += [DirectCheckButton( + text=_('Shadows'), pos=(0, 1, -.45), command=self.on_shadows, + indicator_frameColor=self._common_opt['highlightColor'], + indicator_relief=self._common_btn['relief'], + indicatorValue=self._opt_file['settings']['shadows'], + **self._common_btn)] + self._widgets += [DirectButton( + text=_('Back'), pos=(0, 1, -.8), command=self.on_back, + **self._common_btn)] + self.accept('enforce_resolution', self.enforce_res) + + def _set_credits(self): + self._widgets = [] + tp_scale = TextProperties() + tp_scale.set_text_scale(.64) + TextPropertiesManager.getGlobalPtr().setProperties('scale', tp_scale) + self._widgets += [OnscreenText( + _('Code and gfx\n \1scale\1Flavio Calva\2\n\n\nMusic\n \1scale\1Stefan Grossmann\2'), + pos=(-.9, .55), font=self._common['text_font'], + scale=self._common['scale'], fg=self._common['text_fg'], + align=TextNode.A_left)] + self._widgets += [DirectButton( + text=_('Website'), pos=(-.6, 1, .29), command=self.on_website, + **self._common_btn | {'scale': .08})] + self._widgets += [OnscreenText( + _('Special thanks to:\n \1scale\1rdb\2\n \1scale\1Luisa Tenuta\2\n \1scale\1Damiana Ercolani\2'), + pos=(.1, .55), font=self._common['text_font'], + scale=self._common['scale'], fg=self._common['text_fg'], + align=TextNode.A_left)] + self._widgets += [DirectButton( + text=_('Back'), pos=(0, 1, -.8), command=self.on_back, + **self._common_btn)] + self.accept('enforce_resolution', self.enforce_res) + + def on_play(self): + self.destroy() + self._cursor = MouseCursor( + 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), + (.01, .01)) + self._widgets = [] + cmn = self._common_btn.copy() | { + 'frameSize': (-2.4, 2.4, -2.4, 2.4), + 'frameColor': (1, 1, 1, .8), + 'text_scale': .64} + left = - (dx := .8) * (min(4, len(self._scenes)) - 1) / 2 + for i, cls in enumerate(self._scenes): + top = .1 if len(self._scenes) < 5 else .6 + row = 0 if i < 4 else 1 + self._widgets += [DirectButton( + text=cls.name(), pos=(left + dx * (i % 4), 1, top - dx * row), + command=self.start, extraArgs=[cls], text_wordwrap=6, + frameTexture='assets/images/scenes/%s.dds' % cls.__name__, + **cmn)] + for j in range(4): + tnode = self._widgets[-1].component('text%s' % j).textNode + height = - tnode.getLineHeight() / 2 + height += (tnode.get_height() - tnode.get_line_height()) / 2 + self._widgets[-1].component('text%s' % j).set_pos(0, 0, height) + self._widgets += [DirectButton( + text=_('Back'), pos=(0, 1, -.8), command=self.on_back, + **self._common_btn)] + + def start(self, cls): + self._fsm.demand('Scene', cls) + + def on_options(self): + self.destroy() + self._cursor = MouseCursor( + 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), + (.01, .01)) + self._set_options() + + def on_credits(self): + self.destroy() + self._cursor = MouseCursor( + 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), + (.01, .01)) + self._set_credits() + + def _rearrange_width(self): + max_width = 0 + for wdg in self._widgets: + t_n = wdg.component('text0') + u_l = t_n.textNode.get_upper_left_3d() + l_r = t_n.textNode.get_lower_right_3d() + max_width = max(l_r[0] - u_l[0], max_width) + for wdg in self._widgets: + m_w = max_width / 2 + .8 + wdg['frameSize'] = -m_w, m_w, wdg['frameSize'][2], wdg['frameSize'][3] + + def on_language(self, arg): + lang_code = { + _('English'): 'en_EN', + _('Italian'): 'it_IT'}[arg] + self._lang_mgr.set_lang(lang_code) + self._opt_file['settings']['language'] = lang_code[:2] + self._opt_file.store() + self.on_options() + + def on_volume(self): + self._opt_file['settings']['volume'] = self._slider['value'] + self._music.set_volume(self._slider['value']) + + def on_fullscreen(self, arg): + props = WindowProperties() + props.set_fullscreen(arg) + if not self._fun_test: + base.win.request_properties(props) + # if we actually switch to fullscreen during the tests then + # exwm inside qemu can't restore the correct resolution + # i may re-enable this if/when i run the tests onto a + # physical machine + self._opt_file['settings']['fullscreen'] = int(arg) + self._opt_file.store() + + def on_resolution(self, arg): + info('on resolution: %s (%s)' % (arg, self._enforced_res)) + arg = self._enforced_res or arg + info('set resolution: %s' % arg) + props = WindowProperties() + props.set_size(LVector2i(*[int(_res) for _res in arg.split('x')])) + base.win.request_properties(props) + self._opt_file['settings']['resolution'] = arg + self._opt_file.store() + + def on_aa(self, arg): + self._pipeline.msaa_samples = 4 if arg else 1 + debug(f'msaa: {self._pipeline.msaa_samples}') + self._opt_file['settings']['antialiasing'] = int(arg) + self._opt_file.store() + + def on_shadows(self, arg): + self._pipeline.enable_shadows = int(arg) + debug(f'shadows: {self._pipeline.enable_shadows}') + self._opt_file['settings']['shadows'] = int(arg) + self._opt_file.store() + + def on_website(self): + if platform.startswith('linux'): + environ['LD_LIBRARY_PATH'] = '' + system('xdg-open https://www.ya2.it') + else: + open_new_tab('https://www.ya2.it') + + def on_back(self): + self._opt_file.store() + self.destroy() + self._cursor = MouseCursor( + 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), + (.01, .01)) + self._set_main() + + def destroy(self): + [wdg.destroy() for wdg in self._widgets] + self._cursor.destroy() + self.ignore('enforce_resolution') diff --git a/game/music.py b/game/music.py new file mode 100644 index 0000000..cf6c0c9 --- /dev/null +++ b/game/music.py @@ -0,0 +1,55 @@ +from os.path import dirname, exists, basename +from platform import system +from glob import glob +from pathlib import Path +from random import choice +from logging import info +from panda3d.core import AudioSound, Filename + + +class MusicMgr: + + def __init__(self, volume): + files = self.curr_path + 'assets/audio/music/*.ogg' + self._start_music(glob(files)) + base.musicManager.setVolume(.8 * volume) + base.sfxManagerList[0].setVolume(volume) + taskMgr.add(self._on_frame, 'on frame music') + + @property + def is_appimage(self): + par_path = str(Path(__file__).parent.absolute()) + is_appimage = par_path.startswith('/tmp/.mount_Pmachi') + return is_appimage and par_path.endswith('/usr/bin') + + @property + def curr_path(self): + if system() == 'Windows': + return '' + if exists('main.py'): + return '' + else: + par_path = str(Path(__file__).parent.absolute()) + if self.is_appimage: + par_path = str(Path(par_path).absolute()) + par_path += '/' + return par_path + + def _start_music(self, files): + self._music = loader.load_music(choice(files)) + info('playing music ' + self._music.get_name()) + self._music.play() + + def set_volume(self, volume): + base.musicManager.setVolume(.8 * volume) + base.sfxManagerList[0].setVolume(volume) + + def _on_frame(self, task): + if self._music.status() == AudioSound.READY: + oggs = Filename(self.curr_path + 'assets/audio/music/*.ogg').to_os_specific() + files = glob(oggs) + rm_music = Filename(self.curr_path + 'assets/audio/music/' + basename(self._music.get_name())).to_os_specific() + # basename is needed in windows + files.remove(rm_music) + self._start_music(files) + return task.cont diff --git a/game/scene.py b/game/scene.py new file mode 100644 index 0000000..6cdf9f9 --- /dev/null +++ b/game/scene.py @@ -0,0 +1,587 @@ +from os.path import exists +from os import makedirs +from glob import glob +from logging import debug, info +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 +from direct.gui.OnscreenImage import OnscreenImage +from direct.gui.OnscreenText import OnscreenText +from direct.gui.DirectGui import DirectButton, DirectFrame +from direct.gui.DirectGuiGlobals import FLAT, DISABLED, NORMAL +from direct.showbase.DirectObject import DirectObject +from game.items.background import Background +from game.sidepanel import SidePanel +from ya2.engine.gui.cursor import MouseCursor +from ya2.lib.p3d.gfx import P3dGfxMgr + + +class Scene(DirectObject): + + def __init__(self, world, exit_cb, auto_close_instr, dbg_items, reload_cb, scenes): + super().__init__() + self._world = world + self._exit_cb = exit_cb + self._dbg_items = dbg_items + self._reload_cb = reload_cb + self._scenes = scenes + self._enforce_res = '' + self.accept('enforce_res', self.enforce_res) + self._set_camera() + self._cursor = MouseCursor( + 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1), + (.01, .01)) + self._set_gui() + self._set_lights() + self._set_input() + self._set_mouse_plane() + self.items = [] + self.reset() + self._state = 'init' + self._paused = False + self._item_active = None + if auto_close_instr: + self.__store_state() + self.__restore_state() + else: + self._set_instructions() + self._bg = Background() + self._side_panel = SidePanel(world, self._mouse_plane_node, (-5, 4), (-3, 1), 1, self.items) + self._scene_tsk = taskMgr.add(self.on_frame, 'on_frame') + + @staticmethod + def name(): + return '' + + def _instr_txt(self): + return '' + + def _set_items(self): + self.items = [] + + def screenshot(self, task=None): + tex = Texture('screenshot') + buffer = base.win.make_texture_buffer('screenshot', 512, 512, tex, True ) + cam = base.make_camera(buffer) + cam.reparent_to(render) + cam.node().get_lens().set_fov(base.camLens.get_fov()) + cam.set_pos(0, -20, 0) + cam.look_at(0, 0, 0) + import simplepbr + simplepbr.init( + window=buffer, + camera_node=cam, + use_normal_maps=True, + use_emission_maps=False, + use_occlusion_maps=True, + msaa_samples=4, + enable_shadows=True) + base.graphicsEngine.renderFrame() + base.graphicsEngine.renderFrame() + fname = self.__class__.__name__ + if not exists('assets/images/scenes'): + makedirs('assets/images/scenes') + buffer.save_screenshot('assets/images/scenes/%s.png' % fname) + # img = DirectButton( + # frameTexture=buffer.get_texture(), relief=FLAT, + # frameSize=(-.2, .2, -.2, .2)) + return buffer.get_texture() + + def current_bottom(self): + curr_bottom = 1 + for item in self.items: + if item.repos_done: + curr_bottom = min(curr_bottom, item.get_bottom()) + return curr_bottom + + def reset(self): + [itm.destroy() for itm in self.items] + self._set_items() + self._state = 'init' + self._commands = [] + self._command_idx = 0 + if hasattr(self, '_success_txt'): + self._success_txt.destroy() + del self._success_txt + self.__right_btn['state'] = NORMAL + + def enforce_res(self, val): + self._enforce_res = val + info('enforce res: ' + val) + + def destroy(self): + self.ignore('enforce_res') + self._unset_gui() + self._unset_lights() + self._unset_input() + self._unset_mouse_plane() + [itm.destroy() for itm in self.items] + self._bg.destroy() + self._side_panel.destroy() + self._cursor.destroy() + taskMgr.remove(self._scene_tsk) + if hasattr(self, '_success_txt'): + self._success_txt.destroy() + + def _set_camera(self): + base.camera.set_pos(0, -20, 0) + base.camera.look_at(0, 0, 0) + + def __load_img_btn(self, path, col): + img = OnscreenImage('assets/images/buttons/%s.dds' % path) + img.set_transparency(True) + img.set_color(col) + img.detach_node() + return img + + def _set_gui(self): + def load_images_btn(path, col): + colors = { + 'gray': [ + (.6, .6, .6, 1), # ready + (1, 1, 1, 1), # press + (.8, .8, .8, 1), # rollover + (.4, .4, .4, .4)], + 'green': [ + (.1, .68, .1, 1), + (.1, 1, .1, 1), + (.1, .84, .1, 1), + (.4, .1, .1, .4)]}[col] + return [self.__load_img_btn(path, col) for col in colors] + abl, abr = base.a2dBottomLeft, base.a2dBottomRight + btn_info = [ + ('home', self.on_home, NORMAL, abl, 'gray'), + ('information', self._set_instructions, NORMAL, abl, 'gray'), + ('right', self.on_play, NORMAL, abr, 'green'), + #('next', self.on_next, DISABLED, abr, 'gray'), + #('previous', self.on_prev, DISABLED, abr, 'gray'), + #('rewind', self.reset, NORMAL, abr, 'gray') + ] + num_l = num_r = 0 + btns = [] + for binfo in btn_info: + imgs = load_images_btn(binfo[0], binfo[4]) + if binfo[3] == base.a2dBottomLeft: + sign, num = 1, num_l + num_l += 1 + else: + sign, num = -1, num_r + num_r += 1 + fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) + btn = DirectButton( + image=imgs, scale=.05, pos=(sign * (.06 + .11 * num), 1, .06), + parent=binfo[3], command=binfo[1], state=binfo[2], relief=FLAT, + frameColor=fcols[0] if binfo[2] == NORMAL else fcols[1], + rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'), + clickSound=loader.load_sfx('assets/audio/sfx/click.ogg')) + btn.set_transparency(True) + btns += [btn] + self.__home_btn, self.__info_btn, self.__right_btn = btns + # , self.__next_btn, self.__prev_btn, self.__rewind_btn + if self._dbg_items: + self._info_txt = OnscreenText( + '', parent=base.a2dTopRight, scale=0.04, + pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right) + + def _unset_gui(self): + btns = [ + self.__home_btn, self.__info_btn, self.__right_btn, + #self.__next_btn, self.__prev_btn, self.__rewind_btn + ] + [btn.destroy() for btn in btns] + if self._dbg_items: + self._info_txt.destroy() + + def _set_spotlight(self, name, pos, look_at, color, shadows=False): + light = Spotlight(name) + if shadows: + light.setLens(PerspectiveLens()) + light_np = render.attach_new_node(light) + light_np.set_pos(pos) + light_np.look_at(look_at) + light.set_color(color) + render.set_light(light_np) + return light_np + + def _set_lights(self): + alight = AmbientLight('alight') # for ao + alight.set_color((.15, .15, .15, 1)) + self._alnp = render.attach_new_node(alight) + render.set_light(self._alnp) + self._key_light = self._set_spotlight( + 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1)) + self._shadow_light = self._set_spotlight( + 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True) + self._shadow_light.node().set_shadow_caster(True, 2048, 2048) + self._shadow_light.node().get_lens().set_film_size(2048, 2048) + self._shadow_light.node().get_lens().set_near_far(1, 256) + self._shadow_light.node().set_camera_mask(BitMask32(0x01)) + + def _unset_lights(self): + for light in [self._alnp, self._key_light, self._shadow_light]: + render.clear_light(light) + light.remove_node() + + def _set_input(self): + self.accept('mouse1', self.on_click_l) + self.accept('mouse1-up', self.on_release) + self.accept('mouse3', self.on_click_r) + self.accept('mouse3-up', self.on_release) + + def _unset_input(self): + for evt in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']: + self.ignore(evt) + + def _set_mouse_plane(self): + shape = BulletPlaneShape((0, -1, 0), 0) + #self._mouse_plane_node = BulletRigidBodyNode('mouse plane') + self._mouse_plane_node = BulletGhostNode('mouse plane') + self._mouse_plane_node.addShape(shape) + #np = render.attachNewNode(self._mouse_plane_node) + #self._world.attachRigidBody(self._mouse_plane_node) + self._world.attach_ghost(self._mouse_plane_node) + + def _unset_mouse_plane(self): + self._world.remove_ghost(self._mouse_plane_node) + + def _get_hits(self): + if not base.mouseWatcherNode.has_mouse(): return [] + p_from, p_to = P3dGfxMgr.world_from_to(base.mouseWatcherNode.get_mouse()) + return self._world.ray_test_all(p_from, p_to).get_hits() + + def _update_info(self, item): + txt = '' + if item: + txt = '%.3f %.3f\n%.3f°' % ( + item._np.get_x(), item._np.get_z(), item._np.get_r()) + self._info_txt['text'] = txt + + def _on_click(self, method): + if self._paused: + return + for hit in self._get_hits(): + if hit.get_node() == self._mouse_plane_node: + pos = hit.get_hit_pos() + for hit in self._get_hits(): + for item in [i for i in self.items if hit.get_node() == i.node and i.interactable]: + if not self._item_active: + self._item_active = item + getattr(item, method)(pos) + img = 'move' if method == 'on_click_l' else 'rotate' + if not (img == 'rotate' and not item._instantiated): + self._cursor.set_image('assets/images/buttons/%s.dds' % img) + + def on_click_l(self): + self._on_click('on_click_l') + + def on_click_r(self): + self._on_click('on_click_r') + + def on_release(self): + if self._item_active and not self._item_active._first_command: + self._commands = self._commands[:self._command_idx] + self._commands += [self._item_active] + self._command_idx += 1 + #self.__prev_btn['state'] = NORMAL + #fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) + #self.__prev_btn['frameColor'] = fcols[0] + #if self._item_active._command_idx == len(self._item_active._commands) - 1: + # self.__next_btn['state'] = DISABLED + # self.__next_btn['frameColor'] = fcols[1] + self._item_active = None + [item.on_release() for item in self.items] + self._cursor.set_image('assets/images/buttons/arrowUpLeft.dds') + + def repos(self): + for item in self.items: + item.repos_done = False + self.items = sorted(self.items, key=lambda itm: itm.__class__.__name__) + [item.on_aspect_ratio_changed() for item in self.items] + self._side_panel.update(self.items) + max_x = -float('inf') + for item in self.items: + if not item._instantiated: + max_x = max(item._np.get_x(), max_x) + for item in self.items: + if not item._instantiated: + item.repos_x(max_x) + + def on_aspect_ratio_changed(self): + self.repos() + + def _win_condition(self): + pass + + def _fail_condition(self): + return all(itm.fail_condition() for itm in self.items) and not self._paused and self._state == 'playing' + + def on_frame(self, task): + hits = self._get_hits() + pos = None + for hit in self._get_hits(): + if hit.get_node() == self._mouse_plane_node: + pos = hit.get_hit_pos() + hit_nodes = [hit.get_node() for hit in hits] + if self._item_active: + items_hit = [self._item_active] + else: + items_hit = [itm for itm in self.items if itm.node in hit_nodes] + items_no_hit = [itm for itm in self.items if itm not in items_hit] + [itm.on_mouse_on() for itm in items_hit] + [itm.on_mouse_off() for itm in items_no_hit] + if pos and self._item_active: + self._item_active.on_mouse_move(pos) + if self._dbg_items: + self._update_info(items_hit[0] if items_hit else None) + if self._win_condition(): + self._set_fail() if self._enforce_res == 'fail' else self._set_win() + elif self._state == 'playing' and self._fail_condition(): + self._set_win() if self._enforce_res == 'win' else self._set_fail() + if any(itm._overlapping for itm in self.items): + self._cursor.cursor_img.img.set_color(.9, .1, .1, 1) + else: + self._cursor.cursor_img.img.set_color(.9, .9, .9, 1) + return task.cont + + def cb_inst(self, item): + self.items += [item] + + def on_play(self): + self._state = 'playing' + #self.__prev_btn['state'] = DISABLED + #self.__next_btn['state'] = DISABLED + self.__right_btn['state'] = DISABLED + [itm.play() for itm in self.items] + + def on_next(self): + self._commands[self._command_idx].redo() + self._command_idx += 1 + fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) + #self.__prev_btn['state'] = NORMAL + #self.__prev_btn['frameColor'] = fcols[0] + more_commands = self._command_idx < len(self._commands) + #self.__next_btn['state'] = NORMAL if more_commands else DISABLED + #self.__next_btn['frameColor'] = fcols[0] if more_commands else fcols[1] + + def on_prev(self): + self._command_idx -= 1 + self._commands[self._command_idx].undo() + fcols = (.4, .4, .4, .14), (.3, .3, .3, .05) + #self.__next_btn['state'] = NORMAL + #self.__next_btn['frameColor'] = fcols[0] + #self.__prev_btn['state'] = NORMAL if self._command_idx else DISABLED + #self.__prev_btn['frameColor'] = fcols[0] if self._command_idx else fcols[1] + + def on_home(self): + self._exit_cb() + + def _set_instructions(self): + self._paused = True + self.__store_state() + mgr = TextPropertiesManager.get_global_ptr() + for name in ['mouse_l', 'mouse_r']: + graphic = OnscreenImage('assets/images/buttons/%s.dds' % name) + graphic.set_scale(.5) + graphic.get_texture().set_minfilter(Texture.FTLinearMipmapLinear) + graphic.get_texture().set_anisotropic_degree(2) + mgr.set_graphic(name, graphic) + graphic.set_z(-.2) + graphic.set_transparency(True) + graphic.detach_node() + 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( + self._instr_txt(), parent=frm, font=font, scale=0.06, + fg=(.9, .9, .9, 1), align=TextNode.A_left) + 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'] = -w / 2, 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('exitRight', col) for col in colors] + btn = DirectButton( + image=imgs, scale=btn_scale, + pos=(l_r[0] - btn_scale, 1, l_r[2] - mar - btn_scale), + parent=frm, command=self.__on_close_instructions, 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) + + def _set_win(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) + enabled = self._scenes.index(self.__class__) < len(self._scenes) - 1 + if enabled: + next_scene = self._scenes[self._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 _set_fail(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 have failed!'), + 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) + + 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, + #self.__next_btn, self.__prev_btn, self.__rewind_btn + ] + self.__btn_state = [btn['state'] for btn in btns] + for btn in btns: + btn['state'] = DISABLED + [itm.store_state() for itm in self.items] + + def __restore_state(self): + btns = [ + self.__home_btn, self.__info_btn, self.__right_btn, + #self.__next_btn, self.__prev_btn, self.__rewind_btn + ] + for btn, state in zip(btns, self.__btn_state): + btn['state'] = state + [itm.restore_state() for itm in self.items] + self._paused = False + + def __on_close_instructions(self, frm): + frm.remove_node() + self.__restore_state() diff --git a/game/scenes/__init__.py b/game/scenes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/game/scenes/scene_basketball.py b/game/scenes/scene_basketball.py new file mode 100644 index 0000000..d57adf7 --- /dev/null +++ b/game/scenes/scene_basketball.py @@ -0,0 +1,59 @@ +from game.scene import Scene +from game.items.box import Box +from game.items.shelf import Shelf +from game.items.domino import Domino, UpStrategy, DownStrategy +from game.items.basketball import Basketball +from game.items.teetertooter import TeeterTooter + + +class SceneBasketBall(Scene): + + @staticmethod + def name(): + return _('Basket ball') + + def _set_items(self): + self.items = [] + #self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=5, count=2)] + #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] + self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=1)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, .21))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, .21))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-4.45, 0, -3.18), r=27, restitution=1)] + #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-5.45, 0, -3.18), restitution=1)] + #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 30)) + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, .73), r=37)] + #self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, .78))] + #self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, .78))] + #self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, .78))] + #self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, .78))] + #self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) + #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + + def _instr_txt(self): + txt = _('Scene: ') + self.name() + '\n\n' + txt += _('Goal: you must hit every domino piece\n\n') + txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' + 'keep \5mouse_r\5 pressed to rotate an item') + return txt + + def _win_condition(self): + return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/game/scenes/scene_box.py b/game/scenes/scene_box.py new file mode 100644 index 0000000..c0eee15 --- /dev/null +++ b/game/scenes/scene_box.py @@ -0,0 +1,43 @@ +from game.scene import Scene +from game.items.box import Box, HitStrategy +from game.items.shelf import Shelf +from game.items.domino import Domino +from game.items.basketball import Basketball +from game.items.teetertooter import TeeterTooter + + +class SceneBox(Scene): + + @staticmethod + def name(): + return _('Box') + + def _set_items(self): + self.items = [] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.46, 0, -3.95))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(4.43, 0, -3.95))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.29, 0, .26), r=28.45)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(2.15, 0, -1.49), r=28.45)] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-1.55, 0, 1.23), friction=.4)] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(4.38, 0, -3.35))] + self.items[-1].set_strategy(HitStrategy(self.items[-2], self.items[-1].node, self.items[-1]._world)) + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=2)] + #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-1.14, 0, -.04), tgt_degrees=60)] + #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.49, 0, -.04), tgt_degrees=60)] + #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.94, 0, -.04), tgt_degrees=60)] + #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.55, 0, -.04), tgt_degrees=60)] + #self.items += [TargetDomino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.09, 0, -.04), tgt_degrees=88)] + #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + + def _instr_txt(self): + txt = _('Scene: ') + self.name() + '\n\n' + txt += _('Goal: the left box must hit the right box\n\n') + txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' + 'keep \5mouse_r\5 pressed to rotate an item') + return txt + + def _win_condition(self): + return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/game/scenes/scene_domino.py b/game/scenes/scene_domino.py new file mode 100644 index 0000000..d3edad2 --- /dev/null +++ b/game/scenes/scene_domino.py @@ -0,0 +1,43 @@ +from game.scene import Scene +from game.items.box import Box +from game.items.shelf import Shelf +from game.items.domino import Domino, DownStrategy +from game.items.basketball import Basketball +from game.items.teetertooter import TeeterTooter + + +class SceneDomino(Scene): + + @staticmethod + def name(): + return _('Domino') + + def _set_items(self): + self.items = [] + #self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.2, 0, -.6))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.2, 0, -.6))] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=2)] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-1.14, 0, -.04))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.49, 0, -.04))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.94, 0, -.04))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.55, 0, -.04))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 60)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.09, 0, -.04))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 88)) + #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + + def _instr_txt(self): + txt = _('Scene: ') + self.name() + '\n\n' + txt += _('Goal: every domino piece must fall\n\n') + txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' + 'keep \5mouse_r\5 pressed to rotate an item') + return txt + + def _win_condition(self): + return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/game/scenes/scene_domino_box.py b/game/scenes/scene_domino_box.py new file mode 100644 index 0000000..629047c --- /dev/null +++ b/game/scenes/scene_domino_box.py @@ -0,0 +1,56 @@ +from game.scene import Scene +from game.items.box import Box +from game.items.shelf import Shelf +from game.items.domino import Domino, UpStrategy, DownStrategy +from game.items.basketball import Basketball +from game.items.teetertooter import TeeterTooter + + +class SceneDominoBox(Scene): + + @staticmethod + def name(): + return _('Domino and box') + + def _set_items(self): + self.items = [] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=5, count=2)] + #self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, .21))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, .21))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, -.94), r=37)] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, -.89))] + self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.61, 0, .73), r=37)] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.06, 0, .78))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(0.91, 0, .78))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, .78))] + self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, .78))] + self.items[-1].set_strategy(UpStrategy(self.items[-1]._np, 30)) + #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + + def _instr_txt(self): + txt = _('Scene: ') + self.name() + '\n\n' + txt += _('Goal: only the last piece of each row must be up\n\n') + txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' + 'keep \5mouse_r\5 pressed to rotate an item') + return txt + + def _win_condition(self): + return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/game/scenes/scene_domino_box_basketball.py b/game/scenes/scene_domino_box_basketball.py new file mode 100644 index 0000000..84b5129 --- /dev/null +++ b/game/scenes/scene_domino_box_basketball.py @@ -0,0 +1,45 @@ +from game.scene import Scene +from game.items.box import Box +from game.items.shelf import Shelf +from game.items.domino import Domino, UpStrategy, DownStrategy +from game.items.basketball import Basketball +from game.items.teetertooter import TeeterTooter + + +class SceneDominoBoxBasketball(Scene): + + @staticmethod + def name(): + return _('Domino, box and basket ball') + + def _set_items(self): + self.items = [] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=1, mass=5)] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=1)] + self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(-.3, 1, 2.5))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] + #self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=9)] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.68, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.35, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(3.08, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(3.78, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(4.53, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + + def _instr_txt(self): + txt = _('Scene: ') + self.name() + '\n\n' + txt += _('Goal: every domino piece must be hit\n\n') + txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' + 'keep \5mouse_r\5 pressed to rotate an item') + return txt + + def _win_condition(self): + return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/game/scenes/scene_teeter_domino_box_basketball.py b/game/scenes/scene_teeter_domino_box_basketball.py new file mode 100644 index 0000000..b433106 --- /dev/null +++ b/game/scenes/scene_teeter_domino_box_basketball.py @@ -0,0 +1,55 @@ +from game.scene import Scene +from game.items.box import Box +from game.items.shelf import Shelf +from game.items.domino import Domino, UpStrategy, DownStrategy +from game.items.basketball import Basketball +from game.items.teetertooter import TeeterTooter + + +class SceneTeeterDominoBoxBasketball(Scene): + + @staticmethod + def name(): + return _('Teeter tooter, domino, box and basket ball') + + def _set_items(self): + self.items = [] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=3, count=2, friction=1)] + self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(.98, 1, 1.02))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-6.24, 0, -1.45))] + self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-6.24, 0, -1.20))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, r=24.60, friction=1, pos=(-6.15, 0, -.93))] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.3, friction=1, model_scale=.5, pos=(-5.38, 0, -.93), r=24.60)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(5.37, 0, -.78))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(7.48, 0, -.78))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(4.74, 0, -1.95))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(6.88, 0, -1.95))] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, pos=(4.83, 0, -1.39))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, pos=(5.67, 0, -1.39))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, pos=(6.59, 0, -1.39))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.53, 0, -1.95), restitution=.95)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(2.63, 0, -1.95), restitution=.95)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-3.65, 0, 1.05), r=28, friction=0)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.27, 0, 1.72), restitution=.95)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.88, 0, 1.72), restitution=.95)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-1.67, 0, .55), restitution=.95)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(.52, 0, .55), restitution=.95)] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.5, pos=(-1.73, 0, 1.11))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.5, pos=(-.97, 0, 1.11))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.5, pos=(-.1, 0, 1.11))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + + def _instr_txt(self): + txt = _('Scene: ') + self.name() + '\n\n' + txt += _('Goal: every domino piece must be hit\n\n') + txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' + 'keep \5mouse_r\5 pressed to rotate an item') + return txt + + def _win_condition(self): + return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/game/scenes/scene_teeter_tooter.py b/game/scenes/scene_teeter_tooter.py new file mode 100644 index 0000000..51c5eb3 --- /dev/null +++ b/game/scenes/scene_teeter_tooter.py @@ -0,0 +1,46 @@ +from game.scene import Scene +from game.items.box import Box +from game.items.shelf import Shelf +from game.items.domino import Domino, UpStrategy, DownStrategy +from game.items.basketball import Basketball +from game.items.teetertooter import TeeterTooter + + +class SceneTeeterTooter(Scene): + + @staticmethod + def name(): + return _('Teeter tooter') + + def _set_items(self): + self.items = [] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=3, count=1, friction=1)] + #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=5, count=2)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-2.76, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.56, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(2.27, 0, -.28))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(4.38, 0, -.28))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(1.67, 0, -1.45))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(3.78, 0, -1.45))] + self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-2.74, 0, -1.20))] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=1, r=-25.30, friction=1, pos=(-2.78, 0, -.93))] + self.items += [Box(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=.2, friction=1, model_scale=.5, pos=(-3.61, 0, -.99), r=-25.30)] + self.items += [Shelf(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, mass=0, pos=(-.25, 0, -.57), r=52)] + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(1.73, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(2.57, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + self.items += [Domino(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, pos=(3.50, 0, -.89))] + self.items[-1].set_strategy(DownStrategy(self.items[-1]._np, 35)) + #self.items += [Basketball(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + #self.items += [TeeterTooter(self._world, self._mouse_plane_node, self.cb_inst, self.current_bottom, self.repos, count=3)] + + def _instr_txt(self): + txt = _('Scene: ') + self.name() + '\n\n' + txt += _('Goal: you must hit every domino piece\n\n') + txt += _('keep \5mouse_l\5 pressed to drag an item\n\n' + 'keep \5mouse_r\5 pressed to rotate an item') + return txt + + def _win_condition(self): + return all(itm.strategy.win_condition() for itm in self.items) and not self._paused diff --git a/game/sidepanel.py b/game/sidepanel.py new file mode 100644 index 0000000..ee59d7d --- /dev/null +++ b/game/sidepanel.py @@ -0,0 +1,132 @@ +from textwrap import dedent +from panda3d.core import GeomVertexData, GeomVertexFormat, Geom, \ + GeomVertexWriter, GeomTriangles, GeomNode, Shader, Point3, Plane, Vec3 +from ya2.lib.p3d.gfx import P3dGfxMgr + + +class SidePanel: + + def __init__(self, world, plane_node, top_l, bottom_r, y, items): + self._world = world + self._plane_node = plane_node + self._set((-1, 1), y) + self.update(items) + + def update(self, items): + p_from, p_to = P3dGfxMgr.world_from_to((-1, 1)) + for hit in self._world.ray_test_all(p_from, p_to).get_hits(): + if hit.get_node() == self._plane_node: + pos = hit.get_hit_pos() + y = 0 + corner = -20, 20 + for item in items: + if not item._instantiated: + bounds = item._np.get_tight_bounds() + if bounds[1][1] > y: + y = bounds[1][1] + icorner = item.get_corner() + icorner = P3dGfxMgr.screen_coord(icorner) + if icorner[0] > corner[0]: + corner = icorner[0], corner[1] + if icorner[1] < corner[1]: + corner = corner[0], icorner[1] + self._set((pos[0], pos[2]), y) + bounds = self._np.get_tight_bounds() + corner3d = bounds[1][0], bounds[1][1], bounds[0][2] + corner2d = P3dGfxMgr.screen_coord(corner3d) + plane = Plane(Vec3(0, 1, 0), y) + pos3d, near_pt, far_pt = Point3(), Point3(), Point3() + ar, margin = base.get_aspect_ratio(), .04 + x = corner[0] / ar if ar >= 1 else corner[0] + z = corner[1] * ar if ar < 1 else corner[1] + x += margin / ar if ar >= 1 else margin + z -= margin * ar if ar < 1 else margin + base.camLens.extrude((x, z), near_pt, far_pt) + plane.intersects_line( + pos3d, render.get_relative_point(base.camera, near_pt), + render.get_relative_point(base.camera, far_pt)) + corner_pos3d, near_pt, far_pt = Point3(), Point3(), Point3() + base.camLens.extrude((-1, 1), near_pt, far_pt) + plane.intersects_line( + corner_pos3d, render.get_relative_point(base.camera, near_pt), + render.get_relative_point(base.camera, far_pt)) + self._np.set_pos((pos3d + corner_pos3d) / 2) + scale = Vec3(pos3d[0] - corner_pos3d[0], 1, corner_pos3d[2] - pos3d[2]) + self._np.set_scale(scale) + + def _set(self, pos, y): + if hasattr(self, '_np'): + self._np.remove_node() + vdata = GeomVertexData('quad', GeomVertexFormat.get_v3(), Geom.UHStatic) + vdata.setNumRows(2) + vertex = GeomVertexWriter(vdata, 'vertex') + vertex.add_data3(.5, 0, -.5) + vertex.add_data3(.5, 0, .5) + vertex.add_data3(-.5, 0, .5) + vertex.add_data3(-.5, 0, -.5) + prim = GeomTriangles(Geom.UHStatic) + prim.add_vertices(0, 1, 2) + prim.add_vertices(0, 2, 3) + prim.close_primitive() + geom = Geom(vdata) + geom.add_primitive(prim) + node = GeomNode('gnode') + node.add_geom(geom) + self._np = render.attach_new_node(node) + self._np.setTransparency(True) + self._np.set_pos(pos[0], y, pos[1]) + vert = '''\ + #version 130 + uniform mat4 p3d_ModelViewProjectionMatrix; + in vec4 p3d_Vertex; + void main() { + gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; }''' + frag = '''\ + #version 130 + out vec4 p3d_FragColor; + void main() { + p3d_FragColor = vec4(.04, .04, .04, .08); }''' + self._np.set_shader(Shader.make(Shader.SL_GLSL, dedent(vert), dedent(frag))) + # mat = Material() + # mat.set_base_color((1, 1, 1, 1)) + # mat.set_emission((1, 1, 1, 1)) + # mat.set_metallic(.5) + # mat.set_roughness(.5) + # np.set_material(mat) + # texture_sz = 64 + # base_color_pnm = PNMImage(texture_sz, texture_sz) + # base_color_pnm.fill(.1, .1, .1) + # base_color_pnm.add_alpha() + # base_color_pnm.alpha_fill(.04) + # base_color_tex = Texture('base color') + # base_color_tex.load(base_color_pnm) + # ts = TextureStage('base color') + # ts.set_mode(TextureStage.M_modulate) + # np.set_texture(ts, base_color_tex) + # emission_pnm = PNMImage(texture_sz, texture_sz) + # emission_pnm.fill(0, 0, 0) + # emission_tex = Texture('emission') + # emission_tex.load(emission_pnm) + # ts = TextureStage('emission') + # ts.set_mode(TextureStage.M_emission) + # np.set_texture(ts, emission_tex) + # metal_rough_pnm = PNMImage(texture_sz, texture_sz) + # ambient_occlusion = 1 + # roughness = .5 + # metallicity = .5 + # metal_rough_pnm.fill(ambient_occlusion, roughness, metallicity) + # metal_rough_tex = Texture('ao metal roughness') + # metal_rough_tex.load(metal_rough_pnm) + # ts = TextureStage('ao metal roughness') + # ts.set_mode(TextureStage.M_selector) + # np.set_texture(ts, metal_rough_tex) + # normal_pnm = PNMImage(texture_sz, texture_sz) + # normal_pnm.fill(.5, .5, .1) + # normal_tex = Texture('normals') + # normal_tex.load(normal_pnm) + # ts = TextureStage('normals') + # ts.set_mode(TextureStage.M_normal) + # np.set_texture(ts, normal_tex) + + def destroy(self): + self._np.remove_node() diff --git a/main/main.py b/main/main.py index 08e6487..83c8adb 100644 --- a/main/main.py +++ b/main/main.py @@ -5,7 +5,7 @@ if '--version' in argv: load_prc_file_data('', 'window-type none') from os.path import exists from traceback import print_exc -from entities.app import PmachinesApp +from game.app import PmachinesApp from p3d_appimage import AppImageBuilder diff --git a/setup.py b/setup.py index 3dfd6b5..3e23b38 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ from ya2.build.lang import LanguageBuilder from p3d_appimage import AppImageBuilder from p3d_flatpak import FlatpakBuilder import ya2.engine.log # so logging's info/debug are logged -from entities.app import PmachinesApp +from game.app import PmachinesApp appname = longname = 'pmachines' diff --git a/ya2/build/screenshots.py b/ya2/build/screenshots.py index f1a16b9..0eb1261 100644 --- a/ya2/build/screenshots.py +++ b/ya2/build/screenshots.py @@ -3,7 +3,7 @@ from glob import glob from importlib import import_module from inspect import isclass from multiprocessing import Pool -from entities.scene import Scene +from game.scene import Scene def do_screenshot(cls):