incursionSvc.py (20004B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\incursionSvc.py 2 import carbonui.fontconst 3 from carbon.common.script.sys.service import Service 4 from carbonui.decorative.panelUnderlay import PanelUnderlay 5 from grouprewards import REWARD_TYPE_ISK 6 from grouprewards.data import get_immediate_reward_tables 7 from grouprewards.data import get_max_player_count_from_reward_tables 8 from grouprewards.data import get_max_reward_from_tables_with_reward_type 9 from grouprewards.data import get_min_player_count_from_reward_tables 10 from grouprewards.data import get_reward_for_player_count 11 from grouprewards.data import is_security_criteria_all 12 from grouprewards.data import is_security_criteria_high 13 from grouprewards.data import is_security_criteria_low 14 import blue 15 import uthread 16 import math 17 import localization 18 from eve.client.script.parklife.environmentSvc import GetEnvironmentService 19 from eveservices.xmppchat import GetChatService 20 from fsdBuiltData.common.soundIDs import GetSoundEventName 21 from fsdBuiltData.client.environmentTemplates import INCURSION_ENVIRONMENT_TEMPLATE_ID 22 from talecommon.const import INCURSION_TEMPLATE_CLASSES 23 from talecommon.const import templateClass 24 from eve.client.script.ui.control.eveLabel import Label 25 from eve.client.script.ui.control.eveLabel import EveLabelSmall, EveLabelLarge 26 import carbonui.const as uiconst 27 from carbonui.primitives.container import Container 28 from carbonui.primitives.transform import Transform 29 from carbonui.graphs.graph import GraphArea 30 from carbonui.graphs.grid import Grid 31 from carbonui.graphs.linegraph import LineGraph 32 from carbonui.util.color import Color 33 import carbonui.graphs.axislabels as axislabels 34 import carbonui.graphs.axis as axis 35 INCURSION_CHAT_WARNING_DELAY = 20 36 INCURSION_CHAT_CLOSE_DELAY = 120 37 COLOR_HIGH_SEC = (0.0, 38 0.65, 39 0.0, 40 1.0) 41 COLOR_LOW_NULL_SEC = (1.0, 42 0.0, 43 0.0, 44 1.0) 45 INCURSION_ENVIRONMENT_TEMPLATE = 2 46 47 class IncursionSvc(Service): 48 __guid__ = 'svc.incursion' 49 __notifyevents__ = ['OnTaleRemove', 'OnSessionChanged', 'OnTaleData'] 50 __startupdependencies__ = ['settings'] 51 52 def Run(self, *args): 53 Service.Run(self, *args) 54 self.incursionData = None 55 self.enableSoundOverrides = False 56 self.stationAudioPlaying = False 57 self.addStationSoundThreadRunning = False 58 self.isDisablingHud = False 59 self.isActivatingHud = False 60 self.incursionChatInfoByTaleId = {} 61 self.waitingIncursionChannelTaleID = set() 62 self.rewardsByID = {} 63 self.soundUrlByKey = {'hangar': 10004, 64 const.groupStation: 10007, 65 const.groupAsteroidBelt: 10003, 66 'enterSystem': 10005} 67 68 def OnTaleData(self, solarSystemID, data): 69 for taleData in data.itervalues(): 70 self.SetIncursion(taleData, fadeEffect=False, reason='Entered incursion system') 71 72 def SetIncursion(self, data, fadeEffect = False, reason = None): 73 if data.templateClassID in INCURSION_TEMPLATE_CLASSES: 74 self.LogInfo('Starting the incursion UI for tale', data.taleID, '. Reason:', reason) 75 self.incursionData = data 76 self.enableSoundOverrides = True 77 self.JoinIncursionChat(data.taleID) 78 soundURL = self.GetSoundUrlByKey('enterSystem') 79 if soundURL is not None: 80 sm.GetService('audio').SendUIEvent(soundURL) 81 if session.stationid is not None: 82 self.AddIncursionStationSoundIfApplicable() 83 sm.GetService('infoPanel').UpdateIncursionsPanel() 84 GetEnvironmentService().AddSystemEnvironment('incursion', INCURSION_ENVIRONMENT_TEMPLATE_ID) 85 86 def OnTaleRemove(self, taleID, templateClassID, templateID): 87 if templateClassID not in INCURSION_TEMPLATE_CLASSES: 88 return 89 if self.incursionData is None: 90 return 91 self.LogInfo('OnTaleRemove', taleID) 92 self.enableSoundOverrides = False 93 GetEnvironmentService().RemoveEnvironment('incursion') 94 self.incursionData = None 95 sm.GetService('infoPanel').UpdateIncursionsPanel() 96 self.StartTimeoutOfIncursionChat(taleID) 97 98 def OnSessionChanged(self, isremote, sess, change): 99 for taleID in self.incursionChatInfoByTaleId: 100 self.StartTimeoutOfIncursionChat(taleID) 101 102 if 'solarsystemid2' in change: 103 oldSolarsystem, newSolarsystem = change['solarsystemid2'] 104 if self.incursionData: 105 if self.IsSystemInIncursion(oldSolarsystem): 106 self.enableSoundOverrides = False 107 if self.IsSystemInIncursion(newSolarsystem): 108 self.enableSoundOverrides = True 109 else: 110 self.incursionData = None 111 sm.GetService('infoPanel').UpdateIncursionsPanel() 112 if 'stationid' in change: 113 self.stationAudioPlaying = False 114 oldStationID, newStationID = change['stationid'] 115 if newStationID != None: 116 self.AddIncursionStationSoundIfApplicable() 117 118 def IsSystemInIncursion(self, solarsystemID): 119 return self.incursionData is not None and solarsystemID in self.incursionData.incursedSystems 120 121 def GetMusicState(self, solarsystemid): 122 if self.IsSystemInIncursion(solarsystemid): 123 if self.incursionData is not None: 124 return getattr(self.incursionData, 'musicState', None) 125 126 def _EndTimeoutOfIncursionChat(self, taleID): 127 if taleID in self.waitingIncursionChannelTaleID: 128 self.waitingIncursionChannelTaleID.remove(taleID) 129 130 def IsTaleInTheCurrentSystem(self, taleID): 131 if self.incursionData is not None: 132 if self.incursionData.taleID == taleID: 133 return True 134 return False 135 136 def IsIncursionActive(self): 137 if self.incursionData is not None: 138 return self.incursionData.templateClassID in INCURSION_TEMPLATE_CLASSES 139 return False 140 141 def GetActiveIncursionData(self): 142 return self.incursionData 143 144 def TimeoutOfIncursionChat_Thread(self, taleID): 145 blue.pyos.synchro.SleepWallclock(INCURSION_CHAT_WARNING_DELAY * 1000) 146 if self.IsTaleInTheCurrentSystem(taleID): 147 self._EndTimeoutOfIncursionChat(taleID) 148 return 149 channelID = self._GetChannelId(taleID) 150 self._AnnounceChatClosure(channelID) 151 blue.pyos.synchro.SleepWallclock(INCURSION_CHAT_CLOSE_DELAY * 1000) 152 if self.IsTaleInTheCurrentSystem(taleID): 153 self._EndTimeoutOfIncursionChat(taleID) 154 return 155 GetChatService().LeaveChannel(channelID) 156 if taleID in self.incursionChatInfoByTaleId: 157 del self.incursionChatInfoByTaleId[taleID] 158 self._EndTimeoutOfIncursionChat(taleID) 159 160 def _GetChannelId(self, taleId): 161 return self.incursionChatInfoByTaleId[taleId]['incursionType'] + '_' + str(taleId) 162 163 def _AnnounceChatClosure(self, channelID): 164 window = GetChatService().GetGroupChatWindow(channelID) 165 if window: 166 window.Receive(const.ownerSystem, localization.GetByLabel('UI/Incursion/LeaveChat', minutesRemaining=INCURSION_CHAT_CLOSE_DELAY / 60)) 167 168 def StartTimeoutOfIncursionChat(self, taleID): 169 if taleID not in self.waitingIncursionChannelTaleID: 170 self.waitingIncursionChannelTaleID.add(taleID) 171 uthread.new(self.TimeoutOfIncursionChat_Thread, taleID).context = 'IncursionSvc::StartTimeoutOfIncursionChat' 172 173 def JoinIncursionChat(self, taleID): 174 uthread.new(self._JoinIncursionChat, taleID) 175 176 def _JoinIncursionChat(self, taleID): 177 while not sm.GetService('settings').IsCharSettingsLoaded(): 178 blue.synchro.Yield() 179 180 if not hasattr(self.incursionData, 'hasChat') or not self.incursionData.hasChat: 181 return 182 if taleID not in self.incursionChatInfoByTaleId: 183 self._SetIncursionChatInfo(taleID) 184 channelID = self._GetChannelId(taleID) 185 GetChatService().JoinChannel(channelID) 186 self._PostIncursionChatAnnouncement(channelID) 187 else: 188 channelID = self._GetChannelId(taleID) 189 GetChatService().JoinChannel(channelID) 190 191 def _PostIncursionChatAnnouncement(self, channelID): 192 retries = 0 193 while retries < 3: 194 window = GetChatService().GetGroupChatWindow(channelID) 195 if window: 196 break 197 blue.synchro.SleepWallclock(1000) 198 retries += 1 199 200 if not window or not self.incursionData.chatAnnouncementMessageId: 201 return 202 if self._IsConstellationIncursion(): 203 constellationName = cfg.evelocations.Get(session.constellationid).name 204 message = localization.GetByMessageID(self.incursionData.chatAnnouncementMessageId, constellationName=constellationName) 205 elif self._IsSpreadingIncursion(): 206 message = localization.GetByMessageID(self.incursionData.chatAnnouncementMessageId) 207 else: 208 return 209 window.Receive(const.ownerSystem, message) 210 211 def _SetIncursionChatInfo(self, taleID): 212 self.incursionChatInfoByTaleId[taleID] = {'constellationId': session.constellationid, 213 'distributionNameId': self.incursionData.templateNameID} 214 self.incursionChatInfoByTaleId[taleID]['incursionType'] = '' 215 if self._IsConstellationIncursion(): 216 self.incursionChatInfoByTaleId[taleID]['incursionType'] = 'incursion' 217 elif self._IsSpreadingIncursion(): 218 self.incursionChatInfoByTaleId[taleID]['incursionType'] = 'spreadingIncursion' 219 220 def _IsConstellationIncursion(self): 221 return self.incursionData.templateClassID == templateClass.incursion 222 223 def _IsSpreadingIncursion(self): 224 return self.incursionData.templateClassID == templateClass.spreadingIncursion 225 226 def GetConstellationNameFromTaleIDForIncursionChat(self, taleID): 227 if taleID in self.incursionChatInfoByTaleId and self.incursionChatInfoByTaleId[taleID]['constellationId']: 228 return cfg.evelocations.Get(self.incursionChatInfoByTaleId[taleID]['constellationId']).name 229 return '' 230 231 def GetDistributionName(self, taleId): 232 distributionNameId = self.incursionChatInfoByTaleId.get(taleId, None)['distributionNameId'] 233 if distributionNameId: 234 return localization.GetByMessageID(distributionNameId) 235 return localization.GetByLabel('UI/Generic/Unknown') 236 237 def GetDelayedRewards(self, rewardGroupID): 238 return self.GetDelayedRewardsByGroupIDs([rewardGroupID]).get(rewardGroupID) 239 240 def GetDelayedRewardsByGroupIDs(self, rewardGroupIDs): 241 rewardGroupIDs.sort() 242 rewardsByRewardGroupID = sm.RemoteSvc('rewardMgr').GetDelayedRewardsByGroupIDs(tuple(rewardGroupIDs)) 243 return rewardsByRewardGroupID 244 245 def DoRewardChart(self, rewardID, parent): 246 247 def GetRewardData(): 248 maxRewardValue = 0 249 minPlayerCount = 0 250 maxPlayerCount = 0 251 allSecurityBandTable = None 252 lowSecurityBandTable = None 253 highSecurityBandTable = None 254 rewardTables = get_immediate_reward_tables(rewardID) 255 for rewardTable in rewardTables: 256 if not rewardTables: 257 continue 258 if is_security_criteria_all(rewardTable): 259 maxRewardValue = get_max_reward_from_tables_with_reward_type(rewardTables, REWARD_TYPE_ISK) 260 minPlayerCount = get_min_player_count_from_reward_tables(rewardTables) 261 maxPlayerCount = get_max_player_count_from_reward_tables(rewardTables) 262 allSecurityBandTable = rewardTables[0] 263 break 264 if is_security_criteria_high(rewardTable): 265 highSecurityBandTable = rewardTable 266 elif is_security_criteria_low(rewardTable): 267 lowSecurityBandTable = rewardTable 268 else: 269 continue 270 maxRewardValue = max(maxRewardValue, get_max_reward_from_tables_with_reward_type(rewardTables, REWARD_TYPE_ISK)) 271 minPlayerCount = min(minPlayerCount, get_min_player_count_from_reward_tables(rewardTables)) 272 maxPlayerCount = max(maxPlayerCount, get_max_player_count_from_reward_tables(rewardTables)) 273 274 scale = 1.0 / maxRewardValue 275 majorTick = (maxPlayerCount - minPlayerCount) / 4 276 majorTick = 1 if majorTick < 2 else majorTick 277 data1 = [] 278 data2 = [] if allSecurityBandTable is None else None 279 labels = [] 280 for x in xrange(minPlayerCount, maxPlayerCount + 1): 281 if allSecurityBandTable is not None: 282 quantity = get_reward_for_player_count(allSecurityBandTable, x) * scale 283 data1.append(quantity) 284 else: 285 quantityHigh = get_reward_for_player_count(highSecurityBandTable, x) * scale 286 quantityLow = get_reward_for_player_count(lowSecurityBandTable, x) * scale 287 data1.append(quantityHigh) 288 data2.append(quantityLow) 289 labels.append(x) 290 291 return (data1, 292 data2, 293 labels, 294 majorTick) 295 296 valuesHigh, valuesLow, players, majorTick = GetRewardData() 297 axisValues = (0.0, 0.5, 1.0) 298 main = PanelUnderlay(parent=parent, align=uiconst.TOALL, bgColor=Color.GetGrayRGBA(0.0, 0.25)) 299 300 def valueFilter(x): 301 if x not in axisValues: 302 return '' 303 return str(x) 304 305 def playerFilter(x): 306 if x % majorTick != 0: 307 return '' 308 return str(int(x)) 309 310 verticalAxis = axis.AutoTicksAxis((0.0, 1.0), tickCount=5, margins=(0.01, 0.01), labelFormat=valueFilter) 311 horizontalAxis = axis.CategoryAxis(players, labelFormat=playerFilter) 312 EveLabelLarge(parent=main, align=uiconst.TOTOP, padTop=6, padBottom=0, text='<center>%s</center>' % localization.GetByLabel('UI/Incursion/Reward/Title')) 313 EveLabelSmall(parent=main, align=uiconst.TOBOTTOM, width=120, padBottom=6, text='<center>%s</center>' % localization.GetByLabel('UI/Incursion/Reward/NumberPilots')) 314 axislabels.AxisLabels(parent=main, align=uiconst.TORIGHT, width=24, axis=verticalAxis, orientation=axis.AxisOrientation.VERTICAL, minFactor=1.0, maxFactor=0.0, padBottom=17, padTop=7) 315 axislabels.AxisLabels(parent=main, align=uiconst.TOBOTTOM, height=24, axis=horizontalAxis, orientation=axis.AxisOrientation.HORIZONTAL, textAlignment=uiconst.CENTER, padLeft=24) 316 sideTransform = Transform(name='myTransform', parent=main, align=uiconst.TOLEFT, width=24, rotationCenter=(0.5, 0.5), rotation=math.pi / 2) 317 EveLabelSmall(name='myLabel', parent=sideTransform, width=120, align=uiconst.CENTER, text='<center>%s</center>' % localization.GetByLabel('UI/Incursion/Reward/PayoutMultiplier')) 318 legendContainer = Container(name='container', parent=main, height=14, align=uiconst.TOTOP) 319 graphArea = GraphArea(name='graph', parent=main, align=uiconst.TOALL, bgColor=Color.GetGrayRGBA(0.2, 0.5)) 320 graphArea.AddAxis(axis.AxisOrientation.HORIZONTAL, horizontalAxis, 0.0, 1.0) 321 graphArea.AddAxis(axis.AxisOrientation.VERTICAL, verticalAxis, 1.0, 0.0) 322 Grid(parent=graphArea, axis=verticalAxis, orientation=axis.AxisOrientation.VERTICAL, color=Color.GetGrayRGBA(1.0, 0.4)) 323 324 def _createLegend(items, legendParent): 325 container = legendParent 326 for item, text in items: 327 item.height = 10 328 item.width = 10 329 item.align = uiconst.TOLEFT 330 item.padding = (6, 1, 2, 3) 331 item.SetParent(container) 332 Label(parent=container, align=uiconst.TOLEFT, text=text, color=(1.0, 1.0, 1.0, 0.5), fontsize=carbonui.fontconst.EVE_SMALL_FONTSIZE) 333 334 def _lineGraph(color, values): 335 ranges = [] 336 i0 = 0 337 while i0 < len(values) - 1: 338 val0 = values[i0] 339 repeatRange = [i0, i0] 340 i1 = i0 + 1 341 while i1 < len(values): 342 val1 = values[i1] 343 if val1 != val0: 344 break 345 else: 346 repeatRange[1] = i1 347 i1 += 1 348 349 i0 = i1 350 if repeatRange[0] != repeatRange[1]: 351 ranges.append(repeatRange) 352 353 dataPoints = range(len(values)) 354 valuesPruned = values[:] 355 ranges.reverse() 356 for i0, i1 in ranges: 357 valuesPruned = valuesPruned[:i0 + 1] + valuesPruned[i1:] 358 dataPoints = dataPoints[:i0 + 1] + dataPoints[i1:] 359 360 return LineGraph(parent=graphArea, categoryAxis=horizontalAxis, valueAxis=verticalAxis, values=valuesPruned, lineColor=color, lineWidth=1, dataPoints=dataPoints) 361 362 l0 = _lineGraph(COLOR_HIGH_SEC, valuesHigh) 363 label1 = localization.GetByLabel('UI/Common/Ratio') 364 if valuesLow is not None: 365 label1 = localization.GetByLabel('UI/Common/HighSec') 366 legendItems = [(l0.GetLegendItem(), label1)] 367 if valuesLow is not None: 368 l1 = _lineGraph(COLOR_LOW_NULL_SEC, valuesLow) 369 legendItems.append((l1.GetLegendItem(), localization.GetByLabel('UI/Common/LowNullSec'))) 370 _createLegend(legendItems, legendContainer) 371 372 def GetSoundUrlByKey(self, key): 373 if self.enableSoundOverrides == False: 374 return 375 soundID = self.soundUrlByKey.get(key, None) 376 if soundID is not None: 377 soundUrl = GetSoundEventName(soundID) 378 if soundUrl is None: 379 self.LogError('Unable to find a sound for key', key, 'and soundID', soundID) 380 else: 381 soundUrl = None 382 return soundUrl 383 384 def AddIncursionStationSoundIfApplicable(self): 385 if not self.addStationSoundThreadRunning and self.enableSoundOverrides: 386 self.addStationSoundThreadRunning = True 387 uthread.new(self.AddIncursionStationSoundIfApplicableThread).context = 'incursionSvc::AddIncursionStationSoundIfApplicableThread' 388 389 def AddIncursionStationSoundIfApplicableThread(self): 390 count = 0 391 success = False 392 while success == False and count < 60: 393 blue.synchro.SleepWallclock(1000) 394 count += 1 395 if session.stationid is None: 396 success = True 397 break 398 try: 399 if not self.stationAudioPlaying: 400 activeScene = sm.GetService('sceneManager').GetRegisteredScene('hangar') 401 stringToMatch = 'invisible_sound_locator_' 402 soundLocator = FindSoundLocatorThatStartsWith(activeScene, stringToMatch) 403 addedSound = self.GetSoundUrlByKey('hangar') 404 if addedSound is not None and soundLocator is not None: 405 ReplaceHangarSound(soundLocator, addedSound) 406 self.stationAudioPlaying = True 407 success = True 408 except: 409 self.LogInfo('Could not add incursion station sound trying again in 1 second') 410 411 if success == False: 412 self.LogError('Incursion station audio could not be added after 60 tries') 413 self.addStationSoundThreadRunning = False 414 415 416 def FindSoundLocatorThatStartsWith(scene, startingString): 417 for transform in scene.objects: 418 if transform.name.startswith(startingString): 419 return transform 420 421 422 def ReplaceHangarSound(soundLocator, soundEvent): 423 if not soundLocator.observers: 424 return 425 for objects in soundLocator.observers: 426 objects.observer.SendEvent('fade_out') 427 428 soundLocator.observers[0].observer.SendEvent(soundEvent[6:])