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))])]