class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.loc_text = addTextField(0.45, "[LOC]: ")
        self.imageObject = OnscreenImage(image='models/intro.jpg',
                                         pos=(-0, 0, 0.02))
        self.imageObject.setScale(1.6, 1.2, 1.2)
        base.graphicsEngine.renderFrame()
        base.graphicsEngine.renderFrame()
        base.graphicsEngine.renderFrame()
        base.graphicsEngine.renderFrame()
        base.setBackgroundColor(0, 0, 0, 0)
        base.setFrameRateMeter(True)
        _sky = Vec4(0.01, 0.01, 0.01, 0.01)
        _clouds = Vec4(0.01, 0.01, 0.01, 0.01)  # vx, vy, vx, vy

        base.cam.setPos(0, -20, 4)
        base.cam.lookAt(0, 0, 0)
        self.skyNP = loader.loadModel('models/sky')
        self.skyNP.reparentTo(render)
        self.skyNP.setScale(1000, 1000, 300)
        self.skyNP.setPos(0, 0, 0)
        self.skyNP.setTexture(loader.loadTexture('models/clouds.jpg'))
        self.skyNP.setShader(loader.loadShader('shaders/sky.sha'))
        self.skyNP.setShaderInput('sky', _sky)
        self.skyNP.setShaderInput('clouds', _clouds)

        # Light
        alight = AmbientLight('ambientLight')
        alight.setColor(Vec4(0.5, 0.5, 0.5, 1))
        alightNP = render.attachNewNode(alight)

        dlight = DirectionalLight('directionalLight')
        dlight.setDirection(Vec3(1, 1, -1))
        dlight.setColor(Vec4(0.7, 0.7, 0.7, 1))
        dlightNP = render.attachNewNode(dlight)

        render.clearLight()
        render.setLight(alightNP)
        render.setLight(dlightNP)

        # Input
        self.accept('escape', self.doExit)
        self.accept('r', self.doReset)
        self.accept('f1', self.toggleWireframe)
        self.accept('f2', self.toggleTexture)
        self.accept('f3', self.toggleDebug)
        self.accept('s', self.doScreenshot)

        self.accept('space', self.doJump)

        inputState.watchWithModifiers('forward', 'arrow_up')
        #inputState.watchWithModifiers('left', 'arrow_left')
        inputState.watchWithModifiers('reverse', 'arrow_down')
        #inputState.watchWithModifiers('right', 'arrow_right')
        inputState.watchWithModifiers('turnLeft', 'arrow_left')
        inputState.watchWithModifiers('turnRight', 'arrow_right')

        # Task
        taskMgr.add(self.update, 'updateWorld')

        # Physics
        self.worldNP = render.attachNewNode('World')

        # World
        self.debugNP = self.worldNP.attachNewNode(BulletDebugNode('Debug'))
        self.debugNP.show()

        self.world = BulletWorld()
        self.world.setGravity(Vec3(0, 0, -9.81))
        # self.world.setDebugNode(self.debugNP.node())

        # Ground
        shape = BulletPlaneShape(Vec3(0, 0, 1), 0)

        #img = PNMImage(Filename('models/elevation2.png'))
        #shape = BulletHeightfieldShape(img, 1.0, ZUp)

        np = self.worldNP.attachNewNode(BulletRigidBodyNode('Ground'))
        np.node().addShape(shape)
        np.setPos(0, 0, -100)
        np.setCollideMask(BitMask32.allOn())

        self.world.attachRigidBody(np.node())

        # Box

        # Character
        self.crouching = False

        h = 2.75
        w = 0.9
        shape = BulletCapsuleShape(w, h - 2 * w, ZUp)

        self.character = BulletCharacterControllerNode(shape, 0.4, 'Player')
        self.characterNP = self.worldNP.attachNewNode(self.character)
        self.characterNP.setPos(-2, 0, 14)
        #self.characterNP.setPos(-4, 75, 74)

        self.characterNP.setCollideMask(BitMask32.allOn())
        self.world.attachCharacter(self.character)

        base.cam.reparentTo(self.characterNP)

        self.pvisualNP = Actor("models/panda-model",
                               {"walk": "models/panda-walk4"})
        self.pvisualNP.clearModelNodes()
        self.pvisualNP.setScale(0.003)
        self.pvisualNP.setZ(-1.2)
        self.pvisualNP.reparentTo(self.characterNP)
        self.pvisualNP.setH(180)
        #self.pvisualNP.loop("walk")
        self.accept('arrow_up', self.pandawalk)
        self.accept('arrow_up-up', self.pandastop)
        self.accept('arrow_down', self.pandawalk)
        self.accept('arrow_down-up', self.pandastop)
        '''
    visualNP.reparentTo(self.characterNP)
    visualNP = loader.loadModel('models/pilot-model')
    visualNP.clearModelNodes()
    visualNP.setScale(0.05)
    visualNP.setZ(-1.1)
    visualNP.setH(190)
    visualNP.reparentTo(self.characterNP)
    '''
        shape = BulletBoxShape(Vec3(2, 2, 0.2))

        self.Elev = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.Elev.node().setMass(0)
        self.Elev.node().addShape(shape)
        self.Elev.setPos(-0.4, 90, 5)
        self.Elev.setScale(2)
        self.Elev.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.Elev.node())

        visualNP = loader.loadModel('models/elevator')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.Elev)
        visualNP.setScale(0.5)
        shape = BulletBoxShape(Vec3(2, 2, 0.2))

        self.Elev2 = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.Elev2.node().setMass(0)
        self.Elev2.node().addShape(shape)
        self.Elev2.setPos(0, 0, 28)
        self.Elev2.setScale(2)
        self.Elev2.setH(180)

        self.Elev2.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.Elev2.node())

        visualNP = loader.loadModel('models/elevator')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.Elev2)
        visualNP.setScale(0.5)

        #self.makeblockwood(1,1,1,1,1,1)

        self.makeblock(0, 0, 0, 5, 5, 0.5)
        self.makeblock(0, 10, -1, 2, 20, 0.5)
        self.makeblock(0, 20, 0, 2, 3, 0.5)
        self.makeblock(0, 25, 1, 2, 3, 0.5)
        self.makeblock(0, 30, 2, 2, 3, 0.5)
        self.makeblock(0, 35, 3, 2, 3, 0.5)
        self.makeblock(0, 40, 4, 2, 3, 0.5)
        self.makeblock(0, 50, 4, 10, 20, 0.5)
        self.makeblockgold(-1, 50, 5, 1, 1, 1)
        self.makeblockcrate(0, 50, 6, 0.7, 0.7, 0.7)

        self.makeblockwood(1, 50, 5, 1, 1, 1)
        self.makeblocksilver(-2.5, 50, 5, 1, 1, 1)
        self.makeblockstone(2.5, 50, 5, 1, 1, 1)
        self.makeblock(0, 70, 4, 2, 20, 0.5)
        self.makeblock(0, 90, 4, 20, 20, 0.5)
        self.makeblock(0, 75, 20, 20, 20, 0.5)
        self.makeblockcrate(-1, 75, 25, 1, 1, 1)
        self.makeblockwood(-1, 75, 28, 1, 1, 1)
        self.makeblocksilver(-1, 75, 31, 1, 1, 1)
        self.makeblockgold(-1, 75, 34, 1, 1, 1)
        self.makeblockstone(-1, 75, 38, 1, 1, 1)
        self.makeblock(-0.53, 50, 21.58, 2, 20, 0.5)
        self.makeblock(0, 40, 23, 5, 5, 0.5)
        self.makeblock(0, 20, 23, 5, 5, 0.5)
        self.makeblock(0, 0, 23, 5, 5, 0.5)
        self.makeblock(0, 0, 26, 20, 20, 0.5)
        self.makeblock(0, 25, 46, 50, 50, 0.5)

        self.imageObject.hide()

    # _____HANDLER_____

    def doExit(self):
        self.cleanup()
        sys.exit(1)

    def pandawalk(self):
        self.pvisualNP.loop("walk")

    def pandastop(self):
        self.pvisualNP.stop()

    def doReset(self):
        self.characterNP.setPos(-2, 0, 14)
        self.Elev.setPos(-0.4, 90, 5)
        self.Elev2.setPos(0, 0, 28)

    def toggleWireframe(self):
        base.toggleWireframe()

    def toggleTexture(self):
        base.toggleTexture()

    def toggleDebug(self):
        if self.debugNP.isHidden():
            self.debugNP.show()
        else:
            self.debugNP.hide()

    def doScreenshot(self):
        base.screenshot('Bullet')

    def doJump(self):
        self.character.setMaxJumpHeight(5.0)
        self.character.setJumpSpeed(8.0)
        self.character.doJump()

    def doCrouch(self):
        self.crouching = not self.crouching
        sz = self.crouching and 0.6 or 1.0

        self.characterNP.setScale(Vec3(1, 1, sz))

        #self.character.getShape().setLocalScale(Vec3(1, 1, sz))
        #self.characterNP.setScale(Vec3(1, 1, sz) * 0.3048)
        #self.characterNP.setPos(0, 0, -1 * sz)

    # ____TASK___

    def processInput(self, dt):
        speed = Vec3(0, 0, 0)
        omega = 0.0

        if inputState.isSet('forward'): speed.setY(20.0)
        if inputState.isSet('reverse'): speed.setY(-20.0)
        if inputState.isSet('left'): speed.setX(-10.0)
        if inputState.isSet('right'): speed.setX(10.0)
        if inputState.isSet('turnLeft'): omega = 120.0
        if inputState.isSet('turnRight'): omega = -120.0

        self.character.setAngularMovement(omega)
        self.character.setLinearMovement(speed, True)

    def update(self, task):
        dt = globalClock.getDt()
        # self.characterNP.setHpr(0,0,0)
        base.cam.setHpr(0, -5, 0)

        self.processInput(dt)
        self.world.doPhysics(dt, 4, 1. / 240.)
        render.setShaderInput('time', task.time)
        #charpos = self.characterNP.getPos()
        # self.Elev.setHpr(0,0,0)
        self.Elevtask()
        self.Elevtask2()

        #import pickle
        #gg = pickle.dumps(charpos)
        # self.title.setText(gg)
        self.loc_text.setText('[LOC] : %03.2f, %03.2f,%03.2f ' % \
                              ( self.characterNP.getX(), self.characterNP.getY(), self.characterNP.getZ() ) )

        return task.cont

    def Elevtask(self):
        if not self.characterNP.node() or not self.Elev.node():
            return
        result = self.world.contactTestPair(self.characterNP.node(),
                                            self.Elev.node())
        for contact in result.getContacts():
            cp = contact.getManifoldPoint()
            node0 = contact.getNode0()
            node1 = contact.getNode1()
            dt = globalClock.getDt()
            # print('HI')
            #force = Vec3(0, 0, 0)
            self.ElevZ = self.Elev.getZ()
            print(self.ElevZ)

            #force.setZ( 100.0)
            #force *= 30.0
            #force = render.getRelativeVector(self.Elev, force)
            self.Elev.setZ(self.Elev.getZ() + 4 * dt)
#self.characterNP.setZ(self.Elev.getZ() + 30 * dt)


#self.characterNP.setH(180)

    def Elevtask2(self):
        if not self.characterNP.node() or not self.Elev2.node():
            return
        result = self.world.contactTestPair(self.characterNP.node(),
                                            self.Elev2.node())
        for contact in result.getContacts():
            cp = contact.getManifoldPoint()
            node0 = contact.getNode0()
            node1 = contact.getNode1()
            dt = globalClock.getDt()
            # print('HI')
            #force = Vec3(0, 0, 0)
            self.ElevZ2 = self.Elev2.getZ()
            print(self.ElevZ2)

            #force.setZ( 100.0)
            #force *= 30.0
            #force = render.getRelativeVector(self.Elev, force)
            self.Elev2.setZ(self.Elev2.getZ() + 4 * dt)
            #self.characterNP.setZ(self.Elev.getZ() + 30 * dt)
            #self.characterNP.setH(180)

    def cleanup(self):
        self.world = None
        self.worldNP.removeNode()

    def makeblock(self, x, y, z, xs, ys, zs):
        shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

        self.boxNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.boxNP.node().setMass(0.0)
        self.boxNP.node().addShape(shape)
        self.boxNP.setX(x)
        self.boxNP.setY(y)
        self.boxNP.setZ(z)
        self.boxNP.setScale(xs, ys, zs)

        self.boxNP.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.boxNP.node())

        visualNP = loader.loadModelCopy('models/blockmetal')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.boxNP)
        visualNP.setScale(0.5)

    def makeblockwood(self, x, y, z, xs, ys, zs):
        shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

        self.boxNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.boxNP.node().setMass(10.2)
        self.boxNP.node().addShape(shape)
        self.boxNP.setX(x)
        self.boxNP.setY(y)
        self.boxNP.setZ(z)
        self.boxNP.setScale(xs, ys, zs)

        self.boxNP.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.boxNP.node())

        visualNP = loader.loadModelCopy('models/blockwood')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.boxNP)
        visualNP.setScale(0.5)

    def makeblockgold(self, x, y, z, xs, ys, zs):
        shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

        self.boxNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.boxNP.node().setMass(10.2)
        self.boxNP.node().addShape(shape)
        self.boxNP.setX(x)
        self.boxNP.setY(y)
        self.boxNP.setZ(z)
        self.boxNP.setScale(xs, ys, zs)

        self.boxNP.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.boxNP.node())

        visualNP = loader.loadModelCopy('models/blockgold')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.boxNP)
        visualNP.setScale(0.5)

    def makeblocksilver(self, x, y, z, xs, ys, zs):
        shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

        self.boxNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.boxNP.node().setMass(10.2)
        self.boxNP.node().addShape(shape)
        self.boxNP.setX(x)
        self.boxNP.setY(y)
        self.boxNP.setZ(z)
        self.boxNP.setScale(xs, ys, zs)

        self.boxNP.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.boxNP.node())

        visualNP = loader.loadModelCopy('models/blocksilver')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.boxNP)
        visualNP.setScale(0.5)

    def makeblockstone(self, x, y, z, xs, ys, zs):
        shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

        self.boxNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.boxNP.node().setMass(10.2)
        self.boxNP.node().addShape(shape)
        self.boxNP.setX(x)
        self.boxNP.setY(y)
        self.boxNP.setZ(z)
        self.boxNP.setScale(xs, ys, zs)

        self.boxNP.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.boxNP.node())

        visualNP = loader.loadModelCopy('models/blockstone')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.boxNP)
        visualNP.setScale(0.5)

    def makeblockcrate(self, x, y, z, xs, ys, zs):
        shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))

        self.boxNP = self.worldNP.attachNewNode(BulletRigidBodyNode('Box'))
        self.boxNP.node().setMass(10.2)
        self.boxNP.node().addShape(shape)
        self.boxNP.setX(x)
        self.boxNP.setY(y)
        self.boxNP.setZ(z)
        self.boxNP.setScale(xs, ys, zs)

        self.boxNP.setCollideMask(BitMask32.allOn())
        #self.boxNP.node().setDeactivationEnabled(False)

        self.world.attachRigidBody(self.boxNP.node())

        visualNP = loader.loadModelCopy('models/crate')
        visualNP.clearModelNodes()
        visualNP.reparentTo(self.boxNP)
        visualNP.setScale(0.5)
Beispiel #2
0
class Player:
    def __init__(self, bController, model, scale):
         #(self, model, run, walk, startPos, scale):
        """Initialise the character.
        
        Arguments:
        bController -- The bullet character controller assigned to this player
        model -- The path to the character's model file (string)
                  
        """
        self.bController = bController
        #Define movement map and speeds
        self.speedSprint = 20
        self.speedWalk = 5
        self.speedCrouch = 5
        self.speed = self.speedWalk
        #Capture control status
        """
        Handled by bulletController?
        """
        self.isMoving = False
        self.isJumping = False
        self.isIdle = False
        self.isCrouching = False
        
        self.movementMap = {"forward":Vec3(0,-self.speed,0), 
                            "back":Vec3(0,self.speed,0),
                            "left":Vec3(self.speed,0,0),
                            "right":Vec3(-self.speed,0,0),
                            "crouch":0, 
                            "sprint":0, 
                            "jump":1, 
                            "punch":0, 
                            "kick":0, 
                            "stop":Vec3(0), 
                            "changeView":0
                            }
        
        #Set up key state variables
        self.strafe_left = self.movementMap["stop"]
        self.strafe_right = self.movementMap["stop"]
        self.forward = self.movementMap["stop"]
        self.back = self.movementMap["stop"]
        self.jump = False
        self.sprint = False
        self.crouch = False
        
        #Stop player by default
        self.move = self.movementMap["stop"]

           
        #Define the actor and his animations
        self.actor = Actor(model,
                           {"run":model + "-Run",
                            "walk":model + "-Walk",
                            "idle":model + "-Idle",
                            "jump":model + "-Jump",
                            "idleCrouch":model + "-IdleCrouch",
                            "walkCrouch":model + "-WalkCrouch"})
        
        
        #self.actor.enableBlend()
        
        self.actor.clearModelNodes()
        #Reparent the visual actor node path to the bullet collision node path
        #this is so when we move the bullet sphere node path, our actor will move
        
        self.actor.reparentTo(self.bController.capsuleNP)
        
        self.actor.setBlend(frameBlend = True)#Enable interpolation
        #self.actor.reparentTo(render)
        self.actor.setScale(scale)
        
        #Set up FSM controller
        self.FSM = ActorFSM(self.actor, self.bController)
        
        
        
        taskMgr.add(self.processInput,"processInput") 

    def processInput(self, task):

        #Calculate movement & do animations and stuff
        
        self.move = self.forward + self.back + self.strafe_right + self.strafe_left 
        
        # move the character if any of the move controls are activated.
        self.bController.setLinearMovement(self.move, True)
        
        
        
        """
#############################################################################        
        CALL CONTROLLER CLASS AND CALL FSM's INSTEAD OF DOING IT HERE
#############################################################################  
        """
        
        #Decide what type of movement anim to use
        
        if(self.sprint is True):
            #If we are sprinting..
            self.walkAnim = 'Run'
            self.speed = self.speedSprint
        elif(self.crouch is True): # Can't sprint while crouching ;)
            #If we are crouching..
            print ("Crouching!")
            self.walkAnim = "WalkCrouch"
            self.idleAnim = "IdleCrouch"
            self.speed = self.speedCrouch
        else:
            #Otherwise were walking..
            self.walkAnim = 'Walk'
            self.idleAnim = 'Idle'
            self.speed = self.speedWalk
            
        """ 
        Idling
        """
        if(self.isJumping is False and self.isMoving is False and self.isIdle is True and self.FSM.state != self.idleAnim):
            #If were not moving and not jumping and were supposed to be idle, play the idle anim if we aren't already
            self.FSM.request(self.idleAnim,1)
            
            #We are idle, feel free to do something else, setting isIdle = False.
            print ("We are Idle but ready to do something: isIdle = False")
            
        elif(self.isJumping is False and self.isMoving is False and self.isIdle is False):
            #If were not moving or jumping, were not  doing anything, we should probably be idle if we aren't already          
            self.isIdle = True
        
        """
        Locomotion
        """      
        #TODO: Separate out into animations for forward, back and side stepping
        if(self.move != self.movementMap["stop"] and self.isJumping is False):
            #Check if actor is walking forward/back
            if(self.move != self.movementMap["stop"]):
                if(self.isMoving is False or self.FSM.state != self.walkAnim):
                    self.isMoving = True # were now moving
                    self.isIdle = False # were not idle right now 
                    self.FSM.request(self.walkAnim,1)
                    print ("Started running or walking")
        elif(self.isMoving is True and self.isIdle is False):
            #Only switch of isMoving if we were moving and not idle
            self.isMoving = False
            print ("Finished walking")
            
                  
            #if were moving, set isMoving = 1 and call walking FSM     
        '''
        Jumping
        
        Check if the user is jumping, if they currently aren't jumping:
        make them not idle and mark them as jumping and request the Jump FSM.
        
        If the jump anim isn't playing but we were jumping, mark actor as not jumping.
        
        '''     
        if(self.jump is True):
            #if user pressed jump and were not already jumping, jump
            if(self.isJumping is False and self.FSM.state != 'Jump'):
                
                self.isJumping = True # were jumping 
                self.isIdle = False # were not idle right now
                self.FSM.request('Jump',1)
                print ("Started jumping")
        
        #if we are jumping, check the anim has finished and stop jumping
        self.JumpQuery = self.actor.getAnimControl('jump')
        if(self.isJumping is True and self.JumpQuery.isPlaying() is False):
            self.isJumping = False # finished jumping
            print ("Finished Jumping")

        """
#############################################################################        
        CALL CONTROLLER CLASS AND CALL FSM's INSTEAD OF DOING IT HERE
#############################################################################  
        """        
        
        return Task.cont       
    
    

    def setMove(self, key, moveType):
        """ Used by keyboard setup 
            This gets the input from keyBoardSetup and will capture inputs
        """
        if (moveType == "strafe_left"):
            self.strafe_left = self.movementMap[key]
        if (moveType == "strafe_right"):
            self.strafe_right = self.movementMap[key]
        if (moveType == "forward"):
            self.forward = self.movementMap[key]
        if (moveType == "back"):
            self.back = self.movementMap[key]
        if (moveType == "sprint"):
            self.sprint = key
        if (moveType == "jump"):
            self.jump = key
        if (moveType == "crouch"):
            self.crouch = key
Beispiel #3
0
class Entity(NodePath, Precacheable):
    notify = directNotify.newCategory("Entity")

    NeedNode = True

    def __init__(self, initNode=True):
        if initNode:
            NodePath.__init__(self, ModelNode("entity"))
        self.loaded = False
        self.cEntity = None
        self.outputs = []
        self.bspLoader = None
        self.spawnflags = 0
        self.entState = 0
        self.nextThink = 0.0
        self.lastThink = 0.0
        self.targetName = ""
        self.entnum = 0
        self.entStateTime = 0.0
        self.modelPath = ""
        self.modelIsAnimating = False
        self.modelOrigin = Point3(0)
        self.modelAngles = Vec3(0)
        self.modelScale = Point3(1)
        self.model = None
        self.sequence = None
        self.mapEnt = False
        self.sounds = {}

    def getIsMapEnt(self):
        return self.mapEnt

    def parentGenerated(self):
        print("Parent generated for", self, self.getParentEntity())
        self.tryEntityParent()

    def tryEntityParent(self):
        if not self.NeedNode:
            return

        try:
            parent = self.getParentEntity()
            if parent == render:
                self.reparentTo(render)
            else:
                self.wrtReparentTo(parent)
        except:
            self.reparentTo(render)

    def setupBrushEntityPhysics(self):
        pass
        #base.brushCollisionMaterialData.update(PhysicsUtils.makeBulletCollFromGeoms(self.cEntity.getModelNp()))

    def hasParentEntity(self):
        return len(self.getEntityValue("parent")) > 0

    def getParentEntity(self):
        if not self.hasParentEntity():
            return render
        return self.bspLoader.getPyEntityByTargetName(
            self.getEntityValue("parent"))

    def transitionXform(self, destLandmarkNP, mat):
        self.setMat(destLandmarkNP, mat)

    def isPreservable(self):
        return False

    def getModel(self):
        return [self.modelPath, self.modelIsAnimating]

    def getModelScale(self):
        return [self.modelScale[0], self.modelScale[1], self.modelScale[2]]

    def getModelOrigin(self):
        return [self.modelOrigin[0], self.modelOrigin[1], self.modelOrigin[2]]

    def getModelAngles(self):
        return [self.modelAngles[0], self.modelAngles[1], self.modelAngles[2]]

    def getFrameTime(self):
        return globalClock.getFrameTime()

    def getSound(self, name):
        return self.sounds[name]

    def isSoundPlaying(self, name):
        if name in self.sounds and self.sounds[name]:
            return self.sounds[name].status() == AudioSound.PLAYING
        return False

    def addSound(self,
                 name,
                 soundPath,
                 spatialized=True,
                 setLoop=False,
                 node=None):
        if not node:
            node = self
        if spatialized:
            self.sounds[name] = base.loadSfxOnNode(soundPath, node)
        else:
            self.sounds[name] = loader.loadSfx(soundPath)
        self.sounds[name].setLoop(setLoop)

    def stopSound(self, name):
        if name in self.sounds and self.sounds[name]:
            self.sounds[name].stop()

    def stopAllSounds(self):
        if self.sounds:
            for sound in self.sounds.values():
                sound.stop()

    def playSound(self, name, volume=1.0):
        if name in self.sounds:
            self.sounds[name].setVolume(volume)
            self.sounds[name].setLoop(False)
            self.sounds[name].play()

    def loopSound(self, name, volume=1.0):
        if name in self.sounds:
            self.sounds[name].setVolume(volume)
            self.sounds[name].setLoop(True)
            self.sounds[name].play()

    def clearSequence(self):
        if self.sequence:
            self.sequence.finish()
        self.sequence = None

    def setSequence(self, seq, loop=False, startNow=True):
        self.clearSequence()
        self.sequence = seq
        if startNow:
            if loop:
                self.sequence.loop()
            else:
                self.sequence.start()

    def clearModel(self):
        if self.model:
            if isinstance(self.model, Actor):
                self.model.cleanup()
            else:
                self.model.removeNode()
        self.model = None

    def setModel(self, mdlPath, animating=False):
        self.clearModel()
        self.modelIsAnimating = animating
        if isinstance(mdlPath, str):
            self.modelPath = mdlPath
            if len(mdlPath) == 0:
                return
            if animating:
                self.model = Actor(mdlPath, flattenable=0)
            else:
                self.model = loader.loadModel(mdlPath)
        else:
            # Assume it's an already loaded Actor/Model.
            self.model = mdlPath
        self.model.reparentTo(self)

    def enableModelCollisions(self):
        if self.model:
            base.createAndEnablePhysicsNodes(self.model)

    def disableModelCollisions(self):
        if self.model:
            base.disableAndRemovePhysicsNodes(self.model)

    def setModelOrigin(self, origin):
        self.modelOrigin = Point3(origin)
        if self.model:
            self.model.setPos(origin)

    def setModelAngles(self, angles):
        self.modelAngles = Vec3(angles)
        if self.model:
            self.model.setHpr(angles)

    def setModelScale(self, scale):
        self.modelScale = Point3(scale)
        if self.model:
            self.model.setScale(scale)

    def optimizeModel(self):
        if self.model:
            self.model.clearModelNodes()
            self.model.flattenStrong()

    def getModelNP(self):
        return self.model

    def getTargetName(self):
        return self.targetName

    def setNextThink(self, delay):
        self.nextThink = delay
        self.lastThink = globalClock.getFrameTime()

    def getNextThink(self):
        return self.nextThink

    def shouldThink(self):
        return self.nextThink >= 0 and globalClock.getFrameTime(
        ) - self.lastThink >= self.nextThink

    def setEntityState(self, state):
        self.entState = state
        self.entStateTime = globalClock.getFrameTime()

    def getEntityStateElapsed(self):
        return globalClock.getFrameTime() - self.entStateTime

    def getEntityState(self):
        return self.entState

    def __thinkTask(self, task):
        if self.shouldThink():
            self.lastThink = globalClock.getFrameTime()
            self.nextThink = 0.0
            self.think()

        return task.cont

    def think(self):
        pass

    def hasSpawnFlags(self, flags):
        return (self.spawnflags & flags) != 0

    def getCEntity(self):
        return self.cEntity

    def getLoader(self):
        return self.bspLoader

    def entityTaskName(self, taskName):
        if hasattr(self, 'doId'):
            entnum = self.doId
        else:
            entnum = self.getEntnum()
        return taskName + "-entity_" + str(entnum)

    def getEntnum(self):
        return self.entnum

    def getEntityValue(self, key):
        assert self.cEntity

        return self.cEntity.getEntityValue(key)

    def getEntityValueInt(self, key):
        assert self.cEntity

        try:
            return int(self.cEntity.getEntityValue(key))
        except:
            return 0

    def getEntityValueBool(self, key):
        assert self.cEntity

        try:
            return bool(int(self.cEntity.getEntityValue(key)))
        except:
            return False

    def getEntityValueFloat(self, key):
        assert self.cEntity

        try:
            return float(self.cEntity.getEntityValue(key))
        except:
            return 0.0

    def getEntityValueVector(self, key):
        assert self.cEntity

        return self.cEntity.getEntityValueVector(key)

    def getEntityValueColor(self, key):
        assert self.cEntity

        return self.cEntity.getEntityValueColor(key)

    def task_dispatchOutput(self, target, op, extraArgs, task):
        param = op['parameter']
        params = param.split(';') if len(param) > 0 else []
        params += extraArgs
        getattr(target, op['input']).__call__(*params)
        return task.done

    def dispatchOutput(self, outputName, extraArgs=[]):
        for op in self.outputs:
            if op['output'] == outputName and op['active']:
                target = None
                if 'targetName' in op:
                    target = self.bspLoader.getPyEntityByTargetName(
                        op['targetName'])
                elif 'target' in op:
                    target = op['target']
                if target:
                    if hasattr(target, op['input']) and callable(
                            getattr(target, op['input'])):
                        taskMgr.doMethodLater(
                            op['delay'],
                            self.task_dispatchOutput,
                            "dispatchOutput-" + str(id(op)),
                            extraArgs=[target, op, extraArgs],
                            appendTask=True)
                        if op['once']:
                            op['active'] = False

    def connectOutput(self,
                      outputName,
                      inputEntity,
                      inputName,
                      parameter="",
                      onceOnly=False,
                      delay=0.0):
        self.outputs.append({
            'output': outputName,
            'target': inputEntity,
            'input': inputName,
            'parameter': parameter,
            'delay': delay,
            'once': onceOnly,
            'active': True
        })

    def load(self):
        self.loaded = True

        if hasattr(base, 'bspLoader') and not self.bspLoader:
            self.bspLoader = base.bspLoader

        self.entnum = self.cEntity.getBspEntnum()

        keyvalues = []
        self.bspLoader.getEntityKeyvalues(keyvalues,
                                          self.cEntity.getBspEntnum())
        for k, v in keyvalues:
            if k[:2] != "On":
                continue
            data = v.split(',')
            if len(data) != 5:
                continue
            self.outputs.append({
                'output': k,
                'targetName': data[0],
                'input': data[1],
                'parameter': data[2],
                'delay': float(data[3]),
                'once': bool(int(data[4])),
                'active': True
            })

        self.spawnflags = self.getEntityValueInt("spawnflags")

        self.dispatchOutput("OnSpawn")

    def enableThink(self):
        taskMgr.add(self.__thinkTask, self.entityTaskName("think"))

    def unload(self):
        taskMgr.remove(self.entityTaskName("think"))
        self.stopAllSounds()
        self.sounds = None
        self.clearSequence()
        self.disableModelCollisions()
        self.clearModel()
        self.loaded = None
        self.cEntity = None
        self.outputs = None
        self.bspLoader = None
        self.spawnflags = None
        self.entState = None
        self.nextThink = None
        self.lastThink = None
        self.targetName = None
        self.entnum = None
        self.modelOrigin = None
        self.modelAngles = None
        self.modelScale = None
        self.modelPath = None
        self.modelIsAnimating = None
from panda3d.core import *
from direct.actor.Actor import Actor
actor = Actor()
actor.loadModel("phase_3/models/char/tt_a_chr_dgm_shorts_legs_1000.bam", "legs")
actor.find("**/boots_long").removeNode()
actor.find("**/boots_short").removeNode()
actor.find("**/shoes").removeNode()
actor.loadModel("phase_3/models/char/tt_a_chr_dgm_skirt_head_1000.bam", "head")

actor.loadAnims({"neutral": "phase_3/models/char/tt_a_chr_dgm_shorts_legs_neutral.bam"}, "legs")
actor.loadModel("phase_3/models/char/tt_a_chr_dgm_shorts_torso_1000.bam", "torso")
actor.loadAnims({"neutral": "phase_3/models/char/tt_a_chr_dgm_shorts_torso_1000.bam"}, "torso")
#actor.loadModel("phase_3/models
actor.getPart("torso").reparentTo(actor.find("**/joint_hips"))
actor.getPart("head").reparentTo(actor.getPart("torso").find("**/def_head"))
#actor.getGeomNode().setScale(0.85)
actor.clearModelNodes()
actor.flattenLight()
actor.postFlatten()
actor.ls()
#actor.listJoints()
actor.writeBamFile("flippy.bam")

#head = Loader.getGlobalPtr().loadSync("phase_3/models/char/tt_a_chr_dgm_skirt_head_1000.bam")
#NodePath(head).writeBamFile("flippy_head.bam")