class LabTask03(DirectObject):
  
  #define the state of the game and level
  gameState = 'INIT'
  gameLevel = 1
  cameraState = 'STARTUP'
  count = 0
  attempts = 3
  posX = -200
  posY = 20
  posZ = 30
  score = 0
  contacts = 0
  pause = False
  fire = True
  desiredCamPos = Vec3(-200,30,20)

  def __init__(self):
    self.imageObject = OnscreenImage(image = 'models/splashscreen.png', pos=(0,0,0), scale=(1.4,1,1))
    preloader = Preloader()
    self.musicLoop = loader.loadSfx("music/loop/EndlessBliss.mp3")
    self.snowmansHit = loader.loadSfx("music/effects/snowball_hit.wav")
    self.candleThrow = loader.loadSfx("music/effects/snowball_throw.wav")
    self.presentHit = loader.loadSfx("music/effects/present_hit.wav")
    self.loseSound = loader.loadSfx("music/effects/Failure-WahWah.mp3")
    self.winSound = loader.loadSfx("music/effects/Ta Da-SoundBible.com-1884170640.mp3")
    self.nextLevelSound = loader.loadSfx("music/effects/button-17.wav")
    self.loseScreen = OnscreenImage(image = 'models/losescreen.png', pos=(0,0,0), scale=(1.4,1,1))
    self.loseScreen.hide()
    self.winScreen = OnscreenImage(image = 'models/winscreen.png', pos=(0,0,0), scale=(1.4,1,1))
    self.winScreen.hide()
    self.helpScreen = OnscreenImage(image = 'models/helpscreen.jpg', pos=(0,0,0.1), scale=(1,1,0.8))
    self.helpScreen.hide()
    self.backBtn = DirectButton(text=("Back"), scale = 0.1,  pos = (0,0,-0.8), command = self.doBack)
    self.retryBtn = DirectButton(text="Retry", scale = 0.1, pos = (0,0,0), command = self.doRetry)
    self.retryBtn.hide()
    self.menuBtn = DirectButton(text="Main Menu", scale = 0.1, pos = (0,0,0), command = self.doBack)
    self.menuBtn.hide()
    self.backBtn.hide()
    base.setBackgroundColor(0.1, 0.1, 0.8, 1)
    #base.setFrameRateMeter(True)
    
    # Position the camera
    base.cam.setPos(0, 30, 20)
    base.cam.lookAt(0, 30, 0)

    # 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('f5', self.doScreenshot)
    self.accept('f', self.doShoot, [True])
    self.accept('p', self.doPause)

    inputState.watchWithModifiers('forward', 'w')
    inputState.watchWithModifiers('left', 'a')
    inputState.watchWithModifiers('reverse', 's')
    inputState.watchWithModifiers('right', 'd')
    inputState.watchWithModifiers('turnLeft', 'q')
    inputState.watchWithModifiers('turnRight', 'e')
    
    inputState.watchWithModifiers('moveLineUp', 'i')
    inputState.watchWithModifiers('moveLineDown','k')
    inputState.watchWithModifiers('moveLineRight','l')
    inputState.watchWithModifiers('moveLineLeft','j')
    
    self.font = loader.loadFont('models/SHOWG.TTF')
    self.font.setPixelsPerUnit(60)
    self.attemptText = OnscreenText(text='', pos = (0.9,0.8), scale = 0.07, font = self.font)
    self.levelText = OnscreenText(text='', pos=(-0.9,0.9), scale = 0.07, font = self.font )
    self.scoreText = OnscreenText(text='', pos = (0.9,0.9), scale = 0.07, font = self.font)
    self.text = OnscreenText(text = '', 
                              pos = (0, 0), scale = 0.07, font = self.font)
    self.pauseText = OnscreenText(text='P: Pause', pos= (0.9,0.7), scale = 0.05, font = self.font)
    self.pauseText.hide()
    # Task
    taskMgr.add(self.update, 'updateWorld')

    # Physics
    self.setup()

  # _____HANDLER_____
  
  def doRetry(self):
    self.loseScreen.hide()
    self.levelText.clearText()
    self.scoreText.clearText()
    self.attemptText.clearText()
    self.playGame()
    self.retryBtn.hide()
    self.cleanup()
    self.setup()
    

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

  def doReset(self):
    self.attempts = 3
    self.cameraState = 'STARTUP'
    base.cam.setPos(0,30,20)
    self.arrow.removeNode()
    self.scoreText.clearText()
    self.levelText.clearText()
    self.attemptText.clearText()
    self.cleanup()
    self.setup()
    
  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 doShoot(self, ccd):
    if(self.fire and self.attempts > 0 and self.gameState == 'PLAY'):
      self.cameraState = 'LOOK'
      self.fire = False
      self.candleThrow.play()
      self.attempts -= 1
      #get from/to points from mouse click
      ## pMouse = base.mouseWatcherNode.getMouse()
      ## pFrom = Point3()
      ## pTo = Point3()
      ## base.camLens.extrude(pMouse, pFrom, pTo)
      
      ## pFrom = render.getRelativePoint(base.cam, pFrom)
      ## pTo = render.getRelativePoint(base.cam, pTo)
      
      # calculate initial velocity
      v = self.pTo - self.pFrom
      ratio = v.length() / 40
      v.normalize()
      v *= 70.0 * ratio
      torqueOffset = random.random() * 10
      
      #create bullet
      shape = BulletSphereShape(0.5)
      shape01 = BulletSphereShape(0.5)
      shape02 = BulletSphereShape(0.5)
      shape03 = BulletSphereShape(0.5)
      body = BulletRigidBodyNode('Candle')
      bodyNP = self.worldNP.attachNewNode(body)
      bodyNP.node().addShape(shape, TransformState.makePos(Point3(0,0,0)))
      bodyNP.node().addShape(shape01, TransformState.makePos(Point3(0,0.5,-0.5)))
      bodyNP.node().addShape(shape02,TransformState.makePos(Point3(0,1,-1)))
      bodyNP.node().addShape(shape03,TransformState.makePos(Point3(0,1.5,-1.5)))
      bodyNP.node().setMass(100)
      bodyNP.node().setFriction(1.0)
      bodyNP.node().setLinearVelocity(v)
      bodyNP.node().applyTorque(v*torqueOffset)
      bodyNP.setPos(self.pFrom)
      bodyNP.setCollideMask(BitMask32.allOn())
      
      visNP = loader.loadModel('models/projectile.X')
      visNP.setScale(0.7)
      visNP.clearModelNodes()
      visNP.reparentTo(bodyNP)
      
      #self.bird = bodyNP.node()
      
      if ccd:
          bodyNP.node().setCcdMotionThreshold(1e-7)
          bodyNP.node().setCcdSweptSphereRadius(0.5)
          
      self.world.attachRigidBody(bodyNP.node())
      
      #remove the bullet again after 1 sec
      self.bullets = bodyNP
      taskMgr.doMethodLater(5, self.removeBullet, 'removeBullet', extraArgs=[bodyNP], 
                            appendTask = True)
      
    
  def removeBullet(self, bulletNP, task):
    self.cameraState = 'STAY'
    self.fire = True
    self.world.removeRigidBody(bulletNP.node())
    bulletNP.removeNode()
    if(self.attempts <= 0 and len(self.snowmans)>0):
      self.gameState = 'LOSE'
      self.doContinue()
      
    return task.done

  # ____TASK___

  def processInput(self, dt):
    force = Vec3(0, 0, 0)
    torque = Vec3(0, 0, 0)
    #print self.pTo.getY()
    if inputState.isSet('forward'):
      if(self.pTo.getZ() < 40):
        self.pTo.addZ(0.5)
    if inputState.isSet('reverse'):
      if(self.pTo.getZ() > 0 ):
        self.pTo.addZ(-0.5)
    if inputState.isSet('left'):
      if(self.pTo.getY() < 100):
        self.pTo.addY(0.5)
        self.arrow.setScale(self.arrow.getSx(),self.arrow.getSy()-0.006,self.arrow.getSz())
    if inputState.isSet('right'):
      if(self.pTo.getY() > 60):
        self.pTo.addY(-0.5)
        self.arrow.setScale(self.arrow.getSx(),self.arrow.getSy()+0.006,self.arrow.getSz())
        
    self.arrow.lookAt(self.pTo)
    
  def processContacts(self, dt):
      for box in self.boxes:
        for snowman in self.snowmans:
          result = self.world.contactTestPair(box, snowman.node())
      
          if (result.getNumContacts() > 0):
            self.snowmansHit.play()
            self.score += 100
            self.text.setPos(0,0.7)
            self.text.setText("HIT")
            self.snowmans.remove(snowman)
            self.world.removeRigidBody(snowman.node())
            snowman.removeNode()
            if(len(self.snowmans)  <= 0):
              self.gameState = "NEXT"
              self.text.setText('Nice! Press space to continue')

              
      for box in self.boxes:
        for present in self.presents:
          result01 = self.world.contactTestPair(box,present.node())
          if(result01.getNumContacts() > 0):
            self.presents.remove(present)
            self.world.removeRigidBody(present.node())
            present.removeNode()
            self.presentHit.play()
            self.score += 500
            
  def doContinue(self):
    if(self.gameState == 'INIT'):
      self.gameState = 'MENU'
      self.text.clearText()
      self.musicLoop.setLoop(True)
      self.musicLoop.setVolume(0.5)
      self.musicLoop.play()
      self.startBtn = DirectButton(text=("Play"), scale = 0.1, pos = (0,0,0),command=self.playGame)
      self.helpBtn = DirectButton(text=("Help"), scale = 0.1, pos = (0,0,-0.2),command=self.help)
      self.exitBtn = DirectButton(text=("Exit"), scale = 0.1,  pos = (0,0,-0.4), command = self.doExit)
      return
    
    if self.gameState == 'NEXT':
      self.nextLevelSound.play()
      self.fire = True
      self.gameLevel += 1
      self.score += (self.attempts * 100)
      self.doReset()
      self.gameState = 'PLAY'
      return
    
    if self.gameState == 'LOSE':
      self.loseSound.play()
      self.arrow.removeNode()
      self.loseScreen.show()
      self.levelText.hide()
      self.attemptText.hide()
      self.scoreText.hide()
      self.text.hide()
      self.pauseText.hide()
      self.retryBtn.show()
      return
    
    if self.gameState == 'WIN':
      self.levelText.hide()
      self.attemptText.hide()
      self.scoreText.clearText()
      self.scoreText.setPos(0,0.6)
      self.scoreText.setText("%s"%self.score)
      self.winScreen.show()
      self.winSound.play()
      self.menuBtn.show()
      self.pauseText.hide()
      return
      
    
  def playGame(self):
    print "Play Game"
    self.attempts = 3
    self.score = 0
    self.gameLevel = 1
    self.gameState = 'PLAY'
    self.musicLoop.setVolume(0.3)
    self.imageObject.hide()
    self.startBtn.hide()
    self.exitBtn.hide()
    self.helpBtn.hide()
    self.cleanup()
    self.setup()
    
  def doBack(self):
    self.gameState = 'MENU'
    self.scoreText.setPos(0.9,0.9)
    self.scoreText.hide()
    self.imageObject.show()
    self.startBtn.show()
    self.exitBtn.show()
    self.helpBtn.show()
    self.helpScreen.hide()
    self.backBtn.hide()
    self.menuBtn.hide()
    self.winScreen.hide()
    
  def help(self):
    self.gameState = 'HELP'
    self.startBtn.hide()
    self.exitBtn.hide()
    self.helpBtn.hide()
    self.backBtn.show()
    self.helpScreen.show()
    return
    
  def doPause(self):
    self.pause  = not self.pause
    if(self.pause):
      self.text.setText("Pause")
    else:
      self.text.clearText()
      

  def update(self, task):

    dt = globalClock.getDt()
    if(not self.pause):
      if self.gameState == 'INIT':
        self.text.setPos(0,0)
        self.text.setText('Press space to continue')
        self.accept('space',self.doContinue)
          
      if self.gameState == 'PLAY':
        #print self.posZ
        #if(self.posX < -120):
        #  self.posX += 0.03
        #  self.posZ -= 0.02
        #elif(self.posZ < 70):
        #  self.posZ += 0.02;
        #elif(self.posZ > 70 and self.posX > -120):
        #  self.posZ -= 0.02
        #  self.posX -= 0.03
        #base.cam.setPos(self.posX, self.posZ, self.posY)
        self.levelText.setText('Level: %s'%self.gameLevel)
        self.attemptText.setText('Tries Left: %s'%self.attempts)
        self.scoreText.setText('Score: %s'%self.score)
        self.pauseText.show()
        self.processContacts(dt)
        self.world.doPhysics(dt, 20, 1.0/180.0)
        #self.drawLines()
        self.processInput(dt)
        
        if self.cameraState == 'STARTUP':
          oldPos = base.cam.getPos()
          pos = (oldPos*0.9) + (self.desiredCamPos*0.1)
          base.cam.setPos(pos)
          base.cam.lookAt(0,30,0)
        elif self.cameraState == 'STAY':
          #oldPos = base.cam.getPos()
          #currPos = (oldPos*0.9) + (self.desiredCamPos*0.1)
          #base.cam.setPos(currPos)
          base.cam.lookAt(0,30,0)
        elif self.cameraState == 'LOOK':
          base.cam.lookAt(self.bullets)
          #base.cam.setPos(-200,self.bullets.getZ(),20)
        
      if self.gameState == 'NEXT':
        self.world.doPhysics(dt, 20, 1.0/180.0)
        

      ## self.raycast()
    
    return task.cont
      
  def raycast(self):
    # Raycast for closest hit
    tsFrom = TransformState.makePos(Point3(0,0,0))
    tsTo = TransformState.makePos(Point3(10,0,0))
    shape = BulletSphereShape(0.5)
    penetration = 1.0
    result = self.world.sweepTestClosest(shape, tsFrom, tsTo, penetration)
    if result.hasHit():
        torque = Vec3(10,0,0)
        force = Vec3(0,0,100)
        
        ## for hit in result.getHits():
            ## hit.getNode().applyTorque(torque)
            ## hit.getNode().applyCentralForce(force)
        result.getNode().applyTorque(torque)
        result.getNode().applyCentralForce(force)
        ## print result.getClosestHitFraction()
        ## print result.getHitFraction(), \
            ## result.getNode(), \
            ## result.getHitPos(), \
            ## result.getHitNormal()

  def cleanup(self):
    self.world = None
    self.worldNP.removeNode()
    self.arrow.removeNode()
    self.lines.reset()
    self.text.clearText()

  def setup(self):
    self.attemptText.show()
    self.levelText.show()
    self.scoreText.show()
    self.text.show()
    
    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())
    
    #arrow
    self.scaleY = 10
    self.arrow = loader.loadModel('models/arrow.X')
    self.arrow.setScale(0.5,0.5,0.5)
    self.arrow.setAlphaScale(0.5)
    #self.arrow.setTransparency(TransparencyAttrib.MAlpha) 
    self.arrow.reparentTo(render)
    
    #SkyBox
    skybox = loader.loadModel('models/skybox.X')
    skybox.reparentTo(render)

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

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

    self.world.attachRigidBody(np.node())
    
    visualNP = loader.loadModel('models/ground.X')
    visualNP.clearModelNodes()
    visualNP.reparentTo(np)
    
    #some boxes
    self.boxes = []
    self.snowmans = []
    self.platforms = []
    self.presents = []
    #TODO: Make a table
    #Table Top
    #self.createBox(Vec3(),Vec3())
    if(self.gameLevel == 1):
      self.createBox(Vec3(5,7,1),Vec3(0,5,10),1.0)
      #2 legs
      self.createBox(Vec3(4,1,4),Vec3(0,11,5),1.0)
      self.createBox(Vec3(4,1,4),Vec3(0,-1,5),1.0)
      
      # Pigs
      self.createSnowman(2.0,Vec3(0, 5, 2.0),10.0)
      self.createSnowman(1.5, Vec3(0,-10,4.0),10.0)
      
    if(self.gameLevel == 2):
      #table01
      self.createBox(Vec3(5,14,1),Vec3(0,-2,12),2.0)
      self.createBox(Vec3(5,7,1),Vec3(0,5,10),2.0)
      self.createBox(Vec3(4,1,4),Vec3(0,11,5),1.0)
      self.createBox(Vec3(4,1,4),Vec3(0,-1,5),1.0)
      #table02
      self.createBox(Vec3(5,7,1),Vec3(0,-9,10),2.0)
      self.createBox(Vec3(4,1,4),Vec3(0,-3,5),1.0)
      self.createBox(Vec3(4,1,4),Vec3(0,-15,5),1.0)
      #table03
      self.createBox(Vec3(1,1,1), Vec3(0,-1,14), 2.0)
      self.createBox(Vec3(1,1,1), Vec3(0,-3,14), 2.0)
      self.createBox(Vec3(1,1,1), Vec3(0,3,14), 2.0)
      self.createBox(Vec3(1,1,1), Vec3(0,-5,14), 2.0)
      self.createBox(Vec3(1,1,1), Vec3(0,5,14), 2.0)
      #pigs
      self.createSnowman(2.0,Vec3(0, 5, 2.0),10.0)
      self.createSnowman(2.0, Vec3(0,-9,2.0), 10.0)
      self.createSnowman(2.0,Vec3(0,-23,2.0),10.0)
      
    if(self.gameLevel == 3):
      #table01
      self.createBox(Vec3(4,2,2),Vec3(0,12,12),1.0)
      self.createBox(Vec3(4,1,4),Vec3(0,11,5),1.0)
      self.createBox(Vec3(4,1,4),Vec3(0,13,5),1.0)
      #table02
      self.createBox(Vec3(4,2,2),Vec3(0,-15,12),1.0)
      self.createBox(Vec3(4,1,4),Vec3(0,-14,5),1.0)
      self.createBox(Vec3(4,1,4),Vec3(0,-16,5),1.0)
      #table03
      self.createBox(Vec3(4,10,1),Vec3(0,-1,12),1.0)
      self.createBox(Vec3(4,10,1),Vec3(0,-1,14),1.0)
      self.createBox(Vec3(4,2,4),Vec3(0,-2,5),0.1)
      #table04
      self.createPlatform(Vec3(4,8,1),Vec3(0,7,16),1.0)
      self.createPlatform(Vec3(4,8,1),Vec3(0,-9,16),1.0)
      self.createBox(Vec3(4,1,3),Vec3(0,13,20),1.0)
      self.createBox(Vec3(4,1,3),Vec3(0,-16,20),1.0)
      #table05
      self.createBox(Vec3(4,15,1),Vec3(0,-1,24),1.0)
      self.createStoneBox(Vec3(1,1,1),Vec3(0,2,20),5.0)
      self.createStoneBox(Vec3(1,1,1),Vec3(0,-2,20),5.0)
      self.createStoneBox(Vec3(1,1,1),Vec3(0,4,20),5.0)
      self.createStoneBox(Vec3(1,1,1),Vec3(0,8,20),5.0)
      self.createStoneBox(Vec3(1,1,1),Vec3(0,6,20),5.0)
      
      #pigs
      self.createSnowman(2.0,Vec3(0, 5, 2.0),10.0)
      self.createSnowman(2.0,Vec3(0,-8.5,2.0),10.0)
      self.createSnowman(1.5, Vec3(0,-9,19.5), 7.0)
      
      #presents
      self.createPresent(Vec3(2,2,2),Vec3(0,-20,5))
         
    if(self.gameLevel == 4):
      #wall
      self.createStoneBox(Vec3(4,1.5,10), Vec3(0,20,10),20)
      #table01
      self.createBox(Vec3(4,1,5), Vec3(0,7,7),1)
      self.createBox(Vec3(4,1,5), Vec3(0,0,7),1)
      self.createBox(Vec3(4,1,4), Vec3(0,3,7),1)
      self.createPlatform(Vec3(5,8,1), Vec3(0,4,13),1)
      self.createBox(Vec3(4,1,3), Vec3(0,11,18),1)
      self.createBox(Vec3(4,1,3), Vec3(0,-3,18),1)
      self.createBox(Vec3(4,8,1), Vec3(0,4,25),1)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,4,27),2)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,7,27),2)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,2,27),2)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,2,29),2)
      #stairs
      self.createPlatform(Vec3(4,50,4), Vec3(0,-55,5),100)
      #table02
      self.createBox(Vec3(4,1,5), Vec3(0,-13,15),1)
      self.createBox(Vec3(4,1,5), Vec3(0,-20,15),1)
      self.createBox(Vec3(4,1,4), Vec3(0,-17,15),1)
      self.createPlatform(Vec3(4,8,1), Vec3(0,-16,22),1)
      self.createBox(Vec3(4,1,3), Vec3(0,-9,28),1)
      self.createBox(Vec3(4,1,3), Vec3(0,-23,28),1)
      self.createBox(Vec3(4,8,1), Vec3(0,-16,33),1)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,-16,35),2)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,-13,35),2)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,-18,35),2)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,-18,37),2)
      self.createStoneBox(Vec3(1,1,1), Vec3(0,-14,37),2)
      
      #snowman
      self.createSnowman(2.0,Vec3(0,30,5),1.0)
      self.createSnowman(1.5,Vec3(0,4,18),1.0)
      self.createSnowman(1.5,Vec3(0,-13,26),1.0)
      self.createSnowman(1.5,Vec3(0,-19,26),1.0)
      self.createSnowman(2.0,Vec3(0,12,5),1.0)
      self.createPresent(Vec3(2,2,2),Vec3(0,-25,13))
      self.createPresent(Vec3(3,3,3),Vec3(0,-30,13))
      self.createPresent(Vec3(4,4,4),Vec3(0,-36,13))
      #self.createSnowman(1.5,Vec3(0,4,20),1.0)

      
    if(self.gameLevel == 5):
      #table01
      self.createStoneBox(Vec3(4,7,3), Vec3(0,30,5),10.0)
      self.createStoneBox(Vec3(4,7,3), Vec3(0,-30,5),10.0)
      self.createBox(Vec3(4,1,3), Vec3(0,0,5), 1.0)
      self.createSnowman(1.5,Vec3(0,-6,5),1.0)
      self.createSnowman(1.5,Vec3(0,6,5),1.0)
      self.createBox(Vec3(4,1,3), Vec3(0,-12,5), 1.0)
      self.createBox(Vec3(4,1,3), Vec3(0,12,5), 1.0)
      self.createSnowman(1.5,Vec3(0,-18,5),1.0)
      self.createSnowman(1.5,Vec3(0,18,5),1.0)
      self.createStoneBox(Vec3(4,6,1),Vec3(0,-18,10), 0.1)
      self.createStoneBox(Vec3(4,6,1),Vec3(0,-6,10), 0.1)
      self.createStoneBox(Vec3(4,6,1),Vec3(0,6,10), 0.1)
      self.createStoneBox(Vec3(4,6,1),Vec3(0,18,10), 0.1)
      self.createStoneBox(Vec3(4,1,3),Vec3(0,23,14), 1.0)
      self.createStoneBox(Vec3(4,1,3),Vec3(0,-23,14), 1.0)
      self.createBox(Vec3(4,1,3),Vec3(0,18,14),1.0)
      self.createBox(Vec3(4,1,3),Vec3(0,-18,14),1.0)
      self.createStoneBox(Vec3(4,1,7),Vec3(0,13,20), 2.0)
      self.createStoneBox(Vec3(4,1,7),Vec3(0,-13,20), 2.0)
      self.createBox(Vec3(4,1,3),Vec3(0,8,14),1.0)
      self.createBox(Vec3(4,1,3),Vec3(0,-8,14),1.0)
      self.createStoneBox(Vec3(4,1,3),Vec3(0,3,14), 1.0)
      self.createStoneBox(Vec3(4,1,3),Vec3(0,-3,14), 1.0)
      self.createPlatform(Vec3(4,3.5,1),Vec3(0,-20 ,20), 1.0)
      self.createPlatform(Vec3(4,3.5,1),Vec3(0,20 ,20), 1.0)
      self.createPlatform(Vec3(4,3.5,1),Vec3(0,-5 ,20), 1.0)
      self.createPlatform(Vec3(4,3.5,1),Vec3(0,5 ,20), 1.0)
      self.createStoneBox(Vec3(4,1,3.5),Vec3(0,-18,25), 2.0)
      self.createStoneBox(Vec3(4,1,3.5),Vec3(0,18,25), 2.0)
      self.createStoneBox(Vec3(4,1,3.5),Vec3(0,-7.5,25), 2.0)
      self.createStoneBox(Vec3(4,1,3.5),Vec3(0,7.5,25), 2.0)
      self.createStoneBox(Vec3(4,6,1),Vec3(0,-12,30), 2.0)
      self.createStoneBox(Vec3(4,6,1),Vec3(0,12,30), 2.0)
      self.createBox(Vec3(4,1,5),Vec3(0,-5,30), 2.0)
      self.createBox(Vec3(4,1,5),Vec3(0,5,30), 2.0)
      self.createBox(Vec3(4,5,1),Vec3(0,0,40), 2.0)
      self.createPlatform(Vec3(4,2,0.5),Vec3(0,0,42), 2.0)
      self.createStoneBox(Vec3(4,0.5,2),Vec3(0,-3.5,45), 2.0)
      self.createStoneBox(Vec3(4,0.5,2),Vec3(0,3.5,45), 2.0)
      self.createStoneBox(Vec3(4,4,0.5),Vec3(0,0,48), 2.0)
      
      self.createPresent(Vec3(1.5,1.5,1.5),Vec3(0,22,30))
      self.createPresent(Vec3(1.5,1.5,1.5),Vec3(0,-22,30))
      self.createPresent(Vec3(2,2,1),Vec3(0,0,44))
      self.createPresent(Vec3(3,3,4),Vec3(0,0,33))
      
      
    if(self.gameLevel > 5):
      self.gameState = 'WIN'
      self.doContinue()
      return
        
    # drawing lines between the points 
    ## self.lines = LineNodePath(parent = render, thickness = 3.0, colorVec = Vec4(1, 0, 0, 1))
    ## self.pFrom = Point3(-4, 0, 0.5)
    ## self.pTo = Point3(4, 0, 0.5)
    
    # Aiming line 
    self.lines = LineNodePath(parent = render, thickness = 3.0, 
                              colorVec = Vec4(1, 0, 0, 1))
    self.pFrom = Point3(0, 100, 0.5)
    self.pTo = Point3(0, 60, 10)
    
    self.arrow.setPos(self.pFrom)
      
    
  def drawLines(self):  
    # Draws lines for the ray.
    self.lines.reset()  
    self.lines.drawLines([(self.pFrom,self.pTo)])
    self.lines.create()
    
  def createBox(self,size,pos,mass):
    shape = BulletBoxShape(size)
    body = BulletRigidBodyNode('Obstacle')
    bodyNP = self.worldNP.attachNewNode(body)
    bodyNP.node().addShape(shape)
    bodyNP.node().setMass(mass)
    bodyNP.node().setFriction(1.0)
    bodyNP.node().setDeactivationEnabled(False)
    bodyNP.setPos(pos)
    bodyNP.setCollideMask(BitMask32.allOn())

    self.world.attachRigidBody(bodyNP.node())
            
    visNP = loader.loadModel('models/crate.X')
    visNP.setScale(size*2)
    visNP.clearModelNodes()
    visNP.reparentTo(bodyNP)
    self.boxes.append(body)
    
  def createStoneBox(self,size,pos,mass):
    shape = BulletBoxShape(size)
    body = BulletRigidBodyNode('Obstacle')
    bodyNP = self.worldNP.attachNewNode(body)
    bodyNP.node().addShape(shape)
    bodyNP.node().setMass(mass)
    bodyNP.node().setFriction(1.0)
    bodyNP.node().setDeactivationEnabled(False)
    bodyNP.setPos(pos)
    bodyNP.setCollideMask(BitMask32.allOn())

    self.world.attachRigidBody(bodyNP.node())
            
    visNP = loader.loadModel('models/stone.X')
    visNP.setScale(size*2)
    visNP.clearModelNodes()
    visNP.reparentTo(bodyNP)
    self.boxes.append(body)
    
  def createSnowman(self, size, pos, mass):
    shape = BulletBoxShape(Vec3(size,size,size))
    shape01 = BulletSphereShape(size/2)
    body = BulletRigidBodyNode('Snowman')
    np = self.worldNP.attachNewNode(body)
    np.node().setMass(mass)
    np.node().addShape(shape, TransformState.makePos(Point3(0,0,-1)))
    np.node().addShape(shape01, TransformState.makePos(Point3(0,0,1)))
    np.node().setFriction(10.0)
    np.node().setDeactivationEnabled(False)
    np.setPos(pos)
    np.setCollideMask(BitMask32.allOn())
      
    self.world.attachRigidBody(np.node())
  
    visualNP = loader.loadModel('models/snowman.X')
    visualNP.setScale(size)
    visualNP.clearModelNodes()
    visualNP.reparentTo(np)
    self.snowmans.append(np)
    
  def createPlatform(self,size,pos,mass):
    shape = BulletBoxShape(size)
    body = BulletRigidBodyNode('Platform')
    bodyNP = self.worldNP.attachNewNode(body)
    bodyNP.node().addShape(shape)
    bodyNP.node().setMass(mass)
    bodyNP.node().setFriction(1.0)
    bodyNP.node().setDeactivationEnabled(False)
    bodyNP.setPos(pos)
    bodyNP.setCollideMask(BitMask32.allOn())

    self.world.attachRigidBody(bodyNP.node())
            
    visNP = loader.loadModel('models/crate.X')
    visNP.setScale(size*2)
    visNP.clearModelNodes()
    visNP.reparentTo(bodyNP)
    self.platforms.append(body)
    
  def createPresent(self,size,pos):
    shape = BulletBoxShape(size*0.7)
    body = BulletRigidBodyNode('Present')
    bodyNP = self.worldNP.attachNewNode(body)
    bodyNP.node().addShape(shape)
    bodyNP.node().setMass(1.0)
    bodyNP.node().setFriction(1.0)
    bodyNP.node().setDeactivationEnabled(False)
    bodyNP.setPos(pos)
    bodyNP.setCollideMask(BitMask32.allOn())
    
    self.world.attachRigidBody(bodyNP.node())
    
    visNP = loader.loadModel('models/present.X')
    visNP.setScale(size*2)
    visNP.clearModelNodes()
    visNP.reparentTo(bodyNP)
    self.presents.append(bodyNP)
Exemple #2
0
class TunnelPinchTask(ShowBase, GripStateMachine):
    DATA_DIR = 'data'
    
    def __init__(self, id, session, hand, block, mode, wrist):
        ShowBase.__init__(self)
        GripStateMachine.__init__(self)
        base.disableMouse()
        wp = WindowProperties()
        wp.setSize(1920,1080)
        wp.setFullscreen(True)
        wp.setUndecorated(True)
        base.win.requestProperties(wp)

        self.sub_id = str(id)
        self.sess_id = str(session)
        self.hand = str(hand)
        self.block = str(block)
        self.mode = str(mode)
        self.wrist = str(wrist)
        
        self.prev_blk = os.path.join(self.DATA_DIR,'exp_2',self.sub_id,self.sess_id,self.wrist,self.hand,"B0")
        self.exp_blk0 = os.path.join(self.DATA_DIR,'exp_1',self.sub_id,self.sess_id,self.wrist,self.hand,"B0")
        self.table = np.loadtxt('src/tunnel_pinch_task/trialtable_flex.csv',dtype='str',delimiter=',',skiprows=1)

        indices = {}
        try:
            self.prev_table = np.loadtxt(os.path.join(self.prev_blk, 'final_targets.csv'),dtype='str',delimiter=',',skiprows=1)
        except:
            try:
                self.prev_table = np.loadtxt(os.path.join(self.exp_blk0, 'final_targets.csv'),dtype='str',delimiter=',',skiprows=1)
            except:
                print('Previous target file not found, results may be suboptimal')
        try:
            for i in range(self.prev_table.shape[0]):
                indices[self.prev_table[i,1]] = int(self.prev_table[i,0])-1
            for i in range(self.table.shape[0]):
                self.table[i,11] = self.prev_table[indices[self.table[i,1].strip()],11]
                self.table[i,12] = self.prev_table[indices[self.table[i,1].strip()],12]
                self.table[i,13] = self.prev_table[indices[self.table[i,1].strip()],13]
        except:
            print('Invalid target file')
        
        self.table = np.array([[item.strip() for item in s] for s in self.table])

        ###################################################
        #only use rows relevant to this block
        #HARDCODED! NOTE IN LOG SHEET
        spec_table = []
        for i in range(self.table.shape[0]):
            if int(self.block)%5 == 0: #block 0 to adjust positions
                if "(p)" in self.table[i,2]:
                    spec_table.append(self.table[i])
            elif int(self.block)%5 == 1:
                if "(L)" in self.table[i,2]:
                    spec_table.append(self.table[i])
            elif int(self.block)%5 == 2:
                if "(L+t)" in self.table[i,2]:
                    spec_table.append(self.table[i])
            elif int(self.block)%5 == 3:
                if "(S)" in self.table[i,2]:
                    spec_table.append(self.table[i])
            elif int(self.block)%5 == 4:
                if "(S+t)" in self.table[i,2]:
                    spec_table.append(self.table[i])
        ###################################################
        self.table = np.array(spec_table)

        self.session_dir = os.path.join(self.DATA_DIR,'exp_2',self.sub_id,self.sess_id,self.wrist,self.hand)
        self.subjinfo = self.sub_id + '_' + self.sess_id + '_' + self.hand + '_log.yml'
        self.p_x,self.p_y,self.p_a = GET_POS(self.session_dir,self.subjinfo,self.hand,self.wrist)

        self.rotmat = ROT_MAT(self.p_a,self.hand)

        self.setup_text()
        self.setup_lights()
        self.setup_camera()
        
        self.trial_counter = 0
        self.load_models()
        self.load_audio()
        self.update_trial_command()
        self.countdown_timer = CountdownTimer()
        self.hold_timer = CountdownTimer()

        self.cTrav = CollisionTraverser()
        self.chandler = CollisionHandlerEvent()
        self.chandler.addInPattern('%fn-into-%in')
        self.chandler.addOutPattern('%fn-outof-%in')
        self.chandler.addAgainPattern('%fn-again-%in')
        self.attachcollnodes()

        taskMgr.add(self.read_data, 'read')
        for i in range(5):
            taskMgr.add(self.move_player, 'move%d' % i, extraArgs = [i], appendTask=True)
        taskMgr.add(self.log_data, 'log_data')
        taskMgr.add(self.update_state, 'update_state', sort=1)

        self.accept('space', self.space_on)
        self.accept('escape', self.clean_up)
        self.space = False
        self.statenum = list()

        self.max_time = 20
        self.med_data = None

        self.grip_dir = os.path.join(self.DATA_DIR,'exp_2',self.sub_id,self.sess_id,self.wrist,self.hand,"B"+self.block)
        if not os.path.exists(self.grip_dir):
           print('Making new folders: ' + self.grip_dir)
           os.makedirs(self.grip_dir)

        self.dev = MpDevice(RightHand(calibration_files=['calibs/cal_mat_70_v2.mat',
                                                'calibs/cal_mat_73_v2.mat',
                                                'calibs/cal_mat_56.mat',
                                                'calibs/cal_mat_58_v2.mat',
                                                'calibs/cal_mat_50.mat'], clock=mono_clock.get_time))

    ############
    #SET UP HUD#
    ############
    def setup_text(self):
        self.bgtext = OnscreenText(text='Not recording.', pos=(-0.8, 0.8),
                                 scale=0.08, fg=(0, 0, 0, 1),
                                 bg=(1, 1, 1, 1), frame=(0.2, 0.2, 0.8, 1),
                                 align=TextNode.ACenter)
        self.bgtext.reparentTo(self.aspect2d)

        self.dirtext = OnscreenText( pos=(-0.6, 0.65),
                                 scale=0.08, fg=(0, 0, 0, 1),
                                 bg=(1, 1, 1, 1), frame=(0.2, 0.2, 0.8, 1),
                                 align=TextNode.ACenter)
        self.dirtext.reparentTo(self.aspect2d)
    
    ##########################
    #SET UP SCENE AND PLAYERS#
    ##########################
    def setup_lights(self):
        pl = PointLight('pl')
        pl.setColor((1, 1, 1, 1))
        plNP = self.render.attachNewNode(pl)
        plNP.setPos(-10, -10, 10)
        self.render.setLight(plNP)
        pos = [[[0, 0, 50], [0, 0, -10]],
               [[0, -50, 0], [0, 10, 0]],
               [[-50, 0, 0], [10, 0, 0]]]
        for i in pos:
            dl = Spotlight('dl')
            dl.setColor((1, 1, 1, 1))
            dlNP = self.render.attachNewNode(dl)
            dlNP.setPos(*i[0])
            dlNP.lookAt(*i[1])
            dlNP.node().setShadowCaster(False)
            self.render.setLight(dlNP)

    def setup_camera(self):
        self.cam.setPos(0, 0, 12)
        self.cam.lookAt(0, 2, 0)
        self.camLens.setFov(90)

    def load_models(self):
        self.back_model = self.loader.loadModel('models/back')
        self.back_model.setScale(10, 10, 10)
        if self.hand == "Left":
            self.back_model.setH(90)
        self.back_model.reparentTo(self.render)

        self.player_offsets = [[self.p_x[0]-5, self.p_y[0]+3, 0], [self.p_x[1]-2.5, self.p_y[1]+4.5, 0], [self.p_x[2], self.p_y[2]+5, 0],
                                [self.p_x[3]+2.5, self.p_y[3]+4.5, 0], [self.p_x[4]+5, self.p_y[4]+3, 0]]
        self.p_col =[[0,0,250],[50,0,200],[125,0,125],[200,0,50],[250,0,0]]
        if self.hand == 'Left':
            self.p_col = self.p_col[::-1]

        self.players = list()
        self.contacts = list()        
        for counter, value in enumerate(self.player_offsets):
            self.players.append(self.loader.loadModel('models/target'))
            self.contacts.append(False)

            self.players[counter].setPos(*value)
            self.players[counter].setScale(0.2, 0.2, 0.2)
            self.players[counter].setColorScale(
                self.p_col[counter][0]/255, self.p_col[counter][1]/255, self.p_col[counter][2]/255, 1)
            self.players[counter].reparentTo(self.render)
            self.players[counter].show()

        self.target_select()

    def load_audio(self):
        self.pop = self.loader.loadSfx('audio/Blop.wav')
        self.buzz = self.loader.loadSfx('audio/Buzzer.wav')


    ############################
    #SET UP COLLISION MECHANICS#
    ############################
    def attachcollnodes(self):
        self.inside = [False]*5
        for i in range(5):
            self.fromObject = self.players[i].attachNewNode(CollisionNode('colfromNode'+str(i)))
            self.fromObject.node().addSolid(CollisionSphere(0,0,0,1))
            self.cTrav.addCollider(self.fromObject, self.chandler)

        for i in range(5):
            self.accept('colfromNode%d-into-colintoNode' % i, self.collide1,[i])
            self.accept('colfromNode%d-again-colintoNode' % i, self.collide2,[i])
            self.accept('colfromNode%d-outof-colintoNode' % i, self.collide3,[i])

    def collide1(self,f,collEntry):
        if f in self.highlighted_indices:
            self.players[f].setColorScale(0,1,0,1)
            self.tar.setColorScale(0.2,0.2,0.2,1)
            self.tar.setAlphaScale(0.7)
            self.contacts[f] = True
            taskMgr.doMethodLater(self.delay,self.too_long,'too_long%d' % f,extraArgs = [f])
                
    def collide2(self,f,collEntry):
        for i in self.highlighted_indices:
            if self.contacts[i] == False:
                return
        taskMgr.remove('too_long%d' % f)

    def collide3(self,f,collEntry):
        taskMgr.remove('too_long%d' % f)
        self.reset_fing(f)
        self.tar.setColorScale(0.1,0.1,0.1,1)
        self.tar.setAlphaScale(0.7)

    def too_long(self,f):
        self.reset_fing(f)
        self.tar.setColorScale(0.5,0.2,0.2,1)
        self.tar.setAlphaScale(0.7)

    def reset_fing(self,f):
        self.players[f].setColorScale(
                self.p_col[f][0]/255, self.p_col[f][1]/255, self.p_col[f][2]/255, 1)
        self.contacts[f] = False

    ###############
    #TARGET THINGS#
    ###############
    def show_target(self):
        self.target_select()
        self.intoObject = self.tar.attachNewNode(CollisionNode('colintoNode'))

        if self.table[self.trial_counter,7] == "sphere":
            self.intoObject.node().addSolid(CollisionSphere(0,0,0,1))
        elif self.table[self.trial_counter,7] == "cylinder":
            self.intoObject.node().addSolid(CollisionTube(0,0,-2,0,0,2,1))
        else:
            raise NameError("No such collision type")

        self.tar.show()
        self.occSolid.show()
        self.occLines.show()

        for i in range(5):
            if i not in self.highlighted_indices:
                self.players[i].hide()

    def target_select(self):
        self.tgtscx=float(self.table[self.trial_counter,14])
        self.tgtscy=float(self.table[self.trial_counter,15])
        self.tgtscz=float(self.table[self.trial_counter,16])
        tgttsx=float(self.table[self.trial_counter,11])
        tgttsy=float(self.table[self.trial_counter,12])
        tgttsz=float(self.table[self.trial_counter,13])
        tgtrx=float(self.table[self.trial_counter,17])
        tgtry=float(self.table[self.trial_counter,18])
        tgtrz=float(self.table[self.trial_counter,19])
        if self.hand == 'Left':
            tgttsx *= -1
            tgtrx *= -1

        self.static_task = (str(self.table[self.trial_counter,5]) == "True")
        self.target_model = str(self.table[self.trial_counter,6])
        self.highlighted_indices=[int(s)-1 for s in self.table[self.trial_counter,4].split(' ')]
        if self.hand == 'Left':
            self.highlighted_indices=[4-i for i in self.highlighted_indices]

        self.tgtposx = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][[-2,-1],0]) + tgttsx
        self.tgtposy = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][[0,1],1]) + tgttsy
        if self.hand == 'Left':
            self.tgtposx = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][[0,1],0]) + tgttsx
            self.tgtposy = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][[-2,-1],1]) + tgttsy
        #self.tgtposx = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][:,0])
        #self.tgtposy = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][:,1])
        self.tgtposz = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][:,2]) + tgttsz
        
        self.tar = self.loader.loadModel(self.target_model)
        self.tar.setScale(self.tgtscx,self.tgtscy,self.tgtscz)
        self.tar.setPos(self.tgtposx,self.tgtposy,self.tgtposz)
        self.tar.setHpr(tgtrx,tgtry,tgtrz)
        self.tar.setColorScale(0.1, 0.1, 0.1, 1)
        self.tar.setAlphaScale(0.7)
        self.tar.setTransparency(TransparencyAttrib.MAlpha)
        self.tar.reparentTo(self.render)
        self.tar.hide()

        if len(self.highlighted_indices) == 2:
            dx = self.players[self.highlighted_indices[0]].getX() - self.players[self.highlighted_indices[1]].getX()
            dy = self.players[self.highlighted_indices[0]].getY() - self.players[self.highlighted_indices[1]].getY()
            angle = math.degrees(math.atan(dy/dx))
            self.table[self.trial_counter,9] =  str(angle) + ' ' + str(angle-180)
        
        self.angs=self.table[self.trial_counter,9].split(' ')
        self.angs = [float(a) for a in self.angs]
        self.tunn_width=float(self.table[self.trial_counter,10])
        self.r = 1.5
        if int(self.block) == 0:
            self.r = 0
        self.x = [self.r*math.cos(math.radians(a)) for a in self.angs]
        self.y = [self.r*math.sin(math.radians(a)) for a in self.angs]
        self.occ = draw_shape(self.angs,self.tunn_width,self.r)
        self.occSolid = render.attachNewNode(self.occ[0])
        self.occSolid.setPos(self.tgtposx,self.tgtposy,self.tgtposz)
        self.occSolid.setColorScale(0,1,1,0)
        self.occSolid.setTransparency(TransparencyAttrib.MAlpha)
        self.occSolid.setAlphaScale(0.6)
        self.occLines = render.attachNewNode(self.occ[1])
        self.occLines.setPos(self.tgtposx,self.tgtposy,self.tgtposz)
        self.occSolid.hide()
        self.occLines.hide()
        self.delay=float(self.table[self.trial_counter,8])

        self.distances = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]

        #change camera to be on top of target
        self.cam.setPos(self.tgtposx, self.tgtposy - 2, 12)
        self.back_model.setPos(self.tgtposx,self.tgtposy - 2,0)
        self.cam.lookAt(self.tgtposx, self.tgtposy, 0)

    ##############
    #MOVE FINGERS#
    ##############
    def read_data(self,task):
        error, data = self.dev.read()
        if data is not None:
            data *= 0.001
            self.ts = data.time
            data = np.dot(data,self.rotmat)
            self.data = data
            if self.med_data is None:
                self.med_data = np.median(data, axis=0)

            if self.space:
                self.statenum.extend(([self.checkstate()])*len(data.time))
                
        return task.cont
        
    def move_player(self,p,task):
        if self.data is not None :
            k = p*3
            new_x = 10*np.mean(self.data[-1,k]) + self.player_offsets[p][0] - 10*self.med_data[k]
            new_y = 10*np.mean(self.data[-1,k + 1]) + self.player_offsets[p][1] - 10*self.med_data[k + 1]
            new_z = 10*np.mean(self.data[-1,k + 2]) + self.player_offsets[p][2] - 10*self.med_data[k + 2]

            #make sure digits do not cross each other
            if ((p in range(1,3) and p+1 in self.highlighted_indices and new_x > self.players[p+1].getX())
                or (p in range(2,4) and p-1 in self.highlighted_indices and new_x < self.players[p-1].getX())):
                    new_x = self.players[p].getX()
            
            #make sure digits do not cross into target
            if self.space == True and p in self.highlighted_indices:
                self.distances[p][0] = new_x - self.tar.getX()
                self.distances[p][1] = new_y - self.tar.getY()
                self.distances[p][2] = new_z - self.tar.getZ()
                self.check_pos(p)
                
            self.players[p].setPos(new_x, new_y, new_z)
            
        return task.cont
    
    def check_pos(self, p):
        x = self.distances[p][0]
        y = self.distances[p][1]
        z = self.distances[p][2]
        hit = True
        for i in range(len(self.angs)):
            p_ang = math.acos((x*self.x[i]+y*self.y[i])/(self.r*(x**2+y**2)**0.5))
            if math.sin(p_ang)*(x**2+y**2)**0.5 < self.tunn_width and p_ang < math.pi/2:
                hit = False
                break
        if (abs(z) <= 1.2 #check z location
            and x**2 + y**2 <= self.r**2 #within radius of circle
            and hit == True):
                if self.inside[p] is False:
                    self.ignore('colfromNode%d-into-colintoNode' % p)
                    self.ignore('colfromNode%d-again-colintoNode' % p)
                    self.players[p].setColorScale(1,1,0,1)
                    self.inside[p] = True
        else:
            if self.inside[p] is True and x**2 + y**2 > self.r**2:
                self.accept('colfromNode%d-into-colintoNode' % p, self.collide1,[p])
                self.accept('colfromNode%d-again-colintoNode' % p, self.collide2,[p])
                self.players[p].setColorScale(
                    self.p_col[p][0]/255, self.p_col[p][1]/255, self.p_col[p][2]/255, 1)
                self.inside[p] = False

    ##################
    #CHECK COMPLETION#
    ##################
    def close_to_target(self):
        for i in self.highlighted_indices:
            if self.contacts[i] == False:
                return False

        self.tar.setColorScale(0,1,1,1)
        return True

    def check_hold(self):
        if not self.close_to_target():
            self.hold_timer.reset(0.5)
            return False
        return self.hold_timer.elapsed() < 0

    def adjust_targets(self):
        #no adjustment if more than 2 fingers or position is prone
        if len(self.highlighted_indices) > 2 or self.wrist == 'pron':
            return

        xadj,yadj,zadj = np.mean(np.asarray(self.distances)[self.highlighted_indices],0)
        #xadj = np.mean(np.asarray(self.distances)[self.highlighted_indices][0])
        #yadj = np.mean(np.asarray(self.distances)[self.highlighted_indices][1])
        #zadj = np.mean(np.asarray(self.distances)[self.highlighted_indices][2])

        #do adjustment on all tasks with same name
        if self.hand == 'Left':
            xadj = -xadj
        for i in range(self.trial_counter+1,self.table.shape[0]):
            if self.table[i,1] == self.table[self.trial_counter,1]:
                self.table[i,11] = float(self.table[i,11]) + xadj
                self.table[i,12] = float(self.table[i,12]) + yadj
                self.table[i,13] = float(self.table[i,13]) + zadj
    
    #########
    #LOGGING#
    #########
    def play_success(self):
        if int(self.block) == 0:
            self.adjust_targets()
        self.pop.play()
        self.tar.hide()
        self.highlighted_indices = [0,1,2,3,4]

    def log_text(self):
        self.bgtext.setText('Now logging...')

    def log_data(self, task):
        if (self.trial_counter + 1) <= self.table.shape[0]:
            if self.space:
                self.log_file_name = os.path.join(self.grip_dir,
                                          self.sub_id+"_"+self.sess_id+"_"+self.hand+"_"+
                                              str(self.table[self.trial_counter,1])+"_"+str(self.table[self.trial_counter,0])+".csv" )
                self.movvars = np.column_stack((self.ts, self.statenum, self.data))
                self.statenum = []
                if self.mode=='task':
                    with open(self.log_file_name, 'ab') as f:
                        np.savetxt(f, self.movvars, fmt='%10.5f', delimiter=',')
            return task.cont
        else:
            pass

    def stoplog_text(self):
        self.dirtext.clearText()
        self.bgtext.setText('Done logging!')
        for i in range(5):
            self.players[i].show()

    #######
    #RESET#
    #######
    def delete_file(self):
        if (self.trial_counter + 1) <= self.table.shape[0]:
            self.log_file_name = os.path.join(self.grip_dir,
                                      self.sub_id+"_"+self.sess_id+"_"+self.hand+"_"+
                                          str(self.table[self.trial_counter,1])+"_"+str(self.table[self.trial_counter,0])+".csv" )
            try:
                os.remove(self.log_file_name)
            except OSError:
                pass
        else:
            pass

    def reset_baseline(self):
       self.med_data = None
                   
    def reset_keyboard_bool(self):
        self.space = False

    def hide_target(self):
        self.tar.hide()
        self.occSolid.hide()
        self.occLines.hide()
        self.intoObject.removeNode()
        self.imageObject.destroy()

    def update_trial_command(self):
        self.dirtext.setText(str(self.table[self.trial_counter,2]))
        if self.hand == 'Left':
            xfac = -0.25
        else:
            xfac = 0.25
        self.imageObject = OnscreenImage(image = str(self.table[self.trial_counter,3]),scale=(xfac,0.25,0.25),pos=(-0.8, 0, 0.3))

    def increment_trial_counter(self):
        self.trial_counter += 1
        self.update_trial_command()

    ########
    #TIMERS#
    ########
    def start_trial_countdown(self):
        self.countdown_timer.reset(self.max_time)

    def start_hold_countdown(self):
        self.hold_timer.reset(0.5)

    def start_post_countdown(self):
        self.countdown_timer.reset(2)

    def time_elapsed(self):
        return self.countdown_timer.elapsed() < 0

    #########
    #MACHINE#
    #########
    def update_state(self, task):
        self.step()
        return task.cont

    def wait_for_space(self):
        return self.space

    def space_on(self):
        self.space = True

    #####
    #END#
    #####
    def trial_counter_exceeded(self):
        return (self.trial_counter+1) > self.table.shape[0]-1

    def clean_up(self):
        #write last known positions to 'final_targets' file
        f = open('src/pinch_task/trialtable_flex.csv')
        header = f.readline().rstrip()
        np.savetxt(self.grip_dir + '/final_targets.csv',self.table,fmt='%s',header = header, delimiter=',')
        f.close()
        sys.exit()
class PenaltyKick(ShowBase):
    #angleD
    global co
    co = 0

    def __init__(self):
        #try:
        #if(co == 0):
        ShowBase.__init__(self)
        #	co += 1
        self.disableMouse()
        base.setBackgroundColor(0, 0, 1)
        #camera.setPosHpr(45, -45, 45, 45, -45, 45)
        self.accept("escape", sys.exit)  # Escape quits

        # Disable default mouse-based camera control.  This is a method on the
        # ShowBase class from which we inherit.
        #self.disableMouse()
        camera.setPosHpr(-10, -50, 10, -10, -7, 0)
        #camera.setPosHpr(-50, 40, 0, 270, 0, 0)
        #base.setBackgroundColor(0,1,0)
        #base
        net = loader.loadModel("models/drawnNet")
        net.setColor(1, 1, 0)
        net.setScale(2.5, 2.5, 1.5)
        net.setPosHpr(0, 53, -2, 0, -10, 0)
        net.reparentTo(render)

        global targetY
        targetY = 40

        postR = loader.loadModel("models/box")
        postR.setColor(0.75, 0, 0.25)
        postR.setScale(0.03, 0.03, 1)
        LidR = postR.find('**/lid')
        PandaR = postR.find('**/turningthing')
        HingeNodeR = postR.find('**/box').attachNewNode('nHingeNode')
        HingeNodeR.setPos(.8659, 6.5, 5.4)
        LidR.wrtReparentTo(HingeNodeR)
        HingeNodeR.setHpr(0, 90, 0)
        lidCloseR = Parallel(
            LerpHprInterval(HingeNodeR, 2.0, (0, 90, 0),
                            blendType='easeInOut'))
        postR.setPosHpr(18, 55.5, -2, 0, 0, 0)
        postR.reparentTo(render)

        postGR = loader.loadModel("models/box")
        postGR.setColor(0.75, 0, 0.25)
        postGR.setScale(0.03, 0.03, 1)
        lidGR = postGR.find('**/lid')
        PandaGR = postGR.find('**/turningthing')
        HingeNodeGR = postGR.find('**/box').attachNewNode('nHingeNode')
        HingeNodeGR.setPos(.8659, 6.5, 5.4)
        #lidGR.wrtReparentTo(HingeNodeR)
        HingeNodeGR.setHpr(0, 90, 0)
        lidCloseGR = Parallel(
            LerpHprInterval(HingeNodeGR,
                            2.0, (0, 90, 0),
                            blendType='easeInOut'))
        postGR.setPosHpr(18, 58.5, -4, 90, 0, 90)
        postGR.reparentTo(render)

        postL = loader.loadModel("models/box")
        postL.setColor(0.75, 0, 0.25)
        postL.setScale(0.03, 0.03, 2)
        LidL = postL.find('**/lid')
        PandaL = postL.find('**/turningthing')
        HingeNodeL = postL.find('**/box').attachNewNode('nHingeNode')
        HingeNodeL.setPos(.8659, 6.5, 5.4)
        LidL.wrtReparentTo(HingeNodeL)
        HingeNodeL.setHpr(0, 90, 0)
        lidCloseL = Parallel(
            LerpHprInterval(HingeNodeL, 2.0, (90, 0, 0),
                            blendType='easeInOut'))
        postL.setPosHpr(-18, 55.5, -1, 0, 0, 0)
        postL.reparentTo(render)

        postGL = loader.loadModel("models/box")
        postGL.setColor(0.75, 0, 0.25)
        postGL.setScale(0.03, 0.03, 1)
        lidGL = postGL.find('**/lid')
        PandaGL = postGL.find('**/turningthing')
        HingeNodeGL = postGL.find('**/box').attachNewNode('nHingeNode')
        HingeNodeGL.setPos(.8659, 6.5, 5.4)
        #lidGR.wrtReparentTo(HingeNodeR)
        HingeNodeGL.setHpr(0, 90, 0)
        lidCloseGL = Parallel(
            LerpHprInterval(HingeNodeGL,
                            2.0, (0, 90, 0),
                            blendType='easeInOut'))
        postGL.setPosHpr(-18, 58.5, -4.5, 90, 0, 90)
        postGL.reparentTo(render)
        #camera.setPosHpr(20, 45, 0, 90, 0, 0)

        postT = loader.loadModel("models/box")
        postT.setColor(0.75, 0, 0.25)
        postT.setScale(1.70, 0.03, 0.03)
        LidT = postL.find('**/lid')
        PandaT = postT.find('**/turningthing')
        HingeNodeT = postT.find('**/box').attachNewNode('nHingeNode')
        HingeNodeT.setPos(.8659, 6.5, 5.4)
        LidT.wrtReparentTo(HingeNodeT)
        HingeNodeT.setHpr(0, 90, 0)
        lidCloseT = Parallel(
            LerpHprInterval(HingeNodeT, 2.0, (0, 90, 0),
                            blendType='easeInOut'))
        postT.setPosHpr(0, 55.5, 9.8, 0, 0, 0)
        postT.reparentTo(render)

        global ball
        ballRoot = render.attachNewNode("ballRoot")
        ball = loader.loadModel("models/ball")
        ball.reparentTo(ballRoot)
        #ball.setColor(0.5, 0, 1)
        ball.setScale(6, 6, 6)
        ball.setPosHpr(0, -12, -3, 0, -20, 0)
        #self.kick.setPos(0, 40, 0)
        ballTex = loader.loadTexture("pictures/ball.jpg")
        ball.setTexture(ballTex)
        ball.reparentTo(render)
        ballSphere = ball.find("**/ball")
        ballSphere.node().setFromCollideMask(BitMask32.bit(0))
        ballSphere.node().setIntoCollideMask(BitMask32.allOff())

        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor((.55, .55, .55, 1))
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(LVector3(0, 0, -1))
        directionalLight.setColor((0.375, 0.375, 0.375, 1))
        directionalLight.setSpecularColor((1, 1, 1, 1))

        ballRoot.setLight(render.attachNewNode(ambientLight))
        ballRoot.setLight(render.attachNewNode(directionalLight))
        m = Material()
        m.setSpecular((1, 1, 1, 1))
        m.setShininess(96)
        ball.setMaterial(m, 1)

        cs = CollisionSphere(0, 0, 0, 0.41)
        cNodePath = ball.attachNewNode(CollisionNode('cnode'))
        cNodePath.node().addSolid(cs)
        #cNodePath.show()
        #0, 53, -2
        cW = CollisionPolygon(Point3(-1, 40, -4), Point3(-1, 40, 8),
                              Point3(1, 40, 8), Point3(1, 40, -4))
        cwNodePath = net.attachNewNode(CollisionNode('cwnode'))
        cwNodePath.node().addSolid(cW)
        #cwNodePath.show()

        #queue = CollisionHandlerQueue()
        #traverser.addCollider(cs, queue)
        #traverser.traverse(render)

        ground = loader.loadModel("models/square")
        ground.setPosHpr(0, 35, -5, 0, 0, 0)
        ground.setScale(120, 120, 120)
        #ground.setColor(0.2, 1, 0)
        grass = loader.loadTexture("pictures/grass_1.jpg")
        ground.setTexture(grass)
        ground.reparentTo(render)

        wall = loader.loadModel("models/square")
        wall.setPosHpr(5, 100, 18, 0, 90, 0)
        wall.setScale(90, 50, 75)
        #camera.setPos(0, -150, 0)
        #wall.setColor(0.5, 0.5, 0.5)
        crowd = loader.loadTexture("pictures/crowd.png")
        wall.setTexture(crowd)
        wall.reparentTo(render)

        rightWall = loader.loadModel("models/square")
        rightWall.setPosHpr(48, 45, 0, -90, 90, 0)
        rightWall.setScale(100, 100, 100)
        rightWall.setColor(0.75, 0.75, 0.75)
        rightWall.reparentTo(render)

        global angle
        angle = loader.loadModel("models/box")
        angle.setColor(0.25, 0.25, 0.25)
        angle.setScale(1.0, 0.25, 0.03)
        LidA = angle.find('**/lid')
        PandaA = angle.find('**/turningthing')
        HingeNodeA = angle.find('**/box').attachNewNode('nHingeNode')
        HingeNodeA.setPos(.8659, 6.5, 5.4)
        LidA.wrtReparentTo(HingeNodeA)
        HingeNodeA.setHpr(0, 90, 0)
        lidCloseA = Parallel(
            LerpHprInterval(HingeNodeA, 2.0, (0, 90, 0),
                            blendType='easeInOut'))
        angle.setPosHpr(0, 10.5, -3, 0, 90, 0)
        #bar = loader.loadTexture("pictures/bar.png")
        #angle.setTexture(bar)
        angle.reparentTo(render)

        global angleD
        angleD = loader.loadModel("models/box")
        angleD.setColor(0, 0, 1)
        angleD.setScale(0.1, 0.25, 0.03)
        LidD = angleD.find('**/lid')
        PandaD = angleD.find('**/turningthing')
        HingeNodeD = angleD.find('**/box').attachNewNode('nHingeNode')
        HingeNodeD.setPos(.8659, 6.5, 5.4)
        LidD.wrtReparentTo(HingeNodeD)
        HingeNodeD.setHpr(0, 90, 0)
        lidCloseD = Parallel(
            LerpHprInterval(HingeNodeD, 2.0, (0, 90, 0),
                            blendType='easeInOut'))
        angleD.setPosHpr(0, 10.4, -3, 0, 90, 0)
        angleD.reparentTo(render)

        global power
        power = loader.loadModel("models/box")
        power.setColor(0.25, 0.25, 0.25)
        power.setScale(0.12, 2.2, 0.03)
        LidP = power.find('**/lid')
        PandaP = power.find('**/turningthing')
        HingeNodeP = power.find('**/box').attachNewNode('nHingeNode')
        HingeNodeP.setPos(.8659, 6.5, 5.4)
        LidP.wrtReparentTo(HingeNodeP)
        HingeNodeP.setHpr(0, 90, 0)
        lidCloseP = Parallel(
            LerpHprInterval(HingeNodeP, 2.0, (0, 90, 0),
                            blendType='easeInOut'))
        power.setPosHpr(-18, 10.5, -3, 0, 90, 0)

        global powerD
        powerD = loader.loadModel("models/box")
        powerD.setColor(1, 0, 0)
        powerD.setScale(0.12, 0.05, 0.03)
        LidD = power.find('**/lid')
        PandaD = power.find('**/turningthing')
        HingeNodeD = power.find('**/box').attachNewNode('nHingeNode')
        HingeNodeD.setPos(.8659, 6.5, 5.4)
        LidD.wrtReparentTo(HingeNodeD)
        HingeNodeD.setHpr(0, 90, 0)
        lidCloseD = Parallel(
            LerpHprInterval(HingeNodeD, 2.0, (0, 90, 0),
                            blendType='easeInOut'))
        powerD.setPosHpr(-18, 10, -4.5, 0, 50, 0)

        global bishop
        bishop = loader.loadModel("models/bishop")
        bs = loader.loadTexture("pictures/bishop.png")
        bishop.setTexture(bs)
        bishop.setScale(10, 10, 10)
        #bishop.setColor(0, 0.4, 1)
        bishop.setPosHpr(0, 42, -5, 90, 0, 0)
        bishop.reparentTo(render)

        global bArmL
        bArmL = loader.loadModel("models/bishop")
        bArmL.setScale(2, 2, 4)
        bishop.setTexture(bs)
        #bArmL.setColor(0, 0.4, 1)
        bArmL.setPos(-1, 55, 6)
        #bArmL.reparentTo(render)

        global bArmR
        bArmR = loader.loadModel("models/bishop")
        bArmR.setScale(2, 2, 4)
        bishop.setTexture(bs)
        #bArmR.setColor(0, 0.4, 1)
        bArmR.setPos(1, 55, 6)
        #bArmR.reparentTo(render)

        global start
        start = loader.loadModel("models/square")
        start.setPosHpr(5, -25, 18, 0, 90, 0)
        start.setScale(90, 50, 75)
        #camera.setPos(0, -150, 0)
        start.setColor(0.5, 0.5, 0.5)
        #crowd = loader.loadTexture("pictures/crowd.png")
        #start.setTexture(crowd)
        start.reparentTo(render)

        title = TextNode('title')
        title.setText("Welcome to Penalty Kick!")
        global textNodePath
        textNodePath = aspect2d.attachNewNode(title)
        textNodePath.setScale(0.25)
        title.setWordwrap(8)
        title.setCardColor(0, 0, 0, 0)
        title.setCardAsMargin(0, 0, 0, 0)
        title.setCardDecal(True)
        textNodePath.setPos(-0.75, 0, 0.5)
        self.instructions = \
               OnscreenText(text="Press any key to begin",
                           parent=base.a2dBottomRight, align=TextNode.ARight,
                           pos=(-1, +0.08), fg=(1, 1, 1, 1), scale=.06,
                           shadow=(0, 0, 0, 0.5))
        #self.accept('ball-into-net', ballCollideHandler)
        base.buttonThrowers[0].node().setKeystrokeEvent('keystroke')
        self.accept('keystroke', self.next)
        #textNodePath.reparentTo(render)
    def ballCollideHandler(self, entry):
        ballLerp = Sequence(
            Parallel(
                LerpPosInterval(ball, 0.75, Point3(ball.getX(), 0,
                                                   ball.getZ()),
                                Point3(ball.getX(), ball.getY(), ball.getZ()),
                                None, 'noBlend', 0, 1, 'bLerp')))
        if (counter < 5):
            self.score = \
             score.clear()
            OnscreenText(text=counter + "/5",
                         parent=base.a2dTopRight,
                         align=TextNode.ARight,
                         fg=(1, 1, 1, 1),
                         pos=(-0.1, 0.1),
                         scale=.1,
                         shadow=(0, 0, 0, 0.5))
        counter += 1
        #camera.setPosHpr(-15, -85, 0, -20, -10, 0)
    def nothing(self, x):
        pass
        counter = 0

    def blank_(self):
        pass

    def next(self, x):
        base.buttonThrowers[0].node().setKeystrokeEvent('keystroke')
        self.accept('keystroke', self.nothing)
        self.accept('enter', self.blank_)
        start.setPos(0, -100, 0)
        #self.instructions.clearText()
        textNodePath.setPos(-100, -100, 0)
        self.instructions.clearText()
        self.title = \
            OnscreenText(text="Penalty Kick",
                           parent=base.a2dBottomRight, align=TextNode.ARight,
                           fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=0.15,
                          shadow=(0, 0, 0, 0.5))
        self.instructions = \
         OnscreenText(text="Choose your angle with the space bar",
                           parent=base.a2dTopLeft, align=TextNode.ALeft,
                           pos=(0.05, -0.08), fg=(1, 1, 1, 1), scale=.1,
                           shadow=(0, 0, 0, 0.5))

        self.angleText = \
               OnscreenText(text="Angle",
                           parent=base.a2dBottomRight, align=TextNode.ARight,
                           pos=(-1.3, +.82), fg=(1, 1, 1, 1), scale=0.15,
                           shadow=(0, 0, 0, 0.5))
        #self.score = \
        #	OnscreenText(text="0/5",
        #   			parent=base.a2dTopRight, align=TextNode.ARight,
        #    		fg=(1, 1, 1, 1), pos=(-0.1, 0.1), scale=.08, shadow=(0, 0, 0, 0.5))
        global bLerp
        bLerp = Sequence(
            Parallel(
                LerpPosInterval(bishop, 0.6, Point3(-15, 57, -5),
                                Point3(15, 57, -5), None, 'easeInOut', 0, 1,
                                'bLerp')),
            LerpPosInterval(bishop, 0.5, Point3(15, 57,
                                                -5), Point3(-15, 57, -5), None,
                            'easeInOut', 0, 1, 'bLerp'))
        #bishopInterval = bishop.posInterval
        bLerp.loop()

        #ball sims until location reached, then sims a drop down. no collision check needed

        global angleDLerp
        angleDLerp = Sequence(
            Parallel(
                LerpPosInterval(angleD, 0.6, Point3(-9.5, 10.4, -3),
                                Point3(9.5, 10.4, -3), None, 'easeInOut', 0, 1,
                                'bLerp')),
            LerpPosInterval(angleD, 0.8, Point3(10, 10.4, -3),
                            Point3(-10, 10.4, -3), None, 'easeInOut', 0, 1,
                            'bLerp'))
        angleDLerp.loop()

        global powerDLerp
        powerDLerp = Sequence(
            Parallel(
                LerpPosInterval(powerD, 0.3, Point3(-18, 10, -4.5),
                                Point3(-18, 10, 11), None, 'easeInOut', 0, 1,
                                'bLerp')),
            LerpPosInterval(powerD, 0.5, Point3(-18, 10, 11),
                            Point3(-18, 10, -4.5), None, 'easeInOut', 0, 1,
                            'bLerp'))
        x = 1
        self.accept('space', self.angleDis)

#place
        #print("hi")
        #inp = input("")       # Get the input
        #while inp != "":        # Loop until it is a blank line
        #	print("??")
        #	inp = input()
        #	angleD.setFluidPos(-10, 10.4, -3)
        #	angleD.setFuildPos(10, 10.4, -3)
        #pause = input('')

        #click event, fire ball

    global oneToFive
    oneToFive = 0
    global oneOrTwo
    oneOrTwo = 0

    def angleDis(self):
        self.angleText.clearText()
        ballPos = ball.getPos(render)
        angleDPos = angleD.getPos(render)
        direction = ballPos - angleDPos
        #fn = ForceNode('push')
        #p = LinearVectorForce((direction + ballPos)  * 2)
        #fn.addForce(p)
        #ball.addLinearForce(pull)
        #self.setVelocity(ball, LVector3(ballPos-angleDPos))
        #print(ballPos, angleDPos)
        ballX = ball.getX(render)
        #print(ballPos, ballX)
        #ball.setFluidPos(ballPos + direction * 1)
        ballX = ball.getX(render)
        ballY = ball.getY(render)
        ballZ = ball.getZ(render)
        angleDX = angleD.getX(render)
        angleDY = angleD.getY(render)
        angleDZ = angleD.getZ(render)
        angleDD = (angleDX + 9.5) / (19)
        #print(angleDX)
        if (angleDD <= 0.2):
            oneToFive = 1
        elif (angleDD <= 0.4):
            oneToFive = 2
        elif (angleDD <= 0.6):
            oneToFive = 3
        elif (angleDD <= 0.8):
            oneToFive = 4
        else:
            oneToFive = 5
        #print(oneToFive)
        #global ballLerp
        #ballLerp = Sequence(Parallel(LerpPosInterval(ball, 1, Point3(ball.getX(), 70, ball.getZ() + 10), ball.getPos(), None, 'easeInOut', 0, 1, 'bLerp')))
        #ballLerp.start()
        #ballLerp.finish()
        #print(angleDPos, angleDX, angleDY, angleDZ)
        #print(angleD.getPos(render), direction, ballPos, ballX, ballY, ballZ)
        #print
        self.instructions.clearText()
        self.instructions = \
               OnscreenText(text="Choose the power with the space bar",
                           parent=base.a2dTopLeft, align=TextNode.ALeft,
                           pos=(0.05, -0.08), fg=(1, 1, 1, 1), scale=.06,
                           shadow=(0, 0, 0, 0.5))
        self.powerText = \
               OnscreenText(text="Power",
                           parent=base.a2dBottomRight, align=TextNode.ARight,
                           pos=(-2.2, 1.6), fg=(1, 1, 1, 1), scale=.15,
                           shadow=(0, 0, 0, 0.5))
        power.reparentTo(render)

        #angle.setPos(0,0, -100)
        angleDLerp.finish()
        angleD.setPos(angleDX, angleDY, angleDZ)
        powerD.reparentTo(render)
        powerDLerp.loop()
        self.accept('space', self.powerDis, [oneToFive])

    def powerDis(self, oTF):
        self.powerText.clearText()
        powerDX = powerD.getX(render)
        powerDY = powerD.getY(render)
        powerDZ = powerD.getZ(render)
        powerDLerp.finish()
        powerD.setPos(powerDX, powerDY, powerDZ)
        #print(11-(15.5/2), powerDZ)
        if (powerDZ > 11 - (15.5 / 2)):
            oneOrTwo = 2
        else:
            oneOrTwo = 1

        #go in direction that angle bar says, in relative speed
        ballKZ = 2
        #print(oTF)
        ballKX = oTF * (0.2 * 19)
        if (oneOrTwo == 2):
            #print("hi")
            ballKZ += 8
        fifth = (1 / 3) * 19
        #		print(-9.5 + (oTF * fifth))
        tmp1 = -9.5 - fifth
        tmp2 = 9.5 + fifth
        #print(-9.5 + (oTF * (0.2*(tmp2-tmp1))), tmp2-tmp1, 0.2*(tmp2-tmp1), oTF*(0.2*(tmp2-tmp1)))
        #print(1/3)
        #ball.setX(ball.getX() + 5)
        goalieX = bishop.getX()
        goalieY = bishop.getY()
        goalieZ = bishop.getZ()
        if (abs(goalieX - ballKX) < 0.5):
            self.ballDown()
        bLerp.finish()
        bishop.setPos(goalieX, goalieY, goalieZ)
        #pt = #Point3(2*(-9.5 + (oTF * (0.2*(tmp2-tmp1)))
        #	pt.setY(-1)
        tmp = 1.7 * (-9.5 + (oTF * (0.2 * (tmp2 - tmp1))))
        num = 63
        num2 = 65
        if (oneOrTwo == 2):
            num = 55
        rand = random.randrange(1, 2, 1)

        #print(rand)
        global blocked
        blocked = False
        if (abs(goalieX - tmp) < 5):
            if ((rand == oneOrTwo) or abs(goalieX - tmp) - 3):
                if (oneOrTwo == 2):
                    yyy = tmp - 1.5 * 0.6
                    #print(yyy, tmp)
                    bArmL.setX(yyy)
                    if (oneToFive == 1):
                        bArmR.setX(yyy - 2)
                    elif (oneToFive == 2):
                        bArmR.setX(yyy - 1)
                    elif (oneToFive == 3):
                        bArmR.setX(yyy + 0)
                    elif (oneToFive == 4):
                        bArmR.setX(yyy + 1)
                    else:
                        bArmR.setX(yyy + 2)

                    bArmL.reparentTo(render)
                    bArmR.reparentTo(render)
                bishop.setX(tmp)
                num = 55
                num2 = 55
                blocked = True
            elif (oneOrTwo == 1):
                blocked = True
        #else:
        #	print(goalieX, tmp)
        #print(goalieX, tmp, abs(goalieX-tmp))
        ballLerp = Sequence(
            Parallel(
                LerpPosInterval(ball, 0.5, Point3(tmp, num, ballKZ),
                                ball.getPos(), None, 'easeInOut', 0, 1,
                                'bLerp')),
            LerpPosInterval(ball, 0.75, Point3(tmp, num2, -3),
                            Point3(tmp, num, ballKZ), None, 'easeInOut', 0, 1,
                            'bLerp'), Func(self.blockedC))

        angle.setPos(0, 0, -100)
        angleD.setPos(0, 0, -100)
        power.setPos(0, 0, -111)
        powerD.setPos(0, 0, -111)
        self.instructions.clearText()
        self.title.clearText()
        ballLerp.start()
        #self.__init__()
        #power is according to length of awwor
        #goalie moves in random direction, speed on click
        #if ball/goalie collide, no points for that round.
        #out of five points. Whoever has more at end wins.

    kicked = False

    #def mousemove(event):
    #	print("1")
    #def mousedown(event):
    #	if not kicked:
    #		print("")
    def bC(self):
        #print("l")
        pass

    def blockedC(self):
        if (blocked):
            self.title.clearText()
            self.instructions.clearText()

            start.setPosHpr(5, -25, 18, 0, 90, 0)
            start.setColor(1, 0, 0)

            title1 = TextNode('game-over')
            title1.setText("You Failed. Try Again?")
            global tNP1
            tNP1 = aspect2d.attachNewNode(title1)
            tNP1.setScale(0.25)
            title1.setWordwrap(5)
            title1.setCardColor(0, 0, 0, 0)
            title1.setCardAsMargin(0, 0, 0, 0)
            title1.setCardDecal(True)
            tNP1.setPos(-0.65, 0, 0.5)

            self.instructions = \
                  OnscreenText(text="Press any key to restart",
                              parent=base.a2dBottomRight, align=TextNode.ARight,
                              pos=(-1, +0.08), fg=(1, 1, 1, 1), scale=.08,
                              shadow=(0, 0, 0, 0.5))
            #base.buttonThrowers[0].node().setKeystrokeEvent('keystroke')
            #self.accept('keystroke', self.myFunc)

            #yes = TextNode('yes')
            #yes.setText("Yes")
            #global tNPy
            #tNPy = aspect2d.attachNewNode(yes)
            #tNPy.setScale(0.25)
            #yes.setWordwrap(10)
            #yes.setCardColor(0, 0, 0, 0)
            #yes.setCardAsMargin(0, 0, 0, 0)
            #yes.setCardDecal(True)
            #yes.setCardBorder(2,2)
            #tPNy.setPos(-1.0, 1.0, 1)

            #self.yes_ = \
            #OnscreenText(text="Yes",
            #         parent=base.a2dBottomLeft, align=TextNode.ALeft,
            #         pos=(0.05, 0.05), fg=(1, 1, 1, 1), scale=.35,
            #         shadow=(0, 0, 0, 0.5))
            #self.enableMouse()
            #camera.setPosHpr(-10, -50, 10, -10, -7, 0)
            #self.accept('mouse1', self.yesno_)
            base.buttonThrowers[0].node().setKeystrokeEvent('keystroke')
            self.accept('keystroke', self.myFunc)
        else:
            self.title.clearText()
            self.instructions.clearText()
            start.setPosHpr(5, -25, 18, 0, 90, 0)
            start.setColor(0, 1, 0)

            title2 = TextNode('congrats')
            title2.setText("Congratulations! Do it again?")
            global tNP2
            tNP2 = aspect2d.attachNewNode(title2)
            tNP2.setScale(0.25)
            title2.setWordwrap(8)
            title2.setCardColor(0, 0, 0, 0)
            title2.setCardAsMargin(0, 0, 0, 0)
            title2.setCardDecal(True)
            tNP2.setPos(-1, 0, 0.5)

            self.instructions = \
                  OnscreenText(text="Press any key to restart",
                              parent=base.a2dBottomRight, align=TextNode.ARight,
                              pos=(-1, +0.08), fg=(1, 1, 1, 1), scale=.08,
                              shadow=(0, 0, 0, 0.5))

            base.buttonThrowers[0].node().setKeystrokeEvent('keystroke')
            self.accept('keystroke', self.myFunc)
            #self.no_ = \
            #OnscreenText(text="No",
            #         parent=base.a2dBottomRight, align=TextNode.ALeft,
            #         pos=(0, 0.05), fg=(1, 1, 1, 1), scale=.35,
            #         shadow=(0, 0, 0, 0.5))
            #self.enableMouse()
            #camera.setPosHpr(-10, -50, 10, -10, -7, 0)
            #self.accept('mouse1', self.yesno_)
    def ballDown(self):
        pass

    def myFunc(self, x):
        os.execl(sys.executable, sys.executable, *sys.argv)
        #os.startfile(sys.argv[0])
        #os.execl(sys.executable,'"%s"'%sys.argv[0])
        #ShowBase.restart(False, None)
        #ShowBase.destroy()
        #ShowBase.__init__(self)
    def yesno_(self):
        pass

    def onclick(event):
        #ctypes.windll.user32.MessageBoxW(0, "hi", "title", 1)
        print("clicked", event.x, event.y)
Exemple #4
0
class PinchTask(ShowBase, GripStateMachine):
    DATA_DIR = 'data'
    
    def __init__(self, id, session, hand, block, mode, wrist):
        ShowBase.__init__(self)
        GripStateMachine.__init__(self)
        base.disableMouse()
        wp = WindowProperties()
        wp.setSize(1920,1080)
        wp.setFullscreen(True)
        base.win.requestProperties(wp)

        self.sub_id = str(id)
        self.sess_id = str(session)
        self.hand = str(hand)
        self.block = str(block)
        self.mode = str(mode)
        self.wrist = str(wrist)
        
        self.prev_blk = os.path.join(self.DATA_DIR,'exp_1',self.sub_id,self.sess_id,self.wrist,self.hand,"B0")
        if self.wrist == 'pron':
            self.table = np.loadtxt('src/pinch_task/trialtable_pron.csv',dtype='str',delimiter=',',skiprows=1)
        elif self.wrist == 'flex':
            self.table = np.loadtxt('src/pinch_task/trialtable_flex.csv',dtype='str',delimiter=',',skiprows=1)
        else:
            raise NameError('Wrist position not found')

        try:
            self.prev_table = np.loadtxt(os.path.join(self.prev_blk, 'final_targets.csv'),dtype='str',delimiter=',',skiprows=1)
            indices = {}
            for i in range(self.prev_table.shape[0]):
                indices[self.prev_table[i,1]] = int(self.prev_table[i,0])-1
            for i in range(self.table.shape[0]):
                self.table[i,11] = self.prev_table[indices[self.table[i,1].strip()],11]
                self.table[i,12] = self.prev_table[indices[self.table[i,1].strip()],12]
                self.table[i,13] = self.prev_table[indices[self.table[i,1].strip()],13]
        except:
            print('Previous target file not found')
        
        self.table = np.array([[item.strip() for item in s] for s in self.table])

        ##################################################
        #only use rows relevant to this block
        #HARDCODED! NOTE IN LOG SHEET
        spec_table = []
        for i in range(self.table.shape[0]):
            if int(self.block)%4 == 0:
                if "(p)" in self.table[i,2]:
                    spec_table.append(self.table[i])
            elif int(self.block)%4 == 1:
                if "(t)" in self.table[i,2]:
                    spec_table.append(self.table[i])
            elif int(self.block)%4 == 2:
                if "(s)" in self.table[i,2]:
                    spec_table.append(self.table[i])
            elif int(self.block)%4 == 3:
                if "(t+s)" in self.table[i,2]:
                    spec_table.append(self.table[i])
        ###################################################
        self.table = np.array(spec_table)

        self.session_dir = os.path.join(self.DATA_DIR,'exp_1',self.sub_id,self.sess_id,self.wrist,self.hand)
        self.subjinfo = self.sub_id + '_' + self.sess_id + '_' + self.hand + '_log.yml'
        self.p_x,self.p_y,self.p_a = GET_POS(self.session_dir,self.subjinfo,self.hand,self.wrist)

        self.rotmat = ROT_MAT(self.p_a,self.hand)

        self.setup_text()
        self.setup_lights()
        self.setup_camera()
        
        self.trial_counter = 0
        self.load_models()
        self.load_audio()
        self.update_trial_command()
        self.countdown_timer = CountdownTimer()
        self.hold_timer = CountdownTimer()

        self.cTrav = CollisionTraverser()
        self.chandler = CollisionHandlerEvent()
        self.chandler.addInPattern('%fn-into-%in')
        self.chandler.addAgainPattern('%fn-again-%in')
        self.chandler.addOutPattern('%fn-outof-%in')
        self.attachcollnodes()

        taskMgr.add(self.read_data, 'read')
        for i in range(5):
            taskMgr.add(self.move_player, 'move%d' % i, extraArgs = [i], appendTask=True)
        taskMgr.add(self.log_data, 'log data')
        taskMgr.add(self.update_state, 'update_state', sort=1)

        self.accept('space', self.space_on)
        self.accept('escape', self.clean_up)
        self.space = False
        self.statenum = list()

        self.max_time = 20
        self.med_data = None

        self.grip_dir = os.path.join(self.DATA_DIR,'exp_1',self.sub_id,self.sess_id,self.wrist,self.hand,"B"+self.block)
        if not os.path.exists(self.grip_dir):
           print('Making new folders: ' + self.grip_dir)
           os.makedirs(self.grip_dir)

        self.dev = MpDevice(RightHand(calibration_files=['calibs/cal_mat_15.mat',  # thumb
                                               'calibs/cal_mat_31.mat',
                                               'calibs/cal_mat_8.mat',
                                               'calibs/cal_mat_21.mat',
                                               'calibs/cal_mat_13.mat'], clock=mono_clock.get_time))

        self.init_ser()

    ############
    #SET UP HUD#
    ############
    def setup_text(self):
        #OnscreenImage(parent=self.cam2dp, image='models/background.jpg')
        self.bgtext = OnscreenText(text='Not recording.', pos=(-0.8, 0.8),
                                 scale=0.08, fg=(0, 0, 0, 1),
                                 bg=(1, 1, 1, 1), frame=(0.2, 0.2, 0.8, 1),
                                 align=TextNode.ACenter)
        self.bgtext.reparentTo(self.aspect2d)

        self.dirtext = OnscreenText( pos=(-0.6, 0.65),
                                 scale=0.08, fg=(0, 0, 0, 1),
                                 bg=(1, 1, 1, 1), frame=(0.2, 0.2, 0.8, 1),
                                 align=TextNode.ACenter)
        self.dirtext.reparentTo(self.aspect2d)

    ##########################
    #SET UP SCENE AND PLAYERS#
    ##########################
    def setup_lights(self):
        pl = PointLight('pl')
        pl.setColor((1, 1, 1, 1))
        plNP = self.render.attachNewNode(pl)
        plNP.setPos(-10, -10, 10)
        self.render.setLight(plNP)
        pos = [[[0, 0, 50], [0, 0, -10]],
               [[0, -50, 0], [0, 10, 0]],
               [[-50, 0, 0], [10, 0, 0]]]
        for i in pos:
            dl = Spotlight('dl')
            dl.setColor((1, 1, 1, 1))
            dlNP = self.render.attachNewNode(dl)
            dlNP.setPos(*i[0])
            dlNP.lookAt(*i[1])
            dlNP.node().setShadowCaster(False)
            self.render.setLight(dlNP)

    def setup_camera(self):
        self.cam.setPos(0, -4, 12)
        self.cam.lookAt(0, 2, 0)
        self.camLens.setFov(90)

    def load_models(self):
        self.back_model = self.loader.loadModel('models/back')
        self.back_model.setScale(10, 10, 10)
        if self.hand == "Left":
            self.back_model.setH(90)
        self.back_model.reparentTo(self.render)

        self.player_offsets = [[self.p_x[0]-5, self.p_y[0]+3, 0], [self.p_x[1]-2.5, self.p_y[1]+4.5, 0], [self.p_x[2], self.p_y[2]+5, 0],
                                [self.p_x[3]+2.5, self.p_y[3]+4.5, 0], [self.p_x[4]+5, self.p_y[4]+3, 0]]
        self.p_col =[[0,0,250],[50,0,200],[125,0,125],[200,0,50],[250,0,0]]
        if self.hand == 'Left':
            self.p_col = self.p_col[::-1]

        self.players = list()
        self.contacts = list()        
        for counter, value in enumerate(self.player_offsets):
            self.players.append(self.loader.loadModel('models/target'))
            self.contacts.append(False)

            self.players[counter].setPos(*value)
            self.players[counter].setScale(0.2, 0.2, 0.2)
            self.players[counter].setColorScale(
                self.p_col[counter][0]/255, self.p_col[counter][1]/255, self.p_col[counter][2]/255, 1)
            self.players[counter].reparentTo(self.render)
            self.players[counter].show()

        self.target_select()

    def load_audio(self):
        self.pop = self.loader.loadSfx('audio/Blop.wav')
        self.buzz = self.loader.loadSfx('audio/Buzzer.wav')


    ############################
    #SET UP COLLISION MECHANICS#
    ############################
    def attachcollnodes(self):
        for i in range(5):
            self.fromObject = self.players[i].attachNewNode(CollisionNode('colfromNode'+str(i)))
            self.fromObject.node().addSolid(CollisionSphere(0,0,0,1))
            self.cTrav.addCollider(self.fromObject, self.chandler)

        for i in range(5):
            self.accept('colfromNode%d-into-colintoNode' % i, self.collide1,[i])
            self.accept('colfromNode%d-again-colintoNode' % i, self.collide2,[i])
            self.accept('colfromNode%d-outof-colintoNode' % i, self.collide3,[i])

    def collide1(self,f,collEntry):
        if f in self.highlighted_indices:
            self.players[f].setColorScale(0,1,0,0)
            self.tar.setColorScale(0.2,0.2,0.2,1)
            self.tar.setAlphaScale(0.7)
            self.contacts[f] = True
            taskMgr.doMethodLater(self.delay,self.too_long,'too_long%d' % f,extraArgs = [f])
            self.sendsig(1,f,100)
                
    def collide2(self,f,collEntry):
        if f in self.highlighted_indices:
            dist = np.sqrt(self.distances[f][1]^2+self.distances[f][2]^2+self.distances[f][3]^2)
            self.sendsig(2,f,int(10/dist)+100)
        for i in self.highlighted_indices:
            if self.contacts[i] == False:
                return
        taskMgr.remove('too_long%d' % f)

    def collide3(self,f,collEntry):
        taskMgr.remove('too_long%d' % f)
        self.reset_fing(f)
        self.tar.setColorScale(0.1,0.1,0.1,1)
        self.tar.setAlphaScale(0.7)

    def too_long(self,f):
        self.reset_fing(f)
        self.tar.setColorScale(0.5,0.2,0.2,1)
        self.tar.setAlphaScale(0.7)

    def reset_fing(self,f):
        self.players[f].setColorScale(
                self.p_col[f][0]/255, self.p_col[f][1]/255, self.p_col[f][2]/255, 1)
        self.contacts[f] = False
        self.sendsig(3,f,0)

    ###############
    #TARGET THINGS#
    ###############
    def show_target(self):
        self.target_select()
        self.intoObject = self.tar.attachNewNode(CollisionNode('colintoNode'))

        if self.table[self.trial_counter,7] == "sphere":
            self.intoObject.node().addSolid(CollisionSphere(0,0,0,1))
        elif self.table[self.trial_counter,7] == "cylinder":
            self.intoObject.node().addSolid(CollisionTube(0,0,-2,0,0,2,1))
        else:
            raise NameError("No such collision type")

        self.tar.show()

        #hide players not related to target
        for i in range(5):
            if i not in self.highlighted_indices:
                self.players[i].hide()
        
    def target_select(self):
        self.tgtscx=float(self.table[self.trial_counter,14])
        self.tgtscy=float(self.table[self.trial_counter,15])
        self.tgtscz=float(self.table[self.trial_counter,16])
        tgttsx=float(self.table[self.trial_counter,11])
        tgttsy=float(self.table[self.trial_counter,12])
        tgttsz=float(self.table[self.trial_counter,13])
        tgtrx=float(self.table[self.trial_counter,17])
        tgtry=float(self.table[self.trial_counter,18])
        tgtrz=float(self.table[self.trial_counter,19])
        if self.hand == 'Left':
            tgttsx *= -1
            tgtrx *= -1

        self.static_task = (str(self.table[self.trial_counter,5]) == "True")
        self.target_model = str(self.table[self.trial_counter,6])
        self.highlighted_indices=[int(s)-1 for s in self.table[self.trial_counter,4].split(' ')]
        if self.hand == 'Left':
            self.highlighted_indices=[4-i for i in self.highlighted_indices]

        #NOTE:this position finding is present in all three tasks
        #     it is somewhat hacky and can be improved upon
        self.tgtposx = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][[-2,-1],0]) + tgttsx
        self.tgtposy = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][[0,1],1]) + tgttsy
        if len(self.highlighted_indices) > 3: #for sphere, return to just average of all fingers
            self.tgtposx = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][:,0]) + tgttsx
            #self.tgtposy = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][:,1])
        self.tgtposz = np.mean(np.asarray(self.player_offsets)[self.highlighted_indices][:,2]) + tgttsz

        self.tar = self.loader.loadModel(self.target_model)
        self.tar.setScale(self.tgtscx,self.tgtscy,self.tgtscz)
        self.tar.setPos(self.tgtposx,self.tgtposy,self.tgtposz)
        self.tar.setHpr(tgtrx,tgtry,tgtrz)
        self.tar.setColorScale(0.1, 0.1, 0.1, 1)
        self.tar.setAlphaScale(0.7)
        self.tar.setTransparency(TransparencyAttrib.MAlpha) 
        self.tar.reparentTo(self.render)
        self.tar.hide()

        self.delay=float(self.table[self.trial_counter,8])
        self.loc_angle=math.radians(float(self.table[self.trial_counter,9]))
        self.invf=float(self.table[self.trial_counter,10])
        
        self.distances = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]

    ##############
    #MOVE FINGERS#
    ##############
    def read_data(self,task):
        error, data = self.dev.read()
        if data is not None:
            data *= 0.001
            self.ts = data.time
            data = np.dot(data,self.rotmat)
            self.data = data
            if self.med_data is None:
                self.med_data = np.median(data, axis=0)

            if self.space:
                self.statenum.extend(([self.checkstate()])*len(data.time))
                
        return task.cont
        
    def move_player(self,p,task):
        if self.data is not None :
            k = p*3
            new_x = 10*np.mean(self.data[-1,k]) + self.player_offsets[p][0] - 10*self.med_data[k]
            new_y = 10*np.mean(self.data[-1,k + 1]) + self.player_offsets[p][1] - 10*self.med_data[k + 1]
            new_z = 10*np.mean(self.data[-1,k + 2]) + self.player_offsets[p][2] - 10*self.med_data[k + 2]

            #make sure digits do not cross each other
            if ((p in range(1,3) and p+1 in self.highlighted_indices and new_x > self.players[p+1].getX())
                or (p in range(2,4) and p-1 in self.highlighted_indices and new_x < self.players[p-1].getX())):
                    new_x = self.players[p].getX()
            
            #make sure digits do not cross into target
            if self.space == True and p in self.highlighted_indices:
                self.distances[p][0] = new_x - self.tar.getX()
                self.distances[p][1] = new_y - self.tar.getY()
                self.distances[p][2] = new_z - self.tar.getZ()
                self.check_pos(p)
                
            self.players[p].setPos(new_x, new_y, new_z)
            
        return task.cont
    
    def check_pos(self,p):
        if (abs(self.distances[p][0]) < self.invf*self.tgtscx
            and abs(self.distances[p][1]) < self.invf*self.tgtscy
            and abs(self.distances[p][2]) < self.invf*self.tgtscz):
                self.too_long(p)

    ##################
    #CHECK COMPLETION#
    ##################
    def close_to_target(self):
        for i in self.highlighted_indices:
            if self.contacts[i] == False:
                return False

        if not self.check_angle():
            self.tar.setColorScale(0.1,0.1,0.1,1)
            self.tar.setAlphaScale(0.7)
            return False

        self.tar.setColorScale(0,1,1,1)
        return True

    def check_hold(self):
        if not self.close_to_target():
            self.hold_timer.reset(0.5)
            return False
        return self.hold_timer.elapsed() < 0

    def check_angle(self):
        #hardcoded condition may be able to be assimilated into old angle method somehow
        if self.table[self.trial_counter,1] == 'pts':
            vec = np.subtract(self.players[self.highlighted_indices[1]].getPos(), self.players[self.highlighted_indices[0]].getPos())
            xvec = [1,0,0]
            if self.hand == 'Left':
                xvec = [-1,0,0]
            print(math.degrees(math.acos(np.dot(vec,xvec)/(np.linalg.norm(vec)*np.linalg.norm(xvec)))))
            if math.acos(np.dot(vec,xvec)/(np.linalg.norm(vec)*np.linalg.norm(xvec))) < self.loc_angle:
                return True
            else: return False
        thumbindx = 0
        if self.hand == "Left": thumbindx = 4
        for i in self.highlighted_indices:
            if i == thumbindx:
                continue
            th = self.distances[thumbindx]
            fg = self.distances[i]
            if math.acos(np.dot(th,fg)/(np.linalg.norm(th)*np.linalg.norm(fg))) > self.loc_angle:
                return True
        return False

    def adjust_targets(self):
        #no adjustment if more than 2 fingers or position is prone
        if len(self.highlighted_indices) > 2 or self.wrist == 'pron':
            return

        xadj,yadj,zadj = np.mean(np.asarray(self.distances)[self.highlighted_indices],0)
        #yadj = np.mean(np.asarray(self.distances)[self.highlighted_indices][1])
        #zadj = np.mean(np.asarray(self.distances)[self.highlighted_indices][2])

        #do adjustment on all tasks with same name
        if self.hand == 'Left':
            xadj = -xadj
        for i in range(self.trial_counter+1,self.table.shape[0]):
            if self.table[i,1] == self.table[self.trial_counter,1]:
                self.table[i,11] = float(self.table[i,11]) + xadj
                self.table[i,12] = float(self.table[i,12]) + yadj
                self.table[i,13] = float(self.table[i,13]) + zadj


    #########
    #LOGGING#
    #########
    def play_success(self):
        if int(self.block) == 0:
            self.adjust_targets()
        self.pop.play()
        self.tar.hide()
        self.highlighted_indices = [0,1,2,3,4]

    def log_text(self):
        self.bgtext.setText('Now logging...')

    def log_data(self, task):
        if (self.trial_counter + 1) <= self.table.shape[0]:
            if self.space:
                self.log_file_name = os.path.join(self.grip_dir,
                                          self.sub_id+"_"+self.sess_id+"_"+self.hand+"_"+
                                              str(self.table[self.trial_counter,1])+"_"+str(self.table[self.trial_counter,0])+".csv" )
                self.movvars = np.column_stack((self.ts, self.statenum, self.data))
                self.statenum = []
                if self.mode=='task':
                    with open(self.log_file_name, 'ab') as f:
                        np.savetxt(f, self.movvars, fmt='%10.5f', delimiter=',')
            return task.cont
        else:
            pass

    def stoplog_text(self):
        self.dirtext.clearText()
        self.bgtext.setText('Done logging!')
        for i in range(5):
            self.players[i].show()

    #######
    #RESET#
    #######
    def delete_file(self):
        if (self.trial_counter + 1) <= self.table.shape[0]:
            self.log_file_name = os.path.join(self.grip_dir,
                                      self.sub_id+"_"+self.sess_id+"_"+self.hand+"_"+
                                          str(self.table[self.trial_counter,1])+"_"+str(self.table[self.trial_counter,0])+".csv" )
            try:
                os.remove(self.log_file_name)
            except OSError:
                pass
        else:
            pass

    def reset_baseline(self):
       self.med_data = None
                   
    def reset_keyboard_bool(self):
        self.space = False

    def hide_target(self):
        self.tar.hide()
        self.intoObject.removeNode()
        self.imageObject.destroy()

    def update_trial_command(self):
        self.dirtext.setText(str(self.table[self.trial_counter,2]))
        if self.hand == 'Left':
            xfac = -0.25
        else:
            xfac = 0.25
        self.imageObject = OnscreenImage(image = str(self.table[self.trial_counter,3]),scale=(xfac,0.25,0.25),pos=(-1.2, 0, 0.3))

    def increment_trial_counter(self):
        self.trial_counter += 1
        self.update_trial_command()

    ########
    #TIMERS#
    ########
    def start_trial_countdown(self):
        self.countdown_timer.reset(self.max_time)

    def start_hold_countdown(self):
        self.hold_timer.reset(0.5)

    def start_post_countdown(self):
        self.countdown_timer.reset(2)

    def time_elapsed(self):
        return self.countdown_timer.elapsed() < 0

    #########
    #MACHINE#
    #########
    def update_state(self, task):
        self.step()
        return task.cont

    def wait_for_space(self):
        return self.space

    def space_on(self):
        self.space = True

    #####
    #END#
    #####
    def trial_counter_exceeded(self):
        return (self.trial_counter+1) > self.table.shape[0]-1

    def clean_up(self):
        #write last known positions to 'final_targets' file
        f = open('src/pinch_task/trialtable_flex.csv')
        header = f.readline().rstrip()
        np.savetxt(self.grip_dir + '/final_targets.csv',self.table,fmt='%s',header = header, delimiter=',')
        f.close()
        sys.exit()

    ########
    #SERIAL#
    ########
    def init_ser(self):
        #CONNECT TO HAPTIC DEVICE
        ports = list_ports.comports()
        mydev = next((p.device for p in ports if p.pid == 1155))
        self.ser = serial.Serial(mydev,9600,timeout=.1)

    def sendsig(self,signal,finger,intensity):
        #would read from table, but table not available yet
        if intensity > 255:
            intensity = 255
        dur_int = [signal,finger,intensity]
        data = struct.pack('3B',*dur_int)
        self.ser.write(data)
Exemple #5
0
class SpaceFlight(ShowBase):
  def __init__(self):
    ShowBase.__init__(self)
    self.text = OnscreenText \
    (
      parent = base.a2dBottomCenter,
      align=TextNode.ARight,
      fg=(1, 1, 1, 1),
      pos=(0.2, 1.),
      scale=0.1,
      shadow=(0, 0, 0, 0.5)
    )
    self.setBackgroundColor(0, 0, 0)
    self.disableMouse()
    self.fog = Fog('distanceFog')
    self.fog.setColor(0, 0, 0)
    self.fog.setExpDensity(.002)
    #
    self.queue = CollisionHandlerQueue()
    self.trav = CollisionTraverser('traverser')
    base.cTrav = self.trav
    self.loadSky()
    self.reloadGame()

    self.keyMap = {'left' : 0, 'right' : 0, 'up' : 0, 'down' : 0}
    self.gamePause = False
    #
    self.accept('escape', sys.exit)
    self.accept('p', self.pause)
    self.accept('r', self.reloadGame)
    self.accept('arrow_left', self.setKey, ['left', True])
    self.accept('arrow_right', self.setKey, ['right', True])
    self.accept('arrow_up', self.setKey, ['up', True])
    self.accept('arrow_down', self.setKey, ['down', True])
    self.accept('arrow_left-up', self.setKey, ['left', False])
    self.accept('arrow_right-up', self.setKey, ['right', False])
    self.accept('arrow_up-up', self.setKey, ['up', False])
    self.accept('arrow_down-up', self.setKey, ['down', False])
    #
    taskMgr.add(self.moveShip, 'moveShip')
    taskMgr.add(self.moveAsteroids, 'moveAsteroids')
    taskMgr.add(self.handleCollisions, 'handleCollisions')
    #
    if DEBUG:
      self.trav.showCollisions(render)
      render.find('**/ship_collision').show()
      for asteroid in render.findAllMatches('**/asteroid_collision*'):
        asteroid.show()

  def loadSky(self):
    self.sky = loader.loadModel('models/solar_sky_sphere.egg.pz')
    self.sky_tex = loader.loadTexture('models/stars_1k_tex.jpg')
    self.sky.setTexture(self.sky_tex, 1)
    self.sky.reparentTo(render)
    self.sky.setScale(500)

  def loadShip(self):
    self.ship = loader.loadModel('models/alice-scifi--fighter/fighter.egg')
    self.ship.reparentTo(render)
    self.ship.setPos(START_X, START_Y, START_Z)
    self.ship.setScale(0.25)
    # add some physics
    ship_col = self.ship.attachNewNode(CollisionNode('ship_collision'))
    col_sphere = CollisionSphere(START_X, START_Y, 0, SHIP_SPHERE_RADIUS)
    ship_col.node().addSolid(col_sphere)
    self.trav.addCollider(ship_col, self.queue)

  def spawnAsteroid(self):
    asteroid = loader.loadModel(choice(ASTEROID_SHAPES))
    asteroid_tex = loader.loadTexture('models/rock03.jpg')
    asteroid.setTexture(asteroid_tex, 1)
    asteroid.reparentTo(render)
    asteroid.setFog(self.fog)
    self.asteroids.append(asteroid)
    self.asteroids_rotation.append(randint(ASTEROID_ROTATE_MIN, ASTEROID_ROTATE_MAX))
    #
    num = len(self.asteroids) - 1
    asteroid_col = asteroid.attachNewNode(CollisionNode('asteroid_collision_%d' % num))
    col_sphere = CollisionSphere(0, 0, 0, ASTEROID_SPHERE_RADIUS)
    asteroid_col.node().addSolid(col_sphere)
    #
    asteroid.setX(randint(MIN_X, MAX_X))
    asteroid.setY(randint(ASTEROID_SPAWN_MIN_Y, ASTEROID_SPAWN_MAX_Y))
    asteroid.setZ(randint(MIN_Z, MAX_Z))

  def setKey(self, key, value):
    self.keyMap[key] = value
    if key in ['left', 'right'] and value == False:
      self.ship.setH(0)
    if key in ['up', 'down'] and value == False:
      self.ship.setP(0)

  def updateCamera(self):
    x, y, z = self.ship.getPos()
    self.camera.setPos(x, y - 40, z + 25)
    self.camera.lookAt(x, y, z + 10)

  def moveAsteroids(self, task):
    dt = globalClock.getDt()
    if not self.gamePause:
      for num, asteroid in enumerate(self.asteroids):
        asteroid.setY(asteroid.getY() - ASTEROID_SPEED * dt)
        rotation = self.asteroids_rotation[num]
        asteroid.setH(asteroid.getH() - rotation * ASTEROID_SPEED * dt)
        if asteroid.getY() < self.camera.getY() + 10:
          asteroid.setX(randint(MIN_X, MAX_X))
          asteroid.setY(randint(ASTEROID_SPAWN_MIN_Y, ASTEROID_SPAWN_MAX_Y))
          asteroid.setZ(randint(MIN_Z, MAX_Z))
    return task.cont

  def rollbackOnBoard(self, minPos, maxPos, getFunc, setFunc):
    if getFunc() < minPos:
      setFunc(minPos)
    if getFunc() > maxPos:
      setFunc(maxPos)

  def applyBound(self):
    self.rollbackOnBoard(MIN_X, MAX_X, self.ship.getX, self.ship.setX)
    self.rollbackOnBoard(MIN_Z, MAX_Z, self.ship.getZ, self.ship.setZ)

  def moveShip(self, task):
    dt = globalClock.getDt()
    if not self.gamePause:
      if self.keyMap['left']:
        self.ship.setX(self.ship.getX() - SHIP_SPEED * dt)
        self.ship.setH(TURN_SPEED)
      elif self.keyMap['right']:
        self.ship.setX(self.ship.getX() + SHIP_SPEED * dt)
        self.ship.setH(-TURN_SPEED)
      elif self.keyMap['up']:
        self.ship.setZ(self.ship.getZ() + SHIP_SPEED * dt)
        self.ship.setP(TURN_SPEED)
      elif self.keyMap['down']:
        self.ship.setZ(self.ship.getZ() - 5 * SHIP_SPEED * dt)
        self.ship.setP(-TURN_SPEED)

      self.sky.setP(self.sky.getP() - dt * 10)
      self.applyBound()
      self.updateCamera()

    return task.cont

  def handleCollisions(self, task):
    if not self.gamePause:
      for entry in self.queue.getEntries():
        node = entry.getFromNodePath()
        if node.getName() == 'ship_collision':
          self.gamePause = True
          self.text.setText('You lose :(')
    return task.cont

  def pause(self):
    self.gamePause = not self.gamePause

  def reloadGame(self):
    self.gamePause = False
    self.text.clearText()

    if hasattr(self, 'asteroids'):
      for asteroid in self.asteroids:
        asteroid.removeNode()

    self.asteroids = []
    self.asteroids_rotation = []

    if hasattr(self, 'ship'):
      self.ship.removeNode()

    self.loadShip()

    for _ in xrange(ASTEROID_MAX_CNT):
      self.spawnAsteroid()
Exemple #6
0
class WorldBase(DirectObject):
    """
    Basic 3D world with logging and 2D display
    """
    def __init__(self, controller, mem_logger, display_categories):
        
        self.display_categories = display_categories
        
        self.labels = OnscreenText( "", style = 1, fg = ( 1, 1, 1, 1 ), pos = ( -1.0, 0.9 ), scale = .05 )
        self.txt = OnscreenText( "", style = 1, fg = ( 1, 1, 1, 1 ), pos = ( -0.5, 0.9 ), scale = .05 )

        taskMgr.add( self.loop, "loop" )

        self.enable_screen_updates = True
        self.log = KeplerLogger(mem_logger)        
        self.logging = False
        self.accept("p", self.key_p)
        self.accept( "escape", self.quit )
        self.accept("arrow_up", self.key_up)
        self.accept("arrow_down", self.key_down)
        self.accept("arrow_left", self.key_left)
        self.accept("arrow_right", self.key_right)
        self.accept("l", self.key_l)
        self.accept("s", self.key_s)
        self.accept("a", self.key_a)
        self.accept("f", self.key_f)
        self.accept("g", self.key_g)
        self.accept("d", self.key_d)
        self.accept("o", self.log_start_stop)
        self.accept("q", self.key_q)
        self.accept("w", self.key_w)
        self.accept("z", self.key_z)
        self.accept("x", self.key_x)
        self.accept("c", self.key_c)
        self.accept("v", self.key_v)
        self.accept("b", self.key_b)
        self.accept("n", self.key_n)
            
        self.controller = controller
        base.setFrameRateMeter(True)

        
        #self.txt = OnscreenText( "Nothing", style = 1, fg = ( 1, 1, 1, 1 ), shadow = ( .9, .9, .9, .5 ), pos = ( -1.0, 0.9 ), scale = .07 )
        # task to be called every frame
        
        self.step = 0
        self.last_time = time.time()
        
        self.text_refresh_band = 10
        self.text_refresh_count = self.text_refresh_band
        self._set_title("Keplermatic")
        
        self.init_gui()
        

    def init_gui(self):
        
        x = -0.9
        y = 0.7
        inc_y = -0.1
        cb = CallBack("Logging", self.display_categories, self)
        self.add_gui_checkbox("Logging", False, x, y, cb.callback)
        y += inc_y
        cb = CallBack("Update", self.display_categories,self)
        self.add_gui_checkbox("Update", True, x, y, cb.callback)
        y += inc_y  
        y += inc_y 
        keys = self.display_categories.keys()
        keys.sort()
  
        for k in keys:
            if k in ["axis1","axis2"]:
               cb = CallBack(k, self.display_categories,self)
               category_name = k
               enabled = self.display_categories[category_name]
               self.add_gui_checkbox(category_name, enabled, x, y, cb.callback)
               y += inc_y
        y += inc_y  
        y += inc_y 
                
        for k in keys:
            if k not in ["axis1","axis2"]:
                  cb = CallBack(k, self.display_categories,self)
                  category_name = k
                  enabled = self.display_categories[category_name]
                  self.add_gui_checkbox(category_name, enabled, x, y, cb.callback)
                  y += inc_y
        
  
    def _set_title(self, title):
      from pandac.PandaModules import ConfigVariableString
      mygameserver = ConfigVariableString("window-title","Panda")
      mygameserver.setValue(title)

    def loop(self, task):
        #self.last_time = time.time()
        delta_time = task.time - self.last_time
        # avoid division by 0
        if delta_time < 0.0001:
            delta_time = 0.0001
        self.last_time = task.time
        self.step += 1
        txt = ""
        self._refresh_text(txt)
        self.controller.loop(self.step, task.time, delta_time)
        
        if self.logging:
            data = self.controller.get_display_data()
            self.log.snapshot(get_data_logger(), self.step, task.time, data, ('self'))        
        return Task.cont

    def _refresh_text(self, txt):
        if not self.enable_screen_updates:
            return 
        self.text_refresh_count -=1
        if self.text_refresh_count <0:
            self.text_refresh_count = self.text_refresh_band
            data = self.controller.get_display_data()
            st = str(self.last_time)
            data['Time'] = (st, "sim", 0)
            data['step'] = (self.step, "sim", 0)
            
            self.txt.clearText()
            self.txt.setAlign(TextNode.ALeft)

            keys = data.keys()
            keys.sort()
            txt += "logging: %s\n" % self.logging      
            #print keys
            show_axis1 = self.display_categories['axis1']
            show_axis2 = self.display_categories['axis2']
            
            for name in keys:
                if name == 'self': continue
                display_data = data[name]                
                #print display_data
                value, category, axis = display_data   
                if category not in self.display_categories.keys():
                   category = "other"
                show_category = self.display_categories[category]
                show_it = show_category
                if not show_it:
                   #print "  %s hidden (category %s hidden)" % (name, category)
                   pass
                if axis == 1:
                    if show_axis1 == False:
                       #print "  %s hidden (axis 1)" % name
                       show_it = False
                if axis == 2:
                    if show_axis2 == False:
                       #print "  %s hidden (axis 2)" % name
                       show_it = False
                
                
                if show_it:   
                   #print "**** name %s, cat %s [%s], axis %s, value %s show1 %s, show2 %s" %(name, category, show_it, axis, value, show_axis1, show_axis2)              
                   value_str = "%s" % value
                   if type(value) == type(0.): # float
                      value_str = "%.10f" % value
                   txt += "%s: %s\n" % (name, value_str)
                else:   
                   pass
                   #print "XXXX  name %s, cat %s [%s], axis %s, value %s show1 %s, show2 %s" %(name, category, show_it, axis, value, show_axis1, show_axis2)              
                
            self.txt.appendText(txt)
 
    
    def add_gui_checkbox(self, text, value, x, y, callback):
       b = DirectCheckButton(text = text ,scale=.05, pos=(x,0,y), command=callback)
       b["indicatorValue"] = value
       return b
       

    def quit(self):
        self.controller.stop()
        if len(self.log.data) >0:
            self.log.dump(get_data_logger())
        sys.exit()
        
    def log_start_stop(self):
        
        if self.logging: # stopping
            self.controller.stop()
            self.log.dump(get_data_logger())
            self.log.data = []
        
        self.logging = not self.logging
        print "Logging %s" % self.logging 

    def key_p(self):
      self.enable_screen_updates = not self.enable_screen_updates
      print "enable_screen_updates=", self.enable_screen_updates
        
    def key_right(self):  
        self.controller.key_right()
    def key_left(self): 
        self.controller.key_left()
    def key_l(self): 
        self.controller.key_l()
    def key_s(self): 
        self.controller.key_s()
    def key_a(self): 
        self.controller.key_a()
    def key_f(self): 
        self.controller.key_f()
    def key_g(self):    
        self.controller.key_g()
    def key_d(self): 
        self.controller.key_d()
    def key_up(self):
        self.controller.key_up()
    def key_down(self):    
        self.controller.key_down()
    def key_q(self):    
        self.controller.key_q()
    def key_w(self):    
        self.controller.key_w()   
    def key_z(self):    
        self.controller.key_z()    
    def key_x(self):    
        self.controller.key_x()    
    def key_c(self):    
        self.controller.key_c()    
    def key_v(self):    
        self.controller.key_v()    
    def key_b(self):    
        self.controller.key_b()   
    def key_n(self):    
        self.controller.key_n()    
Exemple #7
0
class HLevel():
    def __init__(self, showbase, physicsDebug=True):
        """


        :type self.Base: direct.showbase.ShowBase.ShowBase
        :type showbase: direct.showbase.ShowBase.ShowBase
        :type physicsDebug: bool
        """
        print "Creating level"
        self.Base = showbase
        self.debugDrawing = physicsDebug
        self.pause = False
        self.bulletSubstep = 0.008
        self.activeLog = False

    def loadAssets(self):
        print "Loading Assets"

    def renderAssets(self):
        print "Rendering assets"

    def setCamera(self):
        print "Setting camera"

    def setLights(self):
        print "Setting lights"

    def destroy(self):
        print "Destroying level"

    def loadEgg(self, egg):
        """
        :type egg: str
        :rtype : panda3d.core.NodePath
        """
        return self.Base.loader.loadModel(egg)

    def renderModel(self, model):
        """

        :type model: str
        """
        model.reparentTo(self.Base.render)

    def renderEgg(self, egg):
        """

        :type egg: str
        :return pada3d.core.NodePath
        """
        m = self.loadEgg(egg)
        self.renderModel(m)
        return m

    def setPhysics(self):
        print "Setting physics"
        self.world = BulletWorld()
        self.world.setGravity(Vec3(0, 0, -10))
        self.Base.taskMgr.add(self.physicsUpdate, "physicsUpdate", priority=0)
        if self.debugDrawing:
            print "Debug drawing"
            self.debug = BulletDebugNode("debug")
            self.debug.showWireframe(True)
            self.debug.showBoundingBoxes(False)
            self.debug.showNormals(True)
            self.debugNP = self.Base.render.attachNewNode(self.debug)
            self.debugNP.show()
            self.world.setDebugNode(self.debug)

    def physicsUpdate(self, task):
        if not self.pause:
            dt = globalClock.getDt()
            self.world.doPhysics(dt, 2, self.bulletSubstep)  # #0.009-0.008
            # print "Physics step"
        return task.cont

    def cameraShake(self, amplitud=0.01, frecuencia=5):
        """

        :type amplitud: float
        :type frecuencia: float
        """
        self.Base.camera.setZ(
            self.Base.camera,
            sin(globalClock.getRealTime() * frecuencia) * amplitud)

    def eggToStatic(self, egg, parent, margin=0.01, name="static"):
        """

        :type egg: str
        :type parent: panda3d.core.NodePath
        :type margin: float
        :type name: str
        :return: tuple(pada3d.bullet.BulletRigidBodyNode,panda3d.core.NodePath)
        """
        m = self.Base.loader.loadModel(egg)
        sTuple = modelToShape(m)
        sTuple[0].setMargin(margin)
        static = BulletRigidBodyNode(name)  # H
        static.addShape(sTuple[0], sTuple[1])
        np = parent.attachNewNode(egg)
        self.world.attachRigidBody(static)
        return static, np

    def togglePause(self):
        """

        """
        if self.pause:
            self.pause = False
        else:
            self.pause = True
        print "Toggle pause", self.pause

    def activateLog(self):
        self.activeLog = True
        self.logText = OnscreenText("NO LOG",
                                    scale=0.07,
                                    fg=(1, 0, 0, 0.8),
                                    bg=(0, 0, 1, 0.2),
                                    frame=(0, 1, 0, 0.2),
                                    pos=(-1.05, .9),
                                    mayChange=True,
                                    align=0)
        self.__logTextLenght = 0
        self.__logAbsoluteLenght = 0
        self.__logText = ""
        self.logText.reparentTo(self.Base.aspect2d)

    def log(self, *args):
        if self.activeLog is False:
            return None
        s = str(self.__logAbsoluteLenght) + ":"
        for a in args:
            s += str(a) + " "
        if self.__logTextLenght < 10:
            self.logText.appendText("\n" + s)
        else:
            self.__logTextLenght = 0
            self.logText.clearText()
            self.logText.setText(s)
        self.__logTextLenght += 1
        self.__logAbsoluteLenght += 1
        self.__logText.join(s)

    def loadFont(self, string):
        return self.Base.loader.loadFont(string)

    def drawLine(self,
                 fromP,
                 toP,
                 thickness=2,
                 color=(1, 0, 0, 1),
                 autoClear=True):
        if autoClear:
            try:
                self.debugLineNP.removeNode()
            except:
                pass
        self.debugLine = LineSegs("DebugLine")
        self.debugLine.reset()
        self.debugLine.setThickness(thickness)
        self.debugLine.setColor(color)
        self.debugLine.moveTo(fromP)
        self.debugLine.drawTo(toP)
        self.debugLineNode = self.debugLine.create()
        self.debugLineNP = NodePath(self.debugLineNode)
        self.debugLineNP.reparentTo(self.Base.render)
        return self.debugLineNP
class MyApp(ShowBase):
    def __init__(self, fStartDirect=True):
        #ConfigVariableBool("fullscreen",0).setValue(1)
        ShowBase.__init__(self) 
        self.loadInitialCamPos()#initialize camera positioning 
        self.assocs=dict()
        self.loadBooleans()
        self.loadLists()
        self.shuffleCards()
        self.loadValues()
        #creates a random sequence of cards we will memorize 
        self.setInputs()
        #set up keyboard and mouse inputs
        self.setUpCollision()
        #sets up collision detection
        self.setUpScene()
        #sets up geometry in the 3D Enviroments
        self.loadSFX()
        self.light=self.setUpPointLight()
        taskMgr.add(self.setUpChest, 'setUpChest')
        #sets up rotating graphic of chest
        
    def loadValues(self):
        self.counter,self.lightCounter,self.fade,self.numFireflies=0,0,0,15
        self.numCards=13
        self.numCardsCopy=copy.copy(self.numCards)
        self.shuffledCardFacesCopy=copy.copy(self.shuffledCardFaces)
        
    def loadLists(self):
        self.cardsPos,self.imageList,self.visitedItems=[],[],[]
        self.buttonList,self.lastCoord,self.fireFlyList=[],[],[]
        self.previousCard,self.nextCard=[],[]
        
    def loadBooleans(self):
        self.toggleChest=False
        self.fadeToggle=False
        self.togglePreviousCard=False
        self.toggleNextCard=False
        self.toggleBar=True
        self.titleText=None
        self.userLocation=None
        
    def loadInitialCamPos(self):
        #we disable our mouse because we be using a different method
        #that will set up our mouse controls 
        base.disableMouse()
        self.camPosx,self.camPosz=15,28
        self.camera.setH(90)
        self.camera.setP(-25)
        self.heading=0
        base.camera.setPos(self.camPosx,0,self.camPosz)
        
    def loadSFX(self):
        self.soundSteps = loader.loadMusic("footsteps.wav")
        #circus in the Sky is a piece written by Uzman Riaz (credit)
        #self.circusInTheSky=loader.loadMusic("circusInTheSky.wav")
        
    def setUpCollision(self):
        #pusher and queue are handlers from when a collision occurs
        self.cTrav = CollisionTraverser()
        self.queue = CollisionHandlerQueue()
        self.pusher = CollisionHandlerPusher()
        #set up scene geometrys
        self.setUpCollisionRay()
        self.setUpCollisionNode()
        
    def setUpPointLight(self):
        taskMgr.doMethodLater(.1, self.createTitleText, 'textFadeInOut')
        #we create multiple point lights to make it appear as if there are
        #'firefly' entities within the chest 
        for n in xrange(self.numFireflies):
            self.generateLights("%fFirefly"%n)
        #sets up a soft ambient light so that our 3D enviroment appears
        #more realistic 
        self.alight = AmbientLight('ambientLight')
        self.alight.setColor(VBase4(.6, .6, .6, 1))
        alnp = render.attachNewNode(self.alight)
        render.setLight(alnp)
        self.treasureChest.setLight(alnp)

    def createTitleText(self,task):
        #task allows up to fade text in and out 
        self.updateFade()
        if self.titleText!=None:
            self.titleText.remove()
        self.titleText=OnscreenText(text="[Click on the chest to begin]",
                                    style=Plain,pos=(0,0),fg=(1,1,1,self.fade))
        if self.toggleChest==True:
            self.titleText.remove()
            return task.done
        return task.again
    
    def generateLights(self,name):
        #generate pointlights and reparenting them to the chest 
        name=PointLight("Light")
        name=render.attachNewNode(name)
        name.setPos(0,0,22)
        name.hide()
        self.modelLight = loader.loadModel("misc/Pointlight.egg.pz")
        self.modelLight.reparentTo(name)
        self.treasureChest.setLight(name)
        self.fireFlyList.append(name)
        
    def setUpChest(self,task):
        if self.toggleChest==True:
            #chest rotates backwards if clicked on
            self.speed=-1.5
        else: self.speed=.5
        #chest rotates forward slowly 
        self.lightCounter+=1
        angle,radius= radians(self.heading),1.2
        x,y,z =(cos(angle)*radius,sin(angle)*radius,sin(angle)*radius)
        self.heading+=self.speed
        self.heading%=360
        self.treasureChest.setH(self.heading)
        if self.lightCounter%7==0:
            for n in xrange(self.numFireflies):
                self.generateFireFlies(x,y,n)
        if abs(self.treasureChest.getH()-360)<2 and self.toggleChest==True:
            #if chest is activated and the chest if facing the camera 
            taskMgr.add(self.moveChest, 'moveChest')
            return task.done 
        else: return Task.cont
        
    def moveChest(self,task):
        #units are calculated so that camera and chest move simultaneously
        #to create smooth transition between frames 
        self.alight.setColor(VBase4(.8, .8, .8, 1))
        self.shiftChestUnits,self.intervals=.1,52.0
        x,y,z=self.treasureChest.getPos()
        newx=x+self.shiftChestUnits
        self.treasureChest.setPos(newx,y,z)
        self.moveCamera()
        if int(x)==self.camPosx-10:
            #if we get within a range of acceptable values
            # we set up our homepage 
            self.homePage()
            return task.done
        else:
            return task.cont
    
    def moveCamera(self):
        self.originalZ,self.targetZ=28.0,22.0
        self.shiftCameraZ=(self.originalZ-self.targetZ)/self.intervals
        self.pitchChange=25.0/self.intervals
        newPitch=self.camera.getP()+self.pitchChange
        newHeight=self.camera.getPos()[2]-self.shiftCameraZ
        camerax,cameray=self.camera.getPos()[0],self.camera.getPos()[1]
        self.camera.setPos(camerax,cameray,newHeight)
        self.camera.setP(newPitch)
        
    def generateFireFlies(self,x,y,n):
        zLowerLimit=21.0
        zHigherLimit=22.0
        light=self.fireFlyList[n]
        self.xFirefly=random.uniform(-x,x)
        self.yFirefly=random.uniform(-y,y)
        self.zFirefly=random.uniform(zLowerLimit,zHigherLimit)
        light.setPos(self.xFirefly,self.yFirefly,self.zFirefly)
        
    def setUpFog(self):
        self.fogDensity=.03
        myFog = Fog("fog")
        myFog.setColor(1,1,1)
        myFog.setExpDensity(self.fogDensity)
        render.setFog(myFog)
        
    def displayHelpPanel(self):
        self.dummyNode1=self.createDummyNode("self.dummyNode1")
        self.dummyNode2=self.createDummyNode("self.dummyNode2")
        self.dummyNode3=self.createDummyNode("self.dummyNode3")
        self.floorPlan=OnscreenImage(
            image="floorplan.png",
            scale=(.4,.25,.55),
            pos=(.7,0,.3))
        self.floorPlan.setTransparency(TransparencyAttrib.MAlpha)
        self.floorPlan.reparentTo(self.dummyNode1)
        self.displayToggleBar()
        self.displayAdjacencies()
        taskMgr.doMethodLater(.1, self.createSubTitle, 'textFadeInOut')
       
    def createSubTitle(self,task):
        self.previousTitlePos=(-1.205,.89)
        self.nextTitlePos=(-1.195,-.43)
        try:
            self.titlePrevious.destroy()
            self.titlePrevious=OnscreenText(
                text="[Previous]",pos=self.previousTitlePos,
                scale=.045,fg=(1,1,1,self.fade))
        except:
            self.titlePrevious=OnscreenText(
                text="[Previous]",pos=self.previousTitlePos,
                scale=.045,fg=(1,1,1,self.fade))
        try :
            self.titleNext.destroy()
            self.titleNext=OnscreenText(
                text="[Next]",pos=self.nextTitlePos,scale=.045,fg=(1,1,1,self.fade))
        except: 
            self.titleNext=OnscreenText(
                text="[Next]",pos=self.nextTitlePos,
                scale=.045,fg=(1,1,1,self.fade))
        return task.again
        
    def createDummyNode(self,name):
        name=render.attachNewNode("2d")
        name.reparentTo(aspect2d)
        return name 

    def displayToggleBar(self):
        self.toggleBarImage=DirectButton(
            image = "hidebar.png",scale=(.07,0.1,.55),pos=(1.17,0,.3),
            relief=None,command=lambda: self.switchDisplays(
                self.toggleBar,self.dummyNode1,self.toggleBarImage,
                "hidebar.png","showbar.png",
                (.07,0.1,.55),(1.17,0,.3)))
        self.togglePreviousBar()
        self.toggleNextBar()
    
    def togglePreviousBar(self):
        self.previousBarPos=(-1.2,0,.57)
        self.togglePreviousBarImage=(DirectButton(
        image = "hidebarshort.png",
        scale=(.07,0.1,.195),pos=self.previousBarPos,relief=None,
        command= lambda: self.switchDisplays(
            self.togglePreviousCard,
            self.dummyNode2,self.togglePreviousBarImage,
            "hidebarshort.png","showbarshort.png",
            (.07,0.1,.195),self.previousBarPos)))
            
    def toggleNextBar(self):
        self.nextBarPos=(-1.2,0,-.7)
        self.toggleNextBarImage=(DirectButton(
        image = "hidebarshort.png",
        scale=(.07,0.1,.195),pos=self.nextBarPos,relief=None,
        command= lambda: self.switchDisplays(
            self.toggleNextCard,self.dummyNode3,self.toggleNextBarImage,
            "hidebarshort.png","showbarshort.png",
            (.07,0.1,.195),self.nextBarPos)))
            
    def switchPreviousCardDisplay(self):
        self.toggleBarPrevious=not(self.toggleBarPrevious)
        
    def displayAdjacencies(self):
        if self.previousCard:
            self.displayPreviousCard()
        if self.nextCard:
            self.displayNextCard()
            
    def displayPreviousCard(self):
        self.previousCardPos=(-1,0,.58)
        self.previousCardTextPos=(-.9,.57)
        prevCard=self.previousCard.split(".")[0]
        self.previousCard=OnscreenImage(
            image=self.previousCard,pos=self.previousCardPos,
            scale=(0.07,0.82,0.12))
        self.previousCard.reparentTo(self.dummyNode2)
        try:
            self.assocPrevText.destroy() 
            self.assocPrevText=OnscreenText(
                text="(%s)"%self.inputs[prevCard],
                pos=self.previousCardTextPos,
                scale=.06,fg=(1,1,1,1),align=TextNode.ALeft)
            self.assocPrevText.reparentTo(self.dummyNode2)
        except:
            self.assocPrevText=OnscreenText(
                text="(%s)"%self.inputs[prevCard],
                pos=self.previousCardTextPos,
                scale=.06,fg=(1,1,1,1),align=TextNode.ALeft)
            self.assocPrevText.reparentTo(self.dummyNode2)
    
    def displayNextCard(self):
        self.nextCardPos=(-1,0,-.685)
        self.nextCardTextPos=(-.9,-.69)
        nextCard=self.nextCard.split(".")[0]
        self.nextCard=OnscreenImage(
            image=self.nextCard,pos=self.nextCardPos,scale=(0.07,0.82,0.12))
        self.nextCard.reparentTo(self.dummyNode3)
        try:
            self.assocNextText.destroy()
            self.assocNextText=OnscreenText(
                text="(%s)"%self.inputs[nextCard],
                pos=self.nextCardTextPos,
                scale=.06,fg=(1,1,1,1),align=TextNode.ALeft)
            self.assocNextText.reparentTo(self.dummyNode3)
        except:
            self.assocNextText=OnscreenText(
                text="(%s)"%self.inputs[nextCard],
                pos=self.nextCardTextPos,scale=.06,
                fg=(1,1,1,1),align=TextNode.ALeft)
            self.assocNextText.reparentTo(self.dummyNode3)
            
            
    def switchDisplays(self,toggleBar,dummyNode,imageNode,hideImage,
                       showImage,scale,pos):
        toggleBar=not(toggleBar)
        if toggleBar==True:
            dummyNode.show()
            imageNode.destroy()
            imageNode=(DirectButton(image = hideImage,scale=scale,pos=pos,
                                    relief=None, command=lambda:
                                        self.switchDisplays(
                                            toggleBar,dummyNode,
                                            imageNode,hideImage,
                                            showImage,scale,pos)))
        else:
            dummyNode.hide()
            imageNode.destroy()
            imageNode=(DirectButton(image = showImage,scale=scale,pos=pos,
                                    relief=None, command= lambda:
                                        self.switchDisplays(
                                            toggleBar,dummyNode,
                                            imageNode,hideImage,
                                            showImage,scale,pos)))

    def shuffleCards(self):
        self.shuffledCardNames=[]
        self.shuffledCardFaces=copy.copy(cardFaces)
        random.shuffle(self.shuffledCardFaces)
        for face in self.shuffledCardFaces:
            self.shuffledCardNames.append(face.split(".")[0])
        
    def setUpCollisionRay(self):
        #Make a collision node for ours selection ray
        #Repurposed from Panda3D Documentation
        self.selectionNode = CollisionNode('mouseRay')
        self.selectionNP = camera.attachNewNode(self.selectionNode)
        self.selectionNode.setFromCollideMask(
            GeomNode.getDefaultCollideMask())
        self.selectionNode.setFromCollideMask(1)
        self.selectionNode.setIntoCollideMask(0)
        self.selectionRay = CollisionRay()
        #Make our ray
        self.selectionNode.addSolid(self.selectionRay)
        #Add it to the collision node
        #Register the ray as something that can cause collisions
        self.cTrav.addCollider(self.selectionNP,self.queue)
    
    def setUpCollisionNode(self):
        #add a collision node to our camera
        self.fromObject = base.camera.attachNewNode(
            CollisionNode('colNode'))
        self.fromObject.setPos(0,0,3)
        self.fromObject.node().addSolid(CollisionSphere(0, 0, 0, .5))
        self.fromObject.node().setIntoCollideMask(0)
        self.cTrav.addCollider(self.fromObject,self.pusher)
        self.pusher.addCollider(
            self.fromObject, base.camera, base.drive.node())
        
    def setInputs(self):
        self.accept("mouse1",self.mouse1Tasks)
        self.accept('escape', sys.exit)

        
    def playFootSteps(self):
        self.soundSteps.play()
        
    def mouse1Tasks(self):
        self.mouseTask()
        
    def setUpScene(self):
        #self.setUpControls()
        self.setUpBackground()
        #set up the items where we can store information
        self.setUpItems()
    
    def mouseTask(self):
        self.scale=53
        self.loadItemList()
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            self.selectionRay.setFromLens(
                base.camNode, mpos.getX(), mpos.getY())
            #self.cTrav.showCollisions(render)
            if self.queue.getNumEntries() > 0:
                self.mouseEntry()
    
    def mouseEntry(self):
        #if we've clicked on something
        self.queue.sortEntries()
        #we get closest item
        pickedObj = self.queue.getEntry(0).getIntoNodePath().getTag("items")
        #check if what we clicked is what we want (has proper tag)
        if len(pickedObj)>0 and (pickedObj not in self.visitedItems):
            self.mouseTaskSupport(pickedObj)
        if self.queue.getEntry(0).getIntoNodePath().getTag("openChest"):
            self.toggleChest=True
            
    def mouseTaskSupport(self,pickedObj):
        self.assocs[
            self.shuffledCardNames[self.counter]]=self.items[int(pickedObj)]
        self.counter+=1
        self.visitedItems.append(pickedObj)
        #self.queue.getEntry(0).getIntoNodePath().clearTag("items")
        #if len(self.visitedItems)==len(self.items):
        if len(self.visitedItems)==5:
            self.testMemory()
        self.drawMiniCards(-self.camera.getPos()[1]/self.scale+.40,
                           self.camera.getPos()[0]/self.scale-.2)
        self.updateDisplayCards()
        self.displayAdjacencies()
    
    def loadItemList(self):
        self.items=["painting","post","tv","bed","carpet","table","tree",
                    "shelf","loveSeat1","loveSeat2","longSofa",
                    "dinnerTable","dinnerChair1","dinnerChair2"]
    def testMemory(self):
        self.nextButton=DirectButton(image = 'arrow.png',
                                     scale=.15,pos=(0.8,0,-0.6),
                                     relief=None, command=self.recallMemory)
        self.nextButton.setTransparency(TransparencyAttrib.MAlpha)
    
    def displayPriorityArrow(self):
        self.arrow=OnscreenImage(image="arrow2.png",
                                 scale=(.05,.1,.05),pos=(-.51,0,-.6),
                                 color=(1,0,0,0))
        self.arrow.setTransparency(TransparencyAttrib.MAlpha)
        taskMgr.doMethodLater(.1, self.fadeArrow, 'arrowFadeInOut')
    
    def displayUserLocation(self,task,scale=(.05,1,.05)):
        self.updateFade()
        if self.userLocation!=None:
            self.userLocation.destroy(),self.userAngle.destroy()
        x=-self.camera.getPos()[1]/self.scale+.40
        y=self.camera.getPos()[0]/self.scale-.2
        self.userLocation=OnscreenImage(
            image="dot.png",pos=(x,0,y),scale=scale,
            color=(1,1,1,self.fade))
        self.userAngle=OnscreenImage(
            image="rotating.png",pos=(x,0,y),scale=scale,
            color=(1,1,1,self.fade))
        self.userAngle.setR(-self.camera.getHpr()[0])
        self.userLocation.setTransparency(TransparencyAttrib.MAlpha)
        self.userAngle.setTransparency(TransparencyAttrib.MAlpha)
        self.userLocation.reparentTo(self.dummyNode1)
        self.userAngle.reparentTo(self.dummyNode1)
        return Task.again
    
    def updateFade(self):
        if self.fadeToggle==True:
            self.fade+=.05
            if self.fade>1:
                self.fadeToggle=False
        elif self.fadeToggle==False:
            self.fade-=.05
            if self.fade<0:
                self.fadeToggle=True
        
    def drawMiniCards(self,x,y):
        self.lastCoord+=([[x,0,y]])
        self._draw_line_segs(self.dummyNode1,self.lastCoord)
        for i in xrange(len(self.lastCoord)):   
            self.miniCards=OnscreenImage(
                image=self.shuffledCardFaces[i],
                pos=(self.lastCoord[i][0],0,self.lastCoord[i][2]),
                scale=(0.03,0.82,0.050))
            self.miniCards.reparentTo(self.dummyNode1)
        
    def _draw_line_segs(self, parent, points, thickness=1.5):
        segs = LineSegs()
        segs.setThickness(thickness)
        segs.setColor( Vec4(1,0,0,.35) )
        if points:
            segs.moveTo(points[0][0], points[0][1], points[0][2])
            for p in points[1:]:
                segs.drawTo(p[0], p[1], p[2])
        lines = NodePath(segs.create())
        lines.reparentTo(parent)

    def recallMemory(self):
        self.checkList=[]
        self.checkListFaces=[]
        self.startCard=0
        self.endCard=13
        self.cardSet=0
        self.bk=OnscreenImage(image="bk.png",scale=(2,1,1))
        self.recallCards(self.startCard,self.endCard, self.cardSet,0)
        self.scaleCards=(0.1,0.1,0.13)
        scaleCards2=(0.07,0.05,0.10)
        self.recallMemoryPrompt=OnscreenText(
                    text="List the cards in the sequence as you recall",
                    pos=(0,.45),scale=.045,fg=(0,0,0,.75))

    def recallCards(self,startCard,endCard,cardSet,createEntries=1):
        for i in xrange(startCard,endCard):
            x=2*(i+1)/(self.numCards+1.0)-1-cardSet*1.75
            self.cardsPos.append(x)
            self.createButtons(i,x)
            if createEntries==1:
                self.createEntries(x, cardSet)
        for image in self.buttonList:
            image.setTransparency(TransparencyAttrib.MAlpha)
    
    def createButtons(self,i,x):
        self.buttonList.append(DirectButton(
            image = cardFaces[i],scale=(0.06,0.1,0.095),pos=(x,0,.3),
            relief=None, command=lambda: self.createCheckButtons(i)))
        
    def createCheckButtons(self,i):
        self.correctCount=0
        self.checkList.append(i)
        self.checkListFaces.append(cardFaces[i])
        for i in xrange(len(self.checkList)):
            x=2*(i+1)/(self.numCards+1.0)-1
            self.cards=OnscreenImage(image=self.checkListFaces[i],
                                     pos=(x,0,-.2),scale=(0.06,0.1,0.095))
        if len(self.checkList)==13:
            correct=self.testResults()
            self.results=OnscreenText(
                text="You recalled %d out of %d correctly"%(
                    correct,self.numCards),pos=(0,-0.85),scale=.1)
    
    def testResults(self):
        for i in xrange(len(self.shuffledCardFaces)):
            if self.shuffledCardFaces[i]==self.checkListFaces[i]:
                self.correctCount+=1
        return self.correctCount 
            
    def updateDisplayCards(self):
        self.numCardsCopy-=1
        self.previousCard=self.shuffledCardFacesCopy.pop(0)
        if len(self.shuffledCardFacesCopy)!=0:
            self.nextCard=self.shuffledCardFacesCopy[0]
        else: self.nextCard=False 
        for image in self.imageList:
            image.destroy()
        self.displayCards()
        
    def displayCards(self):
        for i in xrange(self.numCardsCopy):
            x=2*(i+1)/(self.numCards+1.0)-.65
            self.cardsPos.append(x)
            self.imageList.append(OnscreenImage(
                image="%s"%(self.shuffledCardFacesCopy[i]),
                pos=(x,0,-.8),scale=(0.05,0.05,0.08)))
            #we use middle numbers as coordinates
    def fadeArrow(self,task): 
        self.arrow.setColor(1,0,0,self.fade) 
        return task.cont
        
    def setUpBackground(self):
        self.scale=10
        #load grass and set position, scale, and texture
        self.walls=loader.loadModel("Walls")
        self.walls.setPos(0,0,0)
        self.walls.setScale(self.scale,self.scale,self.scale)
        walltex1 = loader.loadTexture('walltex1.png')
        self.walls.setTexture(walltex1)
        #render our object
        self.walls.reparentTo(render)
        self.loadColGeomsSet1()
        self.loadColGeomsSet2()
        self.setUpBackgroundSupport()
    
    def setUpBackgroundSupport(self):
        for geom in self.colGeoms:
            self.createCollisionGeom(*geom)
        cFloorNode=CollisionNode('floorCollision')
        floorQuad=CollisionPolygon(Point3(5, 0, 0), Point3(-1.5,0,0),
                                   Point3(-1.5, -4, 0),Point3(5, -4, 0))
        cFloorNode.addSolid(floorQuad)
    
    def loadColGeomsSet1(self):
        self.colGeoms=[
            (11,(2.4,-1.80,1),(2.4,-1.80,0),(.55,-1.80,0),
            (.55,-1.80,1)),#Interior 2b (firsthalf),
            (1,(5,-.05,0),(5,-.05,1),(-1.5,-.05,1), (-1.5,-.05,0)),#West Wall
            (2,(-1.5,0,0),(-1.5,0,1),(-1.5,-4,1), (-1.5,-4,0)),#South Edge
            (5,(0.52,0,0),(0.52,0,1),(0.52,-1.2,1), (0.52,-1.2,0)),
            #South Wall_1_Interior
            (3,(0.38,0,1),(0.38,0,0),(0.38,-1.2,0), (0.38,-1.2,1)),
            #South Wall_1_Exterior
            (4,(0.38,-1.7,1),(0.38,-1.7,0),(0.38,-4,0),(0.38,-4,1)),
            #South Wall_2_Exterior
            (6,(0.52,-1.7,0),(0.52,-1.7,1),(0.52,-4,1),(0.52,-4,0)),
            #South Wall_2 Interior
            (7,(1.8,0,1),(1.8,0,0),(1.8,-1.45,0),(1.8,-1.45,1)),
            #Interior 1 (TV)
            (8,(1.9,0,0),(1.9,0,1),(1.9,-1.45,1),(1.9,-1.45,0)),
            #Interior 1b (TV)
            (9,(2.4,-1.95,0),(2.4,-1.95,1),(.55,-1.95,1), (.55,-1.95,0)),
            #Interior 2 (firsthalf)
            (10,(3.95,-1.95,0),(3.95,-1.95,1), (2.7,-1.95,1),(2.7,-1.95,0))]
            #Interior 2 (secondhalf)
        
    def loadColGeomsSet2(self):
        self.colGeoms+=[
            (12,(3.95,-1.80,1),(3.95,-1.80,0), (2.7,-1.80,0),
              (2.7,-1.80,1)), #Interior 2b (secondhalf)
            (13,(2.95,-1.9,0),(2.95,-1.9,1),(2.95,-2.1,1),
                (2.95,-2.1,0)),#North Wall 1 Exterior (firsthalf)E
            (14,(2.95,-2.48,0),(2.95,-2.48,1),(2.95,-4,1),
                (2.95,-4,0)),#North Wall 1 Exterior (secondhalf)E
            (15,(2.7,-1.9,1),(2.7,-1.9,0),(2.7,-2.1,0),
                (2.7,-2.1,1)), #North Wall 1b Interior (firsthalf) E
            (16,(2.7,-2.48,1),(2.7,-2.48,0),(2.7,-4,0),
                (2.7,-4,1)), #North Wall 1b Interior (secondhalf) E
            (17,(3.82,0,1),(3.82,0,0),(3.82,-.35,0),
                Point3(3.82,-.35,1)),#North Wall 2 Interior (firsthalf) W
            (18,(3.82,-1.1,1),(3.82,-1.1,0),(3.82,-1.9,0),
                Point3(3.82,-1.9,1)),#North Wall 2 Interior (secondhalf) W
            (19,(4,0,0),(4,0,1),(4,-.35,1),
                Point3(4,-.35,0)),#North Wall 2 Exterior (firsthalf) W
            (20,(4,-1.1,0),(4,-1.1,1),(4,-1.9,1),
                Point3(4,-1.9,0))#North Wall 2 Exterior (secondhalf) W
            ]
        
    def createCollisionGeom(self,n,firstPts,secondPts,thirdPts,fourthPts):
        collisionNode="wallCollision%d"%n
        collisionNodeName="cWallNode%d"%n
        collisionNodeName=CollisionNode(collisionNode)
        quadName="wallQuad%d"%n
        quadName=CollisionPolygon(Point3(firstPts),Point3(secondPts),
                                  Point3(thirdPts),Point3(fourthPts))
        collisionNodeName.addSolid(quadName)
        self.wallC=self.walls.attachNewNode(collisionNodeName)
        
        self.roof=self.loadItems("Roof","rooftex.png",scaleX=6,scaleY=6)
        self.walls2=self.loadItems("Walls2","walltex2.png")
        self.grass=self.loadItems("grass",'8CYNDAC0.png')
        self.floor=self.loadItems("floor","floor.png")
        self.exterior=self.loadItems("exterior",'brick.png')
        
    def setUpItems(self):
        self.itemList=[]
        self.scale=10
        self.tv=self.loadItems("tv",'tv.png')
        self.carpet=self.loadItems("carpet","carpet.png")
        self.white=self.loadItems("white","walltex2.png",8,4)
        self.painting=self.loadItems("painting","kosbie.png")
        self.wood=self.loadItems("wood","wood.png",8,4)
        self.tree=self.loadItems("GroomedTree",None,1,1,.9,(52,-5,0))
        self.shelf=self.loadItems("shelf","darkwood.png")
        self.couches=self.loadItems("couch","leather.png",5,5)
        self.dining=self.loadItems("chairs","wood.png")
        self.treasureChest=self.loadItems(
            "treasurechest","woodpanel.png",1,1,None,(0,0,20))
        self.sky=self.loadItems("sky","sky.png",1,1)
        self.selectionItems = render.attachNewNode("selectionRoot")
        self.setUpCollisionGeom()
        
    def setUpCollisionGeom(self):
        self.paintingNode=self.loadCollisionPolygon(
            "painting",self.painting,(3.35,-.07,.45),(3.35,-.07,.8),
            (2.9,-.07,.8),(2.9,-.07,.45))
        self.postNode=self.loadCollisionTube(
            "post",self.wood,.25,-2,0,.25,-2,.55,.03)
        self.tvNode=self.loadCollisionPolygon(
            "tv",self.tv,(1.75,-.35,.78),(1.75,-.35,.5),(1.75,-1.1,.5),
            (1.75,-1.1,.78))
        self.bedNode=self.loadCollisionPolygon(
            "bed",self.white,(1,-2.7,.17),(.5,-2.7,.17),(.5,-1.9,.17),
            (1,-1.9,.17))
        self.carpetNode=self.loadCollisionPolygon(
            "carpet",self.carpet,(1.95,-3.15,.05),(2.4,-3.15,.05),
            (2.4,-2.55,.05),(1.95,-2.55,.05))
        self.tableNode=self.loadCollisionPolygon(
            "table",self.wood,(1,-3.5,.26),(1.45,-3.5,.26),
            (1.45,-3.1,.26),(1,-3.1,.26))
        self.supportSetUpCollisionGeom()
        
    def supportSetUpCollisionGeom(self):
        self.treeNode=self.loadCollisionTube(
            "tree",self.tree,0,0,0,0,0,7,2)
        self.shelfNode=self.loadCollisionPolygon(
            "shelf",self.shelf,(.6,-0.05,.78),(.6,-0.05,0),
            (.6,-.43,0),(.6,-.43,.78))
        self.couchNode1=self.loadCollisionTube(
            "couch1",self.couches,1.05,-.96,0,1.05,-.96,.2,.2)
        self.couchNode2=self.loadCollisionTube(
            "couch2",self.couches,1.45,-.96,0,1.45,-.96,.2,.2)
        self.couchNode3=self.loadCollisionTube(
            "couch3",self.couches,1.00,-.16,.1,1.5,-.16,.1,.2)
        self.dinnerTable=self.loadCollisionPolygon(
            "dinnerTable",self.dining,(3.6,-.89,.26),(3.6,-.61,.26),
            (3.1,-.61,.26),(3.1,-.89,.26))
        self.dinnerChairNode1=self.loadCollisionTube(
            "dinnerChair1",self.dining,2.9,-.75,0,2.9,-.75,.2,.1)
        self.dinnerChairNode2=self.loadCollisionTube(
            "dinnerChair1",self.dining,3.7,-.75,0,3.7,-.75,.2,.1)
        self.chestNode=self.loadCollisionTube(
            "chest",self.treasureChest,.02,.02,.25,.07,.07,.25,.25)
        self.chestNode.setTag(
            "openChest","1")
        
        self.itemList+=(self.paintingNode,self.postNode,self.tvNode,
                        self.bedNode,self.carpetNode,self.tableNode,
                        self.treeNode,self.shelfNode,self.couchNode1,
                        self.couchNode2,self.couchNode3,self.dinnerTable,
                        self.dinnerChairNode1,self.dinnerChairNode2)
        self.setTag(self.itemList,"items")
        
    def setTag(self):
        self.itemList+=(self.paintingNode,self.postNode,self.tvNode,
                        self.bedNode,self.carpetNode,self.tableNode,
                        self.treeNode,self.shelfNode,
                        self.couchNode1,self.couchNode2,self.couchNode3,
                        self.dinnerTable,self.dinnerChairNode1,
                        self.dinnerChairNode2)
        self.setTag(self.itemList,"items")
        
    def setTag(self,node,tagKey):
        for i in xrange(len(node)):
            node[i].setTag(tagKey,str(i))
        
    def loadItems(self,modelName,texture=None,scaleX=1,scaleY=1,
                  scale=None,pos=(0,0,0)):
        if scale==None: scale=self.scale
        modelName=loader.loadModel(modelName)
        modelName.setPos(pos)
        modelName.setScale(scale,scale,scale)
        ts=TextureStage("ts")
        if texture!=None:
            modelTex=loader.loadTexture(texture)
            modelName.setTexture(ts,modelTex)
            modelName.setTexScale(ts,scaleX,scaleY)
        modelName.reparentTo(render)
        return modelName
    
    def loadCollisionPolygon(self,modelName,attachGeom,firstPts,
                             secondPts,thirdPts,fourthPts):
        modelName=CollisionNode(modelName)
        quadName=CollisionPolygon(Point3(firstPts),Point3(secondPts),
                                  Point3(thirdPts),Point3(fourthPts))
        modelName.addSolid(quadName)
        modelName=attachGeom.attachNewNode(modelName)
        return modelName
    
    def loadCollisionTube(self,modelName,attachGeom,x0,y0,z0,x1,y1,z1,
                          radius):
        modelName=CollisionNode(modelName)
        cylinder=CollisionTube(x0,y0,z0,x1,y1,z1,radius)
        modelName.addSolid(cylinder)
        modelName=attachGeom.attachNewNode(modelName)
        return modelName
    
    def homePage(self):
        self.introduction=False 
        #self.setUphomePageSFX()
        self.introButton=DirectButton(
            image=("introduction.png"),scale=.5,pos=(-0.8,0,-0.65),
            relief=None,command=self.startIntro)
        self.introButton.setTransparency(TransparencyAttrib.MAlpha)
        self.startButton=DirectButton(
            image=("beginJourney.png"),scale=.5,pos=(+0.8,0,-0.65),
            relief=None, command=self.startJourney)
        self.startButton.setTransparency(TransparencyAttrib.MAlpha)
        self.cylinder=self.loadItems(
            "cylinder","wood.png",1,1,2,(9,-1.3,20.8))
        self.hexagon=self.loadItems(
            "hexagon","wood.png",1,1,2.5,(9,1.4,20.8))
        taskMgr.add(self.rotateMenuItems, 'rotateMenuItems')
    
    #def setUphomePageSFX(self):
        #self.volume=0
        #self.circusInTheSky.setVolume(0)
        #self.circusInTheSky.setLoop(1) 
        #self.circusInTheSky.play()
        #taskMgr.doMethodLater(.5, self.playCircusInTheSky,
        #                      'Fade in Music', extraArgs = [self])
        
    #def playCircusInTheSky(self,task):
        #self.volume+=.00125
        #self.circusInTheSky.setVolume(self.volume)
        #return Task.cont
            
    def rotateMenuItems(self,task):
        self.speed=1.5
        radius=.5
        angle = radians(self.heading)
        x = cos(angle) * radius
        y = sin(angle) * radius
        z = sin(angle) * radius
        self.heading+=self.speed
        self.heading%=360
        self.cylinder.setH(self.heading)
        self.hexagon.setH(self.heading)
        if self.introduction==True:
            self.cylinder.remove()
            self.hexagon.remove()
            return task.done 
        return Task.cont

    def startJourney(self):
        #self.circusInTheSky.stop()
        self.introButton.destroy()
        self.startButton.destroy()
        self.initSetUpAssoc()
        #self.setUp3DEnvironmentIntro()
        ###########change here 
        
    def setUp3DEnvironmentIntro(self):
        self.camera.setP(25)
        self.Enviro3DDummy=self.createDummyNode("3DEnviroDummy")
        self.textBlocks,self.color,self.keyBrightenComplete=[],0.01,False
        self.displayCards()
        self.displayHelpPanel()
        taskMgr.doMethodLater(.05,self.displayUserLocation, 'Track User',
                              extraArgs = [self])
        self.setUpDirectionalLight()
        self.displayPriorityArrow()
        self.accept("arrow_down",self.playFootSteps)
        self.accept("arrow_up",self.playFootSteps)
        self.enviroTextBlocks()
        self.Enviro3DSupportText()
    
    def enviroTextBlocks(self):
        self.enviroTextBlock1=(
            "1. Here is the priority queue for the shuffled cards")
        self.enviroTextBlock2=(
            "2. Toggle displays to show/hide windows")
        self.enviroTextBlock3=(
            "3. See your location in the plan")
        self.textBlocks+=(self.enviroTextBlock1,self.enviroTextBlock2,
                               self.enviroTextBlock3)
        self.textPos=[(0,-.6),(-.6,.6),(.7,-.4)]
        
    def Enviro3DSupportText(self):
        for i in xrange(len(self.textBlocks)):
            self.titleText=OnscreenText(text=self.textBlocks[i],
            pos=self.textPos[i],scale=.058,fg=(1,1,1,1))
            self.titleText.reparentTo(self.Enviro3DDummy)
        taskMgr.doMethodLater(.1, self.init3DActiveEnviro,'3DEnviroEntry')
        taskMgr.doMethodLater(.15,self.brightenKey,"brightenKey")
    
    def init3DActiveEnviro(self,task):
        self.keyButton=DirectButton(
            image = 'key.png',scale=(.5,.2,.15),color=(0,0,0,0),pos=(-.5,0,0),
            relief=None, command=self.setUp3DEnvironmentActive)
        self.keyButton.setTransparency(TransparencyAttrib.MAlpha)
        self.keyButton.reparentTo(self.Enviro3DDummy)
        self.keyButton['state'] = DGG.DISABLED
        return task.done 
        
    def brightenKey(self,task):
        if self.keyBrightenComplete==False:
            self.brightenSpeed=0.025
            if self.color<1:
                self.color+=self.brightenSpeed
                self.keyButton.setColor(0,0,0,self.color)
                return task.again
            else:
                self.entryText=OnscreenText(text="[Click the key to begin]",
                pos=(-.7,.13),scale=.058,fg=(1,1,1,1))
                self.entryText.reparentTo(self.Enviro3DDummy)
                self.keyButton['state'] = DGG.NORMAL
                task.done
        else:
            task.done 

    def setUp3DEnvironmentActive(self):
        base.useDrive()
        self.keyBrightenComplete=True 
        self.Enviro3DDummy.removeNode()

    def setUpDirectionalLight(self):
        dlight = DirectionalLight('dlight')
        dlight.setColor(VBase4(0.8, 0.8, 0.5, 1))
        dlnp = render.attachNewNode(dlight)
        dlnp.setHpr(0, -60, 0)
        render.setLight(dlnp)
        render.setShaderAuto()
        
    def startIntro(self):
        self.introduction=True 
        self.introButton.destroy()
        self.startButton.destroy()
        self.setUpHelp()

    def setUpHelp(self):
        self.titleText=OnscreenText(text="Introduction",
                                    pos=(-0.85,0.85),scale=.1)
        self.text = TextNode('helpText')
        self.textBlocks()
        self.text.setText(self.textBlock1)
        textNodePath = aspect2d.attachNewNode(self.text)
        textNodePath.setScale(0.07)
        textNodePath.setPos(-1,0,0)
        self.text.setWordwrap(30.0)
        self.nextButton=DirectButton(
            image = 'arrow.png',scale=.15,pos=(0.8,0,-0.6),relief=None,
            command=self.nextPage )
        self.nextButton.setTransparency(TransparencyAttrib.MAlpha)
    
    def textBlocks(self):
        self.textBlock1=("This project tests the effectiveness"
        +" of along established 'Method of loci', which is a"
        +" mnemonic device used for memory enhancement which uses"
        +" visualization to organize and recall information."
        +" We will be using this technique to memory a set of 13"
        +" cards (one suit)")
        
        self.textBlock2=(
        "STEP 1 \n\n We create rooms which are connected "
        + "and unique. These are provided for you."
        +"\n\n STEP 2 \n\n Associate each card with visual "
        +"objects (eg. 2 ---> Swan)"
        +"\n\n STEP 3 \n\n We 'place' the cards at specific "
        +"locations in sequence so relationship between "
        +"the location and item are made."
        +"\n\n STEP 4 \n\n Test the associations we've made")
        
    def nextPage(self):

        textNodePath = aspect2d.attachNewNode(self.text)
        textNodePath.setPos(-1,0,.5)
        self.nextButton.destroy()
        self.text.setText(self.textBlock2)
        self.nextButton=DirectButton(
            image = 'arrow.png',scale=.15,pos=(0.8,0,-0.6),relief=None,
            command=(self.clear))
        self.nextButton.setTransparency(TransparencyAttrib.MAlpha)
        
    def clear(self):
        self.text.clearText()
        self.titleText.clearText()
        self.nextButton.destroy()
        self.homePage()
        
    def initSetUpAssoc(self):
        self.bk=OnscreenImage(image="bk.png",scale=(2,1,1))
        self.inputs=dict()
        self.cardsPositions=[]
        self.imageListFaces,self.textObjs,self.entries=[],[],[]
        self.numCardsPage1=7
        self.startCard=0
        self.endCard=7
        self.cardSet=0
        self.callCards(self.startCard,self.endCard, self.cardSet)
        self.goAhead=[]
        self.button1=loader.loadMusic("button1.wav")
        self.assocTextBlocks()
        taskMgr.doMethodLater(.1, lambda task: self.createAssocHelperText(
            task,self.assocTextBlock1),'assocHelperText')
        
    def assocTextBlocks(self):
        self.assocTextBlock1=(
            "[Enter a word you strongly associated with each card]"
            +"\n Helpful Hint: Use less abstract and more descriptive words")
        self.assocTextBlock2=(
            "[That's great! Finish up the rest!]")
            
    def createAssocHelperText(self,task,text):
        self.updateFade()
        try:
            self.assocText.destroy()
            self.assocText=OnscreenText(
            text=text,
            pos=(0,-.2),scale=.05,fg=(0,0,0,self.fade))
        except:
            self.assocText=OnscreenText(
            text=text,
            pos=(0,-.2),scale=.05,fg=(0,0,0,self.fade))
        return task.again 
            
    def callCards(self,startCard,endCard,cardSet,createEntries=1,
                  scaleCards=(0.1,0.1,0.13)):
        for i in xrange(startCard,endCard):
            x=2*(i+1)/(self.numCardsPage1+1.0)-1-cardSet*1.75
            self.cardsPositions.append(x)
            self.imageListFaces.append(
                OnscreenImage(image="%s"%(cardFaces[i]),
                              pos=(x,0,.3),scale=scaleCards))
            self.textObjs.append(OnscreenText(text=str(i),pos=(x,0)))
            #we use middle numbers as coordinates
            self.cardName=cardNames[i]
            if createEntries==1:
                self.createEntries(x, cardSet)
            
    def createEntries(self,x, cardSet):
        self.entries.append(
            DirectEntry(text = "" ,scale=.02,pos=(x-0.1,0.1,0.1),
                        command=(lambda textEntered: self.storeAssoc
                                 (textEntered, x, cardSet)), numLines = 2))
    
    def clearPage(self):
        for image in self.imageListFaces:
            image.destroy()
        for textObj in self.textObjs:
            textObj.destroy()
        for entry in self.entries:
            entry.destroy()
        for image in self.goAhead:
            image.destroy()
        self.assocText.destroy()
        self.clearBK()

            
    def clearBK(self):
        self.bk.destroy()
    
    def storeAssoc(self,textEntered, x, cardSet):
        index=int((x+cardSet*1.75+.75)/0.25)
        self.inputs[cardNames[index]]=textEntered
        self.greenDot=OnscreenImage(
            image="greenDot.png",pos=(x,0,.55),scale=(.25,1,.25))
        self.greenDot.setTransparency(TransparencyAttrib.MAlpha)
        self.goAhead.append(self.greenDot)
        self.button1.play()
        if len(self.inputs)>6 and cardSet==0:
            taskMgr.remove('assocHelperText')
            taskMgr.doMethodLater(.1, lambda task: self.createAssocHelperText(
            task,self.assocTextBlock2),'assocHelperText')
            self.startCard=7
            self.endCard=13
            self.clearBK()
            self.cardSet=1
            self.bk=OnscreenImage(image="bk.png",scale=(2,1,1))
            self.callCards(self.startCard,self.endCard, self.cardSet)
        if len(self.inputs)>12 and cardSet==1:
            taskMgr.remove('assocHelperText')
            self.clearPage()
            self.setUp3DEnvironmentIntro()
    def startGame(self):
        self.bk.destroy()
Exemple #9
0
class Begin(ShowBase):
    def __init__(self):
        global pointball_value
        global title_screen
        global loading_screen
        # Basics
        ShowBase.__init__(self)
        #Setup the window
        base.disableMouse()
        render.setAntialias(AntialiasAttrib.MAuto)
        self.set_windowsettings()
        base.camLens.setFar(asteroid_spawn_distance * 100)
        base.camLens.setNear(2000)
        self.setBackgroundColor(colors.get("black"))
        # Create the directional and ambient lights, and apply them to the world.
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor((0.8, 0.8, 0.8, 1))
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(LVector3(0, 45, -45))
        directionalLight.setColor((1, 1, 1, 1))
        directionalLight.setShadowCaster(True)
        render.setLight(render.attachNewNode(directionalLight))
        render.setLight(render.attachNewNode(ambientLight))
        # Create a black fog and apply it to the world.
        self.fog = Fog('distanceFog')
        self.fog.setColor(0, 0, 0)
        self.fog.setExpDensity(fog_quality)
        render.setFog(self.fog)
        # Initialize Collisions
        base.cTrav = CollisionTraverser()
        base.cTrav.setRespectPrevTransform(True)
        self.collHandEvent = CollisionHandlerEvent()
        self.collHandEvent.addInPattern("%fn-into-%in")
        # Add colision sphere to player for losing state
        cNode = CollisionNode("player")
        cNode.addSolid(CollisionSphere(0, 0, 0, 3))
        self.player_np = base.camera.attachNewNode(cNode)
        base.cTrav.addCollider(self.player_np, self.collHandEvent)
        # Setup initial score
        self.title = OnscreenText(text="Score: {0}".format(score),
                                  parent=base.a2dTopLeft,
                                  scale=.07,
                                  align=TextNode.ALeft,
                                  pos=(0.1, -0.1),
                                  fg=(1, 1, 1, 1),
                                  shadow=(0, 0, 0, 0.5),
                                  font=thunderstrike)
        # Add Occluder Culling - I will need to figure this out later
        # occluder_model = self.loader.loadModel("./Models/cone_10vert.egg")
        # render.setOccluder(occluder_model)

        Begin.keyMap = {
            "forward": False,
            "strafe-left": False,
            "backward": False,
            "strafe-right": False,
            "strafe-up": False,
            "strafe-down": False,
            "roll-left": False,
            "roll-right": False
        }  #True if coresponding key is currently held down.
        #Basic camera movement on the xyz coordinate plane
        self.accept("escape", sys.exit)
        self.accept(
            "w", self.setKey, ["forward", True]
        )  # Pressing the key down sets the state to true. Tasks will function as if pressing key each frame.
        self.accept(
            "w-up", self.setKey, ["forward", False]
        )  # Releasing the key changes the key state in begin.keyMap to False to tasks will stop looping.
        self.accept(
            "a", self.setKey, ["strafe-left", True]
        )  # Both previous comments apply to the following 'accept self.setKey' block of code.
        self.accept("a-up", self.setKey, ["strafe-left", False])
        self.accept("s", self.setKey, ["backward", True])
        self.accept("s-up", self.setKey, ["backward", False])
        self.accept("d", self.setKey, ["strafe-right", True])
        self.accept("d-up", self.setKey, ["strafe-right", False])
        self.accept("space", self.setKey, ["strafe-up", True])
        self.accept("space-up", self.setKey, ["strafe-up", False])
        self.accept("control", self.setKey, ["strafe-down", True])
        self.accept("control-up", self.setKey, ["strafe-down", False])
        self.accept("shift", self.setKey, ["strafe-down", True])
        self.accept("shift-up", self.setKey, ["strafe-down", False])
        self.accept("q", self.setKey, ["roll-left", True])
        self.accept("q-up", self.setKey, ["roll-left", False])
        self.accept("e", self.setKey, ["roll-right", True])
        self.accept("e-up", self.setKey, ["roll-right", False])
        self.accept('mouse1', self.shoot)  # Shoots the projectile
        self.accept('f11', self.fullscreenToggle)
        self.accept('f12', self.framesToggle)
        # Development keys
        self.accept('0', self.stop_moving)  # Stop moving
        self.accept('1', self.angle1)
        self.accept('2', self.angle2)
        self.accept('3', self.angle3)
        self.accept('4', self.angle4)
        self.accept('5', self.angle5)
        self.accept('6', self.angle6)

        # Create the loading bar
        self.loading = DirectFrame(frameSize=(-10, 10, -10, 10),
                                   frameColor=(0, 0, 0, 1))
        self.loading_bar = DirectWaitBar(
            text="Generating Asteroids . . .",
            text_font=thunderstrike,
            text_fg=(0, 0, 0, 1),
            text_shadow=(1, 1, 1, 1),
            #text_pos=(0.001,0.001, -1),
            value=50,
            range=asteroid_max + (2 * int(asteroid_max * 0.05)),
            barColor=(1, 1, 1, 1),
            frameColor=(0, 0, 0, 0),
            parent=self.loading)
        loading_screen = self.loading
        loading_screen.loading_bar = self.loading_bar

        # Create the main menu
        self.quality_name = "Low"
        self.quality_num = 1
        self.quality_applied = 1
        self.menu = DirectFrame(frameSize=(10, -10, 10, -10),
                                frameColor=(0, 0, 0, 1))

        self.menu_title = DirectFrame(
            frameSize=(base.a2dLeft, base.a2dRight, 0.55, 0.8),
            frameTexture=loader.loadTexture("./Fonts/title.png"),
            parent=self.menu)

        title_asteroid = Asteroid("small")
        self.menu_asteroid = DirectFrame(geom=title_asteroid.np,
                                         geom_scale=(0.000008, 0.000008,
                                                     0.000008),
                                         pos=(1.13, 0, 0.56),
                                         frameColor=(0, 0, 0, 1),
                                         enableEdit=1,
                                         parent=self.menu_title)
        title_screen = self.menu
        title_screen.menu_asteroid = self.menu_asteroid
        title_screen.start_btn = self.createButton("Start", self.start_game,
                                                   0.2)
        title_screen.how_to_btn = self.createButton("How to Play",
                                                    self.how_to_play, 0)
        title_screen.exit_btn = self.createButton("Quit", sys.exit, -0.2)
        title_screen.res_apply_btn = self.createButton("Apply",
                                                       self.apply_res_button,
                                                       -0.4, 1,
                                                       (-2, 2.3, -0.6, 1))
        title_screen.res_apply_btn.hide()
        title_screen.resolution_btn = self.createButton(
            f"Resolution ({resolution[0]} x {resolution[1]})", self.resolution,
            -0.4)
        title_screen.fullscreen_btn = self.createButton(
            "Toggle Fullscreen", self.fullscreenToggle, -0.6)
        title_screen.quality_btn = self.createButton(
            f"Qualilty ({self.quality_name})", self.quality, -0.8)
        title_screen.qual_apply_btn = self.createButton(
            "Apply", self.apply_qual_button, -0.8, 1, (-2, 2.3, -0.6, 1))
        title_screen.qual_apply_btn.hide()
        taskMgr.add(Begin.menu, "Menu")
        title_screen.hide()

        # Setup game tasks and create the 3d asteroids.
        taskMgr.add(Begin.createAsteroids, "Generate asteroids")

    def createAsteroids(self):
        global asteroid_max
        global asteroid_total
        global loading_screen
        global title_screen
        global base
        if len(asteroid_total) < asteroid_max:
            asteroid = Asteroid()
            asteroid_total.insert(0, asteroid)
            base.cTrav.addCollider(asteroid.c_np, base.collHandEvent)
            asteroid.add_togame()
            loading_screen.loading_bar["value"] += 1
            return Task.cont
        # Extra asteroids to be instantly available when asteroids break
        if len(extra_smallasteroids) < int(asteroid_max * 0.05):
            extra_smallasteroids.insert(0, Asteroid("small"))
            extra_mediumasteroids.insert(0, Asteroid("medium"))
            loading_screen.loading_bar["value"] += 1
            return Task.cont
        if len(extra_mediumasteroids) < int(asteroid_max * 0.05):
            extra_mediumasteroids.insert(0, Asteroid("medium"))
            loading_screen.loading_bar["value"] += 1
            return Task.cont
        print("did not return")
        loading_screen.hide()
        title_screen.show()
        taskMgr.remove("Generate asteroids")

    def start_game(self):
        global pointball_value
        global cursor_hidden
        global is_living
        self.accept("player-into-asteroid", self.end_game)
        self.accept("player-into-pointball", self.score)
        self.accept(f"missle-into-asteroid", self.shot_asteroid)
        # Set mouse and display settings
        self.lastMouseX, self.lastMouseY = 0, 0
        cursor_hidden = True
        self.set_windowsettings()
        self.startTasks()
        for asteroid in asteroid_total:
            asteroid.asteroid_lerp.resume()
        # Hide the main Menu
        if not (is_living):
            #taskMgr.add(self.score, "Score")
            self.accept('mouse1', self.shoot)
            base.camera.setPos(0, 0, 0)
            taskMgr.remove("Death Spin")
            title_screen.start_btn["text"] = ("Resume", "Resume", " Resume!",
                                              "Resume")
            base.setBackgroundColor(0, 0, 0, 1)
            self.fog.setColor(0, 0, 0)
            is_living = True

        title_screen.hide()
        # Change what the escape key does
        self.accept("escape", self.pause)
        pointball_value = int(time.time())

    def pause(self):
        global is_living
        global score
        global cursor_hidden
        global spaceship_speed_x
        global spaceship_speed_y
        global spaceship_speed_z
        pause_pointball_value = pointball_value
        for asteroid in asteroid_total:
            asteroid.asteroid_lerp.pause()
        # Show + relase the mouse
        title_screen.show()
        cursor_hidden = False
        self.set_windowsettings()
        taskMgr.remove("Rotate player in hpr")
        self.acceptOnce("escape", sys.exit)
        # Change start button text relative to living state + remove death text if died
        if is_living:
            title_screen.start_btn["text"] = ("Resume", "Resume", " Resume!",
                                              "Resume")
        else:
            aspect2d.find("**/-TextNode").removeNode()
            title_screen.start_btn["text"] = ("Retry", "Retry", " Retry!",
                                              "Retry")
            spaceship_speed_x = 0
            spaceship_speed_y = 0
            spaceship_speed_z = 0
            base.camera.setPos(0, 0, 0)
            while len(asteroid_total) > asteroid_max:
                del asteroid_total[0]
            for asteroid in asteroid_total:
                asteroid.asteroid_lerp.finish()
                asteroid.add_togame()
                score = 0
                self.title = OnscreenText(text="Score: {0}".format(score),
                                          parent=base.a2dTopLeft,
                                          scale=.07,
                                          align=TextNode.ALeft,
                                          pos=(0.1, -0.1),
                                          fg=(1, 1, 1, 1),
                                          shadow=(0, 0, 0, 0.5),
                                          font=thunderstrike)

    def quality(self):
        qual_dict = {
            # asteroid_number is chosen by 300 was found to be a good number when testing on a low spec machine. The larger ones keep the same density of asteroids as volume increases
            1: {
                "quality": "Low",
                "asteroid_detail": 36,
                "fog_quality": 0.000001,
                "asteroid_spawn_distance": 3000000,
                "asteroid_number": 300
            },
            2: {
                "quality": "Medium",
                "asteroid_detail": 30,
                "fog_quality": 0.0000008,
                "asteroid_spawn_distance": 4000000,
                "asteroid_number": 711
            },
            3: {
                "quality": "High",
                "asteroid_detail": 20,
                "fog_quality": 0.0000006,
                "asteroid_spawn_distance": 5000000,
                "asteroid_number": 1389
            },
        }
        self.quality_num = self.quality_num + 1 if self.quality_num != 3 else 1
        self.game_quality = qual_dict[self.quality_num]
        self.quality_name = self.game_quality["quality"]
        text = f"Quality ({self.quality_name})"
        title_screen.quality_btn["text"] = (text, text, f" {text}!", text)
        if self.quality_num != self.quality_applied:
            title_screen.qual_apply_btn.show()
        else:
            title_screen.qual_apply_btn.hide()

    def apply_qual_button(self):
        global asteroid_detail
        global fog_quality
        global asteroid_future_distance
        global asteroid_test_distance
        global asteroid_max
        global asteroid_total
        global extra_smallasteroids
        global extra_mediumasteroids
        asteroid_detail = self.game_quality["asteroid_detail"]
        fog_quality = self.game_quality["fog_quality"]
        asteroid_spawn_distance = self.game_quality["asteroid_spawn_distance"]
        asteroid_test_distance = asteroid_spawn_distance * (29.0 / 30.0)
        asteroid_max = self.game_quality["asteroid_number"]
        self.fog.setExpDensity(fog_quality)
        self.quality_applied = self.quality_num
        title_screen.qual_apply_btn.hide()
        asteroid_total = []
        extra_mediumasteroids = []
        extra_smallasteroids = []
        title_screen.hide()
        loading_screen.loading_bar["value"] = 0
        loading_screen.loading_bar["range"] = asteroid_max + (
            2 * int(asteroid_max * 0.05))
        loading_screen.show()
        taskMgr.add(Begin.createAsteroids, "Generate asteroids")

    def resolution(self):
        global resolution
        res_list = [
            # 4:3
            (800, 600),
            (1024, 768),
            (1920, 1440),
            (4096, 3071),
            # 5:4
            (1280, 1024),
            # 16:9
            (1280, 720),
            (1366, 768),
            (1600, 900),
            (1920, 1080),
            (2560, 1440),
            (3840, 2160),
            (4096, 2304),
            #16:10
            (1440, 900),
            (1680, 1050),
            (2304, 1440),
            (4096, 2560),
            # 21:9
            (2160, 1080),
            (3440, 1440)
        ]
        index = res_list.index(resolution)

        resolution = res_list[index +
                              1] if index < len(res_list) - 1 else res_list[0]
        if base.getSize() == resolution:
            title_screen.res_apply_btn.hide()
        else:
            title_screen.res_apply_btn.show()
        text = f"Resolution ({resolution[0]} x {resolution[1]})"
        title_screen.resolution_btn["text"] = (text, text, f" {text}!", text)

    def apply_res_button(self):
        title_screen.res_apply_btn.hide()
        self.set_windowsettings()

    def how_to_play(self):
        webbrowser.open(
            'https://github.com/13r0ck/3d-Space-Rocks-Simulator-2020')

    def createButton(self,
                     text,
                     command,
                     verticalPos,
                     horisontalPos=0,
                     frame_size=(-8, 8, -0.6, 1)):
        btn = DirectButton(text=(text, text, f" {text}!", text),
                           text_fg=(1, 1, 1, 1),
                           pad=(0.7, 0.3),
                           frameSize=frame_size,
                           frameTexture="./Images/Button_Frame.png",
                           relief=1,
                           text_font=thunderstrike,
                           text_scale=0.9,
                           scale=0.1,
                           command=command,
                           pos=(horisontalPos, 0, verticalPos),
                           textMayChange=1)
        btn.reparentTo(self.menu)
        return btn

    def startTasks(self):
        #The tasks below are the functions run every frame so the game will work
        taskMgr.add(Begin.test_distance, "Test Distance")
        taskMgr.add(self.mouseTask, "Rotate player in hpr")
        taskMgr.add(Begin.spaceship_movement, "Move the Player in xyz")
        taskMgr.add(Begin.remove_old_missles, "Remove old missles")
        taskMgr.add(Begin.pointballManager, "Pointballs Manager")

    def menu(self):
        h, p, r = title_screen.menu_asteroid["geom_hpr"]
        dt = globalClock.getDt()
        title_screen.menu_asteroid["geom_hpr"] = LVecBase3f(h + 45 * dt, 0, 0)
        return Task.cont

    ##### // Key Press Functions \\ #####
    def spaceship_movement(self):
        global spaceship_speed_x
        global spaceship_speed_y
        global spaceship_speed_z
        global max_player_speed
        dt = globalClock.getDt()
        # Move the player on the global axis. This is how momentum is not interupted
        cam_pos_init = base.camera.getPos()
        base.camera.setPos(
            cam_pos_init[0] +
            spaceship_speed_x * dt,  # Spaceship X change per frame
            cam_pos_init[1] +
            spaceship_speed_y * dt,  # Spaceship Y change per frame
            cam_pos_init[2] +
            spaceship_speed_z * dt)  #   "       Z   "     "    "
        # Ff a key is pressed, then we will need to do other calulations this frame.
        if Begin.keyMap["forward"] or Begin.keyMap["backward"] or Begin.keyMap[
                "strafe-left"] or Begin.keyMap["strafe-right"] or Begin.keyMap[
                    "strafe-up"] or Begin.keyMap["strafe-down"]:
            local_x, local_y, local_z = 0, 0, 0
            cam_pos1 = base.camera.getPos()
            # Add aribitraty movement on the local axis relative to the key pressed.
            if Begin.keyMap["forward"]:
                local_x += 3000 * dt
            if Begin.keyMap["backward"]:
                local_x -= 3000 * dt
            if Begin.keyMap["strafe-right"]:
                local_y += 3000 * dt
            if Begin.keyMap["strafe-left"]:
                local_y -= 3000 * dt
            if Begin.keyMap["strafe-up"]:
                local_z += 3000 * dt
            if Begin.keyMap["strafe-down"]:
                local_z -= 3000 * dt
            base.camera.setPos(base.camera, local_y, local_x, local_z)
            cam_pos2 = base.camera.getPos()
            #Calculate the global velocity change from the local change
            # Note: dv_xyz delta velocity xyz
            dv_xyz = []
            dv_xyz = [(cam_pos2[i] - cam_pos1[i]) / dt for i in range(0, 3)]
            # Calcualte the magnitude to limit player speed
            mag = math.sqrt((spaceship_speed_x)**2 + (spaceship_speed_y)**2 +
                            (spaceship_speed_z)**2)
            if mag < max_player_speed:
                spaceship_speed_x += dv_xyz[0]
                spaceship_speed_y += dv_xyz[1]
                spaceship_speed_z += dv_xyz[2]
            else:
                possible_x, possible_y, possible_z = spaceship_speed_x, spaceship_speed_y, spaceship_speed_z
                possible_x += dv_xyz[0]
                possible_y += dv_xyz[1]
                possible_z += dv_xyz[2]
                possible_mag = math.sqrt((possible_x)**2 + (possible_y)**2 +
                                         (possible_z)**2)
                if possible_mag < mag:
                    spaceship_speed_x += dv_xyz[0]
                    spaceship_speed_y += dv_xyz[1]
                    spaceship_speed_z += dv_xyz[2]
        # Separate from the top movement. Allow for camera rotation
        if Begin.keyMap["roll-left"]:
            camera_r = base.camera.getR()
            base.camera.setR(camera_r - 1)
        if Begin.keyMap["roll-right"]:
            camera_r = base.camera.getR()
            base.camera.setR((camera_r + 1))
        return Task.cont

    def shoot(self):
        missle = Missle()
        base.cTrav.addCollider(missle.c_np, self.collHandEvent)

    def do_null(self):
        # Redefine the accept key to this to ignore key
        pass

    ##### // Tasks \\ #####
    def score(self, collision_entry):
        global score
        global thunderstrike
        pointball = collision_entry.getIntoNodePath().parent
        score += int(pointball.getTag("value"))
        render.clearLight(pointball.find("**/plight"))
        pointball.removeNode()
        self.title.clearText()
        self.title = OnscreenText(text="Score: {0}".format(score),
                                  parent=base.a2dTopLeft,
                                  scale=.07,
                                  align=TextNode.ALeft,
                                  pos=(0.1, -0.1),
                                  fg=(1, 1, 1, 1),
                                  shadow=(0, 0, 0, 0.5),
                                  font=thunderstrike)
        return Task.cont

    # Test the distance of all asteroids. If the asteroid is too far away turn it around.
    def test_distance(self):
        global asteroid_max
        global asteroid_test_distance
        for asteroid in asteroid_total:
            if asteroid.ttl > 0:
                asteroid.ttl -= globalClock.getDt()
            else:
                asteroid_xyz = asteroid.np.getPos()
                camera_xyz = base.camera.getPos()
                distance = math.sqrt(
                    (asteroid_xyz[0] - camera_xyz[0])**2 +
                    (asteroid_xyz[1] - camera_xyz[1])**2 +
                    (asteroid_xyz[2] - camera_xyz[2])**2)  # Distance formula
                if distance > asteroid_test_distance:
                    start_point = asteroid.get_sphere_points(
                        asteroid_spawn_distance, base.camera)
                    asteroid.asteroid_lerp.finish()
                    asteroid.asteroid_path(
                        start_point)  #move to sphere relative to camera
                    asteroid.ttl = 1
                    #asteroid.c_np.show()
        return Task.cont

    def mouseTask(self, task):
        global test_max_min
        dt = globalClock.getDt()
        # h_max : h_min , p_max : p_min
        mw = self.mouseWatcherNode
        if mw.hasMouse():
            # get the window manager's idea of the mouse position
            x, y = mw.getMouseX(), mw.getMouseY()

            if self.lastMouseX is not None:
                dx, dy = x - self.lastMouseX, y - self.lastMouseY
            else:
                # no data to compare with yet
                dx, dy = 0, 0

            self.lastMouseX, self.lastMouseY = x, y
        else:
            x, y, dx, dy = 0, 0, 0, 0

        self.win.movePointer(0, int(self.win.getProperties().getXSize() / 2),
                             int(self.win.getProperties().getYSize() / 2))
        self.lastMouseX, self.lastMouseY = 0, 0

        # scale position and delta to pixels for user
        w, h = self.win.getSize()

        # rotate camera by delta
        base.camera.setH(base.camera, dx * -800 * dt)
        base.camera.setP(base.camera, dy * 800 * dt)
        return Task.cont

    def remove_old_missles(self):
        global missle_total
        dt = globalClock.getDt()  # delta t per frame
        for missle in missle_total:
            if missle.ttl <= 0:
                render.clearLight(missle.plnp)
                missle.core.removeNode()
            else:
                missle.ttl -= dt
        return Task.cont

    def pointballManager(self):
        global pointball_total
        dt = globalClock.getDt()  # delta t per frame
        for pointball in pointball_total:
            # Animate the size
            scale_xyz = pointball.one.getScale()
            time = pointball.ttl_max - pointball.ttl
            dampened_cos = pointball.max_size * math.exp(
                0.36 * -time) * math.cos(0.5 * math.pi * time)
            dampened_sin = pointball.max_size * math.exp(
                0.36 * -time) * math.sin(0.5 * math.pi * time)
            pointball.one.setScale(dampened_cos, dampened_cos, dampened_cos)
            pointball.two.setScale(dampened_sin, dampened_sin, dampened_sin)
            # Move towards player if in range
            try:
                if pointball.center.getDistance(
                        base.camera) < pointball.attraction_distance:
                    cam_2_ball = pointball.center.getPos(base.camera)
                    total = cam_2_ball[0] + cam_2_ball[1] + cam_2_ball[2]
                    percent_xyz = [cam_2_ball[i] / total for i in range(0, 3)]
                    pointball.center.setPos(
                        base.camera, cam_2_ball[0] - 20000 * percent_xyz[0],
                        cam_2_ball[1] - 20000 * percent_xyz[1],
                        cam_2_ball[2] - 20000 * percent_xyz[2])
            except:
                pass
            # Remove Old PointBalls
            if pointball.ttl <= 0:
                render.clearLight(pointball.plnp)
                pointball.center.removeNode()
            else:
                pointball.ttl -= dt
        #for index in range(0,len(pointball_total)- 1):
        #    if pointball_total[index].ttl <= 0:
        #        del pointball_total[index]
        return Task.cont

    def death_task(self):
        camera_hpr = base.camera.getHpr()
        dt = globalClock.getDt()
        h_speed = float(base.camera.getTag("h_speed"))
        p_speed = float(base.camera.getTag("p_speed"))
        r_speed = float(base.camera.getTag("r_speed"))
        base.camera.setHpr(camera_hpr[0] + h_speed * dt,
                           camera_hpr[1] + p_speed * dt,
                           camera_hpr[2] + r_speed * dt)
        base.camera.setX(base.camera.getX() + 4000 * dt)
        return Task.cont

    ##### // Colision Functions \\ #####
    def shot_asteroid(self, collision_entry):
        global score_list
        global pointball_total
        global pointball_value

        #Remove the missle
        missle = collision_entry.getFromNodePath()
        try:
            render.clearLight(missle.parent.find("**/plight"))
        except:
            pass
        missle.removeNode()
        # Gather large asteroid info so still accesable after deleted
        # Note: na is short for "new_asteroid", has is "hit_asteroid_size"
        hit_asteroid = collision_entry.getIntoNodePath()
        hap = hit_asteroid.parent.getPos()
        has = hit_asteroid.parent.getTag("size")
        # Remove asteroid from list
        for index in range(0, len(asteroid_total) - 1):
            if asteroid_total[index].name == hit_asteroid.name:
                del asteroid_total[index]
                break
        # Delete before smaller asteoids are created to allow for asteroid-into-asteroid collisions
        hit_asteroid.parent.removeNode()
        # If small asteroid, just delete, if not create two of smaller size
        if not (has == "small"):
            # Generate 2 asteroids at oposite poistions (shimmy) within the larger asteoid, and oposite directions
            for index in range(0, 2):
                na = extra_smallasteroids.pop(
                ) if has == "medium" else extra_mediumasteroids.pop()
                # First asteroid can be random pos & direction
                if index == 0:
                    shimmy = [
                        na.radius * random.randrange(-1, 2, 2),
                        na.radius * random.randrange(-1, 2, 2),
                        na.radius * random.randrange(-1, 2, 2)
                    ]
                    spawn_point = [
                        hap[0] + shimmy[0], hap[1] + shimmy[1],
                        hap[2] + shimmy[2]
                    ]
                    future_location = False
                # Second asteroid should be oposite pos & direction of asteroid 1
                else:
                    spawn_point = [
                        hap[0] + shimmy[0] * -1, hap[1] + shimmy[1] * -1,
                        hap[2] + shimmy[2] * -1
                    ]
                    future_location = LPoint3(future_location[0] * -1,
                                              future_location[1] * -1,
                                              future_location[2] * -1)
                # Add asteroid to the game. This code is the same for both asteroids
                pl3_spawn = LPoint3(spawn_point[0], spawn_point[1],
                                    spawn_point[2])
                na.add_togame(pl3_spawn, future_location)
                na.np.setTag("Created", "True")
                future_location = na.future_location
                base.cTrav.addCollider(na.c_np, self.collHandEvent)
                asteroid_total.append(na)
            # Create more asteroid for the ones we just deleted
            for i in range(0, 2):
                if na.size == "small":
                    extra_smallasteroids.insert(0, Asteroid("small"))
                else:
                    extra_mediumasteroids.insert(0, Asteroid("medium"))
        else:
            # Create the point ball
            current_time = int(time.time())
            new_pointball_value = max(100 - (current_time - pointball_value),
                                      20)
            pointball = PointBall(hap, new_pointball_value)
            pointball_total.append(pointball)
            pointball_value = current_time
            base.cTrav.addCollider(pointball.c_np, self.collHandEvent)
            # Add a new asteroid to the scene to
            asteroid = Asteroid()
            asteroid_total.insert(0, asteroid)
            base.cTrav.addCollider(asteroid.c_np, self.collHandEvent)
            asteroid.add_togame(
                asteroid.get_sphere_points(asteroid_spawn_distance,
                                           base.camera))

    def end_game(self, collision_entry):
        global is_living
        global fog_quality
        is_living = False
        asteroid = collision_entry.getIntoNodePath()
        # Make the world red
        self.fog.setColor(0.5, 0, 0)
        self.fog.setExpDensity(fog_quality)
        self.setBackgroundColor(0.5, 0, 0, 1)
        # Set random spin upon death. This is called in the death_task
        base.camera.setTag("h_speed", str(random.randrange(0, 10)))
        base.camera.setTag("p_speed", str(random.randrange(0, 10)))
        base.camera.setTag("r_speed", str(random.uniform(0, 5)))
        # Stop unused tasks in death
        taskMgr.remove("Rotate player in hpr")
        taskMgr.remove("Score")
        taskMgr.remove("Move the Player in xyz")
        self.accept('mouse1', self.do_null)
        # Move player and look so player gets to see their killer
        base.camera.setX(base.camera.getX() +
                         int(asteroid.parent.getTag("radius")) * 2)
        base.camera.lookAt(asteroid)
        # Start the death spiral + death text
        taskMgr.add(Begin.death_task, "Death Spin")
        self.death_text = OnscreenText(
            text="Your Spaceship has Crashed !\nPress [Escape]",
            font=thunderstrike,
            parent=base.aspect2d,
            scale=0.1,
            align=TextNode.ACenter,
            pos=(0, 0),
            fg=(1, 1, 1, 1),
            shadow=(0, 0, 0, 0.5))
        self.death_text.reparentTo(aspect2d)

    ##### // Developement Functions \\ #####
    def stop_moving(self):
        global spaceship_speed_x
        global spaceship_speed_y
        global spaceship_speed_z
        spaceship_speed_x = 0
        spaceship_speed_y = 0
        spaceship_speed_z = 0
        print(f"camera hpr {base.camera.getHpr()}")
        print(f"camera pos {base.camera.getPos()}")

    def angle1(self):
        print("0,0,0")
        base.camera.setHpr(0, 0, 0)

    def angle2(self):
        print("90,0,0")
        base.camera.setHpr(90, 0, 0)

    def angle3(self):
        print("180,0,0")
        base.camera.setHpr(180, 0, 0)

    def angle4(self):
        print("270,0,0")
        base.camera.setHpr(270, 0, 0)

    def angle5(self):
        print("0,-90,0")
        base.camera.setHpr(0, 90, 0)

    def angle6(self):
        print("0,90,0")
        print(base.camera.getHpr())
        base.camera.setHpr(0, -90, 0)

    ##### // Misc Functions \\ #####
    def setKey(self, key, value):
        self.keyMap[key] = value

    def fullscreenToggle(self):
        global fullscreen
        global Frames
        if (not (fullscreen)):
            fullscreen = True
            self.set_windowsettings()
        else:
            fullscreen = False
            self.set_windowsettings()

    def set_windowsettings(self, reset_window=False):
        global fullscreen
        global cursor_hidden
        wp = WindowProperties()
        wp.setCursorHidden(cursor_hidden)
        base.setFrameRateMeter(Frames)
        wp.setFullscreen(fullscreen)
        wp.setSize(resolution)
        self.win.requestProperties(wp)
        if cursor_hidden:
            wp.setMouseMode(WindowProperties.M_relative)
        else:
            wp.setMouseMode(0)
        if reset_window:
            base.openMainWindow()
            base.graphicsEngine.openWindows()

    def framesToggle(self):
        global Frames
        if (Frames):
            base.setFrameRateMeter(False)
            Frames = False
        else:
            base.setFrameRateMeter(True)
            Frames = True

    def translate(self, value, leftMin, leftMax, rightMin, rightMax):
        # Scale value from input range to output range
        leftSpan = leftMax - leftMin
        rightSpan = rightMax - rightMin
        valueScaled = float(value - leftMin) / float(leftSpan)
        return rightMin + (valueScaled * rightSpan)