menu_charge.py (21992B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\devtools\script\menu_charge.py 2 import operator 3 import evetypes 4 import uthread 5 import blue 6 from carbon.common.script.sys.service import Service 7 from eve.client.script.ui.util import uix 8 import carbonui.const as uiconst 9 from carbonui.uicore import uicore 10 from eve.common.lib import appConst as const 11 from eve.common.script.sys import eveCfg 12 from functools import reduce 13 14 class ChargeService(Service): 15 __module__ = __name__ 16 __doc__ = 'Insider Charge' 17 __guid__ = 'svc.charge' 18 __servicename__ = 'charge' 19 __displayname__ = 'Insider Charges and Drone Service' 20 __dependencies__ = ['invCache'] 21 22 def NA(self, name): 23 return '<color=0x60ffffff>' + name + ' <color=0xffff8080>[N/A]' 24 25 def Name(self, rec): 26 if rec.published: 27 return evetypes.GetName(rec.typeID, languageID='en-us') 28 else: 29 return self.NA(evetypes.GetName(rec.typeID, languageID='en-us')) 30 31 def FormattedName(self, name, published): 32 if published: 33 return name 34 else: 35 return self.NA(name) 36 37 def FitDrone(self, drone): 38 if uicore.uilib.Key(uiconst.VK_SHIFT): 39 result = uix.QtyPopup(maxvalue=100, minvalue=1, caption=self.Name(drone), label=u'Quantity', hint='') 40 if result: 41 qty = result['qty'] 42 else: 43 return 44 else: 45 qty = 1 46 sm.RemoteSvc('slash').SlashCmd('/fit me %d %d' % (drone.typeID, qty)) 47 48 def Fit(self, charges): 49 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 50 chargeByFlag = {} 51 shipID = eveCfg.GetActiveShip() 52 shipInv = self.invCache.GetInventoryFromId(shipID) 53 for dogmaItem in dogmaLocation.GetDogmaItem(shipID).GetFittedItems().itervalues(): 54 if dogmaLocation.IsModuleSlave(dogmaItem.itemID, session.shipid): 55 continue 56 desiredSize = dogmaLocation.dogmaStaticMgr.GetTypeAttribute(dogmaItem.typeID, const.attributeChargeSize) 57 capacity = dogmaLocation.GetAttributeValue(dogmaItem.itemID, const.attributeCapacity) 58 for chargeGroupAttributeID in dogmaLocation.dogmaStaticMgr.chargeGroupAttributes: 59 if not dogmaLocation.dogmaStaticMgr.TypeHasAttribute(dogmaItem.typeID, chargeGroupAttributeID): 60 continue 61 chargeGroup = dogmaLocation.GetAttributeValue(dogmaItem.itemID, chargeGroupAttributeID) 62 for a in charges: 63 if a.groupID != chargeGroup: 64 continue 65 if a.volume > capacity: 66 continue 67 if desiredSize is not None and getattr(a, 'chargeSize', -1) != desiredSize: 68 continue 69 slavedModules = dogmaLocation.GetSlaveModules(dogmaItem.itemID, session.shipid) or [] 70 chargeByFlag[dogmaItem.flagID] = [a, int(capacity / a.volume) * (len(slavedModules) + 1)] 71 break 72 else: 73 continue 74 75 break 76 77 tasks = {flagID:(None, '/fit me %d %d flag=%d' % (typeObj.typeID, qtyNeeded, flagID)) for flagID, (typeObj, qtyNeeded) in chargeByFlag.iteritems()} 78 for dogmaItem in dogmaLocation.GetDogmaItem(shipID).GetFittedItems().itervalues(): 79 flagID = dogmaItem.flagID 80 if flagID not in chargeByFlag: 81 continue 82 if dogmaItem.categoryID != const.categoryCharge: 83 continue 84 a, quantityNeeded = chargeByFlag[flagID] 85 if isinstance(dogmaItem.itemID, tuple): 86 if dogmaItem.typeID != a.typeID: 87 tasks[flagID] = [flagID] 88 else: 89 quantityNeeded -= dogmaLocation.GetAttributeValue(dogmaItem.itemID, const.attributeQuantity) 90 tasks[flagID] = [None] 91 else: 92 tasks[flagID] = [flagID] 93 if quantityNeeded > 0: 94 tasks[flagID].append('/fit me %d %d flag=%d' % (a.typeID, quantityNeeded, flagID)) 95 else: 96 tasks[flagID].append(None) 97 98 def _change(removeFlag, addCmd): 99 if removeFlag: 100 shipInv.ClearCharges(removeFlag) 101 count = 100 102 while dogmaLocation.GetSubLocation(eveCfg.GetActiveShip(), removeFlag): 103 blue.pyos.synchro.SleepSim(50) 104 count -= 1 105 if not count: 106 raise RuntimeError('Dogma is sleeping on the job, or something worse happened!') 107 108 if addCmd: 109 sm.RemoteSvc('slash').SlashCmd(addCmd) 110 111 uthread.parallel([ (_change, (flag, cmd)) for flag, cmd in tasks.itervalues() ]) 112 113 def Entry(self, a): 114 return (self.Name(a), self.Fit, ([a],)) 115 116 def DroneMenu(self, *args, **kw): 117 return self._Menu(drones=True) 118 119 def ChargeMenu(self, *args, **kw): 120 return self._Menu() 121 122 def _Menu(self, drones = False): 123 dgm = sm.GetService('godma').GetStateManager() 124 chargesByChargeGroup = {} 125 chargesByLauncherGroup = {} 126 missileLauncherGroups = {} 127 damageTypes = {'1000': ('EM', 'ui_22_32_12', 0.125), 128 '0100': ('Explosive', 'ui_22_32_11', 0.125), 129 '0010': ('Kinetic', 'ui_22_32_9', 0.125), 130 '0001': ('Thermal', 'ui_22_32_10', 0.125), 131 -1: ('Other', 'ui_7_64_15')} 132 for groupID in evetypes.GetGroupIDsByCategories([const.categoryCharge, const.categoryDrone]): 133 chargesByChargeGroup[groupID] = [] 134 135 for typeID in evetypes.GetTypeIDsByGroups(chargesByChargeGroup.keys()): 136 chargesByChargeGroup[evetypes.GetGroupID(typeID)].append(typeID) 137 138 for items in chargesByChargeGroup.itervalues(): 139 if items: 140 typeID = items[0] 141 a = dgm.GetType(typeID) 142 if a.launcherGroup > 1: 143 chargesByLauncherGroup[a.launcherGroup] = items 144 if a.launcherGroup == const.groupMissileLauncherStandard: 145 chargesByLauncherGroup[const.groupMissileLauncherAssault] = items 146 missileLauncherGroups[const.groupMissileLauncherAssault] = True 147 if const.typeMissileLauncherOperation in (getattr(a, 'requiredSkill1', False), getattr(a, 'requiredSkill2', False)): 148 missileLauncherGroups[a.launcherGroup] = True 149 150 def TurretSubMenu(baseName, charges): 151 m = [] 152 ammo = {} 153 damage = {} 154 for a in charges: 155 ammoType = ' '.join(self.Name(a).replace(baseName + ' ', '').split(' ')[:-1]).strip() or 'Standard' 156 if ammoType in ammo: 157 ammo[ammoType].append(a) 158 else: 159 ammo[ammoType] = [a] 160 damage[ammoType] = (a.emDamage + a.explosiveDamage + a.kineticDamage + a.thermalDamage) / (1 << a.chargeSize - 1) 161 162 ammo = [ (damage[key], key, charges) for key, charges in ammo.iteritems() ] 163 ammo.sort() 164 m.append(('<color=0xff708090>Base', None)) 165 m.append(None) 166 lastDamage = baseDamage = ammo[0][0] 167 for damage, ammoType, charges in ammo: 168 if damage != lastDamage: 169 m.append(None) 170 m.append(('<color=0xff708090>Base +%d%%' % int((damage / baseDamage - 1) * 100 + 0.5), None)) 171 m.append(None) 172 lastDamage = damage 173 m.append((ammoType, self.Fit, (charges,))) 174 175 return m 176 177 def TurretMenu(chargeGroups): 178 m = [] 179 chargeGroups = [ (evetypes.GetGroupNameByGroup(x), x) for x in chargeGroups ] 180 chargeGroups.sort() 181 for name, groupID in chargeGroups: 182 filtered = {} 183 rangeMod = {} 184 for typeID in chargesByChargeGroup[groupID]: 185 a = dgm.GetType(typeID) 186 if a.iconID in filtered: 187 filtered[a.iconID].append(a) 188 else: 189 filtered[a.iconID] = [a] 190 rangeMod[a.iconID] = getattr(a, 'weaponRangeMultiplier', 0.0) 191 192 grouped = {} 193 inconsistent = [] 194 while filtered: 195 iconID, items = filtered.popitem() 196 base = self.Name(items[0]).split(' ') 197 base.pop() 198 inconsistent = [] 199 for a in items[:]: 200 words = self.Name(a).split(' ') 201 newbase = base[:] 202 for word in base: 203 if word not in words: 204 newbase.remove(word) 205 206 if newbase: 207 base = newbase 208 else: 209 inconsistent.append(a) 210 items.remove(a) 211 212 baseName = ' '.join(base) 213 if baseName in grouped: 214 g = grouped[baseName] 215 else: 216 g = grouped[baseName] = [] 217 g.extend(items) 218 219 processed = [] 220 while grouped: 221 baseName, items = grouped.popitem() 222 for a in inconsistent: 223 if baseName in self.Name(a): 224 items.append(a) 225 226 items.sort() 227 for a in items: 228 if a.chargeSize == 2: 229 break 230 else: 231 for a in items: 232 if a.chargeSize == 1: 233 break 234 else: 235 a = items[0] 236 237 if not a.published: 238 baseName = self.NA(baseName) 239 if len(items) <= 4: 240 processed.append((rangeMod[a.iconID], (baseName, self.Fit, (items,)))) 241 else: 242 processed.append((rangeMod[a.iconID], (baseName, ('isDynamic', TurretSubMenu, (baseName, items))))) 243 244 processed.sort() 245 processed.reverse() 246 if len(processed) < 5: 247 m.append(('<color=0xff708090>%s' % evetypes.GetGroupNameByGroup(groupID), None)) 248 m.append(None) 249 m += zip(*processed)[1] 250 else: 251 m.append(('<color=0xff708090>Long Range', None)) 252 m.append(None) 253 m += zip(*processed)[1] 254 m.append(None) 255 m.append(('<color=0xff708090>High Damage', None)) 256 m.append(None) 257 258 return m 259 260 def MissileSubMenu(charges): 261 m = [] 262 charges.sort() 263 missiles = {} 264 for damage, a in charges: 265 if a.launcherGroup in missiles: 266 missiles[a.launcherGroup].append(a) 267 else: 268 missiles[a.launcherGroup] = [a] 269 270 for launcherGroup, items in missiles.iteritems(): 271 fitted = [] 272 label = '<color=0xff708090>%s' % evetypes.GetGroupNameByGroup(launcherGroup) 273 if launcherGroup == const.groupMissileLauncherStandard: 274 for row in self.invCache.GetInventoryFromId(eveCfg.GetActiveShip()).ListHardwareModules(): 275 if row.groupID in (const.groupMissileLauncherStandard, const.groupMissileLauncherAssault): 276 fitted.append(row.groupID) 277 278 if len(fitted) > 1: 279 label = '<color=0xff708090>%s / %s' % (evetypes.GetGroupNameByGroup(const.groupMissileLauncherStandard), evetypes.GetGroupNameByGroup(const.groupMissileLauncherAssault)) 280 else: 281 label = '<color=0xff708090>%s' % evetypes.GetGroupNameByGroup(fitted[0]) 282 m.append((label, None)) 283 m.append(None) 284 for a in items: 285 m.append(self.Entry(a)) 286 287 m.append(None) 288 289 return m 290 291 def MissileMenu(chargeGroups): 292 missiles = {} 293 for groupID in chargeGroups: 294 for typeID in chargesByChargeGroup[groupID]: 295 a = dgm.GetType(typeID) 296 if a.launcherGroup == const.groupMissileLauncher: 297 damageSpec = '0000' 298 else: 299 damageSpec = (a.emDamage, 300 a.explosiveDamage, 301 a.kineticDamage, 302 a.thermalDamage) 303 label = damageTypes.get(''.join(map(lambda x: str(int(not not x)), damageSpec)), None) 304 if label in missiles: 305 missiles[label].append((reduce(operator.__add__, damageSpec), a)) 306 else: 307 missiles[label] = [(reduce(operator.__add__, damageSpec), a)] 308 309 missiles = missiles.items() 310 missiles.sort() 311 multi = False 312 for label, items in missiles: 313 if label and len(items) > 1: 314 multi = True 315 316 m = [] 317 m.append(None) 318 entry = None 319 for label, items in missiles: 320 if label is None: 321 if len(items) == 1: 322 entry = self.Entry(items[0][1]) 323 else: 324 entry = (damageTypes[-1][0], ('isDynamic', MissileSubMenu, (items,))) 325 elif multi: 326 m.append((label[0], ('isDynamic', MissileSubMenu, (items,)))) 327 else: 328 m.append(self.Entry(items[0][1])) 329 330 if entry: 331 m.insert(0, None) 332 m.insert(0, entry) 333 return m 334 335 def GenericMenu(chargeGroups, maxVolumeByCG): 336 processed = [] 337 processedLength = 0 338 for groupID in chargeGroups: 339 maxVolume = maxVolumeByCG[groupID] 340 items = [ self.Entry(a) for a in map(dgm.GetType, chargesByChargeGroup[groupID]) if a.volume <= maxVolume ] 341 processed.append((groupID, items)) 342 processedLength += len(items) 343 344 m = [] 345 for groupID, items in processed: 346 m.append(('<color=0xff708090>%s' % evetypes.GetGroupNameByGroup(groupID), None)) 347 m.append(None) 348 m.extend(items) 349 m.append(None) 350 351 return m 352 353 def GetDroneDmgType(a): 354 damageSpec = (a.emDamage, 355 a.explosiveDamage, 356 a.kineticDamage, 357 a.thermalDamage) 358 return damageTypes.get(''.join(map(lambda x: str(int(not not x)), damageSpec)), None) 359 360 def DroneSubMenu(groupID): 361 m = [] 362 if len(chargesByChargeGroup[groupID]) > 16: 363 grouped = {} 364 for typeID in chargesByChargeGroup[groupID]: 365 a = dgm.GetType(typeID) 366 typ = GetDroneDmgType(a) 367 if typ in grouped: 368 grouped[typ].append(a) 369 else: 370 grouped[typ] = [a] 371 372 for label, items in grouped.items(): 373 d = {} 374 for a in items: 375 if a.marketGroupID in d: 376 d[a.marketGroupID].append(a) 377 else: 378 d[a.marketGroupID] = [a] 379 380 grouped[label] = mm = [] 381 if not label: 382 label = damageTypes[-1] 383 for name, id in [('Light Scout Drones', 837), 384 ('Medium Scout Drones', 838), 385 ('Heavy Attack Drones', 839), 386 ('Sentry Drones', 911)]: 387 if id in d: 388 mm.append(None) 389 for a in d[id]: 390 mm.append((self.Name(a), self.FitDrone, (a,))) 391 392 del d[id] 393 394 if d: 395 mm.append(None) 396 for id, items in d.iteritems(): 397 for a in items: 398 mm.append((self.Name(a), self.FitDrone, (a,))) 399 400 if None in grouped: 401 weird = grouped[None] 402 del grouped[None] 403 else: 404 weird = None 405 for label, items in grouped.items(): 406 m.append((label[0], items)) 407 408 m.sort() 409 m.append(None) 410 m.sort() 411 if weird: 412 m.append(None) 413 m.append((damageTypes[-1][0], weird)) 414 else: 415 for typeID in chargesByChargeGroup[groupID]: 416 a = dgm.GetType(typeID) 417 m.append((self.Name(a), self.FitDrone, (a,))) 418 m.sort() 419 420 ew300 = [] 421 ew600 = [] 422 ew900 = [] 423 light = [] 424 medium = [] 425 heavy = [] 426 lists = [ew300, 427 ew600, 428 ew900, 429 light, 430 medium, 431 heavy] 432 for entry in m: 433 try: 434 if entry[0][-3:] == '300': 435 ew300.append(entry) 436 elif entry[0][-3:] == '600': 437 ew600.append(entry) 438 elif entry[0][-3:] == '900': 439 if entry[0][-6:] != 'SW-900': 440 ew900.append(entry) 441 elif entry[0][:5] == 'Light': 442 light.append(entry) 443 elif entry[0][:6] == 'Medium': 444 medium.append(entry) 445 elif entry[0][:5] == 'Heavy': 446 heavy.append(entry) 447 except: 448 None 449 450 for val in lists: 451 val.sort() 452 453 if ew300: 454 m = ew300 455 if ew600: 456 m.append(None) 457 m += ew600 458 if ew900: 459 m.append(None) 460 m += ew900 461 if light: 462 m = light 463 if medium: 464 m.append(None) 465 m += medium 466 if heavy: 467 m.append(None) 468 m += heavy 469 return m 470 471 def DroneMenu(): 472 m = [] 473 for groupID in chargesByChargeGroup: 474 if evetypes.GetCategoryIDByGroup(groupID) == const.categoryDrone: 475 m.append((self.FormattedName(evetypes.GetGroupNameByGroup(groupID), evetypes.IsGroupPublishedByGroup(groupID)), ('isDynamic', DroneSubMenu, (groupID,)))) 476 477 m.sort() 478 return m 479 480 def GetMenu(): 481 m = [] 482 missileCG = {} 483 turretCGbyLG = {const.groupEnergyWeapon: {}, 484 const.groupHybridWeapon: {}, 485 const.groupProjectileWeapon: {}} 486 otherCGbyLG = {} 487 maxVolumeByCG = {} 488 iconIDs = {} 489 for row in self.invCache.GetInventoryFromId(eveCfg.GetActiveShip()).ListHardwareModules(): 490 groupID = row.groupID 491 if groupID in chargesByLauncherGroup: 492 a = dgm.GetType(row.typeID) 493 iconIDs[groupID] = a.iconID 494 chargeGroups = [] 495 for x in range(4): 496 chargeGroup = getattr(a, 'chargeGroup%d' % (x + 1), False) 497 if chargeGroup: 498 chargeGroups.append(chargeGroup) 499 500 if groupID in missileLauncherGroups: 501 iconIDs[const.groupMissileLauncher] = a.iconID 502 for cg in chargeGroups: 503 missileCG[cg] = True 504 505 elif groupID in turretCGbyLG: 506 for cg in chargeGroups: 507 turretCGbyLG[groupID][cg] = True 508 509 else: 510 if groupID not in otherCGbyLG: 511 otherCGbyLG[groupID] = {} 512 for cg in chargeGroups: 513 otherCGbyLG[groupID][cg] = True 514 maxVolumeByCG[cg] = max(maxVolumeByCG.get(cg, 0), a.capacity) 515 516 if missileCG: 517 m.append((evetypes.GetGroupNameByGroup(const.groupMissileLauncher), ('isDynamic', MissileMenu, (missileCG,)))) 518 for groupID in (const.groupEnergyWeapon, const.groupHybridWeapon, const.groupProjectileWeapon): 519 if turretCGbyLG[groupID]: 520 m.append((evetypes.GetGroupNameByGroup(groupID), ('isDynamic', TurretMenu, (turretCGbyLG[groupID],)))) 521 522 for groupID in otherCGbyLG: 523 if otherCGbyLG[groupID]: 524 m.append((evetypes.GetGroupNameByGroup(groupID), ('isDynamic', GenericMenu, (otherCGbyLG[groupID], maxVolumeByCG)))) 525 526 if m: 527 m.insert(0, None) 528 m.insert(0, ('<color=0xff708090>Module groups accepting charges', None)) 529 else: 530 m.insert(0, ('<color=0xff708090>No modules accepting charges', None)) 531 return m 532 533 if drones: 534 return DroneMenu() 535 else: 536 return GetMenu() 537 538 exports = {'insider.ChargeMenu': ChargeMenu, 539 'insider.DroneMenu': DroneMenu}