text: from pandac.PandaModules import *
  from toontown.toonbase.ToontownGlobals import *
  from direct.interval.IntervalGlobal import *
  from direct.fsm import ClassicFSM, State
  from toontown.safezone import SafeZoneLoader
  import random
  from toontown.launcher import DownloadForceAcknowledge
  from toontown.estate import House
  from toontown.estate import Estate
  from toontown.estate import HouseGlobals
  import random
  import math
  from toontown.coghq import MovingPlatform
  from direct.directnotify import DirectNotifyGlobal
  
  class EstateLoader(SafeZoneLoader.SafeZoneLoader):
      notify = DirectNotifyGlobal.directNotify.newCategory('EstateLoader')
  
      def __init__(self, hood, parentFSM, doneEvent):
          SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent)
          del self.fsm
          self.fsm = ClassicFSM.ClassicFSM('EstateLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'estate', 'house']),
           State.State('estate', self.enterEstate, self.exitEstate, ['quietZone']),
           State.State('house', self.enterHouse, self.exitHouse, ['quietZone']),
           State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['house', 'estate']),
           State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final')
          self.musicFile = 'phase_4/audio/bgm/TC_nbrhood.ogg'
          self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.ogg'
          self.dnaFile = 'phase_5.5/dna/estate_1.pdna'
          self.safeZoneStorageDNAFile = None
          self.cloudSwitch = 0
          self.id = MyEstate
          self.estateOwnerId = None
          self.branchZone = None
          self.houseDoneEvent = 'houseDone'
          self.estateDoneEvent = 'estateDone'
          self.enteredHouse = None
          self.houseNode = [None] * 6
          self.houseModels = [None] * HouseGlobals.NUM_HOUSE_TYPES
          self.houseId2house = {}
          self.barrel = None
          self.clouds = []
          self.cloudTrack = None
          self.sunMoonNode = None
          self.fsm.enterInitialState()
  
      def load(self):
          SafeZoneLoader.SafeZoneLoader.load(self)
          self.music = base.loader.loadMusic('phase_4/audio/bgm/TC_nbrhood.ogg')
          self.underwaterSound = base.loader.loadSfx('phase_4/audio/sfx/AV_ambient_water.ogg')
          self.swimSound = base.loader.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.ogg')
          self.submergeSound = base.loader.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.ogg')
          self.birdSound = map(base.loader.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg'])
          self.cricketSound = map(base.loader.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.ogg', 'phase_4/audio/sfx/SZ_TC_bird2.ogg', 'phase_4/audio/sfx/SZ_TC_bird3.ogg'])
          if base.goonsEnabled:
              invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons')
              self.invModels = []
              from toontown.toonbase import ToontownBattleGlobals
              for track in xrange(len(ToontownBattleGlobals.AvPropsNew)):
                  itemList = []
                  for item in xrange(len(ToontownBattleGlobals.AvPropsNew[track])):
                      itemList.append(invModel.find('**/' + ToontownBattleGlobals.AvPropsNew[track][item]))
  
                  self.invModels.append(itemList)
  
              invModel.removeNode()
              del invModel
  
      def unload(self):
          self.ignoreAll()
          base.cr.estateMgr.leaveEstate()
          self.estateOwnerId = None
          self.estateZoneId = None
          if self.place:
              self.place.exit()
              self.place.unload()
              del self.place
          del self.underwaterSound
          del self.swimSound
          del self.submergeSound
          del self.birdSound
          del self.cricketSound
          for node in self.houseNode:
              node.removeNode()
  
          del self.houseNode
          for model in self.houseModels:
              model.removeNode()
  
          del self.houseModels
          del self.houseId2house
          if self.sunMoonNode:
              self.sunMoonNode.removeNode()
              del self.sunMoonNode
              self.sunMoonNode = None
          if self.clouds:
              for cloud in self.clouds:
                  cloud[0].removeNode()
                  del cloud[1]
  
              del self.clouds
          if self.barrel:
              self.barrel.removeNode()
          SafeZoneLoader.SafeZoneLoader.unload(self)
  
      def enter(self, requestStatus):
          self.estateOwnerId = requestStatus.get('ownerId', base.localAvatar.doId)
          base.localAvatar.inEstate = 1
          self.loadCloudPlatforms()
          if base.cloudPlatformsEnabled and 0:
              self.setCloudSwitch(1)
          if self.cloudSwitch:
              self.setCloudSwitch(self.cloudSwitch)
          SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus)
  
      def exit(self):
          self.ignoreAll()
          base.cr.cache.flush()
          base.localAvatar.stopChat()
          base.localAvatar.inEstate = 0
          SafeZoneLoader.SafeZoneLoader.exit(self)
  
      def createSafeZone(self, dnaFile):
          SafeZoneLoader.SafeZoneLoader.createSafeZone(self, dnaFile)
          self.loadHouses()
          self.loadSunMoon()
  
      def loadHouses(self):
          for i in xrange(HouseGlobals.NUM_HOUSE_TYPES):
              self.houseModels[i] = loader.loadModel(HouseGlobals.houseModels[i])
  
          for i in xrange(6):
              posHpr = HouseGlobals.houseDrops[i]
              self.houseNode[i] = self.geom.attachNewNode('esHouse_' + str(i))
              self.houseNode[i].setPosHpr(*posHpr)
  
      def loadSunMoon(self):
          self.sun = loader.loadModel('phase_4/models/props/sun.bam')
          self.moon = loader.loadModel('phase_5.5/models/props/moon.bam')
          self.sunMoonNode = self.geom.attachNewNode('sunMoon')
          self.sunMoonNode.setPosHpr(0, 0, 0, 0, 0, 0)
          if self.sun:
              self.sun.reparentTo(self.sunMoonNode)
              self.sun.setY(270)
              self.sun.setScale(2)
              self.sun.setBillboardPointEye()
          if self.moon:
              self.moon.setP(180)
              self.moon.reparentTo(self.sunMoonNode)
              self.moon.setY(-270)
              self.moon.setScale(15)
              self.moon.setBillboardPointEye()
          self.sunMoonNode.setP(30)
  
      def enterEstate(self, requestStatus):
          self.notify.debug('enterEstate: requestStatus = %s' % requestStatus)
          ownerId = requestStatus.get('ownerId')
          if ownerId:
              self.estateOwnerId = ownerId
          zoneId = requestStatus['zoneId']
          self.notify.debug('enterEstate, ownerId = %s, zoneId = %s' % (self.estateOwnerId, zoneId))
          self.accept(self.estateDoneEvent, self.handleEstateDone)
          self.place = Estate.Estate(self, self.estateOwnerId, zoneId, self.fsm.getStateNamed('estate'), self.estateDoneEvent)
          base.cr.playGame.setPlace(self.place)
          self.place.load()
          self.place.enter(requestStatus)
          self.estateZoneId = zoneId
  
      def exitEstate(self):
          self.notify.debug('exitEstate')
          self.ignore(self.estateDoneEvent)
          self.place.exit()
          self.place.unload()
          self.place = None
          base.cr.playGame.setPlace(self.place)
          base.cr.cache.flush()
          return
  
      def handleEstateDone(self, doneStatus = None):
          if not doneStatus:
              doneStatus = self.place.getDoneStatus()
          how = doneStatus['how']
          shardId = doneStatus['shardId']
          hoodId = doneStatus['hoodId']
          zoneId = doneStatus['zoneId']
          avId = doneStatus.get('avId', -1)
          ownerId = doneStatus.get('ownerId', -1)
          if shardId != None or hoodId != MyEstate:
              self.notify.debug('estate done, and we are backing out to a different hood/shard')
              self.notify.debug('hoodId = %s, avId = %s' % (hoodId, avId))
              self.doneStatus = doneStatus
              messenger.send(self.doneEvent)
              return
          if how in ['tunnelIn',
           'teleportIn',
           'doorIn',
           'elevatorIn']:
              self.notify.debug('staying in estateloader')
              self.fsm.request('quietZone', [doneStatus])
          else:
              self.notify.error('Exited hood with unexpected mode %s' % how)
          return
  
      def enterHouse(self, requestStatus):
          ownerId = requestStatus.get('ownerId')
          if ownerId:
              self.estateOwnerId = ownerId
          self.acceptOnce(self.houseDoneEvent, self.handleHouseDone)
          self.place = House.House(self, self.estateOwnerId, self.fsm.getStateNamed('house'), self.houseDoneEvent)
          base.cr.playGame.setPlace(self.place)
          self.place.load()
          self.place.enter(requestStatus)
  
      def exitHouse(self):
          self.ignore(self.houseDoneEvent)
          self.place.exit()
          self.place.unload()
          self.place = None
          base.cr.playGame.setPlace(self.place)
          return
  
      def handleHouseDone(self, doneStatus = None):
          if not doneStatus:
              doneStatus = self.place.getDoneStatus()
          shardId = doneStatus['shardId']
          hoodId = doneStatus['hoodId']
          if shardId != None or hoodId != MyEstate:
              self.doneStatus = doneStatus
              messenger.send(self.doneEvent)
              return
          how = doneStatus['how']
          if how in ['tunnelIn',
           'teleportIn',
           'doorIn',
           'elevatorIn']:
              self.fsm.request('quietZone', [doneStatus])
          else:
              self.notify.error('Exited hood with unexpected mode %s' % how)
          return
  
      def handleQuietZoneDone(self):
          status = self.quietZoneStateData.getRequestStatus()
          self.fsm.request(status['where'], [status])
  
      def atMyEstate(self):
          if self.estateOwnerId != None:
              if self.estateOwnerId == base.localAvatar.getDoId():
                  return 1
              else:
                  return 0
          else:
              self.notify.warning("We aren't in an estate")
          return
  
      def setHouse(self, houseId):
          try:
              houseDo = base.cr.doId2do[houseId]
              self.enteredHouse = houseDo.house
          except KeyError:
              self.notify.debug("can't find house: %d" % houseId)
  
      def startCloudPlatforms(self):
          return
          if len(self.clouds):
              self.cloudTrack = self.__cloudTrack()
              self.cloudTrack.loop()
  
      def stopCloudPlatforms(self):
          if self.cloudTrack:
              self.cloudTrack.pause()
              del self.cloudTrack
              self.cloudTrack = None
          return
  
      def __cloudTrack(self):
          track = Parallel()
          for cloud in self.clouds:
              axis = cloud[1]
              pos = cloud[0].getPos(render)
              newPos = pos + axis * 30
              reversePos = pos - axis * 30
              track.append(Sequence(LerpPosInterval(cloud[0], 10, newPos), LerpPosInterval(cloud[0], 20, reversePos), LerpPosInterval(cloud[0], 10, pos)))
  
          return track
  
      def debugGeom(self, decomposed):
          print 'numPrimitives = %d' % decomposed.getNumPrimitives()
          for primIndex in xrange(decomposed.getNumPrimitives()):
              prim = decomposed.getPrimitive(primIndex)
              print 'prim = %s' % prim
              print 'isIndexed = %d' % prim.isIndexed()
              print 'prim.getNumPrimitives = %d' % prim.getNumPrimitives()
              for basicPrim in xrange(prim.getNumPrimitives()):
                  print '%d start=%d' % (basicPrim, prim.getPrimitiveStart(basicPrim))
                  print '%d end=%d' % (basicPrim, prim.getPrimitiveEnd(basicPrim))
  
      def loadOnePlatform(self, version, radius, zOffset, score, multiplier):
          self.notify.debug('loadOnePlatform version=%d' % version)
          cloud = NodePath('cloud-%d-%d' % (score, multiplier))
          cloudModel = loader.loadModel('phase_5.5/models/estate/bumper_cloud')
          cc = cloudModel.copyTo(cloud)
          colCube = cc.find('**/collision')
          colCube.setName('cloudSphere-0')
          dTheta = 2.0 * math.pi / self.numClouds
          cloud.reparentTo(self.cloudOrigin)
          axes = [Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)]
          cloud.setPos(radius * math.cos(version * dTheta), radius * math.sin(version * dTheta), 4 * random.random() + zOffset)
          cloud.setScale(4.0)
          self.clouds.append([cloud, random.choice(axes)])
  
      def loadSkyCollision(self):
          plane = CollisionPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 300)))
          plane.setTangible(0)
          planeNode = CollisionNode('cloudSphere-0')
          planeNode.addSolid(plane)
          self.cloudOrigin.attachNewNode(planeNode)
  
      def loadCloudPlatforms(self):
          self.cloudOrigin = self.geom.attachNewNode('cloudOrigin')
          self.cloudOrigin.setZ(30)
          self.loadSkyCollision()
          self.numClouds = 12
          pinballScore = PinballScoring[PinballCloudBumperLow]
          for i in xrange(12):
              self.loadOnePlatform(i, 40, 0, pinballScore[0], pinballScore[1])
  
          pinballScore = PinballScoring[PinballCloudBumperMed]
          for i in xrange(12):
              self.loadOnePlatform(i, 60, 40, pinballScore[0], pinballScore[1])
  
          pinballScore = PinballScoring[PinballCloudBumperHigh]
          for i in xrange(12):
              self.loadOnePlatform(i, 20, 80, pinballScore[0], pinballScore[1])
  
          self.cloudOrigin.stash()
  
      def setCloudSwitch(self, on):
          self.cloudSwitch = on
          if hasattr(self, 'cloudOrigin'):
              if on:
                  self.cloudOrigin.unstash()
              else:
                  self.cloudOrigin.stash()
  