text: from panda3d.core import *
  from direct.task.TaskManagerGlobal import *
  from direct.distributed.ClockDelta import *
  from direct.interval.IntervalGlobal import *
  import GoonGlobals
  from direct.task.Task import Task
  from toontown.toonbase import ToontownGlobals
  from otp.otpbase import OTPGlobals
  from toontown.coghq import DistributedCashbotBossObjectAI, CraneLeagueGlobals
  from direct.showbase import PythonUtil
  import DistributedGoonAI
  import math
  import random
  
  class DistributedCashbotBossGoonAI(DistributedGoonAI.DistributedGoonAI, DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI):
      legLength = 10
      directionTable = [(0, 15),
       (10, 10),
       (-10, 10),
       (20, 8),
       (-20, 8),
       (40, 5),
       (-40, 5),
       (60, 4),
       (-60, 4),
       (80, 3),
       (-80, 3),
       (120, 2),
       (-120, 2),
       (180, 1)]
      offMask = BitMask32(0)
      onMask = CollisionNode.getDefaultCollideMask()
  
      def __init__(self, air, boss):
          DistributedGoonAI.DistributedGoonAI.__init__(self, air, 0)
          DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.__init__(self, air, boss)
          cn = CollisionNode('tubeNode')
          self.tube = CollisionTube(0, 0, 0, 0, 0, 0, 2)
          cn.addSolid(self.tube)
          self.tubeNode = cn
          self.tubeNodePath = self.attachNewNode(self.tubeNode)
          self.feelers = []
          cn = CollisionNode('feelerNode')
          self.feelerLength = self.legLength * 1.5
          feelerStart = 1
          for heading, weight in self.directionTable:
              rad = deg2Rad(heading)
              x = -math.sin(rad)
              y = math.cos(rad)
              seg = CollisionSegment(x * feelerStart, y * feelerStart, 0, x * self.feelerLength, y * self.feelerLength, 0)
              cn.addSolid(seg)
              self.feelers.append(seg)
  
          cn.setIntoCollideMask(self.offMask)
          self.feelerNodePath = self.attachNewNode(cn)
          self.isWalking = 0
          self.cTrav = CollisionTraverser('goon')
          self.cQueue = CollisionHandlerQueue()
          self.cTrav.addCollider(self.feelerNodePath, self.cQueue)
  
      def requestBattle(self, pauseTime):
          avId = self.air.getAvatarIdFromSender()
          avatar = self.air.doId2do.get(avId)
          if avatar:
              self.boss.damageToon(avatar, self.strength)
          DistributedGoonAI.DistributedGoonAI.requestBattle(self, pauseTime)
  
      def sendMovie(self, type, avId = 0, pauseTime = 0):
          if type == GoonGlobals.GOON_MOVIE_WALK:
              self.demand('Walk')
          elif type == GoonGlobals.GOON_MOVIE_BATTLE:
              self.demand('Battle')
          elif type == GoonGlobals.GOON_MOVIE_STUNNED:
              self.demand('Stunned')
          elif type == GoonGlobals.GOON_MOVIE_RECOVERY:
              self.demand('Recovery')
          else:
              self.notify.warning('Ignoring movie type %s' % type)
  
      def __chooseTarget(self, extraDelay = 0):
          direction = self.__chooseDirection()
          if direction == None:
              self.target = None
              self.arrivalTime = None
              self.b_destroyGoon()
              return
          heading, dist = direction
          dist = min(dist, self.legLength)
          targetH = PythonUtil.reduceAngle(self.getH() + heading)
          origH = self.getH()
          h = PythonUtil.fitDestAngle2Src(origH, targetH)
          delta = abs(h - origH)
          turnTime = delta / (self.velocity * 5)
          walkTime = dist / self.velocity
          self.setH(targetH)
          self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0))
          self.departureTime = globalClock.getFrameTime()
          self.arrivalTime = self.departureTime + turnTime + walkTime + extraDelay
          self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime))
          return
  
      def __chooseDirection(self):
          self.tubeNode.setIntoCollideMask(self.offMask)
          self.cTrav.traverse(self.boss.scene)
          self.tubeNode.setIntoCollideMask(self.onMask)
          entries = {}
          self.cQueue.sortEntries()
          for i in xrange(self.cQueue.getNumEntries() - 1, -1, -1):
              entry = self.cQueue.getEntry(i)
              dist = Vec3(entry.getSurfacePoint(self)).length()
              if dist < 1.2:
                  dist = 0
              entries[entry.getFrom()] = dist
  
          netScore = 0
          scoreTable = []
          for i in xrange(len(self.directionTable)):
              heading, weight = self.directionTable[i]
              seg = self.feelers[i]
              dist = entries.get(seg, self.feelerLength)
              score = dist * weight
              netScore += score
              scoreTable.append(score)
  
          if netScore == 0:
              self.notify.info('Could not find a path for %s' % self.doId)
              return None
          s = random.uniform(0, netScore)
          for i in xrange(len(self.directionTable)):
              s -= scoreTable[i]
              if s <= 0:
                  heading, weight = self.directionTable[i]
                  seg = self.feelers[i]
                  dist = entries.get(seg, self.feelerLength)
                  return (heading, dist)
  
          self.notify.warning('Fell off end of weighted table.')
          return (0, self.legLength)
  
      def __startWalk(self):
          if self.arrivalTime == None:
              return
          now = globalClock.getFrameTime()
          availableTime = self.arrivalTime - now
          if availableTime > 0:
              point = self.getRelativePoint(self.boss.scene, self.target)
              self.tube.setPointB(point)
              self.node().resetPrevTransform()
              taskMgr.doMethodLater(availableTime, self.__reachedTarget, self.uniqueName('reachedTarget'))
              self.isWalking = 1
          else:
              self.__reachedTarget(None)
          return
  
      def __stopWalk(self, pauseTime = None):
          if self.isWalking:
              taskMgr.remove(self.uniqueName('reachedTarget'))
              if pauseTime == None:
                  now = globalClock.getFrameTime()
                  t = (now - self.departureTime) / (self.arrivalTime - self.departureTime)
              else:
                  t = pauseTime / (self.arrivalTime - self.departureTime)
              t = min(t, 1.0)
              pos = self.getPos()
              self.setPos(pos + (self.target - pos) * t)
              self.tube.setPointB(0, 0, 0)
              self.isWalking = 0
          return
  
      def __reachedTarget(self, task):
          self.__stopWalk()
          self.__chooseTarget()
          self.__startWalk()
  
      def __recoverWalk(self, task):
          self.demand('Walk')
          return Task.done
  
      def doFree(self, task):
          DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.doFree(self, task)
          self.demand('Walk')
          return Task.done
  
      def requestStunned(self, pauseTime):
          avId = self.air.getAvatarIdFromSender()
  
          if avId not in self.air.doId2do:
              return
  
          av = self.air.doId2do[avId]
          if av.getHp() <= 0:
              return
          if avId not in self.boss.involvedToons:
              return
          if self.state == 'Stunned' or self.state == 'Grabbed':
              return
          self.__stopWalk(pauseTime)
          self.boss.makeTreasure(self)
          self.boss.d_updateGoonsStomped(avId)
          comboTracker = self.boss.comboTrackers[avId]
          comboTracker.incrementCombo(comboTracker.combo+1)
          DistributedGoonAI.DistributedGoonAI.requestStunned(self, pauseTime)
  
      def getMinImpact(self):
          return CraneLeagueGlobals.MIN_GOON_IMPACT
  
      def hitBoss(self, impact, craneId):
          avId = self.air.getAvatarIdFromSender()
          self.validate(avId, 1.0 >= impact >= 0, 'invalid hitBoss impact %s' % impact)
          if avId not in self.boss.involvedToons:
              return
  
          if impact <= self.getMinImpact():
              self.boss.d_updateLowImpactHits(avId)
              return
  
          avatar = self.air.doId2do.get(avId)
          if self.state == 'Dropped' or self.state == 'Grabbed':
              if not self.boss.heldObject:
                  damage = int(impact * 25 * self.scale)
                  self.boss.recordHit(max(damage, 2), impact, craneId)
          self.b_destroyGoon()
  
      def d_setTarget(self, x, y, h, arrivalTime):
          self.sendUpdate('setTarget', [x,
           y,
           h,
           arrivalTime])
  
      def d_destroyGoon(self):
          self.sendUpdate('destroyGoon')
  
      def b_destroyGoon(self):
          self.d_destroyGoon()
          self.destroyGoon()
  
      def destroyGoon(self):
          self.demand('Off')
          if self in self.boss.goons:
              self.boss.goons.remove(self)
  
      def enterOff(self):
          self.tubeNodePath.stash()
          self.feelerNodePath.stash()
  
      def exitOff(self):
          self.tubeNodePath.unstash()
          self.feelerNodePath.unstash()
  
      def enterGrabbed(self, avId, craneId):
          crane = simbase.air.doId2do.get(craneId)
          DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.enterGrabbed(self, avId, craneId)
          taskMgr.remove(self.taskName('recovery'))
          taskMgr.remove(self.taskName('resumeWalk'))
  
      def enterWalk(self):
          self.avId = 0
          self.craneId = 0
          self.__chooseTarget()
          self.__startWalk()
          self.d_setObjectState('W', 0, 0)
  
      def exitWalk(self):
          self.__stopWalk()
  
      def enterEmergeA(self):
          self.avId = 0
          self.craneId = 0
          h = 0
          dist = 15
          pos = self.boss.getPos()
          walkTime = dist / self.velocity
          self.setPosHpr(pos[0], pos[1], pos[2], h, 0, 0)
          self.d_setPosHpr(pos[0], pos[1], pos[2], h, 0, 0)
          self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0))
          self.departureTime = globalClock.getFrameTime()
          self.arrivalTime = self.departureTime + walkTime
          self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime))
          self.__startWalk()
          self.d_setObjectState('a', 0, 0)
          taskMgr.doMethodLater(walkTime, self.__recoverWalk, self.uniqueName('recoverWalk'))
  
      def exitEmergeA(self):
          self.__stopWalk()
          taskMgr.remove(self.uniqueName('recoverWalk'))
  
      def enterEmergeB(self):
          self.avId = 0
          self.craneId = 0
          h = 180
          dist = 15
          pos = self.boss.getPos()
          walkTime = dist / self.velocity
          self.setPosHpr(pos[0], pos[1], pos[2], h, 0, 0)
          self.d_setPosHpr(pos[0], pos[1], pos[2], h, 0, 0)
          self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0))
          self.departureTime = globalClock.getFrameTime()
          self.arrivalTime = self.departureTime + walkTime
          self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime))
          self.__startWalk()
          self.d_setObjectState('b', 0, 0)
          taskMgr.doMethodLater(walkTime, self.__recoverWalk, self.uniqueName('recoverWalk'))
  
      def exitEmergeB(self):
          self.__stopWalk()
          taskMgr.remove(self.uniqueName('recoverWalk'))
  
      def enterBattle(self):
          self.d_setObjectState('B', 0, 0)
  
      def exitBattle(self):
          taskMgr.remove(self.taskName('resumeWalk'))
  
      def enterStunned(self):
          self.d_setObjectState('S', 0, 0)
  
      def exitStunned(self):
          taskMgr.remove(self.taskName('recovery'))
  
      def enterRecovery(self):
          self.d_setObjectState('R', 0, 0)
          taskMgr.doMethodLater(2.0, self.__recoverWalk, self.uniqueName('recoverWalk'))
  
      def exitRecovery(self):
          self.__stopWalk()
          taskMgr.remove(self.uniqueName('recoverWalk'))
  
      def requestWalk(self):
          avId = self.air.getAvatarIdFromSender()
          if avId == self.avId and self.state == 'Stunned':
              craneId, objectId = self.__getCraneAndObject(avId)
              if craneId != 0 and objectId == self.doId:
                  self.demand('Walk', avId, craneId)