text: import math
  from pandac.PandaModules import CardMaker, TextNode
  from direct.gui.DirectGui import DirectFrame, DirectLabel, DirectButton
  from direct.task import Task
  from toontown.toon import NPCToons
  from toontown.hood import ZoneUtil
  from toontown.toonbase import ToontownGlobals
  from toontown.quest import Quests
  from toontown.suit import SuitPlannerBase
  import QuestMapGlobals
  
  class QuestMap(DirectFrame):
  
      def __init__(self, av, **kw):
          DirectFrame.__init__(self, relief=None, sortOrder=50)
          self.initialiseoptions(QuestMap)
          self.container = DirectFrame(parent=self, relief=None)
          self.marker = DirectFrame(parent=self.container, relief=None)
          self.cogInfoFrame = DirectFrame(parent=self.container, relief=None)
          cm = CardMaker('bg')
          cm.setFrame(-0.5, 0.5, -0.5, 0.5)
          bg = self.cogInfoFrame.attachNewNode(cm.generate())
          bg.setTransparency(1)
          bg.setColor(0.5, 0.5, 0.5, 0.5)
          bg.setBin('fixed', 0)
          self.cogInfoFrame['geom'] = bg
          self.cogInfoFrame['geom_pos'] = (0, 0, 0)
          self.cogInfoFrame['geom_scale'] = (6, 1, 2)
          self.cogInfoFrame.setScale(0.05)
          self.cogInfoFrame.setPos(0, 0, 0.6)
          self.buildingMarkers = []
          self.av = av
          self.wantToggle = False
          if base.config.GetBool('want-toggle-quest-map', True):
              self.wantToggle = True
          self.updateMarker = True
          self.cornerPosInfo = None
          self.hqPosInfo = None
          self.fishingSpotInfo = None
          self.load()
          self.setScale(1.5)
          bg.removeNode()
          self.hoodId = None
          self.zoneId = None
          self.suitPercentage = {}
          for currHoodInfo in SuitPlannerBase.SuitPlannerBase.SuitHoodInfo:
              tracks = currHoodInfo[SuitPlannerBase.SuitPlannerBase.SUIT_HOOD_INFO_TRACK]
              self.suitPercentage[currHoodInfo[SuitPlannerBase.SuitPlannerBase.SUIT_HOOD_INFO_ZONE]] = tracks
  
          return
  
      def load(self):
          gui = loader.loadModel('phase_4/models/questmap/questmap_gui')
          icon = gui.find('**/tt_t_gui_qst_arrow')
          iconNP = aspect2d.attachNewNode('iconNP')
          icon.reparentTo(iconNP)
          icon.setR(90)
          self.marker['geom'] = iconNP
          self.marker['image'] = iconNP
          self.marker.setScale(0.05)
          iconNP.removeNode()
          self.mapOpenButton = DirectButton(image=(gui.find('**/tt_t_gui_qst_mapClose'), gui.find('**/tt_t_gui_qst_mapClose'), gui.find('**/tt_t_gui_qst_mapTryToOpen')), relief=None, pos=(1.25, 0, -0.63), scale=0.205, command=self.show)
          self.mapCloseButton = DirectButton(image=(gui.find('**/tt_t_gui_qst_mapOpen'), gui.find('**/tt_t_gui_qst_mapOpen'), gui.find('**/tt_t_gui_qst_mapTryToClose')), relief=None, pos=(1.25, 0, -0.63), scale=0.205, command=self.hide)
          self.mapOpenButton.hide()
          self.mapCloseButton.hide()
          gui.removeNode()
          icons = loader.loadModel('phase_3/models/gui/cog_icons')
          cIcon = icons.find('**/CorpIcon')
          lIcon = icons.find('**/LegalIcon')
          mIcon = icons.find('**/MoneyIcon')
          sIcon = icons.find('**/SalesIcon')
          cogInfoTextColor = (0.2, 0.2, 0.2, 1)
          textPos = (1.2, -0.2)
          textScale = 0.8
          self.cInfo = DirectLabel(parent=self.cogInfoFrame, text='', text_fg=cogInfoTextColor, text_pos=textPos, text_scale=textScale, geom=cIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None)
          self.cInfo.setPos(-2.2, 0, 0.5)
          self.lInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=lIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None)
          self.lInfo.setPos(-2.2, 0, -0.5)
          self.mInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=mIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None)
          self.mInfo.setPos(0.8, 0, 0.5)
          self.sInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=sIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None)
          self.sInfo.setPos(0.8, 0, -0.5)
          icons.removeNode()
          return
  
      def updateCogInfo(self):
          currPercentage = self.suitPercentage.get(self.zoneId)
          if currPercentage is None:
              return
          self.cInfo['text'] = '%s%%' % currPercentage[0]
          self.lInfo['text'] = '%s%%' % currPercentage[1]
          self.mInfo['text'] = '%s%%' % currPercentage[2]
          self.sInfo['text'] = '%s%%' % currPercentage[3]
          return
  
      def destroy(self):
          self.ignore('questPageUpdated')
          self.mapOpenButton.destroy()
          self.mapCloseButton.destroy()
          del self.mapOpenButton
          del self.mapCloseButton
          DirectFrame.destroy(self)
  
      def putBuildingMarker(self, pos, hpr = (0, 0, 0), mapIndex = None):
          marker = DirectLabel(parent=self.container, text='', text_pos=(-0.05, -0.15), text_fg=(1, 1, 1, 1), relief=None)
          gui = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI')
          icon = gui.find('**/startPartyButton_inactive')
          iconNP = aspect2d.attachNewNode('iconNP')
          icon.reparentTo(iconNP)
          icon.setX(-12.0792 / 30.48)
          icon.setZ(-9.7404 / 30.48)
          marker['text'] = '%s' % mapIndex
          marker['text_scale'] = 0.7
          marker['image'] = iconNP
          marker['image_color'] = (1, 0, 0, 1)
          marker['image_scale'] = 6
          marker.setScale(0.05)
          relX, relY = self.transformAvPos(pos)
          marker.setPos(relX, 0, relY)
          self.buildingMarkers.append(marker)
          iconNP.removeNode()
          gui.removeNode()
          return
  
      def updateQuestInfo(self):
          for marker in self.buildingMarkers:
              marker.destroy()
  
          self.buildingMarkers = []
          dnaStore = base.cr.playGame.dnaStore
          for questIndex in self.av.questPage.quests.keys():
              questDesc = self.av.questPage.quests.get(questIndex)
              if questDesc is None:
                  continue
              mapIndex = questIndex + 1
              questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc
              quest = Quests.getQuest(questId)
              fComplete = quest.getCompletionStatus(self.av, questDesc) == Quests.COMPLETE
              if not fComplete:
                  if quest.getType() == Quests.RecoverItemQuest:
                      if quest.getHolder() == Quests.AnyFish:
                          self.putBuildingMarker(self.fishingSpotInfo, mapIndex=mapIndex)
                      continue
                  elif quest.getType() != Quests.DeliverGagQuest and quest.getType() != Quests.DeliverItemQuest and quest.getType() != Quests.VisitQuest and quest.getType() != Quests.TrackChoiceQuest:
                      continue
              if toNpcId == Quests.ToonHQ:
                  self.putBuildingMarker(self.hqPosInfo, mapIndex=mapIndex)
              else:
                  npcZone = NPCToons.getNPCZone(toNpcId)
                  hoodId = ZoneUtil.getCanonicalHoodId(npcZone)
                  branchId = ZoneUtil.getCanonicalBranchZone(npcZone)
                  if self.hoodId == hoodId and self.zoneId == branchId:
                      for blockIndex in range(dnaStore.getNumBlockTitles()):
                          blockNumber = dnaStore.getTitleBlockAt(blockIndex)
                          zone = dnaStore.getZoneFromBlockNumber(blockNumber)
                          branchZone = zone - zone % 100
                          finalZone = branchZone + 500 + blockNumber
                          buildingType = dnaStore.getBlockBuildingType(blockNumber)
                          if npcZone == finalZone:
                              self.putBuildingMarker(dnaStore.getDoorPosHprFromBlockNumber(blockNumber).getPos(), dnaStore.getDoorPosHprFromBlockNumber(blockNumber).getHpr(), mapIndex=mapIndex)
  
          return
  
      def transformAvPos(self, pos):
          if self.cornerPosInfo is None:
              return (0, 0)
          topRight = self.cornerPosInfo[0]
          bottomLeft = self.cornerPosInfo[1]
          relativeX = (pos.getX() - bottomLeft.getX()) / (topRight.getX() - bottomLeft.getX()) - 0.5
          relativeY = (pos.getY() - bottomLeft.getY()) / (topRight.getY() - bottomLeft.getY()) - 0.5
          return (relativeX, relativeY)
  
      def update(self, task):
          if self.av:
              if self.updateMarker:
                  relX, relY = self.transformAvPos(self.av.getPos())
                  self.marker.setPos(relX, 0, relY)
                  self.marker.setHpr(0, 0, -180 - self.av.getH())
          i = 0
          for buildingMarker in self.buildingMarkers:
              buildingMarker.setScale((math.sin(task.time * 16.0 + i * math.pi / 3.0) + 1) * 0.005 + 0.04)
              i = i + 1
  
          return Task.cont
  
      def updateMap(self):
          if self.av:
              try:
                  hoodId = ZoneUtil.getCanonicalHoodId(self.av.getLocation()[1])
                  zoneId = ZoneUtil.getCanonicalBranchZone(self.av.getLocation()[1])
                  mapsGeom = loader.loadModel('phase_4/models/questmap/%s_maps' % ToontownGlobals.dnaMap[hoodId])
                  mapImage = mapsGeom.find('**/%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId))
                  if not mapImage.isEmpty():
                      self.container['image'] = mapImage
                      self.resetFrameSize()
                      self.cornerPosInfo = QuestMapGlobals.CornerPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId))
                      self.hqPosInfo = QuestMapGlobals.HQPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId))
                      self.fishingSpotInfo = QuestMapGlobals.FishingSpotPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId))
                      self.cogInfoPos = QuestMapGlobals.CogInfoPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId))
                      self.cogInfoFrame.setPos(self.cogInfoPos)
                      self.hide()
                      self.hoodId = hoodId
                      self.zoneId = zoneId
                      self.updateQuestInfo()
                      self.updateCogInfo()
                      taskMgr.add(self.update, 'questMapUpdate')
                  else:
                      self.stop()
                  mapsGeom.removeNode()
              except:
                  self.stop()
  
      def start(self):
          self.container.show()
          self.accept('questPageUpdated', self.updateMap)
          self.handleMarker()
          self.updateMap()
  
      def initMarker(self, task):
          if self.av:
              if not hasattr(base.cr.playGame.getPlace(), 'isInterior') or not base.cr.playGame.getPlace().isInterior:
                  relX, relY = self.transformAvPos(self.av.getPos())
                  self.marker.setPos(relX, 0, relY)
                  self.marker.setHpr(0, 0, -180 - self.av.getH())
              self.marker['geom_scale'] = 1.4 * task.time % 0.5 * 10 + 1
              self.marker['geom_color'] = (1,
               1,
               1,
               0.8 - 1.4 * task.time % 0.5 * 2 / 0.8 + 0.2)
          if task.time < 1:
              return Task.cont
          else:
              self.marker['geom_color'] = (1, 1, 1, 0)
              return Task.done
  
      def show(self):
          taskMgr.add(self.initMarker, 'questMapInit')
          DirectFrame.show(self)
          self.mapOpenButton.hide()
          if self.container['image']:
              self.mapCloseButton.show()
  
      def hide(self):
          taskMgr.remove('questMapInit')
          DirectFrame.hide(self)
          if self.container['image']:
              self.mapOpenButton.show()
          self.mapCloseButton.hide()
  
      def toggle(self):
          if self.isHidden():
              self.show()
          else:
              self.hide()
  
      def obscureButton(self):
          self.mapOpenButton.hide()
          self.mapCloseButton.hide()
  
      def stop(self):
          self.container['image'] = None
          for marker in self.buildingMarkers:
              marker.destroy()
  
          self.buildingMarkers = []
          self.container.hide()
          self.hide()
          self.obscureButton()
          self.ignore('questPageUpdated')
          taskMgr.remove('questMapUpdate')
          return
  
      def handleMarker(self):
          if hasattr(base.cr.playGame.getPlace(), 'isInterior') and base.cr.playGame.getPlace().isInterior:
              self.updateMarker = False
          else:
              self.updateMarker = True
  
      def acceptOnscreenHooks(self):
          if self.wantToggle:
              self.accept(ToontownGlobals.MapHotkey, self.toggle)
          else:
              self.accept(ToontownGlobals.MapHotkeyOn, self.show)
              self.accept(ToontownGlobals.MapHotkeyOff, self.hide)
          self.updateMap()
  
      def ignoreOnscreenHooks(self):
          self.ignore(ToontownGlobals.MapHotkey)
          self.ignore(ToontownGlobals.MapHotkeyOn)
          self.ignore(ToontownGlobals.MapHotkeyOff)
          self.obscureButton()
  