fittingSvc.py (56246B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\environment\fittingSvc.py 2 import sys 3 from collections import defaultdict, Counter 4 import blue 5 import dogma.data 6 import evetypes 7 import inventorycommon 8 import inventorycommon.const as invConst 9 import localization 10 import log 11 import uthread 12 import utillib 13 from carbon.common.script.sys.service import Service 14 from carbon.common.script.sys.serviceConst import SERVICE_RUNNING 15 from carbon.common.script.util.format import FmtAmt 16 from carbon.common.script.util.commonutils import StripTags 17 from carbon.common.script.util.linkUtil import GetShowInfoLink 18 from eve.client.script.ui.control.entries.fitting import FittingModuleEntry 19 from eve.client.script.ui.control.entries.header import Header 20 from eve.client.script.ui.control.entries.util import GetFromClass 21 from eve.client.script.ui.shared.fitting.fighters_util import GetFighterNumByTypeIDsInTubes, GetFightersInTubes 22 from eve.client.script.ui.shared.fittingScreen.fittingSearchUtil import SearchFittingHelper 23 from eve.client.script.ui.shared.fittingMgmtWindow import FittingMgmt, ViewFitting 24 from eve.client.script.ui.shared.fittingScreen.missingItemsPopup import OpenBuyAllBox 25 from eve.common.lib.appConst import COMMUNITY_FITTING_CORP 26 from eve.common.script.sys import eveCfg 27 from eve.common.script.sys.eveCfg import GetActiveShip, InShipInSpace 28 from eveexceptions import UserError 29 from evetypes import TYPE_LIST_FILAMENTS, TYPE_LIST_OTHER_CARGO_HOLD_TYPES 30 from globalConfig.getFunctions import AreCommunityFittingsEnabled 31 from player_messaging.client.ui_message import prompt_player_fsd_dialog 32 from shipfitting import Fitting, INVALID_FITTING_ID 33 from inventorycommon.util import IsShipFittingFlag, IsShipFittable, IsSubsystemFlagVisible, IsFighterTubeFlag, IsModularShip 34 from carbonui import uiconst 35 from carbonui.util.various_unsorted import SortListOfTuples, GetClipboardData 36 from shipfitting.fittingStuff import IsThereRigMismatch 37 from shipfitting.importFittingUtil import FindShipAndFittingName, ImportFittingUtil 38 from shipfitting.exportFittingUtil import GetFittingEFTString 39 from textImporting import IsUsingDefaultLanguage 40 from utillib import KeyVal 41 from carbonui.uicore import uicore 42 from eve.common.lib import appConst as const 43 CARGO_MARKER = '_' 44 45 class fittingSvc(Service): 46 __guid__ = 'svc.fittingSvc' 47 __exportedcalls__ = {'GetFittingDictForActiveShip': [], 48 'IsTypeFittedInActiveShip': []} 49 __startupdependencies__ = ['settings', 'invCache'] 50 __notifyevents__ = ['OnSkillsChanged', 51 'OnFittingAdded', 52 'OnFittingDeleted', 53 'OnManyFittingsDeleted', 54 'OnFittingsUpdated', 55 'OnGlobalConfigChanged', 56 'OnCommunityFittingsUpdated', 57 'OnSessionReset'] 58 59 def __init__(self): 60 Service.__init__(self) 61 self.Initialize() 62 self.InitSearchFittingHelper() 63 64 def Initialize(self): 65 self.fittings = {} 66 self.hasSkillByFittingID = {} 67 self.importFittingUtil = None 68 self.inSimulation = False 69 self.busyFitting = False 70 71 def Run(self, ms = None): 72 self.state = SERVICE_RUNNING 73 self.fittings = {} 74 75 def OnSessionReset(self): 76 self.Initialize() 77 self.searchFittingHelper.ResetAllVariables() 78 79 def InitSearchFittingHelper(self): 80 self.searchFittingHelper = SearchFittingHelper(cfg) 81 uthread.new(self.searchFittingHelper.BuildNameDict) 82 83 def GetFittingMgr(self, ownerID): 84 if ownerID == session.charid: 85 return sm.RemoteSvc('charFittingMgr') 86 if ownerID == session.corpid: 87 return sm.RemoteSvc('corpFittingMgr') 88 if session.allianceid and ownerID == session.allianceid: 89 return sm.RemoteSvc('allianceFittingMgr') 90 raise RuntimeError("Can't find the fitting manager you're asking me to get. ownerID:", ownerID) 91 92 def HasLegacyClientFittings(self): 93 if len(settings.char.generic.Get('fittings', {})) > 0: 94 return True 95 return False 96 97 def GetLegacyClientFittings(self): 98 return settings.char.generic.Get('fittings', {}) 99 100 def DeleteLegacyClientFittings(self): 101 settings.char.generic.Set('fittings', None) 102 103 def GetFighterTypesInTubes(self): 104 dogmaLocation = self.GetCurrentDogmaLocation() 105 return {typeID for typeID in GetFighterNumByTypeIDsInTubes(dogmaLocation).iterkeys()} 106 107 def GetFittingDictForCurrentFittingWindowShip(self, putModuleAmmoInCargo = True): 108 if self.IsShipSimulated(): 109 return self.GetFittingDictForSimulatedShip(putModuleAmmoInCargo=putModuleAmmoInCargo) 110 else: 111 return self.GetFittingDictForActiveShip(putModuleAmmoInCargo=putModuleAmmoInCargo) 112 113 def GetFittingDictForActiveShip(self, putModuleAmmoInCargo = True): 114 shipID = eveCfg.GetActiveShip() 115 shipInv = self.invCache.GetInventoryFromId(shipID, locationID=session.stationid) 116 visibleFittedItems = {i for i in shipInv.List() if inventorycommon.ItemIsVisible(i)} 117 if InShipInSpace(): 118 try: 119 drones = sm.GetService('clientDogmaIM').GetDogmaLocation().GetShip().GetDrones() 120 godma = sm.GetService('godma') 121 droneInvItems = filter(None, {godma.GetItem(x.itemID) for x in drones.itervalues() if x.locationID != shipID}) 122 droneDbRows = {blue.DBRow(x.dbrow) for x in droneInvItems} 123 for eachDrone in droneDbRows: 124 eachDrone.flagID = const.flagDroneBay 125 126 visibleFittedItems.update(droneDbRows) 127 except Exception as e: 128 log.LogException('issue when getting drones for a fit') 129 130 fitData = self.CreateFittingData(visibleFittedItems, putModuleAmmoInCargo=putModuleAmmoInCargo) 131 return (shipInv.GetItem().typeID, fitData, visibleFittedItems) 132 133 def IsTypeFittedInActiveShip(self, typeID): 134 _shipType, fitData, _ = self.GetFittingDictForActiveShip() 135 for fitTypeID, _fitFlag, _fitQuantity in fitData: 136 if fitTypeID == typeID: 137 return True 138 139 return False 140 141 def FittingDictForRemoteShip(self, shipID, putModuleAmmoInCargo, dockableLocationID, solarSystemID): 142 inventoryMgr = sm.GetService('invCache').GetInventoryMgr() 143 visibleFittedItems = inventoryMgr.GetContainerContents(shipID, dockableLocationID, solarSystemID) 144 return self.GetFittingDictFromItems(visibleFittedItems, putModuleAmmoInCargo) 145 146 def GetFittingDictFromItems(self, visibleFittedItems, putModuleAmmoInCargo): 147 fittingData = self.CreateFittingData(visibleFittedItems, putModuleAmmoInCargo=putModuleAmmoInCargo) 148 oringalItemIDByFlagAndTypeID = {(x.typeID, x.flagID):x.itemID for x in visibleFittedItems} 149 for eachItem in visibleFittedItems: 150 if IsFighterTubeFlag(eachItem.flagID): 151 fittingData.append((eachItem.typeID, eachItem.flagID, eachItem.stacksize)) 152 153 return (fittingData, oringalItemIDByFlagAndTypeID) 154 155 def GetFittingDictForSimulatedShip(self, putModuleAmmoInCargo = True): 156 dogmaLocation = sm.GetService('clientDogmaIM').GetFittingDogmaLocation() 157 shipID = dogmaLocation.GetCurrentShipID() 158 shipItem = dogmaLocation.GetDogmaItem(shipID) 159 fittedItems = shipItem.GetFittedItems() 160 visibleFittedItems = (i for i in fittedItems.values() if inventorycommon.ItemIsVisible(i)) 161 cargoItems = dogmaLocation.GetHoldItems(const.flagCargo) 162 fightersInTubes = GetFightersInTubes(dogmaLocation) 163 fighterBayItems = dogmaLocation.GetHoldItems(const.flagFighterBay) 164 items = list(visibleFittedItems) 165 items += shipItem.GetDrones().values() 166 items += cargoItems.values() 167 items += fightersInTubes 168 items += fighterBayItems.values() 169 fitData = self.CreateFittingData(items, putModuleAmmoInCargo=putModuleAmmoInCargo) 170 return (shipItem.typeID, fitData, items) 171 172 def CreateFittingData(self, items, putModuleAmmoInCargo = True): 173 fitData = [] 174 dronesByType = defaultdict(int) 175 chargesByType = defaultdict(int) 176 iceByType = defaultdict(int) 177 fightersByType = defaultdict(int) 178 implantsByType = defaultdict(int) 179 modulesInCargo = defaultdict(int) 180 filamentsByType = defaultdict(int) 181 otherByType = defaultdict(int) 182 for item in items: 183 typeID = item.typeID 184 flagID = item.flagID 185 if IsShipFittingFlag(flagID) and IsShipFittable(item.categoryID): 186 fitData.append((int(typeID), int(flagID), 1)) 187 elif item.categoryID == const.categoryDrone and flagID == const.flagDroneBay: 188 dronesByType[typeID] += getattr(item, 'stacksize', 1) 189 elif item.categoryID == const.categoryFighter and flagID == const.flagFighterBay: 190 fightersByType[typeID] += getattr(item, 'stacksize', 1) 191 elif item.categoryID == const.categoryFighter and IsFighterTubeFlag(flagID): 192 fightersByType[typeID] += getattr(item, 'stacksize', 1) 193 elif item.categoryID == const.categoryCharge and flagID == const.flagCargo: 194 chargesByType[typeID] += getattr(item, 'stacksize', 1) 195 elif item.categoryID == const.categoryImplant and flagID == const.flagCargo: 196 implantsByType[typeID] += getattr(item, 'stacksize', 1) 197 elif item.categoryID in (const.categoryModule, const.categorySubSystem) and flagID == const.flagCargo: 198 modulesInCargo[typeID] += getattr(item, 'stacksize', 1) 199 elif hasattr(item, 'groupID') and item.groupID == const.groupIceProduct and flagID == const.flagCargo: 200 iceByType[typeID] += getattr(item, 'stacksize', 1) 201 elif putModuleAmmoInCargo and item.categoryID == const.categoryCharge and flagID in const.moduleSlotFlags: 202 chargesByType[typeID] += getattr(item, 'stacksize', 1) 203 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_FILAMENTS): 204 filamentsByType[typeID] += getattr(item, 'stacksize', 1) 205 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_OTHER_CARGO_HOLD_TYPES): 206 otherByType[typeID] += getattr(item, 'stacksize', 1) 207 208 fitData += self.GetDataToAddToFitData(const.flagDroneBay, dronesByType) 209 fitData += self.GetDataToAddToFitData(const.flagFighterBay, fightersByType) 210 fitData += self.GetDataToAddToFitData(const.flagCargo, chargesByType) 211 fitData += self.GetDataToAddToFitData(const.flagCargo, iceByType) 212 fitData += self.GetDataToAddToFitData(const.flagCargo, implantsByType) 213 fitData += self.GetDataToAddToFitData(const.flagCargo, modulesInCargo) 214 fitData += self.GetDataToAddToFitData(const.flagCargo, filamentsByType) 215 fitData += self.GetDataToAddToFitData(const.flagCargo, otherByType) 216 return fitData 217 218 def GetLoadedChargeTypes(self, items): 219 chargesToLoad = [] 220 for item in items: 221 typeID = item.typeID 222 flagID = item.flagID 223 if item.categoryID == const.categoryCharge and flagID != const.flagCargo: 224 chargesToLoad.append((typeID, flagID)) 225 226 return chargesToLoad 227 228 def GetDataToAddToFitData(self, flag, qtyByTypeID): 229 data = [] 230 for typeID, quantity in qtyByTypeID.iteritems(): 231 data.append((int(typeID), int(flag), int(quantity))) 232 233 return data 234 235 def DisplayFittingFromItems(self, shipTypeID, items): 236 fitting = self.GetFittingFromItems(shipTypeID, items) 237 self.DisplayFitting(fitting) 238 239 def GetFittingFromItems(self, shipTypeID, items): 240 newItems = [] 241 for item in items: 242 if not hasattr(item, 'flagID'): 243 item.flagID = item.flag 244 if not hasattr(item, 'stacksize'): 245 item.stacksize = item.qtyDropped + item.qtyDestroyed 246 item.categoryID = evetypes.GetCategoryID(item.typeID) 247 newItems.append(item) 248 249 fitData = self.CreateFittingData(newItems) 250 fitting = utillib.KeyVal() 251 fitting.shipTypeID = shipTypeID 252 fitting.name = evetypes.GetName(shipTypeID) 253 fitting.ownerID = None 254 fitting.fittingID = None 255 fitting.description = '' 256 fitting.fitData = fitData 257 return fitting 258 259 def PersistFitting(self, ownerID, name, description, fit = None): 260 if name is None or name.strip() == '': 261 raise UserError('FittingNeedsToHaveAName') 262 name = name.strip() 263 fitting = utillib.KeyVal() 264 fitting.name = name[:const.maxLengthFittingName] 265 fitting.description = description[:const.maxLengthFittingDescription] 266 self.PrimeFittings(ownerID) 267 if ownerID == session.corpid: 268 maxFittings = const.maxCorpFittings 269 else: 270 maxFittings = const.maxCharFittings 271 if len(self.fittings[ownerID]) >= maxFittings: 272 owner = cfg.eveowners.Get(ownerID).ownerName 273 raise UserError('OwnerMaxFittings', {'owner': owner, 274 'maxFittings': maxFittings}) 275 if fit is None: 276 fitting.shipTypeID, fitting.fitData, _ = self.GetFittingDictForActiveShip() 277 else: 278 fitting.shipTypeID, fitting.fitData = fit 279 self.VerifyFitting(fitting) 280 fitting.ownerID = ownerID 281 existingFitting = self._GetFittingWithName(fitting.shipTypeID, ownerID, name) 282 if existingFitting: 283 if ownerID == session.corpid: 284 question = 'UpdateFitCorp' 285 else: 286 question = 'UpdateFitPersonal' 287 ret = prompt_player_fsd_dialog(question, {'fittingName': name}, uiconst.YESNO, suppress=uiconst.ID_YES) 288 if ret != uiconst.ID_YES: 289 return 290 if not fitting.description: 291 fitting.description = existingFitting.description 292 fittingMgr = self.GetFittingMgr(ownerID) 293 if existingFitting: 294 fittingID = fittingMgr.UpdateFitting(ownerID, fitting, fittingID=existingFitting.fittingID) 295 else: 296 fittingID = fittingMgr.SaveFitting(ownerID, fitting) 297 if fittingID > 0: 298 if existingFitting: 299 fitting.fittingID = fittingID 300 fitting.savedDate = blue.os.GetWallclockTime() 301 if ownerID in self.fittings: 302 self.fittings[ownerID][fittingID] = fitting 303 self.UpdateFittingWindow() 304 sm.ScatterEvent('OnFittingsUpdated', fitting) 305 return fittingID 306 307 def _GetFittingWithName(self, shipTypeID, ownerID, name): 308 fittingsForOwner = self.GetFittings(ownerID) 309 for eachFitID, eachFit in fittingsForOwner.iteritems(): 310 if eachFit.shipTypeID == shipTypeID and eachFit.name == name: 311 return eachFit 312 313 def PersistManyFittings(self, ownerID, fittings): 314 if ownerID == session.corpid: 315 maxFittings = const.maxCorpFittings 316 else: 317 maxFittings = const.maxCharFittings 318 self.PrimeFittings(ownerID) 319 if len(self.fittings[ownerID]) + len(fittings) > maxFittings: 320 owner = cfg.eveowners.Get(ownerID).ownerName 321 raise UserError('OwnerMaxFittings', {'owner': owner, 322 'maxFittings': maxFittings}) 323 fittingsToSave = {} 324 tmpFittingID = INVALID_FITTING_ID 325 for fitt in fittings: 326 if fitt.name is None or fitt.name.strip() == '': 327 raise UserError('FittingNeedsToHaveAName') 328 fitting = utillib.KeyVal() 329 fitting.fittingID = tmpFittingID 330 fitting.name = fitt.name.strip()[:const.maxLengthFittingName] 331 fitting.description = fitt.description[:const.maxLengthFittingDescription] 332 fitting.shipTypeID = fitt.shipTypeID 333 fitting.fitData = fitt.fitData 334 self.VerifyFitting(fitting) 335 fitting.ownerID = ownerID 336 fittingsToSave[tmpFittingID] = fitting 337 tmpFittingID -= 1 338 339 newFittingIDs = self.GetFittingMgr(ownerID).SaveManyFittings(ownerID, fittingsToSave) 340 for row in newFittingIDs: 341 self.fittings[ownerID][row.realFittingID] = fittingsToSave[row.tempFittingID] 342 self.fittings[ownerID][row.realFittingID].fittingID = row.realFittingID 343 344 self.UpdateFittingWindow() 345 return fitting 346 347 def VerifyFitting(self, fitting): 348 if fitting.name.find('@@') != INVALID_FITTING_ID or fitting.description.find('@@') != INVALID_FITTING_ID: 349 raise UserError('InvalidFittingInvalidCharacter') 350 if fitting.shipTypeID is None: 351 raise UserError('InvalidFittingDataTypeID', {'typeName': fitting.shipTypeID}) 352 shipTypeName = evetypes.GetNameOrNone(fitting.shipTypeID) 353 if shipTypeName is None: 354 raise UserError('InvalidFittingDataTypeID', {'typeName': fitting.shipTypeID}) 355 if evetypes.GetCategoryID(fitting.shipTypeID) not in (const.categoryShip, const.categoryStructure): 356 raise UserError('InvalidFittingDataShipNotShip', {'typeName': shipTypeName}) 357 if len(fitting.fitData) == 0: 358 raise UserError('ParseFittingFittingDataEmpty') 359 for typeID, flag, qty in fitting.fitData: 360 if not evetypes.Exists(typeID): 361 raise UserError('InvalidFittingDataTypeID', {'typeID': typeID}) 362 try: 363 int(flag) 364 except TypeError: 365 raise UserError('InvalidFittingDataInvalidFlag', {'type': typeID}) 366 367 if not (IsShipFittingFlag(flag) or flag in (const.flagDroneBay, const.flagCargo, const.flagFighterBay)): 368 raise UserError('InvalidFittingDataInvalidFlag', {'type': typeID}) 369 try: 370 int(qty) 371 except TypeError: 372 raise UserError('InvalidFittingDataInvalidQuantity', {'type': typeID}) 373 374 if qty == 0: 375 raise UserError('InvalidFittingDataInvalidQuantity', {'type': typeID}) 376 377 return True 378 379 def GetAllFittingsForCharacter(self): 380 ret = uthread.parallel([(self.GetFittings, (session.charid,)), 381 (self.GetFittings, (session.corpid,)), 382 (self.GetCommunityFittings, ()), 383 (self.GetAllianceFittings, ())]) 384 return ret 385 386 def GetCommunityFittings(self): 387 if not AreCommunityFittingsEnabled(sm.GetService('machoNet')): 388 return {} 389 try: 390 if COMMUNITY_FITTING_CORP not in self.fittings: 391 corpFittingMgr = sm.RemoteSvc('corpFittingMgr') 392 communityFittings = corpFittingMgr.GetCommunityFittings() 393 self.fittings[COMMUNITY_FITTING_CORP] = communityFittings 394 return self.fittings[COMMUNITY_FITTING_CORP] 395 except Exception as e: 396 self.LogException(e) 397 return {} 398 399 def GetAllianceFittings(self): 400 if session.allianceid: 401 return self.GetFittings(session.allianceid) 402 return {} 403 404 def GetFittings(self, ownerID): 405 self.PrimeFittings(ownerID) 406 return self.fittings[ownerID] 407 408 def GetFittingsForType(self, ownerID, typeID): 409 allFittings = self.GetFittings(ownerID) 410 return [ fitting for fitting in allFittings.items() if fitting[1]['shipTypeID'] == typeID ] 411 412 def PrimeFittings(self, ownerID): 413 if ownerID not in self.fittings: 414 self.fittings[ownerID] = self.GetFittingMgr(ownerID).GetFittings(ownerID) 415 416 def DeleteFitting(self, ownerID, fittingID): 417 self.LogInfo('deleting fitting', fittingID, 'from owner', ownerID) 418 self.GetFittingMgr(ownerID).DeleteFitting(ownerID, fittingID) 419 if ownerID in self.fittings and fittingID in self.fittings[ownerID]: 420 del self.fittings[ownerID][fittingID] 421 self.UpdateFittingWindow() 422 423 def DeleteManyFittings(self, fittingsByOwnerID): 424 calls = [] 425 ownerIDs = fittingsByOwnerID.keys() 426 totalFittings = 0 427 for eachOwnerID in ownerIDs: 428 fittingIDs = fittingsByOwnerID.get(eachOwnerID, []) 429 totalFittings += len(fittingIDs) 430 calls.append((self.GetFittingMgr(eachOwnerID).DeleteManyFittings, (eachOwnerID, fittingIDs))) 431 432 ret = uthread.parallel(calls) 433 fittingsDeleted = 0 434 for x in ret: 435 fittingsDeleted += len(x) 436 437 if len(ownerIDs) > 1: 438 ownerText = self.GetDeletedFittingsText(ownerIDs, fittingsByOwnerID, ret) 439 else: 440 ownerText = '' 441 prompt_player_fsd_dialog('DeletedManyFittings', {'numFittings': fittingsDeleted, 442 'ownerText': ownerText}) 443 444 def GetDeletedFittingsText(self, ownerIDs, fittingsByOwnerID, deletedFittings): 445 textList = [] 446 for i, ownerID in enumerate(ownerIDs): 447 if not ownerID: 448 continue 449 fittingIDs = fittingsByOwnerID.get(ownerID, []) 450 if fittingIDs: 451 numDeleted = len(deletedFittings[i]) 452 numAttempted = len(fittingIDs) 453 msgName = 'UI/Fitting/FittingWindow/OwnerAndDeletedFittingNum' 454 if numDeleted != numAttempted: 455 msgName = 'UI/Fitting/FittingWindow/OwnerAndDeletedFittingNumWithAttempted' 456 ownerName = cfg.eveowners.Get(ownerID).name 457 text = localization.GetByLabel(msgName, numFittings=numDeleted, ownerName=ownerName, numAttempted=len(fittingIDs)) 458 text = u' \u2022 ' + text 459 textList.append(text) 460 461 return '<br>'.join(textList) 462 463 def LoadFittingFromFittingIDAndGetBuyOptionOnFailure(self, ownerID, fittingID): 464 fitting = self.fittings.get(ownerID, {}).get(fittingID, None) 465 return self.LoadFittingFromFittingListAndGetBuyOptionOnFailure(fitting) 466 467 def LoadFittingFromFittingListAndGetBuyOptionOnFailure(self, fitting): 468 failedToLoad = self.LoadFitting(fitting, getFailedDict=True) 469 failedToLoadCounter = Counter({x[0]:x[1] for x in failedToLoad}) 470 if failedToLoadCounter: 471 OpenBuyAllBox(failedToLoadCounter, fitting) 472 else: 473 sm.GetService('ghostFittingSvc').TryExitSimulation() 474 475 def LoadFittingFromFittingID(self, ownerID, fittingID, getFailedDict = False): 476 fitting = self.fittings.get(ownerID, {}).get(fittingID, None) 477 return self.LoadFitting(fitting, getFailedDict=getFailedDict) 478 479 def CheckBusyFittingAndRaiseIfNeeded(self): 480 if self.busyFitting: 481 sm.GetService('loading').ProgressWnd('', '', 1, 1) 482 raise UserError('CustomInfo', {'info': localization.GetByLabel('UI/Fitting/FittingWindow/FittingManagement/BusyFittingWarning')}) 483 484 def DoFitManyShips(self, shipTypeID, fitInfo, cargoItemsByType, fitRigs, fittingName, maxAvailableFitting, itemContainerID = None): 485 shipAccess = sm.StartService('gameui').GetShipAccess() 486 try: 487 failedInfo = None 488 self.busyFitting = True 489 sm.GetService('inv').ChangeTreeUpdatingState(isUpdateEnabled=False) 490 failedInfo = shipAccess.FitShips(shipTypeID=shipTypeID, fitInfo=fitInfo, itemLocationID=session.stationid or session.structureid, cargoItemsByType=cargoItemsByType, fitRigs=fitRigs, name=fittingName, numToFit=maxAvailableFitting, itemContainerID=itemContainerID) 491 finally: 492 self.busyFitting = False 493 sm.GetService('inv').ChangeTreeUpdatingState(isUpdateEnabled=True) 494 if failedInfo: 495 failedToLoad, failedShipID = failedInfo 496 if failedShipID: 497 self._RenameShip(failedShipID) 498 if failedToLoad: 499 self.ShowFailedToLoadMsg(failedToLoad) 500 501 def _RenameShip(self, failedShipID): 502 badFitName = localization.GetByLabel('UI/Fitting/FittingWindow/FittingManagement/BadFitFittingFailed') 503 self.invCache.GetInventoryMgr().SetLabel(failedShipID, badFitName) 504 505 def GetMaxAvailabeAndMissingForFullFit(self, fitRigs, itemTypes, numToFit, qtyByTypeID, rigTypeIDs): 506 missingForFullFit = {} 507 maxRoundsForEachTypeID = {} 508 for itemTypID, qtyNeeded in itemTypes.iteritems(): 509 if not fitRigs and itemTypID in rigTypeIDs: 510 continue 511 qtyInHangar = qtyByTypeID.get(itemTypID, 0) 512 rounds = int(qtyInHangar / qtyNeeded) 513 maxRoundsForEachTypeID[itemTypID] = rounds 514 if rounds < numToFit: 515 missingForFullFit[itemTypID] = qtyNeeded * numToFit - qtyInHangar 516 517 maxAvailableFitting = min(maxRoundsForEachTypeID.values()) if maxRoundsForEachTypeID else None 518 return (maxAvailableFitting, missingForFullFit) 519 520 def GetMaxAvailable(self, fitRigs, itemTypes, qtyByTypeID, rigTypeIDs): 521 maxRoundsForEachTypeID = {} 522 for itemTypID, qtyNeeded in itemTypes.iteritems(): 523 if not fitRigs and itemTypID in rigTypeIDs: 524 continue 525 qtyInHangar = qtyByTypeID.get(itemTypID, 0) 526 rounds = int(qtyInHangar / qtyNeeded) 527 maxRoundsForEachTypeID[itemTypID] = rounds 528 529 def LoadFitting(self, fitting, getFailedDict = False): 530 self.CheckBusyFittingAndRaiseIfNeeded() 531 self._CheckValidFittingLocation(fitting) 532 shipInv = self.invCache.GetInventoryFromId(eveCfg.GetActiveShip()) 533 shipTypeID = shipInv.GetItem().typeID 534 if shipTypeID != fitting.shipTypeID: 535 raise UserError('ShipTypeInFittingNotSameAsShip') 536 fittingObj = Fitting(fitting.fitData, shipInv) 537 rigsByFlags = fittingObj.GetRigsByFlag() 538 fitRigs = False 539 cargoItemsByType = defaultdict(int) 540 if bool(rigsByFlags): 541 fittedRigsByFlagID = self.GetFittedRigsByFlagID() 542 rigMismatch = IsThereRigMismatch(fittedRigsByFlagID, rigsByFlags) 543 sameRigsInSameSlots = rigsByFlags == fittedRigsByFlagID 544 if rigMismatch and not IsModularShip(shipTypeID): 545 prompt_player_fsd_dialog('CustomNotify', {'notify': localization.GetByLabel('UI/Fitting/ShipHasRigsAlready')}) 546 elif IsModularShip(shipTypeID) or sameRigsInSameSlots: 547 fitRigs = True 548 elif prompt_player_fsd_dialog('FitRigs', {}, uiconst.YESNO) == uiconst.ID_YES: 549 fitRigs = True 550 else: 551 for flagID, typeID in fittingObj.GetModulesByFlag().iteritems(): 552 if flagID in const.rigSlotFlags: 553 cargoItemsByType[typeID] += 1 554 555 cargoItemsByType = dict(cargoItemsByType) 556 itemTypes = fittingObj.GetQuantityByType() 557 itemsToFit = defaultdict(set) 558 for item in self.invCache.GetInventory(const.containerHangar).List(const.flagHangar): 559 if item.typeID in itemTypes: 560 qtyNeeded = itemTypes[item.typeID] 561 if qtyNeeded == 0: 562 continue 563 quantityToTake = min(item.stacksize, qtyNeeded) 564 itemsToFit[item.typeID].add(item.itemID) 565 itemTypes[item.typeID] -= quantityToTake 566 567 numSubsystems = fittingObj.GetFittingSubSystemNumber() 568 if numSubsystems: 569 alreadyFitted = {x.typeID for x in shipInv.List() if IsSubsystemFlagVisible(x.flagID)} 570 if numSubsystems != invConst.numVisibleSubsystems: 571 raise UserError('CantUnfitSubSystems') 572 clientDogmaStaticSvc = sm.GetService('clientDogmaStaticSvc') 573 for flag, module in fittingObj.GetModulesByFlag().iteritems(): 574 if not IsSubsystemFlagVisible(flag): 575 continue 576 if module in alreadyFitted: 577 continue 578 moduleFitsShipType = int(clientDogmaStaticSvc.GetTypeAttribute(module, const.attributeFitsToShipType)) 579 if shipTypeID != moduleFitsShipType: 580 raise UserError('CannotFitSubSystemNotShip', {'subSystemName': (const.UE_TYPEID, module), 581 'validShipName': (const.UE_TYPEID, moduleFitsShipType), 582 'shipName': (const.UE_TYPEID, shipTypeID)}) 583 if module in itemTypes and itemTypes[module] > 0: 584 raise UserError('CantUnfitSubSystems') 585 586 self.CheckBusyFittingAndRaiseIfNeeded() 587 self.busyFitting = True 588 try: 589 fittingObjKeyVal = fittingObj.GetKeyValForApplyingFit() 590 failedToLoad = shipInv.FitFitting(eveCfg.GetActiveShip(), shipTypeID, itemsToFit, session.stationid or session.structureid, fittingObjKeyVal, cargoItemsByType, fitRigs) 591 finally: 592 self.busyFitting = False 593 594 if settings.user.ui.Get('useFittingNameForShips', 0): 595 fittingName = StripTags(fitting.get('name')) 596 fittingName = fittingName[:20] 597 self.invCache.GetInventoryMgr().SetLabel(eveCfg.GetActiveShip(), fittingName) 598 if getFailedDict: 599 return failedToLoad 600 self.ShowFailedToLoadMsg(failedToLoad) 601 602 def ShowFailedToLoadMsg(self, failedToLoad): 603 textList = [] 604 for typeID, qty in failedToLoad: 605 if qty > 0: 606 typeName = evetypes.GetName(typeID) 607 link = GetShowInfoLink(typeID, typeName) 608 textList.append((typeName.lower(), '%sx %s' % (qty, link))) 609 610 if textList: 611 textList = SortListOfTuples(textList) 612 text = '<br>'.join(textList) 613 text = localization.GetByLabel('UI/Fitting/MissingItems', types=text) 614 prompt_player_fsd_dialog('CustomInfo', {'info': text}, modal=False) 615 616 def _CheckValidFittingLocation(self, fitting): 617 if session.stationid is None and session.structureid is None: 618 raise UserError('CannotLoadFittingInSpace') 619 if fitting is None: 620 raise UserError('FittingDoesNotExist') 621 622 def GetQtyInLocationByTypeIDs(self, itemTypes, onlyGetNonSingletons = False, locationID = None): 623 if locationID is None: 624 containerLocation = self.invCache.GetInventory(const.containerHangar) 625 itemList = containerLocation.List(const.flagHangar) 626 else: 627 containerLocation = self.invCache.GetInventoryFromId(locationID) 628 itemList = containerLocation.List() 629 qtyByTypeID = defaultdict(int) 630 for item in itemList: 631 if item.typeID in itemTypes: 632 if onlyGetNonSingletons and item.quantity < 0: 633 continue 634 qtyByTypeID[item.typeID] += item.stacksize 635 636 return qtyByTypeID 637 638 def FindPersonalContainers(self): 639 hangar = self.invCache.GetInventory(const.containerHangar) 640 personalContainers = set() 641 validContainers = (invConst.groupAuditLogSecureContainer, 642 invConst.groupSecureCargoContainer, 643 invConst.groupCargoContainer, 644 invConst.groupFreightContainer) 645 for item in hangar.List(const.flagHangar): 646 if item.groupID in validContainers and item.ownerID == session.charid and item.singleton: 647 personalContainers.add(item) 648 649 return personalContainers 650 651 def GetTypesToFit(self, fitting, shipInv): 652 fittingObj = Fitting(fitting.fitData, shipInv) 653 return (fittingObj.GetChargesByType(), 654 fittingObj.GetDronesByType(), 655 fittingObj.GetFigthersByType(), 656 fittingObj.GetIceByType(), 657 fittingObj.GetQuantityByType(), 658 fittingObj.GetModulesByFlag(), 659 fittingObj.GetRigsByFlag(), 660 fittingObj.GetFittingSubSystemNumber()) 661 662 def GetFittedRigsByFlagID(self): 663 rigsFittedByFlagID = {} 664 shipInv = self.invCache.GetInventoryFromId(eveCfg.GetActiveShip(), locationID=session.stationid) 665 for item in shipInv.List(): 666 if const.flagRigSlot0 <= item.flagID <= const.flagRigSlot7: 667 rigsFittedByFlagID[item.flagID] = item.typeID 668 669 return rigsFittedByFlagID 670 671 def UpdateFittingWindow(self): 672 wnd = FittingMgmt.GetIfOpen() 673 if wnd is not None: 674 wnd.DrawFittings() 675 676 def ChangeNameAndDescription(self, fittingID, ownerID, name, description): 677 if name is None or name.strip() == '': 678 raise UserError('FittingNeedsToHaveAName') 679 name = name.strip() 680 fittings = self.GetFittings(ownerID) 681 if fittingID in fittings: 682 fitting = fittings[fittingID] 683 if name != fitting.name or description != fitting.description: 684 if name.find('@@') != INVALID_FITTING_ID or description.find('@@') != INVALID_FITTING_ID: 685 raise UserError('InvalidFittingInvalidCharacter') 686 self.GetFittingMgr(ownerID).UpdateNameAndDescription(fittingID, ownerID, name, description) 687 self.fittings[ownerID][fittingID].name = name 688 self.fittings[ownerID][fittingID].description = description 689 self.UpdateFittingWindow() 690 691 def GetFitting(self, ownerID, fittingID): 692 self.PrimeFittings(ownerID) 693 if fittingID in self.fittings[ownerID]: 694 return self.fittings[ownerID][fittingID] 695 696 def CheckFittingExist(self, ownerID, shipTypeID, fitData): 697 fittings = self.GetFittings(ownerID) 698 fittingExists = False 699 for fitting in fittings.itervalues(): 700 if fitting.shipTypeID != shipTypeID: 701 continue 702 if fitting.fitData != fitData: 703 continue 704 fittingExists = True 705 706 return fittingExists 707 708 def DisplayFittingFromString(self, fittingString, name = None): 709 fitting, truncated = self.GetFittingFromString(fittingString, name) 710 if fitting == INVALID_FITTING_ID: 711 raise UserError('FittingInvalidForViewing') 712 self.DisplayFitting(fitting, truncated=truncated) 713 714 def DisplayFitting(self, fitting, truncated = False): 715 if uicore.uilib.Key(uiconst.VK_SHIFT): 716 fittingsList = fitting.fitData[:] 717 fittingsList.sort() 718 newFittingStr = '%s' % fittingsList 719 windowID = 'ViewFitting_%s' % newFittingStr 720 else: 721 windowID = 'ViewFitting' 722 wnd = ViewFitting.GetIfOpen(windowID=windowID) 723 if wnd: 724 wnd.ReloadWnd(windowID=windowID, fitting=fitting, truncated=truncated) 725 wnd.Maximize() 726 else: 727 ViewFitting.Open(windowID=windowID, fitting=fitting, truncated=truncated) 728 729 def GetStringForFitting(self, fitting): 730 typesDict = defaultdict(int) 731 drones = {} 732 fighters = {} 733 charges = {} 734 ice = {} 735 implants = {} 736 modules = {} 737 filaments = {} 738 otherCargoItems = {} 739 for typeID, flag, qty in fitting.fitData: 740 categoryID = evetypes.GetCategoryID(typeID) 741 groupID = evetypes.GetGroupID(typeID) 742 if IsShipFittable(categoryID): 743 if flag == const.flagCargo: 744 modules[typeID] = qty 745 else: 746 typesDict[typeID] += 1 747 elif categoryID == const.categoryDrone: 748 drones[typeID] = qty 749 elif categoryID == const.categoryFighter: 750 fighters[typeID] = qty 751 elif categoryID == const.categoryCharge: 752 charges[typeID] = qty 753 elif categoryID == const.categoryImplant: 754 implants[typeID] = qty 755 elif groupID == const.groupIceProduct: 756 ice[typeID] = qty 757 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_FILAMENTS): 758 filaments[typeID] = qty 759 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_OTHER_CARGO_HOLD_TYPES): 760 otherCargoItems[typeID] = qty 761 762 retList = [] 763 subString = str(fitting.shipTypeID) 764 retList.append(subString) 765 for eachDict in [typesDict, 766 drones, 767 fighters, 768 charges, 769 ice, 770 implants, 771 filaments, 772 otherCargoItems]: 773 for typeID, qty in eachDict.iteritems(): 774 subString = '%s;%s' % (typeID, qty) 775 retList.append(subString) 776 777 for typeID, qty in modules.iteritems(): 778 subString = '%s%s;%s' % (typeID, CARGO_MARKER, qty) 779 retList.append(subString) 780 781 ret = ':'.join(retList) 782 ret += '::' 783 return ret 784 785 def GetFittingFromString(self, fittingString, name = None): 786 effectSlots = {const.effectHiPower: const.flagHiSlot0, 787 const.effectMedPower: const.flagMedSlot0, 788 const.effectLoPower: const.flagLoSlot0, 789 const.effectRigSlot: const.flagRigSlot0, 790 const.effectSubSystem: const.flagSubSystemSlot0, 791 const.effectServiceSlot: const.flagServiceSlot0} 792 truncated = False 793 if not fittingString.endswith('::'): 794 truncated = True 795 fittingString = fittingString[:fittingString.rfind(':')] 796 data = fittingString.split(':') 797 fitting = utillib.KeyVal() 798 fitData = [] 799 for line in data: 800 typeInfo = line.split(';') 801 if line == '': 802 continue 803 if len(typeInfo) == 1: 804 try: 805 fitting.shipTypeID = int(typeInfo[0]) 806 except ValueError: 807 return (INVALID_FITTING_ID, truncated) 808 809 continue 810 typeID, qty = typeInfo 811 inCargo = False 812 if typeID.endswith(CARGO_MARKER): 813 typeID = typeID[:-1] 814 inCargo = True 815 try: 816 typeID, qty = int(typeID), int(qty) 817 except ValueError: 818 continue 819 820 powerEffectID = sm.GetService('godma').GetPowerEffectForType(typeID) 821 if powerEffectID is not None and not inCargo: 822 startSlot = effectSlots[powerEffectID] 823 for flag in xrange(startSlot, startSlot + qty): 824 fitData.append((typeID, flag, 1)) 825 826 effectSlots[powerEffectID] = flag + 1 827 else: 828 categoryID = evetypes.GetCategoryID(typeID) 829 groupID = evetypes.GetGroupID(typeID) 830 if categoryID == const.categoryDrone: 831 fitData.append((typeID, const.flagDroneBay, qty)) 832 if categoryID == const.categoryFighter: 833 fitData.append((typeID, const.flagFighterBay, qty)) 834 elif categoryID in (const.categoryCharge, 835 const.categoryImplant, 836 const.categoryModule, 837 const.categorySubSystem): 838 fitData.append((typeID, const.flagCargo, qty)) 839 elif groupID == const.groupIceProduct: 840 fitData.append((typeID, const.flagCargo, qty)) 841 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_FILAMENTS): 842 fitData.append((typeID, const.flagCargo, qty)) 843 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_OTHER_CARGO_HOLD_TYPES): 844 fitData.append((typeID, const.flagCargo, qty)) 845 else: 846 continue 847 848 shipName = name or evetypes.GetName(fitting.shipTypeID) 849 fitting.name = shipName 850 fitting.ownerID = None 851 fitting.fittingID = None 852 fitting.description = '' 853 fitting.fitData = fitData 854 return (fitting, truncated) 855 856 def GetFittingInfoScrollList(self, fitting): 857 scrollListData = self.GetFittingInfoForScrollList(fitting) 858 scrollList = [] 859 for d in scrollListData: 860 if isinstance(d, KeyVal): 861 entry = GetFromClass(FittingModuleEntry, d) 862 scrollList.append(entry) 863 else: 864 scrollList.append(d) 865 866 return scrollList 867 868 def GetFittingInfoForScrollList(self, fitting): 869 scrollListData = [] 870 typesByRack = self.GetTypesByRack(fitting) 871 for key, effectID in [('hiSlots', const.effectHiPower), 872 ('medSlots', const.effectMedPower), 873 ('lowSlots', const.effectLoPower), 874 ('rigSlots', const.effectRigSlot), 875 ('subSystems', const.effectSubSystem), 876 ('serviceSlots', const.effectServiceSlot)]: 877 slots = typesByRack[key] 878 if len(slots) < 1: 879 continue 880 label = dogma.data.get_effect_display_name(effectID) 881 scrollListData.append(GetFromClass(Header, {'label': label})) 882 dataForEffect = [] 883 for typeID, qty in slots.iteritems(): 884 data = self._GetDataForFittingEntry(typeID, qty) 885 data.singleton = 1 886 data.effectID = effectID 887 dataForEffect.append((evetypes.GetGroupID(typeID), data)) 888 889 dataForEffect = SortListOfTuples(dataForEffect) 890 scrollListData.extend(dataForEffect) 891 892 for qtyByTypeIdDict, headerLabelPath, isValidGroup in ((typesByRack['charges'], 'UI/Generic/Charges', True), 893 (typesByRack['ice'], 'UI/Inflight/MoonMining/Processes/Fuel', True), 894 (typesByRack['drones'], 'UI/Drones/Drones', True), 895 (typesByRack['fighters'], 'UI/Common/Fighters', True), 896 (typesByRack['implants'], 'UI/Fitting/FittingWindow/ImplantsAndBoosters', True), 897 (typesByRack['modules'], 'UI/Fitting/FittingWindow/FittingManagement/Modules', True), 898 (typesByRack['filaments'], 'UI/Fitting/FittingWindow/FittingManagement/Filaments', True), 899 (typesByRack['otherInCargo'], 'UI/Fitting/FittingWindow/FittingManagement/OtherItemsInCargo', True), 900 (typesByRack['other'], 'UI/Fitting/FittingWindow/FittingManagement/OtherItems', False)): 901 if len(qtyByTypeIdDict) > 0: 902 scrollListData.append(GetFromClass(Header, {'label': localization.GetByLabel(headerLabelPath)})) 903 scrollListData += self._GetFittingEntryDataForGroup(qtyByTypeIdDict, isValidGroup) 904 905 return scrollListData 906 907 def _GetFittingEntryDataForGroup(self, qtyByTypeIdDict, isValidGroup): 908 dataList = [] 909 for typeID, qty in qtyByTypeIdDict.iteritems(): 910 data = self._GetDataForFittingEntry(typeID, qty, isValidGroup) 911 dataList.append(data) 912 913 return dataList 914 915 def _GetDataForFittingEntry(self, typeID, qty, isValidGroup = True): 916 isObsolete = bool(sm.GetService('clientDogmaStaticSvc').GetTypeAttribute(typeID, const.attributeModuleIsObsolete)) 917 data = utillib.KeyVal(typeID=typeID, showinfo=1, getIcon=True, label=unicode(FmtAmt(qty)) + 'x ' + evetypes.GetName(typeID), isValidGroup=isValidGroup, isObsolete=isObsolete) 918 return data 919 920 def GetTypesByRack(self, fitting): 921 ret = {'hiSlots': defaultdict(int), 922 'medSlots': defaultdict(int), 923 'lowSlots': defaultdict(int), 924 'rigSlots': defaultdict(int), 925 'subSystems': defaultdict(int), 926 'serviceSlots': defaultdict(int), 927 'charges': {}, 928 'drones': {}, 929 'ice': {}, 930 'fighters': {}, 931 'implants': {}, 932 'modules': {}, 933 'filaments': {}, 934 'otherInCargo': {}, 935 'other': {}} 936 for typeID, flag, qty in fitting.fitData: 937 if evetypes.GetCategoryID(typeID) == const.categoryCharge: 938 ret['charges'][typeID] = qty 939 elif evetypes.GetCategoryID(typeID) == const.categoryImplant: 940 ret['implants'][typeID] = qty 941 elif evetypes.GetGroupID(typeID) == const.groupIceProduct: 942 ret['ice'][typeID] = qty 943 elif flag in const.hiSlotFlags: 944 ret['hiSlots'][typeID] += 1 945 elif flag in const.medSlotFlags: 946 ret['medSlots'][typeID] += 1 947 elif flag in const.loSlotFlags: 948 ret['lowSlots'][typeID] += 1 949 elif flag in const.rigSlotFlags: 950 ret['rigSlots'][typeID] += 1 951 elif const.flagServiceSlot0 <= flag <= const.flagServiceSlot7: 952 ret['serviceSlots'][typeID] += 1 953 elif IsSubsystemFlagVisible(flag): 954 ret['subSystems'][typeID] += 1 955 elif flag == const.flagDroneBay: 956 ret['drones'][typeID] = qty 957 elif flag == const.flagFighterBay: 958 ret['fighters'][typeID] = qty 959 elif evetypes.GetCategoryID(typeID) in (const.categoryModule, const.categorySubSystem) and flag == const.flagCargo: 960 ret['modules'][typeID] = qty 961 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_FILAMENTS): 962 ret['filaments'][typeID] = qty 963 elif typeID in evetypes.GetTypeIDsByListID(TYPE_LIST_OTHER_CARGO_HOLD_TYPES): 964 ret['otherInCargo'][typeID] = qty 965 else: 966 ret['other'][typeID] = qty 967 968 return ret 969 970 def HasSkillForFit(self, fitting): 971 fittingID = fitting.fittingID 972 try: 973 return self.hasSkillByFittingID[fittingID] 974 except KeyError: 975 self.LogInfo('HasSkillForFit::Cache miss', fittingID) 976 sys.exc_clear() 977 978 hasSkill = self.hasSkillByFittingID[fittingID] = self.CheckSkillRequirementsForFit(fitting) 979 return hasSkill 980 981 def CheckSkillRequirementsForFit(self, fitting): 982 godma = sm.GetService('godma') 983 if not godma.CheckSkillRequirementsForType(fitting.shipTypeID): 984 return False 985 for typeID, flag, qty in fitting.fitData: 986 if flag in inventorycommon.const.rigSlotFlags: 987 continue 988 if not godma.CheckSkillRequirementsForType(typeID): 989 return False 990 991 return True 992 993 def GetAllFittings(self): 994 ret = {} 995 charFittings = self.GetFittings(session.charid) 996 corpFittings = self.GetFittings(session.corpid) 997 for fittingID in charFittings: 998 ret[fittingID] = charFittings[fittingID] 999 1000 for fittingID in corpFittings: 1001 ret[fittingID] = corpFittings[fittingID] 1002 1003 if session.allianceid: 1004 allianceFittings = self.GetFittings(session.allianceid) 1005 for fittingID, fitting in allianceFittings.iteritems(): 1006 ret[fittingID] = fitting 1007 1008 return ret 1009 1010 def OnSkillsChanged(self, *args): 1011 self.hasSkillByFittingID = {} 1012 1013 def OnFittingAdded(self, ownerID, fitID): 1014 if ownerID in self.fittings: 1015 deleteFits = False 1016 if isinstance(fitID, (int, long)): 1017 if fitID not in self.fittings[ownerID]: 1018 deleteFits = True 1019 elif isinstance(fitID, list): 1020 if any((x in self.fittings[ownerID] for x in fitID)): 1021 deleteFits = True 1022 else: 1023 raise RuntimeError("fitID should always be an int, long, or list. It wasn't. fitID = {} and type(fitID) = {}".format(fitID, type(fitID))) 1024 if deleteFits is True: 1025 del self.fittings[ownerID] 1026 self.UpdateFittingWindow() 1027 sm.ScatterEvent('OnFittingsUpdated') 1028 1029 def OnFittingDeleted(self, ownerID, fitID): 1030 if ownerID in self.fittings: 1031 removed = self._OnFittingDeleted(ownerID, fitID) 1032 if removed: 1033 self.UpdateFittingWindow() 1034 sm.ScatterEvent('OnFittingsUpdated') 1035 1036 def _OnFittingDeleted(self, ownerID, fitID): 1037 if fitID in self.fittings[ownerID]: 1038 del self.fittings[ownerID][fitID] 1039 return True 1040 return False 1041 1042 def OnManyFittingsDeleted(self, ownerID, fitIDs): 1043 if ownerID not in self.fittings: 1044 return 1045 numRemoved = 0 1046 for fitID in fitIDs: 1047 removed = self._OnFittingDeleted(ownerID, fitID) 1048 numRemoved += removed 1049 1050 if numRemoved: 1051 self.UpdateFittingWindow() 1052 sm.ScatterEvent('OnFittingsUpdated') 1053 1054 def OnFittingsUpdated(self, fitting = None): 1055 if not self.IsShipSimulated() or fitting is None: 1056 return 1057 shipTypeID, fittingDataInWnd, allItems = self.GetFittingDictForSimulatedShip(putModuleAmmoInCargo=True) 1058 if fitting.shipTypeID != shipTypeID: 1059 return 1060 savedData = sorted(fitting.fitData) 1061 currentData = sorted(fittingDataInWnd) 1062 if savedData == currentData: 1063 sm.GetService('ghostFittingSvc').ResetSimulationChangedFlag() 1064 1065 def ImportFittingFromClipboard(self, simulate = False, *args): 1066 try: 1067 textInField = GetClipboardData() 1068 shipName, fitName = FindShipAndFittingName(textInField) 1069 importFittingUtil = self.GetImportFittingUtil() 1070 shipTypeID = importFittingUtil.GetTypeIDFromName(shipName.lower()) 1071 if not shipTypeID: 1072 prompt_player_fsd_dialog('ImportingErrorFromClipboard') 1073 return 1074 itemLines, errorLines = importFittingUtil.GetAllItems(textInField) 1075 fitData = importFittingUtil.CreateFittingData(itemLines, shipTypeID) 1076 if not fitData: 1077 prompt_player_fsd_dialog('ImportingErrorFromClipboard') 1078 return 1079 fittingData = utillib.KeyVal(shipTypeID=shipTypeID, fitData=fitData, fittingID=None, description='', name=fitName, ownerID=0) 1080 if simulate: 1081 sm.GetService('ghostFittingSvc').SimulateFitting(fittingData) 1082 else: 1083 self.DisplayFitting(fittingData) 1084 if errorLines: 1085 errorText = '<br>'.join(errorLines) 1086 text = '<b>%s</b><br>%s' % (localization.GetByLabel('UI/SkillQueue/CouldNotReadLines'), errorText) 1087 prompt_player_fsd_dialog('CustomInfo', {'info': text}) 1088 except Exception as e: 1089 log.LogWarn('Failed to import fitting from clipboard, e = ', e) 1090 prompt_player_fsd_dialog('ImportingErrorFromClipboard') 1091 1092 def GetImportFittingUtil(self): 1093 if self.importFittingUtil is None: 1094 self.importFittingUtil = ImportFittingUtil(dogma.data.get_all_type_effects(), sm.GetService('clientDogmaStaticSvc'), IsUsingDefaultLanguage(session)) 1095 return self.importFittingUtil 1096 1097 def ExportFittingToClipboard(self, fitting, isLocalized = False): 1098 fittingString = GetFittingEFTString(fitting, isLocalized=isLocalized) 1099 blue.pyos.SetClipboardData(fittingString) 1100 1101 def SaveFitting(self, *args): 1102 return self.DoSaveFitting() 1103 1104 def DoSaveFitting(self, name = None): 1105 fitting = self.GetFittingForCurrentInWnd() 1106 if name: 1107 fitting.name = name 1108 windowID = 'Save_ViewFitting_%s' % fitting.shipTypeID 1109 wnd = ViewFitting.GetIfOpen(windowID=windowID) 1110 if wnd and not wnd.destroyed: 1111 wnd.ReloadWnd(windowID, fitting, truncated=None) 1112 wnd.Maximize() 1113 else: 1114 ViewFitting.Open(windowID=windowID, fitting=fitting, truncated=None) 1115 1116 def GetFittingForCurrentInWnd(self, putModuleAmmoInCargo = True): 1117 fitting = KeyVal() 1118 fitting.shipTypeID, fitting.fitData, _ = self.GetFittingDictForCurrentFittingWindowShip(putModuleAmmoInCargo=putModuleAmmoInCargo) 1119 ghostFittingSvc = sm.GetService('ghostFittingSvc') 1120 desc = '' 1121 if self.inSimulation: 1122 ownerID = ghostFittingSvc.lastLoadedFitInfo[0] 1123 fittingID = ghostFittingSvc.lastLoadedFitInfo[1] 1124 if ownerID and fittingID: 1125 originalFit = self.GetFitting(ownerID, fittingID) 1126 if originalFit: 1127 desc = originalFit['description'] 1128 fitting.fittingID = None 1129 fitting.description = desc 1130 if self.inSimulation: 1131 shipName = ghostFittingSvc.GetShipName() 1132 else: 1133 shipName = cfg.evelocations.Get(GetActiveShip()).locationName 1134 fitting.name = shipName 1135 fitting.ownerID = 0 1136 return fitting 1137 1138 def GetShipIDForFittingWindow(self): 1139 if self.IsShipSimulated(): 1140 fittingDL = self.GetCurrentDogmaLocation() 1141 return fittingDL.GetCurrentShipID() 1142 return eveCfg.GetActiveShip() 1143 1144 def GetCurrentDogmaLocation(self): 1145 if self.IsShipSimulated(): 1146 return sm.GetService('clientDogmaIM').GetFittingDogmaLocation() 1147 else: 1148 return sm.GetService('clientDogmaIM').GetDogmaLocation() 1149 1150 def IsShipSimulated(self): 1151 return self.inSimulation 1152 1153 def SetSimulationState(self, simulationOn = False): 1154 ghostFittingController = sm.GetService('ghostFittingSvc').GetGhostFittingController() 1155 if ghostFittingController: 1156 ghostFittingController.SetSimulationState(simulationOn) 1157 self.inSimulation = simulationOn 1158 1159 def RemoveAndLoadChargesFromSimulatedShip(self, clientDL, actualShipItemID, simulatedChargeTypesAndQtyByFlagID): 1160 actualShipInv = sm.GetService('invCache').GetInventoryFromId(actualShipItemID) 1161 visibleFittedItems = [ i for i in actualShipInv.List() if inventorycommon.ItemIsVisible(i) ] 1162 actualShip_chargeTypesAndQtyByFlagID = self.GetChargesAndQtyByFlag(visibleFittedItems) 1163 if actualShip_chargeTypesAndQtyByFlagID == simulatedChargeTypesAndQtyByFlagID: 1164 return {} 1165 moduleIDs = {i.itemID for i in visibleFittedItems if i.flagID in inventorycommon.const.moduleSlotFlags and i.categoryID != const.categoryCharge} 1166 moduleIDs = {i for i in moduleIDs if not clientDL.IsModuleSlave(i, actualShipItemID)} 1167 clientDL.UnloadAmmoFromModules(actualShipItemID, moduleIDs, (session.structureid or session.stationid, session.charid, const.flagHangar)) 1168 blue.pyos.synchro.Yield() 1169 return self.FitSimulatedCharges(clientDL, simulatedChargeTypesAndQtyByFlagID) 1170 1171 def FitSimulatedCharges(self, clientDL, simulated_chargeTypesAndQtyByFlagID): 1172 fitted = clientDL.GetShip().GetFittedItems() 1173 fittedByFlagID = {x.flagID:x for x in fitted.itervalues() if x.categoryID != const.categoryCharge} 1174 hangar = sm.GetService('invCache').GetInventory(const.containerHangar) 1175 invItemsByTypeIDs = defaultdict(list) 1176 for x in hangar.List(const.flagHangar): 1177 invItemsByTypeIDs[x.typeID].append(x) 1178 1179 ammoFailedToLoad = defaultdict(int) 1180 for flagID, chargeInfo in simulated_chargeTypesAndQtyByFlagID.iteritems(): 1181 chargeTypeID, qty = chargeInfo 1182 moduleItem = fittedByFlagID.get(flagID, None) 1183 if not moduleItem: 1184 continue 1185 invItemsForType = invItemsByTypeIDs.get(chargeTypeID, None) 1186 if not invItemsForType: 1187 ammoFailedToLoad[chargeTypeID] += qty 1188 continue 1189 try: 1190 moduleID = moduleItem.itemID 1191 if moduleID < invConst.flagSlotLast: 1192 log.LogTraceback('FitSimulatedCharges: ModuleID invalid') 1193 clientDL.LoadChargesToModule(moduleID, invItemsForType, invItemsForType[0].locationID) 1194 except UserError as e: 1195 if e.msg == 'CannotLoadNotEnoughCharges': 1196 ammoFailedToLoad[chargeTypeID] += qty 1197 else: 1198 raise 1199 1200 return ammoFailedToLoad 1201 1202 def GetChargesAndQtyByFlag(self, fittedItems): 1203 chargeTypesAndQtyByFlagID = defaultdict(int) 1204 for item in fittedItems: 1205 flagID = item.flagID 1206 if item.categoryID == const.categoryCharge and flagID in const.moduleSlotFlags: 1207 stacksize = getattr(item, 'stacksize', 1) 1208 chargeTypesAndQtyByFlagID[flagID] = (item.typeID, stacksize) 1209 1210 return chargeTypesAndQtyByFlagID 1211 1212 def OnGlobalConfigChanged(self, configVals): 1213 if not AreCommunityFittingsEnabled(sm.GetService('machoNet')): 1214 self.ClearCommunityFittingCache() 1215 1216 def OnCommunityFittingsUpdated(self): 1217 self.ClearCommunityFittingCache() 1218 1219 def ClearCommunityFittingCache(self): 1220 if COMMUNITY_FITTING_CORP in self.fittings: 1221 self.fittings.pop(COMMUNITY_FITTING_CORP) 1222 sm.StartService('objectCaching').InvalidateCachedMethodCall('corpFittingMgr', 'GetCommunityFittings')