diff --git a/service/settings.py b/service/settings.py index cfc48fc4a..8f750c09d 100644 --- a/service/settings.py +++ b/service/settings.py @@ -29,7 +29,8 @@ pyfalog = Logger(__name__) class SettingsProvider(object): - BASE_PATH = os.path.join(config.savePath, 'settings') + if config.savePath: + BASE_PATH = os.path.join(config.savePath, 'settings') settings = {} _instance = None diff --git a/tests/test_modules/test_eos/test_modifiedAttributeDict.py b/tests/test_modules/test_eos/test_modifiedAttributeDict.py index 1d3da0cbe..55b68fb14 100644 --- a/tests/test_modules/test_eos/test_modifiedAttributeDict.py +++ b/tests/test_modules/test_eos/test_modifiedAttributeDict.py @@ -32,8 +32,8 @@ def test_multiply_stacking_penalties(DB, Saveddata, RifterFit): calculated_resist = RifterFit.ship.getModifiedItemAttr("shieldEmDamageResonance") else: # Calculate what our next resist should be - # See: http://wiki.eveuniversity.org/Eve_Math#Stacking_Penalties - current_effectiveness = math.pow(0.5, (math.pow(0.45 * (_ - 1), 2))) + # Denominator: [math.exp((i / 2.67) ** 2.0) for i in xrange(8)] + current_effectiveness = 1 / math.exp(((_ - 1) / 2.67) ** 2.0) new_item_modifier = 1 + ((item_modifer * current_effectiveness) / 100) calculated_resist = (em_resist * new_item_modifier) @@ -46,7 +46,5 @@ def test_multiply_stacking_penalties(DB, Saveddata, RifterFit): em_resist = RifterFit.ship.getModifiedItemAttr("shieldEmDamageResonance") - # Ohnoes! Our stacking penalty calculations are off! Round off because the ones in Eos are probably wrong after four decimal places. - # TODO: Remove the round when Eos calcs are fixed - assert round(em_resist, 4) == round(calculated_resist, 4) + assert em_resist == calculated_resist # print(str(em_resist) + "==" + str(calculated_resist)) diff --git a/tests/test_modules/test_service/test_fit.py b/tests/test_modules/test_service/test_fit.py new file mode 100644 index 000000000..c1c7f934e --- /dev/null +++ b/tests/test_modules/test_service/test_fit.py @@ -0,0 +1,1029 @@ +# Add root folder to python paths +# This must be done on every test in order to pass in Travis +import os +import sys +script_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.realpath(os.path.join(script_dir, '..', '..', '..'))) + +# noinspection PyPackageRequirements +from _development.helpers import DBInMemory as DB, Gamedata, Saveddata +from _development.helpers_fits import RifterFit, KeepstarFit +from service.fit import Fit +from service.settings import Settings + + +def test_getAllFits(DB, RifterFit, KeepstarFit): + assert len(Fit.getAllFits()) == 0 + DB['db'].save(RifterFit) + assert len(Fit.getAllFits()) == 1 + DB['db'].save(KeepstarFit) + assert len(Fit.getAllFits()) == 2 + +def test_getFitsWithShip(DB, RifterFit, KeepstarFit): + assert Fit.getFitsWithShip(587)[0][1] == 'My Rifter Fit' + + + + +class Fit_Service(object): + @staticmethod + def getBoosterFits(): + """ Lists fits flagged as booster """ + pyfalog.debug("Fetching all fits flagged as a booster.") + fits = eos.db.getBoosterFits() + names = [] + for fit in fits: + names.append((fit.ID, fit.name, fit.shipID)) + + return names + + @staticmethod + def countAllFits(): + pyfalog.debug("Getting count of all fits.") + return eos.db.countAllFits() + + @staticmethod + def countFitsWithShip(stuff): + pyfalog.debug("Getting count of all fits for: {0}", stuff) + count = eos.db.countFitsWithShip(stuff) + return count + + @staticmethod + def getModule(fitID, pos): + fit = eos.db.getFit(fitID) + return fit.modules[pos] + + def newFit(self, shipID, name=None): + pyfalog.debug("Creating new fit for ID: {0}", shipID) + try: + ship = es_Ship(eos.db.getItem(shipID)) + except ValueError: + ship = es_Citadel(eos.db.getItem(shipID)) + fit = FitType(ship) + fit.name = name if name is not None else "New %s" % fit.ship.item.name + fit.damagePattern = self.pattern + fit.targetResists = self.targetResists + fit.character = self.character + fit.booster = self.booster + eos.db.save(fit) + self.recalc(fit) + return fit.ID + + @staticmethod + def toggleBoostFit(fitID): + pyfalog.debug("Toggling as booster for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + fit.booster = not fit.booster + eos.db.commit() + + @staticmethod + def renameFit(fitID, newName): + pyfalog.debug("Renaming fit ({0}) to: {1}", fitID, newName) + fit = eos.db.getFit(fitID) + fit.name = newName + eos.db.commit() + + @staticmethod + def deleteFit(fitID): + pyfalog.debug("Deleting fit for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + + eos.db.remove(fit) + + # refresh any fits this fit is projected onto. Otherwise, if we have + # already loaded those fits, they will not reflect the changes + for projection in fit.projectedOnto.values(): + if projection.victim_fit in eos.db.saveddata_session: # GH issue #359 + eos.db.saveddata_session.refresh(projection.victim_fit) + + @staticmethod + def copyFit(fitID): + pyfalog.debug("Creating copy of fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + newFit = copy.deepcopy(fit) + eos.db.save(newFit) + return newFit.ID + + @staticmethod + def clearFit(fitID): + pyfalog.debug("Clearing fit for fit ID: {0}", fitID) + if fitID is None: + return None + + fit = eos.db.getFit(fitID) + fit.clear() + return fit + + def toggleFactorReload(self, fitID): + pyfalog.debug("Toggling factor reload for fit ID: {0}", fitID) + if fitID is None: + return None + + fit = eos.db.getFit(fitID) + fit.factorReload = not fit.factorReload + eos.db.commit() + self.recalc(fit) + + def switchFit(self, fitID): + pyfalog.debug("Switching fit to fit ID: {0}", fitID) + if fitID is None: + return None + + fit = eos.db.getFit(fitID) + + if self.serviceFittingOptions["useGlobalCharacter"]: + if fit.character != self.character: + fit.character = self.character + + if self.serviceFittingOptions["useGlobalDamagePattern"]: + if fit.damagePattern != self.pattern: + fit.damagePattern = self.pattern + + eos.db.commit() + self.recalc(fit, withBoosters=True) + + def getFit(self, fitID, projected=False, basic=False): + """ + Gets fit from database + + Projected is a recursion flag that is set to reduce recursions into projected fits + Basic is a flag to simply return the fit without any other processing + """ + pyfalog.debug("Getting fit for fit ID: {0}", fitID) + if fitID is None: + return None + fit = eos.db.getFit(fitID) + + if basic: + return fit + + inited = getattr(fit, "inited", None) + + if inited is None or inited is False: + if not projected: + for fitP in fit.projectedFits: + self.getFit(fitP.ID, projected=True) + self.recalc(fit, withBoosters=True) + fit.fill() + + # Check that the states of all modules are valid + self.checkStates(fit, None) + + eos.db.commit() + fit.inited = True + return fit + + @staticmethod + def searchFits(name): + pyfalog.debug("Searching for fit: {0}", name) + results = eos.db.searchFits(name) + fits = [] + for fit in results: + fits.append(( + fit.ID, fit.name, fit.ship.item.ID, fit.ship.item.name, fit.booster, + fit.timestamp)) + return fits + + def addImplant(self, fitID, itemID, recalc=True): + pyfalog.debug("Adding implant to fit ({0}) for item ID: {1}", fitID, itemID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID, eager="attributes") + try: + implant = es_Implant(item) + except ValueError: + pyfalog.warning("Invalid item: {0}", itemID) + return False + + fit.implants.append(implant) + if recalc: + self.recalc(fit) + return True + + def removeImplant(self, fitID, position): + pyfalog.debug("Removing implant from position ({0}) for fit ID: {1}", position, fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + implant = fit.implants[position] + fit.implants.remove(implant) + self.recalc(fit) + return True + + def addBooster(self, fitID, itemID): + pyfalog.debug("Adding booster ({0}) to fit ID: {1}", itemID, fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID, eager="attributes") + try: + booster = es_Booster(item) + except ValueError: + pyfalog.warning("Invalid item: {0}", itemID) + return False + + fit.boosters.append(booster) + self.recalc(fit) + return True + + def removeBooster(self, fitID, position): + pyfalog.debug("Removing booster from position ({0}) for fit ID: {1}", position, fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + booster = fit.boosters[position] + fit.boosters.remove(booster) + self.recalc(fit) + return True + + def project(self, fitID, thing): + pyfalog.debug("Projecting fit ({0}) onto: {1}", fitID, thing) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + + if isinstance(thing, int): + thing = eos.db.getItem(thing, + eager=("attributes", "group.category")) + + if isinstance(thing, FitType): + if thing in fit.projectedFits: + return + + fit.__projectedFits[thing.ID] = thing + + # this bit is required -- see GH issue # 83 + eos.db.saveddata_session.flush() + eos.db.saveddata_session.refresh(thing) + elif thing.category.name == "Drone": + drone = None + for d in fit.projectedDrones.find(thing): + if d is None or d.amountActive == d.amount or d.amount >= 5: + drone = d + break + + if drone is None: + drone = es_Drone(thing) + fit.projectedDrones.append(drone) + + drone.amount += 1 + elif thing.category.name == "Fighter": + fighter = es_Fighter(thing) + fit.projectedFighters.append(fighter) + elif thing.group.name == "Effect Beacon": + module = es_Module(thing) + module.state = State.ONLINE + fit.projectedModules.append(module) + else: + module = es_Module(thing) + module.state = State.ACTIVE + if not module.canHaveState(module.state, fit): + module.state = State.OFFLINE + fit.projectedModules.append(module) + + eos.db.commit() + self.recalc(fit) + return True + + def addCommandFit(self, fitID, thing): + pyfalog.debug("Projecting command fit ({0}) onto: {1}", fitID, thing) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + + if thing in fit.commandFits: + return + + fit.__commandFits[thing.ID] = thing + + # this bit is required -- see GH issue # 83 + eos.db.saveddata_session.flush() + eos.db.saveddata_session.refresh(thing) + + eos.db.commit() + self.recalc(fit) + return True + + def toggleProjected(self, fitID, thing, click): + pyfalog.debug("Toggling projected on fit ({0}) for: {1}", fitID, thing) + fit = eos.db.getFit(fitID) + if isinstance(thing, es_Drone): + if thing.amountActive == 0 and thing.canBeApplied(fit): + thing.amountActive = thing.amount + else: + thing.amountActive = 0 + elif isinstance(thing, es_Fighter): + thing.active = not thing.active + elif isinstance(thing, es_Module): + thing.state = self.__getProposedState(thing, click) + if not thing.canHaveState(thing.state, fit): + thing.state = State.OFFLINE + elif isinstance(thing, FitType): + projectionInfo = thing.getProjectionInfo(fitID) + if projectionInfo: + projectionInfo.active = not projectionInfo.active + + eos.db.commit() + self.recalc(fit) + + def toggleCommandFit(self, fitID, thing): + pyfalog.debug("Toggle command fit ({0}) for: {1}", fitID, thing) + fit = eos.db.getFit(fitID) + commandInfo = thing.getCommandInfo(fitID) + if commandInfo: + commandInfo.active = not commandInfo.active + + eos.db.commit() + self.recalc(fit) + + def changeAmount(self, fitID, projected_fit, amount): + """Change amount of projected fits""" + pyfalog.debug("Changing fit ({0}) for projected fit ({1}) to new amount: {2}", fitID, projected_fit.getProjectionInfo(fitID), amount) + fit = eos.db.getFit(fitID) + amount = min(20, max(1, amount)) # 1 <= a <= 20 + projectionInfo = projected_fit.getProjectionInfo(fitID) + if projectionInfo: + projectionInfo.amount = amount + + eos.db.commit() + self.recalc(fit) + + def changeActiveFighters(self, fitID, fighter, amount): + pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", fighter.itemID, amount) + fit = eos.db.getFit(fitID) + fighter.amountActive = amount + + eos.db.commit() + self.recalc(fit) + + def removeProjected(self, fitID, thing): + pyfalog.debug("Removing projection on fit ({0}) from: {1}", fitID, thing) + fit = eos.db.getFit(fitID) + if isinstance(thing, es_Drone): + fit.projectedDrones.remove(thing) + elif isinstance(thing, es_Module): + fit.projectedModules.remove(thing) + elif isinstance(thing, es_Fighter): + fit.projectedFighters.remove(thing) + else: + del fit.__projectedFits[thing.ID] + # fit.projectedFits.remove(thing) + + eos.db.commit() + self.recalc(fit) + + def removeCommand(self, fitID, thing): + pyfalog.debug("Removing command projection from fit ({0}) for: {1}", fitID, thing) + fit = eos.db.getFit(fitID) + del fit.__commandFits[thing.ID] + + eos.db.commit() + self.recalc(fit) + + def appendModule(self, fitID, itemID): + pyfalog.debug("Appending module for fit ({0}) using item: {1}", fitID, itemID) + fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID, eager=("attributes", "group.category")) + try: + m = es_Module(item) + except ValueError: + pyfalog.warning("Invalid item: {0}", itemID) + return False + + if m.item.category.name == "Subsystem": + fit.modules.freeSlot(m.getModifiedItemAttr("subSystemSlot")) + + if m.fits(fit): + m.owner = fit + numSlots = len(fit.modules) + fit.modules.append(m) + if m.isValidState(State.ACTIVE): + m.state = State.ACTIVE + + # As some items may affect state-limiting attributes of the ship, calculate new attributes first + self.recalc(fit) + # Then, check states of all modules and change where needed. This will recalc if needed + self.checkStates(fit, m) + + fit.fill() + eos.db.commit() + + return numSlots != len(fit.modules) + else: + return None + + def removeModule(self, fitID, position): + pyfalog.debug("Removing module from position ({0}) for fit ID: {1}", position, fitID) + fit = eos.db.getFit(fitID) + if fit.modules[position].isEmpty: + return None + + numSlots = len(fit.modules) + fit.modules.toDummy(position) + self.recalc(fit) + self.checkStates(fit, None) + fit.fill() + eos.db.commit() + return numSlots != len(fit.modules) + + def changeModule(self, fitID, position, newItemID): + pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", position, fitID) + fit = eos.db.getFit(fitID) + + # Dummy it out in case the next bit fails + fit.modules.toDummy(position) + + item = eos.db.getItem(newItemID, eager=("attributes", "group.category")) + try: + m = es_Module(item) + except ValueError: + pyfalog.warning("Invalid item: {0}", newItemID) + return False + + if m.fits(fit): + m.owner = fit + fit.modules.toModule(position, m) + if m.isValidState(State.ACTIVE): + m.state = State.ACTIVE + + # As some items may affect state-limiting attributes of the ship, calculate new attributes first + self.recalc(fit) + # Then, check states of all modules and change where needed. This will recalc if needed + self.checkStates(fit, m) + + fit.fill() + eos.db.commit() + + return True + else: + return None + + def moveCargoToModule(self, fitID, moduleIdx, cargoIdx, copyMod=False): + """ + Moves cargo to fitting window. Can either do a copy, move, or swap with current module + If we try to copy/move into a spot with a non-empty module, we swap instead. + To avoid redundancy in converting Cargo item, this function does the + sanity checks as opposed to the GUI View. This is different than how the + normal .swapModules() does things, which is mostly a blind swap. + """ + pyfalog.debug("Moving cargo item to module for fit ID: {1}", fitID) + fit = eos.db.getFit(fitID) + + module = fit.modules[moduleIdx] + cargo = fit.cargo[cargoIdx] + + # Gather modules and convert Cargo item to Module, silently return if not a module + try: + cargoP = es_Module(cargo.item) + cargoP.owner = fit + if cargoP.isValidState(State.ACTIVE): + cargoP.state = State.ACTIVE + except: + pyfalog.warning("Invalid item: {0}", cargo.item) + return + + if cargoP.slot != module.slot: # can't swap modules to different racks + return + + # remove module that we are trying to move cargo to + fit.modules.remove(module) + + if not cargoP.fits(fit): # if cargo doesn't fit, rollback and return + fit.modules.insert(moduleIdx, module) + return + + fit.modules.insert(moduleIdx, cargoP) + + if not copyMod: # remove existing cargo if not cloning + if cargo.amount == 1: + fit.cargo.remove(cargo) + else: + cargo.amount -= 1 + + if not module.isEmpty: # if module is placeholder, we don't want to convert/add it + for x in fit.cargo.find(module.item): + x.amount += 1 + break + else: + moduleP = es_Cargo(module.item) + moduleP.amount = 1 + fit.cargo.insert(cargoIdx, moduleP) + + eos.db.commit() + self.recalc(fit) + + @staticmethod + def swapModules(fitID, src, dst): + pyfalog.debug("Swapping modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID) + fit = eos.db.getFit(fitID) + # Gather modules + srcMod = fit.modules[src] + dstMod = fit.modules[dst] + + # To swap, we simply remove mod and insert at destination. + fit.modules.remove(srcMod) + fit.modules.insert(dst, srcMod) + fit.modules.remove(dstMod) + fit.modules.insert(src, dstMod) + + eos.db.commit() + + def cloneModule(self, fitID, src, dst): + """ + Clone a module from src to dst + This will overwrite dst! Checking for empty module must be + done at a higher level + """ + pyfalog.debug("Cloning modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID) + fit = eos.db.getFit(fitID) + # Gather modules + srcMod = fit.modules[src] + dstMod = fit.modules[dst] # should be a placeholder module + + new = copy.deepcopy(srcMod) + new.owner = fit + if new.fits(fit): + # insert copy if module meets hardpoint restrictions + fit.modules.remove(dstMod) + fit.modules.insert(dst, new) + + eos.db.commit() + self.recalc(fit) + + def addCargo(self, fitID, itemID, amount=1, replace=False): + """ + Adds cargo via typeID of item. If replace = True, we replace amount with + given parameter, otherwise we increment + """ + pyfalog.debug("Adding cargo ({0}) fit ID: {1}", itemID, fitID) + + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID) + cargo = None + + # adding from market + for x in fit.cargo.find(item): + if x is not None: + # found item already in cargo, use previous value and remove old + cargo = x + fit.cargo.remove(x) + break + + if cargo is None: + # if we don't have the item already in cargo, use default values + cargo = es_Cargo(item) + + fit.cargo.append(cargo) + if replace: + cargo.amount = amount + else: + cargo.amount += amount + + self.recalc(fit) + eos.db.commit() + + return True + + def removeCargo(self, fitID, position): + pyfalog.debug("Removing cargo from position ({0}) fit ID: {1}", position, fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + charge = fit.cargo[position] + fit.cargo.remove(charge) + self.recalc(fit) + return True + + def addFighter(self, fitID, itemID): + pyfalog.debug("Adding fighters ({0}) to fit ID: {1}", itemID, fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID, eager=("attributes", "group.category")) + if item.category.name == "Fighter": + fighter = None + ''' + for d in fit.fighters.find(item): + if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]): + drone = d + break + ''' + if fighter is None: + fighter = es_Fighter(item) + used = fit.getSlotsUsed(fighter.slot) + total = fit.getNumSlots(fighter.slot) + standardAttackActive = False + for ability in fighter.abilities: + if ability.effect.isImplemented and ability.effect.handlerName == u'fighterabilityattackm': + # Activate "standard attack" if available + ability.active = True + standardAttackActive = True + else: + # Activate all other abilities (Neut, Web, etc) except propmods if no standard attack is active + if ability.effect.isImplemented and \ + standardAttackActive is False and \ + ability.effect.handlerName != u'fighterabilitymicrowarpdrive' and \ + ability.effect.handlerName != u'fighterabilityevasivemaneuvers': + ability.active = True + + if used >= total: + fighter.active = False + + if fighter.fits(fit) is True: + fit.fighters.append(fighter) + else: + return False + + eos.db.commit() + self.recalc(fit) + return True + else: + return False + + def removeFighter(self, fitID, i): + pyfalog.debug("Removing fighters from fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + f = fit.fighters[i] + fit.fighters.remove(f) + + eos.db.commit() + self.recalc(fit) + return True + + def addDrone(self, fitID, itemID, numDronesToAdd=1): + pyfalog.debug("Adding {0} drones ({1}) to fit ID: {2}", numDronesToAdd, itemID, fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID, eager=("attributes", "group.category")) + if item.category.name == "Drone": + drone = None + for d in fit.drones.find(item): + if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]): + drone = d + break + + if drone is None: + drone = es_Drone(item) + if drone.fits(fit) is True: + fit.drones.append(drone) + else: + return False + drone.amount += numDronesToAdd + eos.db.commit() + self.recalc(fit) + return True + else: + return False + + def mergeDrones(self, fitID, d1, d2, projected=False): + pyfalog.debug("Merging drones on fit ID: {0}", fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + if d1.item != d2.item: + return False + + if projected: + fit.projectedDrones.remove(d1) + else: + fit.drones.remove(d1) + + d2.amount += d1.amount + d2.amountActive += d1.amountActive + + # If we have less than the total number of drones active, make them all active. Fixes #728 + # This could be removed if we ever add an enhancement to make drone stacks partially active. + if d2.amount > d2.amountActive: + d2.amountActive = d2.amount + + eos.db.commit() + self.recalc(fit) + return True + + @staticmethod + def splitDrones(fit, d, amount, l): + pyfalog.debug("Splitting drones for fit ID: {0}", fit) + total = d.amount + active = d.amountActive > 0 + d.amount = amount + d.amountActive = amount if active else 0 + + newD = es_Drone(d.item) + newD.amount = total - amount + newD.amountActive = newD.amount if active else 0 + l.append(newD) + eos.db.commit() + + def splitProjectedDroneStack(self, fitID, d, amount): + pyfalog.debug("Splitting projected drone stack for fit ID: {0}", fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + self.splitDrones(fit, d, amount, fit.projectedDrones) + + def splitDroneStack(self, fitID, d, amount): + pyfalog.debug("Splitting drone stack for fit ID: {0}", fitID) + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + self.splitDrones(fit, d, amount, fit.drones) + + def removeDrone(self, fitID, i, numDronesToRemove=1): + pyfalog.debug("Removing {0} drones for fit ID: {1}", numDronesToRemove, fitID) + fit = eos.db.getFit(fitID) + d = fit.drones[i] + d.amount -= numDronesToRemove + if d.amountActive > 0: + d.amountActive -= numDronesToRemove + + if d.amount == 0: + del fit.drones[i] + + eos.db.commit() + self.recalc(fit) + return True + + def toggleDrone(self, fitID, i): + pyfalog.debug("Toggling drones for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + d = fit.drones[i] + if d.amount == d.amountActive: + d.amountActive = 0 + else: + d.amountActive = d.amount + + eos.db.commit() + self.recalc(fit) + return True + + def toggleFighter(self, fitID, i): + pyfalog.debug("Toggling fighters for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + f = fit.fighters[i] + f.active = not f.active + + eos.db.commit() + self.recalc(fit) + return True + + def toggleImplant(self, fitID, i): + pyfalog.debug("Toggling implant for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + implant = fit.implants[i] + implant.active = not implant.active + + eos.db.commit() + self.recalc(fit) + return True + + def toggleImplantSource(self, fitID, source): + pyfalog.debug("Toggling implant source for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + fit.implantSource = source + + eos.db.commit() + self.recalc(fit) + return True + + def toggleBooster(self, fitID, i): + pyfalog.debug("Toggling booster for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + booster = fit.boosters[i] + booster.active = not booster.active + + eos.db.commit() + self.recalc(fit) + return True + + def toggleFighterAbility(self, fitID, ability): + pyfalog.debug("Toggling fighter ability for fit ID: {0}", fitID) + fit = eos.db.getFit(fitID) + ability.active = not ability.active + eos.db.commit() + self.recalc(fit) + + def changeChar(self, fitID, charID): + pyfalog.debug("Changing character ({0}) for fit ID: {1}", charID, fitID) + if fitID is None or charID is None: + if charID is not None: + self.character = Character.getInstance().all5() + + return + + fit = eos.db.getFit(fitID) + fit.character = self.character = eos.db.getCharacter(charID) + self.recalc(fit) + + @staticmethod + def isAmmo(itemID): + return eos.db.getItem(itemID).category.name == "Charge" + + def setAmmo(self, fitID, ammoID, modules): + pyfalog.debug("Set ammo for fit ID: {0}", fitID) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + ammo = eos.db.getItem(ammoID) if ammoID else None + + for mod in modules: + if mod.isValidCharge(ammo): + mod.charge = ammo + + self.recalc(fit) + + @staticmethod + def getTargetResists(fitID): + pyfalog.debug("Get target resists for fit ID: {0}", fitID) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + return fit.targetResists + + def setTargetResists(self, fitID, pattern): + pyfalog.debug("Set target resist for fit ID: {0}", fitID) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + fit.targetResists = pattern + eos.db.commit() + + self.recalc(fit) + + @staticmethod + def getDamagePattern(fitID): + pyfalog.debug("Get damage pattern for fit ID: {0}", fitID) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + return fit.damagePattern + + def setDamagePattern(self, fitID, pattern): + pyfalog.debug("Set damage pattern for fit ID: {0}", fitID) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + fit.damagePattern = self.pattern = pattern + eos.db.commit() + + self.recalc(fit) + + def setMode(self, fitID, mode): + pyfalog.debug("Set mode for fit ID: {0}", fitID) + if fitID is None: + return + + fit = eos.db.getFit(fitID) + fit.mode = mode + eos.db.commit() + + self.recalc(fit) + + def setAsPattern(self, fitID, ammo): + pyfalog.debug("Set as pattern for fit ID: {0}", fitID) + if fitID is None: + return + + sDP = DamagePattern.getInstance() + dp = sDP.getDamagePattern("Selected Ammo") + if dp is None: + dp = es_DamagePattern() + dp.name = "Selected Ammo" + + fit = eos.db.getFit(fitID) + for attr in ("em", "thermal", "kinetic", "explosive"): + setattr(dp, "%sAmount" % attr, ammo.getAttribute("%sDamage" % attr) or 0) + + fit.damagePattern = dp + self.recalc(fit) + + def checkStates(self, fit, base): + pyfalog.debug("Check states for fit ID: {0}", fit) + changed = False + for mod in fit.modules: + if mod != base: + # fix for #529, where a module may be in incorrect state after CCP changes mechanics of module + if not mod.canHaveState(mod.state) or not mod.isValidState(mod.state): + mod.state = State.ONLINE + changed = True + + for mod in fit.projectedModules: + # fix for #529, where a module may be in incorrect state after CCP changes mechanics of module + if not mod.canHaveState(mod.state, fit) or not mod.isValidState(mod.state): + mod.state = State.OFFLINE + changed = True + + for drone in fit.projectedDrones: + if drone.amountActive > 0 and not drone.canBeApplied(fit): + drone.amountActive = 0 + changed = True + + # If any state was changed, recalculate attributes again + if changed: + self.recalc(fit) + + def toggleModulesState(self, fitID, base, modules, click): + pyfalog.debug("Toggle module state for fit ID: {0}", fitID) + changed = False + proposedState = self.__getProposedState(base, click) + + if proposedState != base.state: + changed = True + base.state = proposedState + for mod in modules: + if mod != base: + p = self.__getProposedState(mod, click, proposedState) + mod.state = p + if p != mod.state: + changed = True + + if changed: + eos.db.commit() + fit = eos.db.getFit(fitID) + + # As some items may affect state-limiting attributes of the ship, calculate new attributes first + self.recalc(fit) + # Then, check states of all modules and change where needed. This will recalc if needed + self.checkStates(fit, base) + + # Old state : New State + localMap = { + State.OVERHEATED: State.ACTIVE, + State.ACTIVE: State.ONLINE, + State.OFFLINE: State.ONLINE, + State.ONLINE: State.ACTIVE} + projectedMap = { + State.OVERHEATED: State.ACTIVE, + State.ACTIVE: State.OFFLINE, + State.OFFLINE: State.ACTIVE, + State.ONLINE: State.ACTIVE} # Just in case + # For system effects. They should only ever be online or offline + projectedSystem = { + State.OFFLINE: State.ONLINE, + State.ONLINE: State.OFFLINE} + + def __getProposedState(self, mod, click, proposedState=None): + pyfalog.debug("Get proposed state for module.") + if mod.slot == Slot.SUBSYSTEM or mod.isEmpty: + return State.ONLINE + + if mod.slot == Slot.SYSTEM: + transitionMap = self.projectedSystem + else: + transitionMap = self.projectedMap if mod.projected else self.localMap + + currState = mod.state + + if proposedState is not None: + state = proposedState + elif click == "right": + state = State.OVERHEATED + elif click == "ctrl": + state = State.OFFLINE + else: + state = transitionMap[currState] + if not mod.isValidState(state): + state = -1 + + if mod.isValidState(state): + return state + else: + return currState + + def refreshFit(self, fitID): + pyfalog.debug("Refresh fit for fit ID: {0}", fitID) + if fitID is None: + return None + + fit = eos.db.getFit(fitID) + eos.db.commit() + self.recalc(fit) + + def recalc(self, fit, withBoosters=True): + pyfalog.info("=" * 10 + "recalc" + "=" * 10) + if fit.factorReload is not self.serviceFittingOptions["useGlobalForceReload"]: + fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"] + fit.clear() + + fit.calculateModifiedAttributes(withBoosters=False)