NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

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}