skillQueueSvc.py (49576B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\skillQueueSvc.py 2 import sys 3 from contextlib import contextmanager 4 import blue 5 import carbonui.const as uiconst 6 import evetypes 7 import gametime 8 import localization 9 from carbon.client.script.environment.AudioUtil import PlaySound 10 from carbon.common.script.sys.service import Service 11 from carbonui.util.various_unsorted import GetAttrs 12 from characterskills.queue import SKILLQUEUE_MAX_NUM_SKILLS, GetQueueEntry, GetSkillQueueTimeLength 13 from characterskills.skill_training import SkillLevelAlreadyTrainedError 14 from characterskills.util import GetSkillLevelRaw, GetLevelProgress 15 from eve.client.script.ui.util import uix 16 from eveexceptions import UserError 17 from menu import MenuLabel 18 from omega.client.clone_grade_svc import get_clone_grade_svc 19 from player_messaging.client.ui_message import prompt_player_fsd_dialog 20 from skills import skillConst 21 from skills.client.errors import SkillQueueOverwritten 22 from textImporting import IsUsingDefaultLanguage 23 from textImporting.importSkillplan import ImportSkillPlan 24 from uthread import UnLock, ReentrantLock 25 from utillib import KeyVal 26 27 class SkillQueueService(Service): 28 __exportedcalls__ = {'SkillInTraining': []} 29 __guid__ = 'svc.skillqueue' 30 __servicename__ = 'skillqueue' 31 __displayname__ = 'Skill Queue Client Service' 32 __dependencies__ = ['godma', 'skills', 'machoNet'] 33 __notifyevents__ = ['OnSkillQueuePausedServer', 34 'OnMultipleCharactersTrainingUpdated', 35 'OnNewSkillQueueSaved', 36 'OnSubscriptionChanged', 37 'OnCharacterSessionChanged', 38 'OnSessionReset'] 39 40 def __init__(self): 41 super(SkillQueueService, self).__init__() 42 self.skillQueue = [] 43 self.cachedSkillQueue = None 44 self.skillQueueCache = None 45 self.skillplanImporter = None 46 self.maxSkillqueueTimeLength = None 47 self._isAllCharacterTrainingSlotsUsed = None 48 49 def Run(self, memStream = None): 50 self.PrimeSkillQueue() 51 52 def ReInitialize(self): 53 self.skillQueue = [] 54 self.cachedSkillQueue = None 55 self.skillQueueCache = None 56 self.skillplanImporter = None 57 self.maxSkillqueueTimeLength = None 58 self._isAllCharacterTrainingSlotsUsed = None 59 60 def OnCharacterSessionChanged(self, _oldCharacterID, newCharacterID): 61 self.ReInitialize() 62 self.PrimeCache() 63 if newCharacterID is not None: 64 self.PrimeSkillQueue() 65 66 def OnSessionReset(self): 67 self.ReInitialize() 68 69 def PrimeSkillQueue(self): 70 self.skillQueue, freeSkillPoints = self.GetSkillQueueAndFreeSPsFromServer() 71 if freeSkillPoints is not None and freeSkillPoints > 0: 72 self.skills.SetFreeSkillPoints(freeSkillPoints, showPointer=False) 73 74 def GetMaxSkillQueueLimitLength(self): 75 if not self.maxSkillqueueTimeLength: 76 self.maxSkillqueueTimeLength = GetSkillQueueTimeLength(get_clone_grade_svc().get_clone_grade_with_grace_period()) 77 return self.maxSkillqueueTimeLength 78 79 def OnSubscriptionChanged(self): 80 self.maxSkillqueueTimeLength = None 81 82 def _ValidateCache(self): 83 if self.IsTransactionOpen(): 84 raise SkillQueueOverwritten(session.charid, self.cachedSkillQueue) 85 86 def BeginTransaction(self): 87 with self.ChangeQueueLock(): 88 sendEvent = False 89 skillInTraining = self.SkillInTraining() 90 try: 91 self._ValidateCache() 92 except SkillQueueOverwritten as exc: 93 sendEvent = True 94 self.LogException(exc) 95 96 self.skillQueueCache = None 97 self.skillQueue, freeSkillPoints = self.GetSkillQueueAndFreeSPsFromServer() 98 if freeSkillPoints > 0: 99 self.skills.SetFreeSkillPoints(freeSkillPoints) 100 self.cachedSkillQueue = self.GetQueue() 101 if not skillInTraining: 102 self.UpdateQueueTimestamps() 103 if sendEvent: 104 sm.ScatterEvent('OnSkillQueueRefreshed') 105 106 def GetSkillQueueAndFreeSPsFromServer(self): 107 return self.skills.GetSkillHandler().GetSkillQueueAndFreePoints() 108 109 def UpdateQueueTimestamps(self): 110 queueWithTimestamps = [] 111 for idx, trainingSkill in enumerate(self.skillQueue): 112 startTime = None 113 if idx > 0: 114 startTime = self.skillQueue[idx - 1].trainingEndTime 115 currentSkill = self.skills.GetSkillIncludingLapsed(trainingSkill.trainingTypeID) 116 queueEntry = GetQueueEntry(trainingSkill.trainingTypeID, trainingSkill.trainingToLevel, idx, currentSkill, queueWithTimestamps, lambda x, y: self.GetTimeForTraining(x, y, startTime), KeyVal, True) 117 queueWithTimestamps.append(queueEntry) 118 119 self.skillQueue = queueWithTimestamps 120 121 def RollbackTransaction(self): 122 with self.ChangeQueueLock(): 123 if self.cachedSkillQueue is None: 124 self.LogError('%s: Cannot rollback a skill queue transaction - no transaction was opened!' % session.charid) 125 return 126 self.skillQueue = self.cachedSkillQueue 127 self.skillQueueCache = None 128 self.cachedSkillQueue = None 129 130 def CommitTransaction(self, activate = True): 131 with self.ChangeQueueLock(): 132 if activate: 133 self._CheckCloneGradeRestrictions() 134 self.PrimeCache(force=True) 135 if self.IsQueueChanged() or activate: 136 try: 137 if activate: 138 self.TrimQueue() 139 queueInfo = {idx:(x.trainingTypeID, x.trainingToLevel) for idx, x in enumerate(self.skillQueue)} 140 skillHandler = self.skills.GetSkillHandler() 141 skillHandler.SaveNewQueue(queueInfo, activate=activate) 142 self.cachedSkillQueue = None 143 sm.ScatterEvent('OnSkillQueueRefreshed') 144 except Exception as e: 145 msg = getattr(e, 'msg', None) 146 if msg not in ('UserAlreadyHasSkillInTraining', 'SkillInQueueRequiresOmegaCloneState', 'SkillInQueueOverAlphaSpTrainingSize'): 147 self.RollbackTransaction() 148 sm.ScatterEvent('OnSkillQueueRefreshed') 149 raise 150 151 def _CheckCloneGradeRestrictions(self): 152 if get_clone_grade_svc().is_omega_with_grace_period(): 153 return 154 for skill in self.skillQueue: 155 levelLimit = get_clone_grade_svc().get_max_skill_level(skill.trainingTypeID) 156 if levelLimit < skill.trainingToLevel: 157 raise UserError('SkillInQueueRequiresOmegaCloneState') 158 159 def CheckCanAppendSkill(self, skillTypeID, skillLevel = None, check = False, performLengthTest = True): 160 if skillLevel is None: 161 skillLevel = self.FindNextLevel(skillTypeID, skillQueue=self.GetQueue()) 162 position = len(self.skillQueue) 163 return self.CheckCanInsertSkillAtPosition(skillTypeID, skillLevel, position, check, performLengthTest) 164 165 def CheckCanInsertSkillAtPosition(self, skillTypeID, skillLevel, position = None, check = 0, performLengthTest = True): 166 if position is None or position < 0 or position > len(self.skillQueue): 167 raise UserError('QueueInvalidPosition') 168 self.PrimeCache() 169 mySkill = self.skills.GetSkillIncludingLapsed(skillTypeID) 170 ret = True 171 try: 172 if mySkill is None: 173 raise UserError('QueueSkillNotUploaded') 174 if mySkill.trainedSkillLevel >= skillLevel: 175 raise UserError('QueueCannotTrainPreviouslyTrainedSkills') 176 if mySkill.trainedSkillLevel >= skillConst.skill_max_level: 177 raise UserError('QueueCannotTrainPastMaximumLevel', {'typeName': (const.UE_TYPEID, skillTypeID)}) 178 if skillTypeID in self.skillQueueCache: 179 for lvl, lvlPosition in self.skillQueueCache[skillTypeID].iteritems(): 180 if lvl < skillLevel and lvlPosition >= position: 181 raise UserError('QueueCannotPlaceSkillLevelsOutOfOrder') 182 elif lvl > skillLevel and lvlPosition < position: 183 raise UserError('QueueCannotPlaceSkillLevelsOutOfOrder') 184 185 if position >= 0 and performLengthTest: 186 timeLeft, _ = self.GetTrainingLengthOfQueue(position) 187 if timeLeft > self.GetMaxSkillQueueLimitLength(): 188 raise UserError('QueueTooLong') 189 if mySkill.trainedSkillPoints == 0: 190 requirements = sm.GetService('skills').GetRequiredSkills(skillTypeID) 191 for requiredTypeID, requiredLevel in requirements.iteritems(): 192 isRequirementsQueued = False 193 if requiredTypeID in self.skillQueueCache: 194 for level, queuedPosition in self.skillQueueCache[requiredTypeID].iteritems(): 195 if level <= requiredLevel and queuedPosition > position: 196 raise UserError('QueueCannotPlaceSkillBeforeRequirements') 197 if level >= requiredLevel and queuedPosition < position: 198 isRequirementsQueued = True 199 200 skill = self.skills.GetSkill(requiredTypeID) 201 if skill is None: 202 raise UserError('QueueCannotPlaceSkillBeforeRequirements') 203 isRequirementsTrained = skill.trainedSkillLevel >= requiredLevel 204 if not isRequirementsTrained and not isRequirementsQueued: 205 raise UserError('QueueCannotPlaceSkillBeforeRequirements') 206 207 dependencies = sm.GetService('skills').GetDependentSkills(skillTypeID) 208 for dependentTypeID, requiredLevel in dependencies.iteritems(): 209 if dependentTypeID not in self.skillQueueCache: 210 continue 211 for level, queuedPosition in self.skillQueueCache[dependentTypeID].iteritems(): 212 if requiredLevel == skillLevel and queuedPosition < position: 213 raise UserError('QueueCannotPlaceSkillAfterDependentSkills') 214 215 if get_clone_grade_svc().is_skill_level_restricted(skillTypeID, skillLevel): 216 raise UserError('QueueCannotTrainOmegaRestrictedSkill', {'skillID': skillTypeID}) 217 except UserError as ue: 218 checkedErrors = ('QueueCannotPlaceSkillAfterDependentSkills', 'QueueCannotPlaceSkillBeforeRequirements', 'QueueCannotPlaceSkillLevelsOutOfOrder', 'QueueCannotTrainPreviouslyTrainedSkills', 'QueueSkillNotUploaded', 'QueueTooLong', 'QueueCannotTrainOmegaRestrictedSkill', 'QueueCannotTrainPastMaximumLevel') 219 if check and ue.msg in checkedErrors: 220 sys.exc_clear() 221 ret = False 222 else: 223 raise 224 225 return ret 226 227 def AddSkillToQueue(self, skillTypeID, skillLevel = None, position = None): 228 if not evetypes.IsPublished(skillTypeID): 229 raise UserError('ItemNotASkill', {'skillName': skillTypeID}) 230 if skillLevel is None: 231 skillLevel = self.FindNextLevel(skillTypeID, skillQueue=self.GetQueue()) 232 if skillLevel > skillConst.skill_max_level: 233 return 234 if self.FindInQueue(skillTypeID, skillLevel) is not None: 235 raise UserError('QueueSkillAlreadyPresent') 236 skillQueueLength = len(self.skillQueue) 237 if skillQueueLength >= SKILLQUEUE_MAX_NUM_SKILLS: 238 raise UserError('QueueTooManySkills', {'num': SKILLQUEUE_MAX_NUM_SKILLS}) 239 newPos = position if position is not None and position >= 0 else skillQueueLength 240 currentSkill = self.skills.GetSkill(skillTypeID) 241 self.CheckCanInsertSkillAtPosition(skillTypeID, skillLevel, newPos) 242 startTime = None 243 if newPos != 0: 244 startTime = self.skillQueue[newPos - 1].trainingEndTime 245 queueEntry = GetQueueEntry(skillTypeID, skillLevel, newPos, currentSkill, self.skillQueue, lambda x, y: self.GetTimeForTraining(x, y, startTime), KeyVal, self.SkillInTraining() is not None) 246 if newPos == skillQueueLength: 247 self.skillQueue.append(queueEntry) 248 else: 249 if newPos > skillQueueLength: 250 raise UserError('QueueInvalidPosition') 251 self.skillQueue.insert(newPos, queueEntry) 252 for entry in self.skillQueue[newPos + 1:]: 253 entry.queuePosition += 1 254 255 self.skillQueueCache = None 256 self.AddToCache(skillTypeID, skillLevel, newPos) 257 self.TrimQueue() 258 self.OnClientQueueModified() 259 PlaySound(uiconst.SOUND_ADD_OR_USE) 260 if newPos == 0: 261 sm.ScatterEvent('OnClientEvent_SkillPrioritized', skillTypeID) 262 sm.ScatterEvent('OnClientEvent_SkillAddedToQueue', skillTypeID, skillLevel) 263 return newPos 264 265 def AddSkillsToQueue(self, skills, ignoreAlreadyPresent = False): 266 numSkillsAdded = 0 267 try: 268 while skills: 269 skillID, level = skills.pop(0) 270 if level is None: 271 level = self.FindNextLevel(skillID, skillQueue=self.GetQueue()) 272 if self.FindInQueue(skillID, level) is not None: 273 if ignoreAlreadyPresent: 274 continue 275 raise UserError('QueueSkillAlreadyPresent') 276 skillQueueLength = len(self.skillQueue) 277 if skillQueueLength >= SKILLQUEUE_MAX_NUM_SKILLS: 278 if numSkillsAdded < 1: 279 raise UserError('QueueTooManySkills', {'num': SKILLQUEUE_MAX_NUM_SKILLS}) 280 self.TrimQueue() 281 self.OnClientQueueModified() 282 skillList = '<br>'.join([ '- %s' % localization.GetByLabel('UI/InfoWindow/SkillAndLevel', skill=typeID, skillLevel=lvl) for typeID, lvl in [(skillID, level)] + skills ]) 283 raise UserError('QueueTooManySkillNotAllAdded', {'maxNumSkills': SKILLQUEUE_MAX_NUM_SKILLS, 284 'skillList': skillList}) 285 position = len(self.skillQueue) 286 self.CheckCanInsertSkillAtPosition(skillID, level, position) 287 startTime = None 288 if position > 0: 289 startTime = self.skillQueue[position - 1].trainingEndTime 290 entry = GetQueueEntry(skillID, level, position, self.skills.GetSkillIncludingLapsed(skillID), self.skillQueue, lambda x, y: self.GetTimeForTraining(x, y, startTime), KeyVal, self.SkillInTraining() is not None) 291 self.skillQueue.append(entry) 292 numSkillsAdded += 1 293 self.AddToCache(skillID, level, position) 294 295 PlaySound(uiconst.SOUND_ADD_OR_USE) 296 except Exception: 297 self.LogException() 298 return numSkillsAdded 299 300 self.TrimQueue() 301 self.OnClientQueueModified() 302 return numSkillsAdded 303 304 def AddSkillAndRequirementsToQueue(self, skillTypeID, skillLevel = None): 305 if not self.IsTransactionOpen(): 306 self.BeginTransaction() 307 if skillLevel is None: 308 skillLevel = self.FindNextLevel(skillTypeID, skillQueue=self.GetQueue()) 309 if skillLevel > skillConst.skill_max_level: 310 return 311 required = self.skills.GetSkillsMissingToUseItemRecursive(skillTypeID) 312 required[skillTypeID] = skillLevel 313 314 def already_trained_or_queued(x): 315 skillID, level = x 316 skill = self.skills.GetSkillIncludingLapsed(skillID) 317 if skill is None or skill.trainedSkillLevel is None or skill.trainedSkillLevel >= level: 318 return False 319 queued = self.skills.GetMaxSkillLevelsInQueue() 320 if queued.get(skillID, 0) >= level: 321 return False 322 return True 323 324 skills = filter(already_trained_or_queued, required.items()) 325 skillList = [] 326 while skills: 327 skillID, level = skills.pop(0) 328 required = self.skills.GetRequiredSkillsRecursive(skillID) 329 if any((sid in required for sid, _ in skills)): 330 skills.append((skillID, level)) 331 continue 332 nextLevel = int(self.FindNextLevel(skillID, skillQueue=self.GetQueue())) 333 for lvl in xrange(nextLevel, int(level + 1)): 334 skillList.append((skillID, lvl)) 335 336 self.AddSkillsToQueue(skillList) 337 338 def IsTransactionOpen(self): 339 return self.cachedSkillQueue is not None 340 341 def RemoveSkillFromQueue(self, skillTypeID, skillLevel): 342 self.CheckCanRemoveSkillFromQueue(skillTypeID, skillLevel) 343 self.InternalRemoveFromQueue(skillTypeID, skillLevel) 344 self.OnClientQueueModified() 345 PlaySound(uiconst.SOUND_REMOVE) 346 sm.ScatterEvent('OnClientEvent_SkillsRemovedFromQueue', ((skillTypeID, skillLevel),)) 347 348 def RemoveSkillsFromQueue(self, skills): 349 removed = [] 350 try: 351 for typeID, level in skills: 352 self.CheckCanRemoveSkillFromQueue(typeID, level) 353 self.InternalRemoveFromQueue(typeID, level) 354 removed.append((typeID, level)) 355 356 finally: 357 if removed: 358 self.OnClientQueueModified() 359 PlaySound(uiconst.SOUND_REMOVE) 360 sm.ScatterEvent('OnClientEvent_SkillsRemovedFromQueue', removed) 361 362 def OnClientQueueModified(self): 363 if self.IsAllCharacterTrainingSlotsUsed(): 364 activate = False 365 else: 366 activate = True 367 try: 368 self.CommitTransaction(activate) 369 self.BeginTransaction() 370 sm.ScatterEvent('OnSkillQueueModified') 371 except UserError as e: 372 if e.msg == 'SkillInQueueRequiresOmegaCloneState': 373 sm.ScatterEvent('OnSkillQueueModified') 374 elif e.msg == 'SkillInQueueOverAlphaSpTrainingSize': 375 self.CommitTransaction(activate=False) 376 self.BeginTransaction() 377 sm.ScatterEvent('OnSkillQueueModified') 378 else: 379 raise 380 381 def CheckCanRemoveSkillFromQueue(self, skillTypeID, skillLevel): 382 self.PrimeCache() 383 if skillTypeID not in self.skillQueueCache: 384 return 385 for cacheLevel in self.skillQueueCache[skillTypeID]: 386 if cacheLevel > skillLevel: 387 raise UserError('QueueCannotRemoveSkillsWithHigherLevelsStillInQueue') 388 389 dependencies = sm.GetService('skills').GetDependentSkills(skillTypeID) 390 for dependentTypeID, requiredLevel in dependencies.iteritems(): 391 if skillLevel <= requiredLevel and dependentTypeID in self.skillQueueCache: 392 raise UserError('QueueCannotRemoveSkillsWithDependentSkillsInQueue') 393 394 def FindInQueue(self, skillTypeID, skillLevel): 395 self.PrimeCache() 396 if skillTypeID not in self.skillQueueCache: 397 return None 398 if skillLevel not in self.skillQueueCache[skillTypeID]: 399 return None 400 return self.skillQueueCache[skillTypeID][skillLevel] 401 402 def MoveSkillToPosition(self, skillTypeID, skillLevel, position): 403 self.CheckCanInsertSkillAtPosition(skillTypeID, skillLevel, position) 404 self.PrimeCache() 405 currentPosition = self.skillQueueCache[skillTypeID][skillLevel] 406 if currentPosition < position: 407 position -= 1 408 self.InternalRemoveFromQueue(skillTypeID, skillLevel) 409 newPosition = self.AddSkillToQueue(skillTypeID, skillLevel, position) 410 return newPosition 411 412 def GetQueue(self): 413 return self.skillQueue[:] 414 415 def GetQueueAsRequirements(self): 416 return [ (s.trainingTypeID, s.trainingToLevel) for s in self.GetQueue() ] 417 418 def GetSkillLevelsInQueue(self): 419 return {skill.trainingTypeID:skill.trainingToLevel for skill in self.GetQueue()} 420 421 def GetServerQueue(self): 422 if self.IsTransactionOpen(): 423 return self.cachedSkillQueue[:] 424 else: 425 return self.GetQueue() 426 427 def GetNumberOfSkillsInQueue(self): 428 return len(self.skillQueue) 429 430 def GetTrainingLengthOfQueue(self, position = None): 431 if position is not None and position < 0: 432 raise RuntimeError('Invalid queue position: ', position) 433 trainingTime = 0 434 spInQueue = 0 435 skillBoosters = self.GetSkillAcceleratorBoosters() 436 playerTheoreticalSkillPoints = {} 437 skills = self.skills.GetSkillsIncludingLapsed() 438 currentIndex = 0 439 finalIndex = position 440 if finalIndex is None: 441 finalIndex = len(self.skillQueue) 442 for trainingSkill in self.skillQueue: 443 queueSkillTypeID = trainingSkill.trainingTypeID 444 queueSkillLevel = trainingSkill.trainingToLevel 445 if currentIndex >= finalIndex: 446 break 447 currentIndex += 1 448 if queueSkillTypeID not in playerTheoreticalSkillPoints: 449 skill = self.skills.GetSkill(queueSkillTypeID) 450 playerTheoreticalSkillPoints[queueSkillTypeID] = self.GetSkillPointsFromSkillObject(queueSkillTypeID, skill) 451 addedSP, addedTime, isAccelerated = self.GetAddedSpAndAddedTimeForSkill(queueSkillTypeID, queueSkillLevel, skills, playerTheoreticalSkillPoints, trainingTime, skillBoosters) 452 trainingTime += addedTime 453 spInQueue += addedSP 454 playerTheoreticalSkillPoints[queueSkillTypeID] += addedSP 455 456 return (trainingTime, spInQueue) 457 458 def GetTrainingEndTimeOfQueue(self): 459 timeLeft, _ = self.GetTrainingLengthOfQueue() 460 timeEnd = gametime.GetWallclockTime() + timeLeft 461 inTraining = self.SkillInTraining() 462 if inTraining and self.FindInQueue(inTraining.typeID, inTraining.trainedSkillLevel + 1) > 0: 463 fullTrainingTime = self.GetTrainingLengthOfSkill(inTraining.typeID, inTraining.trainedSkillLevel + 1) 464 ETA = self.GetEndOfTraining(inTraining.typeID) 465 if ETA is not None: 466 timeEnd -= fullTrainingTime[1] 467 leftTime = ETA - blue.os.GetWallclockTime() 468 timeEnd += leftTime 469 return long(timeEnd) 470 471 def GetTrainingLengthOfSkill(self, skillTypeID, skillLevel, position = None): 472 if position is not None and (position < 0 or position > len(self.skillQueue)): 473 raise RuntimeError('GetTrainingLengthOfSkill received an invalid position.') 474 trainingTime = 0 475 currentIndex = 0 476 targetIndex = position 477 if targetIndex is None: 478 targetIndex = self.FindInQueue(skillTypeID, skillLevel) 479 if targetIndex is None: 480 targetIndex = len(self.skillQueue) 481 playerTheoreticalSkillPoints = {} 482 skills = self.skills.GetSkillsIncludingLapsed() 483 skillBoosters = self.GetSkillAcceleratorBoosters() 484 for trainingSkill in self.skillQueue: 485 queueSkillTypeID = trainingSkill.trainingTypeID 486 queueSkillLevel = trainingSkill.trainingToLevel 487 if currentIndex >= targetIndex: 488 break 489 elif queueSkillTypeID == skillTypeID and queueSkillLevel == skillLevel and currentIndex < targetIndex: 490 currentIndex += 1 491 continue 492 addedSP, addedTime, _ = self.GetAddedSpAndAddedTimeForSkill(queueSkillTypeID, queueSkillLevel, skills, playerTheoreticalSkillPoints, trainingTime, skillBoosters) 493 currentIndex += 1 494 trainingTime += addedTime 495 playerTheoreticalSkillPoints[queueSkillTypeID] += addedSP 496 497 addedSP, addedTime, isAccelerated = self.GetAddedSpAndAddedTimeForSkill(skillTypeID, skillLevel, skills, playerTheoreticalSkillPoints, trainingTime, skillBoosters) 498 trainingTime += addedTime 499 return (long(trainingTime), long(addedTime), isAccelerated) 500 501 def GetSkillPointsAndTimeNeededToTrain(self, skillTypeID, skillLevel, existingSkillPoints = 0, trainingStartTime = None): 502 calculator = self.skills.GetSkillTrainingTimeCalculator() 503 try: 504 if existingSkillPoints: 505 skillPointsToTrain, trainingTime = calculator.get_skill_points_and_time_to_train_given_existing_skill_points(skillTypeID, skillLevel, trainingStartTime, existingSkillPoints) 506 else: 507 skillPointsToTrain, trainingTime = calculator.get_skill_points_and_time_to_train(skillTypeID, skillLevel, trainingStartTime) 508 except SkillLevelAlreadyTrainedError: 509 skillPointsToTrain = 0 510 trainingTime = 0 511 512 return (skillPointsToTrain, float(trainingTime)) 513 514 def TrimQueue(self): 515 trainingTime = 0 516 skillBoosters = self.GetSkillAcceleratorBoosters() 517 playerTheoreticalSkillPoints = {} 518 skills = self.skills.GetSkillsIncludingLapsed() 519 cutoffIndex = 0 520 for trainingSkill in self.skillQueue: 521 queueSkillTypeID = trainingSkill.trainingTypeID 522 queueSkillLevel = trainingSkill.trainingToLevel 523 cutoffIndex += 1 524 addedSP, addedTime, isAccelerated = self.GetAddedSpAndAddedTimeForSkill(queueSkillTypeID, queueSkillLevel, skills, playerTheoreticalSkillPoints, trainingTime, skillBoosters) 525 trainingTime += addedTime 526 playerTheoreticalSkillPoints[queueSkillTypeID] += addedSP 527 if trainingTime > self.GetMaxSkillQueueLimitLength(): 528 break 529 530 if cutoffIndex < len(self.skillQueue): 531 removedSkills = self.skillQueue[cutoffIndex:] 532 self.skillQueue = self.skillQueue[:cutoffIndex] 533 self.skillQueueCache = None 534 prompt_player_fsd_dialog('skillQueueTrimmed', {'num': len(removedSkills)}) 535 536 def GetSkillAcceleratorBoosters(self): 537 return self.skills.GetSkillAcceleratorBoosters() 538 539 def GetAddedSpAndAddedTimeForSkill(self, skillTypeID, skillLevel, skillSet, theoreticalSkillPointsDict, trainingTimeOffset, skillBoosters): 540 skillStartTime = long(trainingTimeOffset) + gametime.GetWallclockTime() 541 isAccelerated = skillBoosters.is_any_booster_active_at_time(skillStartTime) 542 if skillTypeID not in theoreticalSkillPointsDict: 543 skillObj = skillSet.get(skillTypeID, None) 544 theoreticalSkillPointsDict[skillTypeID] = self.GetSkillPointsFromSkillObject(skillTypeID, skillObj) 545 addedSP, addedTime = self.GetSkillPointsAndTimeNeededToTrain(skillTypeID, skillLevel, theoreticalSkillPointsDict[skillTypeID], skillStartTime) 546 return (addedSP, addedTime, isAccelerated) 547 548 def GetAllTrainingLengths(self): 549 trainingTime = 0 550 skillBoosters = self.GetSkillAcceleratorBoosters() 551 resultsDict = {} 552 playerTheoreticalSkillPoints = {} 553 skills = self.skills.GetSkillsIncludingLapsed() 554 for trainingSkill in self.skillQueue: 555 queueSkillTypeID = trainingSkill.trainingTypeID 556 queueSkillLevel = trainingSkill.trainingToLevel 557 addedSP, addedTime, isAccelerated = self.GetAddedSpAndAddedTimeForSkill(queueSkillTypeID, queueSkillLevel, skills, playerTheoreticalSkillPoints, trainingTime, skillBoosters) 558 trainingTime += addedTime 559 playerTheoreticalSkillPoints[queueSkillTypeID] += addedSP 560 resultsDict[queueSkillTypeID, queueSkillLevel] = (trainingTime, addedTime, isAccelerated) 561 562 return resultsDict 563 564 def ApplyFreeSkillPointsToQueue(self): 565 handler = sm.GetService('skills').GetSkillHandler() 566 pointsBySkillTypeID = handler.GetFreeSkillPointsAppliedToQueue() 567 if pointsBySkillTypeID: 568 handler.ApplyFreeSkillPointsToQueue() 569 570 def ApplyFreeSkillPointsToSkills(self, skillTypeIDsAndLevels): 571 handler = sm.GetService('skills').GetSkillHandler() 572 pointsBySkillTypeID = handler.GetFreeSkillPointsAppliedToSkills(skillTypeIDsAndLevels) 573 if pointsBySkillTypeID: 574 handler.ApplyFreeSkillPointsToSkills(skillTypeIDsAndLevels) 575 576 def GetSkillLevelAndProgressWithFreePoints(self, pointsBySkillTypeID): 577 skillSvc = sm.GetService('skills') 578 levelBySkillTypeID = {} 579 for typeID, points in pointsBySkillTypeID.iteritems(): 580 currentPoints = skillSvc.MySkillPoints(typeID) 581 totalPoints = currentPoints + points 582 rank = skillSvc.GetSkillRank(typeID) 583 level = GetSkillLevelRaw(totalPoints, rank) 584 progress = GetLevelProgress(totalPoints, rank) 585 levelBySkillTypeID[typeID] = (level, progress) 586 587 return levelBySkillTypeID 588 589 def GetProgressOfAllSkills(self): 590 skillSvc = sm.GetService('skills') 591 progressBySkillTypeID = {} 592 for skill in self.GetQueue(): 593 currentPoints = skillSvc.MySkillPoints(skill.trainingTypeID) 594 rank = skillSvc.GetSkillRank(skill.trainingTypeID) 595 progress = GetLevelProgress(currentPoints, rank) 596 progressBySkillTypeID[skill.trainingTypeID] = progress 597 598 return progressBySkillTypeID 599 600 def InternalRemoveFromQueue(self, skillTypeID, skillLevel): 601 if not len(self.skillQueue): 602 return 603 skillPosition = self.FindInQueue(skillTypeID, skillLevel) 604 if skillPosition is None: 605 raise UserError('QueueSkillNotPresent') 606 if skillPosition == len(self.skillQueue): 607 del self.skillQueueCache[skillTypeID][skillLevel] 608 self.skillQueue.pop() 609 else: 610 self.skillQueueCache = None 611 self.skillQueue.pop(skillPosition) 612 613 def ClearCache(self): 614 self.skillQueueCache = None 615 616 def AddToCache(self, skillTypeID, skillLevel, position): 617 self.PrimeCache() 618 if skillTypeID not in self.skillQueueCache: 619 self.skillQueueCache[skillTypeID] = {} 620 self.skillQueueCache[skillTypeID][skillLevel] = position 621 622 def GetPlayerAttributeDict(self): 623 return self.skills.GetCharacterAttributes() 624 625 def PrimeCache(self, force = False): 626 if force: 627 self.skillQueueCache = None 628 if self.skillQueueCache is None: 629 i = 0 630 self.skillQueueCache = {} 631 for trainingSkill in self.skillQueue: 632 self.AddToCache(trainingSkill.trainingTypeID, trainingSkill.trainingToLevel, i) 633 i += 1 634 635 def GetSkillPointsFromSkillObject(self, skillTypeID, skillInfo): 636 if skillInfo is None: 637 return 0 638 totalSkillPoints = skillInfo.trainedSkillPoints or 0 639 trainingSkill = self.SkillInTraining(skillTypeID) 640 serverQueue = self.GetServerQueue() 641 if trainingSkill and len(serverQueue): 642 skillPointsTrained = self.GetEstimatedSkillPointsTrained(skillTypeID) 643 totalSkillPoints = max(skillPointsTrained, totalSkillPoints) 644 return totalSkillPoints 645 646 def GetEstimatedSkillPointsTrained(self, skillTypeID): 647 startTime = self.GetStartTimeOfQueue() 648 currentTime = gametime.GetWallclockTime() 649 if startTime is None: 650 startTime = currentTime 651 trainingCalculator = self.skills.GetSkillTrainingTimeCalculator() 652 with trainingCalculator.specific_current_time_context(startTime): 653 skillPointsTrained = trainingCalculator.get_skill_points_trained_at_sample_time(skillTypeID, startTime, currentTime) 654 return skillPointsTrained 655 656 def GetEstimatedTotalSkillPoints(self): 657 cachedTotalSkillPoints = sm.GetService('skills').GetTotalSkillPointsForCharacter() 658 currentSkill = self.SkillInTraining() 659 if currentSkill: 660 currentSkillPointsForSkill = self.GetSkillPointsFromSkillObject(currentSkill.typeID, currentSkill) 661 cachedCurrentSkill = sm.GetService('skills').GetSkill(currentSkill.typeID) 662 if cachedCurrentSkill: 663 return cachedTotalSkillPoints - cachedCurrentSkill.trainedSkillPoints + currentSkillPointsForSkill 664 return cachedTotalSkillPoints 665 666 def OnServerSkillsChanged(self, skillInfos): 667 self.PrimeCache() 668 for skillTypeID, skillInfo in skillInfos.iteritems(): 669 skill = self.skills.GetSkill(skillTypeID) 670 if not skill and skillInfo.trainedSkillLevel > 0: 671 self.LogError('skillQueueSvc::OnServerSkillsChanged skill %s not found' % skillTypeID) 672 continue 673 self._RemoveSkillFromQueueCacheIfTrained(skillInfo, skillTypeID) 674 self._RemoveSkillFromCachedQueueIfTrained(skillInfos, skillTypeID) 675 676 def OnCloseApp(self): 677 if self.IsQueueChanged(): 678 self.CommitTransaction() 679 680 def _RemoveSkillFromQueueCacheIfTrained(self, skillInfo, skillTypeID): 681 skillLevel = skillInfo.trainedSkillLevel 682 if self.skillQueueCache and skillTypeID in self.skillQueueCache: 683 if skillLevel in self.skillQueueCache[skillTypeID]: 684 self.InternalRemoveFromQueue(skillTypeID, skillLevel) 685 686 def _RemoveSkillFromCachedQueueIfTrained(self, skillInfos, skillTypeID): 687 if self.cachedSkillQueue: 688 keepSkills = [] 689 for trainingSkill in self.cachedSkillQueue: 690 if trainingSkill.trainingTypeID == skillTypeID: 691 finishedSkill = skillInfos[skillTypeID] 692 if trainingSkill.trainingToLevel <= finishedSkill.trainedSkillLevel: 693 continue 694 keepSkills.append(trainingSkill) 695 696 self.cachedSkillQueue = keepSkills 697 698 def OnSkillQueuePausedServer(self, *args): 699 queue = self.skillQueue 700 if self.IsTransactionOpen(): 701 queue = self.cachedSkillQueue 702 if queue: 703 for skillEntry in queue: 704 skillEntry.trainingStartTime = skillEntry.trainingEndTime = None 705 706 sm.ScatterEvent('OnSkillQueuePaused') 707 PlaySound('skill_training_stop_play') 708 709 def OnNewSkillQueueSaved(self, newQueue): 710 if self.IsTransactionOpen(): 711 self.cachedSkillQueue = newQueue 712 self.skillQueue = newQueue 713 sm.ScatterEvent('OnSkillQueueChanged') 714 715 def TrainSkillNow(self, skillTypeID, toSkillLevel, *args): 716 inTraining = self.SkillInTraining() 717 if inTraining and prompt_player_fsd_dialog('ConfirmSkillTrainingNow', {'name': evetypes.GetName(inTraining.typeID), 718 'lvl': inTraining.trainedSkillLevel + 1}, uiconst.OKCANCEL) != uiconst.ID_OK: 719 return 720 self.BeginTransaction() 721 try: 722 if self.FindInQueue(skillTypeID, toSkillLevel) is not None: 723 self.MoveSkillToPosition(skillTypeID, toSkillLevel, 0) 724 message = ('SkillQueueStarted',) 725 else: 726 self.AddSkillToQueue(skillTypeID, toSkillLevel, 0) 727 skillName = localization.GetByLabel('UI/SkillQueue/Skills/SkillNameAndLevel', skill=skillTypeID, amount=toSkillLevel) 728 if inTraining: 729 message = ('AddedToQueue', {'skillname': skillName}) 730 else: 731 message = ('AddedToQueueAndStarted', {'skillname': skillName}) 732 self.CommitTransaction() 733 prompt_player_fsd_dialog(*message) 734 except Exception: 735 self.RollbackTransaction() 736 raise 737 738 def AddSkillToEnd(self, skillTypeID, current, nextLevel = None): 739 queueLength = self.GetNumberOfSkillsInQueue() 740 if queueLength >= SKILLQUEUE_MAX_NUM_SKILLS: 741 raise UserError('QueueTooManySkills', {'num': SKILLQUEUE_MAX_NUM_SKILLS}) 742 totalTime, _ = self.GetTrainingLengthOfQueue() 743 if totalTime > self.GetMaxSkillQueueLimitLength(): 744 raise UserError('QueueTooLong') 745 if nextLevel is None: 746 queue = self.GetServerQueue() 747 nextLevel = self.FindNextLevel(skillTypeID, current, queue) 748 if self.FindInQueue(skillTypeID, nextLevel) is not None: 749 raise UserError('QueueSkillAlreadyPresent') 750 self.BeginTransaction() 751 try: 752 self.AddSkillToQueue(skillTypeID, nextLevel) 753 self.CommitTransaction() 754 except Exception: 755 self.RollbackTransaction() 756 raise 757 758 text = localization.GetByLabel('UI/SkillQueue/Skills/SkillNameAndLevel', skill=skillTypeID, amount=nextLevel) 759 if self.SkillInTraining(): 760 prompt_player_fsd_dialog('AddedToQueue', {'skillname': text}) 761 else: 762 prompt_player_fsd_dialog('AddedToQueueAndStarted', {'skillname': text}) 763 sm.ScatterEvent('OnSkillQueueRefreshed') 764 765 def FindNextLevel(self, skillTypeID, current = None, skillQueue = None): 766 skill = self.skills.GetSkillIncludingLapsed(skillTypeID) 767 current = skill.trainedSkillLevel or 0 if skill else 0 768 if skillQueue is None: 769 skillQueue = self.GetServerQueue() 770 skillQueue = [ (skill.trainingTypeID, skill.trainingToLevel) for skill in skillQueue ] 771 nextLevel = None 772 for i in xrange(1, 7): 773 if i <= current: 774 continue 775 inQueue = bool((skillTypeID, i) in skillQueue) 776 if inQueue is False: 777 nextLevel = i 778 break 779 780 return nextLevel 781 782 def OnMultipleCharactersTrainingUpdated(self): 783 self.PrimeCache(True) 784 self._isAllCharacterTrainingSlotsUsed = None 785 sm.ScatterEvent('OnMultipleCharactersTrainingRefreshed') 786 787 def GetMultipleCharacterTraining(self, force = False): 788 userSvc = sm.RemoteSvc('userSvc') 789 if force: 790 userSvc.InvalidateMultiCharacterTrainingCache(session.userid) 791 return userSvc.GetMultiCharactersTrainingSlots() 792 793 def IsSkillInQueue(self, skillTypeID): 794 self.PrimeCache() 795 if skillTypeID in self.skillQueueCache: 796 return True 797 return False 798 799 def IsAllCharacterTrainingSlotsUsed(self): 800 if self._isAllCharacterTrainingSlotsUsed is None: 801 queues = self.GetMultipleCharacterTraining().items() 802 characterData = sm.GetService('cc').GetCharacterSelectionData() 803 activeQueues = 1 + len(queues) 804 usedQueues = 0 805 for characterDetails in characterData.details.values(): 806 isTraining = characterDetails.GetSkillInTrainingInfo()['currentSkill'] is not None 807 if characterDetails.charID != session.charid and isTraining: 808 usedQueues += 1 809 810 self._isAllCharacterTrainingSlotsUsed = usedQueues == activeQueues 811 return self._isAllCharacterTrainingSlotsUsed 812 813 def IsQueueWndOpen(self): 814 from eve.client.script.ui.shared.neocom.characterSheetWindow import CharacterSheetWindow 815 return CharacterSheetWindow.IsOpen() 816 817 def FindHighestLevelInQueue(self, skillTypeID): 818 if self.IsSkillInQueue(skillTypeID): 819 return max(self.skillQueueCache[skillTypeID]) 820 821 def RemoveHighestLevelFromQueue(self, skillTypeID): 822 if self.IsSkillInQueue(skillTypeID): 823 self.RemoveSkillFromQueue(skillTypeID, self.FindHighestLevelInQueue(skillTypeID)) 824 825 def GetAddMenuForSkillEntries(self, skillTypeID, skillInfo): 826 m = [] 827 if skillInfo is None: 828 return m 829 skillLevel = skillInfo.trainedSkillLevel 830 if skillLevel is not None: 831 if skillLevel < skillConst.skill_max_level: 832 queue = self.GetQueue() 833 nextLevel = self.FindNextLevel(skillTypeID, skillLevel, queue) 834 if not self.SkillInTraining(skillTypeID): 835 trainingTime, totalTime, _ = self.GetTrainingLengthOfSkill(skillTypeID, skillLevel + 1, 0) 836 if trainingTime <= 0: 837 takesText = localization.GetByLabel('UI/SkillQueue/Skills/CompletionImminent') 838 else: 839 takesText = localization.GetByLabel('UI/SkillQueue/Skills/SkillTimeLeft', timeLeft=long(trainingTime)) 840 if nextLevel < 6 and self.FindInQueue(skillTypeID, skillLevel + 1) is None: 841 trainText = MenuLabel('UI/SkillQueue/AddSkillMenu/AddToFrontOfQueueTime', {'takes': takesText}) 842 m.append((trainText, self.AddSkillToQueue, (skillTypeID, nextLevel, 0))) 843 if self.IsSkillInQueue(skillTypeID): 844 removeText = MenuLabel('UI/SkillQueue/AddSkillMenu/RemoveHighestLevelFromQueue', {'level': self.FindHighestLevelInQueue(skillTypeID)}) 845 m.append((removeText, self.RemoveHighestLevelFromQueue, (skillTypeID,))) 846 if nextLevel < 6: 847 label = MenuLabel('UI/SkillQueue/AddSkillMenu/AddToEndOfQueue', {'nextLevel': nextLevel}) 848 m.append((label, self.AddSkillToQueue, (skillInfo.typeID,))) 849 label = MenuLabel('UI/SkillQueue/AddSkillMenu/TrainToLevel5', {'nextLevel': nextLevel}) 850 m.append((label, self.AddAllLevels, (skillInfo.typeID, nextLevel))) 851 if sm.GetService('skills').GetFreeSkillPoints() > 0: 852 diff = sm.GetService('skills').SkillpointsNextLevel(skillTypeID) + 0.5 - skillInfo.trainedSkillPoints 853 m.append((MenuLabel('UI/SkillQueue/AddSkillMenu/ApplySkillPoints'), self.UseFreeSkillPoints, (skillInfo.typeID, diff))) 854 if m: 855 m.append(None) 856 return m 857 858 def AddAllLevels(self, typeID, nextLevel): 859 skills = [ (typeID, lvl) for lvl in xrange(nextLevel, 6) ] 860 self.AddSkillsToQueue(skills, ignoreAlreadyPresent=True) 861 862 def UseFreeSkillPoints(self, skillTypeID, diff): 863 inTraining = self.SkillInTraining() 864 if inTraining is not None and inTraining.typeID == skillTypeID: 865 prompt_player_fsd_dialog('CannotApplyFreePointsWhileTrainingSkill') 866 return 867 freeSkillPoints = sm.StartService('skills').GetFreeSkillPoints() 868 text = localization.GetByLabel('UI/SkillQueue/AddSkillMenu/UseSkillPointsWindow', skill=skillTypeID, skillPoints=int(diff)) 869 caption = localization.GetByLabel('UI/SkillQueue/AddSkillMenu/ApplySkillPoints') 870 ret = uix.QtyPopup(maxvalue=freeSkillPoints, caption=caption, label=text, setvalue=int(diff)) 871 if ret is None: 872 return 873 sp = int(ret.get('qty', '')) 874 sm.StartService('skills').ApplyFreeSkillPoints(skillTypeID, sp) 875 PlaySound('st_allocate_skillpoints_play') 876 877 def SkillInTraining(self, skillTypeID = None): 878 activeQueue = self.GetServerQueue() 879 if len(activeQueue) and activeQueue[0].trainingEndTime: 880 if skillTypeID is None or activeQueue[0].trainingTypeID == skillTypeID: 881 return self.skills.GetSkill(activeQueue[0].trainingTypeID) 882 883 def GetActiveSkillTypeID(self): 884 skill = self.SkillInTraining() 885 if skill: 886 return skill.typeID 887 888 def GetTimeForTraining(self, skillTypeID, toLevel, trainingStartTime = 0): 889 currentTraining = self.SkillInTraining(skillTypeID) 890 currentSkillPointsDict = {} 891 currentTime = gametime.GetWallclockTime() 892 if currentTraining: 893 trainingEndTime = self.GetEndOfTraining(skillTypeID) 894 timeForTraining = trainingEndTime - currentTime 895 else: 896 timeOffset = 0 897 if trainingStartTime: 898 timeOffset = trainingStartTime - currentTime 899 skill = self.skills.GetSkill(skillTypeID) 900 attributes = self.GetPlayerAttributeDict() 901 skillBoosters = self.GetSkillAcceleratorBoosters() 902 skillBoosters.apply_expired_attributes_at_time_offset(attributes, timeOffset) 903 currentSkillPointsDict[skillTypeID] = self.GetSkillPointsFromSkillObject(skillTypeID, skill) 904 _, timeForTraining = self.GetSkillPointsAndTimeNeededToTrain(skillTypeID, toLevel, currentSkillPointsDict[skillTypeID], trainingStartTime or currentTime) 905 return long(timeForTraining) 906 907 def GetEndOfTraining(self, skillTypeID): 908 skillQueue = self.GetServerQueue() 909 if not len(skillQueue) or skillQueue[0].trainingTypeID != skillTypeID: 910 return None 911 else: 912 return skillQueue[0].trainingEndTime 913 914 def GetStartTimeOfQueue(self): 915 skillQueue = self.GetServerQueue() 916 if not skillQueue: 917 return None 918 else: 919 return skillQueue[0].trainingStartTime 920 921 def IsMoveAllowed(self, draggedNode, checkedIdx): 922 queue = self.GetQueue() 923 if checkedIdx is None: 924 checkedIdx = len(queue) 925 guid = getattr(draggedNode, '__guid__', None) 926 skillSvc = sm.GetService('skills') 927 if getattr(draggedNode, 'skillID', None): 928 if guid == 'listentry.SkillEntry': 929 level = self.FindNextLevel(draggedNode.skillID, None, queue) 930 else: 931 level = draggedNode.Get('trainToLevel', 1) 932 if draggedNode.inQueue is None: 933 level += 1 934 return self.CheckCanInsertSkillAtPosition(draggedNode.skillID, level, checkedIdx, check=1, performLengthTest=False) 935 if guid in ('xtriui.InvItem', 'listentry.InvItem'): 936 category = GetAttrs(draggedNode, 'rec', 'categoryID') 937 if category != const.categorySkill: 938 return 939 typeID = GetAttrs(draggedNode, 'rec', 'typeID') 940 if typeID is None: 941 return 942 if skillSvc.IsSkillInjected(typeID): 943 return False 944 meetsReq = sm.StartService('godma').CheckSkillRequirementsForType(typeID) 945 if not meetsReq: 946 return False 947 return True 948 if guid == 'listentry.SkillTreeEntry': 949 typeID = draggedNode.typeID 950 if typeID is None: 951 return False 952 if not skillSvc.IsSkillInjected(typeID): 953 return False 954 skill = skillSvc.GetSkill(typeID) 955 level = self.FindNextLevel(typeID, skill.trainedSkillLevel, queue) 956 return self.CheckCanInsertSkillAtPosition(typeID, level, checkedIdx, check=1, performLengthTest=False) 957 if getattr(draggedNode, 'typeID', None): 958 if evetypes.GetCategoryID(draggedNode.typeID) == const.categorySkill: 959 level = self.FindNextLevel(draggedNode.typeID, None, queue) 960 return self.CheckCanInsertSkillAtPosition(draggedNode.typeID, level, checkedIdx, check=1, performLengthTest=False) 961 962 def IsSkillEntry(self, draggedNode): 963 if getattr(draggedNode, 'skillID', None): 964 return True 965 guid = getattr(draggedNode, '__guid__', None) 966 if guid in ('xtriui.InvItem', 'listentry.InvItem'): 967 category = GetAttrs(draggedNode, 'rec', 'categoryID') 968 if category == const.categorySkill: 969 return True 970 else: 971 if guid == 'listentry.SkillTreeEntry': 972 return True 973 if getattr(draggedNode, 'typeID', None): 974 if evetypes.GetCategoryID(draggedNode.typeID) == const.categorySkill: 975 return True 976 return False 977 978 def IsRemoveAllowed(self, typeID, level): 979 try: 980 self.CheckCanRemoveSkillFromQueue(typeID, level) 981 return True 982 except UserError: 983 return False 984 985 def GetSkillPlanImporter(self): 986 if self.skillplanImporter is None: 987 self.skillplanImporter = ImportSkillPlan(IsUsingDefaultLanguage(session)) 988 return self.skillplanImporter 989 990 @contextmanager 991 def ChangeQueueLock(self): 992 ReentrantLock(self, 'SkillQueueSvc:xActLock') 993 try: 994 yield 995 finally: 996 UnLock(self, 'SkillQueueSvc:xActLock') 997 998 def IsQueueChanged(self): 999 if self.cachedSkillQueue is None: 1000 return False 1001 if len(self.skillQueue) != len(self.cachedSkillQueue): 1002 return True 1003 for s1, s2 in zip(self.skillQueue, self.cachedSkillQueue): 1004 isSameType = s1.trainingTypeID == s2.trainingTypeID 1005 isSameLevel = s1.trainingToLevel == s2.trainingToLevel 1006 if not isSameType or not isSameLevel: 1007 return True 1008 1009 return False 1010 1011 def GetTimeSavedByUnallocatedPoints(self, unallocatedPoints): 1012 totalTimeSaved = 0 1013 allTrainingLengths = self.GetAllTrainingLengths() 1014 skillInTraining = self.SkillInTraining() 1015 actualEnd = self.GetEndOfTraining(skillInTraining.typeID) if skillInTraining else None 1016 for skill in self.GetQueue(): 1017 trainingID = (skill.trainingTypeID, skill.trainingToLevel) 1018 if trainingID not in allTrainingLengths: 1019 continue 1020 timeLeft = allTrainingLengths[trainingID][1] 1021 if skillInTraining and skill.trainingTypeID == skillInTraining.typeID and skill.trainingToLevel - 1 == skillInTraining.trainedSkillLevel: 1022 if actualEnd is not None: 1023 timeLeft = float(actualEnd - blue.os.GetWallclockTime()) 1024 if unallocatedPoints > 0: 1025 pointsTrained = skill.trainingDestinationSP - skill.trainingStartSP 1026 if unallocatedPoints >= pointsTrained: 1027 totalTimeSaved += timeLeft 1028 else: 1029 fractionCovered = min(1.0, unallocatedPoints / float(pointsTrained)) 1030 totalTimeSaved += fractionCovered * timeLeft 1031 unallocatedPoints -= pointsTrained 1032 1033 return totalTimeSaved