From f2c2e2e65a7aebc2f1c66fba3a166a31a296e8bd Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Sat, 25 Aug 2018 19:27:03 +0300 Subject: [PATCH] Implement mutated attributes import --- eos/db/gamedata/queries.py | 15 ++++ service/eftPort.py | 142 +++++++++++++++++++++++++++++-------- service/port.py | 1 - 3 files changed, 126 insertions(+), 32 deletions(-) diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index 90e757788..c6ee5c70b 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -396,6 +396,21 @@ def getAbyssalTypes(): return set([r.resultingTypeID for r in gamedata_session.query(DynamicItem.resultingTypeID).distinct()]) +@cachedQuery(1, "itemID") +def getDynamicItem(itemID, eager=None): + try: + if isinstance(itemID, int): + if eager is None: + result = gamedata_session.query(DynamicItem).filter(DynamicItem.ID == itemID).one() + else: + result = gamedata_session.query(DynamicItem).options(*processEager(eager)).filter(DynamicItem.ID == itemID).one() + else: + raise TypeError("Need integer as argument") + except exc.NoResultFound: + result = None + return result + + def getRequiredFor(itemID, attrMapping): Attribute1 = aliased(Attribute) Attribute2 = aliased(Attribute) diff --git a/service/eftPort.py b/service/eftPort.py index ca7cc1be6..c5b1da145 100644 --- a/service/eftPort.py +++ b/service/eftPort.py @@ -22,7 +22,7 @@ import re from logbook import Logger -from eos.db.gamedata.queries import getAttributeInfo +from eos.db.gamedata.queries import getAttributeInfo, getDynamicItem from eos.saveddata.cargo import Cargo from eos.saveddata.citadel import Citadel from eos.saveddata.booster import Booster @@ -44,7 +44,7 @@ SLOT_ORDER = (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERV OFFLINE_SUFFIX = ' /OFFLINE' -def fetchItem(typeName, eagerCat=True): +def fetchItem(typeName, eagerCat=False): sMkt = Market.getInstance() eager = 'group.category' if eagerCat else None try: @@ -150,7 +150,7 @@ class RegularItemSpec(BaseItemSpec): def __fetchCharge(self, chargeName): if chargeName: charge = fetchItem(chargeName, eagerCat=True) - if charge.category.name != 'Charge': + if not charge or charge.category.name != 'Charge': charge = None else: charge = None @@ -203,6 +203,8 @@ class AbstractFit: self.drones = {} # Format: {item: Drone} self.fighters = [] self.cargo = {} # Format: {item: Cargo} + # Other stuff + self.mutations = {} # Format: {reference: (mutaplamid item, {attr ID: attr value})} @property def __slotContainerMap(self): @@ -259,10 +261,28 @@ class AbstractFit: self.getContainerBySlot(m.slot).append(m) def __makeModule(self, itemSpec): - try: - m = Module(itemSpec.item) - except ValueError: - return None + # Mutate item if needed + m = None + if itemSpec.mutationIdx in self.mutations: + mutaItem, mutaAttrs = self.mutations[itemSpec.mutationIdx] + mutaplasmid = getDynamicItem(mutaItem.ID) + if mutaplasmid: + try: + m = Module(mutaplasmid.resultingItem, itemSpec.item, mutaplasmid) + except ValueError: + pass + else: + for attrID, mutator in m.mutators.items(): + if attrID in mutaAttrs: + mutator.value = mutaAttrs[attrID] + # If we still don't have item (item is not mutated or we + # failed to construct mutated item), try to make regular item + if m is None: + try: + m = Module(itemSpec.item) + except ValueError: + return None + if itemSpec.charge is not None and m.isValidCharge(itemSpec.charge): m.charge = itemSpec.charge if itemSpec.offline and m.isValidState(State.OFFLINE): @@ -425,10 +445,13 @@ class EftPort: return aFit = AbstractFit() + aFit.mutations = cls.__getMutationData(lines) + pyfalog.error('{}'.format(aFit.mutations)) + nameChars = '[^,/\[\]]' # Characters which are allowed to be used in name stubPattern = '^\[.+\]$' - modulePattern = '^(?P[^,/]+)(, (?P[^,/]+))?(?P{})?( \[(?P\d+)\])?$'.format(OFFLINE_SUFFIX) - droneCargoPattern = '^(?P[^,/]+) x(?P\d+)$' + modulePattern = '^(?P{0}+)(, (?P{0}+))?(?P{1})?( \[(?P\d+)\])?$'.format(nameChars, OFFLINE_SUFFIX) + droneCargoPattern = '^(?P{}+) x(?P\d+)$'.format(nameChars) sections = [] for section in cls.__importSectionIter(lines): @@ -467,6 +490,7 @@ class EftPort: clearTail(section.itemSpecs) sections.append(section) + hasDroneBay = any(s.isDroneBay for s in sections) hasFighterBay = any(s.isFighterBay for s in sections) for section in sections: @@ -555,28 +579,61 @@ class EftPort: del lines[-1] return lines - @classmethod - def __createFit(cls, lines): - """Create fit and set top-level entity (ship or citadel).""" - fit = Fit() - header = lines.pop(0) - m = re.match('\[(?P[\w\s]+), (?P.+)\]', header) - if not m: - pyfalog.warning('EftPort.importEft: corrupted fit header') - raise EftImportError - shipType = m.group('shipType').strip() - fitName = m.group('fitName').strip() - try: - ship = fetchItem(shipType, eagerCat=False) - try: - fit.ship = Ship(ship) - except ValueError: - fit.ship = Citadel(ship) - fit.name = fitName - except: - pyfalog.warning('EftPort.importEft: exception caught when parsing header') - raise EftImportError - return fit + @staticmethod + def __getMutationData(lines): + data = {} + consumedIndices = set() + for i in range(len(lines)): + line = lines[i] + m = re.match('^\[(?P\d+)\]', line) + if m: + ref = int(m.group('ref')) + # Attempt to apply mutation is useless w/o mutaplasmid, so skip it + # altogether if we have no info on it + try: + mutaName = lines[i + 1] + except IndexError: + continue + else: + consumedIndices.add(i) + consumedIndices.add(i + 1) + # Get custom attribute values + mutaAttrs = {} + try: + mutaAttrsLine = lines[i + 2] + except IndexError: + pass + else: + consumedIndices.add(i + 2) + pairs = [p.strip() for p in mutaAttrsLine.split(',')] + for pair in pairs: + try: + attrName, value = pair.split(' ') + except ValueError: + continue + try: + value = float(value) + except (ValueError, TypeError): + continue + attrInfo = getAttributeInfo(attrName.strip()) + if attrInfo is None: + continue + mutaAttrs[attrInfo.ID] = value + mutaItem = fetchItem(mutaName) + if mutaItem is None: + continue + data[ref] = (mutaItem, mutaAttrs) + # If we got here, we have seen at least correct reference line and + # mutaplasmid name line + i += 2 + # Bonus points for seeing correct attrs line. Worst case we + # will have to scan it once again + if mutaAttrs: + i += 1 + # Cleanup the lines from mutaplasmid info + for i in sorted(consumedIndices, reverse=True): + del lines[i] + return data @staticmethod def __importSectionIter(lines): @@ -590,3 +647,26 @@ class EftPort: section.lines.append(line) if section.lines: yield section + + @classmethod + def __createFit(cls, lines): + """Create fit and set top-level entity (ship or citadel).""" + fit = Fit() + header = lines.pop(0) + m = re.match('\[(?P[\w\s]+), (?P.+)\]', header) + if not m: + pyfalog.warning('EftPort.importEft: corrupted fit header') + raise EftImportError + shipType = m.group('shipType').strip() + fitName = m.group('fitName').strip() + try: + ship = fetchItem(shipType) + try: + fit.ship = Ship(ship) + except ValueError: + fit.ship = Citadel(ship) + fit.name = fitName + except: + pyfalog.warning('EftPort.importEft: exception caught when parsing header') + raise EftImportError + return fit diff --git a/service/port.py b/service/port.py index bf1ff4f90..1337825c8 100644 --- a/service/port.py +++ b/service/port.py @@ -908,7 +908,6 @@ class Port(object): return fit_list - @classmethod def exportEft(cls, fit): return EftPort.exportEft(fit, mutations=False, implants=False)