diff --git a/eos/effects/dronemaxvelocitybonus.py b/eos/effects/dronemaxvelocitybonus.py index 938b34fa0..e3fc1ba6e 100644 --- a/eos/effects/dronemaxvelocitybonus.py +++ b/eos/effects/dronemaxvelocitybonus.py @@ -8,4 +8,4 @@ type = "passive" def handler(fit, container, context): level = container.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), - "maxVelocity", container.getModifiedItemAttr("droneMaxVelocityBonus") * level) + "maxVelocity", container.getModifiedItemAttr("droneMaxVelocityBonus") * level, stackingPenalties=True) diff --git a/eos/effects/thermodynamicsskilldamagebonus.py b/eos/effects/thermodynamicsskilldamagebonus.py index 54569b06a..0386ad6e6 100644 --- a/eos/effects/thermodynamicsskilldamagebonus.py +++ b/eos/effects/thermodynamicsskilldamagebonus.py @@ -6,5 +6,5 @@ type = "passive" def handler(fit, skill, context): - fit.modules.filteredItemBoost(lambda mod: True, "heatDamage", + fit.modules.filteredItemBoost(lambda mod: "heatDamage" in mod.item.attributes, "heatDamage", skill.getModifiedItemAttr("thermodynamicsHeatDamage") * skill.level) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 85cd2645d..9316b32f7 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -57,8 +57,15 @@ class Character(object): def init(self): self.__skillIdMap = {} + for skill in self.__skills: self.__skillIdMap[skill.itemID] = skill + + # get a list of skills that the character does no have, and add them (removal of old skills happens in the + # Skill loading) + for skillID in set(self.getSkillIDMap().keys()).difference(set(self.__skillIdMap.keys())): + self.addSkill(Skill(self, skillID, self.defaultLevel)) + self.dirtySkills = set() self.alphaClone = None diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index e768bf216..d44f8adca 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -1016,6 +1016,16 @@ class Fit(object): def getNumSlots(self, type): return self.ship.getModifiedItemAttr(self.slots[type]) or 0 + def getHardpointsFree(self, type): + if type == Hardpoint.NONE: + return 1 + elif type == Hardpoint.TURRET: + return self.ship.getModifiedItemAttr('turretSlotsLeft') - self.getHardpointsUsed(Hardpoint.TURRET) + elif type == Hardpoint.MISSILE: + return self.ship.getModifiedItemAttr('launcherSlotsLeft') - self.getHardpointsUsed(Hardpoint.MISSILE) + else: + raise ValueError("%d is not a valid value for Hardpoint Enum", type) + @property def calibrationUsed(self): return self.getItemAttrOnlineSum(self.modules, 'upgradeCost') diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 536be8614..ea4bac1a3 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -74,7 +74,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): """An instance of this class represents a module together with its charge and modified attributes""" DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") MINING_ATTRIBUTES = ("miningAmount",) - SYSTEM_GROUPS = ("Effect Beacon", "MassiveEnvironments", "Uninteractable Localized Effect Beacon", "Non-Interactable Object") + SYSTEM_GROUPS = ("Effect Beacon", "MassiveEnvironments", "Abyssal Hazards", "Non-Interactable Object") def __init__(self, item, baseItem=None, mutaplasmid=None): """Initialize a module from the program""" @@ -527,7 +527,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if max is not None: current = 0 # if self.owner != fit else -1 # Disabled, see #1278 for mod in fit.modules: - if mod.item and mod.item.groupID == self.item.groupID: + if (mod.item and mod.item.groupID == self.item.groupID and + self.modPosition != mod.modPosition): current += 1 if current >= max: @@ -535,12 +536,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): # Check this only if we're told to do so if hardpointLimit: - if self.hardpoint == Hardpoint.TURRET: - if fit.ship.getModifiedItemAttr('turretSlotsLeft') - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: - return False - elif self.hardpoint == Hardpoint.MISSILE: - if fit.ship.getModifiedItemAttr('launcherSlotsLeft') - fit.getHardpointsUsed(Hardpoint.MISSILE) < 1: - return False + if fit.getHardpointsFree(self.hardpoint) < 1: + return False return True diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index 1ce95c906..ac0bd5c1b 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -195,7 +195,7 @@ class WhProjector(ContextMenu): def getLocalizedEnvironments(self): sMkt = Market.getInstance() - grp = sMkt.getGroup("Uninteractable Localized Effect Beacon") + grp = sMkt.getGroup("Abyssal Hazards") effects = dict() diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 36b3da6b1..d6bf4ae0c 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -686,7 +686,22 @@ class FittingView(d.Display): # only consider changing color if we're dealing with a Module if type(mod) is Module: - if slotMap[mod.slot] or getattr(mod, 'restrictionOverridden', None): # Color too many modules as red + hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None) + # If module had broken fitting restrictions but now doesn't, + # ensure it is now valid, and remove restrictionOverridden + # variable. More in #1519 + if not fit.ignoreRestrictions and hasRestrictionOverriden: + clean = False + if mod.fits(fit, False): + if not mod.hardpoint: + clean = True + elif fit.getHardpointsFree(mod.hardpoint) >= 0: + clean = True + if clean: + del mod.restrictionOverridden + hasRestrictionOverriden = not hasRestrictionOverriden + + if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51)) elif sFit.serviceFittingOptions["colorFitBySlot"]: # Color by slot it enabled self.SetItemBackgroundColour(i, self.slotColour(mod.slot)) @@ -726,7 +741,7 @@ class FittingView(d.Display): # noinspection PyPropertyAccess def MakeSnapshot(self, maxColumns=1337): if self.FVsnapshot: - del self.FVsnapshot + self.FVsnapshot = None tbmp = wx.Bitmap(16, 16) tdc = wx.MemoryDC() diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index c2a1ed31c..521ac6168 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -20,6 +20,7 @@ from gui.bitmap_loader import BitmapLoader from gui.utils import draw from gui.utils import color as color_utils from service.fit import Fit +from gui.utils import fonts _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() @@ -357,7 +358,7 @@ class _TabRenderer: self.tab_bitmap = None self.tab_back_bitmap = None self.padding = 4 - self.font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) + self.font = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) self.tab_img = img self.position = (0, 0) # Not used internally for rendering - helper for tab container @@ -1322,7 +1323,7 @@ class PFNotebookPagePreview(wx.Frame): self.padding = 15 self.transp = 0 - hfont = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) + hfont = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) self.SetFont(hfont) tx, ty = self.GetTextExtent(self.title) @@ -1384,7 +1385,7 @@ class PFNotebookPagePreview(wx.Frame): mdc.SetBackground(wx.Brush(color)) mdc.Clear() - font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) + font = wx.Font(11, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) mdc.SetFont(font) x, y = mdc.GetTextExtent(self.title) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 5675c835e..a70bea814 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -35,7 +35,7 @@ class CopySelectDialog(wx.Dialog): style=wx.DEFAULT_DIALOG_STYLE) mainSizer = wx.BoxSizer(wx.VERTICAL) - copyFormats = ["EFT", "EFT (Implants)", "XML", "DNA", "CREST", "MultiBuy"] + copyFormats = ["EFT", "EFT (Implants)", "XML", "DNA", "ESI", "MultiBuy"] copyFormatTooltips = {CopySelectDialog.copyFormatEft: "EFT text format", CopySelectDialog.copyFormatEftImps: "EFT text format", CopySelectDialog.copyFormatXml: "EVE native XML format", diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 99d8f33eb..af3da77a5 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -157,6 +157,19 @@ class EveFittings(wx.Frame): self.statusbar.SetStatusText(msg) +class ESIServerExceptionHandler(object): + def __init__(self, parentWindow, ex): + dlg = wx.MessageDialog(parentWindow, + "There was an issue starting up the localized server, try setting " + "Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> " + "Login Authentication Method. If this doesn't fix the problem please file an " + "issue on Github.", + "Add Character Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + pyfalog.error(ex) + + class ESIExceptionHandler(object): # todo: make this a generate excetpion handler for all calls def __init__(self, parentWindow, ex): @@ -325,10 +338,12 @@ class SsoCharacterMgmt(wx.Dialog): self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE) self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE) - @staticmethod - def addChar(event): - sEsi = Esi.getInstance() - sEsi.login() + def addChar(self, event): + try: + sEsi = Esi.getInstance() + sEsi.login() + except Exception as ex: + ESIServerExceptionHandler(self, ex) def delChar(self, event): item = self.lcCharacters.GetFirstSelected() diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 0d5456dd0..796e3aa4c 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -90,7 +90,7 @@ class exportHtmlThread(threading.Thread):