skillQueuePanelNew.py (45481B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\skillPlan\skillQueuePanel\skillQueuePanelNew.py 2 import blue 3 import clonegrade 4 import eve.client.script.ui.shared.pointerTool.pointerToolConst as pConst 5 import evetypes 6 import expertSystems.client 7 import gametime 8 import inventorycommon.const as invconst 9 import localization 10 import telemetry 11 import textImporting.importSkillplanConst as importConst 12 import uthread 13 import uthread2 14 from carbon.client.script.environment.AudioUtil import PlaySound 15 from carbon.common.script.util.commonutils import GetAttrs 16 from carbonui import uiconst 17 from carbonui.text.styles import TextDetail 18 from carbonui.control.button import Button 19 from carbonui.control.dragdrop import dragdata 20 from carbonui.control.scroll import Scroll 21 from carbonui.primitives.container import Container 22 from carbonui.primitives.containerAutoSize import ContainerAutoSize 23 from carbonui.primitives.line import Line 24 from carbonui.uianimations import animations 25 from carbonui.uicore import uicore 26 from carbonui.util.color import Color 27 from carbonui.util.various_unsorted import GetClipboardData 28 from characterskills import SKILLQUEUE_MAX_NUM_SKILLS 29 from characterskills.util import GetSPForLevelRaw 30 from eve.client.script.ui import eveColor 31 from eve.client.script.ui.control.entries.util import GetFromClass 32 from eve.client.script.ui.control.eveLabel import EveLabelSmall, EveLabelLarge, EveCaptionLarge 33 from eve.client.script.ui.control.utilMenu import UtilMenu 34 from eve.client.script.ui.shared.cloneGrade import cloneStateUtil 35 from eve.client.script.ui.shared.monetization.multiTrainingOverlay import MultiTrainingOverlay 36 from eve.client.script.ui.shared.neocom.skillConst import COLOR_SKILL_2, COLOR_TRAININGALLOWED, COLOR_TRAININGNOTALLOWED, COLOR_MOVE_INDICATOR 37 from eve.client.script.ui.shared.neocom.timelineContainer import TimelineContainer 38 from eve.client.script.ui.skillPlan import skillPlanUISignals 39 from eve.client.script.ui.skillPlan.skillPlanEditor.skillPlanDropIndicatorCont import SkillQueueDropIndicatorCont, IsMultiSkillEntryMove 40 from eve.client.script.ui.skillPlan.skillQueuePanel.skillImportingMsg import SkillImportStatusWindow 41 from eve.client.script.ui.skillPlan.skillQueuePanel.skillPurchaseOverlay import show_skill_purchase_overlay 42 from eve.client.script.ui.skillPlan.skillQueuePanel.skillQueueButtons import ExpertSystemIconButton, ApplySkillPointsButtonTooltip, BUTTON_SIZE, ICON_SIZE, SkillInjectorTooltip, AlphaInjectorTooltip, OmegaTrainingSpeedButton 43 from eve.client.script.ui.skillPlan.skillQueuePanel.skillQueueLastDropEntry import SkillQueueLastDropEntry 44 from eve.client.script.ui.skillPlan.skillQueuePanel.skillQueueSkillEntry import SkillQueueSkillEntry 45 from eve.client.script.ui.skilltrading.applySkillPointsWindow import ApplySkillPointsWindow 46 from eve.common.lib import appConst 47 from eveexceptions import UserError 48 from eveui import clipboard 49 from localization import GetByLabel 50 from omega.client.clone_grade_svc import get_clone_grade_svc 51 from player_messaging.client.ui_message import prompt_player_fsd_dialog 52 from textImporting.importSkillplan import SkillPlanImportingStatus 53 ACTION_ICON = 'res:/UI/Texture/classes/UtilMenu/BulletIcon.png' 54 QUEUEBAR_HEIGHT = 30 55 COLOR_BG = (0.2, 0.2, 0.2, 0.5) 56 FILTER_ALL = 0 57 FILTER_PARTIAL = 1 58 FILTER_EXCLUDELVL5 = 2 59 FITSINQUEUE_DEFAULT = 0 60 NORMAL_COLORS = [(1, 1, 1, 0.4), (1, 1, 1, 0.8)] 61 62 def ShowMultiTrainingMessageOrRaise(e): 63 if not MultiTrainingOverlay.IsSuppressed(): 64 skillPlanUISignals.on_show_multi_training_msg() 65 sm.ScatterEvent('OnShowMultiTrainingMessage') 66 else: 67 raise e 68 69 70 class SkillQueuePanelNew(Container): 71 __notifyevents__ = ['OnSkillsChanged', 72 'OnSkillQueueChanged', 73 'OnSkillQueuePaused', 74 'OnSkillQueueRefreshed', 75 'OnFreeSkillPointsChanged_Local', 76 'OnSubscriptionChanged', 77 'OnSkillQueueModified'] 78 79 def ApplyAttributes(self, attributes): 80 Container.ApplyAttributes(self, attributes) 81 self.queueLastApplied = [] 82 self.isSaving = False 83 self.isApplying = False 84 self.skillTimer = None 85 self.timelineCont = None 86 self.updateThread = None 87 self._isSkillPurchaseOverlayActive = False 88 self.godma = sm.GetService('godma') 89 self.skills = sm.GetService('skills') 90 self.skillQueueSvc = sm.GetService('skillqueue') 91 self.ConstructLayout() 92 self.InitializeData() 93 self.ReloadUpdateThread() 94 sm.RegisterNotify(self) 95 96 @telemetry.ZONE_METHOD 97 def InitializeData(self, *args): 98 self.UpdateTimeData() 99 self.skillQueueSvc.BeginTransaction() 100 self.queueLastApplied = self.skillQueueSvc.GetQueue() 101 uthread.new(self.LoadQueue) 102 self.UpdateFreeSkillPoints() 103 self.UpdateApplySkillPointsButton() 104 105 @telemetry.ZONE_METHOD 106 def ConstructLayout(self): 107 self.ConstructTopCont() 108 self.ConstructSkillQueueBar() 109 self.ConstructBottomCont() 110 self.ConstructUnallocatedBanner() 111 self.ConstructScroll() 112 113 def ConstructUnallocatedBanner(self): 114 self.unallocatedTextBanner = Container(name='unallocatedTextBanner', parent=self, align=uiconst.TOBOTTOM, height=40, bgColor=Color(*eveColor.SMOKE_BLUE).SetOpacity(0.2).GetRGBA()) 115 self.unallocatedText = EveLabelLarge(parent=self.unallocatedTextBanner, align=uiconst.CENTER) 116 117 def ConstructBottomCont(self): 118 self.bottomCont = Container(name='bottomCont', parent=self, align=uiconst.TOBOTTOM, height=56, padBottom=8) 119 textCont = ContainerAutoSize(parent=self.bottomCont, align=uiconst.BOTTOMLEFT, width=300) 120 self.timeLeftLabel = EveCaptionLarge(name='timeLeftLabel', parent=textCont, align=uiconst.TOBOTTOM, padTop=-2, top=-4) 121 EveLabelLarge(parent=textCont, align=uiconst.TOBOTTOM, text=GetByLabel('UI/SkillPlan/TrainingTime')) 122 uthread.new(self._UpdateTrainingTimeLabelThread) 123 buttonIconCont = ContainerAutoSize(name='ButtonIconCont', parent=self.bottomCont, align=uiconst.BOTTOMRIGHT, height=BUTTON_SIZE) 124 self._ConstructBottomContButtons(buttonIconCont) 125 self.UpdatePausePlayButtonTexturePath() 126 127 def _ConstructBottomContButtons(self, buttonIconCont): 128 padLeft = 8 129 self.omegaButton = OmegaTrainingSpeedButton(name='omegaButton', parent=buttonIconCont, align=uiconst.TORIGHT, iconSize=32, fixedwidth=60, padLeft=padLeft, texturePath='res:/UI/Texture/classes/Seasons/omega_32x32.png', label='2x') 130 self.CheckDisplayOmegaButton() 131 self.applySkillPointsButtonIcon = Button(name='ApplySkillPointsButtonIcon', parent=buttonIconCont, align=uiconst.TORIGHT, func=self.OnApplySkillPointsButton, iconSize=ICON_SIZE, padLeft=padLeft, uniqueUiName=pConst.UNIQUE_NAME_APPLY_SP_BTN) 132 self.UpdateApplySkillPointsButton() 133 if expertSystems.is_expert_systems_enabled(): 134 from eve.client.script.ui.shared.neocom.characterSheetWindow import CharacterSheetWindow 135 ExpertSystemIconButton(parent=buttonIconCont, align=uiconst.TORIGHT, padLeft=padLeft, iconSize=ICON_SIZE, func=CharacterSheetWindow.OpenExpertSystems, texturePath='res:/UI/Texture/classes/ExpertSystems/logo_simple_white_32.png') 136 self.pausePlayButton = Button(parent=buttonIconCont, align=uiconst.TORIGHT, iconSize=20, func=self.StartOrStopTraining, padLeft=padLeft, uniqueUiName=pConst.UNIQUE_NAME_START_TRAINING_BTN) 137 138 def UpdateApplySkillPointsButton(self): 139 if self._CanApplySkillPoints(): 140 tooltipPanelClassInfo = ApplySkillPointsButtonTooltip(len(self.skillQueueSvc.GetQueue()), self.skills.GetFreeSkillPoints()) 141 texturePath = 'res:/UI/Texture/classes/SkillPlan/buttonIcons/plusIcon.png' 142 elif cloneStateUtil.IsOmega(): 143 tooltipPanelClassInfo = SkillInjectorTooltip() 144 texturePath = 'res:/UI/Texture/classes/skilltrading/marketSkillinjector.png' 145 else: 146 tooltipPanelClassInfo = AlphaInjectorTooltip() 147 texturePath = 'res:/UI/Texture/classes/skilltrading/alphaSkillInjector.png' 148 self.applySkillPointsButtonIcon.tooltipPanelClassInfo = tooltipPanelClassInfo 149 self.applySkillPointsButtonIcon.texturePath = texturePath 150 151 def OnApplySkillPointsButton(self, *args): 152 if self._CanApplySkillPoints(): 153 ApplySkillPointsWindow.Open() 154 elif cloneStateUtil.IsOmega(): 155 self.ShowInjectorInMarket() 156 else: 157 self.BuyAlphaInjector() 158 159 def _CanApplySkillPoints(self): 160 return len(self.skillQueueSvc.GetQueue()) > 0 and self.skills.GetFreeSkillPoints() 161 162 def UpdatePausePlayButtonTexturePath(self): 163 if self.skillQueueSvc.SkillInTraining(): 164 texturePath = 'res:/UI/Texture/classes/SkillPlan/buttonIcons/pauseIcon.png' 165 hint = GetByLabel('UI/SkillPlan/PauseTraining') 166 else: 167 texturePath = 'res:/UI/Texture/classes/SkillPlan/buttonIcons/playIcon.png' 168 hint = GetByLabel('UI/SkillPlan/ResumeTraining') 169 self.pausePlayButton.texturePath = texturePath 170 self.pausePlayButton.SetHint(hint) 171 172 def _UpdateTrainingTimeLabelThread(self): 173 while not self.destroyed: 174 timeLeft, spLeft = self.skillQueueSvc.GetTrainingLengthOfQueue() 175 if self.skillQueueSvc.SkillInTraining(): 176 self.timeLeftLabel.SetRGBA(*eveColor.WHITE) 177 self.timeLeftLabel.text = self.GetTimeLeftText(timeLeft) 178 self.timeLeftLabel.text = self.GetTimeLeftText(timeLeft) 179 self.spLeftLabel.text = GetByLabel('UI/SkillPlan/SpLeftInQueue', spLeft=spLeft) 180 elif not self.skillQueueSvc.GetQueue(): 181 self.timeLeftLabel.SetRGBA(*eveColor.WHITE) 182 self.timeLeftLabel.text = '-' 183 self.spLeftLabel.text = '-' 184 else: 185 self.timeLeftLabel.SetRGBA(*eveColor.HOT_RED) 186 self.timeLeftLabel.text = GetByLabel('UI/SkillPlan/TrainingPaused') 187 self.spLeftLabel.text = GetByLabel('UI/SkillPlan/SpLeftInQueue', spLeft=spLeft) 188 blue.synchro.SleepWallclock(500) 189 190 def GetTimeLeftText(self, timeLeft): 191 if session.languageID == 'DE': 192 if timeLeft >= gametime.DAY: 193 return localization.formatters.FormatTimeIntervalShortWritten(long(timeLeft), showFrom='day', showTo='hour') 194 elif timeLeft > gametime.HOUR: 195 return localization.formatters.FormatTimeIntervalShortWritten(long(timeLeft), showFrom='hour', showTo='minute') 196 else: 197 return localization.formatters.FormatTimeIntervalShortWritten(long(timeLeft), showFrom='minute', showTo='second') 198 else: 199 if timeLeft >= gametime.DAY: 200 return localization.formatters.FormatTimeIntervalShortWritten(long(timeLeft), showFrom='day', showTo='minute') 201 return localization.formatters.FormatTimeIntervalShortWritten(long(timeLeft), showFrom='hour', showTo='second') 202 203 def ShowInjectorInMarket(self): 204 sm.StartService('marketutils').ShowMarketDetails(invconst.typeSkillInjector, None) 205 PlaySound(uiconst.SOUND_BUTTON_CLICK) 206 207 def BuyAlphaInjector(self): 208 sm.GetService('vgsService').OpenStore(typeIds=[invconst.typeAlphaTrainingInjector]) 209 210 def ConstructTopCont(self): 211 self.topCont = Container(name='topCont', parent=self, align=uiconst.TOTOP, height=26) 212 self.captionLabel = EveCaptionLarge(parent=self.topCont, align=uiconst.CENTERLEFT, state=uiconst.UI_NORMAL, text=GetByLabel('UI/CharacterSheet/CharacterSheetWindow/SkillTabs/TrainingQueue')) 213 self.captionLabel.LoadTooltipPanel = self.LoadCaptionLabelTooltipPanel 214 UtilMenu(menuAlign=uiconst.TOPLEFT, parent=self.topCont, align=uiconst.CENTERRIGHT, texturePath='res:/UI/Texture/Icons/77_32_49.png', pos=(0, 0, 24, 24), GetUtilMenu=self.GetUtilMenu, iconSize=24) 215 216 def LoadCaptionLabelTooltipPanel(self, tooltipPanel, *args): 217 tooltipPanel.LoadStandardSpacing() 218 tooltipPanel.AddMediumHeader(text=GetByLabel('UI/SkillPlan/SkillQueueCounterHeader')) 219 tooltipPanel.AddLabelMedium(text=GetByLabel('UI/SkillPlan/SkillQueueCounterDescription', numMax=SKILLQUEUE_MAX_NUM_SKILLS), wrapWidth=300) 220 221 def UpdateCaptionLabelText(self, numSkills): 222 text = GetByLabel('UI/CharacterSheet/CharacterSheetWindow/SkillTabs/TrainingQueue') 223 text += ' <color=%s>%s<fontsize=12>/%s</fontsize></color>' % (eveColor.LED_GREY_HEX, numSkills, SKILLQUEUE_MAX_NUM_SKILLS) 224 self.captionLabel.text = text 225 226 def ConstructScroll(self): 227 self.dropIndicatorCont = SkillQueueDropIndicatorCont(parent=self, align=uiconst.TOBOTTOM) 228 self.dropIndicatorCont.OnDragEnter = self.OnDropContDragEnter 229 self.dropIndicatorCont.OnDropData = self.OnDropContDropData 230 self.scroll = Scroll(name='skillQueueScroll', parent=self, state=uiconst.UI_PICKCHILDREN, padding=(0, 12, 0, 0)) 231 self.scroll.HideBackground() 232 self.scroll.sr.content.OnDropData = self.DoDropData 233 self.scroll.sr.content.OnDragEnter = self.OnContentDragEnter 234 self.scroll.sr.content.OnDragExit = self.OnContentDragExit 235 self.scroll.sr.content.RemoveSkillFromQueue = self.RemoveSkillFromQueue 236 self.scroll.OnDelete = self.RemoveSkillFromQueue 237 self.changeIndicator = Line(parent=self.scroll, align=uiconst.TOBOTTOM, state=uiconst.UI_DISABLED, color=COLOR_SKILL_2, weight=2, padTop=-2, opacity=0.0, idx=0) 238 239 def ConstructSkillQueueBar(self): 240 skillQueueBar = Container(name='skillQueueBar', parent=self, align=uiconst.TOBOTTOM, height=12, padBottom=14) 241 self.timelineCont = TimelineContainer(name='barCont', parent=skillQueueBar, align=uiconst.TOTOP, state=uiconst.UI_NORMAL, height=12, bgColor=COLOR_BG, barHeight=12) 242 self.timelineCont.OnMouseEnter = self.OnTimelineContMouseEnter 243 self.timelineCont.OnMouseExit = self.OnTimelineContMouseExit 244 self.belowBarCont = Container(name='belowBarCont', parent=skillQueueBar, align=uiconst.TOTOP, top=4, height=20, clipChildren=True) 245 self.spLeftLabel = TextDetail(parent=self.belowBarCont, align=uiconst.TOTOP_NOPUSH, text='', top=1) 246 self.timelineTickCont = Container(name='timelineTickCont', parent=self.belowBarCont, align=uiconst.TOTOP, height=20, opacity=0.0, top=1) 247 248 def OnTimelineContMouseEnter(self, *args): 249 animations.FadeIn(self.timelineTickCont) 250 animations.FadeOut(self.spLeftLabel) 251 self.DrawBars(animate=True) 252 253 def OnTimelineContMouseExit(self): 254 animations.FadeOut(self.timelineTickCont) 255 animations.FadeIn(self.spLeftLabel, endVal=0.75) 256 self.DrawBars(animate=True) 257 258 def GetUtilMenu(self, menuParent): 259 menuParent.AddIconEntry(icon='res:/UI/Texture/classes/UtilMenu/BulletIcon.png', text=GetByLabel('UI/SkillPlan/CopySkillsToClipboard'), callback=self.CopySkillsToClipboard) 260 hint = GetByLabel('UI/SkillQueue/ImportSkillplanFormatHint', typeID=appConst.typeSpaceshipCommand) 261 menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/SkillQueue/AddSkillPlanToEndOfQueue'), hint=hint, callback=self.ImportFromClipboard) 262 menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/SkillQueue/ReplaceSkillsWithImportedPlan'), hint=hint, callback=self.ImportFromClipboardReplace) 263 menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/SkillQueue/ClearSkillQueue'), callback=self.ClearSkillQueue) 264 265 def CopySkillsToClipboard(self): 266 skillsTxt = '' 267 for typeID, level in self.skillQueueSvc.GetQueueAsRequirements(): 268 skillsTxt += '%s %s\n' % (evetypes.GetName(typeID), level) 269 270 clipboard.set(skillsTxt) 271 272 def ClearSkillQueue(self): 273 self.DoRemove(None, self.scroll.GetNodes()) 274 275 def GrayButton(self, btn, gray = 1): 276 inTraining = self.skillQueueSvc.SkillInTraining() 277 if gray and not inTraining: 278 btn.SetLabel(['<color=gray>', GetByLabel('UI/Commands/Pause')]) 279 btn.state = uiconst.UI_DISABLED 280 else: 281 btn.SetLabel(GetByLabel('UI/Commands/Pause')) 282 btn.state = uiconst.UI_NORMAL 283 284 def ApplySkillQueueIfNotActive(self, *args): 285 self.ApplySkillQueue(activate=not self.IsTraining()) 286 287 def ApplySkillQueue(self, activate = True): 288 beginNewTransaction = True 289 try: 290 self.SaveChanges(activate=activate) 291 except UserError as e: 292 if e.msg == 'UserAlreadyHasSkillInTraining': 293 beginNewTransaction = False 294 ShowMultiTrainingMessageOrRaise(e) 295 elif e.msg == 'SkillInQueueRequiresOmegaCloneState': 296 beginNewTransaction = False 297 raise 298 elif e.msg == 'SkillInQueueOverAlphaSpTrainingSize': 299 message = localization.GetByLabel('UI/SkillQueue/AlphaTrainingLimitReached', limit=clonegrade.CLONE_STATE_ALPHA_MAX_TRAINING_SP) 300 sm.ScatterEvent('OnShowAlphaTrainingMessage', message) 301 skillPlanUISignals.on_show_alpha_training_msg(message) 302 if activate: 303 self.ApplySkillQueue(activate=False) 304 else: 305 beginNewTransaction = False 306 else: 307 raise 308 finally: 309 if beginNewTransaction and not self.skillQueueSvc.IsTransactionOpen(): 310 self.skillQueueSvc.BeginTransaction() 311 312 def SaveChanges(self, activate = True): 313 if self.isSaving: 314 return 315 self.isSaving = True 316 try: 317 queue = self.skillQueueSvc.GetQueue() 318 self.skillQueueSvc.CommitTransaction(activate=activate) 319 self.queueLastApplied = queue 320 finally: 321 self.isSaving = False 322 323 def PauseTraining(self, *args): 324 inTraining = self.skillQueueSvc.SkillInTraining() 325 if inTraining: 326 sm.StartService('skills').AbortTrain() 327 self.ApplySkillQueue(activate=False) 328 329 def Close(self, *args): 330 Container.Close(self) 331 uthread.new(self.SaveQueueOnClose) 332 333 def SaveQueueOnClose(self): 334 if self.isSaving: 335 return 336 queue = {x.queuePosition:(x.trainingTypeID, x.trainingToLevel) for x in self.skillQueueSvc.GetQueue()} 337 queueLastApplied = {x.queuePosition:(x.trainingTypeID, x.trainingToLevel) for x in self.queueLastApplied} 338 if queue != queueLastApplied: 339 isActive = bool(self.skillQueueSvc.SkillInTraining()) 340 self.skillQueueSvc.CommitTransaction(activate=isActive) 341 else: 342 self.skillQueueSvc.RollbackTransaction() 343 344 @telemetry.ZONE_METHOD 345 def LoadQueue(self): 346 self.queueTimer = 0 347 mySkills = sm.StartService('skills').GetSkillsIncludingLapsed() 348 queue = self.skillQueueSvc.GetQueue() 349 allTrainingLengths = self.skillQueueSvc.GetAllTrainingLengths() 350 scrolllist = [] 351 for traininigSkill in queue: 352 skill = mySkills.get(traininigSkill.trainingTypeID, None) 353 if skill is None: 354 self.skillQueueSvc.RemoveSkillFromQueue(traininigSkill.trainingTypeID, traininigSkill.trainingToLevel) 355 continue 356 time = allTrainingLengths.get((traininigSkill.trainingTypeID, traininigSkill.trainingToLevel), [0, 0, 0]) 357 entry = self.GetRightEntry(traininigSkill.trainingTypeID, skill, traininigSkill.trainingToLevel, time[1], time[2]) 358 scrolllist.append(entry) 359 360 if scrolllist: 361 lastDropEntry = GetFromClass(SkillQueueLastDropEntry) 362 scrolllist.append(lastDropEntry) 363 self.scroll.Load(contentList=scrolllist, noContentHint=GetByLabel('UI/SkillQueue/NoSkillsInTraining')) 364 self.UpdateTime() 365 self.UpdateAlphaTrainingLimitFrames() 366 self.UpdateCaptionLabelText(len(queue)) 367 368 def UpdateAlphaTrainingLimitFrames(self): 369 isOmega = get_clone_grade_svc().is_omega_with_grace_period() 370 nodes = self.scroll.GetNodes() 371 accumulatedSkillPoints = 0 372 totalSkillPoints = sm.GetService('skills').GetTotalSkillPointsForCharacterCached() 373 for node in nodes: 374 if not node.skill: 375 continue 376 if isOmega: 377 if node.panel: 378 node.panel.HideDisabledFrame() 379 continue 380 skillPointsForPreviousLevel = GetSPForLevelRaw(node.skill.skillRank, node.trainToLevel - 1) 381 skillPointsForThisLevel = GetSPForLevelRaw(node.skill.skillRank, node.trainToLevel) 382 totalSkillPointsInThisLevel = skillPointsForThisLevel - skillPointsForPreviousLevel 383 currentSkillPoints = self.skillQueueSvc.GetEstimatedSkillPointsTrained(node.skill.typeID) 384 skillPointsAlreadyTrainedThisLevel = max(currentSkillPoints - skillPointsForPreviousLevel, 0) 385 addedSkillPoints = max(0, totalSkillPointsInThisLevel - skillPointsAlreadyTrainedThisLevel) 386 accumulatedSkillPoints += addedSkillPoints 387 if totalSkillPoints + accumulatedSkillPoints > clonegrade.CLONE_STATE_ALPHA_MAX_TRAINING_SP: 388 node.disabledForTraining = True 389 elif get_clone_grade_svc().get_max_skill_level(node.skill.typeID) < node.trainToLevel: 390 node.disabledForTraining = True 391 else: 392 node.disabledForTraining = False 393 if node.panel: 394 if node.disabledForTraining: 395 node.panel.ShowDisabledFrame() 396 else: 397 node.panel.HideDisabledFrame() 398 399 def UpdateActiveSkillFlashAnimation(self): 400 isTraining = self.skillQueueSvc.SkillInTraining() is not None 401 nodes = self.scroll.GetNodes() 402 for i, node in enumerate(nodes): 403 if not node.panel or isinstance(node.panel, SkillQueueLastDropEntry): 404 continue 405 if i == 0 and isTraining: 406 node.panel.StartFlashAnimation() 407 else: 408 node.panel.StopFlashAnimation() 409 410 @telemetry.ZONE_METHOD 411 def GetRightEntry(self, typeID, skill, trainToLevel, timeLeft, isAccelerated): 412 skill = self.skills.GetSkillIncludingLapsed(typeID) 413 isTrained = skill is not None 414 skillInTraining = self.skillQueueSvc.SkillInTraining() 415 inTraining = skillInTraining is not None and skillInTraining.typeID == typeID 416 return GetFromClass(SkillQueueSkillEntry, {'invtype': typeID, 417 'skill': skill, 418 'trained': isTrained, 419 'inQueue': 1, 420 'trainToLevel': trainToLevel, 421 'currentLevel': skill.trainedSkillLevel, 422 'timeLeft': timeLeft, 423 'skillID': typeID, 424 'inTraining': [0, 1][inTraining], 425 'isAccelerated': isAccelerated, 426 'RemoveFromQueue': self.EntryRemoveFromQueue}) 427 428 def EntryRemoveFromQueue(self, x): 429 self.DoRemove(None, [x]) 430 431 def UpdateTime(self): 432 self.UpdateTimeData() 433 self.UpdateSkillQueueBar() 434 435 @telemetry.ZONE_METHOD 436 def UpdateTimeData(self): 437 self.queueEnds = self.skillQueueSvc.GetTrainingEndTimeOfQueue() 438 self.queueTimeLeft = self.queueEnds - blue.os.GetWallclockTime() 439 self.allTrainingLengths = self.skillQueueSvc.GetAllTrainingLengths() 440 self.lasttime = blue.os.GetWallclockTime() 441 442 def ReloadUpdateThread(self): 443 if self.updateThread: 444 self.updateThread.kill() 445 self.updateThread = uthread.new(self.UpdateThread) 446 447 @telemetry.ZONE_METHOD 448 def UpdateThread(self, *args): 449 while True: 450 blue.synchro.SleepWallclock(1000) 451 if self.destroyed: 452 break 453 self._Update() 454 455 def _Update(self): 456 inTraining = self.skillQueueSvc.SkillInTraining() 457 now = blue.os.GetWallclockTime() 458 lasttime = self.lasttime or now 459 diff = now - lasttime 460 queueEnds = self.queueEnds 461 timeLeft = self.queueTimeLeft 462 if queueEnds is not None and queueEnds >= now and timeLeft is not None: 463 if inTraining and self.skillQueueSvc.FindInQueue(inTraining.typeID, inTraining.trainedSkillLevel + 1) is not None: 464 timeLeft = timeLeft - diff 465 self.queueTimeLeft = timeLeft 466 else: 467 queueEnds = self.queueEnds + diff 468 self.queueEnds = queueEnds 469 self.lasttime = now 470 self.UpdateSkillQueueBar() 471 472 def UpdateSkillQueueBar(self): 473 self.DrawBars() 474 self.DrawTimelineTicks() 475 476 @telemetry.ZONE_METHOD 477 def DrawBars(self, animate = False): 478 self.timelineCont.FlushLine() 479 offset = 0.0 480 queue = self.skillQueueSvc.GetQueue() 481 entries = self.scroll.GetNodes() 482 unallocatedPoints = sm.GetService('skills').GetFreeSkillPoints() 483 unallocatedWidth = 0.0 484 timelineDuration = self.GetQueueDuration() 485 inTraining = self.skillQueueSvc.SkillInTraining() 486 actualEnd = self.skillQueueSvc.GetEndOfTraining(inTraining.typeID) if inTraining else None 487 for i, (skill, entry) in enumerate(zip(queue, entries)): 488 trainingID = (skill.trainingTypeID, skill.trainingToLevel) 489 if trainingID not in self.allTrainingLengths: 490 continue 491 timeLeft = self.allTrainingLengths[trainingID][1] 492 if inTraining and skill.trainingTypeID == inTraining.typeID and skill.trainingToLevel - 1 == inTraining.trainedSkillLevel: 493 if actualEnd is not None: 494 timeLeft = float(actualEnd - blue.os.GetWallclockTime()) 495 width = timeLeft / timelineDuration 496 segments = [] 497 if unallocatedPoints > 0: 498 pointsTrained = skill.trainingDestinationSP - skill.trainingStartSP 499 if unallocatedPoints >= pointsTrained: 500 segments.append((width, Color.WHITE)) 501 unallocatedWidth += width 502 else: 503 fractionCovered = min(1.0, unallocatedPoints / float(pointsTrained)) 504 segments.append((width * fractionCovered, Color.WHITE)) 505 segments.append((width * (1.0 - fractionCovered), NORMAL_COLORS[i % 2])) 506 unallocatedWidth += fractionCovered * width 507 unallocatedPoints -= pointsTrained 508 else: 509 segments.append((width, NORMAL_COLORS[i % 2])) 510 try: 511 if entry.panel is not None: 512 if uicore.uilib.mouseOver == self.timelineCont: 513 entry.panel.DrawTimeline(offset, segments, animate) 514 else: 515 entry.panel.HideTimeline(animate) 516 except AttributeError: 517 pass 518 519 for w, c in segments: 520 color = NORMAL_COLORS[i % 2] 521 self.timelineCont.AddSegment(w, color) 522 523 offset += width 524 525 self.timelineCont.SetDuration(timelineDuration) 526 527 def DrawTimelineTicks(self): 528 INTERVALS = [(appConst.HOUR, 529 6 * appConst.HOUR, 530 28, 531 localization.formatters.TIME_CATEGORY_HOUR), 532 (6 * appConst.HOUR, 533 appConst.DAY, 534 29, 535 localization.formatters.TIME_CATEGORY_DAY), 536 (appConst.DAY, 537 appConst.WEEK, 538 31, 539 localization.formatters.TIME_CATEGORY_DAY), 540 (appConst.WEEK, 541 appConst.MONTH28, 542 28, 543 localization.formatters.TIME_CATEGORY_MONTH), 544 (appConst.MONTH30, 545 6 * appConst.MONTH30, 546 25, 547 localization.formatters.TIME_CATEGORY_MONTH), 548 (appConst.MONTH30, 549 appConst.YEAR360, 550 999, 551 localization.formatters.TIME_CATEGORY_YEAR)] 552 for minorTick, majorTick, tickLimit, timeCategory in INTERVALS: 553 queueDuration = self.GetQueueDuration() 554 tickCount = queueDuration / float(minorTick) 555 if tickCount > tickLimit: 556 continue 557 self.timelineTickCont.Flush() 558 for i in xrange(1, int(tickCount) + 1): 559 timeValue = i * minorTick 560 isMajorTick = timeValue % majorTick == 0 561 if not isMajorTick: 562 continue 563 self.ConstructTimelineTick(i / tickCount, timeCategory, timeValue) 564 565 break 566 567 def ConstructTimelineTick(self, left, timeCategory, timeValue): 568 cont = Container(parent=self.timelineTickCont, align=uiconst.TOPLEFT_PROP, left=left, width=1, height=1) 569 Line(parent=cont, align=uiconst.TOPLEFT, weight=1, top=-1, width=1, height=5, color=COLOR_BG) 570 text = localization.formatters.FormatTimeIntervalShortWritten(timeValue, showFrom=timeCategory, showTo=timeCategory) 571 w, _ = EveLabelSmall.MeasureTextSize(text=text) 572 EveLabelSmall(parent=cont, align=uiconst.RELATIVE, left=-0.5 * w, top=0, text=text, opacity=0.4) 573 574 def GetQueueDuration(self): 575 return max(self.queueTimeLeft, appConst.DAY + appConst.HOUR) 576 577 def OnFreeSkillPointsChanged_Local(self): 578 self.UpdateFreeSkillPoints() 579 self.UpdateApplySkillPointsButton() 580 581 def UpdateFreeSkillPoints(self): 582 unallocatedPoints = sm.GetService('skills').GetFreeSkillPoints() 583 if unallocatedPoints > 0: 584 color = Color.RGBtoHex(*eveColor.CRYO_BLUE) 585 text = GetByLabel('UI/SkillQueue/FreeSkillPointAmount', points=unallocatedPoints, color=color) 586 self.unallocatedText.SetText(text) 587 self.unallocatedTextBanner.display = True 588 else: 589 self.unallocatedTextBanner.display = False 590 591 def AddSkillThroughSkillEntry(self, data, queue, idx = -1): 592 if not data.Get('trained', True): 593 self.HandleMissingSkill(data.skillID, idx) 594 return False 595 nextLevel = self.skillQueueSvc.FindNextLevel(data.skillID, None, queue) 596 if nextLevel is None or nextLevel > 5: 597 prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/SkillQueue/SkillFullyPlanned')}) 598 return False 599 isCloneStateRestricted = get_clone_grade_svc().is_skill_level_restricted(data.skillID, nextLevel) 600 if isCloneStateRestricted: 601 prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/SkillQueue/SkillRequiresCloneStateUpgrade')}) 602 return False 603 self.DoAddSkill(data.invtype, nextLevel, idx) 604 return True 605 606 def AddSkillsThroughOtherEntry(self, skillID, idx = None, queue = None, nextLevel = None): 607 mySkills = sm.StartService('skills').GetSkillsIncludingLapsed() 608 skill = mySkills.get(skillID, None) 609 if skill is None: 610 self.HandleMissingSkill(skillID, idx) 611 return 612 if nextLevel is None: 613 if queue is None: 614 queue = self.skillQueueSvc.GetQueue() 615 nextLevel = self.skillQueueSvc.FindNextLevel(skillID, skill.trainedSkillLevel, queue) 616 if nextLevel > 5: 617 return 618 self.DoAddSkill(skillID, nextLevel, idx) 619 sm.ScatterEvent('OnSkillQueueRefreshed') 620 return True 621 622 def DoAddSkill(self, typeID, level, position = None): 623 self.skillQueueSvc.AddSkillToQueue(typeID, level, position=position) 624 625 def HandleMissingSkill(self, typeID, position = None): 626 if self._isSkillPurchaseOverlayActive: 627 return 628 self._isSkillPurchaseOverlayActive = True 629 controller = show_skill_purchase_overlay(self, typeID, position) 630 controller.on_closed.connect(self._OnSkillPurchaseOverlayClosed) 631 632 def _OnSkillPurchaseOverlayClosed(self): 633 self._isSkillPurchaseOverlayActive = False 634 635 def OnSkillQueueModified(self): 636 self.LoadQueueAndAnimate() 637 638 def LoadQueueAndAnimate(self): 639 self.ReloadUpdateThread() 640 animations.FadeTo(self.timelineCont, 1.0, 0.0, duration=0.2, sleep=True) 641 self.LoadQueue() 642 duration = 2.0 643 animations.FadeTo(self.timelineCont, 0.0, 1.0, duration=duration, curveType=uiconst.ANIM_OVERSHOT5) 644 645 def DoMove(self, data, newIdx, queueLength, movingBelow = 0, allEntries = None): 646 skillPos = newIdx 647 if newIdx == -1: 648 skillPos = queueLength - 1 649 if len(allEntries) == 1: 650 self._DoMoveOneEntry(data, skillPos) 651 else: 652 self._DoMoveManyEntries(skillPos, queueLength, allEntries) 653 654 def _DoMoveOneEntry(self, data, skillPos): 655 trainToLevel = data.Get('trainToLevel', None) 656 if data.skillID and trainToLevel: 657 self.skillQueueSvc.MoveSkillToPosition(data.skillID, trainToLevel, skillPos) 658 659 def _DoMoveManyEntries(self, skillPos, queueLength, allEntries): 660 dataAbovePoint = [ x for x in allEntries if x.idx < skillPos ] 661 dataBelowPoint = [ x for x in allEntries if x.idx >= skillPos ] 662 reverseData = dataAbovePoint[:] 663 reverseData.reverse() 664 nextSkillPos = skillPos 665 for eachData in reverseData: 666 for testPos in xrange(nextSkillPos, 0, -1): 667 if self.skillQueueSvc.CheckCanInsertSkillAtPosition(eachData.skillID, eachData.trainToLevel, testPos, check=True): 668 self.skillQueueSvc.MoveSkillToPosition(eachData.skillID, eachData.trainToLevel, testPos) 669 if nextSkillPos == testPos: 670 nextSkillPos -= 1 671 break 672 673 for eachData in dataBelowPoint: 674 for testPos in xrange(skillPos, queueLength): 675 if self.skillQueueSvc.CheckCanInsertSkillAtPosition(eachData.skillID, eachData.trainToLevel, testPos, check=True): 676 self.skillQueueSvc.MoveSkillToPosition(eachData.skillID, eachData.trainToLevel, testPos) 677 if testPos == skillPos: 678 skillPos += 1 679 break 680 681 uthread2.call_after_wallclocktime_delay(self.ReselectDraggedNodes, 0.3, allEntries) 682 683 def ReselectDraggedNodes(self, allEntries): 684 allDragged = {(x.skillID, x.Get('trainToLevel', None)) for x in allEntries} 685 nodesToSelect = [] 686 for eachNode in self.scroll.GetNodes(): 687 if (eachNode.skillID, eachNode.Get('trainToLevel', None)) in allDragged: 688 nodesToSelect.append(eachNode) 689 690 self.scroll.SelectNodes(nodesToSelect) 691 692 def RemoveSkillFromQueue(self, *args): 693 selected = self.scroll.GetSelected() 694 self.DoRemove(None, selected) 695 696 def DoRemove(self, dragObj, entries, *args): 697 entries.reverse() 698 removeList = [] 699 for entry in entries: 700 if entry.__guid__ != 'listentry.SkillQueueSkillEntry': 701 continue 702 if not GetAttrs(entry, 'inQueue'): 703 return 704 removeList.append(entry) 705 706 skills = [ (entry.invtype, entry.Get('trainToLevel', -1)) for entry in removeList ] 707 self.skillQueueSvc.RemoveSkillsFromQueue(skills) 708 self.LoadQueue() 709 710 def OnContentDragEnter(self, dragObj, entries, *args): 711 self.ShowPositionIndicator(entries) 712 713 def ShowPositionIndicator(self, entries): 714 color = self.GetIndicatorColor(entries) 715 if not self.scroll.IsScrollbarVisible() and self.scroll.GetNodes(): 716 lastDropEntry = self.GetLastDropEntry() 717 if lastDropEntry: 718 lastDropEntry.ShowIndicator(color) 719 else: 720 self.changeIndicator.SetRGBA(*color) 721 722 def GetIndicatorColor(self, entries): 723 if IsMultiSkillEntryMove(entries): 724 return COLOR_MOVE_INDICATOR 725 try: 726 allowedMove = self.skillQueueSvc.IsMoveAllowed(entries[0], None) 727 except UserError: 728 allowedMove = False 729 730 if allowedMove: 731 color = COLOR_TRAININGALLOWED 732 elif self.skillQueueSvc.IsSkillEntry(entries[0]): 733 color = COLOR_TRAININGNOTALLOWED 734 else: 735 color = (0, 0, 0, 0) 736 return color 737 738 def GetLastDropEntry(self): 739 nodes = self.scroll.GetNodes() 740 if nodes: 741 return nodes[-1].panel 742 743 def OnContentDragExit(self, *args): 744 self.HidePositionIndicator() 745 746 def HidePositionIndicator(self): 747 self.changeIndicator.opacity = 0.0 748 lastDropEntry = self.GetLastDropEntry() 749 if lastDropEntry: 750 lastDropEntry.HideIndicator() 751 752 def OnDropContDragEnter(self, dragObject, entries): 753 uthread2.StartTasklet(self.dropIndicatorCont.OnDragEnterFromParent, entries, self.scroll) 754 755 def OnDropContDropData(self, dropObj, entries): 756 if self.scroll.IsScrollbarVisible() and self.scroll.GetScrollProportion() >= 1.0: 757 self.DoDropData(dropObj, entries) 758 self.dropIndicatorCont.HideIndicator() 759 760 def DoDropData(self, dragObj, entries, idx = -1): 761 queue = self.skillQueueSvc.GetQueue() 762 self.HidePositionIndicator() 763 if not entries: 764 return 765 if idx == -1: 766 idx = len(queue) 767 data = entries[0] 768 if isinstance(data, dragdata.TypeDragData) and evetypes.IsSkill(data.typeID): 769 self.AddSkillsThroughOtherEntry(data.typeID, idx, queue) 770 elif data.__guid__ == 'listentry.SkillQueueSkillEntry': 771 if data.Get('inQueue', None) and not uicore.uilib.Key(uiconst.VK_SHIFT): 772 movingBelow = 0 773 if idx > data.idx: 774 movingBelow = 1 775 newIdx = max(0, idx) 776 if data.skillID: 777 self.DoMove(data, newIdx, len(queue), movingBelow, allEntries=entries) 778 else: 779 self.AddSkillThroughSkillEntry(data, queue, idx) 780 elif data.__guid__ == 'listentry.SkillEntry': 781 self.AddSkillThroughSkillEntry(data, queue, idx) 782 elif data.__guid__ in ('xtriui.InvItem', 'listentry.InvItem'): 783 category = GetAttrs(data, 'rec', 'categoryID') 784 if category == appConst.categorySkill: 785 sm.StartService('skills').InjectSkillIntoBrain([data.item]) 786 blue.pyos.synchro.SleepWallclock(500) 787 self.AddSkillsThroughOtherEntry(data.item.typeID, idx, queue, nextLevel=1) 788 elif data.__guid__ == 'listentry.SkillTreeEntry': 789 self.AddSkillsThroughOtherEntry(data.typeID, idx, queue) 790 elif data.__guid__ in ('uicls.GenericDraggableForTypeID', 'listentry.GenericMarketItem'): 791 categoryID = evetypes.GetCategoryID(data.typeID) 792 if categoryID == appConst.categorySkill: 793 self.AddSkillsThroughOtherEntry(data.typeID, idx, queue) 794 795 def UpdateQueuedSkillsRemoveButton(self): 796 for node in self.scroll.GetNodes(): 797 if node.__guid__ != 'listentry.SkillQueueSkillEntry': 798 continue 799 if node.panel: 800 node.panel.UpdateRemoveButton() 801 802 def ChangeTimesOnEntriesInQueue(self): 803 timesForSkillsInQueue = self.skillQueueSvc.GetAllTrainingLengths() 804 for node in self.scroll.GetNodes(): 805 if node.__guid__ != 'listentry.SkillQueueSkillEntry': 806 continue 807 times = timesForSkillsInQueue.get((node.skillID, node.trainToLevel)) 808 if times: 809 timeLeft, addedTime, isAccelerated = times 810 else: 811 addedTime = 0 812 isAccelerated = False 813 self.ChangeTimeOnSingleEntry(node, timeLeft=addedTime, isAccelerated=isAccelerated) 814 815 def ChangeTimeOnSingleEntry(self, node, timeLeft = 0, isAccelerated = False): 816 timeHasChanged = long(node.timeLeft) != long(timeLeft) 817 node.timeLeft = timeLeft 818 node.isAccelerated = isAccelerated 819 if node.panel and timeHasChanged: 820 if timeLeft > 0: 821 timeLeftText = localization.formatters.FormatTimeIntervalShortWritten(long(timeLeft), showFrom='day', showTo='second') 822 else: 823 timeLeftText = '' 824 node.panel.timeLeftText.SetText(timeLeftText) 825 node.panel.UpdateAcceleratedMarker() 826 node.panel.FillBoxes(node.skill.trainedSkillLevel, node.trainToLevel) 827 828 def ReloadEntriesIfNeeded(self): 829 self.ChangeTimesOnEntriesInQueue() 830 self.UpdateQueuedSkillsRemoveButton() 831 832 def OnDropData(self, *args): 833 self.DoDropData(*args) 834 835 def OnDragEnter(self, *args): 836 self.OnContentDragEnter(*args) 837 838 def OnDragExit(self, *args): 839 self.OnContentDragExit(*args) 840 841 def OnSkillsChanged(self, *args): 842 self.queueLastApplied = self.skillQueueSvc.GetServerQueue() 843 self.skillQueueSvc.PrimeCache(force=True) 844 self.UpdatePausePlayButtonTexturePath() 845 self.UpdateApplySkillPointsButton() 846 self.LoadQueueAndAnimate() 847 848 def OnSubscriptionChanged(self, *args): 849 self.OnSkillsChanged() 850 self.CheckDisplayOmegaButton() 851 852 def CheckDisplayOmegaButton(self): 853 self.omegaButton.display = not get_clone_grade_svc().is_omega_with_grace_period() 854 855 def StartOrStopTraining(self, *args): 856 if self.skillQueueSvc.SkillInTraining(): 857 self.PauseTraining() 858 else: 859 self.ApplySkillQueue() 860 PlaySound('skill_training_start_play') 861 862 def SaveSkillQueue(self): 863 isTraining = bool(self.skillQueueSvc.SkillInTraining()) 864 self.ApplySkillQueue(activate=isTraining) 865 866 def OnSkillQueueChanged(self): 867 self.queueLastApplied = self.skillQueueSvc.GetQueue() 868 self.OnSkillsChanged() 869 870 def OnSkillQueueRefreshed(self): 871 self.OnSkillQueueChanged() 872 873 def OnSkillQueuePaused(self): 874 self.OnSkillQueueChanged() 875 876 def ImportFromClipboardReplace(self): 877 return self.ImportFromClipboard(clearQueue=1) 878 879 def ImportFromClipboard(self, clearQueue = 0, *args): 880 sm.GetService('loading').ProgressWnd(GetByLabel('UI/SkillQueue/ImportingSkillPlan'), '', 1, 6) 881 uthread.new(self.ImportFromClipboard_thread, clearQueue) 882 883 def ImportFromClipboard_thread(self, clearQueue = 0, *args): 884 importingText = GetByLabel('UI/SkillQueue/ImportingSkillPlan') 885 skillplanImporter = self.skillQueueSvc.GetSkillPlanImporter() 886 sm.GetService('loading').ProgressWnd(importingText, '', 2, 6) 887 skillPlanText = GetClipboardData() 888 skillList, failedLines = skillplanImporter.GetSkillsToAdd(skillPlanText, clearQueue) 889 if clearQueue: 890 nodes = self.scroll.GetNodes() 891 self.DoRemove(None, nodes) 892 importingStatus = SkillPlanImportingStatus() 893 sm.GetService('loading').ProgressWnd(importingText, '', 3, 6) 894 skills = sm.GetService('skills').GetSkills() 895 for typeID, skillLevel, why in skillList: 896 blue.pyos.BeNice() 897 if importingStatus.TooManySkillsAdded() and why == importConst.MANUAL_ADD: 898 importingStatus.AddToFailed(typeID, skillLevel, importConst.FAILED_TOO_MANY_SKILLS) 899 continue 900 lowerLevelFailReason = importingStatus.ReasonForFailingForLowerLevel(typeID, skillLevel) 901 if lowerLevelFailReason is not None and lowerLevelFailReason != importConst.FAILED_SKILL_IN_QUEUE: 902 importingStatus.AddToFailed(typeID, skillLevel, lowerLevelFailReason) 903 continue 904 skill = skills.get(typeID, None) 905 if skill is None or skill.trainedSkillLevel is None: 906 importingStatus.AddToFailed(typeID, skillLevel, importConst.FAILED_SKILL_NOT_INJECTED) 907 continue 908 if skill.trainedSkillLevel >= skillLevel: 909 importingStatus.AddToFailed(typeID, skillLevel, importConst.FAILED_PREVIOUSLY_TRAINED) 910 continue 911 nextLevel = self.skillQueueSvc.FindNextLevel(typeID, skill.trainedSkillLevel, self.skillQueueSvc.skillQueue) 912 if get_clone_grade_svc().is_skill_level_restricted(typeID, skillLevel): 913 importingStatus.AddToFailed(typeID, skillLevel, importConst.FAILED_OMEGA_RESTRICTION) 914 continue 915 if nextLevel < skillLevel: 916 importingStatus.AddToFailed(typeID, skillLevel, importConst.FAILED_INCORRECT_ORDER) 917 continue 918 try: 919 self.DoAddSkill(typeID, skillLevel) 920 except UserError as userError: 921 importingStatus.AddToFailed(typeID, skillLevel, userError.msg) 922 except Exception as e: 923 importingStatus.AddToFailed(typeID, skillLevel, 'unknown') 924 else: 925 importingStatus.IncreaseAddedCount() 926 927 sm.GetService('loading').ProgressWnd(importingText, '', 5, 6) 928 self.UpdateTime() 929 self.ReloadEntriesIfNeeded() 930 sm.GetService('loading').ProgressWnd(importingText, '', 6, 6) 931 wnd = SkillImportStatusWindow.Open() 932 wnd.LoadInitialImportingStatus(importingStatus, failedLines) 933 934 def ApplySkillPoints(self, *args): 935 if self.isApplying: 936 return 937 self.isApplying = True 938 try: 939 isTraining = self.IsTraining() 940 self.SaveChanges(activate=isTraining) 941 self.skillQueueSvc.ApplyFreeSkillPointsToQueue() 942 self.skillQueueSvc.BeginTransaction() 943 PlaySound('st_allocate_skillpoints_play') 944 finally: 945 self.isApplying = False 946 947 def IsTraining(self): 948 return self.skillQueueSvc.SkillInTraining() is not None