text: from toontown.estate import DistributedPlantBase
  from direct.interval.IntervalGlobal import *
  from direct.directnotify import DirectNotifyGlobal
  from direct.showbase import PythonUtil
  from toontown.toonbase import ToontownBattleGlobals
  from toontown.toontowngui import TTDialog
  from toontown.toontowngui.TeaserPanel import TeaserPanel
  from toontown.toonbase import TTLocalizer
  import GardenGlobals
  import HouseGlobals
  from direct.task import Task
  from panda3d.core import *
  from otp.otpbase import OTPGlobals
  from toontown.estate import DistributedLawnDecor
  DIRT_AS_WATER_INDICATOR = True
  
  class DistributedGagTree(DistributedPlantBase.DistributedPlantBase):
      notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGagTree')
  
      def __init__(self, cr):
          DistributedPlantBase.DistributedPlantBase.__init__(self, cr)
          base.tree = self
          self.collSphereRadius = 4.2
          self.confirmDialog = None
          self.resultDialog = None
          self.dirtMound = None
          self.sandMound = None
          self.needToPlant = 0
          self.needToLoad = 0
          self.backupFruits = []
          self.signHasBeenStuck2Ground = False
          self._teaserPanel = None
          self.setName('DistributedGagTree')
          return
  
      def delete(self):
          DistributedPlantBase.DistributedPlantBase.delete(self)
          if self._teaserPanel:
              self._teaserPanel.destroy()
              self._teaserPanel = None
          del self.prop
          del self.prop2
          del self.dirtMound
          del self.sandMound
          self.signModel.removeNode()
          self.signModel = None
          return
  
      def setTypeIndex(self, typeIndex):
          DistributedPlantBase.DistributedPlantBase.setTypeIndex(self, typeIndex)
          track, level = GardenGlobals.getTreeTrackAndLevel(typeIndex)
          self.gagTrack = track
          self.gagLevel = level
          invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons')
          propName = ToontownBattleGlobals.AvPropsNew[track][level]
          self.prop = invModel.find('**/' + propName)
          self.prop.setScale(7)
          invModel.removeNode()
          invModel2 = loader.loadModel('phase_3.5/models/gui/inventory_icons')
          propName = ToontownBattleGlobals.AvPropsNew[track][level]
          self.prop2 = invModel2.find('**/' + propName)
          self.prop2.setScale(7)
          self.filename = self.attributes['filename']
          self.maxFruit = self.attributes['maxFruit']
          if hasattr(self, 'needToLoad'):
              if self.needToLoad:
                  self.loadModel()
  
      def loadModel(self):
          if not hasattr(self, 'filename'):
              self.needToLoad = 1
              return
          if not self.rotateNode:
              self.rotateNode = self.plantPath.attachNewNode('rotate')
          all = loader.loadModel(self.filename)
          self.modelName = self.getModelName()
          if self.isWilted():
              self.modelName += '_wilt'
          self.model = all.find('**/' + self.modelName)
          all.detachNode()
          shadow = self.model.find('**/shadow1')
          if shadow:
              shadow.hide()
          self.model.reparentTo(self.rotateNode)
          if self.isFruiting() and not self.isWilted():
              self.fruits = []
              for i in xrange(1, self.maxFruit + 1):
                  pos = self.model.find('**/locator' + str(i))
                  if pos and not pos.isEmpty():
                      fruit = self.prop.copyTo(self.model)
                      fruit.setPos(pos, 0, 0, 0)
                      fruit.setScale(13)
                      self.fruits.append(fruit)
  
              self.createBackupFruits()
          if DIRT_AS_WATER_INDICATOR:
              self.dirtMound = loader.loadModel('phase_5.5/models/estate/dirt_mound')
              self.dirtMound.reparentTo(self.model)
              self.sandMound = loader.loadModel('phase_5.5/models/estate/sand_mound')
              self.sandMound.reparentTo(self.model)
          self.adjustGrowth()
          self.signModel = loader.loadModel('phase_5.5/models/estate/garden_sign.bam')
          self.signModel.setPos(3.5, 0, 0.025)
          self.signModel.reparentTo(self.rotateNode)
          owner = self.getOwnerIndex()
          color = HouseGlobals.houseColors[owner]
          for geomName in ('sign', 'sign1'):
              sign = self.signModel.find('**/' + geomName)
              if sign:
                  sign.setColor(*color)
  
          self.prop.setPos(0.1, -0.17, 1.63)
          self.prop.reparentTo(self.signModel)
          self.prop2.setPos(0.15, 0.17, 1.63)
          self.prop2.setH(self.prop.getH() + 180)
          self.prop2.reparentTo(self.signModel)
          self.needToLoad = 0
          if self.needToPlant:
              self.stickParts()
  
      def setupShadow(self):
          DistributedPlantBase.DistributedPlantBase.setupShadow(self)
          self.adjustGrowth()
  
      def makeMovieNode(self):
          self.movieNode = self.rotateNode.attachNewNode('moviePos')
          self.movieNode.setPos(0, -5, 0)
          self.createBackupFruits()
  
      def handlePicking(self):
          messenger.send('wakeup')
          if self.isFruiting() and self.canBeHarvested():
              if self.velvetRoped():
                  self._teaserPanel = TeaserPanel(pageName='pickGags')
                  localAvatar._gagTreeVelvetRoped = None
              else:
                  self.startInteraction()
                  self.doHarvesting()
              return
          fullName = self.name
          text = TTLocalizer.ConfirmRemoveTree % {'tree': fullName}
          if self.hasDependentTrees():
              text += TTLocalizer.ConfirmWontBeAbleToHarvest
          self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=text, command=self.confirmCallback)
          self.confirmDialog.show()
          self.startInteraction()
          return
  
      def confirmCallback(self, value):
          self.confirmDialog.destroy()
          self.confirmDialog = None
          if value > 0:
              self.doPicking()
          else:
              self.finishInteraction()
          return
  
      def doPicking(self):
          if not self.canBePicked():
              return
          self.sendUpdate('removeItem', [])
  
      def createBackupFruits(self):
          if not hasattr(self, 'fruits'):
              return
          if not self.fruits:
              return
          if not hasattr(self, 'movieNode'):
              return
          if not self.movieNode:
              return
          if self.movieNode.isEmpty():
              return
          if not self.signHasBeenStuck2Ground:
              return
          if not self.backupFruits:
              for fruit in self.fruits:
                  newFruit = fruit.copyTo(render)
                  newFruit.setPos(fruit.getPos(render))
                  newFruit.setH(self.movieNode.getH(render))
                  newFruit.hide()
                  self.backupFruits.append(newFruit)
  
      def clearBackupFruits(self):
          self.backupFruits = []
  
      def doHarvesting(self):
          if not self.canBePicked():
              return
          if hasattr(self, 'backupFruits'):
              for fruit in self.backupFruits:
                  fruit.show()
  
          self.sendUpdate('requestHarvest', [])
  
      def getTrack(self):
          return self.gagTrack
  
      def getGagLevel(self):
          return self.gagLevel
  
      def setWaterLevel(self, waterLevel):
          self.waterLevel = waterLevel
          self.adjustWaterIndicator()
  
      def setGrowthLevel(self, growthLevel):
          self.growthLevel = growthLevel
          if self.model:
              newModelName = self.getModelName()
              if True:
                  self.model.removeNode()
                  self.loadModel()
                  self.adjustWaterIndicator()
                  self.stick2Ground()
              else:
                  self.adjustGrowth()
  
      def adjustGrowth(self):
          newScale = self.growthLevel + 1
          if newScale > 1:
              newScale = 1
          shadowScale = 2.5
          collScale = 1.5
          if self.isSeedling():
              shadowScale = 1
              collScale = 1
          if self.shadowJoint:
              self.shadowJoint.setScale(shadowScale)
          if DIRT_AS_WATER_INDICATOR:
              dirtMoundScale = shadowScale * 1.5
              dirtMoundDepth = 2.0
              if self.isEstablished():
                  dirtMoundScale = shadowScale * 1.2
              self.dirtMound.setScale(dirtMoundScale, dirtMoundScale, dirtMoundDepth)
              self.sandMound.setScale(dirtMoundScale, dirtMoundScale, dirtMoundDepth)
              self.adjustWaterIndicator()
  
      def setWilted(self, wilted):
          self.wilted = wilted
  
      def isWilted(self):
          return self.wilted
  
      def setMovie(self, mode, avId):
          if mode == GardenGlobals.MOVIE_HARVEST:
              self.doHarvestTrack(avId)
          elif mode == GardenGlobals.MOVIE_WATER:
              self.doWaterTrack(avId)
          elif mode == GardenGlobals.MOVIE_FINISHPLANTING:
              self.doFinishPlantingTrack(avId)
          elif mode == GardenGlobals.MOVIE_REMOVE:
              self.doDigupTrack(avId)
  
      def doFinishPlantingTrack(self, avId):
          toon = base.cr.doId2do.get(avId)
          if not toon:
              return
          self.finishMovies()
          self.movie = Sequence()
          if self.model:
              self.model.setTransparency(1)
              self.model.setAlphaScale(0)
              self.movie.append(LerpFunc(self.model.setAlphaScale, fromData=0, toData=1, duration=3))
          if self.signModel:
              self.signModel.hide()
              self.movie.append(Func(self.signModel.show))
              self.movie.append(LerpScaleInterval(self.signModel, 1, 1, 0))
          self.movie.append(Func(toon.loop, 'neutral'))
          if avId == localAvatar.doId:
              self.movie.append(Func(self.finishInteraction))
              self.movie.append(Func(self.movieDone))
              self.movie.append(Func(self.doResultDialog))
          self.movie.start()
  
      def doHarvestTrack(self, avId):
          toon = base.cr.doId2do.get(avId)
          if not toon:
              return
          self.finishMovies()
          moveTrack = self.generateToonMoveTrack(toon)
          harvestTrack = self.generateHarvestTrack(toon)
          self.movie = Sequence(self.startCamIval(avId), moveTrack, harvestTrack, self.stopCamIval(avId))
          if avId == localAvatar.doId:
              self.movie.append(Func(self.finishInteraction))
              self.movie.append(Func(self.movieDone))
          self.movie.start()
  
      def setupShadow(self):
          if DIRT_AS_WATER_INDICATOR:
              pass
          else:
              DistributedPlantBase.DistributedPlantBase.setupShadow(self)
  
      def generateHarvestTrack(self, toon):
          pos = toon.getPos(render)
          pos.setZ(pos.getZ() + 2)
          fruitTrack = Parallel()
          for fruit in self.backupFruits:
              fruitTrack.append(Sequence(Func(fruit.show), LerpPosInterval(fruit, 1.5, pos, startPos=Point3(fruit.getX(), fruit.getY(), fruit.getZ() + self.model.getZ())), Func(fruit.removeNode)))
  
          self.fruits = None
          harvestTrack = Sequence(fruitTrack, Func(self.clearBackupFruits))
          return harvestTrack
  
      def adjustWaterIndicator(self):
          DistributedPlantBase.DistributedPlantBase.adjustWaterIndicator(self)
          if self.dirtMound:
              curWaterLevel = self.waterLevel
              if curWaterLevel > self.maxWaterLevel:
                  curWaterLevel = self.maxWaterLevel
              if curWaterLevel > 0:
                  darkestColorScale = 0.4
                  lightestColorScale = 1.0
                  scaleRange = lightestColorScale - darkestColorScale
                  scaleIncrement = scaleRange / self.maxWaterLevel
                  darker = lightestColorScale - scaleIncrement * curWaterLevel
                  self.dirtMound.setColorScale(darker, darker, darker, 1.0)
                  self.sandMound.hide()
                  self.dirtMound.show()
              else:
                  self.sandMound.show()
                  self.dirtMound.hide()
  
      def stickParts(self):
          if not hasattr(self, 'signModel'):
              self.needToPlant = 1
              return Task.done
          if self.signModel.isEmpty():
              return Task.done
          testPath = NodePath('testPath')
          testPath.reparentTo(render)
          cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0)
          cRayNode = CollisionNode(self.uniqueName('estate-FloorRay'))
          cRayNode.addSolid(cRay)
          cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask)
          cRayNode.setIntoCollideMask(BitMask32.allOff())
          cRayNodePath = testPath.attachNewNode(cRayNode)
          queue = CollisionHandlerQueue()
          picker = CollisionTraverser()
          picker.addCollider(cRayNodePath, queue)
          testPath.setPos(self.signModel.getX(render), self.signModel.getY(render), 0)
          picker.traverse(render)
          if queue.getNumEntries() > 0:
              queue.sortEntries()
              for index in xrange(queue.getNumEntries()):
                  entry = queue.getEntry(index)
                  if DistributedLawnDecor.recurseParent(entry.getIntoNode(), 'terrain_DNARoot'):
                      self.signModel.wrtReparentTo(render)
                      self.signModel.setZ(entry.getSurfacePoint(render)[2] + self.stickUp + 0.1)
                      self.signModel.wrtReparentTo(self.rotateNode)
                      self.signHasBeenStuck2Ground = True
                      self.createBackupFruits()
                      return Task.done
  
          return Task.done
  
      def canBeHarvested(self):
          if not base.cr.isPaid():
              if self.velvetRoped():
                  if hasattr(localAvatar, '_gagTreeVelvetRoped'):
                      return False
          myTrack, myLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex)
          levelsInTrack = []
          levelTreeDict = {}
          allGagTrees = base.cr.doFindAll('DistributedGagTree')
          for gagTree in allGagTrees:
              if gagTree.getOwnerId() == localAvatar.doId:
                  curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(gagTree.typeIndex)
                  if curTrack == myTrack:
                      levelsInTrack.append(curLevel)
                      levelTreeDict[curLevel] = gagTree
  
          for levelToTest in xrange(myLevel):
              if levelToTest not in levelsInTrack:
                  return False
              curTree = levelTreeDict[levelToTest]
              if not curTree.isGTEFullGrown():
                  return False
  
          return True
  
      def hasDependentTrees(self):
          myTrack, myLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex)
          allGagTrees = base.cr.doFindAll('DistributedGagTree')
          for gagTree in allGagTrees:
              if gagTree.getOwnerId() == localAvatar.doId:
                  curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(gagTree.typeIndex)
                  if curTrack == myTrack:
                      if myLevel < curLevel:
                          return True
  
          return False
  
      def doResultDialog(self):
          self.startInteraction()
          curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex)
          species = GardenGlobals.getTreeTypeIndex(curTrack, curLevel)
          treeName = GardenGlobals.PlantAttributes[species]['name']
          stringToShow = TTLocalizer.getResultPlantedSomethingSentence(treeName)
          self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=stringToShow, command=self.resultsCallback)
  
      def resultsCallback(self, value):
          if self.resultDialog:
              self.resultDialog.destroy()
              self.resultDialog = None
          self.finishInteraction()
          return
  
      def velvetRoped(self):
          return not base.cr.isPaid() and ToontownBattleGlobals.gagIsPaidOnly(self.gagTrack, self.gagLevel)
  
      def allowedToPick(self):
          retval = True
          if self.velvetRoped():
              retval = False
          return retval
  
      def unlockPick(self):
          retval = True
          toon = base.localAvatar
          inventory = toon.inventory
          load = inventory.totalProps
          maxCarry = toon.getMaxCarry()
          if load >= maxCarry and not self.gagLevel > ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL:
              retval = False
          if inventory.numItem(self.gagTrack, self.gagLevel) >= inventory.getMax(self.gagTrack, self.gagLevel):
              retval = False
          return retval
  