Exemple #1
0
    def __init__(self,
                 camera,
                 debug=False,
                 audio3d=None,
                 client=None,
                 server=None):
        self.objects = {}

        self.incarnators = []

        self.collidables = set()

        self.updatables = set()
        self.updatables_to_add = set()
        self.garbage = set()
        self.scene = NodePath('world')

        # Set up the physics world. TODO: let maps set gravity.
        self.gravity = DEFAULT_GRAVITY
        self.physics = BulletWorld()
        self.physics.set_gravity(self.gravity)

        self.debug = debug

        if debug:
            debug_node = BulletDebugNode('Debug')
            debug_node.show_wireframe(True)
            debug_node.show_constraints(True)
            debug_node.show_bounding_boxes(False)
            debug_node.show_normals(False)
            np = self.scene.attach_new_node(debug_node)
            np.show()
            self.physics.set_debug_node(debug_node)
Exemple #2
0
    def __init__(self, camera, debug=False, audio3d=None, client=None, server=None):
        self.objects = {}

        self.incarnators = []

        self.collidables = set()

        self.updatables = set()
        self.updatables_to_add = set()
        self.garbage = set()
        self.scene = NodePath('world')


        # Set up the physics world. TODO: let maps set gravity.
        self.gravity = DEFAULT_GRAVITY
        self.physics = BulletWorld()
        self.physics.set_gravity(self.gravity)

        self.debug = debug

        if debug:
            debug_node = BulletDebugNode('Debug')
            debug_node.show_wireframe(True)
            debug_node.show_constraints(True)
            debug_node.show_bounding_boxes(False)
            debug_node.show_normals(False)
            np = self.scene.attach_new_node(debug_node)
            np.show()
            self.physics.set_debug_node(debug_node)
Exemple #3
0
 def __init__(self, camera, debug=False, audio3d=None, client=None, server=None):
     self.objects = {}
     self.incarnators = []
     self.collidables = set()
     self.updatables = set()
     self.updatables_to_add = set()
     self.garbage = set()
     self.render = NodePath('world')
     self.camera = camera
     self.audio3d = audio3d
     self.ambient = self._make_ambient()
     self.celestials = CompositeObject()
     self.sky = self.attach(Sky())
     # Set up the physics world. TODO: let maps set gravity.
     self.gravity = DEFAULT_GRAVITY
     self.physics = BulletWorld()
     self.physics.set_gravity(self.gravity)
     self.debug = debug
     self.client = client
     self.server = server
     if debug:
         debug_node = BulletDebugNode('Debug')
         debug_node.show_wireframe(True)
         debug_node.show_constraints(True)
         debug_node.show_bounding_boxes(False)
         debug_node.show_normals(False)
         np = self.render.attach_new_node(debug_node)
         np.show()
         self.physics.set_debug_node(debug_node)
Exemple #4
0
 def __init__(self,
              camera,
              debug=False,
              audio3d=None,
              client=None,
              server=None):
     self.objects = {}
     self.incarnators = []
     self.collidables = set()
     self.updatables = set()
     self.updatables_to_add = set()
     self.garbage = set()
     self.render = NodePath('world')
     self.camera = camera
     self.audio3d = audio3d
     self.ambient = self._make_ambient()
     self.celestials = CompositeObject()
     self.sky = self.attach(Sky())
     # Set up the physics world. TODO: let maps set gravity.
     self.gravity = DEFAULT_GRAVITY
     self.physics = BulletWorld()
     self.physics.set_gravity(self.gravity)
     self.debug = debug
     self.client = client
     self.server = server
     if debug:
         debug_node = BulletDebugNode('Debug')
         debug_node.show_wireframe(True)
         debug_node.show_constraints(True)
         debug_node.show_bounding_boxes(False)
         debug_node.show_normals(False)
         np = self.render.attach_new_node(debug_node)
         np.show()
         self.physics.set_debug_node(debug_node)
Exemple #5
0
 def bullet_debug(self):
     debug_node = BulletDebugNode('Debug')
     debug_node.show_wireframe(True)
     debug_node.show_constraints(True)
     debug_node.show_bounding_boxes(False)
     debug_node.show_normals(False)
     self.debug_np = self.render.attach_new_node(debug_node)
     self.environment.physics_world.set_debug_node(debug_node)
    def setup_debug(self):
        """Set up a debug node, which will render skeletons for all the
        physics objects in the physics world."""

        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.world.set_debug_node(debug_node)
        self.debug_np = self.render.attach_new_node(debug_node)
Exemple #7
0
    def setup_debug(self):
        """Set up a debug node, which will render skeletons for all the
        physics objects in the physics world."""

        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.world.set_debug_node(debug_node)
        self.debug_np = self.render.attach_new_node(debug_node)
Exemple #8
0
 def setup(self):
     self.node = NodePath('world')
     self.node.set_transparency(TransparencyAttrib.MAlpha)
     if self.debug:
         d = BulletDebugNode('Debug')
         d.show_wireframe(True)
         d.show_normals(True)
         self.node.attach_new_node(d).show()
         self.physics.set_debug_node(d)
     if self.camera:
         self.camera.node().get_lens().set_fov(80.0, 50.0)
         self.camera.node().get_lens().set_near(0.1)
     # Default ambient light
     alight = AmbientLight('ambient')
     alight.set_color(DEFAULT_AMBIENT_COLOR)
     self.ambient = self.node.attach_new_node(alight)
     self.node.set_light(self.ambient)
     # Default directional lights
     self.add_celestial(math.radians(20), math.radians(45), (1, 1, 1, 1), 0.4, 30.0)
     self.add_celestial(math.radians(200), math.radians(20), (1, 1, 1, 1), 0.3, 30.0)
Exemple #9
0
    def __init__(self):
        load_prc_file_data(
            "", """
            win-size 1920 1080
            window-title Panda3D Arena Sample FPS Bullet Auto Colliders PBR HW Skinning
            show-frame-rate-meter #t
            framebuffer-srgb #t
            framebuffer-multisample 1
            multisamples 4
            view-frustum-cull 0
            textures-power-2 none
            hardware-animated-vertices #t
            gl-depth-zero-to-one true
            clock-frame-rate 60
            interpolate-frames 1
            cursor-hidden #t
            fullscreen #f
        """)

        # Initialize the showbase
        super().__init__()
        gltf.patch_loader(self.loader)

        props = WindowProperties()
        props.set_mouse_mode(WindowProperties.M_relative)
        base.win.request_properties(props)
        base.set_background_color(0.5, 0.5, 0.8)

        self.camLens.set_fov(80)
        self.camLens.set_near_far(0.01, 90000)
        self.camLens.set_focal_length(7)
        # self.camera.set_pos(0, 0, 2)

        # ConfigVariableManager.getGlobalPtr().listVariables()

        # point light generator
        for x in range(0, 3):
            plight_1 = PointLight('plight')
            # add plight props here
            plight_1_node = self.render.attach_new_node(plight_1)
            # group the lights close to each other to create a sun effect
            plight_1_node.set_pos(random.uniform(-21, -20),
                                  random.uniform(-21, -20),
                                  random.uniform(20, 21))
            self.render.set_light(plight_1_node)

        # point light for volumetric lighting filter
        plight_1 = PointLight('plight')
        # add plight props here
        plight_1_node = self.render.attach_new_node(plight_1)
        # group the lights close to each other to create a sun effect
        plight_1_node.set_pos(random.uniform(-21, -20),
                              random.uniform(-21, -20), random.uniform(20, 21))
        self.render.set_light(plight_1_node)

        scene_filters = CommonFilters(base.win, base.cam)
        scene_filters.set_bloom()
        scene_filters.set_high_dynamic_range()
        scene_filters.set_exposure_adjust(0.6)
        scene_filters.set_gamma_adjust(1.1)
        # scene_filters.set_volumetric_lighting(plight_1_node, 32, 0.5, 0.7, 0.1)
        # scene_filters.set_blur_sharpen(0.9)
        # scene_filters.set_ambient_occlusion(32, 0.05, 2.0, 0.01, 0.000002)

        self.accept("f3", self.toggle_wireframe)
        self.accept("escape", sys.exit, [0])

        exponential_fog = Fog('world_fog')
        exponential_fog.set_color(0.6, 0.7, 0.7)
        # this is a very low fog value, set it higher for a greater effect
        exponential_fog.set_exp_density(0.00009)
        self.render.set_fog(exponential_fog)

        self.game_start = 0

        from panda3d.bullet import BulletWorld
        from panda3d.bullet import BulletCharacterControllerNode
        from panda3d.bullet import ZUp
        from panda3d.bullet import BulletCapsuleShape
        from panda3d.bullet import BulletTriangleMesh
        from panda3d.bullet import BulletTriangleMeshShape
        from panda3d.bullet import BulletBoxShape
        from panda3d.bullet import BulletGhostNode
        from panda3d.bullet import BulletRigidBodyNode
        from panda3d.bullet import BulletPlaneShape

        self.world = BulletWorld()
        self.world.set_gravity(Vec3(0, 0, -9.81))

        arena_1 = self.loader.load_model('models/arena_1.gltf')
        arena_1.reparent_to(self.render)
        arena_1.set_pos(0, 0, 0)

        def make_collision_from_model(input_model, node_number, mass, world,
                                      target_pos, h_adj):
            # tristrip generation from static models
            # generic tri-strip collision generator begins
            geom_nodes = input_model.find_all_matches('**/+GeomNode')
            geom_nodes = geom_nodes.get_path(node_number).node()
            # print(geom_nodes)
            geom_target = geom_nodes.get_geom(0)
            # print(geom_target)
            output_bullet_mesh = BulletTriangleMesh()
            output_bullet_mesh.add_geom(geom_target)
            tri_shape = BulletTriangleMeshShape(output_bullet_mesh,
                                                dynamic=False)
            print(output_bullet_mesh)

            body = BulletRigidBodyNode('input_model_tri_mesh')
            np = self.render.attach_new_node(body)
            np.node().add_shape(tri_shape)
            np.node().set_mass(mass)
            np.node().set_friction(0.01)
            np.set_pos(target_pos)
            np.set_scale(1)
            np.set_h(h_adj)
            # np.set_p(180)
            # np.set_r(180)
            np.set_collide_mask(BitMask32.allOn())
            world.attach_rigid_body(np.node())

        make_collision_from_model(arena_1, 0, 0, self.world,
                                  (arena_1.get_pos()), 0)

        # load the scene shader
        scene_shader = Shader.load(Shader.SL_GLSL,
                                   "shaders/simplepbr_vert_mod_1.vert",
                                   "shaders/simplepbr_frag_mod_1.frag")
        self.render.set_shader(scene_shader)
        self.render.set_antialias(AntialiasAttrib.MMultisample)
        scene_shader_attrib = ShaderAttrib.make(scene_shader)
        scene_shader_attrib = scene_shader_attrib.setFlag(
            ShaderAttrib.F_hardware_skinning, True)

        # initialize player character physics the Bullet way
        shape_1 = BulletCapsuleShape(0.75, 0.5, ZUp)
        player_node = BulletCharacterControllerNode(
            shape_1, 0.1, 'Player')  # (shape, mass, player name)
        # player_node.set_max_slope(0.1)
        # player_node.set_linear_movement(1, True)
        player_np = self.render.attach_new_node(player_node)
        player_np.set_pos(-20, -10, 10)
        player_np.set_collide_mask(BitMask32.allOn())
        self.world.attach_character(player_np.node())
        # cast player_np to self.player
        self.player = player_np

        # reparent player character to render node
        fp_character = actor_data.player_character
        fp_character.reparent_to(self.render)
        fp_character.set_scale(1)
        # set the actor skinning hardware shader
        fp_character.set_attrib(scene_shader_attrib)

        self.camera.reparent_to(self.player)
        # reparent character to FPS cam
        fp_character.reparent_to(self.player)
        fp_character.set_pos(0, 0, -0.95)
        # self.camera.set_x(self.player, 1)
        self.camera.set_y(self.player, 0.03)
        self.camera.set_z(self.player, 0.5)

        # player gun begins
        self.player_gun = actor_data.arm_handgun
        self.player_gun.reparent_to(self.render)
        self.player_gun.reparent_to(self.camera)
        self.player_gun.set_x(self.camera, 0.1)
        self.player_gun.set_y(self.camera, 0.4)
        self.player_gun.set_z(self.camera, -0.1)

        # directly make a text node to display text
        text_1 = TextNode('text_1_node')
        text_1.set_text("")
        text_1_node = self.aspect2d.attach_new_node(text_1)
        text_1_node.set_scale(0.05)
        text_1_node.set_pos(-1.4, 0, 0.92)
        # import font and set pixels per unit font quality
        nunito_font = loader.load_font('fonts/Nunito/Nunito-Light.ttf')
        nunito_font.set_pixels_per_unit(100)
        nunito_font.set_page_size(512, 512)
        # apply font
        text_1.set_font(nunito_font)
        # small caps
        # text_1.set_small_caps(True)

        # on-screen target dot for aiming
        target_dot = TextNode('target_dot_node')
        target_dot.set_text(".")
        target_dot_node = self.aspect2d.attach_new_node(target_dot)
        target_dot_node.set_scale(0.075)
        target_dot_node.set_pos(0, 0, 0)
        # target_dot_node.hide()
        # apply font
        target_dot.set_font(nunito_font)
        target_dot.set_align(TextNode.ACenter)
        # see the Task section for relevant dot update logic

        # directly make a text node to display text
        text_2 = TextNode('text_2_node')
        text_2.set_text("Neutralize the NPC by shooting the head." + '\n' +
                        "Press 'f' to toggle the flashlight.")
        text_2_node = self.aspect2d.attach_new_node(text_2)
        text_2_node.set_scale(0.04)
        text_2_node.set_pos(-1.4, 0, 0.8)
        # import font and set pixels per unit font quality
        nunito_font = self.loader.load_font('fonts/Nunito/Nunito-Light.ttf')
        nunito_font.set_pixels_per_unit(100)
        nunito_font.set_page_size(512, 512)
        # apply font
        text_2.set_font(nunito_font)
        text_2.set_text_color(0, 0.3, 1, 1)

        # print player position on mouse click
        def print_player_pos():
            print(self.player.get_pos())
            self.player.node().do_jump()

        self.accept('mouse3', print_player_pos)

        self.flashlight_state = 0

        def toggle_flashlight():
            current_flashlight = self.render.find_all_matches("**/flashlight")

            if self.flashlight_state == 0:
                if len(current_flashlight) == 0:
                    self.slight = 0
                    self.slight = Spotlight('flashlight')
                    self.slight.set_shadow_caster(True, 1024, 1024)
                    self.slight.set_color(VBase4(0.5, 0.6, 0.6,
                                                 1))  # slightly bluish
                    lens = PerspectiveLens()
                    lens.set_near_far(0.5, 5000)
                    self.slight.set_lens(lens)
                    self.slight.set_attenuation((0.5, 0, 0.0000005))
                    self.slight = self.render.attach_new_node(self.slight)
                    self.slight.set_pos(-0.1, 0.3, -0.4)
                    self.slight.reparent_to(self.camera)
                    self.flashlight_state = 1
                    self.render.set_light(self.slight)

                elif len(current_flashlight) > 0:
                    self.render.set_light(self.slight)
                    self.flashlight_state = 1

            elif self.flashlight_state > 0:
                self.render.set_light_off(self.slight)
                self.flashlight_state = 0

        self.accept('f', toggle_flashlight)

        # add a few random physics boxes
        for x in range(0, 40):
            # dynamic collision
            random_vec = Vec3(1, 1, 1)
            special_shape = BulletBoxShape(random_vec)
            # rigidbody
            body = BulletRigidBodyNode('random_prisms')
            d_coll = self.render.attach_new_node(body)
            d_coll.node().add_shape(special_shape)
            d_coll.node().set_mass(0.9)
            d_coll.node().set_friction(0.5)
            d_coll.set_collide_mask(BitMask32.allOn())
            # turn on Continuous Collision Detection
            d_coll.node().set_ccd_motion_threshold(0.000000007)
            d_coll.node().set_ccd_swept_sphere_radius(0.30)
            d_coll.node().set_deactivation_enabled(False)
            d_coll.set_pos(random.uniform(-60, -20), random.uniform(-60, -20),
                           random.uniform(5, 10))
            box_model = self.loader.load_model('models/1m_cube.gltf')
            box_model.reparent_to(self.render)
            box_model.reparent_to(d_coll)
            box_model.set_color(random.uniform(0, 1), random.uniform(0, 1),
                                random.uniform(0, 1), 1)
            self.world.attach_rigid_body(d_coll.node())

        # portal #1 begins
        # make a new texture buffer, render node, and attach a camera
        mirror_buffer = self.win.make_texture_buffer("mirror_buff", 4096, 4096)
        mirror_render = NodePath("mirror_render")
        mirror_render.set_shader(scene_shader)
        self.mirror_cam = self.make_camera(mirror_buffer)
        self.mirror_cam.reparent_to(mirror_render)
        self.mirror_cam.set_pos(0, -60, 5)
        self.mirror_cam.set_hpr(0, 25, 0)
        self.mirror_cam.node().get_lens().set_focal_length(10)
        self.mirror_cam.node().get_lens().set_fov(90)

        mirror_filters = CommonFilters(mirror_buffer, self.mirror_cam)
        # mirror_filters.set_high_dynamic_range()
        mirror_filters.set_exposure_adjust(1.1)
        # mirror_filters.set_gamma_adjust(1.3)

        # load in a mirror/display object model in normal render space
        self.mirror_model = self.loader.loadModel(
            'models/wide_screen_video_display.egg')
        self.mirror_model.reparent_to(self.render)
        self.mirror_model.set_pos(-20, 0, 1)
        self.mirror_model.set_sz(3)
        # self.mirror_model.flatten_strong()

        # mirror scene model load-in
        # reparent to mirror render node
        house_uv = self.loader.load_model('models/hangar_1.gltf')
        house_uv.reparent_to(mirror_render)
        windows = house_uv.find('**/clear_arches')
        windows.hide()
        house_uv.set_pos(0, 0, 0)
        house_uv.set_scale(1)

        # the portal ramp
        house_uv = self.loader.load_model('models/ramp_1.gltf')
        house_uv.reparent_to(mirror_render)
        house_uv.set_h(180)
        house_uv.set_scale(1.5)
        house_uv.set_pos(0, -50, 0)

        # mirror scene lighting
        # point light generator
        for x in range(0, 1):
            plight_1 = PointLight('plight')
            # add plight props here
            plight_1_node = mirror_render.attach_new_node(plight_1)
            # group the lights close to each other to create a sun effect
            plight_1_node.set_pos(random.uniform(-21, -20),
                                  random.uniform(-21, -20),
                                  random.uniform(20, 21))
            mirror_render.set_light(plight_1_node)

        # set the live buffer texture to the mirror/display model in normal render space
        self.mirror_model.set_texture(mirror_buffer.get_texture())

        # secret hangar far off somewhere on the graph
        house_uv = self.loader.load_model('models/hangar_1.gltf')
        house_uv.reparent_to(self.render)
        windows = house_uv.find('**/clear_arches')
        windows.hide()
        house_uv.set_pos(400, 400, -1)

        # the portal ring
        house_uv = self.loader.load_model('models/ring_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(90)
        house_uv.set_pos(-20, 0, -2)

        # the portal ramp
        house_uv = self.loader.load_model('models/ramp_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(0)
        house_uv.set_pos(-20, -5.5, 0)
        r_pos = house_uv.get_pos()
        make_collision_from_model(house_uv, 0, 0, self.world,
                                  (r_pos[0], r_pos[1], r_pos[2] + 1), 0)

        self.count_frames_1 = 0
        self.screen_cap_num = 1

        def make_screenshot():
            # Ensure the frame is rendered.
            base.graphicsEngine.render_frame()

            # Grab the screenshot into a big image
            full = PNMImage()
            base.win.get_screenshot(full)

            # Now reduce it
            reduced = PNMImage(500, 300)
            reduced.gaussianFilterFrom(1, full)

            # And write it out.
            reduced.write(
                Filename('screen_cap_' + str(self.screen_cap_num) + '.jpg'))

            self.screen_cap_num += 1

        def update_portal_cam(Task):
            if self.count_frames_1 < 30:
                self.count_frames_1 += 1

            if self.count_frames_1 == 15:
                pass
                # make_screenshot()

            if self.count_frames_1 == 29:
                self.count_frames_1 = 0

            p_dist = (self.player.get_pos() -
                      self.mirror_model.get_pos(base.render)).length()
            target_fov = 115

            if p_dist < 2.25:
                target_fov = 145
                self.player.set_pos(400, 400, 3)
                # adjust the ground plane
                self.ground_plane.set_pos(0, 0, 0)
                # move the normal point lights
                lights = self.render.find_all_matches('**/plight*')
                for l in lights:
                    l.set_pos(400, 400, 21)

            player_h = self.player.get_h()
            self.mirror_cam.set_h(player_h)
            self.mirror_cam.node().get_lens().set_fov(target_fov)

            return Task.cont

        self.task_mgr.add(update_portal_cam)

        # portal #2 begins
        # the portal ring
        house_uv = self.loader.load_model('models/ring_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(90)
        house_uv.set_pos(400, 400, -2)

        # the portal ramp
        house_uv = self.loader.load_model('models/ramp_1.gltf')
        house_uv.reparent_to(self.render)
        house_uv.set_h(180)
        house_uv.set_pos(400, 405.5, 0)
        r_pos = house_uv.get_pos()
        make_collision_from_model(house_uv, 0, 0, self.world,
                                  (r_pos[0], r_pos[1], r_pos[2] + 1), 180)

        # NPC_1 load-in
        comp_shape_1 = BulletCapsuleShape(0.05, 0.01, ZUp)
        npc_1_node = BulletCharacterControllerNode(
            comp_shape_1, 0.2, 'NPC_A_node')  # (shape, mass, character name)
        np = self.render.attach_new_node(npc_1_node)
        np.set_pos(-40, -40, 5)
        np.set_collide_mask(BitMask32.allOn())
        self.world.attach_character(np.node())
        np.set_h(random.randint(0, 180))
        npc_model_1 = actor_data.NPC_1
        npc_model_1.reparent_to(np)
        # set the actor skinning hardware shader
        npc_model_1.set_attrib(scene_shader_attrib)
        # get the separate head model
        npc_1_head = self.loader.load_model('models/npc_1_head.gltf')
        npc_1_head.reparent_to(actor_data.NPC_1.get_parent())

        # npc base animation loop
        npc_1_control = actor_data.NPC_1.get_anim_control('walking')
        if not npc_1_control.is_playing():
            actor_data.NPC_1.stop()
            actor_data.NPC_1.loop("walking", fromFrame=60, toFrame=180)
            actor_data.NPC_1.set_play_rate(6.0, 'walking')

        # create special hit areas
        # use Task section for npc collision movement logic
        # special head node size
        special_shape = BulletBoxShape(Vec3(0.1, 0.1, 0.1))
        # ghost npc node
        body = BulletGhostNode('special_node_A')
        special_node = self.render.attach_new_node(body)
        special_node.node().add_shape(
            special_shape, TransformState.makePos(Point3(0, 0, 0.4)))
        # special_node.node().set_mass(0)
        # special_node.node().set_friction(0.5)
        special_node.set_collide_mask(BitMask32(0x0f))
        # turn on Continuous Collision Detection
        special_node.node().set_deactivation_enabled(False)
        special_node.node().set_ccd_motion_threshold(0.000000007)
        special_node.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_ghost(special_node.node())

        # dynamic collision
        special_shape = BulletBoxShape(Vec3(0.3, 0.15, 0.6))
        # rigidbody npc node
        body = BulletRigidBodyNode('d_coll_A')
        d_coll = self.render.attach_new_node(body)
        d_coll.node().add_shape(special_shape,
                                TransformState.makePos(Point3(0, 0, 0.7)))
        # d_coll.node().set_mass(0)
        d_coll.node().set_friction(0.5)
        d_coll.set_collide_mask(BitMask32.allOn())
        # turn on Continuous Collision Detection
        d_coll.node().set_deactivation_enabled(False)
        d_coll.node().set_ccd_motion_threshold(0.000000007)
        d_coll.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_rigid_body(d_coll.node())

        # npc state variables
        self.npc_1_is_dead = False
        self.npc_1_move_increment = Vec3(0, 0, 0)
        self.gun_anim_is_playing = False

        # npc movement timer
        def npc_1_move_gen():
            while not self.npc_1_is_dead:
                m_incs = []
                for x in range(0, 2):
                    m_incs.append(random.uniform(2, 5))

                print('NPC_1 movement increments this cycle: ' + str(m_incs))
                self.npc_1_move_increment[0] = m_incs[0]
                self.npc_1_move_increment[1] = m_incs[1]
                time.sleep(3)
                self.npc_1_move_increment[0] = m_incs[0]
                self.npc_1_move_increment[1] = m_incs[1]
                time.sleep(3)
                self.npc_1_move_increment[0] = (-1 * m_incs[0]) * 2
                self.npc_1_move_increment[1] = (-1 * m_incs[1]) * 2
                time.sleep(3)
                self.npc_1_move_increment[0] = 0
                self.npc_1_move_increment[1] = 0

        # activate the movement timer in a dedicated thread to prevent lockup with .sleep()
        threading2._start_new_thread(npc_1_move_gen, ())

        def is_npc_1_shot():
            # animate the gun
            gun_ctrl = actor_data.arm_handgun.get_anim_control('shoot')
            if not gun_ctrl.is_playing():
                actor_data.arm_handgun.stop()
                actor_data.arm_handgun.play("shoot")
                actor_data.arm_handgun.set_play_rate(15.0, 'shoot')

            # target dot ray test
            # get mouse data
            mouse_watch = base.mouseWatcherNode
            if mouse_watch.has_mouse():
                posMouse = base.mouseWatcherNode.get_mouse()
                posFrom = Point3()
                posTo = Point3()
                base.camLens.extrude(posMouse, posFrom, posTo)
                posFrom = self.render.get_relative_point(base.cam, posFrom)
                posTo = self.render.get_relative_point(base.cam, posTo)
                rayTest = self.world.ray_test_closest(posFrom, posTo)
                target = rayTest.get_node()
                target_dot = self.aspect2d.find_all_matches(
                    "**/target_dot_node")

                if 'special_node_A' in str(target):

                    def npc_cleanup():
                        # the head is hit, the npc is dead
                        self.npc_1_is_dead = True
                        text_2.set_text('Congrats, you have won!')
                        npc_1_control = actor_data.NPC_1.get_anim_control(
                            'walking')
                        if npc_1_control.is_playing():
                            actor_data.NPC_1.stop()
                        npc_1_control = actor_data.NPC_1.get_anim_control(
                            'death')
                        if not npc_1_control.is_playing():
                            actor_data.NPC_1.play('death')

                        # Bullet node removals
                        self.world.remove(target)
                        rigid_target = self.render.find('**/d_coll_A')
                        self.world.remove(rigid_target.node())

                    threading2._start_new_thread(npc_cleanup, ())

        self.accept('mouse1', is_npc_1_shot)

        def smooth_load_physics():
            # this is a patch to speed up the cold start hitch on success condition
            # Bullet node removals
            self.world.remove(special_node.node())
            rigid_target = self.render.find('**/d_coll_A')
            self.world.remove(rigid_target.node())
            print('NPC physics init removed.')

        smooth_load_physics()

        # repeat the NPC physics initialization after smooth_load_physics
        # create special hit areas
        # use Task section for npc collision movement logic
        # special head node size
        special_shape = BulletBoxShape(Vec3(0.1, 0.1, 0.1))
        # ghost npc node
        body = BulletGhostNode('special_node_A')
        special_node = self.render.attach_new_node(body)
        special_node.node().add_shape(
            special_shape, TransformState.makePos(Point3(0, 0, 0.4)))
        # special_node.node().set_mass(0)
        # special_node.node().set_friction(0.5)
        special_node.set_collide_mask(BitMask32(0x0f))
        # turn on Continuous Collision Detection
        special_node.node().set_deactivation_enabled(False)
        special_node.node().set_ccd_motion_threshold(0.000000007)
        special_node.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_ghost(special_node.node())

        # dynamic collision
        special_shape = BulletBoxShape(Vec3(0.3, 0.15, 0.6))
        # rigidbody npc node
        body = BulletRigidBodyNode('d_coll_A')
        d_coll = self.render.attach_new_node(body)
        d_coll.node().add_shape(special_shape,
                                TransformState.makePos(Point3(0, 0, 0.7)))

        # d_coll.node().set_mass(0)
        d_coll.node().set_friction(0.5)
        d_coll.set_collide_mask(BitMask32.allOn())
        # turn on Continuous Collision Detection
        d_coll.node().set_deactivation_enabled(False)
        d_coll.node().set_ccd_motion_threshold(0.000000007)
        d_coll.node().set_ccd_swept_sphere_radius(0.30)
        self.world.attach_rigid_body(d_coll.node())

        # 3D player movement system begins
        self.keyMap = {
            "left": 0,
            "right": 0,
            "forward": 0,
            "backward": 0,
            "run": 0,
            "jump": 0
        }

        def setKey(key, value):
            self.keyMap[key] = value

        # define button map
        self.accept("a", setKey, ["left", 1])
        self.accept("a-up", setKey, ["left", 0])
        self.accept("d", setKey, ["right", 1])
        self.accept("d-up", setKey, ["right", 0])
        self.accept("w", setKey, ["forward", 1])
        self.accept("w-up", setKey, ["forward", 0])
        self.accept("s", setKey, ["backward", 1])
        self.accept("s-up", setKey, ["backward", 0])
        self.accept("shift", setKey, ["run", 1])
        self.accept("shift-up", setKey, ["run", 0])
        self.accept("space", setKey, ["jump", 1])
        self.accept("space-up", setKey, ["jump", 0])
        # disable mouse
        self.disable_mouse()

        # the player movement speed
        self.movementSpeedForward = 5
        self.movementSpeedBackward = 5
        self.dropSpeed = -0.2
        self.striveSpeed = 6
        self.ease = -10.0
        self.static_pos_bool = False
        self.static_pos = Vec3()

        def move(Task):
            if self.game_start > 0:
                if not self.npc_1_is_dead:
                    npc_pos_1 = actor_data.NPC_1.get_parent().get_pos()
                    # place head hit box
                    special_node.set_pos(npc_pos_1[0], npc_pos_1[1],
                                         npc_pos_1[2] + 1)
                    special_node.set_h(actor_data.NPC_1.get_h())
                    # dynamic collision node
                    d_coll.set_pos(npc_pos_1[0], npc_pos_1[1], npc_pos_1[2])
                    d_coll.set_h(actor_data.NPC_1.get_h())
                    # make the npc look at the player continuously
                    actor_data.NPC_1.look_at(self.player)
                    npc_1_head.look_at(self.player)

                    if actor_data.NPC_1.get_p() > 3:
                        actor_data.NPC_1.set_p(3)

                    if npc_1_head.get_p() > 3:
                        npc_1_head.set_p(3)

                    m_inst = self.npc_1_move_increment
                    t_inst = globalClock.get_dt()
                    actor_data.NPC_1.get_parent().set_pos(
                        npc_pos_1[0] + (m_inst[0] * t_inst),
                        npc_pos_1[1] + (m_inst[1] * t_inst), npc_pos_1[2])

                if self.npc_1_is_dead:
                    npc_1_head.hide()
                    inst_h = actor_data.NPC_1.get_h()
                    inst_p = actor_data.NPC_1.get_p()
                    actor_data.NPC_1.set_hpr(inst_h, inst_p, 0)

                # target dot ray test
                # turns the target dot red
                # get mouse data
                mouse_watch = base.mouseWatcherNode
                if mouse_watch.has_mouse():
                    posMouse = base.mouseWatcherNode.get_mouse()
                    posFrom = Point3()
                    posTo = Point3()
                    base.camLens.extrude(posMouse, posFrom, posTo)
                    posFrom = self.render.get_relative_point(base.cam, posFrom)
                    posTo = self.render.get_relative_point(base.cam, posTo)
                    rayTest = self.world.ray_test_closest(posFrom, posTo)
                    target = rayTest.get_node()
                    target_dot = self.aspect2d.find_all_matches(
                        "**/target_dot_node")

                    if 'special_node_A' in str(target):
                        # the npc is recognized, make the dot red
                        for dot in target_dot:
                            dot.node().set_text_color(0.9, 0.1, 0.1, 1)

                    if 'd_coll_A' in str(target):
                        # the npc is recognized, make the dot red
                        for dot in target_dot:
                            dot.node().set_text_color(0.9, 0.1, 0.1, 1)

                    if 'special_node_A' not in str(target):
                        # no npc recognized, make the dot white
                        if 'd_coll_A' not in str(target):
                            for dot in target_dot:
                                dot.node().set_text_color(1, 1, 1, 1)

                # get mouse data
                mouse_watch = base.mouseWatcherNode
                if mouse_watch.has_mouse():
                    pointer = base.win.get_pointer(0)
                    mouseX = pointer.get_x()
                    mouseY = pointer.get_y()

                # screen sizes
                window_Xcoord_halved = base.win.get_x_size() // 2
                window_Ycoord_halved = base.win.get_y_size() // 2
                # mouse speed
                mouseSpeedX = 0.2
                mouseSpeedY = 0.2
                # maximum and minimum pitch
                maxPitch = 90
                minPitch = -50
                # cam view target initialization
                camViewTarget = LVecBase3f()

                if base.win.movePointer(0, window_Xcoord_halved,
                                        window_Ycoord_halved):
                    p = 0

                    if mouse_watch.has_mouse():
                        # calculate the pitch of camera
                        p = self.camera.get_p() - (
                            mouseY - window_Ycoord_halved) * mouseSpeedY

                    # sanity checking
                    if p < minPitch:
                        p = minPitch
                    elif p > maxPitch:
                        p = maxPitch

                    if mouse_watch.has_mouse():
                        # directly set the camera pitch
                        self.camera.set_p(p)
                        camViewTarget.set_y(p)

                    # rotate the self.player's heading according to the mouse x-axis movement
                    if mouse_watch.has_mouse():
                        h = self.player.get_h() - (
                            mouseX - window_Xcoord_halved) * mouseSpeedX

                    if mouse_watch.has_mouse():
                        # sanity checking
                        if h < -360:
                            h += 360

                        elif h > 360:
                            h -= 360

                        self.player.set_h(h)
                        camViewTarget.set_x(h)

                    # hide the gun if looking straight down
                    if p < -30:
                        self.player_gun.hide()
                    if p > -30:
                        self.player_gun.show()

                if self.keyMap["left"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_x(self.player,
                                      -self.striveSpeed * globalClock.get_dt())

                    myAnimControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myAnimControl.isPlaying():
                        actor_data.player_character.play("walking")
                        actor_data.player_character.set_play_rate(
                            4.0, 'walking')

                if not self.keyMap["left"]:
                    if not self.static_pos_bool:
                        self.static_pos_bool = True
                        self.static_pos = self.player.get_pos()

                    self.player.set_x(self.static_pos[0])
                    self.player.set_y(self.static_pos[1])
                    self.player.set_z(self.player,
                                      self.dropSpeed * globalClock.get_dt())

                if self.keyMap["right"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_x(self.player,
                                      self.striveSpeed * globalClock.get_dt())

                    myAnimControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myAnimControl.isPlaying():
                        actor_data.player_character.play("walking")
                        actor_data.player_character.set_play_rate(
                            4.0, 'walking')

                if not self.keyMap["right"]:
                    if not self.static_pos_bool:
                        self.static_pos_bool = True
                        self.static_pos = self.player.get_pos()

                    self.player.set_x(self.static_pos[0])
                    self.player.set_y(self.static_pos[1])
                    self.player.set_z(self.player,
                                      self.dropSpeed * globalClock.get_dt())

                if self.keyMap["forward"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_y(
                        self.player,
                        self.movementSpeedForward * globalClock.get_dt())

                    myAnimControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myAnimControl.isPlaying():
                        actor_data.player_character.play("walking")
                        actor_data.player_character.set_play_rate(
                            4.0, 'walking')

                if self.keyMap["forward"] != 1:
                    if not self.static_pos_bool:
                        self.static_pos_bool = True
                        self.static_pos = self.player.get_pos()

                    self.player.set_x(self.static_pos[0])
                    self.player.set_y(self.static_pos[1])
                    self.player.set_z(self.player,
                                      self.dropSpeed * globalClock.get_dt())

                if self.keyMap["backward"]:
                    if self.static_pos_bool:
                        self.static_pos_bool = False

                    self.player.set_y(
                        self.player,
                        -self.movementSpeedBackward * globalClock.get_dt())

                    myBackControl = actor_data.player_character.get_anim_control(
                        'walking')
                    if not myBackControl.isPlaying():
                        myBackControl.stop()
                        actor_data.player_character.play('walking')
                        actor_data.player_character.set_play_rate(
                            -4.0, 'walking')

            return Task.cont

        # infinite ground plane
        # the effective world-Z limit
        ground_plane = BulletPlaneShape(Vec3(0, 0, 1), 0)
        node = BulletRigidBodyNode('ground')
        node.add_shape(ground_plane)
        node.set_friction(0.1)
        self.ground_plane = self.render.attach_new_node(node)
        self.ground_plane.set_pos(0, 0, -1)
        self.world.attach_rigid_body(node)

        # Bullet debugger
        from panda3d.bullet import BulletDebugNode
        debugNode = BulletDebugNode('Debug')
        debugNode.show_wireframe(True)
        debugNode.show_constraints(True)
        debugNode.show_bounding_boxes(False)
        debugNode.show_normals(False)
        debugNP = self.render.attach_new_node(debugNode)
        self.world.set_debug_node(debugNP.node())

        # debug toggle function
        def toggle_debug():
            if debugNP.is_hidden():
                debugNP.show()
            else:
                debugNP.hide()

        self.accept('f1', toggle_debug)

        def update(Task):
            if self.game_start < 1:
                self.game_start = 1
            return Task.cont

        def physics_update(Task):
            dt = globalClock.get_dt()
            self.world.do_physics(dt)
            return Task.cont

        self.task_mgr.add(move)
        self.task_mgr.add(update)
        self.task_mgr.add(physics_update)