NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

agents.py (37406B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\station\agents\agents.py
      2 import blue
      3 import triui
      4 import carbonui.const as uiconst
      5 import clonegrade
      6 import evetypes
      7 import localization
      8 import log
      9 from stacklesslib.locks import Lock
     10 import telemetry
     11 import types
     12 import uthread
     13 import weakref
     14 from appConst import factionByRace
     15 from carbon.client.script.environment.AudioUtil import PlaySound
     16 from carbon.common.script.sys.service import Service
     17 from carbon.common.script.sys.serviceConst import ROLE_SERVICE, SERVICE_RUNNING
     18 from carbon.common.script.util.format import FmtTimeInterval, FmtDate
     19 from characterdata.schools import get_school, get_school_agent_ids
     20 from collections import defaultdict
     21 from eve.client.script.ui.shared.cloneGrade import ORIGIN_AGENTS
     22 from eve.client.script.ui.station.agents.agentDialogueWindow import AgentDialogueWindow
     23 from eve.client.script.ui.station.agents.missionDetailsWindow import MissionDetailsWindow
     24 from eve.client.script.ui.util import uix
     25 from eve.client.script.ui.util.utilWindows import NamePopup
     26 from eve.common.lib import appConst
     27 from eve.common.script.net import eveMoniker
     28 from eve.common.script.sys.idCheckers import IsStation, IsSolarSystem
     29 from eve.common.script.util.eveFormat import FmtISK
     30 from evedungeons.client.data import GetDungeon
     31 from evemissions.client.const import CAREER_PATH_BY_DIVISION
     32 from evemissions.client.ui.missiongiver import MissionGiver
     33 from evemissions.client.ui.utils import get_agent_conversation_window_id, get_mission_details_window_id
     34 from featuremanagement.client.flag import flag_bool
     35 from globalConfig import GetNewMissionGiverEnabled
     36 from jobboard.client import open_mission
     37 from jobboard.client.feature_flag import is_job_board_available, are_missions_in_job_board_available
     38 from omega.client.clone_grade_svc import get_clone_grade_svc
     39 from player_messaging import PromptOptions, PromptIcon
     40 from player_messaging.client import get_supressed_result, set_suppress_result
     41 from player_messaging.client.ui_message import prompt_player, prompt_player_fsd_dialog
     42 SERVICETYPE_RESEARCH = 'research'
     43 SERVICETYPE_LOCATE = 'locate'
     44 SERVICETYPE_MISSION = 'mission'
     45 SERVICETYPE_OFFER = 'offer'
     46 HERALDRY_ENABLED_KEY = 'are_heraldry_agents_enabled'
     47 HERALDRY_ENABLED_DEFAULT = True
     48 AGENT_TYPES_WITH_NEW_UI = [appConst.agentTypeHeraldry]
     49 AGENT_IDS_WITH_NEW_UI = [appConst.oreEpicArcAgentID,
     50  appConst.AIREpicArcAIRAgentID,
     51  appConst.AIREpicArcAmarrAgentID,
     52  appConst.AIREpicArcCaldariAgentID,
     53  appConst.AIREpicArcGallenteAgentID,
     54  appConst.AIREpicArcMinmatarAgentID]
     55 
     56 class Agents(Service):
     57     __exportedcalls__ = {'OpenDialogueWindow': [],
     58      'RemoteNamePopup': [ROLE_SERVICE],
     59      'GetQuantity': [ROLE_SERVICE],
     60      'PopupSelect': [ROLE_SERVICE],
     61      'YesNo': [ROLE_SERVICE],
     62      'MessageBox': [ROLE_SERVICE],
     63      'SingleChoiceBox': [ROLE_SERVICE],
     64      'CheckCargoCapacity': [ROLE_SERVICE],
     65      'GetAgentByID': [],
     66      'GetAgentsByID': [],
     67      'GetAgentsByStationID': [],
     68      'GetAgentsByCorpID': [],
     69      'GetAgentsByFactionID': [],
     70      'IsCareerAgentStation': [],
     71      'IsCareerAgentSystem': [],
     72      'IsAgent': [],
     73      'PopupMission': [ROLE_SERVICE],
     74      'RemoveOfferFromJournal': [],
     75      'ShowMissionObjectives': [ROLE_SERVICE],
     76      'PopupDungeonShipRestrictionList': [ROLE_SERVICE]}
     77     __guid__ = 'svc.agents'
     78     __servicename__ = 'agents'
     79     __displayname__ = 'Agent Service'
     80     __dependencies__ = ['map']
     81     __notifyevents__ = ['OnAgentMissionChange',
     82      'OnSessionReset',
     83      'OnInteractWith',
     84      'ProcessUIRefresh',
     85      'OnDatacoreBought',
     86      'OnAgentSlashCommandExecuted',
     87      'OnGlobalConfigChanged']
     88     __startupdependencies__ = ['faction']
     89 
     90     def __GetAllAgents(self):
     91         with self._getAllAgentsLock:
     92             if self.allAgents is None:
     93                 self.fetchingAgents = True
     94                 agents = sm.RemoteSvc('agentMgr').GetAgents().Clone()
     95                 self.allAgentsByID = agents.Index('agentID')
     96                 self.allAgentsByStationID = agents.Filter('stationID')
     97                 self.allAgentsBySolarSystemID = agents.Filter('solarsystemID')
     98                 self.allAgentsByCorpID = agents.Filter('corporationID')
     99                 self.allAgentsByFactionID = agents.Filter('factionID')
    100                 self.allAgentsByType = agents.Filter('agentTypeID')
    101                 self.allAgents = agents
    102 
    103     def GetAgentByID(self, agentID):
    104         self.__GetAllAgents()
    105         if agentID in self.allAgentsByID:
    106             return self.allAgentsByID[agentID]
    107 
    108     def GetAgentIDByNameInStation(self, agentName, stationID):
    109         agentsInStation = self.GetAgentsByStationID()[stationID]
    110         for agent in agentsInStation:
    111             if cfg.eveowners.Get(agent.agentID).name == agentName:
    112                 return agent.agentID
    113 
    114     def GetAgentsByID(self):
    115         self.__GetAllAgents()
    116         return self.allAgentsByID
    117 
    118     def GetAgentsByStationID(self):
    119         self.__GetAllAgents()
    120         return self.allAgentsByStationID
    121 
    122     def GetAgentsBySolarSystemID(self):
    123         self.__GetAllAgents()
    124         return self.allAgentsBySolarSystemID
    125 
    126     def GetAgentsByCorpID(self, corpID):
    127         self.__GetAllAgents()
    128         return self.allAgentsByCorpID[corpID]
    129 
    130     def GetAgentsByFactionID(self, factionID):
    131         self.__GetAllAgents()
    132         return self.allAgentsByFactionID[factionID]
    133 
    134     def GetAgentsByType(self, agentTypeID):
    135         self.__GetAllAgents()
    136         return self.allAgentsByType[agentTypeID]
    137 
    138     def GetAllAgents(self):
    139         self.__GetAllAgents()
    140         return self.allAgents
    141 
    142     def _GetCareerPathID(self, agentID):
    143         agentInfo = self.GetAgentByID(agentID)
    144         if agentInfo:
    145             divisionID = agentInfo.divisionID
    146             if divisionID in CAREER_PATH_BY_DIVISION:
    147                 return CAREER_PATH_BY_DIVISION[divisionID]
    148 
    149     def IsCareerAgent(self, agentID):
    150         agentInfo = self.GetAgentByID(agentID)
    151         if agentInfo:
    152             agentTypeID = agentInfo.agentTypeID
    153             return self.IsCareerAgentType(agentTypeID)
    154         return False
    155 
    156     def GetAgentLocation(self, agentID):
    157         agentInfo = self.GetAgentByID(agentID)
    158         if agentInfo:
    159             return agentInfo.stationID or agentInfo.solarsystemID
    160 
    161     def GetAgentConversationWindowName(self, agentID):
    162         careerPathID = self._GetCareerPathID(agentID)
    163         return get_agent_conversation_window_id(careerPathID, agentID)
    164 
    165     def GetMissionJournalWindowName(self, agentID):
    166         careerPathID = self._GetCareerPathID(agentID)
    167         return get_mission_details_window_id(careerPathID, agentID)
    168 
    169     def IsCareerAgentStation(self, stationID):
    170         if stationID is None or not IsStation(stationID):
    171             return False
    172         agentsByStation = self.GetAgentsByStationID()
    173         if stationID in agentsByStation:
    174             for agent in agentsByStation[stationID]:
    175                 if agent.agentTypeID == appConst.agentTypeCareerAgent:
    176                     return True
    177 
    178         return False
    179 
    180     def IsCareerAgentSystem(self, solarSystemID):
    181         if solarSystemID is None or not IsSolarSystem(solarSystemID):
    182             return False
    183         agentsBySolarSystem = self.GetAgentsBySolarSystemID()
    184         if solarSystemID in agentsBySolarSystem:
    185             for agent in agentsBySolarSystem[solarSystemID]:
    186                 if agent.agentTypeID == appConst.agentTypeCareerAgent:
    187                     return True
    188 
    189         return False
    190 
    191     def IsAgent(self, agentID):
    192         self.__GetAllAgents()
    193         return agentID in self.allAgentsByID
    194 
    195     def CheckPrimeCompletedCareerAgentIDs(self, agentIDs):
    196         allPresent = all([ agentID in self._careerAgentsCompletedByID for agentID in agentIDs ])
    197         if not allPresent:
    198             careerAgentsAvailableByID = sm.RemoteSvc('agentMgr').GetCompletedCareerAgentIDs(agentIDs)
    199             self._careerAgentsCompletedByID.update(careerAgentsAvailableByID)
    200 
    201     def IsCareerAgentCompleted(self, agentID):
    202         if agentID not in self._careerAgentsCompletedByID:
    203             self.CheckPrimeCompletedCareerAgentIDs([agentID])
    204         return self._careerAgentsCompletedByID[agentID]
    205 
    206     def IsCareerAgentType(self, agentTypeID):
    207         return agentTypeID == appConst.agentTypeCareerAgent
    208 
    209     def IsEpicArcAgent(self, agentID):
    210         return agentID in self.GetEpicArcAgentIDs()
    211 
    212     def GetEpicArcAgentIDs(self):
    213         return set([ agent.agentID for agent in self.GetAgentsByType(agentTypeID=appConst.agentTypeEpicArcAgent) ])
    214 
    215     def GetClosestAgent(self, agentTypeID = None, divisionID = None, factionID = None, careerPathID = None, onlyIncomplete = False):
    216         factionID = self._GetMyFactionID() if factionID is None else factionID
    217         agentsBySolarSystemID = defaultdict(list)
    218         agents = self.GetAgentsByType(agentTypeID) if agentTypeID else self.GetAllAgents()
    219         for agent in agents:
    220             if factionID == agent.factionID:
    221                 if divisionID and divisionID != agent.divisionID:
    222                     continue
    223                 if careerPathID and careerPathID != CAREER_PATH_BY_DIVISION.get(agent.divisionID, None):
    224                     continue
    225                 if onlyIncomplete and self.IsCareerAgentType(agentTypeID) and self.IsCareerAgentCompleted(agent.agentID):
    226                     continue
    227                 agentsBySolarSystemID[agent.solarsystemID].append(agent.agentID)
    228 
    229         if not agentsBySolarSystemID:
    230             return
    231         closestSolarSystemID = self._GetClosestSystem(agentsBySolarSystemID.keys())
    232         closestAgents = agentsBySolarSystemID[closestSolarSystemID]
    233         if len(closestAgents):
    234             return closestAgents[0]
    235 
    236     def GetMySuggestedCareerAgents(self):
    237         agents = self._GetMySchoolAgents()
    238         return self._GetNearestSetOfAgents(agents)
    239 
    240     def GetMySuggestetCareerAgentStation(self):
    241         agents = self.GetMySuggestedCareerAgents()
    242         return agents[0].stationID
    243 
    244     def IsCareerAgentPathCompleted(self, agentDivisionID):
    245         agents = self.GetMySuggestedCareerAgents()
    246         for agent in agents:
    247             if agent.divisionID == agentDivisionID:
    248                 return self.IsCareerAgentCompleted(agent.agentID)
    249 
    250     def _GetMyFactionAgents(self):
    251         allCareerAgents = self.GetAgentsByType(appConst.agentTypeCareerAgent)
    252         myFactionID = self._GetMyFactionID()
    253         return [ agent for agent in allCareerAgents if agent.factionID == myFactionID ]
    254 
    255     def _GetMySchoolAgents(self):
    256         charInfo = sm.RemoteSvc('charMgr').GetPublicInfo(session.charid)
    257         school_id = charInfo.schoolID
    258         school_agent_ids = get_school_agent_ids(school_id)
    259         return sorted([ agent for agent in self.GetAgentsByID().values() if agent.agentID in school_agent_ids ], key=lambda agent: agent.agentID)
    260 
    261     def _GetMyFactionID(self):
    262         myFactionID = factionByRace[session.raceID]
    263         return myFactionID
    264 
    265     def _GetNearestSetOfAgents(self, myFactionAgents):
    266         agentsBySolarSystemID = defaultdict(list)
    267         for agent in myFactionAgents:
    268             agentsBySolarSystemID[agent.solarsystemID].append(agent)
    269 
    270         solarSystemID = self._GetClosestSystem(agentsBySolarSystemID.keys())
    271         return agentsBySolarSystemID[solarSystemID]
    272 
    273     def _GetClosestSystem(self, solarSystemIDs):
    274         sortedList = sorted(solarSystemIDs, key=self._GetNumJumpsToSystem)
    275         return sortedList[0]
    276 
    277     def _GetNumJumpsToSystem(self, solarSystemID):
    278         return sm.GetService('clientPathfinderService').GetJumpCountFromCurrent(solarSystemID)
    279 
    280     def GetAgentTimers(self, agentIDs):
    281         timers = {}
    282         characterID = session.charid
    283         if characterID:
    284             for agentID in agentIDs:
    285                 agent = self.GetAgentMoniker(agentID)
    286                 timers[agentID] = agent.GetReplayTimestamp(characterID)
    287 
    288         return timers
    289 
    290     def GetAgentMoniker(self, agentID):
    291         if agentID not in self.agentMonikers:
    292             if getattr(self, 'allAgentsByID', False) and agentID in self.allAgentsByID and self.allAgentsByID[agentID].stationID:
    293                 self.agentMonikers[agentID] = eveMoniker.GetAgent(agentID, self.allAgentsByID[agentID].stationID)
    294             else:
    295                 self.agentMonikers[agentID] = eveMoniker.GetAgent(agentID)
    296         self.lastMonikerAccess = blue.os.GetWallclockTime()
    297         return self.agentMonikers[agentID]
    298 
    299     def _ClearAgentMonikersThread(self):
    300         while self.state == SERVICE_RUNNING:
    301             blue.pyos.synchro.SleepWallclock(300000)
    302             if blue.os.GetWallclockTime() > self.lastMonikerAccess + 30 * appConst.MIN:
    303                 self.agentMonikers.clear()
    304 
    305     def Run(self, memStream = None):
    306         self._getAllAgentsLock = Lock()
    307         self.allAgents = None
    308         self.careerAgents = None
    309         self.divisions = None
    310         self.agentMonikers = {}
    311         self.agentArgs = {}
    312         self.missionArgs = {}
    313         self.lastMonikerAccess = blue.os.GetWallclockTime()
    314         self._careerAgentsCompletedByID = {}
    315         self.processing = 0
    316         self.isExecutingAgentAction = False
    317         self.isUpdatingJournalWindow = False
    318         self.agentDialogWindows = weakref.WeakValueDictionary()
    319         self.missionDetailsWindows = weakref.WeakValueDictionary()
    320         self.agentSolarSystems = {}
    321         self._disabledMissions = None
    322         self.isHeraldryAvailable = HERALDRY_ENABLED_DEFAULT
    323         flag_bool(key=HERALDRY_ENABLED_KEY, default_value=HERALDRY_ENABLED_DEFAULT, callback=self._OnFlagChanged)
    324         self._UpdateMissionGiverClass()
    325         uthread.worker('agents::ClearMonikers', self._ClearAgentMonikersThread)
    326 
    327     @property
    328     def disabledMissions(self):
    329         if self._disabledMissions is None:
    330             self._disabledMissions = sm.RemoteSvc('agentMgr').GetDisabledMissions()
    331         return self._disabledMissions
    332 
    333     def IsMissionDisabled(self, missionID):
    334         return missionID in self.disabledMissions
    335 
    336     def _OnFlagChanged(self, evaluate_flag):
    337         previousValue = self.isHeraldryAvailable
    338         self.isHeraldryAvailable = evaluate_flag()
    339         if self.isHeraldryAvailable != previousValue:
    340             sm.ScatterEvent('OnHeraldryAvailabilityChanged')
    341 
    342     def IsHeraldryAvailable(self):
    343         return self.isHeraldryAvailable
    344 
    345     def _UpdateMissionGiverClass(self):
    346         isNewMissionGiverEnabled = GetNewMissionGiverEnabled(sm.GetService('machoNet'))
    347         self.careerAgentWindowClass = MissionGiver if isNewMissionGiverEnabled else AgentDialogueWindow
    348 
    349     def OnGlobalConfigChanged(self, *args, **kwargs):
    350         self._UpdateMissionGiverClass()
    351         self.ReloadAgentDialogWindows()
    352 
    353     def CheckCargoCapacity(self, mandatoryCargoUnits, extraFlagsToCheck):
    354         activeShipID = session.shipid
    355         if activeShipID is None:
    356             capacity, used = (0, 0)
    357         else:
    358             capacity, used = sm.GetService('invCache').GetInventoryFromId(activeShipID).GetCapacity(appConst.flagCargo)
    359             for flag in extraFlagsToCheck:
    360                 flagCapacity, flagUsed = sm.GetService('invCache').GetInventoryFromId(activeShipID).GetCapacity(flag)
    361                 capacity += flagCapacity
    362                 used += flagUsed
    363 
    364         if session.stationid is None and capacity - (used + mandatoryCargoUnits) < 0:
    365             rejectMessage = localization.GetByLabel('UI/Agents/StandardMissionCargoCapWarning', cargoUnits=mandatoryCargoUnits)
    366             self.MessageBox(localization.GetByLabel('UI/Agents/CannotAcceptMission'), rejectMessage)
    367             return ('mission.offeredinsufficientcargospace', rejectMessage)
    368 
    369     def YesNo(self, title, body, agentID = None, contentID = None, suppressID = None):
    370         if isinstance(title, basestring):
    371             titleText = title
    372         else:
    373             titleText = self.ProcessMessage((title, contentID), agentID)
    374         if isinstance(body, basestring):
    375             bodyText = body
    376         else:
    377             bodyText = self.ProcessMessage((body, contentID), agentID)
    378         ret = self.ShowMessageWindow(bodyText, titleText, PromptOptions.YESNO, PromptIcon.QUESTION, suppressID)
    379         return ret == uiconst.ID_YES
    380 
    381     def MessageBox(self, title, body, agentID = None, contentID = None, suppressID = None):
    382         if isinstance(title, basestring):
    383             titleText = title
    384         else:
    385             titleText = self.ProcessMessage((title, contentID), agentID)
    386         if isinstance(body, basestring):
    387             bodyText = body
    388         else:
    389             bodyText = self.ProcessMessage((body, contentID), agentID)
    390         options = {'text': bodyText,
    391          'title': titleText,
    392          'buttons': triui.OK,
    393          'icon': triui.INFO}
    394         self.ShowMessageWindow(bodyText, titleText, PromptOptions.OK, PromptIcon.INFO, suppressID)
    395 
    396     def ShowMessageWindow(self, text, title, buttons, icon, suppressID = None):
    397         suppText = None
    398         if suppressID:
    399             suppText = localization.GetByLabel('UI/Common/SuppressionShowMessage')
    400         result = prompt_player(text=text, title=title, options=buttons, icon=icon, suppress_id=suppressID, suppress_hint=suppText)
    401         return result
    402 
    403     def SingleChoiceBox(self, title, body, choices, agentID = None, contentID = None, suppressID = None):
    404         if isinstance(title, basestring):
    405             titleText = title
    406         else:
    407             titleText = self.ProcessMessage((title, contentID), agentID)
    408         if isinstance(body, basestring):
    409             bodyText = body
    410         else:
    411             bodyText = self.ProcessMessage((body, contentID), agentID)
    412         choicesText = []
    413         for choice in choices:
    414             if type(choice) is tuple:
    415                 choicesText.append(localization.GetByLabel(choice[0], **choice[1]))
    416             else:
    417                 choicesText.append(choice)
    418 
    419         options = {'text': bodyText,
    420          'title': titleText,
    421          'icon': triui.QUESTION,
    422          'buttons': uiconst.OKCANCEL,
    423          'radioOptions': choicesText}
    424         if suppressID:
    425             supp = get_supressed_result(suppressID)
    426             if supp is not None:
    427                 return supp
    428             options['suppText'] = localization.GetByLabel('UI/Common/SuppressionShowMessageRemember')
    429         ret, block = sm.StartService('gameui').RadioButtonMessageBox(**options)
    430         if suppressID and block:
    431             set_suppress_result(suppressID, ret)
    432         return (ret[0] == uiconst.ID_OK, ret[1])
    433 
    434     def GetQuantity(self, **keywords):
    435         for k in ('caption', 'label'):
    436             if k in keywords and not isinstance(keywords[k], basestring):
    437                 keywords[k] = self.ProcessMessage((keywords[k], None), keywords.get('agentID', None))
    438 
    439         ret = uix.QtyPopup(**keywords)
    440         if not ret:
    441             return
    442         return ret.get('qty', None)
    443 
    444     def RemoteNamePopup(self, caption, label, agentID):
    445         if isinstance(caption, basestring):
    446             captionText = caption
    447         else:
    448             captionText = self.ProcessMessage((caption, None), agentID)
    449         if isinstance(label, basestring):
    450             labelText = label
    451         else:
    452             labelText = self.ProcessMessage((label, None), agentID)
    453         return NamePopup(captionText, labelText)
    454 
    455     def PopupSelect(self, title, explanation, agentID, **keywords):
    456         if isinstance(title, basestring):
    457             titleText = title
    458         else:
    459             titleText = self.ProcessMessage((title, None), agentID)
    460         if isinstance(explanation, basestring):
    461             explanationText = explanation
    462         else:
    463             explanationText = self.ProcessMessage((explanation, None), agentID)
    464         if 'typeIDs' in keywords:
    465             displayList = []
    466             for typeID in keywords['typeIDs']:
    467                 displayList.append([evetypes.GetName(typeID), typeID, typeID])
    468 
    469             ret = uix.ListWnd(displayList, 'type', titleText, explanationText, 0, 300)
    470         else:
    471             return
    472         if ret:
    473             return ret[2]
    474         else:
    475             return
    476 
    477     def Stop(self, memStream = None):
    478         self.LogInfo('Stopping Agent Services')
    479         Service.Stop(self)
    480 
    481     def GetAuraAgentID(self):
    482         charinfo = sm.RemoteSvc('charMgr').GetPublicInfo(session.charid)
    483         schoolinfo = get_school(charinfo.schoolID)
    484         corpinfo = sm.GetService('corp').GetCorporation(schoolinfo.corporationID)
    485         agents = self.allAgentsByStationID[corpinfo.stationID]
    486         for agent in agents:
    487             if agent.agentTypeID == appConst.agentTypeAura:
    488                 return agent.agentID
    489 
    490     def OnInteractWith(self, agentID):
    491         self.OpenDialogueWindow(agentID)
    492 
    493     def _GetWindowClass(self, agentID, agentTypeID):
    494         if not self.IsCareerAgent(agentID) and agentTypeID not in AGENT_TYPES_WITH_NEW_UI and agentID not in AGENT_IDS_WITH_NEW_UI:
    495             return AgentDialogueWindow
    496         return self.careerAgentWindowClass
    497 
    498     def OnAgentSlashCommandExecuted(self, agentID):
    499         windowName = self.GetAgentConversationWindowName(agentID)
    500         agentInfo = self.GetAgentByID(agentID)
    501         windowClass = self._GetWindowClass(agentID, agentInfo.agentTypeID)
    502         window = windowClass.GetIfOpen(windowID=windowName)
    503         if window:
    504             window.Close()
    505             self.OpenDialogueWindow(agentID)
    506 
    507     def IsCheatingWithAgent(self, agentID):
    508         return self.GetAgentMoniker(agentID).IsCheatingWithAgent()
    509 
    510     def ShouldAlwaysAllowReplay(self, agentID):
    511         return self.GetAgentMoniker(agentID).ShouldAlwaysAllowReplay()
    512 
    513     @telemetry.ZONE_METHOD
    514     def OpenDialogueWindow(self, agentID, maximize = True):
    515         agentInfo = self.GetAgentByID(agentID)
    516         if agentInfo.level >= clonegrade.RESTRICTED_AGENT_LEVEL:
    517             if get_clone_grade_svc().get_clone_grade_with_grace_period() == clonegrade.CLONE_STATE_ALPHA:
    518                 sm.GetService('cmd').OpenCloneUpgradeWindow(ORIGIN_AGENTS, 'toohighagentlevel')
    519                 return
    520         wnd = None
    521         if agentID in self.agentDialogWindows:
    522             wnd = self.agentDialogWindows[agentID]
    523             if wnd.destroyed:
    524                 wnd = None
    525             if wnd is not None and not wnd.destroyed:
    526                 if maximize:
    527                     wnd.Maximize()
    528         if wnd is None:
    529             agentInfo = self.GetAgentByID(agentID)
    530             if agentInfo and agentInfo.agentTypeID == appConst.agentTypeAura:
    531                 prompt_player_fsd_dialog('NewNPEClickOnTutorialLinkDisabled', {})
    532                 return
    533             windowName = self.GetAgentConversationWindowName(agentID)
    534             windowClass = self._GetWindowClass(agentID, agentInfo.agentTypeID)
    535             wnd = windowClass.Open(windowID=windowName, agentID=agentID, npcCharacterID=agentID)
    536             self.agentDialogWindows[agentID] = wnd
    537         wnd.InteractWithAgent()
    538         if not wnd.destroyed and wnd.stacked:
    539             if not isinstance(wnd, MissionGiver):
    540                 wnd.RefreshBrowsers()
    541 
    542     def ReloadAgentDialogWindows(self):
    543         windowStates = {agentID:window.state for agentID, window in self.agentDialogWindows.iteritems()}
    544         for agentID in windowStates.iterkeys():
    545             window = self.agentDialogWindows.get(agentID, None)
    546             if window or not window.destroyed:
    547                 window.Close()
    548 
    549         for agentID, state in windowStates.iteritems():
    550             self.OpenDialogueWindow(agentID)
    551             self.agentDialogWindows[agentID].SetState(state)
    552 
    553     def ProcessUIRefresh(self):
    554         if not getattr(self, 'divisions', None):
    555             return
    556         for row in self.divisions.values():
    557             row.divisionName = localization.GetByMessageID(row.divisionNameID)
    558             row.leaderType = localization.GetByMessageID(row.leaderTypeID)
    559 
    560         self.ReloadAgentDialogWindows()
    561 
    562     def GetAgentArgs(self, agentID):
    563         agentInfo = self.GetAgentByID(agentID)
    564         if not agentInfo:
    565             return {}
    566         agentArgs = {'agentID': agentInfo.agentID}
    567         agentArgs['agentCorpID'] = agentInfo.corporationID
    568         agentArgs['agentFactionID'] = agentInfo.factionID
    569         agentArgs['agentSolarSystemID'] = agentInfo.solarsystemID
    570         agentArgs['agentLocation'] = agentInfo.solarsystemID
    571         if getattr(agentInfo, 'stationID', None):
    572             agentArgs['agentStationID'] = agentInfo.stationID
    573             agentArgs['agentLocation'] = agentInfo.stationID
    574         agentArgs['agentConstellationID'] = self.map.GetConstellationForSolarSystem(agentInfo.solarsystemID)
    575         agentArgs['agentRegionID'] = self.map.GetRegionForSolarSystem(agentInfo.solarsystemID)
    576         return agentArgs
    577 
    578     def PrimeMessageArguments(self, agentID, contentID, missionKeywords = None):
    579         if contentID is not None:
    580             if agentID is None:
    581                 raise RuntimeError('Agent message received a content ID without an agent ID. Something is wrong!')
    582             if (agentID, contentID) not in self.missionArgs:
    583                 if not missionKeywords:
    584                     missionKeywords = self.GetAgentMoniker(agentID).GetMissionKeywords(contentID)
    585                 self.missionArgs[agentID, contentID] = missionKeywords
    586         if agentID is not None:
    587             if agentID not in self.agentArgs:
    588                 self.agentArgs[agentID] = self.GetAgentArgs(agentID)
    589 
    590     def ProcessMessage(self, msg, agentID = None):
    591         if isinstance(msg, types.TupleType):
    592             msgInfo, contentID = msg
    593             if isinstance(msgInfo, basestring):
    594                 return msgInfo
    595             msgArgs = {}
    596             self.PrimeMessageArguments(agentID, contentID)
    597             if contentID is not None:
    598                 msgArgs.update(self.missionArgs[agentID, contentID])
    599             if agentID is not None:
    600                 msgArgs.update(self.agentArgs[agentID])
    601             if isinstance(msgInfo, tuple):
    602                 for k in msgInfo[1]:
    603                     if k in ('missionCompletionText', 'missionOfferText', 'missionBriefingText', 'declineMessageText', 'locationString') or isinstance(msgInfo[1][k], tuple):
    604                         msgInfo[1][k] = self.ProcessMessage((msgInfo[1][k], contentID), agentID)
    605 
    606                 msgArgs.update(msgInfo[1])
    607                 try:
    608                     return localization.GetByLabel(msgInfo[0], **msgArgs)
    609                 except:
    610                     log.LogException('Error parsing message with label %s' % msgInfo[0])
    611                     return localization.GetByLabel('UI/Agents/Dialogue/StandardMission/CorruptBriefing')
    612 
    613             else:
    614                 try:
    615                     return localization.GetByMessageID(msgInfo, **msgArgs)
    616                 except:
    617                     log.LogException('Error parsing agent message with ID %s' % msgInfo)
    618                     return localization.GetByLabel('UI/Agents/Dialogue/StandardMission/CorruptBriefing') + '<br>----------------------<br>' + localization._GetRawByMessageID(msgInfo)
    619 
    620         else:
    621             return msg
    622 
    623     def DoAction(self, agentID, actionID = None):
    624         if self.isExecutingAgentAction:
    625             return
    626         self.isExecutingAgentAction = True
    627         try:
    628             if agentID in self.agentDialogWindows:
    629                 window = self.agentDialogWindows[agentID]
    630                 window.InteractWithAgent(actionID)
    631         finally:
    632             self.isExecutingAgentAction = False
    633 
    634     def OnAgentMissionChange(self, actionID, agentID):
    635         if agentID in self.agentDialogWindows:
    636             agentDialogueWindow = self.agentDialogWindows[agentID]
    637             if actionID in (appConst.agentMissionOfferRemoved, appConst.agentMissionReset, appConst.agentTalkToMissionCompleted):
    638                 if not agentDialogueWindow.destroyed:
    639                     agentDialogueWindow.CloseByUser()
    640                 del self.agentDialogWindows[agentID]
    641         if actionID in (appConst.agentMissionOffered, appConst.agentMissionReset):
    642             keys = [ x for x in self.missionArgs.keys() if x[0] == agentID ]
    643             for key in keys:
    644                 del self.missionArgs[key]
    645 
    646         wnd = self.agentDialogWindows.get(agentID, None)
    647         if isinstance(wnd, MissionGiver):
    648             if actionID == appConst.agentMissionAccepted:
    649                 PlaySound('career_agent_window_accept_mission_play')
    650             elif actionID == appConst.agentMissionCompleted:
    651                 PlaySound('career_agent_window_mission_complete_play')
    652         elif actionID == appConst.agentMissionCompleted:
    653             PlaySound('ui_notify_mission_rewards_play')
    654         if actionID == appConst.agentMissionCompleted:
    655             if agentID in self._careerAgentsCompletedByID:
    656                 self._careerAgentsCompletedByID.pop(agentID)
    657 
    658     def OnDatacoreBought(self, characterID, agentID, balance):
    659         self.LogInfo('OnDatacoreBought', characterID, agentID, balance)
    660         sm.ScatterEvent('OnAgentMissionChange', appConst.agentMissionResearchUpdatePPD, agentID)
    661         sm.ScatterEvent('OnAccountChange', 'cash', characterID, balance)
    662 
    663     def OnSessionReset(self):
    664         self.careerAgents = None
    665         self.agentDialogWindows.clear()
    666 
    667     def PopupMission(self, agentID, charID = None, contentID = None):
    668         if is_job_board_available() and are_missions_in_job_board_available():
    669             self._PopupMissionInJobBoard(agentID, charID)
    670         else:
    671             self._PopupMissionJournalWindow(agentID, charID, contentID)
    672 
    673     def _PopupMissionInJobBoard(self, agentID, charID):
    674         open_mission(agentID, charID)
    675 
    676     def _PopupMissionJournalWindow(self, agentID, charID = None, contentID = None):
    677         wnd = self.missionDetailsWindows.get(agentID, None)
    678         if not wnd or wnd.destroyed:
    679             windowName = self.GetMissionJournalWindowName(agentID)
    680             wnd = MissionDetailsWindow.Open(windowID=windowName, caption=localization.GetByLabel('UI/Agents/MissionJournalWithAgent', agentID=agentID))
    681             self.missionDetailsWindows[agentID] = wnd
    682         uthread.new(wnd.ReconstructLayout, agentID, charID, contentID)
    683 
    684     def GetMissionInfo(self, agentID, charID = None, contentID = None):
    685         return self.GetAgentMoniker(agentID).GetMissionJournalInfo(charID, contentID)
    686 
    687     def GetMissionBriefingInfo(self, agentID):
    688         briefingInformation = self.GetAgentMoniker(agentID).GetMissionBriefingInfo()
    689         if briefingInformation and briefingInformation[appConst.agentMissionBriefingMissionID]:
    690             self.missionArgs[agentID, briefingInformation[appConst.agentMissionBriefingMissionID]] = briefingInformation[appConst.agentMissionBriefingKeywords]
    691         return briefingInformation
    692 
    693     def CheckCourierCargo(self, agentID, stationID, contentID):
    694         missionInfo = self.GetAgentMoniker(agentID).GetMissionJournalInfo()
    695         if missionInfo:
    696             for objType, objData in missionInfo['objectives']['objectives']:
    697                 if objType == 'transport':
    698                     pickupOwnerID, pickupLocation, dropoffOwnerID, dropoffLocation, cargo = objData
    699                     return cargo['hasCargo']
    700 
    701         return False
    702 
    703     def PopupDungeonShipRestrictionList(self, agentID, charID = None, dungeonID = None):
    704         from evedungeons.client.ship_restrictions_window import ShipRestrictionsWindow
    705         ShipRestrictionsWindow.Open(dungeon_id=dungeonID)
    706 
    707     def PopupDungeonFittedTypesRestrictionList(self, dungeonID):
    708         from eve.client.script.ui.control.typeList.type_list_window import TypeListWindow
    709         dungeonData = GetDungeon(dungeonID)
    710         for connection in dungeonData.connections:
    711             if connection.disallowedFittedTypesList:
    712                 header_text = localization.GetByMessageID(dungeonData.dungeonNameID)
    713                 window_caption = localization.GetByLabel('UI/Agents/Dialogue/DungeonDisallowedFittedTypesRestrictionsCaption')
    714                 info_text = localization.GetByLabel('UI/Agents/Dialogue/DungeonDisallowedFittedTypesRestrictionsInfo')
    715                 TypeListWindow.OpenTypeList(connection.disallowedFittedTypesList, header_text=header_text, window_caption=window_caption, info_text=info_text)
    716 
    717     def RemoveOfferFromJournal(self, agentID):
    718         self.GetAgentMoniker(agentID).RemoveOfferFromJournal()
    719 
    720     def ShowMissionObjectives(self, agentID, contentID = None):
    721         if agentID not in self.agentDialogWindows:
    722             self.OpenDialogueWindow(agentID)
    723         elif not self.isUpdatingJournalWindow:
    724             self.isUpdatingJournalWindow = True
    725             try:
    726                 agentDialogueWindow = self.agentDialogWindows[agentID]
    727                 agentDialogueWindow.ShowMissionObjectives(contentID)
    728             finally:
    729                 self.isUpdatingJournalWindow = False
    730 
    731     def GetSolarSystemOfAgent(self, agentID):
    732         if agentID not in self.agentSolarSystems:
    733             self.agentSolarSystems[agentID] = sm.RemoteSvc('agentMgr').GetSolarSystemOfAgent(agentID)
    734         return self.agentSolarSystems[agentID]
    735 
    736     def ProcessAgentInfoKeyVal(self, data):
    737         infoFunc = {SERVICETYPE_RESEARCH: self._ProcessResearchServiceInfo,
    738          SERVICETYPE_LOCATE: self._ProcessLocateServiceInfo,
    739          SERVICETYPE_MISSION: self._ProcessMissionServiceInfo}.get(data.agentServiceType, None)
    740         if infoFunc:
    741             return infoFunc(data)
    742         else:
    743             return []
    744 
    745     def _ProcessResearchServiceInfo(self, data):
    746         header = localization.GetByLabel('UI/Agents/Research/ResearchServices', session.languageID)
    747         skillList = []
    748         for skillTypeID, skillInfo in data.skills:
    749             skillList.append(localization.GetByLabel('UI/Agents/Research/SkillListing', session.languageID, skillID=skillTypeID, skillLevel=skillInfo.effectiveSkillLevel))
    750 
    751         if not skillList:
    752             skills = localization.GetByLabel('UI/Agents/Research/ErrorNoRelevantResearchSkills', session.languageID)
    753         else:
    754             skillList = localization.util.Sort(skillList)
    755             skills = localization.formatters.FormatGenericList(skillList)
    756         details = [(localization.GetByLabel('UI/Agents/Research/RelevantSkills', session.languageID), skills)]
    757         status = []
    758         if data.researchData:
    759             researchData = data.researchData
    760             researchStuff = [(localization.GetByLabel('UI/Agents/Research/ResearchField', session.languageID), evetypes.GetName(researchData['skillTypeID'])), (localization.GetByLabel('UI/Agents/Research/CurrentStatus', session.languageID), localization.GetByLabel('UI/Agents/Research/CurrentStatusRP', session.languageID, rpAmount=researchData['points'])), (localization.GetByLabel('UI/Agents/Research/ResearchRate', session.languageID), localization.GetByLabel('UI/Agents/Research/ResearchRateRPDay', session.languageID, rpAmount=researchData['pointsPerDay']))]
    761             status = [(localization.GetByLabel('UI/Agents/Research/YourResearch', session.languageID), researchStuff)]
    762         return [(header, details)] + status
    763 
    764     def _ProcessLocateServiceInfo(self, data):
    765         header = localization.GetByLabel('UI/Agents/Locator/LocationServices', session.languageID)
    766         if data.frequency:
    767             details = [(localization.GetByLabel('UI/Agents/Locator/MaxFrequency', session.languageID), localization.GetByLabel('UI/Agents/Locator/EveryInterval', session.languageID, interval=data.frequency))]
    768         else:
    769             details = [(localization.GetByLabel('UI/Agents/Locator/MaxFrequency', session.languageID), localization.GetByLabel('UI/Generic/NotAvailableShort', session.languageID))]
    770         for delayRange, delay, cost in data.delays:
    771             rangeText = [localization.GetByLabel('UI/Agents/Locator/SameSolarSystem', session.languageID),
    772              localization.GetByLabel('UI/Agents/Locator/SameConstellation', session.languageID),
    773              localization.GetByLabel('UI/Agents/Locator/SameRegion', session.languageID),
    774              localization.GetByLabel('UI/Agents/Locator/DifferentRegion', session.languageID)][delayRange]
    775             if not delay:
    776                 delay = localization.GetByLabel('UI/Agents/Locator/ResultsInstantaneous', session.languageID)
    777             else:
    778                 delay = FmtTimeInterval(delay * appConst.SEC)
    779             details.append((rangeText, localization.formatters.FormatGenericList((FmtISK(cost), delay))))
    780 
    781         if data.callbackID:
    782             details.append((localization.GetByLabel('UI/Agents/Locator/Availability', session.languageID), localization.GetByLabel('UI/Agents/Locator/NotAvailableInProgress', session.languageID)))
    783         elif data.lastUsed and blue.os.GetWallclockTime() - data.lastUsed < data.frequency:
    784             details.append((localization.GetByLabel('UI/Agents/Locator/AvailableAgain', session.languageID), FmtDate(data.lastUsed + data.frequency)))
    785         return [(header, details)]
    786 
    787     def _ProcessMissionServiceInfo(self, data):
    788         if data.available:
    789             return [(localization.GetByLabel('UI/Agents/MissionServices', session.languageID), [(localization.GetByLabel('UI/Agents/MissionAvailability', session.languageID), localization.GetByLabel('UI/Agents/MissionAvailabilityStandard', session.languageID))])]
    790         else:
    791             return [(localization.GetByLabel('UI/Agents/MissionServices', session.languageID), [(localization.GetByLabel('UI/Agents/MissionAvailability', session.languageID), localization.GetByLabel('UI/Agents/MissionAvailabilityNone', session.languageID))])]