infoPanelSvc.py (21573B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\infoPanels\infoPanelSvc.py 2 import sys 3 import eveformat.client 4 import evelink.client 5 import localization 6 import log 7 import telemetry 8 import uthread 9 import uthread2 10 from achievement_system.client.api.signal import on_achievements_tracked_changed 11 from carbon.common.script.sys import service 12 from carbon.common.script.util import commonutils 13 from carbon.common.script.util.timerstuff import AutoTimer 14 from carbonui import uiconst 15 from carbonui.primitives.container import Container 16 from carbonui.uicore import uicore 17 from carbonui.util import colorblind 18 from eve.client.script.ui.crimewatch import crimewatchTimers 19 from eve.client.script.ui.shared.careerPortal.cpSignals import on_cp_goal_tracking_removed, on_cp_goal_tracking_added 20 from eve.client.script.ui.shared.infoPanels import sessionTimeIndicator 21 from eve.client.script.ui.shared.infoPanels.const import infoPanelConst 22 from eve.client.script.ui.shared.infoPanels.const import infoPanelUIConst 23 from eve.client.script.ui.shared.infoPanels.infoPanelContainer import InfoPanelContainer 24 from eve.client.script.ui.shared.infoPanels.milestoneTimer.milestoneTimer import MilestoneTimer 25 from eve.common.script.sys import idCheckers 26 from eveformat.client import solar_system_security_status 27 from jobboard.client import job_board_signals 28 from milestones.common.constants import MILESTONE_REWARD_MINUTES, MILESTONE_HUNDRED_AND_TWENTY_MINUTES_ID 29 from operations.common.const import OPERATION_CATEGORY_RECOMMENDATIONS 30 from uthread2 import StartTasklet 31 MISSION_INFO_PANEL_UPDATE_DELAY_MSEC = 1000 32 OPERATION_INFO_PANEL_UPDATE_DELAY_MSEC = 1000 33 RECOMMENDATION_INFO_PANEL_UPDATE_DELAY_SEC = 0.5 34 AIR_NPE_INFO_PANEL_UPDATE_DELAY_SEC = 0.5 35 36 class InfoPanelSvc(service.Service): 37 __update_on_reload__ = 1 38 __guid__ = 'svc.infoPanel' 39 __startupdependencies__ = ['missionObjectivesTracker'] 40 __notifyevents__ = ['OnSessionChanged', 41 'OnClientEvent_WarpFinished', 42 'OnViewStateChanged', 43 'OnSovereigntyChanged', 44 'OnSystemStatusChanged', 45 'OnEntitySelectionChanged', 46 'OnChallengeProgressUpdate', 47 'OnChallengeCompleted', 48 'OnRWDungeonEnteredInClient', 49 'OnRWDungeonExitedInClient', 50 'OnRWTimerEndedInClient', 51 'OnOperationMadeActive', 52 'OnOperationCompleted', 53 'OnOperationReplayed', 54 'OnOperationTaskTransition', 55 'OnTutorialSkipped', 56 'OnOperationDeactivatedUpdate', 57 'OnRecommendationsUpdated', 58 'OnAirNpeStateChanged', 59 'OnAirNpeObjectiveUpdated', 60 'OnInsurgencyCampaignStartedForLocation_Local', 61 'OnInsurgencyCampaignEndingForLocation_Local', 62 'OnInsurgencyCampaignUpdatedForLocation_Local'] 63 64 def Run(self, *args): 65 self.sidePanel = None 66 self.infoPanelContainer = None 67 self.sessionTimer = None 68 self.sessionTimerUpdatePending = False 69 self.operationInfoPanelUpdater = None 70 self.recommendationsInfoPanelUpdater = None 71 self.missionInfoPanelUpdater = None 72 self.airNpeInfoPanelUpdater = None 73 self.combatTimerContainer = None 74 self.milestoneTimer = None 75 self.expandedMission = None 76 77 def Reload(self): 78 if self.sidePanel: 79 self.sidePanel.Close() 80 self.sidePanel = None 81 if self.sessionTimer: 82 self.sessionTimer.Close() 83 if self.combatTimerContainer: 84 self.combatTimerContainer.Release() 85 if self.milestoneTimer: 86 self.milestoneTimer.Close() 87 self.milestoneTimer = None 88 self.ReconstructAllPanels() 89 self.DisconnectSignals() 90 91 @telemetry.ZONE_FUNCTION 92 def ConstructSidePanel(self): 93 self.sidePanel = Container(parent=uicore.layer.sidePanels, name='sidePanel', align=uiconst.TOLEFT_NOPUSH, width=infoPanelUIConst.PANELWIDTH, padding=(0, 12, 0, 0)) 94 self.sidePanel.cacheContents = True 95 self.combatTimerContainer = crimewatchTimers.TimerContainer(parent=self.sidePanel, left=infoPanelUIConst.LEFTPAD) 96 self.infoPanelContainer = InfoPanelContainer(parent=self.sidePanel, align=uiconst.TOTOP) 97 self.sessionTimer = sessionTimeIndicator.SessionTimeIndicator(parent=self.sidePanel, pos=(16, 35, 24, 24), state=uiconst.UI_HIDDEN, align=uiconst.TOPLEFT) 98 if self.sessionTimerUpdatePending: 99 self.UpdateSessionTimer() 100 self.missionObjectivesTracker.RegisterMissionTracking() 101 102 def ConstructMilestoneTimer(self): 103 milestoneSvc = sm.GetService('milestoneSvc') 104 timeLeft = milestoneSvc.GetTimeLeftInMilestone() 105 if timeLeft is None: 106 return 107 self.milestoneTimer = MilestoneTimer(parent=self.sidePanel, left=10, iconPath='res:/UI/Texture/Crimewatch/iskIcon.png', timeLeft=timeLeft, totalTime=MILESTONE_REWARD_MINUTES, milestoneID=MILESTONE_HUNDRED_AND_TWENTY_MINUTES_ID) 108 109 def UpdateSessionTimer(self): 110 if self.sessionTimer: 111 StartTasklet(self.sessionTimer.AnimSessionChange) 112 self.sessionTimerUpdatePending = False 113 else: 114 self.sessionTimerUpdatePending = True 115 116 def ShowHideSidePanel(self, hide = 1, *args): 117 if self.sidePanel is not None and not self.sidePanel.destroyed: 118 if hide: 119 self.sidePanel.state = uiconst.UI_HIDDEN 120 else: 121 self.sidePanel.state = uiconst.UI_PICKCHILDREN 122 123 def GetPanelByTypeID(self, panelTypeID): 124 if self.infoPanelContainer: 125 return self.infoPanelContainer.GetPanelByTypeID(panelTypeID) 126 127 @telemetry.ZONE_FUNCTION 128 def OnViewStateChanged(self, oldView, newView): 129 self.ReconstructAllPanels(animate=True, settingsID=newView) 130 131 @telemetry.ZONE_FUNCTION 132 def ReconstructAllPanels(self, animate = False, settingsID = None): 133 if not session.charid: 134 return 135 if not self.sidePanel: 136 self.ConstructSidePanel() 137 if not self.milestoneTimer: 138 self.ConstructMilestoneTimer() 139 if not settingsID: 140 settingsID = sm.GetService('viewState').GetCurrentView().name 141 self.infoPanelContainer.ReconstructPanels(settingsID, animate) 142 143 def UpdateAllPanels(self): 144 if not session.charid or not self.sidePanel: 145 return 146 sm.ChainEvent('ProcessUpdateInfoPanel', None) 147 148 def ConnectSignals(self): 149 on_cp_goal_tracking_added.connect(self.OnCareerProgramTrackingUpdated) 150 on_cp_goal_tracking_removed.connect(self.OnCareerProgramTrackingUpdated) 151 job_board_signals.on_job_board_feature_availability_changed.connect(self.OnJobBoardAvailabilityChanged) 152 job_board_signals.on_tracked_jobs_changed.connect(self.OnTrackedJobsChanged) 153 colorblind.on_colorblind_mode_changed.connect(self.OnColorBlindModeChanged) 154 on_achievements_tracked_changed.connect(self.OnTrackedAchievementChanged) 155 156 def DisconnectSignals(self): 157 on_cp_goal_tracking_added.disconnect(self.OnCareerProgramTrackingUpdated) 158 on_cp_goal_tracking_removed.disconnect(self.OnCareerProgramTrackingUpdated) 159 job_board_signals.on_job_board_feature_availability_changed.disconnect(self.OnJobBoardAvailabilityChanged) 160 job_board_signals.on_tracked_jobs_changed.disconnect(self.OnTrackedJobsChanged) 161 colorblind.on_colorblind_mode_changed.disconnect(self.OnColorBlindModeChanged) 162 on_achievements_tracked_changed.disconnect(self.OnTrackedAchievementChanged) 163 164 @telemetry.ZONE_FUNCTION 165 def OnSessionChanged(self, isRemote, sess, change): 166 if not session.charid: 167 return 168 if 'charid' in change and change['charid'][1] is not None: 169 self.Reload() 170 self.ConnectSignals() 171 self.UpdateSessionTimer() 172 self.UpdateFactionalWarfarePanel() 173 self.UpdateLocationInfoPanel() 174 self.UpdateOperationsPanel() 175 self.UpdateAirNpePanel() 176 177 def OnClientEvent_WarpFinished(self, *args, **kwargs): 178 self.UpdateOperationsPanel() 179 self.UpdateAirNpePanel() 180 181 def OnAirNpeStateChanged(self, *args, **kwargs): 182 self.UpdateAirNpePanel(skip_wait=True) 183 184 def GetActiveOperationObjective(self): 185 if not session.charid or not self.infoPanelContainer: 186 return None 187 panel = self.GetPanelByTypeID(infoPanelConst.PANEL_OPERATIONS) 188 if not panel or not panel.IsAvailable(): 189 return None 190 return panel.GetFirstActiveObjective() 191 192 def IsPanelAvailable(self, panelTypeID): 193 if not session.charid or not self.infoPanelContainer: 194 return False 195 panel = self.GetPanelByTypeID(panelTypeID) 196 if not panel or not panel.IsAvailable(): 197 return False 198 return True 199 200 def IsMissionsPanelActive(self): 201 return self.IsPanelAvailable(infoPanelConst.PANEL_MISSIONS) 202 203 def OnSovereigntyChanged(self, solarSystemID, allianceID): 204 self.UpdateAllPanels() 205 206 def OnSystemStatusChanged(self, *args): 207 self.UpdateAllPanels() 208 209 def OnEntitySelectionChanged(self, entityID): 210 self.UpdateAllPanels() 211 212 def OnColorBlindModeChanged(self): 213 self.ReconstructAllPanels() 214 215 def OnRWDungeonEnteredInClient(self): 216 self.UpdatePanel(infoPanelConst.PANEL_RESOURCE_WARS) 217 218 def OnRWDungeonExitedInClient(self): 219 self.UpdatePanel(infoPanelConst.PANEL_RESOURCE_WARS) 220 221 def OnRWTimerEndedInClient(self): 222 self.UpdatePanel(infoPanelConst.PANEL_RESOURCE_WARS) 223 224 @telemetry.ZONE_FUNCTION 225 def UpdateMissionsPanel(self, callback = None): 226 if self.missionInfoPanelUpdater is None: 227 self.missionInfoPanelUpdater = AutoTimer(interval=MISSION_INFO_PANEL_UPDATE_DELAY_MSEC, method=self._UpdateMissionsPanelThread, callback=callback) 228 229 def _UpdateMissionsPanelThread(self, callback): 230 self.missionInfoPanelUpdater = None 231 self.UpdatePanel(infoPanelConst.PANEL_MISSIONS, False) 232 if callback: 233 callback() 234 235 def SetExpandedMission(self, featureID, missionID): 236 if missionID == -1 and self.expandedMission.get('featureID') != featureID: 237 return 238 if self.expandedMission and self.expandedMission.get('featureID') == featureID and self.expandedMission.get('missionID') == missionID: 239 return 240 self.expandedMission = {'featureID': featureID, 241 'missionID': missionID} 242 settings.char.ui.Set('infoPanelExpandedMission', self.expandedMission) 243 sm.ScatterEvent('OnExpandedMissionChanged', **self.expandedMission) 244 245 def GetExpandedMission(self): 246 if self.expandedMission is None: 247 self.expandedMission = settings.char.ui.Get('infoPanelExpandedMission', {}) 248 return self.expandedMission 249 250 def UpdateExpandedAgentMission(self): 251 agentID = self.missionObjectivesTracker.GetAgentForLastAcceptedMission() 252 panel = self.GetPanelByTypeID(infoPanelConst.PANEL_MISSIONS) 253 if panel and panel.IsAvailable(): 254 panel.UpdateExpandedStateForMissionID(agentID) 255 256 @telemetry.ZONE_FUNCTION 257 def UpdateOperationsPanel(self, categoryID = None): 258 if categoryID == OPERATION_CATEGORY_RECOMMENDATIONS or categoryID is None: 259 self.LogInfo('UpdateOperationsPanel::Recommendations - categoryID: %s' % categoryID) 260 if self.recommendationsInfoPanelUpdater: 261 return 262 self.recommendationsInfoPanelUpdater = uthread2.call_after_wallclocktime_delay(self._UpdateRecommendationsPanelThread, RECOMMENDATION_INFO_PANEL_UPDATE_DELAY_SEC) 263 if categoryID == OPERATION_CATEGORY_RECOMMENDATIONS: 264 return 265 if self.operationInfoPanelUpdater is None: 266 self.operationInfoPanelUpdater = AutoTimer(interval=OPERATION_INFO_PANEL_UPDATE_DELAY_MSEC, method=self._UpdateOperationsPanelThread) 267 268 def _UpdateOperationsPanelThread(self): 269 self.operationInfoPanelUpdater = None 270 self.UpdatePanel(infoPanelConst.PANEL_OPERATIONS) 271 272 def _UpdateRecommendationsPanelThread(self): 273 self.recommendationsInfoPanelUpdater = None 274 self.UpdatePanel(infoPanelConst.PANEL_RECOMMENDATIONS, False) 275 276 @telemetry.ZONE_FUNCTION 277 def UpdateAirNpePanel(self, skip_wait = False): 278 if skip_wait: 279 if self.airNpeInfoPanelUpdater: 280 self.airNpeInfoPanelUpdater.kill() 281 self.airNpeInfoPanelUpdater = None 282 self.UpdatePanel(infoPanelConst.PANEL_AIR_NPE, False) 283 return 284 if self.airNpeInfoPanelUpdater: 285 return 286 self.airNpeInfoPanelUpdater = uthread2.call_after_wallclocktime_delay(self._UpdateAirNpePanelThread, AIR_NPE_INFO_PANEL_UPDATE_DELAY_SEC) 287 288 def _UpdateAirNpePanelThread(self): 289 self.airNpeInfoPanelUpdater = None 290 self.UpdatePanel(infoPanelConst.PANEL_AIR_NPE, False) 291 292 @telemetry.ZONE_FUNCTION 293 def UpdateFactionalWarfarePanel(self): 294 self.UpdatePanel(infoPanelConst.PANEL_FACTIONAL_WARFARE) 295 296 @telemetry.ZONE_FUNCTION 297 def UpdateLocationInfoPanel(self): 298 self.UpdatePanel(infoPanelConst.PANEL_LOCATION_INFO) 299 300 @telemetry.ZONE_FUNCTION 301 def UpdateIncursionsPanel(self): 302 self.UpdatePanel(infoPanelConst.PANEL_INCURSIONS) 303 304 @telemetry.ZONE_FUNCTION 305 def UpdateDungeonProgressionPanel(self): 306 self.UpdatePanel(infoPanelConst.PANEL_DUNGEON_PROGRESSION) 307 308 @telemetry.ZONE_FUNCTION 309 def UpdateChallengesPanel(self): 310 self.UpdatePanel(infoPanelConst.PANEL_SEASONS) 311 312 def UpdateJobBoardPanel(self): 313 self.UpdatePanel(infoPanelConst.PANEL_JOB_BOARD) 314 315 def UpdateSkyhookTheftPanel(self): 316 self.UpdatePanel(infoPanelConst.PANEL_SKYHOOK_THEFT) 317 318 def UpdateWorldEventsPanel(self): 319 self.UpdatePanel(infoPanelConst.PANEL_WORLD_EVENTS) 320 321 def UpdateAbyssPanel(self): 322 self.UpdatePanel(infoPanelConst.PANEL_ABYSS) 323 324 def OnOperationMadeActive(self, categoryID, operationID, oldCategoryID, oldOperationID, isSilent): 325 self.UpdateOperationsPanel(categoryID) 326 327 def OnOperationCompleted(self, categoryID, operationID): 328 if categoryID != OPERATION_CATEGORY_RECOMMENDATIONS: 329 self.UpdateOperationsPanel(categoryID) 330 331 def OnOperationReplayed(self, categoryID, operationID): 332 self.UpdateOperationsPanel(categoryID) 333 334 def OnOperationQuit(self, categoryID, operationID): 335 self.UpdateOperationsPanel(categoryID) 336 337 def OnOperationTaskTransition(self, categoryID, operationID, taskID, fromState, toState): 338 if categoryID != OPERATION_CATEGORY_RECOMMENDATIONS: 339 self.UpdateOperationsPanel(categoryID) 340 341 def OnTutorialSkipped(self): 342 self.UpdateOperationsPanel() 343 344 def OnOperationDeactivatedUpdate(self): 345 self.UpdateOperationsPanel() 346 347 def OnChallengeProgressUpdate(self, challengeID, newProgress): 348 self.UpdateChallengesPanel() 349 350 def OnChallengeCompleted(self, *args): 351 self.UpdateChallengesPanel() 352 353 def OnInsurgencyCampaignStartedForLocation_Local(self, campaignSnapshot): 354 self.ReconstructAllPanels() 355 356 def OnInsurgencyCampaignEndingForLocation_Local(self): 357 self.ReconstructAllPanels() 358 359 def OnInsurgencyCampaignUpdatedForLocation_Local(self, campaignSnapshot): 360 self.ReconstructAllPanels() 361 362 @telemetry.ZONE_FUNCTION 363 def UpdatePanel(self, panelTypeID, ignoreViewSettings = True): 364 if not session.charid: 365 return 366 sm.ChainEvent('ProcessUpdateInfoPanel', panelTypeID) 367 if not self.infoPanelContainer: 368 return 369 uthread.Lock(self.infoPanelContainer) 370 try: 371 self.infoPanelContainer.UpdatePanel(panelTypeID, ignoreViewSettings) 372 finally: 373 uthread.UnLock(self.infoPanelContainer) 374 375 def EnsureSolarSystemIDOrRaise(self, itemID): 376 if idCheckers.IsStation(itemID): 377 solar_system_id = cfg.stations.Get(itemID).solarSystemID 378 elif idCheckers.IsSolarSystem(itemID): 379 solar_system_id = itemID 380 else: 381 structure = sm.GetService('structureDirectory').GetStructureInfo(itemID) 382 if structure is None: 383 raise RuntimeError('Invalid destination') 384 solar_system_id = structure.solarSystemID 385 return solar_system_id 386 387 def GetSolarSystemNameAndSecurityStatusDisplayString(self, solar_system_id, altText = None): 388 if altText: 389 altText = commonutils.StripTags(altText, stripOnly=['localized']) 390 solar_system_text = self.GetSolarSystemText(solar_system_id, solarSystemAlt=altText) 391 return solar_system_text 392 393 def GetSolarSystemConstellationTraceDisplayString(self, solar_system_id, traceFontSize = 12): 394 solar_system_info = cfg.mapSystemCache.Get(solar_system_id) 395 constellation_id = solar_system_info.constellationID 396 region_id = solar_system_info.regionID 397 if idCheckers.IsWormholeRegion(region_id): 398 return None 399 else: 400 trace = u'{sep}{constellation}'.format(constellation=evelink.location_link(constellation_id), sep=u'{space}<{space}'.format(space=eveformat.font_size(' ', size=8))) 401 if traceFontSize: 402 trace = eveformat.font_size(trace, traceFontSize) 403 return trace 404 405 def GetSolarSystemRegionTraceDisplayString(self, solar_system_id, traceFontSize = 12): 406 solar_system_info = cfg.mapSystemCache.Get(solar_system_id) 407 region_id = solar_system_info.regionID 408 if idCheckers.IsWormholeRegion(region_id): 409 return None 410 else: 411 trace = u'{sep}{region}'.format(region=evelink.location_link(region_id), sep=u'{space}<{space}'.format(space=eveformat.font_size(' ', size=8))) 412 if traceFontSize: 413 trace = eveformat.font_size(trace, traceFontSize) 414 return trace 415 416 def GetSolarSystemRegionAndConstellationTraceDisplayString(self, solar_system_id, traceFontSize = 12): 417 solar_system_info = cfg.mapSystemCache.Get(solar_system_id) 418 constellation_id = solar_system_info.constellationID 419 region_id = solar_system_info.regionID 420 if idCheckers.IsWormholeRegion(region_id): 421 return None 422 else: 423 trace = u'{sep}{constellation}{sep}{region}'.format(constellation=evelink.location_link(constellation_id), region=evelink.location_link(region_id), sep=u'{space}<{space}'.format(space=eveformat.font_size(' ', size=8))) 424 if traceFontSize: 425 trace = eveformat.font_size(trace, traceFontSize) 426 return trace 427 428 def GetSolarSystemTrace(self, itemID, altText = None, traceFontSize = 12): 429 solar_system_id = self.EnsureSolarSystemIDOrRaise(itemID) 430 solar_system_text = self.GetSolarSystemNameAndSecurityStatusDisplayString(solar_system_id, altText=altText) 431 trace = self.GetSolarSystemRegionAndConstellationTraceDisplayString(solar_system_id, traceFontSize=traceFontSize) 432 if trace: 433 return u'{solar_system}{trace}'.format(solar_system=solar_system_text, trace=trace) 434 else: 435 return solar_system_text 436 437 def GetLawlessSolarSystemTrace(self, itemID, traceFontSize = 12): 438 solar_system_id = self.EnsureSolarSystemIDOrRaise(itemID) 439 solar_system_text = cfg.evelocations.Get(solar_system_id).name 440 trace = self.GetSolarSystemRegionAndConstellationTraceDisplayString(solar_system_id, traceFontSize=traceFontSize) 441 if trace: 442 return u'<b>{solar_system}</b> {lawlessText}<br>{trace}'.format(solar_system=solar_system_text, lawlessText=solar_system_security_status(solar_system_id, lawless=True), trace=trace) 443 else: 444 return solar_system_text 445 446 def GetSolarSystemText(self, solarSystemID, solarSystemAlt = None): 447 try: 448 return u'{solar_system_name}</b> {security_status}'.format(solar_system_name=evelink.location_link(solarSystemID, hint=solarSystemAlt), security_status=self.GetSecurityStatusText(solarSystemID)) 449 except Exception: 450 self.LogError('Solar system not found', solarSystemID) 451 return '' 452 453 def GetSecurityStatusText(self, solarSystemID): 454 try: 455 return eveformat.hint(hint=localization.GetByLabel('UI/Map/StarMap/SecurityStatus'), text=eveformat.solar_system_security_status(solarSystemID)) 456 except Exception: 457 log.LogException() 458 sys.exc_clear() 459 return '' 460 461 def OnRecommendationsUpdated(self, *args, **kwargs): 462 self.UpdatePanel(infoPanelConst.PANEL_RECOMMENDATIONS) 463 464 def OnAirNpeObjectiveUpdated(self, current_goal = None, *args, **kwargs): 465 if current_goal: 466 panel = self.GetPanelByTypeID(infoPanelConst.PANEL_AIR_NPE) 467 if not panel: 468 self.SetExpandedMission('air_npe', 0) 469 self.UpdateAirNpePanel(skip_wait=True) 470 471 def OnCareerProgramTrackingUpdated(self, *args, **kwargs): 472 self.UpdatePanel(infoPanelConst.PANEL_CAREER_PROGRAM) 473 474 def OnTrackedAchievementChanged(self, achievement_ids): 475 self.UpdatePanel(infoPanelConst.PANEL_ACHIEVEMENT) 476 477 def OnJobBoardAvailabilityChanged(self, *args, **kwargs): 478 self.UpdateMissionsPanel() 479 self.UpdateJobBoardPanel() 480 481 def OnTrackedJobsChanged(self, *args, **kwargs): 482 self.UpdateJobBoardPanel() 483 484 def GetVisibleJobs(self, job_ids): 485 panel = self.GetPanelByTypeID(infoPanelConst.PANEL_JOB_BOARD) 486 if panel: 487 return panel.get_visible_jobs(job_ids) 488 return []