Example #1
0
 def build(self):
     from eos import db
     self.__extraDrains = []
     self.__ehp = None
     self.__weaponDPS = None
     self.__minerYield = None
     self.__weaponVolley = None
     self.__droneDPS = None
     self.__droneVolley = None
     self.__droneYield = None
     self.__sustainableTank = None
     self.__effectiveSustainableTank = None
     self.__effectiveTank = None
     self.__calculated = False
     self.__capStable = None
     self.__capState = None
     self.__capUsed = None
     self.__capRecharge = None
     self.__calculatedTargets = []
     self.factorReload = False
     self.fleet = None
     self.boostsFits = set()
     self.gangBoosts = None
     self.ecmProjectedStr = 1
     self.extraAttributes = ModifiedAttributeDict(self)
     self.extraAttributes.original = self.EXTRA_ATTRIBUTES
     self.ship = Ship(db.getItem(
         self.shipID)) if self.shipID is not None else None
     if self.ship is not None:
         self.mode = self.ship.checkModeItem(
             db.getItem(self.modeID) if self.modeID else None)
     else:
         self.mode = None
Example #2
0
    def importDna(string):
        sMkt = service.Market.getInstance()
        info = string.split(":")

        f = Fit()
        try:
            f.ship = Ship(sMkt.getItem(int(info[0])))
            f.name = "{0} - DNA Imported".format(f.ship.item.name)
        except UnicodeEncodeError as e:

            def logtransform(s):
                if len(s) > 10:
                    return s[:10] + "..."
                return s

            logger.exception("Couldn't import ship data %r",
                             [logtransform(s) for s in info])
            return None

        moduleList = []
        for itemInfo in info[1:]:
            if itemInfo:
                itemID, amount = itemInfo.split(";")
                item = sMkt.getItem(int(itemID), eager="group.category")

                if item.category.name == "Drone":
                    d = Drone(item)
                    d.amount = int(amount)
                    f.drones.append(d)
                elif item.category.name == "Charge":
                    c = Cargo(item)
                    c.amount = int(amount)
                    f.cargo.append(c)
                else:
                    for i in xrange(int(amount)):
                        try:
                            m = Module(item)
                        except:
                            continue
                        # Add subsystems before modules to make sure T3 cruisers have subsystems installed
                        if item.category.name == "Subsystem":
                            if m.fits(f):
                                f.modules.append(m)
                        else:
                            m.owner = f
                            if m.isValidState(State.ACTIVE):
                                m.state = State.ACTIVE
                            moduleList.append(m)

        # Recalc to get slot numbers correct for T3 cruisers
        service.Fit.getInstance().recalc(f)

        for module in moduleList:
            if module.fits(f):
                module.owner = f
                if module.isValidState(State.ACTIVE):
                    module.state = State.ACTIVE
                f.modules.append(module)

        return f
Example #3
0
    def importDna(string):
        sMkt = service.Market.getInstance()
        info = string.split(":")

        f = Fit()
        f.ship = Ship(sMkt.getItem(int(info[0])))
        f.name = "{0} - DNA Imported".format(f.ship.item.name)

        for itemInfo in info[1:]:
            if itemInfo:
                itemID, amount = itemInfo.split(";")
                item = sMkt.getItem(int(itemID), eager="group.category")

                if item.category.name == "Drone":
                    d = Drone(item)
                    d.amount = int(amount)
                    f.drones.append(d)
                elif item.category.name == "Charge":
                    c = Cargo(item)
                    c.amount = int(amount)
                    f.cargo.append(c)
                else:
                    for i in xrange(int(amount)):
                        try:
                            m = Module(item)
                            f.modules.append(m)
                        except:
                            pass
                        if m.isValidState(State.ACTIVE):
                            m.state = State.ACTIVE

        return f
Example #4
0
    def importDna(cls, string):
        from eos import db
        info = string.split(":")
        f = Fit()
        f.ship = Ship(db.getItem(int(info[0])))
        f.name = "{0} - DNA Imported".format(f.ship.item.name)
        for itemInfo in info[1:]:
            if itemInfo:
                itemID, amount = itemInfo.split(";")
                item = db.getItem(int(itemID), eager="group.category")

                if item.category.name == "Drone":
                    d = Drone(item)
                    d.amount = int(amount)
                    f.drones.append(d)
                elif item.category.name == "Charge":
                    for i in xrange(int(amount)):
                        for mod in f.modules:
                            if (mod.isValidCharge(item) and mod.charge == None):
                                mod.charge = item
                                break;
                else:
                    for i in xrange(int(amount)):
                        try:
                            m = Module(item)
                            f.modules.append(m)
                        except:
                            pass
                        if m.isValidState(State.ACTIVE):
                            m.state = State.ACTIVE

        return f
Example #5
0
File: fit.py Project: mickoes/Pyfa
 def build(self):
     from eos import db
     self.__extraDrains = []
     self.__ehp = None
     self.__weaponDPS = None
     self.__minerYield = None
     self.__weaponVolley = None
     self.__droneDPS = None
     self.__droneVolley = None
     self.__droneYield = None
     self.__sustainableTank = None
     self.__effectiveSustainableTank = None
     self.__effectiveTank = None
     self.__calculated = False
     self.__capStable = None
     self.__capState = None
     self.__capUsed = None
     self.__capRecharge = None
     self.__calculatedTargets = []
     self.factorReload = False
     self.fleet = None
     self.boostsFits = set()
     self.gangBoosts = None
     self.ecmProjectedStr = 1
     self.extraAttributes = ModifiedAttributeDict(self)
     self.extraAttributes.original = self.EXTRA_ATTRIBUTES
     self.ship = Ship(db.getItem(self.shipID)) if self.shipID is not None else None
     if self.ship is not None:
         self.mode = self.ship.checkModeItem(db.getItem(self.modeID) if self.modeID else None)
     else:
         self.mode = None
Example #6
0
    def init(self):
        """Initialize a fit from the database and validate"""
        self.__ship = None
        self.__mode = None

        if self.shipID:
            item = eos.db.getItem(self.shipID)
            if item is None:
                logger.error("Item (id: %d) does not exist", self.shipID)
                return

            try:
                self.__ship = Ship(item, self)
                # @todo extra attributes is now useless, however it set to be
                # the same as ship attributes for ease (so we don't have to
                # change all instances in source). Remove this at some point
                self.extraAttributes = self.__ship.itemModifiedAttributes
            except ValueError:
                logger.error("Item (id: %d) is not a Ship", self.shipID)
                return

        if self.modeID and self.__ship:
            item = eos.db.getItem(self.modeID)
            # Don't need to verify if it's a proper item, as validateModeItem assures this
            self.__mode = self.ship.validateModeItem(item)
        else:
            self.__mode = self.ship.validateModeItem(None)

        self.build()
Example #7
0
    def importXml(cls, text):
        doc = xml.dom.minidom.parseString(text.encode("utf-8"))
        fittings = doc.getElementsByTagName("fittings").item(0)
        fittings = fittings.getElementsByTagName("fitting")
        fits = []
        from eos import db
        for fitting in fittings:

                f = Fit()
                f.name = fitting.getAttribute("name")
                shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
                f.ship = Ship(db.getItem(shipType))
                hardwares = fitting.getElementsByTagName("hardware")
                for hardware in hardwares:
                    try:
                        moduleName = hardware.getAttribute("type")
                        item = db.getItem(moduleName, eager="group.category")
                        if item:
                            if item.category.name == "Drone":
                                d = Drone(item)
                                d.amount = int(hardware.getAttribute("qty"))
                                f.drones.append(d)
                            else:
                                m = Module(item)
                                if m.isValidState(State.ACTIVE):
                                    m.state = State.ACTIVE

                                f.modules.append(m)
                    except Exception:
                        continue

                fits.append(f)


        return fits
Example #8
0
    def importCrest(str):
        fit = json.loads(str)
        sMkt = service.Market.getInstance()

        f = Fit()
        f.name = fit['name']

        try:
            try:
                f.ship = Ship(sMkt.getItem(fit['ship']['id']))
            except ValueError:
                f.ship = Citadel(sMkt.getItem(fit['ship']['id']))
        except:
            return None

        items = fit['items']
        items.sort(key=lambda k: k['flag'])

        moduleList = []
        for module in items:
            try:
                item = sMkt.getItem(module['type']['id'],
                                    eager="group.category")
                if module['flag'] == INV_FLAG_DRONEBAY:
                    d = Drone(item)
                    d.amount = module['quantity']
                    f.drones.append(d)
                elif module['flag'] == INV_FLAG_CARGOBAY:
                    c = Cargo(item)
                    c.amount = module['quantity']
                    f.cargo.append(c)
                else:
                    try:
                        m = Module(item)
                    # When item can't be added to any slot (unknown item or just charge), ignore it
                    except ValueError:
                        continue
                    # Add subsystems before modules to make sure T3 cruisers have subsystems installed
                    if item.category.name == "Subsystem":
                        if m.fits(f):
                            f.modules.append(m)
                    else:
                        if m.isValidState(State.ACTIVE):
                            m.state = State.ACTIVE

                        moduleList.append(m)

            except:
                continue

        # Recalc to get slot numbers correct for T3 cruisers
        service.Fit.getInstance().recalc(f)

        for module in moduleList:
            if module.fits(f):
                f.modules.append(module)

        return f
Example #9
0
File: port.py Project: Blinker/Pyfa
    def importXml(text, callback=None, encoding="utf-8"):
        sMkt = service.Market.getInstance()

        doc = xml.dom.minidom.parseString(text.encode(encoding))
        fittings = doc.getElementsByTagName("fittings").item(0)
        fittings = fittings.getElementsByTagName("fitting")
        fits = []

        for i, fitting in enumerate(fittings):
            f = Fit()
            f.name = fitting.getAttribute("name")
            # <localized hint="Maelstrom">Maelstrom</localized>
            shipType = fitting.getElementsByTagName("shipType").item(
                0).getAttribute("value")
            try:
                f.ship = Ship(sMkt.getItem(shipType))
            except:
                continue
            hardwares = fitting.getElementsByTagName("hardware")
            for hardware in hardwares:
                try:
                    moduleName = hardware.getAttribute("type")
                    try:
                        item = sMkt.getItem(moduleName, eager="group.category")
                    except:
                        continue
                    if item:
                        if item.category.name == "Drone":
                            d = Drone(item)
                            d.amount = int(hardware.getAttribute("qty"))
                            f.drones.append(d)
                        elif hardware.getAttribute("slot").lower() == "cargo":
                            # although the eve client only support charges in cargo, third-party programs
                            # may support items or "refits" in cargo. Support these by blindly adding all
                            # cargo, not just charges
                            c = Cargo(item)
                            c.amount = int(hardware.getAttribute("qty"))
                            f.cargo.append(c)
                        else:
                            try:
                                m = Module(item)
                            # When item can't be added to any slot (unknown item or just charge), ignore it
                            except ValueError:
                                continue
                            if m.isValidState(State.ACTIVE):
                                m.state = State.ACTIVE

                            f.modules.append(m)
                except KeyboardInterrupt:
                    continue
            fits.append(f)
            if callback:
                wx.CallAfter(callback, None)

        return fits
Example #10
0
    def importCrest(info):
        sMkt = service.Market.getInstance()
        network = service.Network.getInstance()
        try:
            response = network.request(
                "https://public-crest.eveonline.com/killmails/%s/%s/" % info,
                network.EVE)
        except:
            return

        kill = (json.loads(response.read()))['victim']

        fit = Fit()
        fit.ship = Ship(sMkt.getItem(kill['shipType']['name']))
        fit.name = "CREST: %s's %s" % (kill['character']['name'],
                                       kill['shipType']['name'])

        # sort based on flag to get proper rack position
        items = sorted(kill['items'], key=lambda k: k['flag'])

        # We create a relation between module flag and module position on fit at time of append:
        # this allows us to know which module to apply charges to if need be (see below)
        flagMap = {}

        # Charges may show up before or after the module. We process modules first,
        # storing any charges that are fitted in a dict and noting their flag (module).
        charges = {}

        for mod in items:
            if mod['flag'] == 5:  # throw out cargo
                continue

            item = sMkt.getItem(mod['itemType']['name'],
                                eager="group.category")

            if item.category.name == "Drone":
                d = Drone(item)
                d.amount = mod[
                    'quantityDropped'] if 'quantityDropped' in mod else mod[
                        'quantityDestroyed']
                fit.drones.append(d)
            elif item.category.name == "Charge":
                charges[mod['flag']] = item
            else:
                m = Module(item)
                if m.isValidState(State.ACTIVE):
                    m.state = State.ACTIVE
                fit.modules.append(m)
                flagMap[mod['flag']] = fit.modules.index(m)

        for flag, item in charges.items():
            # we do not need to verify valid charge as it comes directly from CCP
            fit.modules[flagMap[flag]].charge = item

        return fit
Example #11
0
File: fit.py Project: qryss/Pyfa
    def importXml(cls, text):
        doc = xml.dom.minidom.parseString(text.encode("utf-8"))
        fittings = doc.getElementsByTagName("fittings").item(0)
        fittings = fittings.getElementsByTagName("fitting")
        fits = []
        from eos import db
        for fitting in fittings:
            f = Fit()
            f.name = fitting.getAttribute("name")
            # <localized hint="Maelstrom">Maelstrom</localized>
            shipType = fitting.getElementsByTagName("shipType").item(
                0).getAttribute("value")
            try:
                f.ship = Ship(db.getItem(shipType))
            except:
                continue
            hardwares = fitting.getElementsByTagName("hardware")
            for hardware in hardwares:
                try:
                    moduleName = hardware.getAttribute("type")
                    try:
                        item = db.getItem(moduleName, eager="group.category")
                    except:
                        continue
                    if item:
                        if item.category.name == "Drone":
                            d = Drone(item)
                            d.amount = int(hardware.getAttribute("qty"))
                            f.drones.append(d)
                        else:
                            try:
                                m = Module(item)
                            # When item can't be added to any slot (unknown item or just charge), ignore it
                            except ValueError:
                                continue
                            if m.isValidState(State.ACTIVE):
                                m.state = State.ACTIVE

                            f.modules.append(m)
                except KeyboardInterrupt:
                    continue

                fits.append(f)

        return fits
Example #12
0
File: port.py Project: Blinker/Pyfa
    def importDna(string):
        sMkt = service.Market.getInstance()
        info = string.split(":")

        f = Fit()
        try:
            f.ship = Ship(sMkt.getItem(int(info[0])))
            f.name = "{0} - DNA Imported".format(f.ship.item.name)
        except UnicodeEncodeError as e:

            def logtransform(s):
                if len(s) > 10:
                    return s[:10] + "..."
                return s

            logger.exception("Couldn't import ship data %r",
                             [logtransform(s) for s in info])
            return None

        for itemInfo in info[1:]:
            if itemInfo:
                itemID, amount = itemInfo.split(";")
                item = sMkt.getItem(int(itemID), eager="group.category")

                if item.category.name == "Drone":
                    d = Drone(item)
                    d.amount = int(amount)
                    f.drones.append(d)
                elif item.category.name == "Charge":
                    c = Cargo(item)
                    c.amount = int(amount)
                    f.cargo.append(c)
                else:
                    for i in xrange(int(amount)):
                        try:
                            m = Module(item)
                            f.modules.append(m)
                        except:
                            pass
                        if m.isValidState(State.ACTIVE):
                            m.state = State.ACTIVE

        return f
Example #13
0
File: port.py Project: mecnoc/Pyfa
    def importCrest(str):
        fit = json.loads(str)
        sMkt = service.Market.getInstance()

        f = Fit()
        f.name = fit['name']

        try:
            f.ship = Ship(sMkt.getItem(fit['ship']['id']))
        except:
            return None

        items = fit['items']
        items.sort(key=lambda k: k['flag'])
        for module in items:
            try:
                item = sMkt.getItem(module['type']['id'],
                                    eager="group.category")
                if item.category.name == "Drone":
                    d = Drone(item)
                    d.amount = module['quantity']
                    f.drones.append(d)
                elif item.category.name == "Charge":
                    c = Cargo(item)
                    c.amount = module['quantity']
                    f.cargo.append(c)
                else:
                    try:
                        m = Module(item)
                    # When item can't be added to any slot (unknown item or just charge), ignore it
                    except ValueError:
                        continue
                    if m.isValidState(State.ACTIVE):
                        m.state = State.ACTIVE

                    f.modules.append(m)
            except:
                continue

        return f
Example #14
0
    def init(self):
        """Initialize a fit from the database and validate"""
        self.__ship = None
        self.__mode = None

        if self.shipID:
            item = eos.db.getItem(self.shipID)
            if item is None:
                logger.error("Item (id: %d) does not exist", self.shipID)
                return

            try:
                self.__ship = Ship(item)
            except ValueError:
                logger.error("Item (id: %d) is not a Ship", self.shipID)
                return

        if self.modeID and self.__ship:
            item = eos.db.getItem(self.modeID)
            # Don't need to verify if it's a proper item, as validateModeItem assures this
            self.__mode = self.ship.validateModeItem(item)

        self.build()
Example #15
0
    def importFittingWindow(string, activeFit):
        sMkt = service.Market.getInstance()
        sFit = service.Fit.getInstance()

        activeFit = sFit.getFit(activeFit)

        # if the current fit has mods, do not mess with it. Instead, make new fit
        if activeFit.modCount > 0:
            fit = Fit()
            fit.ship = Ship(sMkt.getItem(activeFit.ship.item.ID))
            fit.name = "%s (copy)" % activeFit.name
        else:
            fit = activeFit
        lines = re.split('[\n\r]+', string)

        droneMap = {}
        cargoMap = {}
        modules = []

        for i in range(1, len(lines)):
            line = lines[i].strip()
            if not line:
                continue

            try:
                amount, modName = line.split("x ")
                amount = int(amount)
                item = sMkt.getItem(modName, eager="group.category")
            except:
                # if no data can be found (old names)
                continue

            if item.category.name == "Drone":
                if not modName in droneMap:
                    droneMap[modName] = 0
                droneMap[modName] += amount
            elif item.category.name == "Charge":
                if not modName in cargoMap:
                    cargoMap[modName] = 0
                cargoMap[modName] += amount
            else:
                for _ in xrange(amount):
                    try:
                        m = Module(item)
                    except ValueError:
                        continue
                    # If we are importing T3 ship, we must apply subsystems first, then
                    # calcModAttr() to get the ship slots
                    if m.slot == Slot.SUBSYSTEM and m.fits(fit):
                        fit.modules.append(m)
                    else:
                        modules.append(m)

        fit.clear()
        fit.calculateModifiedAttributes()

        for m in modules:
            # we check to see if module fits as a basic sanity check
            # if it doesn't then the imported fit is most likely invalid
            # (ie: user tried to import Legion fit to a Rifter)
            if m.fits(fit):
                fit.modules.append(m)
                if m.isValidState(State.ACTIVE):
                    m.state = State.ACTIVE
                m.owner = fit  # not sure why this is required when it's not for other import methods, but whatever
            else:
                return

        for droneName in droneMap:
            d = Drone(sMkt.getItem(droneName))
            d.amount = droneMap[droneName]
            fit.drones.append(d)

        for cargoName in cargoMap:
            c = Cargo(sMkt.getItem(cargoName))
            c.amount = cargoMap[cargoName]
            fit.cargo.append(c)

        return fit
Example #16
0
class Fit(object):
    """Represents a fitting, with modules, ship, implants, etc."""
    EXTRA_ATTRIBUTES = {"armorRepair": 0,
                        "hullRepair": 0,
                        "shieldRepair": 0,
                        "maxActiveDrones": 0,
                        "maxTargetsLockedFromSkills": 2,
                        "droneControlRange": 20000,
                        "cloaked": False,
                        "siege": False}

    PEAK_RECHARGE = 0.25

    def __init__(self):
        self.__modules = HandledModuleList()
        self.__drones = HandledDroneList()
        self.__implants = HandledImplantBoosterList()
        self.__boosters = HandledImplantBoosterList()
        self.__projectedFits = HandledProjectedFitList()
        self.__projectedModules = HandledProjectedModList()
        self.__projectedDrones = HandledProjectedDroneList()
        self.__character = None
        self.__owner = None
        self.shipID = None
        self.projected = False
        self.name = ""
        self.fleet = None
        self.boostsFits = set()
        self.gangBoosts = None
        self.timestamp = time.time()
        self.build()

    @classmethod
    def importAuto(cls, string, sourceFileName=None):
        # Get first line and strip space symbols of it
        # to avoid possible detection errors
        firstLine = re.split("[\n\r]+", string, maxsplit=1)[0]
        firstLine = firstLine.strip()
        # If XML-style start of tag encountered, detect as XML
        if re.match("<", firstLine):
            return "XML", cls.importXml(string)
        # If we've got source file name which is used to describe ship name
        # and first line contains something like [setup name], detect as eft config file
        elif re.match("\[.*\]", firstLine) and sourceFileName is not None:
            shipName = sourceFileName.rsplit('.')[0]
            return "EFT Config", cls.importEftCfg(shipName, string)
        # If no file is specified and there's comma between brackets,
        # consider that we have [ship, setup name] and detect like eft export format
        elif re.match("\[.*,.*\]", firstLine):
            return "EFT", (cls.importEft(string),)
        # Use DNA format for all other cases
        else:
            return "DNA", (cls.importDna(string),)

    @classmethod
    def importDna(cls, string):
        from eos import db
        info = string.split(":")
        f = Fit()
        f.ship = Ship(db.getItem(int(info[0])))
        f.name = "{0} - DNA Imported".format(f.ship.item.name)
        for itemInfo in info[1:]:
            if itemInfo:
                itemID, amount = itemInfo.split(";")
                item = db.getItem(int(itemID), eager="group.category")

                if item.category.name == "Drone":
                    d = Drone(item)
                    d.amount = int(amount)
                    f.drones.append(d)
                elif item.category.name == "Charge":
                    for i in xrange(int(amount)):
                        for mod in f.modules:
                            if (mod.isValidCharge(item) and mod.charge == None):
                                mod.charge = item
                                break;
                else:
                    for i in xrange(int(amount)):
                        try:
                            m = Module(item)
                            f.modules.append(m)
                        except:
                            pass
                        if m.isValidState(State.ACTIVE):
                            m.state = State.ACTIVE

        return f

    @classmethod
    def importEft(cls, eftString):
        from eos import db
        offineSuffix = " /OFFLINE"
        fit = cls()
        eftString = eftString.strip()
        lines = re.split('[\n\r]+', eftString)
        info = lines[0][1:-1].split(",", 1)
        if len(info) == 2:
            shipType = info[0].strip()
            fitName = info[1].strip()
        else:
            shipType = info[0].strip()
            fitName = "Imported %s" % shipType

        try:
            fit.ship = Ship(db.getItem(shipType))
            fit.name = fitName
        except:
            return
        droneMap = {}
        for i in range(1, len(lines)):
            line = lines[i]
            setOffline = line.endswith(offineSuffix)
            if setOffline == True:
                line = line[:len(line) - len(offineSuffix)]
            modAmmo = line.split(",")
            modDrone = modAmmo[0].split(" x")
            if len(modAmmo) == 2: ammoName = modAmmo[1].strip()
            else: ammoName = None
            modName = modDrone[0].strip()
            if len(modDrone) == 2: droneAmount = modDrone[1].strip()
            else: droneAmount = None
            try:
                item = db.getItem(modName, eager="group.category")
            except:
                try:
                    item = db.getItem(modAmmo[0], eager="group.category")
                except:
                    continue

            if item.category.name == "Drone":
                droneAmount = int(droneAmount) if droneAmount is not None else 1
                if not modName in droneMap:
                    droneMap[modName] = 0
                droneMap[modName] += droneAmount
            elif item.category.name == "Implant":
                fit.implants.append(Implant(item))
            else:
                m = Module(item)
                if ammoName:
                    try:
                        m.charge = db.getItem(ammoName)
                    except:
                        pass

                if setOffline == True and m.isValidState(State.OFFLINE):
                    m.state = State.OFFLINE
                elif m.isValidState(State.ACTIVE):
                    m.state = State.ACTIVE

                fit.modules.append(m)

        for droneName in droneMap:
            d = Drone(db.getItem(droneName))
            d.amount = droneMap[droneName]
            fit.drones.append(d)

        return fit

    @classmethod
    def importEftCfg(cls, shipname, contents):
        """Handle import from EFT config store file"""
        # Check if we have such ship in database, bail if we don't
        from eos import db
        try:
            db.getItem(shipname)
        except:
            return
        # If client didn't take care of encoding file contents into Unicode,
        # do it using fallback encoding ourselves
        if isinstance(contents, str):
            contents = unicode(contents, "cp1252")
        # List for fits
        fits = []
        # List for starting line numbers for each fit
        fitIndices = []
        # Separate string into lines
        lines = re.split('[\n\r]+', contents)
        for line in lines:
            # Detect fit header
            if line[:1] == "[" and line[-1:] == "]":
                # Line index where current fit starts
                startPos = lines.index(line)
                fitIndices.append(startPos)

        for i, startPos in enumerate(fitIndices):
            # End position is last file line if we're trying to get it for last fit,
            # or start position of next fit minus 1
            endPos = len(lines) if i == len(fitIndices) - 1 else fitIndices[i + 1]
            # Finally, get lines for current fitting
            fitLines = lines[startPos:endPos]
            try:
                # Create fit object
                f = Fit()
                # Strip square brackets and pull out a fit name
                f.name = fitLines[0][1:-1]
                # Assign ship to fitting
                f.ship = Ship(db.getItem(shipname))
                for i in range(1, len(fitLines)):
                    line = fitLines[i]
                    # Parse line into some data we will need
                    misc = re.match("(Drones|Implant|Booster)_(Active|Inactive)=(.+)",line)
                    if misc:
                        entityType = misc.group(1)
                        entityState = misc.group(2)
                        entityData = misc.group(3)
                        if entityType == "Drones":
                            droneData = re.match("(.+),([0-9]+)", entityData)
                            # Get drone name and attempt to detect drone number
                            droneName = droneData.group(1) if droneData else entityData
                            droneAmount = int(droneData.group(2)) if droneData else 1
                            # Bail if we can't get item or it's not from drone category
                            try:
                                droneItem = db.getItem(droneName, eager="group.category")
                            except:
                                continue
                            if droneItem.category.name != "Drone":
                                continue
                            # Add drone to the fitting
                            d = Drone(droneItem)
                            d.amount = droneAmount
                            if entityState == "Active":
                                d.amountActive = droneAmount
                            elif entityState == "Inactive":
                                d.amountActive = 0
                            f.drones.append(d)
                        elif entityType == "Implant":
                            # Bail if we can't get item or it's not from implant category
                            try:
                                implantItem = db.getItem(entityData, eager="group.category")
                            except:
                                continue
                            if implantItem.category.name != "Implant":
                                continue
                            # Add implant to the fitting
                            imp = Implant(implantItem)
                            if entityState == "Active":
                                imp.active = True
                            elif entityState == "Inactive":
                                imp.active = False
                            f.implants.append(imp)
                        elif entityType == "Booster":
                            # Bail if we can't get item or it's not from implant category
                            try:
                                boosterItem = db.getItem(entityData, eager="group.category")
                            except:
                                continue
                            # All boosters have implant category
                            if boosterItem.category.name != "Implant":
                                continue
                            # Add booster to the fitting
                            b = Booster(boosterItem)
                            if entityState == "Active":
                                b.active = True
                            elif entityState == "Inactive":
                                b.active = False
                            f.boosters.append(b)
                    # If we don't have any prefixes, then it's a module
                    else:
                        withCharge = re.match("(.+),(.+)", line)
                        modName = withCharge.group(1) if withCharge else line
                        chargeName = withCharge.group(2) if withCharge else None
                        # If we can't get module item, skip it
                        try:
                            modItem = db.getItem(modName)
                        except:
                            continue
                        # Create module and activate it if it's activable
                        m = Module(modItem)
                        if m.isValidState(State.ACTIVE):
                            m.state = State.ACTIVE
                        # Add charge to mod if applicable, on any errors just don't add anything
                        if chargeName:
                            try:
                                chargeItem = db.getItem(chargeName, eager="group.category")
                                if chargeItem.category.name == "Charge":
                                    m.charge = chargeItem
                            except:
                                pass
                        # Append module to fit
                        f.modules.append(m)
                # Append fit to list of fits
                fits.append(f)
            # Skip fit silently if we get an exception
            except Exception:
                pass

        return fits

    @classmethod
    def importXml(cls, text):
        doc = xml.dom.minidom.parseString(text.encode("utf-8"))
        fittings = doc.getElementsByTagName("fittings").item(0)
        fittings = fittings.getElementsByTagName("fitting")
        fits = []
        from eos import db
        for fitting in fittings:

                f = Fit()
                f.name = fitting.getAttribute("name")
                shipType = fitting.getElementsByTagName("shipType").item(0).getAttribute("value")
                f.ship = Ship(db.getItem(shipType))
                hardwares = fitting.getElementsByTagName("hardware")
                for hardware in hardwares:
                    try:
                        moduleName = hardware.getAttribute("type")
                        item = db.getItem(moduleName, eager="group.category")
                        if item:
                            if item.category.name == "Drone":
                                d = Drone(item)
                                d.amount = int(hardware.getAttribute("qty"))
                                f.drones.append(d)
                            else:
                                m = Module(item)
                                if m.isValidState(State.ACTIVE):
                                    m.state = State.ACTIVE

                                f.modules.append(m)
                    except Exception:
                        continue

                fits.append(f)


        return fits

    EXPORT_ORDER_EFT = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM]
    def exportEft(self):
        offineSuffix = " /OFFLINE"
        export = "[%s, %s]\n" % (self.ship.item.name, self.name)
        stuff = {}
        for module in self.modules:
            slot = module.slot
            if not slot in stuff: stuff[slot] = []
            curr = module.item.name if module.item else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "")
            if module.charge:
                curr += ", %s" % module.charge.name
            if module.state == State.OFFLINE:
                curr += offineSuffix
            curr += "\n"
            stuff[slot].append(curr)

        for slotType in self.EXPORT_ORDER_EFT:
            data = stuff.get(slotType)
            if data is not None:
                export += "\n"
                for curr in data:
                    export += curr

        if len(self.drones) > 0:
            export += "\n\n"
            for drone in self.drones:
                export += "%s x%s\n" % (drone.item.name, drone.amount)

        if export[-1] == "\n":
            export = export[:-1]

        return export

    def exportEftImps(self):
        export = self.exportEft()
        if len(self.implants) > 0:
            export += "\n\n\n"
            for implant in self.implants:
                export += "%s\n" % (implant.item.name)

        if export[-1] == "\n":
            export = export[:-1]

        return export

    def exportDna(self):
        dna = str(self.shipID)
        mods = OrderedDict()
        for mod in self.modules:
            if not mod.isEmpty:
                if not mod.itemID in mods:
                    mods[mod.itemID] = 0
                mods[mod.itemID] += 1

        for mod in mods:
            dna += ":{0};{1}".format(mod, mods[mod])

        for drone in self.drones:
            dna += ":{0};{1}".format(drone.itemID, drone.amount)

        return dna + "::"

    @classmethod
    def exportXml(cls, *fits):
        doc = xml.dom.minidom.Document()
        fittings = doc.createElement("fittings")
        doc.appendChild(fittings)

        for fit in fits:
            fitting = doc.createElement("fitting")
            fitting.setAttribute("name", fit.name)
            fittings.appendChild(fitting)
            description = doc.createElement("description")
            description.setAttribute("value", "")
            fitting.appendChild(description)
            shipType = doc.createElement("shipType")
            shipType.setAttribute("value", fit.ship.item.name)
            fitting.appendChild(shipType)

            slotNum = {}
            for module in fit.modules:
                if module.isEmpty:
                    continue

                slot = module.slot
                if not slot in slotNum: slotNum[slot] = 0
                slotId = slotNum[slot]
                slotNum[slot] += 1
                hardware = doc.createElement("hardware")
                hardware.setAttribute("type", module.item.name)
                slotName = Slot.getName(slot).lower()
                slotName = slotName if slotName != "high" else "hi"
                hardware.setAttribute("slot", "%s slot %d" % (slotName, slotId))
                fitting.appendChild(hardware)

            for drone in fit.drones:
                hardware = doc.createElement("hardware")
                hardware.setAttribute("qty", "%d" % drone.amount)
                hardware.setAttribute("slot", "drone bay")
                hardware.setAttribute("type", drone.item.name)
                fitting.appendChild(hardware)

        return doc.toprettyxml()

    @reconstructor
    def init(self):
        self.build()

    def build(self):
        from eos import db
        self.__extraDrains = []
        self.__ehp = None
        self.__weaponDPS = None
        self.__weaponVolley = None
        self.__droneDPS = None
        self.__sustainableTank = None
        self.__effectiveSustainableTank = None
        self.__effectiveTank = None
        self.__calculated = False
        self.__capStable = None
        self.__capState = None
        self.__capUsed = None
        self.__capRecharge = None
        self.__calculatedTargets = []
        self.factorReload = False
        self.fleet = None
        self.boostsFits = set()
        self.gangBoosts = None
        self.extraAttributes = ModifiedAttributeDict(self)
        self.extraAttributes.original = self.EXTRA_ATTRIBUTES
        self.ship = Ship(db.getItem(self.shipID)) if self.shipID is not None else None

    @property
    def damagePattern(self):
        return self.__damagePattern

    @damagePattern.setter
    def damagePattern(self, damagePattern):
        self.__damagePattern = damagePattern
        self.__ehp = None
        self.__effectiveTank = None

    @property
    def character(self):
        return self.__character if self.__character is not None else Character.getAll0()

    @character.setter
    def character(self, char):
        self.__character = char

    @property
    def ship(self):
        return self.__ship

    @ship.setter
    def ship(self, ship):
        self.__ship = ship
        self.shipID = ship.item.ID if ship is not None else None

    @property
    def drones(self):
        return self.__drones

    @property
    def modules(self):
        return self.__modules

    @property
    def implants(self):
        return self.__implants

    @property
    def boosters(self):
        return self.__boosters

    @property
    def projectedModules(self):
        return self.__projectedModules

    @property
    def projectedFits(self):
        return self.__projectedFits

    @property
    def projectedDrones(self):
        return self.__projectedDrones

    @property
    def weaponDPS(self):
        if self.__weaponDPS is None:
            self.calculateWeaponStats()

        return self.__weaponDPS

    @property
    def weaponVolley(self):
        if self.__weaponVolley is None:
            self.calculateWeaponStats()

        return self.__weaponVolley

    @property
    def droneDPS(self):
        if self.__droneDPS is None:
            self.calculateWeaponStats()

        return self.__droneDPS

    @property
    def totalDPS(self):
        return self.droneDPS + self.weaponDPS

    @property
    def maxTargets(self):
        return min(self.extraAttributes["maxTargetsLockedFromSkills"], self.ship.getModifiedItemAttr("maxLockedTargets"))

    @property
    def maxTargetRange(self):
        return min(self.ship.getModifiedItemAttr("maxTargetRange"), 250000)

    @property
    def scanStrength(self):
        return max([self.ship.getModifiedItemAttr("scan%sStrength" % scanType)
                    for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric")])

    @property
    def scanType(self):
        maxStr = -1
        type = None
        for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric"):
            currStr = self.ship.getModifiedItemAttr("scan%sStrength" % scanType)
            if currStr > maxStr:
                maxStr = currStr
                type = scanType
            elif currStr == maxStr:
                type = "Multispectral"

        return type

    @property
    def alignTime(self):
        agility = self.ship.getModifiedItemAttr("agility")
        mass = self.ship.getModifiedItemAttr("mass")

        return -log(0.25) * agility * mass / 1000000

    @property
    def appliedImplants(self):
        implantsBySlot = {}
        if self.character:
            for implant in self.character.implants:
                implantsBySlot[implant.slot] = implant

        for implant in self.implants:
            implantsBySlot[implant.slot] = implant

        return implantsBySlot.values()

    @validates("ID", "ownerID", "shipID")
    def validator(self, key, val):
        map = {"ID": lambda val: isinstance(val, int),
               "ownerID" : lambda val: isinstance(val, int),
               "shipID" : lambda val: isinstance(val, int) or val is None}

        if map[key](val) == False: raise ValueError(str(val) + " is not a valid value for " + key)
        else: return val

    def clear(self):
        self.__effectiveTank = None
        self.__weaponDPS = None
        self.__weaponVolley = None
        self.__effectiveSustainableTank = None
        self.__sustainableTank = None
        self.__droneDPS = None
        self.__ehp = None
        self.__calculated = False
        self.__capStable = None
        self.__capState = None
        self.__capUsed = None
        self.__capRecharge = None
        del self.__calculatedTargets[:]
        del self.__extraDrains[:]

        if self.ship is not None: self.ship.clear()
        c = chain(self.modules, self.drones, self.boosters, self.implants, self.projectedDrones, self.projectedModules, self.projectedFits, (self.character, self.extraAttributes))
        for stuff in c:
            if stuff is not None and stuff != self: stuff.clear()

    #Methods to register and get the thing currently affecting the fit,
    #so we can correctly map "Affected By"
    def register(self, currModifier):
        self.__modifier = currModifier
        if hasattr(currModifier, "itemModifiedAttributes"):
            currModifier.itemModifiedAttributes.fit = self
        if hasattr(currModifier, "chargeModifiedAttributes"):
            currModifier.chargeModifiedAttributes.fit = self

    def getModifier(self):
        return self.__modifier

    def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None):
        refreshBoosts = False
        if withBoosters is True:
            refreshBoosts = True
        if dirtyStorage is not None and self.ID in dirtyStorage:
            refreshBoosts = True
        if dirtyStorage is not None:
            dirtyStorage.update(self.boostsFits)
        if self.fleet is not None and refreshBoosts is True:
            self.gangBoosts = self.fleet.recalculateLinear(withBoosters=withBoosters, dirtyStorage=dirtyStorage)
        elif self.fleet is None:
            self.gangBoosts = None
        if dirtyStorage is not None:
            try:
                dirtyStorage.remove(self.ID)
            except KeyError:
                pass
        # If we're not explicitly asked to project fit onto something,
        # set self as target fit
        if targetFit is None:
            targetFit = self
            forceProjected = False
        # Else, we're checking all target projectee fits
        elif targetFit not in self.__calculatedTargets:
            self.__calculatedTargets.append(targetFit)
            targetFit.calculateModifiedAttributes(dirtyStorage=dirtyStorage)
            forceProjected = True
        # Or do nothing if target fit is calculated
        else:
            return

        # If fit is calculated and we have nothing to do here, get out
        if self.__calculated == True and forceProjected == False:
            return

        # Mark fit as calculated
        self.__calculated = True

        # There's a few things to keep in mind here
        # 1: Early effects first, then regular ones, then late ones, regardless of anything else
        # 2: Some effects aren't implemented
        # 3: Some effects are implemented poorly and will just explode on us
        # 4: Errors should be handled gracefully and preferably without crashing unless serious
        for runTime in ("early", "normal", "late"):
            # Build a little chain of stuff
            # Avoid adding projected drones and modules when fit is projected onto self
            # TODO: remove this workaround when proper self-projection using virtual duplicate fits is implemented
            if targetFit == self and forceProjected is True:
                c = chain((self.character, self.ship), self.drones, self.boosters, self.appliedImplants, self.modules)
            else:
                c = chain((self.character, self.ship), self.drones, self.boosters, self.appliedImplants, self.modules,
                          self.projectedDrones, self.projectedModules)

            for item in c:
                # Registering the item about to affect the fit allows us to track "Affected By" relations correctly
                if item is not None:
                    self.register(item)
                    item.calculateModifiedAttributes(self, runTime, False)
                    if forceProjected is True:
                        targetFit.register(item)
                        item.calculateModifiedAttributes(targetFit, runTime, True)
            if self.gangBoosts is not None:
                #print self.gangBoosts
                contextMap = {Skill: "skill",
                              Ship: "ship",
                              Module: "module",
                              Implant: "implant"}
                for name, info in self.gangBoosts.iteritems():
                    # Unpack all data required to run effect properly
                    effect, thing = info[1]
                    if effect.runTime == runTime:
                        context = ("gang", contextMap[type(thing)])
                        if isinstance(thing, Module):
                            if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \
                            (effect.isType("active") and thing.state >= State.ACTIVE):
                                # Run effect, and get proper bonuses applied
                                try:
                                    effect.handler(targetFit, thing, context)
                                except:
                                    pass
                        else:
                            # Run effect, and get proper bonuses applied
                            try:
                                effect.handler(targetFit, thing, context)
                            except:
                                pass
        for fit in self.projectedFits:
            fit.calculateModifiedAttributes(self, dirtyStorage=dirtyStorage)

    def fill(self):
        """
        Fill this fit's module slots with enough dummy slots so that all slots are used.
        This is mostly for making the life of gui's easier.
        GUI's can call fill() and then stop caring about empty slots completely.
        """
        if self.ship is None:
            return

        for slotType in (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM):
            amount = self.getSlotsFree(slotType, True)
            if amount > 0:
                for _ in xrange(int(amount)):
                    self.modules.append(Module.buildEmpty(slotType))

            if amount < 0:
                #Look for any dummies of that type to remove
                toRemove = []
                for mod in self.modules:
                    if mod.isEmpty and mod.slot == slotType:
                        toRemove.append(mod)
                        amount += 1
                        if amount == 0:
                            break
                for mod in toRemove:
                    self.modules.remove(mod)

    def unfill(self):
        for i in xrange(len(self.modules) - 1, -1, -1):
            mod = self.modules[i]
            if mod.isEmpty:
                del self.modules[i]

    def getItemAttrSum(self, dict, attr):
        amount = 0
        for mod in dict:
            add = mod.getModifiedItemAttr(attr)
            if add is not None:
                amount += add

        return amount

    def getItemAttrOnlineSum(self, dict, attr):
        amount = 0
        for mod in dict:
            add = mod.getModifiedItemAttr(attr) if mod.state >= State.ONLINE else None
            if add is not None:
                amount += add

        return amount

    def getHardpointsUsed(self, type):
        amount = 0
        for mod in self.modules:
            if mod.hardpoint is type and not mod.isEmpty:
                amount += 1

        return amount

    def getSlotsUsed(self, type, countDummies=False):
        amount = 0
        for mod in self.modules:
            if mod.slot is type and (not mod.isEmpty or countDummies):
                amount += 1

        return amount

    def getSlotsFree(self, type, countDummies=False):
        slots = {Slot.LOW: "lowSlots",
                 Slot.MED: "medSlots",
                 Slot.HIGH: "hiSlots",
                 Slot.RIG: "rigSlots",
                 Slot.SUBSYSTEM: "maxSubSystems"}

        slotsUsed = self.getSlotsUsed(type, countDummies)
        totalSlots = self.ship.getModifiedItemAttr(slots[type]) or 0
        return int(totalSlots - slotsUsed)

    @property
    def calibrationUsed(self):
        return self.getItemAttrSum(self.modules, 'upgradeCost')

    @property
    def pgUsed(self):
        return self.getItemAttrOnlineSum(self.modules, "power")

    @property
    def cpuUsed(self):
        return self.getItemAttrOnlineSum(self.modules, "cpu")

    @property
    def droneBandwidthUsed(self):
        amount = 0
        for d in self.drones:
            amount += d.getModifiedItemAttr("droneBandwidthUsed") * d.amountActive

        return amount

    @property
    def droneBayUsed(self):
        amount = 0
        for d in self.drones:
            amount += d.item.volume * d.amount

        return amount

    @property
    def activeDrones(self):
        amount = 0
        for d in self.drones:
            amount +=d.amountActive

        return amount

    # Expresses how difficult a target is to probe down with scan probes
    # If this is <1.08, the ship is unproabeable
    @property
    def probeSize(self):
        sigRad = self.ship.getModifiedItemAttr("signatureRadius")
        sensorStr = float(self.scanStrength)
        probeSize = sigRad / sensorStr if sensorStr != 0 else None
        # http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1532170&page=2#42
        if probeSize is not None:
            # http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333691
            # http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333763
            # Tests by tester128 and several conclusions by me, prove that cap is in range
            # from 1.1 to 1.12, we're picking average value
            probeSize = max(probeSize, 1.11)
        return probeSize

    @property
    def warpSpeed(self):
        base = self.ship.getModifiedItemAttr("baseWarpSpeed") or 1
        multiplier = self.ship.getModifiedItemAttr("warpSpeedMultiplier") or 1
        return 3 * base * multiplier

    @property
    def maxWarpDistance(self):
        capacity = self.ship.getModifiedItemAttr("capacitorCapacity")
        mass = self.ship.getModifiedItemAttr("mass")
        warpCapNeed = self.ship.getModifiedItemAttr("warpCapacitorNeed")
        return capacity / (mass * warpCapNeed)

    @property
    def capStable(self):
        if self.__capStable is None:
            self.simulateCap()

        return self.__capStable

    @property
    def capState(self):
        """
        If the cap is stable, the capacitor state is the % at which it is stable.
        If the cap is unstable, this is the amount of time before it runs out
        """
        if self.__capState is None:
            self.simulateCap()

        return self.__capState

    @property
    def capUsed(self):
        if self.__capUsed is None:
            self.simulateCap()

        return self.__capUsed

    @property
    def capRecharge(self):
        if self.__capRecharge is None:
            self.simulateCap()

        return self.__capRecharge


    @property
    def sustainableTank(self):
        if self.__sustainableTank is None:
            self.calculateSustainableTank()

        return self.__sustainableTank

    def calculateSustainableTank(self, effective=True):
        if self.__sustainableTank is None:
            if self.capStable:
                sustainable = {}
                sustainable["armorRepair"] = self.extraAttributes["armorRepair"]
                sustainable["shieldRepair"] = self.extraAttributes["shieldRepair"]
                sustainable["hullRepair"] = self.extraAttributes["hullRepair"]
            else:
                sustainable = {}

                repairers = []
                #Map a repairer type to the attribute it uses
                groupAttrMap = {"Armor Repair Unit": "armorDamageAmount",
                     "Fueled Armor Repairer": "armorDamageAmount",
                     "Hull Repair Unit": "structureDamageAmount",
                     "Shield Booster": "shieldBonus",
                     "Fueled Shield Booster": "shieldBonus",
                     "Remote Armor Repairer": "armorDamageAmount",
                     "Remote Shield Booster": "shieldBonus"}
                #Map repairer type to attribute
                groupStoreMap = {"Armor Repair Unit": "armorRepair",
                                 "Hull Repair Unit": "hullRepair",
                                 "Shield Booster": "shieldRepair",
                                 "Fueled Shield Booster": "shieldRepair",
                                 "Remote Armor Repairer": "armorRepair",
                                 "Remote Shield Booster": "shieldRepair",
                                 "Fueled Armor Repairer": "armorRepair",}

                capUsed = self.capUsed
                for attr in ("shieldRepair", "armorRepair", "hullRepair"):
                    sustainable[attr] = self.extraAttributes[attr]
                    dict = self.extraAttributes.getAfflictions(attr)
                    if self in dict:
                        for mod, _, amount in dict[self]:
                            if mod.projected is False:
                                usesCap = True
                                try:
                                    if mod.capUse:
                                        capUsed -= mod.capUse
                                    else:
                                        usesCap = False
                                except AttributeError:
                                    usesCap = False
                                # Modules which do not use cap are not penalized based on cap use
                                if usesCap:
                                    cycleTime = mod.getModifiedItemAttr("duration")
                                    amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
                                    sustainable[attr] -= amount / (cycleTime / 1000.0)
                                    repairers.append(mod)


                #Sort repairers by efficiency. We want to use the most efficient repairers first
                repairers.sort(key=lambda mod: mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) / mod.getModifiedItemAttr("capacitorNeed"), reverse = True)

                #Loop through every module until we're above peak recharge
                #Most efficient first, as we sorted earlier.
                #calculate how much the repper can rep stability & add to total
                totalPeakRecharge = self.capRecharge
                for mod in repairers:
                    if capUsed > totalPeakRecharge: break
                    cycleTime = mod.cycleTime
                    capPerSec = mod.capUse
                    if capPerSec is not None and cycleTime is not None:
                        #Check how much this repper can work
                        sustainability = min(1, (totalPeakRecharge - capUsed) / capPerSec)

                        #Add the sustainable amount
                        amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
                        sustainable[groupStoreMap[mod.item.group.name]] += sustainability * (amount / (cycleTime / 1000.0))
                        capUsed += capPerSec

            sustainable["passiveShield"] = self.calculateShieldRecharge()
            self.__sustainableTank = sustainable

        return self.__sustainableTank

    def calculateCapRecharge(self, percent = PEAK_RECHARGE):
        capacity = self.ship.getModifiedItemAttr("capacitorCapacity")
        rechargeRate = self.ship.getModifiedItemAttr("rechargeRate") / 1000.0
        return 10 / rechargeRate * sqrt(percent) * (1 - sqrt(percent)) * capacity

    def calculateShieldRecharge(self, percent = PEAK_RECHARGE):
        capacity = self.ship.getModifiedItemAttr("shieldCapacity")
        rechargeRate = self.ship.getModifiedItemAttr("shieldRechargeRate") / 1000.0
        return 10 / rechargeRate * sqrt(percent) * (1 - sqrt(percent)) * capacity

    def addDrain(self, cycleTime, capNeed, clipSize=0):
        self.__extraDrains.append((cycleTime, capNeed, clipSize))

    def removeDrain(self, i):
        del self.__extraDrains[i]

    def iterDrains(self):
        return self.__extraDrains.__iter__()

    def __generateDrain(self):
        drains = []
        capUsed = 0
        capAdded = 0
        for mod in self.modules:
            if mod.state >= State.ACTIVE:
                    cycleTime = mod.rawCycleTime or 0
                    reactivationTime = mod.getModifiedItemAttr("moduleReactivationDelay") or 0
                    fullCycleTime = cycleTime + reactivationTime
                    if fullCycleTime > 0:
                        capNeed = mod.capUse
                        if capNeed > 0:
                            capUsed += capNeed
                        else:
                            capAdded -= capNeed

                        drains.append((int(fullCycleTime), mod.getModifiedItemAttr("capacitorNeed") or 0, mod.numShots or 0))

        for fullCycleTime, capNeed, clipSize in self.iterDrains():
            drains.append((int(fullCycleTime), capNeed, clipSize))
            if capNeed > 0:
                capUsed += capNeed / (fullCycleTime / 1000.0)
            else:
                capAdded += -capNeed / (fullCycleTime / 1000.0)

        return drains, capUsed, capAdded

    def simulateCap(self):
        drains, self.__capUsed, self.__capRecharge = self.__generateDrain()
        self.__capRecharge += self.calculateCapRecharge()
        if len(drains) > 0:
            sim = capSim.CapSimulator()
            sim.init(drains)
            sim.capacitorCapacity = self.ship.getModifiedItemAttr("capacitorCapacity")
            sim.capacitorRecharge = self.ship.getModifiedItemAttr("rechargeRate")
            sim.stagger = True
            sim.scale = False
            sim.t_max = 6 * 60 * 60 * 1000
            sim.reload = self.factorReload
            sim.run()

            capState = (sim.cap_stable_low + sim.cap_stable_high) / (2 * sim.capacitorCapacity)
            self.__capStable = capState > 0
            self.__capState = min(100, capState * 100) if self.__capStable else sim.t / 1000.0
        else:
            self.__capStable = True
            self.__capState = 100

    @property
    def hp(self):
        hp = {}
        for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
            hp[type] = self.ship.getModifiedItemAttr(attr)

        return hp

    @property
    def ehp(self):
        if self.__ehp is None:
            if self.damagePattern is None:
                ehp = self.hp
            else:
                ehp = self.damagePattern.calculateEhp(self)
            self.__ehp = ehp

        return self.__ehp

    @property
    def tank(self):
        hps = {"passiveShield" : self.calculateShieldRecharge()}
        for type in ("shield", "armor", "hull"):
            hps["%sRepair" % type] = self.extraAttributes["%sRepair" % type]

        return hps

    @property
    def effectiveTank(self):
        if self.__effectiveTank is None:
            if self.damagePattern is None:
                ehps = self.tank
            else:
                ehps = self.damagePattern.calculateEffectiveTank(self, self.extraAttributes)

            self.__effectiveTank = ehps

        return self.__effectiveTank

    @property
    def effectiveSustainableTank(self):
        if self.__effectiveSustainableTank is None:
            if self.damagePattern is None:
                eshps = self.sustainableTank
            else:
                eshps = self.damagePattern.calculateEffectiveTank(self, self.sustainableTank)

            self.__effectiveSustainableTank = eshps

        return self.__effectiveSustainableTank


    def calculateLockTime(self, radius):
        scanRes = self.ship.getModifiedItemAttr("scanResolution")
        if scanRes is not None and scanRes > 0:
            # Yes, this function returns time in seconds, not miliseconds.
            # 40,000 is indeed the correct constant here.
            return min(40000 / scanRes / asinh(radius)**2, 30*60)
        else:
            return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0

    def calculateWeaponStats(self):
        weaponDPS = 0
        droneDPS = 0
        weaponVolley = 0

        for mod in self.modules:
            dps, volley = mod.damageStats
            weaponDPS += dps
            weaponVolley += volley

        for drone in self.drones:
            droneDPS += drone.dps

        self.__weaponDPS = weaponDPS
        self.__weaponVolley = weaponVolley
        self.__droneDPS = droneDPS

    @property
    def fits(self):
        for mod in self.modules:
            if not mod.fits(self):
                return False

        return True

    def __deepcopy__(self, memo):
        copy = Fit()
        #Character and owner are not copied
        copy.character = self.__character
        copy.owner = self.owner
        copy.ship = deepcopy(self.ship, memo)
        copy.name = "%s copy" % self.name
        copy.damagePattern = self.damagePattern

        toCopy = ("modules", "drones", "implants", "boosters", "projectedModules", "projectedDrones")
        for name in toCopy:
            orig = getattr(self, name)
            c = getattr(copy, name)
            for i in orig:
                c.append(deepcopy(i, memo))

        for fit in self.projectedFits:
            copy.projectedFits.append(fit)

        return copy
Example #17
0
    def importEft(eftString):
        sMkt = service.Market.getInstance()
        offineSuffix = " /OFFLINE"

        fit = Fit()
        eftString = eftString.strip()
        lines = re.split('[\n\r]+', eftString)
        info = lines[0][1:-1].split(",", 1)

        if len(info) == 2:
            shipType = info[0].strip()
            fitName = info[1].strip()
        else:
            shipType = info[0].strip()
            fitName = "Imported %s" % shipType

        try:
            ship = sMkt.getItem(shipType)
            try:
                fit.ship = Ship(ship)
            except ValueError:
                fit.ship = Citadel(ship)
            fit.name = fitName
        except:
            return

        # maintain map of drones and their quantities
        droneMap = {}
        cargoMap = {}
        moduleList = []
        for i in range(1, len(lines)):
            ammoName = None
            extraAmount = None

            line = lines[i].strip()
            if not line:
                continue

            setOffline = line.endswith(offineSuffix)
            if setOffline is True:
                # remove offline suffix from line
                line = line[:len(line) - len(offineSuffix)]

            modAmmo = line.split(",")
            # matches drone and cargo with x{qty}
            modExtra = modAmmo[0].split(" x")

            if len(modAmmo) == 2:
                # line with a module and ammo
                ammoName = modAmmo[1].strip()
                modName = modAmmo[0].strip()
            elif len(modExtra) == 2:
                # line with drone/cargo and qty
                extraAmount = modExtra[1].strip()
                modName = modExtra[0].strip()
            else:
                # line with just module
                modName = modExtra[0].strip()

            try:
                # get item information. If we are on a Drone/Cargo line, throw out cargo
                item = sMkt.getItem(modName, eager="group.category")
            except:
                # if no data can be found (old names)
                continue

            if item.category.name == "Drone":
                extraAmount = int(
                    extraAmount) if extraAmount is not None else 1
                if not modName in droneMap:
                    droneMap[modName] = 0
                droneMap[modName] += extraAmount
            elif item.category.name == "Fighter":
                extraAmount = int(
                    extraAmount) if extraAmount is not None else 1
                fighterItem = Fighter(item)
                if (extraAmount > fighterItem.fighterSquadronMaxSize
                    ):  #Amount bigger then max fightergroup size
                    extraAmount = fighterItem.fighterSquadronMaxSize
                if fighterItem.fits(fit):
                    fit.fighters.append(fighterItem)

            if len(
                    modExtra
            ) == 2 and item.category.name != "Drone" and item.category.name != "Fighter":
                extraAmount = int(
                    extraAmount) if extraAmount is not None else 1
                if not modName in cargoMap:
                    cargoMap[modName] = 0
                cargoMap[modName] += extraAmount
            elif item.category.name == "Implant":
                fit.implants.append(Implant(item))
            # elif item.category.name == "Subsystem":
            #     try:
            #         subsystem = Module(item)
            #     except ValueError:
            #         continue
            #
            #     if subsystem.fits(fit):
            #         fit.modules.append(subsystem)
            else:
                try:
                    m = Module(item)
                except ValueError:
                    continue
                # Add subsystems before modules to make sure T3 cruisers have subsystems installed
                if item.category.name == "Subsystem":
                    if m.fits(fit):
                        fit.modules.append(m)
                else:
                    if ammoName:
                        try:
                            ammo = sMkt.getItem(ammoName)
                            if m.isValidCharge(ammo) and m.charge is None:
                                m.charge = ammo
                        except:
                            pass

                    if setOffline is True and m.isValidState(State.OFFLINE):
                        m.state = State.OFFLINE
                    elif m.isValidState(State.ACTIVE):
                        m.state = State.ACTIVE

                    moduleList.append(m)

        # Recalc to get slot numbers correct for T3 cruisers
        service.Fit.getInstance().recalc(fit)

        for m in moduleList:
            if m.fits(fit):
                m.owner = fit
                if not m.isValidState(m.state):
                    print "Error: Module", m, "cannot have state", m.state

                fit.modules.append(m)

        for droneName in droneMap:
            d = Drone(sMkt.getItem(droneName))
            d.amount = droneMap[droneName]
            fit.drones.append(d)

        for cargoName in cargoMap:
            c = Cargo(sMkt.getItem(cargoName))
            c.amount = cargoMap[cargoName]
            fit.cargo.append(c)

        return fit
Example #18
0
    def importEftCfg(cls, shipname, contents):
        """Handle import from EFT config store file"""
        # Check if we have such ship in database, bail if we don't
        from eos import db
        try:
            db.getItem(shipname)
        except:
            return
        # If client didn't take care of encoding file contents into Unicode,
        # do it using fallback encoding ourselves
        if isinstance(contents, str):
            contents = unicode(contents, "cp1252")
        # List for fits
        fits = []
        # List for starting line numbers for each fit
        fitIndices = []
        # Separate string into lines
        lines = re.split('[\n\r]+', contents)
        for line in lines:
            # Detect fit header
            if line[:1] == "[" and line[-1:] == "]":
                # Line index where current fit starts
                startPos = lines.index(line)
                fitIndices.append(startPos)

        for i, startPos in enumerate(fitIndices):
            # End position is last file line if we're trying to get it for last fit,
            # or start position of next fit minus 1
            endPos = len(lines) if i == len(fitIndices) - 1 else fitIndices[i + 1]
            # Finally, get lines for current fitting
            fitLines = lines[startPos:endPos]
            try:
                # Create fit object
                f = Fit()
                # Strip square brackets and pull out a fit name
                f.name = fitLines[0][1:-1]
                # Assign ship to fitting
                f.ship = Ship(db.getItem(shipname))
                for i in range(1, len(fitLines)):
                    line = fitLines[i]
                    # Parse line into some data we will need
                    misc = re.match("(Drones|Implant|Booster)_(Active|Inactive)=(.+)",line)
                    if misc:
                        entityType = misc.group(1)
                        entityState = misc.group(2)
                        entityData = misc.group(3)
                        if entityType == "Drones":
                            droneData = re.match("(.+),([0-9]+)", entityData)
                            # Get drone name and attempt to detect drone number
                            droneName = droneData.group(1) if droneData else entityData
                            droneAmount = int(droneData.group(2)) if droneData else 1
                            # Bail if we can't get item or it's not from drone category
                            try:
                                droneItem = db.getItem(droneName, eager="group.category")
                            except:
                                continue
                            if droneItem.category.name != "Drone":
                                continue
                            # Add drone to the fitting
                            d = Drone(droneItem)
                            d.amount = droneAmount
                            if entityState == "Active":
                                d.amountActive = droneAmount
                            elif entityState == "Inactive":
                                d.amountActive = 0
                            f.drones.append(d)
                        elif entityType == "Implant":
                            # Bail if we can't get item or it's not from implant category
                            try:
                                implantItem = db.getItem(entityData, eager="group.category")
                            except:
                                continue
                            if implantItem.category.name != "Implant":
                                continue
                            # Add implant to the fitting
                            imp = Implant(implantItem)
                            if entityState == "Active":
                                imp.active = True
                            elif entityState == "Inactive":
                                imp.active = False
                            f.implants.append(imp)
                        elif entityType == "Booster":
                            # Bail if we can't get item or it's not from implant category
                            try:
                                boosterItem = db.getItem(entityData, eager="group.category")
                            except:
                                continue
                            # All boosters have implant category
                            if boosterItem.category.name != "Implant":
                                continue
                            # Add booster to the fitting
                            b = Booster(boosterItem)
                            if entityState == "Active":
                                b.active = True
                            elif entityState == "Inactive":
                                b.active = False
                            f.boosters.append(b)
                    # If we don't have any prefixes, then it's a module
                    else:
                        withCharge = re.match("(.+),(.+)", line)
                        modName = withCharge.group(1) if withCharge else line
                        chargeName = withCharge.group(2) if withCharge else None
                        # If we can't get module item, skip it
                        try:
                            modItem = db.getItem(modName)
                        except:
                            continue
                        # Create module and activate it if it's activable
                        m = Module(modItem)
                        if m.isValidState(State.ACTIVE):
                            m.state = State.ACTIVE
                        # Add charge to mod if applicable, on any errors just don't add anything
                        if chargeName:
                            try:
                                chargeItem = db.getItem(chargeName, eager="group.category")
                                if chargeItem.category.name == "Charge":
                                    m.charge = chargeItem
                            except:
                                pass
                        # Append module to fit
                        f.modules.append(m)
                # Append fit to list of fits
                fits.append(f)
            # Skip fit silently if we get an exception
            except Exception:
                pass

        return fits
Example #19
0
    def importEft(cls, eftString):
        from eos import db
        offineSuffix = " /OFFLINE"
        fit = cls()
        eftString = eftString.strip()
        lines = re.split('[\n\r]+', eftString)
        info = lines[0][1:-1].split(",", 1)
        if len(info) == 2:
            shipType = info[0].strip()
            fitName = info[1].strip()
        else:
            shipType = info[0].strip()
            fitName = "Imported %s" % shipType

        try:
            fit.ship = Ship(db.getItem(shipType))
            fit.name = fitName
        except:
            return
        droneMap = {}
        for i in range(1, len(lines)):
            line = lines[i]
            setOffline = line.endswith(offineSuffix)
            if setOffline == True:
                line = line[:len(line) - len(offineSuffix)]
            modAmmo = line.split(",")
            modDrone = modAmmo[0].split(" x")
            if len(modAmmo) == 2: ammoName = modAmmo[1].strip()
            else: ammoName = None
            modName = modDrone[0].strip()
            if len(modDrone) == 2: droneAmount = modDrone[1].strip()
            else: droneAmount = None
            try:
                item = db.getItem(modName, eager="group.category")
            except:
                try:
                    item = db.getItem(modAmmo[0], eager="group.category")
                except:
                    continue

            if item.category.name == "Drone":
                droneAmount = int(droneAmount) if droneAmount is not None else 1
                if not modName in droneMap:
                    droneMap[modName] = 0
                droneMap[modName] += droneAmount
            elif item.category.name == "Implant":
                fit.implants.append(Implant(item))
            else:
                m = Module(item)
                if ammoName:
                    try:
                        m.charge = db.getItem(ammoName)
                    except:
                        pass

                if setOffline == True and m.isValidState(State.OFFLINE):
                    m.state = State.OFFLINE
                elif m.isValidState(State.ACTIVE):
                    m.state = State.ACTIVE

                fit.modules.append(m)

        for droneName in droneMap:
            d = Drone(db.getItem(droneName))
            d.amount = droneMap[droneName]
            fit.drones.append(d)

        return fit
Example #20
0
class Fit(object):
    """Represents a fitting, with modules, ship, implants, etc."""
    EXTRA_ATTRIBUTES = {
        "armorRepair": 0,
        "hullRepair": 0,
        "shieldRepair": 0,
        "maxActiveDrones": 0,
        "maxTargetsLockedFromSkills": 2,
        "droneControlRange": 20000,
        "cloaked": False,
        "siege": False
    }

    PEAK_RECHARGE = 0.25

    def __init__(self):
        self.__modules = HandledModuleList()
        self.__drones = HandledDroneList()
        self.__cargo = HandledCargoList()
        self.__implants = HandledImplantBoosterList()
        self.__boosters = HandledImplantBoosterList()
        self.__projectedFits = HandledProjectedFitList()
        self.__projectedModules = HandledProjectedModList()
        self.__projectedDrones = HandledProjectedDroneList()
        self.__character = None
        self.__owner = None
        self.shipID = None
        self.projected = False
        self.name = ""
        self.fleet = None
        self.boostsFits = set()
        self.gangBoosts = None
        self.timestamp = time.time()
        self.ecmProjectedStr = 1
        self.modeID = None
        self.build()

    @reconstructor
    def init(self):
        self.build()

    def build(self):
        from eos import db
        self.__extraDrains = []
        self.__ehp = None
        self.__weaponDPS = None
        self.__minerYield = None
        self.__weaponVolley = None
        self.__droneDPS = None
        self.__droneVolley = None
        self.__droneYield = None
        self.__sustainableTank = None
        self.__effectiveSustainableTank = None
        self.__effectiveTank = None
        self.__calculated = False
        self.__capStable = None
        self.__capState = None
        self.__capUsed = None
        self.__capRecharge = None
        self.__calculatedTargets = []
        self.factorReload = False
        self.fleet = None
        self.boostsFits = set()
        self.gangBoosts = None
        self.ecmProjectedStr = 1
        self.extraAttributes = ModifiedAttributeDict(self)
        self.extraAttributes.original = self.EXTRA_ATTRIBUTES
        self.ship = Ship(db.getItem(
            self.shipID)) if self.shipID is not None else None
        if self.ship is not None:
            self.mode = self.ship.checkModeItem(
                db.getItem(self.modeID) if self.modeID else None)
        else:
            self.mode = None

    @property
    def targetResists(self):
        return self.__targetResists

    @targetResists.setter
    def targetResists(self, targetResists):
        self.__targetResists = targetResists
        self.__weaponDPS = None
        self.__weaponVolley = None
        self.__droneDPS = None
        self.__droneVolley = None

    @property
    def damagePattern(self):
        return self.__damagePattern

    @damagePattern.setter
    def damagePattern(self, damagePattern):
        self.__damagePattern = damagePattern
        self.__ehp = None
        self.__effectiveTank = None

    @property
    def mode(self):
        return self._mode

    @mode.setter
    def mode(self, mode):
        self._mode = mode
        self.modeID = mode.item.ID if mode is not None else None

    @property
    def character(self):
        return self.__character if self.__character is not None else Character.getAll0(
        )

    @character.setter
    def character(self, char):
        self.__character = char

    @property
    def ship(self):
        return self.__ship

    @ship.setter
    def ship(self, ship):
        self.__ship = ship
        self.shipID = ship.item.ID if ship is not None else None
        #  set mode of new ship
        self.mode = self.ship.checkModeItem(None) if ship is not None else None

    @property
    def drones(self):
        return self.__drones

    @property
    def cargo(self):
        return self.__cargo

    @property
    def modules(self):
        return self.__modules

    @property
    def implants(self):
        return self.__implants

    @property
    def boosters(self):
        return self.__boosters

    @property
    def projectedModules(self):
        return self.__projectedModules

    @property
    def projectedFits(self):
        return self.__projectedFits

    @property
    def projectedDrones(self):
        return self.__projectedDrones

    @property
    def weaponDPS(self):
        if self.__weaponDPS is None:
            self.calculateWeaponStats()

        return self.__weaponDPS

    @property
    def weaponVolley(self):
        if self.__weaponVolley is None:
            self.calculateWeaponStats()

        return self.__weaponVolley

    @property
    def droneDPS(self):
        if self.__droneDPS is None:
            self.calculateWeaponStats()

        return self.__droneDPS

    @property
    def droneVolley(self):
        if self.__droneVolley is None:
            self.calculateWeaponStats()

        return self.__droneVolley

    @property
    def totalDPS(self):
        return self.droneDPS + self.weaponDPS

    @property
    def totalVolley(self):
        return self.droneVolley + self.weaponVolley

    @property
    def minerYield(self):
        if self.__minerYield is None:
            self.calculateMiningStats()

        return self.__minerYield

    @property
    def droneYield(self):
        if self.__droneYield is None:
            self.calculateMiningStats()

        return self.__droneYield

    @property
    def totalYield(self):
        return self.droneYield + self.minerYield

    @property
    def maxTargets(self):
        return min(self.extraAttributes["maxTargetsLockedFromSkills"],
                   self.ship.getModifiedItemAttr("maxLockedTargets"))

    @property
    def maxTargetRange(self):
        return self.ship.getModifiedItemAttr("maxTargetRange")

    @property
    def scanStrength(self):
        return max([
            self.ship.getModifiedItemAttr("scan%sStrength" % scanType)
            for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric")
        ])

    @property
    def scanType(self):
        maxStr = -1
        type = None
        for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric"):
            currStr = self.ship.getModifiedItemAttr("scan%sStrength" %
                                                    scanType)
            if currStr > maxStr:
                maxStr = currStr
                type = scanType
            elif currStr == maxStr:
                type = "Multispectral"

        return type

    @property
    def jamChance(self):
        return (1 - self.ecmProjectedStr) * 100

    @property
    def alignTime(self):
        agility = self.ship.getModifiedItemAttr("agility")
        mass = self.ship.getModifiedItemAttr("mass")

        return -log(0.25) * agility * mass / 1000000

    @property
    def appliedImplants(self):
        implantsBySlot = {}
        if self.character:
            for implant in self.character.implants:
                implantsBySlot[implant.slot] = implant

        for implant in self.implants:
            implantsBySlot[implant.slot] = implant

        return implantsBySlot.values()

    @validates("ID", "ownerID", "shipID")
    def validator(self, key, val):
        map = {
            "ID": lambda val: isinstance(val, int),
            "ownerID": lambda val: isinstance(val, int),
            "shipID": lambda val: isinstance(val, int) or val is None
        }

        if map[key](val) == False:
            raise ValueError(str(val) + " is not a valid value for " + key)
        else:
            return val

    def clear(self):
        self.__effectiveTank = None
        self.__weaponDPS = None
        self.__minerYield = None
        self.__weaponVolley = None
        self.__effectiveSustainableTank = None
        self.__sustainableTank = None
        self.__droneDPS = None
        self.__droneVolley = None
        self.__droneYield = None
        self.__ehp = None
        self.__calculated = False
        self.__capStable = None
        self.__capState = None
        self.__capUsed = None
        self.__capRecharge = None
        self.ecmProjectedStr = 1
        del self.__calculatedTargets[:]
        del self.__extraDrains[:]

        if self.ship is not None: self.ship.clear()
        c = chain(self.modules, self.drones, self.boosters, self.implants,
                  self.projectedDrones, self.projectedModules,
                  self.projectedFits, (self.character, self.extraAttributes))
        for stuff in c:
            if stuff is not None and stuff != self: stuff.clear()

    #Methods to register and get the thing currently affecting the fit,
    #so we can correctly map "Affected By"
    def register(self, currModifier):
        self.__modifier = currModifier
        if hasattr(currModifier, "itemModifiedAttributes"):
            currModifier.itemModifiedAttributes.fit = self
        if hasattr(currModifier, "chargeModifiedAttributes"):
            currModifier.chargeModifiedAttributes.fit = self

    def getModifier(self):
        return self.__modifier

    def calculateModifiedAttributes(self,
                                    targetFit=None,
                                    withBoosters=False,
                                    dirtyStorage=None):
        refreshBoosts = False
        if withBoosters is True:
            refreshBoosts = True
        if dirtyStorage is not None and self.ID in dirtyStorage:
            refreshBoosts = True
        if dirtyStorage is not None:
            dirtyStorage.update(self.boostsFits)
        if self.fleet is not None and refreshBoosts is True:
            self.gangBoosts = self.fleet.recalculateLinear(
                withBoosters=withBoosters, dirtyStorage=dirtyStorage)
        elif self.fleet is None:
            self.gangBoosts = None
        if dirtyStorage is not None:
            try:
                dirtyStorage.remove(self.ID)
            except KeyError:
                pass
        # If we're not explicitly asked to project fit onto something,
        # set self as target fit
        if targetFit is None:
            targetFit = self
            forceProjected = False
        # Else, we're checking all target projectee fits
        elif targetFit not in self.__calculatedTargets:
            self.__calculatedTargets.append(targetFit)
            targetFit.calculateModifiedAttributes(dirtyStorage=dirtyStorage)
            forceProjected = True
        # Or do nothing if target fit is calculated
        else:
            return

        # If fit is calculated and we have nothing to do here, get out
        if self.__calculated == True and forceProjected == False:
            return

        # Mark fit as calculated
        self.__calculated = True

        # There's a few things to keep in mind here
        # 1: Early effects first, then regular ones, then late ones, regardless of anything else
        # 2: Some effects aren't implemented
        # 3: Some effects are implemented poorly and will just explode on us
        # 4: Errors should be handled gracefully and preferably without crashing unless serious
        for runTime in ("early", "normal", "late"):
            # Build a little chain of stuff
            # Avoid adding projected drones and modules when fit is projected onto self
            # TODO: remove this workaround when proper self-projection using virtual duplicate fits is implemented
            if forceProjected is True:
                c = chain((self.character, self.ship, self.mode), self.drones,
                          self.boosters, self.appliedImplants, self.modules)
            else:
                c = chain((self.character, self.ship, self.mode), self.drones,
                          self.boosters, self.appliedImplants, self.modules,
                          self.projectedDrones, self.projectedModules)

            if self.gangBoosts is not None:
                contextMap = {
                    Skill: "skill",
                    Ship: "ship",
                    Module: "module",
                    Implant: "implant"
                }
                for name, info in self.gangBoosts.iteritems():
                    # Unpack all data required to run effect properly
                    effect, thing = info[1]
                    if effect.runTime == runTime:
                        context = ("gang", contextMap[type(thing)])
                        if isinstance(thing, Module):
                            if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \
                            (effect.isType("active") and thing.state >= State.ACTIVE):
                                # Run effect, and get proper bonuses applied
                                try:
                                    self.register(thing)
                                    effect.handler(self, thing, context)
                                except:
                                    pass
                        else:
                            # Run effect, and get proper bonuses applied
                            try:
                                self.register(thing)
                                effect.handler(self, thing, context)
                            except:
                                pass

            for item in c:
                # Registering the item about to affect the fit allows us to track "Affected By" relations correctly
                if item is not None:
                    self.register(item)
                    item.calculateModifiedAttributes(self, runTime, False)
                    if forceProjected is True:
                        targetFit.register(item)
                        item.calculateModifiedAttributes(
                            targetFit, runTime, True)

        for fit in self.projectedFits:
            fit.calculateModifiedAttributes(self,
                                            withBoosters=withBoosters,
                                            dirtyStorage=dirtyStorage)

    def fill(self):
        """
        Fill this fit's module slots with enough dummy slots so that all slots are used.
        This is mostly for making the life of gui's easier.
        GUI's can call fill() and then stop caring about empty slots completely.
        """
        if self.ship is None:
            return

        for slotType in (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG,
                         Slot.SUBSYSTEM):
            amount = self.getSlotsFree(slotType, True)
            if amount > 0:
                for _ in xrange(int(amount)):
                    self.modules.append(Module.buildEmpty(slotType))

            if amount < 0:
                #Look for any dummies of that type to remove
                toRemove = []
                for mod in self.modules:
                    if mod.isEmpty and mod.slot == slotType:
                        toRemove.append(mod)
                        amount += 1
                        if amount == 0:
                            break
                for mod in toRemove:
                    self.modules.remove(mod)

    def unfill(self):
        for i in xrange(len(self.modules) - 1, -1, -1):
            mod = self.modules[i]
            if mod.isEmpty:
                del self.modules[i]

    @property
    def modCount(self):
        x = 0
        for i in xrange(len(self.modules) - 1, -1, -1):
            mod = self.modules[i]
            if not mod.isEmpty:
                x += 1
        return x

    def getItemAttrSum(self, dict, attr):
        amount = 0
        for mod in dict:
            add = mod.getModifiedItemAttr(attr)
            if add is not None:
                amount += add

        return amount

    def getItemAttrOnlineSum(self, dict, attr):
        amount = 0
        for mod in dict:
            add = mod.getModifiedItemAttr(
                attr) if mod.state >= State.ONLINE else None
            if add is not None:
                amount += add

        return amount

    def getHardpointsUsed(self, type):
        amount = 0
        for mod in self.modules:
            if mod.hardpoint is type and not mod.isEmpty:
                amount += 1

        return amount

    def getSlotsUsed(self, type, countDummies=False):
        amount = 0
        for mod in self.modules:
            if mod.slot is type and (not mod.isEmpty or countDummies):
                amount += 1

        return amount

    def getSlotsFree(self, type, countDummies=False):
        slots = {
            Slot.LOW: "lowSlots",
            Slot.MED: "medSlots",
            Slot.HIGH: "hiSlots",
            Slot.RIG: "rigSlots",
            Slot.SUBSYSTEM: "maxSubSystems"
        }

        if type == Slot.MODE:
            # Mode slot doesn't really exist, return default 0
            return 0

        slotsUsed = self.getSlotsUsed(type, countDummies)
        totalSlots = self.ship.getModifiedItemAttr(slots[type]) or 0
        return int(totalSlots - slotsUsed)

    @property
    def calibrationUsed(self):
        return self.getItemAttrSum(self.modules, 'upgradeCost')

    @property
    def pgUsed(self):
        return self.getItemAttrOnlineSum(self.modules, "power")

    @property
    def cpuUsed(self):
        return self.getItemAttrOnlineSum(self.modules, "cpu")

    @property
    def droneBandwidthUsed(self):
        amount = 0
        for d in self.drones:
            amount += d.getModifiedItemAttr(
                "droneBandwidthUsed") * d.amountActive

        return amount

    @property
    def droneBayUsed(self):
        amount = 0
        for d in self.drones:
            amount += d.item.volume * d.amount

        return amount

    @property
    def cargoBayUsed(self):
        amount = 0
        for c in self.cargo:
            amount += c.getModifiedItemAttr("volume") * c.amount

        return amount

    @property
    def activeDrones(self):
        amount = 0
        for d in self.drones:
            amount += d.amountActive

        return amount

    # Expresses how difficult a target is to probe down with scan probes
    # If this is <1.08, the ship is unproabeable
    @property
    def probeSize(self):
        sigRad = self.ship.getModifiedItemAttr("signatureRadius")
        sensorStr = float(self.scanStrength)
        probeSize = sigRad / sensorStr if sensorStr != 0 else None
        # http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1532170&page=2#42
        if probeSize is not None:
            # http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333691
            # http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333763
            # Tests by tester128 and several conclusions by me, prove that cap is in range
            # from 1.1 to 1.12, we're picking average value
            probeSize = max(probeSize, 1.11)
        return probeSize

    @property
    def warpSpeed(self):
        base = self.ship.getModifiedItemAttr("baseWarpSpeed") or 1
        multiplier = self.ship.getModifiedItemAttr("warpSpeedMultiplier") or 1
        return base * multiplier

    @property
    def maxWarpDistance(self):
        capacity = self.ship.getModifiedItemAttr("capacitorCapacity")
        mass = self.ship.getModifiedItemAttr("mass")
        warpCapNeed = self.ship.getModifiedItemAttr("warpCapacitorNeed")
        return capacity / (mass * warpCapNeed)

    @property
    def capStable(self):
        if self.__capStable is None:
            self.simulateCap()

        return self.__capStable

    @property
    def capState(self):
        """
        If the cap is stable, the capacitor state is the % at which it is stable.
        If the cap is unstable, this is the amount of time before it runs out
        """
        if self.__capState is None:
            self.simulateCap()

        return self.__capState

    @property
    def capUsed(self):
        if self.__capUsed is None:
            self.simulateCap()

        return self.__capUsed

    @property
    def capRecharge(self):
        if self.__capRecharge is None:
            self.simulateCap()

        return self.__capRecharge

    @property
    def sustainableTank(self):
        if self.__sustainableTank is None:
            self.calculateSustainableTank()

        return self.__sustainableTank

    def calculateSustainableTank(self, effective=True):
        if self.__sustainableTank is None:
            if self.capStable:
                sustainable = {}
                sustainable["armorRepair"] = self.extraAttributes[
                    "armorRepair"]
                sustainable["shieldRepair"] = self.extraAttributes[
                    "shieldRepair"]
                sustainable["hullRepair"] = self.extraAttributes["hullRepair"]
            else:
                sustainable = {}

                repairers = []
                #Map a repairer type to the attribute it uses
                groupAttrMap = {
                    "Armor Repair Unit": "armorDamageAmount",
                    "Fueled Armor Repairer": "armorDamageAmount",
                    "Hull Repair Unit": "structureDamageAmount",
                    "Shield Booster": "shieldBonus",
                    "Fueled Shield Booster": "shieldBonus",
                    "Remote Armor Repairer": "armorDamageAmount",
                    "Remote Shield Booster": "shieldBonus"
                }
                #Map repairer type to attribute
                groupStoreMap = {
                    "Armor Repair Unit": "armorRepair",
                    "Hull Repair Unit": "hullRepair",
                    "Shield Booster": "shieldRepair",
                    "Fueled Shield Booster": "shieldRepair",
                    "Remote Armor Repairer": "armorRepair",
                    "Remote Shield Booster": "shieldRepair",
                    "Fueled Armor Repairer": "armorRepair",
                }

                capUsed = self.capUsed
                for attr in ("shieldRepair", "armorRepair", "hullRepair"):
                    sustainable[attr] = self.extraAttributes[attr]
                    dict = self.extraAttributes.getAfflictions(attr)
                    if self in dict:
                        for mod, _, amount, used in dict[self]:
                            if not used:
                                continue
                            if mod.projected is False:
                                usesCap = True
                                try:
                                    if mod.capUse:
                                        capUsed -= mod.capUse
                                    else:
                                        usesCap = False
                                except AttributeError:
                                    usesCap = False
                                # Modules which do not use cap are not penalized based on cap use
                                if usesCap:
                                    cycleTime = mod.getModifiedItemAttr(
                                        "duration")
                                    amount = mod.getModifiedItemAttr(
                                        groupAttrMap[mod.item.group.name])
                                    sustainable[attr] -= amount / (cycleTime /
                                                                   1000.0)
                                    repairers.append(mod)

                #Sort repairers by efficiency. We want to use the most efficient repairers first
                repairers.sort(key=lambda mod: mod.getModifiedItemAttr(
                    groupAttrMap[mod.item.group.name]) / mod.
                               getModifiedItemAttr("capacitorNeed"),
                               reverse=True)

                #Loop through every module until we're above peak recharge
                #Most efficient first, as we sorted earlier.
                #calculate how much the repper can rep stability & add to total
                totalPeakRecharge = self.capRecharge
                for mod in repairers:
                    if capUsed > totalPeakRecharge: break
                    cycleTime = mod.cycleTime
                    capPerSec = mod.capUse
                    if capPerSec is not None and cycleTime is not None:
                        #Check how much this repper can work
                        sustainability = min(1, (totalPeakRecharge - capUsed) /
                                             capPerSec)

                        #Add the sustainable amount
                        amount = mod.getModifiedItemAttr(
                            groupAttrMap[mod.item.group.name])
                        sustainable[groupStoreMap[
                            mod.item.group.name]] += sustainability * (
                                amount / (cycleTime / 1000.0))
                        capUsed += capPerSec

            sustainable["passiveShield"] = self.calculateShieldRecharge()
            self.__sustainableTank = sustainable

        return self.__sustainableTank

    def calculateCapRecharge(self, percent=PEAK_RECHARGE):
        capacity = self.ship.getModifiedItemAttr("capacitorCapacity")
        rechargeRate = self.ship.getModifiedItemAttr("rechargeRate") / 1000.0
        return 10 / rechargeRate * sqrt(percent) * (1 -
                                                    sqrt(percent)) * capacity

    def calculateShieldRecharge(self, percent=PEAK_RECHARGE):
        capacity = self.ship.getModifiedItemAttr("shieldCapacity")
        rechargeRate = self.ship.getModifiedItemAttr(
            "shieldRechargeRate") / 1000.0
        return 10 / rechargeRate * sqrt(percent) * (1 -
                                                    sqrt(percent)) * capacity

    def addDrain(self, cycleTime, capNeed, clipSize=0):
        self.__extraDrains.append((cycleTime, capNeed, clipSize))

    def removeDrain(self, i):
        del self.__extraDrains[i]

    def iterDrains(self):
        return self.__extraDrains.__iter__()

    def __generateDrain(self):
        drains = []
        capUsed = 0
        capAdded = 0
        for mod in self.modules:
            if mod.state >= State.ACTIVE:
                if mod.getModifiedItemAttr("capacitorNeed") != 0:
                    cycleTime = mod.rawCycleTime or 0
                    reactivationTime = mod.getModifiedItemAttr(
                        "moduleReactivationDelay") or 0
                    fullCycleTime = cycleTime + reactivationTime
                    if fullCycleTime > 0:
                        capNeed = mod.capUse
                        if capNeed > 0:
                            capUsed += capNeed
                        else:
                            capAdded -= capNeed

                        drains.append((int(fullCycleTime),
                                       mod.getModifiedItemAttr("capacitorNeed")
                                       or 0, mod.numShots or 0))

        for fullCycleTime, capNeed, clipSize in self.iterDrains():
            drains.append((int(fullCycleTime), capNeed, clipSize))
            if capNeed > 0:
                capUsed += capNeed / (fullCycleTime / 1000.0)
            else:
                capAdded += -capNeed / (fullCycleTime / 1000.0)

        return drains, capUsed, capAdded

    def simulateCap(self):
        drains, self.__capUsed, self.__capRecharge = self.__generateDrain()
        self.__capRecharge += self.calculateCapRecharge()
        if len(drains) > 0:
            sim = capSim.CapSimulator()
            sim.init(drains)
            sim.capacitorCapacity = self.ship.getModifiedItemAttr(
                "capacitorCapacity")
            sim.capacitorRecharge = self.ship.getModifiedItemAttr(
                "rechargeRate")
            sim.stagger = True
            sim.scale = False
            sim.t_max = 6 * 60 * 60 * 1000
            sim.reload = self.factorReload
            sim.run()

            capState = (sim.cap_stable_low +
                        sim.cap_stable_high) / (2 * sim.capacitorCapacity)
            self.__capStable = capState > 0
            self.__capState = min(100, capState *
                                  100) if self.__capStable else sim.t / 1000.0
        else:
            self.__capStable = True
            self.__capState = 100

    @property
    def hp(self):
        hp = {}
        for (type, attr) in (('shield', 'shieldCapacity'),
                             ('armor', 'armorHP'), ('hull', 'hp')):
            hp[type] = self.ship.getModifiedItemAttr(attr)

        return hp

    @property
    def ehp(self):
        if self.__ehp is None:
            if self.damagePattern is None:
                ehp = self.hp
            else:
                ehp = self.damagePattern.calculateEhp(self)
            self.__ehp = ehp

        return self.__ehp

    @property
    def tank(self):
        hps = {"passiveShield": self.calculateShieldRecharge()}
        for type in ("shield", "armor", "hull"):
            hps["%sRepair" % type] = self.extraAttributes["%sRepair" % type]

        return hps

    @property
    def effectiveTank(self):
        if self.__effectiveTank is None:
            if self.damagePattern is None:
                ehps = self.tank
            else:
                ehps = self.damagePattern.calculateEffectiveTank(
                    self, self.extraAttributes)

            self.__effectiveTank = ehps

        return self.__effectiveTank

    @property
    def effectiveSustainableTank(self):
        if self.__effectiveSustainableTank is None:
            if self.damagePattern is None:
                eshps = self.sustainableTank
            else:
                eshps = self.damagePattern.calculateEffectiveTank(
                    self, self.sustainableTank)

            self.__effectiveSustainableTank = eshps

        return self.__effectiveSustainableTank

    def calculateLockTime(self, radius):
        scanRes = self.ship.getModifiedItemAttr("scanResolution")
        if scanRes is not None and scanRes > 0:
            # Yes, this function returns time in seconds, not miliseconds.
            # 40,000 is indeed the correct constant here.
            return min(40000 / scanRes / asinh(radius)**2, 30 * 60)
        else:
            return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0

    def calculateMiningStats(self):
        minerYield = 0
        droneYield = 0

        for mod in self.modules:
            minerYield += mod.miningStats

        for drone in self.drones:
            droneYield += drone.miningStats

        self.__minerYield = minerYield
        self.__droneYield = droneYield

    def calculateWeaponStats(self):
        weaponDPS = 0
        droneDPS = 0
        weaponVolley = 0
        droneVolley = 0

        for mod in self.modules:
            dps, volley = mod.damageStats(self.targetResists)
            weaponDPS += dps
            weaponVolley += volley

        for drone in self.drones:
            dps, volley = drone.damageStats(self.targetResists)
            droneDPS += dps
            droneVolley += volley

        self.__weaponDPS = weaponDPS
        self.__weaponVolley = weaponVolley
        self.__droneDPS = droneDPS
        self.__droneVolley = droneVolley

    @property
    def fits(self):
        for mod in self.modules:
            if not mod.fits(self):
                return False

        return True

    def __deepcopy__(self, memo):
        copy = Fit()
        #Character and owner are not copied
        copy.character = self.__character
        copy.owner = self.owner
        copy.ship = deepcopy(self.ship, memo)
        copy.name = "%s copy" % self.name
        copy.damagePattern = self.damagePattern
        copy.targetResists = self.targetResists

        toCopy = ("modules", "drones", "cargo", "implants", "boosters",
                  "projectedModules", "projectedDrones")
        for name in toCopy:
            orig = getattr(self, name)
            c = getattr(copy, name)
            for i in orig:
                c.append(deepcopy(i, memo))

        for fit in self.projectedFits:
            copy.projectedFits.append(fit)

        return copy
Example #21
0
    def importEftCfg(shipname, contents, callback=None):
        """Handle import from EFT config store file"""

        # Check if we have such ship in database, bail if we don't
        sMkt = service.Market.getInstance()
        try:
            sMkt.getItem(shipname)
        except:
            return []  # empty list is expected

        # If client didn't take care of encoding file contents into Unicode,
        # do it using fallback encoding ourselves
        if isinstance(contents, str):
            contents = unicode(contents, "cp1252")

        fits = []  # List for fits
        fitIndices = []  # List for starting line numbers for each fit
        lines = re.split('[\n\r]+', contents)  # Separate string into lines

        for line in lines:
            # Detect fit header
            if line[:1] == "[" and line[-1:] == "]":
                # Line index where current fit starts
                startPos = lines.index(line)
                fitIndices.append(startPos)

        for i, startPos in enumerate(fitIndices):
            # End position is last file line if we're trying to get it for last fit,
            # or start position of next fit minus 1
            endPos = len(lines) if i == len(fitIndices) - 1 else fitIndices[i +
                                                                            1]

            # Finally, get lines for current fitting
            fitLines = lines[startPos:endPos]

            try:
                # Create fit object
                f = Fit()
                # Strip square brackets and pull out a fit name
                f.name = fitLines[0][1:-1]
                # Assign ship to fitting
                try:
                    f.ship = Ship(sMkt.getItem(shipname))
                except ValueError:
                    f.ship = Citadel(sMkt.getItem(shipname))

                moduleList = []
                for x in range(1, len(fitLines)):
                    line = fitLines[x]
                    if not line:
                        continue

                    # Parse line into some data we will need
                    misc = re.match(
                        "(Drones|Implant|Booster)_(Active|Inactive)=(.+)",
                        line)
                    cargo = re.match("Cargohold=(.+)", line)

                    if misc:
                        entityType = misc.group(1)
                        entityState = misc.group(2)
                        entityData = misc.group(3)
                        if entityType == "Drones":
                            droneData = re.match("(.+),([0-9]+)", entityData)
                            # Get drone name and attempt to detect drone number
                            droneName = droneData.group(
                                1) if droneData else entityData
                            droneAmount = int(
                                droneData.group(2)) if droneData else 1
                            # Bail if we can't get item or it's not from drone category
                            try:
                                droneItem = sMkt.getItem(
                                    droneName, eager="group.category")
                            except:
                                continue
                            if droneItem.category.name == "Drone":
                                # Add drone to the fitting
                                d = Drone(droneItem)
                                d.amount = droneAmount
                                if entityState == "Active":
                                    d.amountActive = droneAmount
                                elif entityState == "Inactive":
                                    d.amountActive = 0
                                f.drones.append(d)
                            elif droneItem.category.name == "Fighter":  # EFT saves fighter as drones
                                ft = Fighter(droneItem)
                                ft.amount = int(
                                    droneAmount
                                ) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
                                f.fighters.append(ft)
                            else:
                                continue
                        elif entityType == "Implant":
                            # Bail if we can't get item or it's not from implant category
                            try:
                                implantItem = sMkt.getItem(
                                    entityData, eager="group.category")
                            except:
                                continue
                            if implantItem.category.name != "Implant":
                                continue
                            # Add implant to the fitting
                            imp = Implant(implantItem)
                            if entityState == "Active":
                                imp.active = True
                            elif entityState == "Inactive":
                                imp.active = False
                            f.implants.append(imp)
                        elif entityType == "Booster":
                            # Bail if we can't get item or it's not from implant category
                            try:
                                boosterItem = sMkt.getItem(
                                    entityData, eager="group.category")
                            except:
                                continue
                            # All boosters have implant category
                            if boosterItem.category.name != "Implant":
                                continue
                            # Add booster to the fitting
                            b = Booster(boosterItem)
                            if entityState == "Active":
                                b.active = True
                            elif entityState == "Inactive":
                                b.active = False
                            f.boosters.append(b)
                    # If we don't have any prefixes, then it's a module
                    elif cargo:
                        cargoData = re.match("(.+),([0-9]+)", cargo.group(1))
                        cargoName = cargoData.group(
                            1) if cargoData else cargo.group(1)
                        cargoAmount = int(
                            cargoData.group(2)) if cargoData else 1
                        # Bail if we can't get item
                        try:
                            item = sMkt.getItem(cargoName)
                        except:
                            continue
                        # Add Cargo to the fitting
                        c = Cargo(item)
                        c.amount = cargoAmount
                        f.cargo.append(c)
                    else:
                        withCharge = re.match("(.+),(.+)", line)
                        modName = withCharge.group(1) if withCharge else line
                        chargeName = withCharge.group(
                            2) if withCharge else None
                        # If we can't get module item, skip it
                        try:
                            modItem = sMkt.getItem(modName)
                        except:
                            continue

                        # Create module
                        m = Module(modItem)

                        # Add subsystems before modules to make sure T3 cruisers have subsystems installed
                        if modItem.category.name == "Subsystem":
                            if m.fits(f):
                                f.modules.append(m)
                        else:
                            m.owner = f
                            # Activate mod if it is activable
                            if m.isValidState(State.ACTIVE):
                                m.state = State.ACTIVE
                            # Add charge to mod if applicable, on any errors just don't add anything
                            if chargeName:
                                try:
                                    chargeItem = sMkt.getItem(
                                        chargeName, eager="group.category")
                                    if chargeItem.category.name == "Charge":
                                        m.charge = chargeItem
                                except:
                                    pass
                            # Append module to fit
                            moduleList.append(m)

                # Recalc to get slot numbers correct for T3 cruisers
                service.Fit.getInstance().recalc(f)

                for module in moduleList:
                    if module.fits(f):
                        f.modules.append(module)

                # Append fit to list of fits
                fits.append(f)

                if callback:
                    wx.CallAfter(callback, None)
            # Skip fit silently if we get an exception
            except Exception:
                pass

        return fits
Example #22
0
File: fit.py Project: mickoes/Pyfa
class Fit(object):
    """Represents a fitting, with modules, ship, implants, etc."""
    EXTRA_ATTRIBUTES = {"armorRepair": 0,
                        "hullRepair": 0,
                        "shieldRepair": 0,
                        "maxActiveDrones": 0,
                        "maxTargetsLockedFromSkills": 2,
                        "droneControlRange": 20000,
                        "cloaked": False,
                        "siege": False}

    PEAK_RECHARGE = 0.25

    def __init__(self):
        self.__modules = HandledModuleList()
        self.__drones = HandledDroneList()
        self.__cargo = HandledCargoList()
        self.__implants = HandledImplantBoosterList()
        self.__boosters = HandledImplantBoosterList()
        self.__projectedFits = HandledProjectedFitList()
        self.__projectedModules = HandledProjectedModList()
        self.__projectedDrones = HandledProjectedDroneList()
        self.__character = None
        self.__owner = None
        self.shipID = None
        self.projected = False
        self.name = ""
        self.fleet = None
        self.boostsFits = set()
        self.gangBoosts = None
        self.timestamp = time.time()
        self.ecmProjectedStr = 1
        self.modeID = None
        self.build()

    @reconstructor
    def init(self):
        self.build()

    def build(self):
        from eos import db
        self.__extraDrains = []
        self.__ehp = None
        self.__weaponDPS = None
        self.__minerYield = None
        self.__weaponVolley = None
        self.__droneDPS = None
        self.__droneVolley = None
        self.__droneYield = None
        self.__sustainableTank = None
        self.__effectiveSustainableTank = None
        self.__effectiveTank = None
        self.__calculated = False
        self.__capStable = None
        self.__capState = None
        self.__capUsed = None
        self.__capRecharge = None
        self.__calculatedTargets = []
        self.factorReload = False
        self.fleet = None
        self.boostsFits = set()
        self.gangBoosts = None
        self.ecmProjectedStr = 1
        self.extraAttributes = ModifiedAttributeDict(self)
        self.extraAttributes.original = self.EXTRA_ATTRIBUTES
        self.ship = Ship(db.getItem(self.shipID)) if self.shipID is not None else None
        if self.ship is not None:
            self.mode = self.ship.checkModeItem(db.getItem(self.modeID) if self.modeID else None)
        else:
            self.mode = None

    @property
    def targetResists(self):
        return self.__targetResists

    @targetResists.setter
    def targetResists(self, targetResists):
        self.__targetResists = targetResists
        self.__weaponDPS = None
        self.__weaponVolley = None
        self.__droneDPS = None
        self.__droneVolley = None

    @property
    def damagePattern(self):
        return self.__damagePattern

    @damagePattern.setter
    def damagePattern(self, damagePattern):
        self.__damagePattern = damagePattern
        self.__ehp = None
        self.__effectiveTank = None

    @property
    def mode(self):
        return self._mode

    @mode.setter
    def mode(self, mode):
        self._mode = mode
        self.modeID = mode.item.ID if mode is not None else None

    @property
    def character(self):
        return self.__character if self.__character is not None else Character.getAll0()

    @character.setter
    def character(self, char):
        self.__character = char

    @property
    def ship(self):
        return self.__ship

    @ship.setter
    def ship(self, ship):
        self.__ship = ship
        self.shipID = ship.item.ID if ship is not None else None
        #  set mode of new ship
        self.mode = self.ship.checkModeItem(None) if ship is not None else None

    @property
    def drones(self):
        return self.__drones

    @property
    def cargo(self):
        return self.__cargo

    @property
    def modules(self):
        return self.__modules

    @property
    def implants(self):
        return self.__implants

    @property
    def boosters(self):
        return self.__boosters

    @property
    def projectedModules(self):
        return self.__projectedModules

    @property
    def projectedFits(self):
        return self.__projectedFits

    @property
    def projectedDrones(self):
        return self.__projectedDrones

    @property
    def weaponDPS(self):
        if self.__weaponDPS is None:
            self.calculateWeaponStats()

        return self.__weaponDPS

    @property
    def weaponVolley(self):
        if self.__weaponVolley is None:
            self.calculateWeaponStats()

        return self.__weaponVolley

    @property
    def droneDPS(self):
        if self.__droneDPS is None:
            self.calculateWeaponStats()

        return self.__droneDPS

    @property
    def droneVolley(self):
        if self.__droneVolley is None:
            self.calculateWeaponStats()

        return self.__droneVolley

    @property
    def totalDPS(self):
        return self.droneDPS + self.weaponDPS

    @property
    def totalVolley(self):
        return self.droneVolley + self.weaponVolley

    @property
    def minerYield(self):
        if self.__minerYield is None:
            self.calculateMiningStats()

        return self.__minerYield

    @property
    def droneYield(self):
        if self.__droneYield is None:
            self.calculateMiningStats()

        return self.__droneYield

    @property
    def totalYield(self):
        return self.droneYield + self.minerYield

    @property
    def maxTargets(self):
        return min(self.extraAttributes["maxTargetsLockedFromSkills"], self.ship.getModifiedItemAttr("maxLockedTargets"))

    @property
    def maxTargetRange(self):
        return self.ship.getModifiedItemAttr("maxTargetRange")

    @property
    def scanStrength(self):
        return max([self.ship.getModifiedItemAttr("scan%sStrength" % scanType)
                    for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric")])

    @property
    def scanType(self):
        maxStr = -1
        type = None
        for scanType in ("Magnetometric", "Ladar", "Radar", "Gravimetric"):
            currStr = self.ship.getModifiedItemAttr("scan%sStrength" % scanType)
            if currStr > maxStr:
                maxStr = currStr
                type = scanType
            elif currStr == maxStr:
                type = "Multispectral"

        return type

    @property
    def jamChance(self):
        return (1-self.ecmProjectedStr)*100

    @property
    def alignTime(self):
        agility = self.ship.getModifiedItemAttr("agility")
        mass = self.ship.getModifiedItemAttr("mass")

        return -log(0.25) * agility * mass / 1000000

    @property
    def appliedImplants(self):
        implantsBySlot = {}
        if self.character:
            for implant in self.character.implants:
                implantsBySlot[implant.slot] = implant

        for implant in self.implants:
            implantsBySlot[implant.slot] = implant

        return implantsBySlot.values()

    @validates("ID", "ownerID", "shipID")
    def validator(self, key, val):
        map = {"ID": lambda val: isinstance(val, int),
               "ownerID" : lambda val: isinstance(val, int),
               "shipID" : lambda val: isinstance(val, int) or val is None}

        if map[key](val) == False: raise ValueError(str(val) + " is not a valid value for " + key)
        else: return val

    def clear(self):
        self.__effectiveTank = None
        self.__weaponDPS = None
        self.__minerYield = None
        self.__weaponVolley = None
        self.__effectiveSustainableTank = None
        self.__sustainableTank = None
        self.__droneDPS = None
        self.__droneVolley = None
        self.__droneYield = None
        self.__ehp = None
        self.__calculated = False
        self.__capStable = None
        self.__capState = None
        self.__capUsed = None
        self.__capRecharge = None
        self.ecmProjectedStr = 1
        del self.__calculatedTargets[:]
        del self.__extraDrains[:]

        if self.ship is not None: self.ship.clear()
        c = chain(self.modules, self.drones, self.boosters, self.implants, self.projectedDrones, self.projectedModules, self.projectedFits, (self.character, self.extraAttributes))
        for stuff in c:
            if stuff is not None and stuff != self: stuff.clear()

    #Methods to register and get the thing currently affecting the fit,
    #so we can correctly map "Affected By"
    def register(self, currModifier):
        self.__modifier = currModifier
        if hasattr(currModifier, "itemModifiedAttributes"):
            currModifier.itemModifiedAttributes.fit = self
        if hasattr(currModifier, "chargeModifiedAttributes"):
            currModifier.chargeModifiedAttributes.fit = self

    def getModifier(self):
        return self.__modifier

    def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None):
        refreshBoosts = False
        if withBoosters is True:
            refreshBoosts = True
        if dirtyStorage is not None and self.ID in dirtyStorage:
            refreshBoosts = True
        if dirtyStorage is not None:
            dirtyStorage.update(self.boostsFits)
        if self.fleet is not None and refreshBoosts is True:
            self.gangBoosts = self.fleet.recalculateLinear(withBoosters=withBoosters, dirtyStorage=dirtyStorage)
        elif self.fleet is None:
            self.gangBoosts = None
        if dirtyStorage is not None:
            try:
                dirtyStorage.remove(self.ID)
            except KeyError:
                pass
        # If we're not explicitly asked to project fit onto something,
        # set self as target fit
        if targetFit is None:
            targetFit = self
            forceProjected = False
        # Else, we're checking all target projectee fits
        elif targetFit not in self.__calculatedTargets:
            self.__calculatedTargets.append(targetFit)
            targetFit.calculateModifiedAttributes(dirtyStorage=dirtyStorage)
            forceProjected = True
        # Or do nothing if target fit is calculated
        else:
            return

        # If fit is calculated and we have nothing to do here, get out
        if self.__calculated == True and forceProjected == False:
            return

        # Mark fit as calculated
        self.__calculated = True

        # There's a few things to keep in mind here
        # 1: Early effects first, then regular ones, then late ones, regardless of anything else
        # 2: Some effects aren't implemented
        # 3: Some effects are implemented poorly and will just explode on us
        # 4: Errors should be handled gracefully and preferably without crashing unless serious
        for runTime in ("early", "normal", "late"):
            # Build a little chain of stuff
            # Avoid adding projected drones and modules when fit is projected onto self
            # TODO: remove this workaround when proper self-projection using virtual duplicate fits is implemented
            if forceProjected is True:
                c = chain((self.character, self.ship, self.mode), self.drones, self.boosters, self.appliedImplants, self.modules)
            else:
                c = chain((self.character, self.ship, self.mode), self.drones, self.boosters, self.appliedImplants, self.modules,
                          self.projectedDrones, self.projectedModules)

            if self.gangBoosts is not None:
                contextMap = {Skill: "skill",
                              Ship: "ship",
                              Module: "module",
                              Implant: "implant"}
                for name, info in self.gangBoosts.iteritems():
                    # Unpack all data required to run effect properly
                    effect, thing = info[1]
                    if effect.runTime == runTime:
                        context = ("gang", contextMap[type(thing)])
                        if isinstance(thing, Module):
                            if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \
                            (effect.isType("active") and thing.state >= State.ACTIVE):
                                # Run effect, and get proper bonuses applied
                                try:
                                    self.register(thing)
                                    effect.handler(self, thing, context)
                                except:
                                    pass
                        else:
                            # Run effect, and get proper bonuses applied
                            try:
                                self.register(thing)
                                effect.handler(self, thing, context)
                            except:
                                pass

            for item in c:
                # Registering the item about to affect the fit allows us to track "Affected By" relations correctly
                if item is not None:
                    self.register(item)
                    item.calculateModifiedAttributes(self, runTime, False)
                    if forceProjected is True:
                        targetFit.register(item)
                        item.calculateModifiedAttributes(targetFit, runTime, True)

        for fit in self.projectedFits:
            fit.calculateModifiedAttributes(self, withBoosters=withBoosters, dirtyStorage=dirtyStorage)

    def fill(self):
        """
        Fill this fit's module slots with enough dummy slots so that all slots are used.
        This is mostly for making the life of gui's easier.
        GUI's can call fill() and then stop caring about empty slots completely.
        """
        if self.ship is None:
            return

        for slotType in (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM):
            amount = self.getSlotsFree(slotType, True)
            if amount > 0:
                for _ in xrange(int(amount)):
                    self.modules.append(Module.buildEmpty(slotType))

            if amount < 0:
                #Look for any dummies of that type to remove
                toRemove = []
                for mod in self.modules:
                    if mod.isEmpty and mod.slot == slotType:
                        toRemove.append(mod)
                        amount += 1
                        if amount == 0:
                            break
                for mod in toRemove:
                    self.modules.remove(mod)

    def unfill(self):
        for i in xrange(len(self.modules) - 1, -1, -1):
            mod = self.modules[i]
            if mod.isEmpty:
                del self.modules[i]

    @property
    def modCount(self):
        x=0
        for i in xrange(len(self.modules) - 1, -1, -1):
            mod = self.modules[i]
            if not mod.isEmpty:
                x += 1
        return x

    def getItemAttrSum(self, dict, attr):
        amount = 0
        for mod in dict:
            add = mod.getModifiedItemAttr(attr)
            if add is not None:
                amount += add

        return amount

    def getItemAttrOnlineSum(self, dict, attr):
        amount = 0
        for mod in dict:
            add = mod.getModifiedItemAttr(attr) if mod.state >= State.ONLINE else None
            if add is not None:
                amount += add

        return amount

    def getHardpointsUsed(self, type):
        amount = 0
        for mod in self.modules:
            if mod.hardpoint is type and not mod.isEmpty:
                amount += 1

        return amount

    def getSlotsUsed(self, type, countDummies=False):
        amount = 0
        for mod in self.modules:
            if mod.slot is type and (not mod.isEmpty or countDummies):
                amount += 1

        return amount

    def getSlotsFree(self, type, countDummies=False):
        slots = {Slot.LOW: "lowSlots",
                 Slot.MED: "medSlots",
                 Slot.HIGH: "hiSlots",
                 Slot.RIG: "rigSlots",
                 Slot.SUBSYSTEM: "maxSubSystems"}

        if type == Slot.MODE:
            # Mode slot doesn't really exist, return default 0
            return 0

        slotsUsed = self.getSlotsUsed(type, countDummies)
        totalSlots = self.ship.getModifiedItemAttr(slots[type]) or 0
        return int(totalSlots - slotsUsed)

    @property
    def calibrationUsed(self):
        return self.getItemAttrSum(self.modules, 'upgradeCost')

    @property
    def pgUsed(self):
        return self.getItemAttrOnlineSum(self.modules, "power")

    @property
    def cpuUsed(self):
        return self.getItemAttrOnlineSum(self.modules, "cpu")

    @property
    def droneBandwidthUsed(self):
        amount = 0
        for d in self.drones:
            amount += d.getModifiedItemAttr("droneBandwidthUsed") * d.amountActive

        return amount

    @property
    def droneBayUsed(self):
        amount = 0
        for d in self.drones:
            amount += d.item.volume * d.amount

        return amount

    @property
    def cargoBayUsed(self):
        amount = 0
        for c in self.cargo:
            amount += c.getModifiedItemAttr("volume") * c.amount

        return amount

    @property
    def activeDrones(self):
        amount = 0
        for d in self.drones:
            amount +=d.amountActive

        return amount

    # Expresses how difficult a target is to probe down with scan probes
    # If this is <1.08, the ship is unproabeable
    @property
    def probeSize(self):
        sigRad = self.ship.getModifiedItemAttr("signatureRadius")
        sensorStr = float(self.scanStrength)
        probeSize = sigRad / sensorStr if sensorStr != 0 else None
        # http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1532170&page=2#42
        if probeSize is not None:
            # http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333691
            # http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333763
            # Tests by tester128 and several conclusions by me, prove that cap is in range
            # from 1.1 to 1.12, we're picking average value
            probeSize = max(probeSize, 1.11)
        return probeSize

    @property
    def warpSpeed(self):
        base = self.ship.getModifiedItemAttr("baseWarpSpeed") or 1
        multiplier = self.ship.getModifiedItemAttr("warpSpeedMultiplier") or 1
        return base * multiplier

    @property
    def maxWarpDistance(self):
        capacity = self.ship.getModifiedItemAttr("capacitorCapacity")
        mass = self.ship.getModifiedItemAttr("mass")
        warpCapNeed = self.ship.getModifiedItemAttr("warpCapacitorNeed")
        return capacity / (mass * warpCapNeed)

    @property
    def capStable(self):
        if self.__capStable is None:
            self.simulateCap()

        return self.__capStable

    @property
    def capState(self):
        """
        If the cap is stable, the capacitor state is the % at which it is stable.
        If the cap is unstable, this is the amount of time before it runs out
        """
        if self.__capState is None:
            self.simulateCap()

        return self.__capState

    @property
    def capUsed(self):
        if self.__capUsed is None:
            self.simulateCap()

        return self.__capUsed

    @property
    def capRecharge(self):
        if self.__capRecharge is None:
            self.simulateCap()

        return self.__capRecharge


    @property
    def sustainableTank(self):
        if self.__sustainableTank is None:
            self.calculateSustainableTank()

        return self.__sustainableTank

    def calculateSustainableTank(self, effective=True):
        if self.__sustainableTank is None:
            if self.capStable:
                sustainable = {}
                sustainable["armorRepair"] = self.extraAttributes["armorRepair"]
                sustainable["shieldRepair"] = self.extraAttributes["shieldRepair"]
                sustainable["hullRepair"] = self.extraAttributes["hullRepair"]
            else:
                sustainable = {}

                repairers = []
                #Map a repairer type to the attribute it uses
                groupAttrMap = {"Armor Repair Unit": "armorDamageAmount",
                     "Fueled Armor Repairer": "armorDamageAmount",
                     "Hull Repair Unit": "structureDamageAmount",
                     "Shield Booster": "shieldBonus",
                     "Fueled Shield Booster": "shieldBonus",
                     "Remote Armor Repairer": "armorDamageAmount",
                     "Remote Shield Booster": "shieldBonus"}
                #Map repairer type to attribute
                groupStoreMap = {"Armor Repair Unit": "armorRepair",
                                 "Hull Repair Unit": "hullRepair",
                                 "Shield Booster": "shieldRepair",
                                 "Fueled Shield Booster": "shieldRepair",
                                 "Remote Armor Repairer": "armorRepair",
                                 "Remote Shield Booster": "shieldRepair",
                                 "Fueled Armor Repairer": "armorRepair",}

                capUsed = self.capUsed
                for attr in ("shieldRepair", "armorRepair", "hullRepair"):
                    sustainable[attr] = self.extraAttributes[attr]
                    dict = self.extraAttributes.getAfflictions(attr)
                    if self in dict:
                        for mod, _, amount, used in dict[self]:
                            if not used:
                                continue
                            if mod.projected is False:
                                usesCap = True
                                try:
                                    if mod.capUse:
                                        capUsed -= mod.capUse
                                    else:
                                        usesCap = False
                                except AttributeError:
                                    usesCap = False
                                # Modules which do not use cap are not penalized based on cap use
                                if usesCap:
                                    cycleTime = mod.getModifiedItemAttr("duration")
                                    amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
                                    sustainable[attr] -= amount / (cycleTime / 1000.0)
                                    repairers.append(mod)


                #Sort repairers by efficiency. We want to use the most efficient repairers first
                repairers.sort(key=lambda mod: mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name]) / mod.getModifiedItemAttr("capacitorNeed"), reverse = True)

                #Loop through every module until we're above peak recharge
                #Most efficient first, as we sorted earlier.
                #calculate how much the repper can rep stability & add to total
                totalPeakRecharge = self.capRecharge
                for mod in repairers:
                    if capUsed > totalPeakRecharge: break
                    cycleTime = mod.cycleTime
                    capPerSec = mod.capUse
                    if capPerSec is not None and cycleTime is not None:
                        #Check how much this repper can work
                        sustainability = min(1, (totalPeakRecharge - capUsed) / capPerSec)

                        #Add the sustainable amount
                        amount = mod.getModifiedItemAttr(groupAttrMap[mod.item.group.name])
                        sustainable[groupStoreMap[mod.item.group.name]] += sustainability * (amount / (cycleTime / 1000.0))
                        capUsed += capPerSec

            sustainable["passiveShield"] = self.calculateShieldRecharge()
            self.__sustainableTank = sustainable

        return self.__sustainableTank

    def calculateCapRecharge(self, percent = PEAK_RECHARGE):
        capacity = self.ship.getModifiedItemAttr("capacitorCapacity")
        rechargeRate = self.ship.getModifiedItemAttr("rechargeRate") / 1000.0
        return 10 / rechargeRate * sqrt(percent) * (1 - sqrt(percent)) * capacity

    def calculateShieldRecharge(self, percent = PEAK_RECHARGE):
        capacity = self.ship.getModifiedItemAttr("shieldCapacity")
        rechargeRate = self.ship.getModifiedItemAttr("shieldRechargeRate") / 1000.0
        return 10 / rechargeRate * sqrt(percent) * (1 - sqrt(percent)) * capacity

    def addDrain(self, cycleTime, capNeed, clipSize=0):
        self.__extraDrains.append((cycleTime, capNeed, clipSize))

    def removeDrain(self, i):
        del self.__extraDrains[i]

    def iterDrains(self):
        return self.__extraDrains.__iter__()

    def __generateDrain(self):
        drains = []
        capUsed = 0
        capAdded = 0
        for mod in self.modules:
            if mod.state >= State.ACTIVE:
                if mod.getModifiedItemAttr("capacitorNeed") != 0:
                    cycleTime = mod.rawCycleTime or 0
                    reactivationTime = mod.getModifiedItemAttr("moduleReactivationDelay") or 0
                    fullCycleTime = cycleTime + reactivationTime
                    if fullCycleTime > 0:
                        capNeed = mod.capUse
                        if capNeed > 0:
                            capUsed += capNeed
                        else:
                            capAdded -= capNeed

                        drains.append((int(fullCycleTime), mod.getModifiedItemAttr("capacitorNeed") or 0, mod.numShots or 0))

        for fullCycleTime, capNeed, clipSize in self.iterDrains():
            drains.append((int(fullCycleTime), capNeed, clipSize))
            if capNeed > 0:
                capUsed += capNeed / (fullCycleTime / 1000.0)
            else:
                capAdded += -capNeed / (fullCycleTime / 1000.0)

        return drains, capUsed, capAdded

    def simulateCap(self):
        drains, self.__capUsed, self.__capRecharge = self.__generateDrain()
        self.__capRecharge += self.calculateCapRecharge()
        if len(drains) > 0:
            sim = capSim.CapSimulator()
            sim.init(drains)
            sim.capacitorCapacity = self.ship.getModifiedItemAttr("capacitorCapacity")
            sim.capacitorRecharge = self.ship.getModifiedItemAttr("rechargeRate")
            sim.stagger = True
            sim.scale = False
            sim.t_max = 6 * 60 * 60 * 1000
            sim.reload = self.factorReload
            sim.run()

            capState = (sim.cap_stable_low + sim.cap_stable_high) / (2 * sim.capacitorCapacity)
            self.__capStable = capState > 0
            self.__capState = min(100, capState * 100) if self.__capStable else sim.t / 1000.0
        else:
            self.__capStable = True
            self.__capState = 100

    @property
    def hp(self):
        hp = {}
        for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
            hp[type] = self.ship.getModifiedItemAttr(attr)

        return hp

    @property
    def ehp(self):
        if self.__ehp is None:
            if self.damagePattern is None:
                ehp = self.hp
            else:
                ehp = self.damagePattern.calculateEhp(self)
            self.__ehp = ehp

        return self.__ehp

    @property
    def tank(self):
        hps = {"passiveShield" : self.calculateShieldRecharge()}
        for type in ("shield", "armor", "hull"):
            hps["%sRepair" % type] = self.extraAttributes["%sRepair" % type]

        return hps

    @property
    def effectiveTank(self):
        if self.__effectiveTank is None:
            if self.damagePattern is None:
                ehps = self.tank
            else:
                ehps = self.damagePattern.calculateEffectiveTank(self, self.extraAttributes)

            self.__effectiveTank = ehps

        return self.__effectiveTank

    @property
    def effectiveSustainableTank(self):
        if self.__effectiveSustainableTank is None:
            if self.damagePattern is None:
                eshps = self.sustainableTank
            else:
                eshps = self.damagePattern.calculateEffectiveTank(self, self.sustainableTank)

            self.__effectiveSustainableTank = eshps

        return self.__effectiveSustainableTank


    def calculateLockTime(self, radius):
        scanRes = self.ship.getModifiedItemAttr("scanResolution")
        if scanRes is not None and scanRes > 0:
            # Yes, this function returns time in seconds, not miliseconds.
            # 40,000 is indeed the correct constant here.
            return min(40000 / scanRes / asinh(radius)**2, 30*60)
        else:
            return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0

    def calculateMiningStats(self):
        minerYield = 0
        droneYield = 0

        for mod in self.modules:
            minerYield += mod.miningStats

        for drone in self.drones:
            droneYield += drone.miningStats

        self.__minerYield = minerYield
        self.__droneYield = droneYield

    def calculateWeaponStats(self):
        weaponDPS = 0
        droneDPS = 0
        weaponVolley = 0
        droneVolley = 0

        for mod in self.modules:
            dps, volley = mod.damageStats(self.targetResists)
            weaponDPS += dps
            weaponVolley += volley

        for drone in self.drones:
            dps, volley = drone.damageStats(self.targetResists)
            droneDPS += dps
            droneVolley += volley

        self.__weaponDPS = weaponDPS
        self.__weaponVolley = weaponVolley
        self.__droneDPS = droneDPS
        self.__droneVolley = droneVolley

    @property
    def fits(self):
        for mod in self.modules:
            if not mod.fits(self):
                return False

        return True

    def __deepcopy__(self, memo):
        copy = Fit()
        #Character and owner are not copied
        copy.character = self.__character
        copy.owner = self.owner
        copy.ship = deepcopy(self.ship, memo)
        copy.name = "%s copy" % self.name
        copy.damagePattern = self.damagePattern
        copy.targetResists = self.targetResists

        toCopy = ("modules", "drones", "cargo", "implants", "boosters", "projectedModules", "projectedDrones")
        for name in toCopy:
            orig = getattr(self, name)
            c = getattr(copy, name)
            for i in orig:
                c.append(deepcopy(i, memo))

        for fit in self.projectedFits:
            copy.projectedFits.append(fit)

        return copy
Example #23
0
    def importEft(eftString):
        sMkt = service.Market.getInstance()
        offineSuffix = " /OFFLINE"

        fit = Fit()
        eftString = eftString.strip()
        lines = re.split('[\n\r]+', eftString)
        info = lines[0][1:-1].split(",", 1)

        if len(info) == 2:
            shipType = info[0].strip()
            fitName = info[1].strip()
        else:
            shipType = info[0].strip()
            fitName = "Imported %s" % shipType

        try:
            ship = sMkt.getItem(shipType)
            fit.ship = Ship(ship)
            fit.name = fitName
        except:
            return

        # maintain map of drones and their quantities
        droneMap = {}
        cargoMap = {}
        for i in range(1, len(lines)):
            ammoName = None
            extraAmount = None

            line = lines[i].strip()
            if not line:
                continue

            setOffline = line.endswith(offineSuffix)
            if setOffline is True:
                # remove offline suffix from line
                line = line[:len(line) - len(offineSuffix)]

            modAmmo = line.split(",")
            # matches drone and cargo with x{qty}
            modExtra = modAmmo[0].split(" x")

            if len(modAmmo) == 2:
                # line with a module and ammo
                ammoName = modAmmo[1].strip()
                modName = modAmmo[0].strip()
            elif len(modExtra) == 2:
                # line with drone/cargo and qty
                extraAmount = modExtra[1].strip()
                modName = modExtra[0].strip()
            else:
                # line with just module
                modName = modExtra[0].strip()

            try:
                # get item information. If we are on a Drone/Cargo line, throw out cargo
                item = sMkt.getItem(modName, eager="group.category")
            except:
                # if no data can be found (old names)
                continue

            if item.category.name == "Drone":
                extraAmount = int(
                    extraAmount) if extraAmount is not None else 1
                if not modName in droneMap:
                    droneMap[modName] = 0
                droneMap[modName] += extraAmount
            if len(modExtra) == 2 and item.category.name != "Drone":
                extraAmount = int(
                    extraAmount) if extraAmount is not None else 1
                if not modName in cargoMap:
                    cargoMap[modName] = 0
                cargoMap[modName] += extraAmount
            elif item.category.name == "Implant":
                fit.implants.append(Implant(item))
            else:
                try:
                    m = Module(item)
                except ValueError:
                    continue
                if ammoName:
                    try:
                        ammo = sMkt.getItem(ammoName)
                        if m.isValidCharge(ammo) and m.charge is None:
                            m.charge = ammo
                    except:
                        pass

                if setOffline is True and m.isValidState(State.OFFLINE):
                    m.state = State.OFFLINE
                elif m.isValidState(State.ACTIVE):
                    m.state = State.ACTIVE

                fit.modules.append(m)

        for droneName in droneMap:
            d = Drone(sMkt.getItem(droneName))
            d.amount = droneMap[droneName]
            fit.drones.append(d)

        for cargoName in cargoMap:
            c = Cargo(sMkt.getItem(cargoName))
            c.amount = cargoMap[cargoName]
            fit.cargo.append(c)

        return fit
Example #24
0
    def importXml(text, callback=None, encoding="utf-8"):
        sMkt = service.Market.getInstance()

        doc = xml.dom.minidom.parseString(text.encode(encoding))
        fittings = doc.getElementsByTagName("fittings").item(0)
        fittings = fittings.getElementsByTagName("fitting")
        fits = []

        for i, fitting in enumerate(fittings):
            f = Fit()
            f.name = fitting.getAttribute("name")
            # <localized hint="Maelstrom">Maelstrom</localized>
            shipType = fitting.getElementsByTagName("shipType").item(
                0).getAttribute("value")
            try:
                try:
                    f.ship = Ship(sMkt.getItem(shipType))
                except ValueError:
                    f.ship = Citadel(sMkt.getItem(shipType))
            except:
                continue
            hardwares = fitting.getElementsByTagName("hardware")
            moduleList = []
            for hardware in hardwares:
                try:
                    moduleName = hardware.getAttribute("type")
                    try:
                        item = sMkt.getItem(moduleName, eager="group.category")
                    except:
                        continue
                    if item:
                        if item.category.name == "Drone":
                            d = Drone(item)
                            d.amount = int(hardware.getAttribute("qty"))
                            f.drones.append(d)
                        elif item.category.name == "Fighter":
                            ft = Fighter(item)
                            ft.amount = int(
                                hardware.getAttribute("qty")
                            ) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize
                            f.fighters.append(ft)
                        elif hardware.getAttribute("slot").lower() == "cargo":
                            # although the eve client only support charges in cargo, third-party programs
                            # may support items or "refits" in cargo. Support these by blindly adding all
                            # cargo, not just charges
                            c = Cargo(item)
                            c.amount = int(hardware.getAttribute("qty"))
                            f.cargo.append(c)
                        else:
                            try:
                                m = Module(item)
                            # When item can't be added to any slot (unknown item or just charge), ignore it
                            except ValueError:
                                continue
                            # Add subsystems before modules to make sure T3 cruisers have subsystems installed
                            if item.category.name == "Subsystem":
                                if m.fits(f):
                                    m.owner = f
                                    f.modules.append(m)
                            else:
                                if m.isValidState(State.ACTIVE):
                                    m.state = State.ACTIVE

                                moduleList.append(m)

                except KeyboardInterrupt:
                    continue

            # Recalc to get slot numbers correct for T3 cruisers
            service.Fit.getInstance().recalc(f)

            for module in moduleList:
                if module.fits(f):
                    module.owner = f
                    f.modules.append(module)

            fits.append(f)
            if callback:
                wx.CallAfter(callback, None)

        return fits