skillsvc.py (48619B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\skillsvc.py 2 import operator 3 from carbon.common.script.sys.service import Service 4 from collections import defaultdict, OrderedDict 5 from copy import deepcopy 6 import blue 7 import telemetry 8 import carbonui.const as uiconst 9 import characterskills 10 import clonegrade 11 import eve.common.script.util.notificationconst as notificationConst 12 import evetypes 13 import localization 14 import uthread 15 import utillib 16 from carbon.client.script.environment.AudioUtil import PlaySound 17 from characterskills.client import skill_signals 18 from characterskills.client.skill_training import ClientCharacterSkillInterface 19 from characterskills.common.character_skill_entry import CharacterSkillEntry 20 from characterskills.skill_accelerators import SkillAcceleratorBoosters 21 from characterskills.skill_training import SkillTrainingTimeCalculator 22 from characterskills.util import GetSkillPointsPerMinute, GetSPForLevelRaw, GetSkillLevelRaw, GetAllSkillGroups 23 from dogma.const import attributePrimaryAttribute, attributeSkillTimeConstant, attributeSecondaryAttribute 24 from dogma.effects import IsBoosterSkillAccelerator 25 from eve.common.script.sys import eveCfg 26 from omega.client.clone_grade_svc import get_clone_grade_svc 27 from player_messaging.client.ui_message import prompt_player_fsd_dialog 28 from shipprogression.shipUnlockSvc import GetShipUnlockService 29 from skills.client.skillController import SkillController 30 from eve.client.script.ui.skilltrading.skillExtractorWindow import SkillExtractorWindow 31 from eve.common.lib import appConst as const 32 from eveexceptions import UserError 33 from evetypes.skills import get_required_skills_index, get_dogma_required_skills 34 from inventorycommon import const as invconst 35 from notifications.common.formatters.skillPoints import UnusedSkillPointsFormatter 36 from notifications.common.notification import Notification, SimpleNotification 37 from eve.common.script.util import notificationconst 38 SKILLREQ_DONTHAVE = 1 39 SKILLREQ_HAVEBUTNOTTRAINED = 2 40 SKILLREQ_HAVEANDTRAINED = 3 41 SKILLREQ_HAVEANDTRAINEDFULLY = 4 42 SKILLREQ_TRIALRESTRICTED = 5 43 TEXTURE_PATH_BY_SKILLREQ = {SKILLREQ_DONTHAVE: 'res:/UI/Texture/Classes/Skills/doNotHaveFrame.png', 44 SKILLREQ_HAVEBUTNOTTRAINED: 'res:/UI/Texture/Classes/Skills/levelPartiallyTrainedFrame.png', 45 SKILLREQ_HAVEANDTRAINED: 'res:/UI/Texture/Classes/Skills/levelTrainedFrame.png', 46 SKILLREQ_HAVEANDTRAINEDFULLY: 'res:/UI/Texture/Classes/Skills/fullyTrainedFrame.png', 47 SKILLREQ_TRIALRESTRICTED: 'res:/UI/Texture/Classes/Skills/trialRestrictedFrame.png'} 48 SHIP_SKILLREQ_HINT = {SKILLREQ_DONTHAVE: 'UI/InfoWindow/ShipSkillReqDoNotHave', 49 SKILLREQ_HAVEBUTNOTTRAINED: 'UI/InfoWindow/ShipSkillReqPartiallyTrained', 50 SKILLREQ_HAVEANDTRAINED: 'UI/InfoWindow/ShipSkillReqTrained', 51 SKILLREQ_HAVEANDTRAINEDFULLY: 'UI/InfoWindow/ShipSkillReqFullyTrained', 52 SKILLREQ_TRIALRESTRICTED: 'UI/InfoWindow/ShipSkillReqRestrictedForTrial'} 53 SKILL_SKILLREQ_HINT = {SKILLREQ_DONTHAVE: 'UI/InfoWindow/SkillReqDoNotHave', 54 SKILLREQ_HAVEBUTNOTTRAINED: 'UI/InfoWindow/SkillReqPartiallyTrained', 55 SKILLREQ_HAVEANDTRAINED: 'UI/InfoWindow/SkillReqTrained', 56 SKILLREQ_HAVEANDTRAINEDFULLY: 'UI/InfoWindow/SkillReqFullyTrained', 57 SKILLREQ_TRIALRESTRICTED: 'UI/InfoWindow/SkillReqRestrictedForTrial'} 58 ITEM_SKILLREQ_HINT = {SKILLREQ_DONTHAVE: 'UI/InfoWindow/ItemSkillReqDoNotHave', 59 SKILLREQ_HAVEBUTNOTTRAINED: 'UI/InfoWindow/ItemSkillReqPartiallyTrained', 60 SKILLREQ_HAVEANDTRAINED: 'UI/InfoWindow/ItemSkillReqTrained', 61 SKILLREQ_HAVEANDTRAINEDFULLY: 'UI/InfoWindow/ItemSkillReqFullyTrained', 62 SKILLREQ_TRIALRESTRICTED: 'UI/InfoWindow/ItemSkillReqRestrictedForTrial'} 63 HIGHLIGHT_SKILLS_TRAINED = 2243 64 HIGHLIGHT_SP_ADDED = 2244 65 HIGHLIGHT_SKILLS_TRAINED_AND_SP_ADDED = 2259 66 67 class SkillsSvc(Service): 68 __guid__ = 'svc.skills' 69 __notifyevents__ = ['ProcessSessionChange', 70 'OnServerSkillsChanged', 71 'OnServerSkillsRemoved', 72 'OnSkillForcedRefresh', 73 'OnRespecInfoChanged', 74 'OnFreeSkillPointsChanged', 75 'OnServerBoostersChanged', 76 'OnServerImplantsChanged', 77 'OnCloneDestruction', 78 'OnLogonSkillsTrained', 79 'OnAlphaInjectionAvailable', 80 'OnSkillBundleInjected', 81 'OnSessionReset'] 82 __servicename__ = 'skills' 83 __displayname__ = 'Skill Client Service' 84 __dependencies__ = ['nonDiminishingInjection', 'settings'] 85 __startupdependencies__ = ['godma'] 86 87 def Run(self, memStream = None): 88 self.LogInfo('Starting Skills') 89 self.Reset() 90 GetShipUnlockService(self) 91 92 def Stop(self, memStream = None): 93 self.Reset() 94 95 def ProcessSessionChange(self, isremote, session, change): 96 if session.charid is None: 97 self.Reset() 98 99 def OnSessionReset(self): 100 self.Reset() 101 self.ResetSkillHistory() 102 103 def Reset(self): 104 self.allskills = None 105 self.skillGroups = None 106 self.respecInfo = None 107 self.depedentSkills = None 108 self.myskills = None 109 self.mySkillsIncludingLapsed = None 110 self.skillHistory = None 111 self.freeSkillPoints = None 112 self.skillHandler = None 113 self.boosters = None 114 self.implants = None 115 self.characterAttributes = None 116 self._skillAcceleratorBoosters = None 117 self.skillsByGroupIDTypeID = None 118 self._charTotalSkillPoints = None 119 120 def ResetSkillHistory(self): 121 self.skillHistory = None 122 123 def GetSkillHandler(self): 124 if not self.skillHandler: 125 self.skillHandler = session.ConnectToRemoteService('skillMgr2').GetMySkillHandler() 126 return self.skillHandler 127 128 def RefreshMySkills(self): 129 if self.myskills is not None: 130 self.LogError('skillSvc is force refreshing client side skill state!') 131 self.myskills = self.GetSkillHandler().GetSkills() 132 133 def RefreshMySkillsIncludingLapsed(self): 134 if self.mySkillsIncludingLapsed is not None: 135 self.LogError('skillSvc is force refreshing client side skill state!') 136 self.mySkillsIncludingLapsed = self.GetSkillHandler().GetAllSkills() 137 138 def OnLogonSkillsTrained(self, skillInfos, canTrain): 139 levelsTrained = [] 140 for skillTypeID, skillInfo in skillInfos.iteritems(): 141 for x in xrange(skillInfo.currentLevel + 1, skillInfo.skillLevel + 1): 142 levelsTrained.append((skillTypeID, x)) 143 144 if levelsTrained: 145 PlaySound('msg_SkillTrained_play') 146 self.TriggerSkillsTrainedNotification(levelsTrained) 147 if canTrain: 148 self._CheckTriggerNoSkillsInQueueNotification() 149 GetShipUnlockService().OnSkillsChanged(skillInfos) 150 151 def OnServerSkillsChanged(self, skillInfos, event, timeStamp): 152 levelsTrained = self._UpdateMySkillsAndReturnLevelsTrained(skillInfos, timeStamp) 153 self.ResetSkillHistory() 154 sm.GetService('skillqueue').OnServerSkillsChanged(skillInfos) 155 if levelsTrained: 156 PlaySound('msg_SkillTrained_play') 157 self.TriggerSkillsTrainedNotification(levelsTrained) 158 sm.ScatterEvent('OnSkillLevelsTrained', levelsTrained) 159 skill_signals.on_skill_levels_trained(levelsTrained) 160 if event: 161 sm.ScatterEvent(event, skillInfos) 162 sm.ScatterEvent('OnSkillsChanged', skillInfos) 163 164 def OnServerSkillsRemoved(self, skillInfos, timeStamp): 165 self._UpdateMySkillsAndReturnLevelsTrained(skillInfos, timeStamp) 166 self.ResetSkillHistory() 167 sm.GetService('skillqueue').OnServerSkillsChanged(skillInfos) 168 sm.ScatterEvent('OnSkillsChanged', skillInfos) 169 170 def _UpdateMySkillsAndReturnLevelsTrained(self, skillInfos, timeStamp): 171 levelsTrained = [] 172 self.GetSkills() 173 skills = self.GetSkillsIncludingLapsed() 174 for skillTypeID, skillInfo in skillInfos.iteritems(): 175 if skillInfo.trainedSkillPoints >= 0: 176 currentSkill = skills.get(skillTypeID, None) 177 if currentSkill and getattr(currentSkill, 'timeStamp', 0) > timeStamp: 178 continue 179 if not currentSkill or currentSkill.trainedSkillLevel != skillInfo.trainedSkillLevel: 180 if currentSkill and currentSkill.trainedSkillLevel: 181 startLevel = currentSkill.trainedSkillLevel + 1 182 else: 183 startLevel = 1 184 endLevel = skillInfo.trainedSkillLevel + 1 185 for x in xrange(startLevel, endLevel): 186 levelsTrained.append((skillTypeID, x)) 187 188 skillInfo.timeStamp = timeStamp 189 self.myskills[skillTypeID] = skillInfo 190 self.mySkillsIncludingLapsed[skillTypeID] = skillInfo 191 elif skillInfo.virtualSkillLevel is not None: 192 currentSkill = skills.get(skillTypeID, None) 193 if currentSkill and getattr(currentSkill, 'timeStamp', 0) > timeStamp: 194 continue 195 skillInfo = skillInfo.GetCopyWithNewSkillPoints(None) 196 skillInfo.timeStamp = timeStamp 197 self.myskills[skillTypeID] = skillInfo 198 self.mySkillsIncludingLapsed[skillTypeID] = skillInfo 199 else: 200 del self.myskills[skillTypeID] 201 del self.mySkillsIncludingLapsed[skillTypeID] 202 203 return levelsTrained 204 205 def GetSkills(self, renew = 0): 206 if self.myskills is None or renew: 207 self.RefreshMySkills() 208 return self.myskills 209 210 def GetCharacterAttributes(self, renew = False): 211 if self.characterAttributes is None or renew: 212 self.GetBoosters(True) 213 self.GetImplants(True) 214 self.characterAttributes = self.GetSkillHandler().GetAttributes() 215 for attributeID, attributeValue in self.characterAttributes.iteritems(): 216 self.godma.GetStateManager().ApplyAttributeChange(session.charid, session.charid, attributeID, blue.os.GetWallclockTime(), attributeValue, None, False) 217 218 return deepcopy(self.characterAttributes) 219 220 def GetCharacterAttribute(self, attributeID, includeBoosters = True): 221 if get_clone_grade_svc().is_omega_with_grace_period(): 222 attrValue = self.GetCharacterAttributes()[attributeID] 223 else: 224 attrValue = self.GetCharacterAttributes()[attributeID] * clonegrade.ALPHA_TRAINING_MULTIPLIER 225 if not includeBoosters: 226 boosters = self.GetSkillAcceleratorBoosters() 227 bonus = boosters.get_attribute_bonus(attributeID) 228 if not get_clone_grade_svc().is_omega_with_grace_period(): 229 bonus = bonus * clonegrade.ALPHA_TRAINING_MULTIPLIER 230 attrValue -= bonus 231 return attrValue 232 233 def GetSkill(self, skillTypeID, renew = 0): 234 if self.myskills is None or renew: 235 self.RefreshMySkills() 236 return self.myskills.get(skillTypeID, None) 237 238 def GetSkillIncludingLapsed(self, skillTypeID, renew = 0): 239 if self.mySkillsIncludingLapsed is None or renew: 240 self.RefreshMySkillsIncludingLapsed() 241 return self.mySkillsIncludingLapsed.get(skillTypeID, None) 242 243 def GetSkillsIncludingLapsed(self, renew = 0): 244 if self.mySkillsIncludingLapsed is None or renew: 245 self.RefreshMySkillsIncludingLapsed() 246 return self.mySkillsIncludingLapsed 247 248 @telemetry.ZONE_METHOD 249 def MySkillLevel(self, skillTypeID): 250 skill = self.GetSkill(skillTypeID) 251 if skill is not None: 252 return skill.trainedSkillLevel 253 return 0 254 255 def MySkillLevelIncludingQueued(self, skillID): 256 inQueue = self.GetMaxSkillLevelsInQueue() 257 skill = self.GetSkillIncludingLapsed(skillID) 258 if skill is None: 259 return 0 260 return max(skill.trainedSkillLevel, inQueue.get(skillID, 0)) 261 262 def MySkillPoints(self, skillTypeID): 263 skill = self.GetSkill(skillTypeID) 264 if skill is not None: 265 return skill.trainedSkillPoints 266 267 def MyEffectiveSkillLevelsByID(self, renew = 0): 268 skills = {} 269 for skillTypeID, skill in self.GetSkills(renew).iteritems(): 270 skills[skillTypeID] = skill.effectiveSkillLevel 271 272 return skills 273 274 def SkillPointsCurrentLevel(self, skillTypeID): 275 skill = self.GetSkillIncludingLapsed(skillTypeID) 276 return GetSPForLevelRaw(skill.skillRank, skill.trainedSkillLevel) 277 278 def SkillpointsNextLevel(self, skillTypeID): 279 skill = self.GetSkillIncludingLapsed(skillTypeID) 280 if not skill or skill.trainedSkillLevel >= characterskills.MAX_SKILL_LEVEL: 281 return None 282 return GetSPForLevelRaw(skill.skillRank, skill.trainedSkillLevel + 1) 283 284 def HasSkill(self, skillTypeID): 285 return skillTypeID in self.GetSkillsIncludingLapsed() 286 287 def IsSkillInjected(self, skillTypeID): 288 skill = self.GetSkillIncludingLapsed(skillTypeID) 289 return skill and skill.trainedSkillLevel is not None 290 291 @telemetry.ZONE_METHOD 292 def GetAllSkills(self): 293 if not self.allskills: 294 self.allskills = {} 295 for typeID in evetypes.GetTypeIDsByCategory(const.categorySkill): 296 if evetypes.IsPublished(typeID): 297 self.allskills[typeID] = CharacterSkillEntry(typeID, 0, 0, self.GetSkillRank(typeID), None) 298 299 return self.allskills 300 301 def GetEffectiveLevel(self, typeID): 302 skills = self.GetSkills() 303 if typeID in skills: 304 return skills[typeID].effectiveSkillLevel 305 306 def GetMyLevel(self, typeID): 307 skills = self.GetSkills() 308 if typeID in skills: 309 return skills[typeID].trainedSkillLevel 310 311 def GetMyLevelIncludingLapsed(self, typeID): 312 skills = self.GetSkillsIncludingLapsed() 313 if typeID in skills: 314 return skills[typeID].trainedSkillLevel 315 316 def GetMySkillPoints(self, typeID): 317 skills = self.GetSkills() 318 if typeID in skills: 319 return skills[typeID].trainedSkillPoints 320 else: 321 return 0 322 323 def GetMySkillPointsIncludingLapsed(self, typeID): 324 skills = self.GetSkillsIncludingLapsed() 325 if typeID in skills: 326 return skills[typeID].trainedSkillPoints 327 else: 328 return 0 329 330 @telemetry.ZONE_METHOD 331 def GetAllSkillGroups(self): 332 if not self.skillGroups: 333 skillGroupIDs = GetAllSkillGroups() 334 skillgroups = [ utillib.KeyVal(groupID=groupID, groupName=evetypes.GetGroupNameByGroup(groupID)) for groupID in skillGroupIDs ] 335 skillgroups = localization.util.Sort(skillgroups, key=operator.attrgetter('groupName')) 336 self.skillGroups = skillgroups 337 return self.skillGroups 338 339 @telemetry.ZONE_METHOD 340 def GetSkillHistory(self, maxresults = 50): 341 if self.skillHistory is None: 342 self.skillHistory = self.GetSkillHandler().GetSkillHistory(maxresults) 343 return self.skillHistory 344 345 def GetDependentSkills(self, typeID): 346 if self.depedentSkills is None: 347 self.depedentSkills = defaultdict(dict) 348 for skillTypeID in self.GetAllSkills(): 349 requirements = self.GetRequiredSkills(skillTypeID) 350 for dependentTypeID, level in requirements.iteritems(): 351 self.depedentSkills[dependentTypeID][skillTypeID] = level 352 353 return self.depedentSkills[typeID] 354 355 @telemetry.ZONE_METHOD 356 def GetRecentlyTrainedSkills(self): 357 skillChanges = {} 358 skillData = self.GetSkillHandler().GetSkillChangesForISIS() 359 for typeID, pointChange in skillData: 360 currentSkillPoints = self.MySkillPoints(typeID) or 0 361 timeConstant = self.godma.GetTypeAttribute2(typeID, const.attributeSkillTimeConstant) 362 pointsBefore = currentSkillPoints - pointChange 363 oldLevel = GetSkillLevelRaw(pointsBefore, timeConstant) 364 if self.MySkillLevel(typeID) > oldLevel: 365 skillChanges[typeID] = oldLevel 366 367 return skillChanges 368 369 @telemetry.ZONE_METHOD 370 def GetSkillGroups(self, advanced = False): 371 if session.charid: 372 ownSkills = self.GetSkills() 373 skillQueue = sm.GetService('skillqueue').GetServerQueue() 374 skillsInQueue = [ skill.trainingTypeID for skill in skillQueue ] 375 else: 376 ownSkills = [] 377 skillsInQueue = [] 378 ownSkillTypeIDs = [] 379 ownSkillsByGroupID = defaultdict(list) 380 ownSkillsInTrainingByGroupID = defaultdict(list) 381 ownSkillsInQueueByGroupID = defaultdict(list) 382 ownSkillPointsByGroupID = defaultdict(int) 383 for skillTypeID, skill in ownSkills.iteritems(): 384 groupID = evetypes.GetGroupID(skillTypeID) 385 ownSkillsByGroupID[groupID].append(skill) 386 if sm.GetService('skillqueue').SkillInTraining(skillTypeID): 387 ownSkillsInTrainingByGroupID[groupID].append(skill) 388 if skillTypeID in skillsInQueue: 389 ownSkillsInQueueByGroupID[groupID].append(skillTypeID) 390 ownSkillPointsByGroupID[groupID] += skill.skillPoints 391 ownSkillTypeIDs.append(skillTypeID) 392 393 missingSkillsByGroupID = defaultdict(list) 394 if advanced: 395 allSkills = self.GetAllSkills() 396 for skillTypeID, skill in allSkills.iteritems(): 397 if skillTypeID not in ownSkillTypeIDs: 398 groupID = evetypes.GetGroupID(skillTypeID) 399 missingSkillsByGroupID[groupID].append(skill) 400 401 skillsByGroup = [] 402 skillgroups = self.GetAllSkillGroups() 403 for invGroup in skillgroups: 404 mySkillsInGroup = ownSkillsByGroupID[invGroup.groupID] 405 skillsIDontHave = missingSkillsByGroupID[invGroup.groupID] 406 mySkillsInTraining = ownSkillsInTrainingByGroupID[invGroup.groupID] 407 mySkillsInQueue = ownSkillsInQueueByGroupID[invGroup.groupID] 408 skillPointsInGroup = ownSkillPointsByGroupID[invGroup.groupID] 409 skillsByGroup.append([invGroup, 410 mySkillsInGroup, 411 skillsIDontHave, 412 mySkillsInTraining, 413 mySkillsInQueue, 414 skillPointsInGroup]) 415 416 return skillsByGroup 417 418 def IsSkillRequirementMet(self, typeID): 419 required = self.GetRequiredSkills(typeID) 420 for skillTypeID, lvl in required.iteritems(): 421 if self.MySkillLevel(skillTypeID) < lvl: 422 return False 423 424 return True 425 426 def IsSkillRequirementMetIncludingSkillQueue(self, typeID): 427 required = self.GetRequiredSkills(typeID) 428 inQueue = self.GetMaxSkillLevelsInQueue() 429 for skillTypeID, lvl in required.iteritems(): 430 if self.MySkillLevel(skillTypeID) < lvl: 431 if skillTypeID not in inQueue or inQueue[skillTypeID] < lvl: 432 return False 433 434 return True 435 436 @telemetry.ZONE_METHOD 437 def GetMaxSkillLevelsInQueue(self, maxQueueIdx = None): 438 skillQueue = sm.GetService('skillqueue').GetQueue() 439 if maxQueueIdx is not None: 440 skillQueue = skillQueue[:maxQueueIdx + 1] 441 inQueue = {} 442 for skill in skillQueue: 443 inQueue[skill.trainingTypeID] = skill.trainingToLevel 444 445 return inQueue 446 447 def GetSkillInQueueIndex(self, skillID, level): 448 for i, skill in enumerate(sm.GetService('skillqueue').GetQueue()): 449 if skill.trainingTypeID == skillID and skill.trainingToLevel == level: 450 return i 451 452 @telemetry.ZONE_METHOD 453 def GetRequiredSkills(self, typeID): 454 return get_dogma_required_skills(typeID) 455 456 def GetRequiredSkillsLevel(self, skills): 457 if not skills: 458 return SKILLREQ_HAVEANDTRAINED 459 allLevel5 = True 460 haveAll = True 461 missingSkill = False 462 for skillTypeID, level in skills: 463 mySkill = self.GetSkill(skillTypeID) 464 if mySkill is None: 465 missingSkill = True 466 continue 467 if mySkill.trainedSkillLevel < level: 468 haveAll = False 469 if mySkill.trainedSkillLevel != 5: 470 allLevel5 = False 471 472 if missingSkill: 473 return SKILLREQ_DONTHAVE 474 elif not haveAll: 475 return SKILLREQ_HAVEBUTNOTTRAINED 476 elif allLevel5: 477 return SKILLREQ_HAVEANDTRAINEDFULLY 478 else: 479 return SKILLREQ_HAVEANDTRAINED 480 481 def IsUnlockedWithExpertSystem(self, typeID): 482 skills = self.GetRequiredSkills(typeID) 483 isUnlockedWithExpertSystem = False 484 for skillTypeID, requiredLevel in skills.iteritems(): 485 mySkill = self.GetSkill(skillTypeID) 486 if mySkill: 487 trainedSkillLevel = mySkill.trainedSkillLevel or 0 488 else: 489 return False 490 if trainedSkillLevel < requiredLevel and mySkill.effectiveSkillLevel >= requiredLevel: 491 isUnlockedWithExpertSystem = True 492 elif trainedSkillLevel < requiredLevel: 493 return False 494 495 return isUnlockedWithExpertSystem 496 497 def GetRequiredSkillsLevelTexturePathAndHint(self, skills, typeID = None): 498 skillLevel = self.GetRequiredSkillsLevel(skills) 499 texturePath = TEXTURE_PATH_BY_SKILLREQ[skillLevel] 500 if typeID is None: 501 hint = ITEM_SKILLREQ_HINT[skillLevel] 502 else: 503 categoryID = evetypes.GetCategoryID(typeID) 504 if categoryID == invconst.categoryShip: 505 hint = SHIP_SKILLREQ_HINT[skillLevel] 506 elif categoryID == invconst.categorySkill: 507 hint = SKILL_SKILLREQ_HINT[skillLevel] 508 else: 509 hint = ITEM_SKILLREQ_HINT[skillLevel] 510 return (texturePath, localization.GetByLabel(hint)) 511 512 def GetRequiredSkillsRecursive(self, typeID): 513 ret = {} 514 self._GetAllSkillsRequiredToUseTypeRecursive(typeID, ret) 515 return ret 516 517 def _GetAllSkillsRequiredToUseTypeRecursive(self, typeID, ret): 518 for skillTypeID, lvl in self.GetRequiredSkills(typeID).iteritems(): 519 ret[skillTypeID] = max(ret.get(skillTypeID, 0), lvl) 520 if skillTypeID != typeID: 521 self._GetAllSkillsRequiredToUseTypeRecursive(skillTypeID, ret) 522 523 def GetTopLevelSkillsMissingToUseItem(self, typeID): 524 requiredSkills = self.GetRequiredSkills(typeID) 525 ret = [] 526 for typeID, level in requiredSkills.iteritems(): 527 if self.GetMyLevel(typeID) < level: 528 ret.append((typeID, level)) 529 530 return ret 531 532 def GetTotalMissingSkillLevelsToUseItem(self, typeID): 533 requiredSkills = self.GetRequiredSkillsRecursive(typeID) 534 missingLevels = 0 535 for typeID, level in requiredSkills.iteritems(): 536 myLevel = self.GetMyLevel(typeID) 537 if myLevel is None: 538 myLevel = 0 539 if myLevel < level: 540 missingLevels += level - myLevel 541 542 return missingLevels 543 544 def GetMissingSkillBooksFromList(self, skills): 545 missingSkills = [] 546 for skillID, level in skills: 547 skill = self.GetSkill(skillID) 548 if not skill or skill.trainedSkillPoints is None: 549 missingSkills.append(skillID) 550 551 return missingSkills 552 553 def GetSkillsMissingToUseItemRecursiveList(self, typeID): 554 missingDict = self.GetSkillsMissingToUseItemRecursive(typeID) 555 return [ (typeID, level) for typeID, level in missingDict.iteritems() ] 556 557 def GetSkillsMissingToUseItemRecursive(self, typeID): 558 ret = OrderedDict() 559 self._GetMissingSkillsRequiredToUseTypeRecursive(typeID, ret) 560 return ret 561 562 def _GetMissingSkillsRequiredToUseTypeRecursive(self, typeID, ret): 563 for skillTypeID, lvl in self.GetRequiredSkills(typeID).iteritems(): 564 if self.GetMyLevel(skillTypeID) < lvl: 565 if skillTypeID != typeID: 566 mySkill = self.GetSkillIncludingLapsed(skillTypeID) 567 if not mySkill or mySkill.trainedSkillPoints <= 0: 568 self._GetMissingSkillsRequiredToUseTypeRecursive(skillTypeID, ret) 569 ret[skillTypeID] = max(ret.get(skillTypeID, 0), lvl) 570 571 def GetSkillBooksMissingFromList(self, skills): 572 return [ typeID for typeID in skills.iterkeys() if self.GetSkillIncludingLapsed(typeID) is None ] 573 574 def GetSkillsMissingToUseAllSkillsFromListRecursiveAsList(self, skills): 575 missingDict = self.GetSkillsMissingToUseAllSkillsFromListRecursive(skills) 576 return [ (typeID, level) for typeID, level in missingDict.iteritems() ] 577 578 def GetSkillsMissingToUseAllSkillsFromListRecursive(self, skills): 579 ret = OrderedDict() 580 self._GetSkillsMissingToUseAllSkillsFromListRecursive(skills, ret) 581 return ret 582 583 def _GetSkillsMissingToUseAllSkillsFromListRecursive(self, skills, ret): 584 for skillTypeID, lvl in skills: 585 if self.GetMyLevel(skillTypeID) < lvl: 586 if skillTypeID not in skills: 587 mySkill = self.GetSkillIncludingLapsed(skillTypeID) 588 if not mySkill or mySkill.trainedSkillPoints <= 0: 589 self._GetMissingSkillsRequiredToUseTypeRecursive(skillTypeID, ret) 590 ret[skillTypeID] = max(ret.get(skillTypeID, 0), lvl) 591 592 def GetSkillTrainingTimeLeftToUseType(self, skillTypeID, includeBoosters = True): 593 if self.IsSkillRequirementMet(skillTypeID): 594 return 0 595 required = self.GetRequiredSkillsRecursive(skillTypeID) 596 totalTime = self.GetSkillTrainingTimeLeftForTypesAndLevels(required, includeBoosters) 597 return long(totalTime) 598 599 def GetSkillTrainingTimeLeftForTypesAndLevels(self, required, includeBoosters): 600 totalTime = 0 601 skillsIncludingLapsed = self.GetSkillsIncludingLapsed() 602 requiredMax = {} 603 for typeID, lvl in required.iteritems(): 604 haveSkill = skillsIncludingLapsed.get(typeID, None) 605 if haveSkill and haveSkill.trainedSkillLevel >= lvl: 606 continue 607 elif typeID not in requiredMax: 608 requiredMax[typeID] = int(lvl) 609 610 for typeID, trainToLevel in requiredMax.iteritems(): 611 trainedSkillLevel = 0 612 skillEntry = skillsIncludingLapsed.get(typeID, None) 613 if skillEntry and skillEntry.trainedSkillLevel is not None: 614 trainedSkillLevel = skillEntry.trainedSkillLevel 615 for trainingLevel in xrange(trainedSkillLevel + 1, trainToLevel + 1): 616 totalTime += self.GetRawTrainingTimeForSkillLevel(typeID, trainingLevel, includeBoosters=includeBoosters) 617 618 return totalTime 619 620 def GetSkillPointsRequiredForSkills(self, skillTypeIDsAndLevels): 621 622 def _get_skillpoint_cost_for_level(skill_rank, skill_level): 623 if skill_level <= 0: 624 return 0 625 return GetSPForLevelRaw(skill_rank, skill_level) - GetSPForLevelRaw(skill_rank, skill_level - 1) 626 627 pointsBySkillTypeID = OrderedDict() 628 for skillTypeID, level in skillTypeIDsAndLevels: 629 if self.GetMyLevel(skillTypeID) >= level: 630 continue 631 rank = self.GetSkillRank(skillTypeID) 632 pointsPresent = 0 633 existingSkill = self.GetSkill(skillTypeID) 634 if existingSkill is not None and existingSkill.trainedSkillLevel == level - 1: 635 pointsPresent = existingSkill.trainedSkillPoints - GetSPForLevelRaw(rank, existingSkill.trainedSkillLevel) 636 remainingPointsToLevel = _get_skillpoint_cost_for_level(rank, level) - pointsPresent 637 pointsBySkillTypeID[skillTypeID] = remainingPointsToLevel + pointsBySkillTypeID.get(skillTypeID, 0) 638 639 return pointsBySkillTypeID 640 641 def GetTrainingTimeLeftForSkillLevels(self, skillsLevels, includeBoosters = True): 642 requiredSkills = {} 643 for skillTypeID, level in skillsLevels.iteritems(): 644 if requiredSkills.get(skillTypeID, 0) < level: 645 requiredSkills[skillTypeID] = level 646 newRequirements = self.GetRequiredSkillsRecursive(skillTypeID) 647 for newSkillID, newLevel in newRequirements.iteritems(): 648 if requiredSkills.get(newSkillID, 0) < newLevel: 649 requiredSkills[newSkillID] = newLevel 650 651 totalTime = self.GetSkillTrainingTimeLeftForTypesAndLevels(requiredSkills, includeBoosters) 652 return totalTime 653 654 def GetSkillToolTip(self, skillTypeID, level): 655 if session.charid is None: 656 return 657 mySkill = self.GetSkill(skillTypeID) 658 mySkillLevel = 0 659 if mySkill is not None: 660 mySkillLevel = mySkill.trainedSkillLevel or 0 661 tooltipText = evetypes.GetDescription(skillTypeID) 662 tooltipTextList = [] 663 for i in xrange(int(mySkillLevel) + 1, int(level) + 1): 664 timeLeft = self.GetRawTrainingTimeForSkillLevel(skillTypeID, i) 665 tooltipTextList.append(localization.GetByLabel('UI/SkillQueue/Skills/SkillLevelAndTrainingTime', skillLevel=i, timeLeft=long(timeLeft))) 666 667 levelsText = '<br>'.join(tooltipTextList) 668 if levelsText: 669 tooltipText += '<br><br>' + levelsText 670 return tooltipText 671 672 def GetSkillpointsPerMinute(self, skillTypeID, includeBoosters = True): 673 primaryAttributeID = self.GetPrimarySkillAttribute(skillTypeID) 674 secondaryAttributeID = self.GetSecondarySkillAttribute(skillTypeID) 675 playerPrimaryAttribute = self.GetCharacterAttribute(primaryAttributeID, includeBoosters=includeBoosters) 676 playerSecondaryAttribute = self.GetCharacterAttribute(secondaryAttributeID, includeBoosters=includeBoosters) 677 return GetSkillPointsPerMinute(playerPrimaryAttribute, playerSecondaryAttribute) 678 679 def GetRawTrainingTimeForSkillLevel(self, skillTypeID, skillLevel, includeBoosters = True): 680 skillTimeConstant = self.GetSkillRank(skillTypeID) 681 rawSkillPointsToTrain = GetSPForLevelRaw(skillTimeConstant, skillLevel) 682 trainingRate = self.GetSkillpointsPerMinute(skillTypeID, includeBoosters=includeBoosters) 683 existingSP = 0 684 priorLevel = skillLevel - 1 685 skillInfo = self.GetSkills().get(skillTypeID, None) 686 if skillInfo: 687 existingSP = GetSPForLevelRaw(skillTimeConstant, priorLevel) 688 if priorLevel >= 0 and priorLevel == skillInfo.trainedSkillLevel: 689 existingSP = sm.GetService('skillqueue').GetSkillPointsFromSkillObject(skillTypeID, skillInfo) 690 if existingSP > rawSkillPointsToTrain: 691 return 0 692 skillPointsToTrain = rawSkillPointsToTrain - existingSP 693 trainingTimeInMinutes = float(skillPointsToTrain) / float(trainingRate) 694 return trainingTimeInMinutes * const.MIN 695 696 @telemetry.ZONE_METHOD 697 def GetSkillCount(self): 698 return len(self.GetSkills()) 699 700 @telemetry.ZONE_METHOD 701 def GetSkillPoints(self, groupID = None): 702 return sum([ skillInfo.trainedSkillPoints or 0 for skillTypeID, skillInfo in self.GetSkills().iteritems() if groupID is None or evetypes.GetGroupID(skillTypeID) == groupID ]) 703 704 def GetTotalSkillPointsForCharacter(self): 705 trainedSkillPoints = sum([ skillInfo.trainedSkillPoints or 0 for skillTypeID, skillInfo in self.GetSkillsIncludingLapsed().iteritems() ]) 706 unallocatedSkillPoints = self.GetFreeSkillPoints() 707 return trainedSkillPoints + unallocatedSkillPoints 708 709 def GetTotalSkillPointsForCharacterCached(self): 710 if not self._charTotalSkillPoints: 711 self._charTotalSkillPoints = self.GetTotalSkillPointsForCharacter() 712 return self._charTotalSkillPoints 713 714 def GetSkillRank(self, skillTypeID): 715 return self.godma.GetTypeAttribute(skillTypeID, attributeSkillTimeConstant) 716 717 def GetPrimarySkillAttribute(self, skillTypeID): 718 return self.godma.GetTypeAttribute(skillTypeID, attributePrimaryAttribute) 719 720 def GetSecondarySkillAttribute(self, skillTypeID): 721 return self.godma.GetTypeAttribute(skillTypeID, attributeSecondaryAttribute) 722 723 def GetSkillAcceleratorBoosters(self): 724 if self._skillAcceleratorBoosters is None: 725 self._skillAcceleratorBoosters = self._GetSkillAcceleratorBoosters() 726 return self._skillAcceleratorBoosters 727 728 def _GetSkillAcceleratorBoosters(self): 729 myGodmaItem = self.godma.GetItem(session.charid) 730 skillBoosters = SkillAcceleratorBoosters(self.godma.GetTypeAttribute2) 731 dogmaStaticMgr = sm.GetService('clientDogmaStaticSvc') 732 for booster in myGodmaItem.boosters: 733 if IsBoosterSkillAccelerator(dogmaStaticMgr, booster): 734 skillBoosters.add_booster(booster.expiryTime, booster.boosterTypeID) 735 736 return skillBoosters 737 738 def Train(self, skillX): 739 skill = sm.GetService('skillqueue').SkillInTraining() 740 if skill and prompt_player_fsd_dialog('ConfirmResetSkillTraining', {'name': evetypes.GetName(skill.typeID), 741 'lvl': skill.skillLevel + 1}, uiconst.OKCANCEL) != uiconst.ID_OK: 742 return 743 self.GetSkillHandler().CharStartTrainingSkill(skillX.itemID, skillX.locationID) 744 745 def InjectSkillIntoBrain(self, skillX): 746 skillIDList = [ skill.itemID for skill in skillX ] 747 if not skillIDList: 748 return 749 for skill in skillX: 750 if not evetypes.IsPublished(skill.typeID): 751 raise UserError('ItemNotASkill', {'skillName': skill.typeID}) 752 753 try: 754 self.godma.GetDogmaLM().InjectSkillIntoBrain(skillIDList) 755 except UserError as e: 756 raise 757 else: 758 for skill in skillX: 759 sm.GetService('notificationSvc').MakeAndScatterNotification(notificationConst.notificationTypeSkillInjected, data={'typeID': skill.typeID}) 760 761 PlaySound(uiconst.SOUND_ADD_OR_USE) 762 763 def AbortTrain(self): 764 self.GetSkillHandler().AbortTraining() 765 766 @telemetry.ZONE_METHOD 767 def GetRespecInfo(self): 768 if self.respecInfo is None: 769 self.respecInfo = self.GetSkillHandler().GetRespecInfo() 770 return self.respecInfo 771 772 def OnRespecInfoChanged(self, *args): 773 self.respecInfo = None 774 self.GetCharacterAttributes(True) 775 sm.ScatterEvent('OnRespecInfoUpdated') 776 777 def OnOpenCharacterSheet(self, skillIDs, *args): 778 sm.GetService('charactersheet').ForceShowSkillHistoryHighlighting(skillIDs) 779 780 def MakeSkillQueueEmptyNotification(self, skillQueueNotification): 781 queueText = localization.GetByLabel('UI/SkillQueue/NoSkillsInQueue') 782 skillQueueNotification = Notification.MakeSkillNotification(header=queueText, text='', created=blue.os.GetWallclockTime(), callBack=sm.StartService('skills').OnOpenCharacterSheet, callbackargs=None, notificationType=Notification.SKILL_NOTIFICATION_EMPTYQUEUE) 783 return skillQueueNotification 784 785 def TriggerSkillsTrainedNotification(self, levelsTrained): 786 if len(levelsTrained) > 1: 787 text = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/SkillTabs/SkillTrainingComplete') 788 subtext = localization.GetByLabel('UI/SkillQueue/Skills/NumberOfSkills', amount=len(levelsTrained)) 789 self._TriggerSkillNotification(levelsTrained, text, subtext) 790 else: 791 skillTypeID, level = levelsTrained[0] 792 text = localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/SkillTabs/SkillTrainingComplete') 793 subtext = localization.GetByLabel('UI/SkillQueue/Skills/SkillNameAndLevel', skill=skillTypeID, amount=level) 794 self._TriggerSkillNotification(levelsTrained, text, subtext) 795 796 def _CheckTriggerNoSkillsInQueueNotification(self): 797 queue = sm.GetService('skillqueue').GetServerQueue() 798 if not len(queue): 799 emptyQueueNotification = self.MakeSkillQueueEmptyNotification(None) 800 sm.ScatterEvent('OnNewNotificationReceived', emptyQueueNotification) 801 802 def _TriggerSkillNotification(self, skillChanges, text, subtext): 803 header = '%s - %s' % (text, subtext) 804 skillNotification = Notification.MakeSkillNotification(header=header, text='', created=blue.os.GetWallclockTime(), callBack=sm.StartService('skills').OnOpenCharacterSheet, callbackargs=skillChanges) 805 sm.ScatterEvent('OnNewNotificationReceived', skillNotification) 806 807 def OnFreeSkillPointsChanged(self, newFreeSkillPoints): 808 self.SetFreeSkillPoints(newFreeSkillPoints) 809 810 @telemetry.ZONE_METHOD 811 def GetFreeSkillPoints(self): 812 if self.freeSkillPoints is None: 813 self.freeSkillPoints = self.GetSkillHandler().GetFreeSkillPoints() 814 return self.freeSkillPoints 815 816 def ApplyFreeSkillPointsToSkills(self, skillTypeIDsAndLevels): 817 if self.freeSkillPoints is None: 818 self.GetFreeSkillPoints() 819 for skillTypeID, level in skillTypeIDsAndLevels: 820 if self.IsSkillInjected(skillTypeID) == False: 821 raise UserError('CannotApplyFreePointsDoNotHaveSkill', {'skillName': evetypes.GetName(skillTypeID)}) 822 skill = self.GetSkill(skillTypeID) 823 if skill is None: 824 raise UserError('CannotApplyFreePointsDoNotHaveSkill', {'skillName': evetypes.GetName(skillTypeID)}) 825 826 def ApplyFreeSkillPoints(self, skillTypeID, pointsToApply): 827 if self.freeSkillPoints is None: 828 self.GetFreeSkillPoints() 829 inTraining = sm.GetService('skillqueue').SkillInTraining() 830 if inTraining is not None and inTraining.typeID == skillTypeID: 831 raise UserError('CannotApplyFreePointsWhileTrainingSkill') 832 if self.IsSkillInjected(skillTypeID) == False: 833 raise UserError('CannotApplyFreePointsDoNotHaveSkill', {'skillName': evetypes.GetName(skillTypeID)}) 834 skill = self.GetSkill(skillTypeID) 835 if skill is None: 836 raise UserError('CannotApplyFreePointsDoNotHaveSkill', {'skillName': evetypes.GetName(skillTypeID)}) 837 spAtMaxLevel = GetSPForLevelRaw(skill.skillRank, 5) 838 if skill.trainedSkillPoints + pointsToApply > spAtMaxLevel: 839 pointsToApply = spAtMaxLevel - skill.trainedSkillPoints 840 if pointsToApply > self.freeSkillPoints: 841 raise UserError('CannotApplyFreePointsNotEnoughRemaining', {'pointsRequested': pointsToApply, 842 'pointsRemaining': self.freeSkillPoints}) 843 if pointsToApply <= 0: 844 return 845 newFreePoints = self.GetSkillHandler().ApplyFreeSkillPoints(skill.typeID, pointsToApply) 846 self.SetFreeSkillPoints(newFreePoints) 847 848 def SetFreeSkillPoints(self, newFreePoints, showPointer = True): 849 isFreeSkillPointsChange = self.freeSkillPoints is None or newFreePoints != self.freeSkillPoints 850 if not isFreeSkillPointsChange: 851 return 852 isFreeSkillPointsIncrease = self.freeSkillPoints is None or newFreePoints > self.freeSkillPoints 853 if isFreeSkillPointsIncrease: 854 uthread.new(self.ShowSkillPointsNotification_thread) 855 self.freeSkillPoints = newFreePoints 856 sm.ScatterEvent('OnFreeSkillPointsChanged_Local') 857 if isFreeSkillPointsIncrease and showPointer: 858 sm.GetService('uiHighlightingService').highlight_ui_element(HIGHLIGHT_SP_ADDED) 859 860 def MakeAndScatterSkillPointNotification(self): 861 notificationData = UnusedSkillPointsFormatter.MakeData() 862 sm.GetService('notificationSvc').MakeAndScatterNotification(type=notificationConst.notificationTypeUnusedSkillPoints, data=notificationData) 863 864 def ShowSkillPointsNotification(self, number = (0, 0), time = 5000, *args): 865 skillPointsNow = self.GetFreeSkillPoints() 866 skillPointsLast = settings.user.ui.Get('freeSkillPoints', -1) 867 if skillPointsLast == skillPointsNow: 868 return 869 if skillPointsNow <= 0: 870 return 871 self.MakeAndScatterSkillPointNotification() 872 settings.user.ui.Set('freeSkillPoints', skillPointsNow) 873 874 def ShowSkillPointsNotification_thread(self): 875 blue.pyos.synchro.SleepWallclock(5000) 876 if session.charid: 877 self.ShowSkillPointsNotification() 878 879 def OnSkillForcedRefresh(self): 880 uthread.Pool('skillSvc::OnSkillForcedRefresh', self.ForceRefresh) 881 882 def ForceRefresh(self): 883 self.Reset() 884 sm.ScatterEvent('OnSkillQueueRefreshed') 885 886 def OnServerBoostersChanged(self, *args): 887 self.GetCharacterAttributes(True) 888 self._skillAcceleratorBoosters = None 889 sm.ScatterEvent('OnBoosterUpdated') 890 sm.ScatterEvent('OnSkillQueueRefreshed') 891 892 def OnServerImplantsChanged(self, *args): 893 skillQueueSvc = sm.GetService('skillqueue') 894 self.GetCharacterAttributes(True) 895 if skillQueueSvc.GetQueue(): 896 sm.ScatterEvent('OnSkillQueueRefreshed') 897 sm.ScatterEvent('OnImplantsChanged') 898 899 def OnCloneDestruction(self, *args): 900 self.GetCharacterAttributes(True) 901 sm.ScatterEvent('OnBoosterUpdated') 902 903 def OnJumpCloneTransitionCompleted(self): 904 self.GetCharacterAttributes(True) 905 sm.ScatterEvent('OnBoosterUpdated') 906 907 def GetBoosters(self, forced = 0): 908 if self.boosters is None or forced: 909 self.boosters = self.GetSkillHandler().GetBoosters() 910 return self.boosters 911 912 def GetImplants(self, forced = 0): 913 if self.implants is None or forced: 914 self.implants = self.GetSkillHandler().GetImplants() 915 return self.implants 916 917 def ExtractSkills(self, skills, itemID): 918 skills = {s:p for s, p in skills.iteritems()} 919 token = sm.GetService('connection').GetAccessToken() 920 self.GetSkillHandler().ExtractSkills(skills, itemID, token) 921 922 def ActivateSkillInjector(self, itemID, quantity): 923 self.GetSkillHandler().InjectSkillpoints(itemID, quantity) 924 sm.GetService('audio').SendUIEvent('st_activate_skill_injector_play') 925 sm.ScatterEvent('OnSkillPointsInjected_Local', quantity) 926 927 def CheckInjectionConstraints(self, itemID, quantity): 928 self.GetSkillHandler().CheckInjectionConstraints(itemID, quantity) 929 930 def ActivateSkillExtractor(self, item): 931 if eveCfg.InSpace(): 932 raise UserError('SkillExtractorNotDockedInStation', {'extractor': const.typeSkillExtractor}) 933 skillPoints = self.GetSkillHandler().GetSkillPoints() 934 freeSkillPoints = self.GetSkillHandler().GetFreeSkillPoints() 935 if skillPoints + freeSkillPoints < const.SKILL_TRADING_MINIMUM_SP_TO_EXTRACT: 936 raise UserError('SkillExtractionNotEnoughSP', {'limit': const.SKILL_TRADING_MINIMUM_SP_TO_EXTRACT, 937 'extractor': const.typeSkillExtractor}) 938 if blue.pyos.packaged: 939 token = sm.GetService('connection').GetAccessToken() 940 if token is None: 941 raise UserError('TokenRequiredForSkillExtraction') 942 SkillExtractorWindow.OpenOrReload(itemID=item.itemID) 943 944 def GetSkillPointAmountFromInjectors(self, typeID, quantity): 945 nonDiminishingInjectionsRemaining = self.nonDiminishingInjection.GetRemaining() 946 return self.GetSkillHandler().GetDiminishedSpFromInjectors(typeID, quantity, nonDiminishingInjectionsRemaining) 947 948 def GetNextAlphaInjectionDateTime(self): 949 return sm.RemoteSvc('alphaInjectorMgr').GetNextAvailableInjection() 950 951 def GetDurationUntilNextAlphaInjection(self): 952 return max(0, self.GetNextAlphaInjectionDateTime() - blue.os.GetWallclockTime()) 953 954 def SplitSkillInjector(self, itemID, quantity): 955 self.GetSkillHandler().SplitSkillInjector(itemID, quantity) 956 957 def CombineSkillInjector(self, itemID, quantity): 958 if quantity < const.SKILL_TRADING_SMALL_INJECTOR_DIVISOR: 959 raise UserError('CombineSkillInjectorTooFewInjectors', {'minQuantity': const.SKILL_TRADING_SMALL_INJECTOR_DIVISOR, 960 'injectorType': const.typeSmallSkillInjector}) 961 self.GetSkillHandler().CombineSkillInjector(itemID, quantity) 962 963 def GetSkillTrainingTimeCalculator(self): 964 return SkillTrainingTimeCalculator(ClientCharacterSkillInterface(self)) 965 966 def GetTypesUnlockedByTrainingToLevel(self, skillTypeID, level, includeQueue = True): 967 typeIndex = get_required_skills_index().get(skillTypeID, {}) 968 typeIndex = typeIndex.get(level, {}) 969 if includeQueue: 970 maxQueueIdx = self.GetSkillInQueueIndex(skillTypeID, level) 971 skillsInQueue = self.GetMaxSkillLevelsInQueue(maxQueueIdx) 972 else: 973 skillsInQueue = {} 974 ret = defaultdict(dict) 975 for marketGroupID, typesByMetaGroupID in typeIndex.iteritems(): 976 for metaGroupID, typeIDs in typesByMetaGroupID.iteritems(): 977 publishedTypeIDs = [ typeID for typeID in typeIDs if self._IsUnlockedType(skillTypeID, typeID, skillsInQueue) ] 978 if publishedTypeIDs: 979 ret[marketGroupID][metaGroupID] = publishedTypeIDs 980 981 return dict(ret) 982 983 def _IsUnlockedType(self, skillID, typeID, skillsInQueue): 984 required = self.GetRequiredSkills(typeID) 985 for requiredSkillID, requiredLevel in required.iteritems(): 986 if requiredSkillID == skillID: 987 continue 988 elif self.MySkillLevel(requiredSkillID) < requiredLevel: 989 if skillsInQueue.get(requiredSkillID, 0) < requiredLevel: 990 return False 991 992 return True 993 994 def _ConstructSkillControllers(self): 995 self.skillsByGroupIDTypeID = defaultdict(dict) 996 typeIDs = evetypes.GetTypeIDsByCategory(invconst.categorySkill) 997 for typeID in typeIDs: 998 if not evetypes.IsPublished(typeID) and not self.GetSkill(typeID): 999 continue 1000 groupID = evetypes.GetGroupID(typeID) 1001 self.skillsByGroupIDTypeID[groupID][typeID] = SkillController(typeID) 1002 1003 def GetSkillControllers(self): 1004 if self.skillsByGroupIDTypeID is None: 1005 self._ConstructSkillControllers() 1006 return self.skillsByGroupIDTypeID 1007 1008 def OnAlphaInjectionAvailable(self): 1009 uthread.new(self._NotifyAlphaInjectionAvailable) 1010 1011 def _NotifyAlphaInjectionAvailable(self): 1012 notification = SimpleNotification(subject=localization.GetByLabel('UI/SkillTrading/AlphaInjectionAvailable'), created=blue.os.GetWallclockTime(), notificationID=None, notificationTypeID=notificationconst.notificationTypeAlphaInjectorAvailable) 1013 sm.ScatterEvent('OnNewNotificationReceived', notification) 1014 1015 def PurchaseSkills(self, skillTypeIDs, confirm = True, skillPlanTemplate = None): 1016 context = characterskills.client.SkillPurchaseContext(skill_service=self, skill_plan_template=skillPlanTemplate) 1017 return characterskills.client.purchase_skills(context, skillTypeIDs, confirm=confirm) 1018 1019 def WaitUntilSkillsAreAvailable(self, skillTypeIDs): 1020 skill_type_ids = set(skillTypeIDs) 1021 seen = set() 1022 while skill_type_ids != seen: 1023 blue.pyos.BeNice() 1024 for skill_type_id in skill_type_ids: 1025 if self.GetSkillIncludingLapsed(skill_type_id) is None: 1026 break 1027 seen.add(skill_type_id) 1028 1029 sm.ScatterEvent('OnSkillsAvailable', skillTypeIDs) 1030 1031 def GetSkillBundleInfo(self, skillBundleID): 1032 return self.GetSkillHandler().GetSkillBundleInfo(skillBundleID) 1033 1034 def GetSkillBundleHighlight(self, numberOfSkills, sp): 1035 if numberOfSkills > 0 and sp > 0: 1036 return HIGHLIGHT_SKILLS_TRAINED_AND_SP_ADDED 1037 if numberOfSkills > 0: 1038 return HIGHLIGHT_SKILLS_TRAINED 1039 if sp > 0: 1040 return HIGHLIGHT_SP_ADDED 1041 1042 def OnSkillBundleInjected(self, numberOfSkills, sp): 1043 highlight_id = self.GetSkillBundleHighlight(numberOfSkills, sp) 1044 if highlight_id: 1045 sm.GetService('uiHighlightingService').highlight_ui_element(highlight_id) 1046 1047 def GetTrainingTimeForSkills(self, skills): 1048 trainingTime = 0 1049 for skillID, level in skills.iteritems(): 1050 try: 1051 myLevel = self.GetSkillIncludingLapsed(skillID).trainedSkillLevel or 0 1052 except AttributeError: 1053 myLevel = 0 1054 1055 for i in xrange(int(myLevel) + 1, int(level) + 1): 1056 trainingTime += self.GetRawTrainingTimeForSkillLevel(skillID, i) 1057 1058 return trainingTime 1059 1060 def GetTrainingTimeForSkillsExcludeTrained(self, skills): 1061 trainingTime = 0 1062 for skillID, level in skills.iteritems(): 1063 for i in xrange(1, int(level) + 1): 1064 trainingTime += self.GetRawTrainingTimeForSkillLevel(skillID, i) 1065 1066 return trainingTime