characterSelection.py (40862B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\login\charSelection\characterSelection.py 2 import logging 3 import paperdoll 4 import blue 5 from carbon.client.script.environment.AudioUtil import PlaySound 6 from carbon.common.script.net import moniker 7 from carbon.common.script.sys.serviceConst import ROLEMASK_ELEVATEDPLAYER 8 from carbonui.control.layer import LayerCore 9 from carbonui.primitives.fill import Fill 10 from carbonui.primitives.sprite import Sprite 11 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel 12 from player_messaging.client.ui_message import message_player, prompt_player_fsd_dialog 13 from eve.client.script.ui.login.charSelection.surveyBanner import SurveyBanner 14 from eve.client.script.ui.util import uix 15 from eve.client.script.ui.util.uiComponents import ButtonEffect, Component 16 import uthread 17 import log 18 import carbonui.const as uiconst 19 import localization 20 import evetypes 21 from carbonui.primitives.container import Container 22 from carbonui.control.buttonIcon import ButtonIcon 23 from eve.client.script.ui.shared.redeem.redeemPanel import GetRedeemPanel 24 from eve.client.script.ui.login.charSelection.lapsedGametimeWarning import LapsedGametimeWarning 25 from eve.client.script.ui.shared.cloneGrade.multiLoginBlockedWindow import MultiLoginBlockedWindow 26 from eve.client.script.ui.shared.cloneGrade import ORIGIN_CHARACTERSELECTION 27 from eve.common.lib import appConst 28 from eveexceptions import ServiceNotFound, UserError, ExceptionEater 29 from eve.client.script.ui.login.charSelection.characterSlots import SmallCharacterSlot, SmallEmptySlot 30 from eve.client.script.ui.login.charSelection.characterSlots import CharacterDetailsLocation as CharLocation 31 import evegraphics.settings as gfxsettings 32 import eve.client.script.ui.login.charSelection.characterSelectionUtils as csUtil 33 import eve.client.script.ui.login.charSelection.characterSelectionColors as csColors 34 from carbonui.uicore import uicore 35 from eveprefs import boot 36 import launchdarkly 37 LOGO_WIDTH = 205 38 if boot.region == 'optic': 39 LOGO_WIDTH = 551 40 LOGO_HEIGHT = 81 41 LOGO_COLOR = (1, 42 1, 43 1, 44 1) 45 MINIMUM_LOGOHEIGHT = 50 46 LOGO_PADDING = 40 47 BG_WIDTH = 2117 48 BG_HEIGHT = 1200 49 FEATURE_BAR_HEIGHT = 100 50 FEATURE_BAR_INNER_HEIGHT = 90 51 BANNER_WIDTH = 550 52 WARNING_BANNER_HEIGHT = 40 53 logger = logging.getLogger(__name__) 54 55 class CharacterSelection(LayerCore): 56 __notifyevents__ = ['OnSetDevice', 57 'OnJumpQueueMessage', 58 'OnCharacterHandler', 59 'OnUIRefresh', 60 'OnUIScalingChange', 61 'OnGraphicSettingsChanged', 62 'OnUIoffsetChanged'] 63 isTopLevelWindow = True 64 minSidePadding = 50 65 66 def ApplyAttributes(self, attributes): 67 self.bannerAdService = None 68 self.isInitialLogin = True 69 super(CharacterSelection, self).ApplyAttributes(attributes) 70 71 def OnSetDevice(self): 72 if not self.isopen: 73 return 74 self.OnRefreshScreen() 75 76 def OnUIRefresh(self): 77 self.OnRefreshScreen() 78 79 def OnUIScalingChange(self, *args): 80 self.OnRefreshScreen() 81 82 def OnGraphicSettingsChanged(self, changes): 83 if gfxsettings.UI_CAMERA_OFFSET in changes: 84 self.ChangeUIoffset() 85 86 def OnUIoffsetChanged(self): 87 self.ChangeUIoffset() 88 89 def ChangeUIoffset(self): 90 cameraOffset = settings.user.ui.offsetUIwithCamera 91 uiOffsetWithCamera = settings.user.ui.cameraOffset 92 if self.uiOffset != (cameraOffset, uiOffsetWithCamera): 93 self.OnRefreshScreen() 94 95 def OnCloseView(self): 96 self.isTabStop = False 97 screen = self.selectionScreen 98 self.selectionScreen = None 99 self.ClearBackground() 100 if screen is not None and not screen.destroyed: 101 screen.Close() 102 self.characterSlotList = [] 103 self.slotsByCharID.clear() 104 self.slotsByIdx.clear() 105 self.ResetVariablesForUiElements() 106 107 def OnJumpQueueMessage(self, msgtext, ready): 108 if ready: 109 log.LogInfo('Jump Queue: ready, slamming through...') 110 self.__Confirm(sm.GetService('jumpQueue').GetPreparedQueueCharID()) 111 else: 112 log.LogInfo('Jump Queue: message=', msgtext) 113 message_player(msgtext) 114 115 def OnCharacterHandler(self, *_): 116 self.SetData() 117 self.OnRefreshScreen() 118 119 def GetChars(self): 120 return sm.GetService('cc').GetCharactersToSelect() 121 122 def ReduceCharacterGraphics(self): 123 gfxsettings.Set(gfxsettings.GFX_CHAR_FAST_CHARACTER_CREATION, True, pending=False) 124 gfxsettings.Set(gfxsettings.GFX_CHAR_TEXTURE_QUALITY, 2, pending=False) 125 126 def OnOpenView(self, **kwargs): 127 self.isTabStop = True 128 self.remoteCharacterSvc = sm.RemoteSvc('charUnboundMgr') 129 self.mapSvc = sm.GetService('map') 130 self.SetData() 131 self.OnRefreshScreen() 132 self.AnimateScreenIn() 133 sm.GetService('audio').SendUIEvent('character_selection_start') 134 with ExceptionEater('Failing to perform survey checks'): 135 sm.GetService('survey').PerformSurveyChecks() 136 uthread.new(evetypes.GetTypeIDByNameDict) 137 charId = blue.os.GetStartupArgValue('character') 138 if charId: 139 self.ready = True 140 self.ConfirmWithCharID(int(charId)) 141 isElevated = session.role & ROLEMASK_ELEVATEDPLAYER 142 if isElevated and settings.public.ui.Get('Insider', True): 143 try: 144 insider = sm.GetService('insider') 145 uthread.new(insider.Show, True, True) 146 except ServiceNotFound: 147 pass 148 149 is_auto_select = self._CheckAutoSelectCharacter() 150 self._CheckOpenRewardsWindow(is_auto_select) 151 self.isInitialLogin = False 152 153 def _CheckOpenRewardsWindow(self, is_auto_select): 154 if not self._ShouldOpenRewardsWindow(): 155 return 156 if is_auto_select: 157 sm.GetService('loginCampaignService').blink_reward_window_on_dock() 158 else: 159 with ExceptionEater('Failing to open reward window'): 160 uthread.new(self._OpenRewardsWindow) 161 162 def _ShouldOpenRewardsWindow(self): 163 if sm.GetService('loginCampaignService').should_open_reward_window_on_login(): 164 return True 165 if sm.GetService('seasonalLoginCampaignService').should_open_reward_window_on_login(): 166 return True 167 if sm.GetService('loginCampaignService').should_reopen_DLI_window(): 168 return True 169 return False 170 171 def _CheckAutoSelectCharacter(self): 172 if not self.isInitialLogin: 173 return False 174 try: 175 for arg in blue.pyos.GetArg()[1:]: 176 if arg.startswith('/autoSelectCharacter:'): 177 try: 178 slotNum = int(arg.split(':')[1]) 179 slot = self.slotsByCharID.get(slotNum, None) 180 if not slot: 181 slot = self.slotsByIdx[slotNum] 182 except: 183 raise RuntimeError('Format of parameter incorrect, should be /autoSelectCharacter:slotID<int> (slotNum can characterID or index (0, 1 or 2)') 184 185 self.EnterGameWithCharacter(slot) 186 return True 187 188 except Exception as e: 189 logger.exception(e) 190 191 return False 192 193 def _OpenRewardsWindow(self): 194 from eve.client.script.ui.shared.loginRewards.loginRewardsWnd import DailyLoginRewardsWnd 195 wnd = DailyLoginRewardsWnd.Open() 196 wnd.Lock() 197 wnd.ShowDialog(modal=True, closeWhenClicked=True) 198 199 def SetData(self, force = False): 200 characterSelectionData = self.GetCharacterSelectionData(force=force) 201 self.chars = characterSelectionData.GetChars() 202 self.subscriptionEndTimes = characterSelectionData.GetSubscriptionEndTime() 203 self.numSlotsOwnedByUser = characterSelectionData.GetNumCharacterSlots() 204 self.slotsToDisplay = characterSelectionData.GetMaxServerCharacters() 205 self.surveyID = None 206 uthread.new(self.FetchSurveyID) 207 208 def FetchSurveyID(self): 209 self.surveyID = sm.GetService('survey').GetActiveSurveyID() 210 if self.surveyID: 211 self.DisplaySurveyBanner() 212 213 def OnRefreshScreen(self): 214 if self._IsSelectionScreenDisabled(): 215 return 216 uicore.registry.SetFocus(self) 217 self.ready = False 218 self.countDownThread = None 219 self.characterSlotList = [] 220 self.adSprite = None 221 self.uiOffset = (0, 0) 222 self.maxFullSlotSize = None 223 self.Flush() 224 self.ClearBackground() 225 self.selectionScreen = Container(name='selectionScreen', parent=self, state=uiconst.UI_PICKCHILDREN) 226 self.InitUI() 227 self.AddBackground() 228 self.AddFeatureContainer() 229 self.LoadCharacterSlots() 230 self.AdjustFeatureBarPosition() 231 self.CollapseOrExpandSlots(animate=False, loadingSlots=False) 232 self.AdjustLogo() 233 self.AddLoginRewardWindowButton() 234 self.loadingWheel = LoadingWheel(parent=self.selectionScreen, align=uiconst.CENTER, state=uiconst.UI_NORMAL, idx=0) 235 self.loadingWheel.display = False 236 237 def AddLoginRewardWindowButton(self): 238 with ExceptionEater('Failing to add reward button'): 239 loginCampaignService = sm.GetService('loginCampaignService') 240 if loginCampaignService.should_reward_btn_be_visible(): 241 ButtonIcon(parent=self.featureContainer, align=uiconst.BOTTOMRIGHT, texturePath='res:/UI/Texture/\\WindowIcons/gift.png', iconSize=64, width=64, height=64, padRight=30, padLeft=-30, state=uiconst.UI_NORMAL, func=self._OpenRewardsWindow, idx=0) 242 243 def _IsSelectionScreenDisabled(self): 244 return self._IsSelectionScreenAvailable() and self.selectionScreen.state == uiconst.UI_DISABLED 245 246 def _IsSelectionScreenAvailable(self): 247 return hasattr(self, 'selectionScreen') and self.selectionScreen and not self.selectionScreen.destroyed 248 249 def AnimateScreenIn(self): 250 uicore.animations.MorphScalar(self.bg, 'opacity', startVal=0.0, endVal=1.0, duration=1.0) 251 uicore.animations.MorphScalar(self.logo, 'opacity', startVal=0.0, endVal=1.0, duration=0.5, timeOffset=0.5) 252 uicore.animations.MorphScalar(self.featureContainer, 'opacity', startVal=0.0, endVal=1.0, duration=0.5, timeOffset=1.5) 253 slotDelay = 0.5 254 uthread.new(self.PlaySound_thread, event='character_selection_animstart', sleepTime=slotDelay) 255 for idx, eachSlot in self.slotsByIdx.iteritems(): 256 baseAnimationOffset = slotDelay + idx * 0.1 257 eachSlot.AnimateSlotIn(animationOffset=baseAnimationOffset, soundFunction=self.PlaySound_thread, charContHeight=self.charactersCont.height) 258 259 def PlaySound_thread(self, event, sleepTime): 260 blue.pyos.synchro.Sleep(1000 * sleepTime) 261 sm.GetService('audio').SendUIEvent(event) 262 263 def AnimateScreenOut(self, excludeSlotForCharID): 264 self.selectionScreen.state = uiconst.UI_DISABLED 265 slotDelay = 0.5 266 slots = self.slotsByIdx.values() 267 slots = sorted(slots, key=lambda x: x.slotIdx, reverse=True) 268 counter = 0 269 uthread.new(self.PlaySound_thread, event='character_selection_animstart', sleepTime=slotDelay) 270 for eachSlot in slots: 271 if eachSlot.charID == excludeSlotForCharID: 272 continue 273 baseAnimationOffset = slotDelay + counter * 0.1 274 eachSlot.AnimateSlotOut(animationOffset=baseAnimationOffset, soundFunction=self.PlaySound_thread, charContHeight=self.charactersCont.height) 275 counter += 1 276 277 uicore.animations.MorphScalar(self.logo, 'opacity', startVal=1.0, endVal=0.0, duration=0.5, timeOffset=2.0) 278 uicore.animations.MorphScalar(self.featureContainer, 'opacity', startVal=1.0, endVal=0.0, duration=0.3, timeOffset=0) 279 self.redeemPanel.FadeOut() 280 281 def _HandleSubscriptionDowngradeStatus(self): 282 try: 283 if self.remoteCharacterSvc.GetCharOmegaDowngradeStatus(): 284 self.warningBar.height = self._GetWarningHeight() 285 self.lapsedWarning = LapsedGametimeWarning(parent=self.warningBar, name='lapsed_sub_warning', height=0) 286 uicore.animations.MorphScalar(self.lapsedWarning, 'height', startVal=self.lapsedWarning.height, endVal=self.lapsedWarning.default_height, duration=0.1, timeOffset=0) 287 except Exception: 288 log.LogTraceback('Failed to construct lapsed game time warning in character selection') 289 290 def EnableScreen(self): 291 self.selectionScreen.state = uiconst.UI_PICKCHILDREN 292 self.AnimateScreenIn() 293 294 def GetCharacterSelectionData(self, force = False): 295 return sm.GetService('cc').GetCharacterSelectionData(force=force) 296 297 def ResetVariablesForUiElements(self): 298 self.redeemPanel = None 299 self.topBorder = None 300 self.warningBar = None 301 self.centerArea = None 302 self.logoCont = None 303 self.logo = None 304 self.charactersCont = None 305 self.adContainer = None 306 self.featureContainer = None 307 self.openStoreContainer = None 308 309 def InitUI(self): 310 self.selectionScreen.Flush() 311 redeemPanelClass = GetRedeemPanel() 312 self.redeemPanel = redeemPanelClass(name='redeemPanel', width=uicore.desktop.width, align=uiconst.TOBOTTOM, parent=self.selectionScreen, animationDuration=csUtil.COLLAPSE_TIME, dragEnabled=True, instructionText=localization.GetByLabel('UI/RedeemWindow/DragAndDropToGive'), redeemButtonBorderColor=csColors.REDEEM_BORDER, redeemButtonBackgroundColor=csColors.REDEEM_BORDER_BACKGROUND, redeemButtonFillColor=csColors.REDEEM_BORDER_FILL, textColor=csColors.REDEEM_PANEL_AVAILABLE_TEXT, redeemPanelBackgroundColor=csColors.REDEEM_PANEL_FILL, onVisibilityChanged=self.OnRedeemPanelVisibilityChanged, onCollapsibilityChanged=self.OnRedeemPanelCollapsibilityChanged) 313 self.topBorder = Container(name='topBorder', parent=self.selectionScreen, align=uiconst.TOTOP_NOPUSH, height=40, state=uiconst.UI_PICKCHILDREN) 314 warningWrapper = Container(name='warningWrapper', parent=self.selectionScreen, align=uiconst.TOALL) 315 self.warningBar = Container(name='warningBar', parent=warningWrapper, align=uiconst.TOTOP, height=0, state=uiconst.UI_PICKCHILDREN) 316 self.centerArea = Container(name='centerAra', parent=self.selectionScreen, align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN) 317 self.logoCont = Container(parent=self, name='logoCont', align=uiconst.TOTOP_NOPUSH, height=100, state=uiconst.UI_NORMAL) 318 self.logo = Sprite(parent=self.logoCont, texturePath='res:/UI/Texture/classes/CharacterSelection/logo.png', align=uiconst.CENTER, pos=(0, 319 0, 320 LOGO_WIDTH, 321 LOGO_HEIGHT), color=LOGO_COLOR) 322 self.charactersCont = Container(name='charactersCont', parent=self.centerArea, align=uiconst.CENTER, state=uiconst.UI_PICKCHILDREN, width=1050, height=600) 323 self.SetupCharacterSlots() 324 uthread.new(self._HandleSubscriptionDowngradeStatus) 325 326 def AddBackground(self): 327 clientHeight = uicore.desktop.height 328 percentOfClientHeight = float(clientHeight) / BG_HEIGHT 329 newHeight = clientHeight 330 newWidth = int(percentOfClientHeight * BG_WIDTH) 331 self.bg = Sprite(parent=uicore.desktop, name='charselBackground', texturePath='res:/UI/Texture/classes/CharacterSelection/background.png', align=uiconst.CENTER, pos=(0, 332 0, 333 newWidth, 334 newHeight), state=uiconst.UI_DISABLED) 335 if csUtil.IsMonochromeStyleEnabled(): 336 csUtil.SetMonochromeStyle(self.bg, True) 337 self.bgOverlay = Fill(bgParent=uicore.desktop, color=(0, 0, 0, 1.0)) 338 push = sm.GetService('window').GetCameraLeftOffset(self.bg.width, align=uiconst.CENTER, left=0) 339 self.bg.left = push 340 341 def ClearBackground(self): 342 if getattr(self, 'bg', None): 343 self.bg.Close() 344 self.bgOverlay.Close() 345 if getattr(self, 'logoCont', None): 346 self.logoCont.Close() 347 348 def ResetOpenStoreVisibility(self): 349 self.openStoreContainer.StopAnimations() 350 self.openStoreContainer.opacity = 1.0 351 352 def FadeInOpenStoreContainer(self, duration): 353 self.openStoreContainer.StopAnimations() 354 uicore.animations.FadeIn(self.openStoreContainer, duration=duration) 355 356 def FadeOutOpenStoreContainer(self, duration): 357 self.openStoreContainer.StopAnimations() 358 uicore.animations.FadeOut(self.openStoreContainer, duration=duration) 359 360 def OnRedeemPanelVisibilityChanged(self): 361 self.ResetOpenStoreVisibility() 362 self.AdjustFeatureBarPosition() 363 self.CollapseOrExpandSlots(animate=False, loadingSlots=False) 364 self.AdjustLogo() 365 366 def OnRedeemPanelCollapsibilityChanged(self): 367 if self.redeemPanel.IsCollapsed(): 368 self.FadeInOpenStoreContainer(duration=csUtil.COLLAPSE_TIME) 369 else: 370 self.FadeOutOpenStoreContainer(duration=csUtil.COLLAPSE_TIME) 371 372 def CollapseOrExpandSlots(self, animate = True, loadingSlots = False): 373 shouldShipBeVisible = self.ShouldShipBeVisible() 374 self.ChangeSlotCollapsedState(animate=animate, loadingSlots=loadingSlots) 375 for eachSlot in self.slotsByIdx.itervalues(): 376 if shouldShipBeVisible: 377 eachSlot.ExpandSlot(animate=animate) 378 else: 379 eachSlot.CollapseSlot(animate=animate) 380 381 def ShouldShipBeVisible(self): 382 if self.maxFullSlotSize: 383 maxFullSlotSize = self.maxFullSlotSize 384 else: 385 l, ct, cw, ch = self.charactersCont.GetAbsolute() 386 maxFullSlotSize = ch 387 redeemPanelHeight = self.redeemPanel.height 388 emptySpace = uicore.desktop.height - maxFullSlotSize 389 if emptySpace < 2 * (MINIMUM_LOGOHEIGHT + LOGO_PADDING) or emptySpace < FEATURE_BAR_HEIGHT + redeemPanelHeight: 390 shipVisible = False 391 else: 392 shipVisible = True 393 return shipVisible 394 395 def ChangeSlotCollapsedState(self, animate, loadingSlots = False): 396 shouldShipBeVisible = self.ShouldShipBeVisible() 397 maxCurrentCharacterSlotHeight = self.GetMaxCharacterSlotHeight(shipVisible=shouldShipBeVisible) 398 charactersContTop = 0 399 diff = self.charactersCont.height - maxCurrentCharacterSlotHeight 400 if animate or loadingSlots and not self.redeemPanel.IsCollapsed(): 401 charactersContTop = min(0, int(-diff / 2.0)) 402 freeSpace = uicore.desktop.height - maxCurrentCharacterSlotHeight 403 404 def FindExtraShift(componentHeight): 405 shift = 0 406 if freeSpace / 2.0 < componentHeight: 407 shift = componentHeight - int(freeSpace / 2.0) 408 return shift 409 410 extraShift = 0 411 if self.redeemPanel.IsVisible() and not self.redeemPanel.IsCollapsed(): 412 extraShift = FindExtraShift(self.redeemPanel.height) 413 if extraShift: 414 charactersContTop = min(charactersContTop, -extraShift) 415 uicore.animations.MorphScalar(self.logo, 'opacity', startVal=self.logo.opacity, endVal=1.0, duration=csUtil.COLLAPSE_TIME) 416 if extraShift: 417 cl, ct, cw, ch = self.charactersCont.GetAbsolute() 418 if ct - extraShift < self.logoCont.height - LOGO_PADDING / 2: 419 uicore.animations.MorphScalar(self.logo, 'opacity', startVal=self.logo.opacity, endVal=0.05, duration=csUtil.COLLAPSE_TIME) 420 if animate: 421 uicore.animations.MorphScalar(self.charactersCont, 'height', startVal=self.charactersCont.height, endVal=maxCurrentCharacterSlotHeight, duration=csUtil.COLLAPSE_TIME) 422 uicore.animations.MorphScalar(self.charactersCont, 'top', startVal=self.charactersCont.top, endVal=charactersContTop, duration=csUtil.COLLAPSE_TIME) 423 else: 424 uicore.animations.StopAnimation(self.charactersCont, 'height') 425 self.charactersCont.height = maxCurrentCharacterSlotHeight 426 self.charactersCont.top = charactersContTop 427 428 def GetSpaceForRedeemPanel(self): 429 return min(self.redeemPanel.height, self.redeemPanel.GetCollapsedHeight()) 430 431 def AdjustFeatureBarPosition(self): 432 shouldShipBeVisible = self.ShouldShipBeVisible() 433 maxCurrentCharacterSlotHeight = self.GetMaxCharacterSlotHeight(shouldShipBeVisible) 434 redeemPanelHeight = self.GetSpaceForRedeemPanel() 435 availableHeight = int((uicore.desktop.height - maxCurrentCharacterSlotHeight) / 2.0) - redeemPanelHeight 436 self.featureContainer.top = int(availableHeight / 2.0) + redeemPanelHeight - FEATURE_BAR_HEIGHT / 2 437 438 def GetMaxCharacterSlotHeight(self, shipVisible = True): 439 if self.characterSlotList: 440 return max((slot.GetSlotHeight(shipVisible=shipVisible) for slot in self.characterSlotList)) 441 else: 442 return max([ slot.GetSlotHeight(shipVisible=shipVisible) for slot in self.slotsByIdx.itervalues() ]) 443 444 def SetupCharacterSlots(self): 445 self.characterSlotList = [] 446 self.slotsByCharID = {} 447 self.slotsByIdx = {} 448 self.slotsToDisplay = min(self.numSlotsOwnedByUser + 1, self.slotsToDisplay) 449 paddingFromImage = SmallCharacterSlot.GetExtraWidth() 450 spaceForEachSlot = (uicore.desktop.width - 2 * self.minSidePadding) / self.slotsToDisplay 451 maxSize = SmallCharacterSlot.maxImageSize + paddingFromImage 452 spaceForEachSlot = min(maxSize, spaceForEachSlot) 453 occupiedSlots = len(self.chars[:self.slotsToDisplay]) 454 for idx in xrange(occupiedSlots): 455 slot = SmallCharacterSlot(name='characterSlot_%s' % idx, parent=self.charactersCont, callback=self.EnterGameWithCharacter, deleteCallback=self.Terminate, undoDeleteCallback=self.UndoTermination, terminateCallback=self.Terminate, slotIdx=idx, width=spaceForEachSlot) 456 slot.OnMouseEnter = (self.OnSlotOnMouseEnter, slot) 457 slot.OnMouseExit = (self.OnSlotMouseExit, slot) 458 self.characterSlotList.append(slot) 459 self.slotsByIdx[idx] = slot 460 461 for idx in xrange(occupiedSlots, self.slotsToDisplay): 462 if idx > self.numSlotsOwnedByUser - 1: 463 callback = self.GoBuySlot 464 ownSlot = False 465 else: 466 callback = self.CreateCharacter 467 ownSlot = True 468 slot = SmallEmptySlot(name='emptySlot_%s' % idx, parent=self.charactersCont, callback=callback, slotIdx=idx, width=spaceForEachSlot, ownSlot=ownSlot) 469 self.slotsByIdx[idx] = slot 470 slot.OnMouseEnter = (self.OnSlotOnMouseEnter, slot) 471 slot.OnMouseExit = (self.OnSlotMouseExit, slot) 472 473 self.charactersCont.width = spaceForEachSlot * self.slotsToDisplay 474 self.SetUIOffset() 475 476 def SetUIOffset(self): 477 cameraOffset = settings.user.ui.offsetUIwithCamera 478 uiOffsetWithCamera = settings.user.ui.cameraOffset 479 push = sm.GetService('window').GetCameraLeftOffset(self.charactersCont.width, align=uiconst.CENTER, left=0) 480 self.uiOffset = (cameraOffset, uiOffsetWithCamera) 481 self.charactersCont.left = push 482 self.logo.left = push 483 484 def LoadCharacterSlots(self): 485 allSlots = self.characterSlotList[:] 486 for characterInfo in self.chars[:self.slotsToDisplay]: 487 charID = characterInfo.characterID 488 characterSlot = allSlots.pop(0) 489 self.LoadSlotForCharacter(charID, characterSlot) 490 characterSlot.SetMouseExitState() 491 492 if self.characterSlotList: 493 maxShipIconHeight = max((slot.GetShipAndLocationContHeight() for slot in self.characterSlotList)) 494 else: 495 shipPadding = CharLocation.paddingShipAlignmentTop + CharLocation.paddingShipAlignmentBottom 496 maxShipIconHeight = CharLocation.minShipSize + CharLocation.locationContHeight + shipPadding 497 for slot in self.slotsByIdx.itervalues(): 498 slot.SetShipContHeight(maxShipIconHeight) 499 500 maxSlotHeight = self.GetMaxCharacterSlotHeight() 501 self.maxFullSlotSize = maxSlotHeight 502 self.charactersCont.height = maxSlotHeight 503 self.ready = True 504 505 def LoadSlotForCharacter(self, charID, characterSlot): 506 self.slotsByCharID[charID] = characterSlot 507 characterDetails = self.GetCharacterSelectionData().GetCharInfo(charID) 508 characterSlot.LoadSlot(charID, characterDetails) 509 510 def AddFeatureContainer(self): 511 distanceFromBottom = self.GetSpaceForRedeemPanel() 512 self.featureContainer = Container(parent=self.selectionScreen, name='featureContainer', align=uiconst.TOBOTTOM_NOPUSH, height=FEATURE_BAR_HEIGHT, top=distanceFromBottom) 513 innerFeatureContainer = Container(parent=self.featureContainer, name='innerFeatureCont', align=uiconst.CENTER, width=834, height=FEATURE_BAR_HEIGHT) 514 self.openStoreContainer = Container(name='openStoreContainer', parent=innerFeatureContainer, state=uiconst.UI_PICKCHILDREN, align=uiconst.CENTER, width=270, height=FEATURE_BAR_INNER_HEIGHT) 515 OpenStoreButton(parent=self.openStoreContainer, align=uiconst.CENTER, onClick=uicore.cmd.ToggleAurumStore) 516 self.adContainer = Container(name='adContainer', parent=innerFeatureContainer, align=uiconst.CENTERRIGHT, width=BANNER_WIDTH, height=FEATURE_BAR_INNER_HEIGHT) 517 if self.surveyID: 518 self.DisplaySurveyBanner() 519 push = sm.GetService('window').GetCameraLeftOffset(innerFeatureContainer.width, align=uiconst.CENTER, left=0) 520 innerFeatureContainer.left = push 521 522 def DisplaySurveyBanner(self): 523 with ExceptionEater('Failing to load survey banner'): 524 if not getattr(self, 'adContainer', None) or self.adContainer.destroyed: 525 return 526 if getattr(self, 'adSprite', None): 527 self.adSprite.Close() 528 self.adSprite = None 529 self.adContainer.Flush() 530 SurveyBanner(parent=self.adContainer, surveyID=self.surveyID) 531 self.openStoreContainer.align = uiconst.CENTERLEFT 532 533 def AdjustLogo(self): 534 cl, ct, cw, ch = self.charactersCont.GetAbsolute() 535 if ct > 0: 536 availableHeightAbove = ct 537 else: 538 availableHeightAbove = int((uicore.desktop.height - self.maxFullSlotSize) / 2.0) 539 logoContHeight = max(availableHeightAbove, MINIMUM_LOGOHEIGHT + LOGO_PADDING) 540 self.logoCont.height = logoContHeight 541 self.logo.display = True 542 availableHeightForLogo = logoContHeight - LOGO_PADDING 543 percentage = max(0.55, min(1.0, availableHeightForLogo / float(LOGO_HEIGHT))) 544 newHeight = int(percentage * LOGO_HEIGHT) 545 newWidth = int(percentage * LOGO_WIDTH) 546 self.logo.height = newHeight 547 self.logo.width = newWidth 548 549 def OnSlotOnMouseEnter(self, slot, *args): 550 if not slot.mouseOverState: 551 sm.GetService('audio').SendUIEvent('character_hover_picture') 552 for eachSlot in self.slotsByIdx.itervalues(): 553 if eachSlot.charID: 554 characterData = self.GetCharacterSelectionData().GetCharInfo(eachSlot.charID) 555 deletePrepTime = characterData.GetDeletePrepareTime() 556 if deletePrepTime: 557 continue 558 if eachSlot == slot: 559 eachSlot.SetMouseOverState(animate=True) 560 else: 561 eachSlot.SetMouseExitState(animate=True) 562 563 def OnSlotMouseExit(self, slot, *args): 564 if uicore.uilib.mouseOver.IsUnder(slot): 565 return 566 slot.SetMouseExitState(animate=True) 567 568 def GoBuySlot(self, slotSelected): 569 uicore.cmd.GetCommandAndExecute('OpenAccountManagement', origin='characterSelection') 570 571 def CreateCharacter(self, slotSelected): 572 if not self._CanLogin(): 573 return 574 self.CreateNewCharacter() 575 576 def _CanLogin(self): 577 computerhash = sm.GetService('connection').GetComputerHash() 578 if computerhash is not None: 579 if not sm.RemoteSvc('multiLoginBlocker').Login(computerhash): 580 MultiLoginBlockedWindow.Open(origin=ORIGIN_CHARACTERSELECTION) 581 return False 582 return True 583 584 def EnterGameWithCharacter(self, slotSelected): 585 characterData = self.GetCharacterSelectionData().GetCharInfo(slotSelected.charID) 586 deletePrepTime = characterData.GetDeletePrepareTime() 587 if deletePrepTime is not None: 588 return 589 if not self._CanLogin(): 590 return 591 slotSelected.SetMouseOverState() 592 slotSelected.PlaySelectedAnimation() 593 self.ConfirmWithCharID(slotSelected.charID) 594 595 def CreateNewCharacter(self): 596 if not self.ready: 597 prompt_player_fsd_dialog('Busy') 598 return 599 lowEnd = gfxsettings.GetDeviceClassification() == gfxsettings.DEVICE_LOW_END 600 if lowEnd: 601 msg2 = prompt_player_fsd_dialog('ReduceGraphicsSettings', {}, uiconst.YESNO, default=uiconst.ID_NO) 602 if msg2 == uiconst.ID_YES: 603 self.ReduceCharacterGraphics() 604 prompt_player_fsd_dialog('CCNewChar') 605 uthread.new(sm.GetService('gameui').GoCharacterCreation) 606 607 def CreateNewAvatar(self, charID): 608 charData = self.GetCharacterSelectionData().GetCharInfo(charID) 609 charDetails = charData.charDetails 610 if charData.GetPaperDollState == paperdoll.State.force_recustomize: 611 prompt_player_fsd_dialog('ForcedPaperDollRecustomization') 612 uthread.new(sm.GetService('gameui').GoCharacterCreation, charID, charDetails.gender, charDetails.raceID, charDetails.bloodlineID, dollState=charData.GetPaperDollState()) 613 614 def Confirm(self): 615 if not self.characterSlotList: 616 return 617 slot = self.characterSlotList[0] 618 self.EnterGameWithCharacter(slot) 619 620 def ConfirmWithCharID(self, charID, *_): 621 log.LogInfo('Character selection: Character selection confirmation') 622 if not self.ready: 623 log.LogInfo('Character selection: Denied character selection confirmation, not ready') 624 prompt_player_fsd_dialog('Busy') 625 return 626 isInSync = self.WaitForClockSynchroAndGetSynchroState() 627 if not isInSync: 628 prompt_player_fsd_dialog('ClockSynchroInProgress') 629 return 630 if sm.GetService('jumpQueue').GetPreparedQueueCharID() != charID: 631 self.__Confirm(charID) 632 633 def WaitForClockSynchroAndGetSynchroState(self): 634 for x in xrange(300): 635 if not sm.GetService('connection').IsClockSynchronizing(): 636 return True 637 if x > 30: 638 log.general.Log('Clock synchronization still in progress after %d seconds' % x, log.LGINFO) 639 blue.pyos.synchro.SleepWallclock(1000) 640 641 return not sm.GetService('connection').IsClockSynchronizing() 642 643 def CheckCharacterLockStatus(self, charID): 644 lockTypeID = sm.RemoteSvc('charUnboundMgr').GetCharacterLockType(charID) 645 if lockTypeID is not None: 646 if lockTypeID == appConst.charLockInTransferQueue: 647 raise UserError('CharacterTransferring') 648 elif lockTypeID == appConst.charLockOnSale: 649 raise UserError('CharacterOnSale') 650 else: 651 raise UserError('CharacterLocked') 652 653 def EnterAsCharacter(self, charID, secondChoiceID, skipTutorial = False): 654 MAX_RETRIES = 10 655 RETRY_SECONDS = 6 656 self.CheckCharacterLockStatus(charID) 657 if secondChoiceID is not None: 658 self.CheckCharacterLockStatus(secondChoiceID) 659 for numTries in xrange(MAX_RETRIES): 660 try: 661 sm.GetService('sessionMgr').PerformSessionChange('charsel', self.remoteCharacterSvc.SelectCharacterID, charID, secondChoiceID, skipTutorial) 662 launchdarkly.get_client().character_select(session.userid, charID, session.languageID) 663 return 664 except UserError as e: 665 if e.msg == 'SystemCheck_SelectFailed_Loading' and numTries < MAX_RETRIES - 1: 666 log.LogNotice('System is currently loading. Retrying %s/%s' % (numTries, MAX_RETRIES)) 667 blue.pyos.synchro.SleepWallclock(RETRY_SECONDS * 1000) 668 else: 669 self.EnableScreen() 670 raise 671 672 self.EnableScreen() 673 674 def __Confirm(self, charID, secondChoiceID = None): 675 charData = self.GetCharacterSelectionData().GetCharInfo(charID) 676 dollState = charData.GetPaperDollState() 677 sm.GetService('cc').StoreCurrentDollState(dollState) 678 if dollState in (paperdoll.State.force_recustomize, paperdoll.State.no_existing_customization): 679 self.CreateNewAvatar(charID) 680 return 681 self.ready = False 682 self.TryEnterGame(charID, secondChoiceID) 683 if charID: 684 mailCount = charData.GetUnreaddMailCount() 685 if mailCount > 0: 686 uthread.new(sm.GetService('mailSvc').CheckNewMessages_thread, mailCount) 687 688 def TryEnterGame(self, charID, secondChoiceID, skipTutorial = False): 689 sm.GetService('audio').SendUIEvent('msg_OnLogin_play') 690 sm.GetService('audio').SendUIEvent('msg_OnConnecting_play') 691 self.ShowLoading() 692 try: 693 prompt_player_fsd_dialog('OnCharSel') 694 sm.GetService('jumpQueue').PrepareQueueForCharID(charID) 695 populationCap = moniker.Moniker('populationCap', (charID, appConst.groupCharacter)) 696 locations = populationCap.GetCharacterLoadSlot(charID) 697 if len(locations) > 1: 698 selectedSolarSystemID = self.AskForAlternativeSystems(locations.keys()) 699 if selectedSolarSystemID is None: 700 sm.GetService('jumpQueue').PrepareQueueForCharID(None) 701 self.ready = True 702 return 703 slotKey = locations[selectedSolarSystemID] 704 populationCap.MoveCharacterToNewSystem(charID, selectedSolarSystemID, slotKey) 705 try: 706 self.selectionScreen.state = uiconst.UI_DISABLED 707 self.AnimateScreenOut(excludeSlotForCharID=charID) 708 self.EnterAsCharacter(charID, secondChoiceID, skipTutorial) 709 except UserError as e: 710 self.EnableScreen() 711 if e.msg != 'SystemCheck_SelectFailed_Queued': 712 sm.GetService('jumpQueue').PrepareQueueForCharID(None) 713 raise 714 except: 715 self.EnableScreen() 716 sm.GetService('jumpQueue').PrepareQueueForCharID(None) 717 raise 718 719 except: 720 self.EnableScreen() 721 self.HideLoading() 722 sm.GetService('loading').FadeOut() 723 self.ready = True 724 raise 725 726 def AskForAlternativeSystems(self, neighbors): 727 selectText = localization.GetByLabel('UI/CharacterSelection/SelectAlternativeSystem') 728 validNeighbors = [] 729 for ssid in neighbors: 730 systemItem = self.mapSvc.GetItem(ssid) 731 regionID = self.mapSvc.GetRegionForSolarSystem(ssid) 732 regionItem = self.mapSvc.GetItem(regionID) 733 factionID = systemItem.factionID 734 factionName = '' 735 if factionID: 736 factionName = cfg.eveowners.Get(factionID).ownerName 737 label = '%s<t>%s<t>%s<t>%s' % (systemItem.itemName, 738 regionItem.itemName, 739 self.mapSvc.GetSecurityStatus(ssid), 740 factionName) 741 validNeighborTuple = (label, ssid, None) 742 validNeighbors.append(validNeighborTuple) 743 744 loadingSvc = sm.StartService('loading') 745 self.HideLoading() 746 loadingSvc.FadeOut() 747 scrollHeaders = [localization.GetByLabel('UI/Common/LocationTypes/SolarSystem'), 748 localization.GetByLabel('UI/Common/LocationTypes/Region'), 749 localization.GetByLabel('UI/Common/Security'), 750 localization.GetByLabel('UI/Sovereignty/Sovereignty')] 751 ret = uix.ListWnd(validNeighbors, None, localization.GetByLabel('UI/CharacterSelection/SystemCongested'), selectText, 1, scrollHeaders=scrollHeaders, minw=555, windowName='AlternativeSystemSelect') 752 if ret: 753 return ret[1] 754 755 def Terminate(self, charID, *args): 756 if not self.ready: 757 prompt_player_fsd_dialog('Busy') 758 return 759 try: 760 self.ready = 0 761 characterData = self.GetCharacterSelectionData().GetCharInfo(charID) 762 deletePrepTime = characterData.GetDeletePrepareTime() 763 if deletePrepTime: 764 now = blue.os.GetWallclockTime() 765 if deletePrepTime < now: 766 self.DeleteCharacter(charID, characterData.charDetails.gender) 767 else: 768 timeLeft = deletePrepTime - now 769 infoMsg = localization.GetByLabel('UI/CharacterSelection/AlreadyInBiomassQueue', charID=charID, timeLef=timeLeft) 770 prompt_player_fsd_dialog('CustomInfo', {'info': infoMsg}) 771 else: 772 self.SubmitToBiomassQueue(charID) 773 finally: 774 self.ready = 1 775 776 def DeleteCharacter(self, charID, gender): 777 prompt_player_fsd_dialog('CCTerminate') 778 if prompt_player_fsd_dialog('AskDeleteCharacter', {'charID': charID}, uiconst.YESNO) != uiconst.ID_YES: 779 return 780 self.ShowLoading() 781 if gender == appConst.genderFemale: 782 beginMsg = 'CCTerminateForGoodFemaleBegin' 783 endMsg = 'CCTerminateForGoodFemale' 784 else: 785 beginMsg = 'CCTerminateForGoodMaleBegin' 786 endMsg = 'CCTerminateForGoodMale' 787 prompt_player_fsd_dialog(beginMsg) 788 try: 789 error = self.remoteCharacterSvc.DeleteCharacter(charID) 790 prompt_player_fsd_dialog(endMsg) 791 finally: 792 self.HideLoading() 793 self.ready = 1 794 795 if error: 796 prompt_player_fsd_dialog(error) 797 return 798 self.SetData(force=True) 799 self.OnRefreshScreen() 800 801 def SubmitToBiomassQueue(self, charID): 802 if prompt_player_fsd_dialog('AskSubmitToBiomassQueue', {'charID': charID}, uiconst.YESNO) != uiconst.ID_YES: 803 return 804 ret = self.remoteCharacterSvc.PrepareCharacterForDelete(charID) 805 if ret: 806 prompt_player_fsd_dialog('SubmitToBiomassQueueConfirm', {'charID': charID, 807 'when': ret - blue.os.GetWallclockTime()}) 808 self.UpdateSlot(charID) 809 810 def UndoTermination(self, charID, *args): 811 sm.RemoteSvc('charUnboundMgr').CancelCharacterDeletePrepare(charID) 812 self.UpdateSlot(charID) 813 814 def UpdateSlot(self, charID): 815 self.GetCharacterSelectionData(force=True) 816 slot = self.slotsByCharID.get(charID, None) 817 if slot: 818 characterDetails = self.GetCharacterSelectionData().GetCharInfo(charID) 819 slot.RefreshCharacterDetails(characterDetails) 820 slot.SetDeleteUI() 821 else: 822 self.OnRefreshScreen() 823 824 def ShowLoading(self, forceOn = 0): 825 try: 826 self.loadingWheel.forcedOn = forceOn 827 self.loadingWheel.Show() 828 except: 829 log.LogError('Failed to show the loading wheel') 830 831 def HideLoading(self, forceOff = 0): 832 try: 833 if not self.loadingWheel.forcedOn or forceOff: 834 self.loadingWheel.Hide() 835 self.loadingWheel.forcedOn = 0 836 except: 837 log.LogError('Failed to hide the loading wheel') 838 839 def _GetWarningHeight(self): 840 if uicore.desktop.height < 810: 841 return 1 842 return WARNING_BANNER_HEIGHT 843 844 def OnBannerAdDisplayed(self): 845 self.adContainer.display = True 846 self.openStoreContainer.align = uiconst.CENTERLEFT 847 848 def OnBannerAdHidden(self): 849 self.adContainer.display = False 850 self.openStoreContainer.align = uiconst.CENTER 851 852 853 @Component(ButtonEffect(opacityIdle=0.0, opacityHover=1.4, opacityMouseDown=1.7, bgElementFunc=lambda parent, _: parent.highlight, audioOnEntry=uiconst.SOUND_BUTTON_HOVER)) 854 855 class OpenStoreButton(Container): 856 default_name = 'OpenStoreButton' 857 default_width = 270 858 default_height = 90 859 default_state = uiconst.UI_NORMAL 860 861 def ApplyAttributes(self, attributes): 862 Container.ApplyAttributes(self, attributes) 863 self.logo = Sprite(name='Logo', bgParent=self, texturePath='res:/UI/Texture/Vgs/storeLogo.png', align=uiconst.TOALL) 864 self.highlight = Sprite(name='Highlight', bgParent=self, texturePath='res:/UI/Texture/Vgs/storeLogoGlow.png', align=uiconst.TOALL) 865 if csUtil.IsMonochromeStyleEnabled(): 866 csUtil.SetMonochromeStyle(self.logo, True) 867 self.onClick = attributes.onClick 868 869 def OnClick(self): 870 PlaySound(uiconst.SOUND_BUTTON_CLICK) 871 self.onClick()