diff --git a/eos/effects.py b/eos/effects.py index 1235d901a..6cec0b528 100644 --- a/eos/effects.py +++ b/eos/effects.py @@ -10689,6 +10689,7 @@ class Effect3526(BaseEffect): Used by: Ships from group: Force Recon Ship (8 of 9) + Ship: Venture Skill: Cynosural Field Theory """ diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index e909651c4..0037c6014 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -318,10 +318,15 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "energyDestabilizationRange", "empFieldRange", "ecmBurstRange", "warpScrambleRange", "cargoScanRange", "shipScanRange", "surveyScanRange") + maxRange = None for attr in attrs: maxRange = self.getModifiedItemAttr(attr, None) if maxRange is not None: - return maxRange + break + if maxRange is not None: + if 'burst projector' in self.item.name.lower(): + maxRange -= self.owner.ship.getModifiedItemAttr("radius") + return maxRange missileMaxRangeData = self.missileMaxRangeData if missileMaxRangeData is None: return None diff --git a/graphs/gui/frame.py b/graphs/gui/frame.py index f56c08d48..ef94f4009 100644 --- a/graphs/gui/frame.py +++ b/graphs/gui/frame.py @@ -27,7 +27,7 @@ import gui.globalEvents as GE import gui.mainFrame from graphs.data.base import FitGraph from graphs.events import RESIST_MODE_CHANGED -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.bitmap_loader import BitmapLoader from service.const import GraphCacheCleanupReason from service.settings import GraphSettings diff --git a/gui/auxFrame.py b/gui/auxWindow.py similarity index 94% rename from gui/auxFrame.py rename to gui/auxWindow.py index 4f3fbedb4..e42b68fab 100644 --- a/gui/auxFrame.py +++ b/gui/auxWindow.py @@ -22,7 +22,7 @@ import wx -class AuxiliaryFrame(wx.Frame): +class AuxiliaryMixin: _instance = None @@ -68,3 +68,11 @@ class AuxiliaryFrame(wx.Frame): def OnSuppressedAction(self, event): return + + +class AuxiliaryFrame(AuxiliaryMixin, wx.Frame): + pass + + +class AuxiliaryDialog(AuxiliaryMixin, wx.Dialog): + pass diff --git a/gui/bitmap_loader.py b/gui/bitmap_loader.py index 928dfb15e..325cbc7e3 100644 --- a/gui/bitmap_loader.py +++ b/gui/bitmap_loader.py @@ -89,7 +89,7 @@ class BitmapLoader: @classmethod def loadBitmap(cls, name, location): if cls.scaling_factor is None: - cls.scaling_factor = int(wx.GetApp().GetTopWindow().GetContentScaleFactor()) + cls.scaling_factor = 1 if 'wxGTK' in wx.PlatformInfo else int(wx.GetApp().GetTopWindow().GetContentScaleFactor()) scale = cls.scaling_factor filename, img = cls.loadScaledBitmap(name, location, scale) diff --git a/gui/builtinContextMenus/graphFitAmmoPicker.py b/gui/builtinContextMenus/graphFitAmmoPicker.py index 76d79178f..f13d63446 100644 --- a/gui/builtinContextMenus/graphFitAmmoPicker.py +++ b/gui/builtinContextMenus/graphFitAmmoPicker.py @@ -2,7 +2,7 @@ import wx import gui.mainFrame -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryDialog from gui.contextMenu import ContextMenuSingle from service.ammo import Ammo from service.market import Market @@ -32,22 +32,34 @@ class GraphFitAmmoPicker(ContextMenuSingle): GraphFitAmmoPicker.register() -class AmmoPickerFrame(AuxiliaryFrame): +class AmmoPickerFrame(AuxiliaryDialog): def __init__(self, parent, fit): super().__init__(parent, title='Choose Different Ammo', style=wx.DEFAULT_DIALOG_STYLE, resizeable=True) padding = 5 mainSizer = wx.BoxSizer(wx.VERTICAL) + contents = AmmoPickerContents(self, fit) mainSizer.Add(contents, 1, wx.EXPAND | wx.ALL, padding) + buttonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL) + if buttonSizer: + mainSizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, padding) + self.SetSizer(mainSizer) self.Layout() contW, contH = contents.GetVirtualSize() - bestW = min(1000, contW + padding * 2) - bestH = min(700, contH + padding * 2) + bestW = contW + padding * 2 + bestH = contH + padding * 2 + if buttonSizer: + # Yeah right... whatever + buttW, buttH = buttonSizer.GetSize() + bestW = max(bestW, buttW + padding * 2) + bestH += buttH + padding * 2 + bestW = min(1000, bestW) + bestH = min(700, bestH) self.SetSize(bestW, bestH) self.SetMinSize(wx.Size(int(bestW * 0.7), int(bestH * 0.7))) self.CenterOnParent() @@ -62,11 +74,12 @@ class AmmoPickerFrame(AuxiliaryFrame): class AmmoPickerContents(wx.ScrolledCanvas): + indent = 15 + def __init__(self, parent, fit): wx.ScrolledCanvas.__init__(self, parent) self.SetScrollRate(0, 15) - indent = 15 mods = self.getMods(fit) drones = self.getDrones(fit) fighters = self.getFighters(fit) @@ -75,68 +88,78 @@ class AmmoPickerContents(wx.ScrolledCanvas): mainSizer = wx.BoxSizer(wx.VERTICAL) + moduleSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer.Add(moduleSizer, 0, wx.ALL, 0) + + self.droneSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer.Add(self.droneSizer, 0, wx.ALL, 0) + + fighterSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer.Add(fighterSizer, 0, wx.ALL, 0) + firstRadio = True - currentRb = None - - def addRadioButton(text): - nonlocal firstRadio, currentRb - if not firstRadio: - rb = wx.RadioButton(self, wx.ID_ANY, text, style=wx.RB_GROUP) - rb.SetValue(True) - firstRadio = True - else: - rb = wx.RadioButton(self, wx.ID_ANY, text) - rb.SetValue(False) - rb.Bind(wx.EVT_RADIOBUTTON, self.rbSelected) - currentRb = rb - mainSizer.Add(rb, 0, wx.EXPAND | wx.ALL, 0) - - def addCheckbox(text, indentLvl=0): - cb = wx.CheckBox(self, -1, text) - mainSizer.Add(cb, 0, wx.EXPAND | wx.LEFT, indent * indentLvl) - if currentRb is not None: - self.rbCheckboxMap.setdefault(currentRb, []).append(cb) - - def addLabel(text, indentLvl=0): - text = text[0].capitalize() + text[1:] - label = wx.StaticText(self, wx.ID_ANY, text) - mainSizer.Add(label, 0, wx.EXPAND | wx.LEFT, indent * indentLvl) - if currentRb is not None: - self.rbLabelMap.setdefault(currentRb, []).append(label) for modInfo, modAmmo in mods: text = '\n'.join('{}x {}'.format(amount, item.name) for item, amount in modInfo) - addRadioButton(text) + modRb = self.addRadioButton(moduleSizer, text, firstRadio) + firstRadio = False # Get actual module, as ammo getters need it mod = next((m for m in fit.modules if m.itemID == next(iter(modInfo))[0].ID), None) _, ammoTree = Ammo.getInstance().getModuleStructuredAmmo(mod) if len(ammoTree) == 1: for ammoCatName, ammos in ammoTree.items(): for ammo in ammos: - addCheckbox(ammo.name, indentLvl=1) + self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=1) else: for ammoCatName, ammos in ammoTree.items(): if len(ammos) == 1: ammo = next(iter(ammos)) - addCheckbox(ammo.name, indentLvl=1) + self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=1) else: - addLabel('{}:'.format(ammoCatName), indentLvl=1) + self.addLabel(moduleSizer, '{}:'.format(ammoCatName), modRb, indentLvl=1) for ammo in ammos: - addCheckbox(ammo.name, indentLvl=2) + self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=2) if drones: - addRadioButton('Drones') + droneRb = self.addRadioButton(self.droneSizer, 'Drones', firstRadio) from gui.builtinAdditionPanes.droneView import DroneView for drone in sorted(drones, key=DroneView.droneKey): - addCheckbox('{}x {}'.format(drone.amount, drone.item.name), indentLvl=1) + self.addCheckbox(self.droneSizer, '{}x {}'.format(drone.amount, drone.item.name), droneRb, indentLvl=1) + addBtn = wx.Button(self, wx.ID_ANY, '+', style=wx.BU_EXACTFIT) + addBtn.Bind(wx.EVT_BUTTON, self.OnDroneGroupAdd) + mainSizer.Add(addBtn, 0, wx.LEFT, self.indent) if fighters: - addRadioButton('Fighters') + fighterRb = self.addRadioButton(fighterSizer, 'Fighters', firstRadio) from gui.builtinAdditionPanes.fighterView import FighterDisplay for fighter in sorted(fighters, key=FighterDisplay.fighterKey): - addCheckbox('{}x {}'.format(fighter.amount, fighter.item.name), indentLvl=1) + self.addCheckbox(fighterSizer, '{}x {}'.format(fighter.amount, fighter.item.name), fighterRb, indentLvl=1) self.SetSizer(mainSizer) self.refreshStatus() + def addRadioButton(self, sizer, text, firstRadio=False): + if firstRadio: + rb = wx.RadioButton(self, wx.ID_ANY, text, style=wx.RB_GROUP) + rb.SetValue(True) + else: + rb = wx.RadioButton(self, wx.ID_ANY, text) + rb.SetValue(False) + rb.Bind(wx.EVT_RADIOBUTTON, self.rbSelected) + sizer.Add(rb, 0, wx.EXPAND | wx.ALL, 0) + return rb + + def addCheckbox(self, sizer, text, currentRb, indentLvl=0): + cb = wx.CheckBox(self, -1, text) + sizer.Add(cb, 0, wx.EXPAND | wx.LEFT, self.indent * indentLvl) + if currentRb is not None: + self.rbCheckboxMap.setdefault(currentRb, []).append(cb) + + def addLabel(self, sizer, text, currentRb, indentLvl=0): + text = text[0].capitalize() + text[1:] + label = wx.StaticText(self, wx.ID_ANY, text) + sizer.Add(label, 0, wx.EXPAND | wx.LEFT, self.indent * indentLvl) + if currentRb is not None: + self.rbLabelMap.setdefault(currentRb, []).append(label) + def getMods(self, fit): sMkt = Market.getInstance() sAmmo = Ammo.getInstance() @@ -201,6 +224,12 @@ class AmmoPickerContents(wx.ScrolledCanvas): break return fighters + def OnDroneGroupAdd(self, event): + event.Skip() + sizer = wx.BoxSizer(wx.HORIZONTAL) + label = wx.StaticText() + self.droneSizer.Add(sizer, 0, wx.EXPAND | wx.LEFT, self.indent) + def refreshStatus(self): for map in (self.rbLabelMap, self.rbCheckboxMap): for rb, items in map.items(): diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index b91465efc..02a5be5d9 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -83,12 +83,12 @@ class MaxRange(ViewColumn): lines.append('Missile flight range') lowerRange, higherRange, higherChance = missileRangeData if roundToPrec(higherChance, 3) not in (0, 1): - lines.append('{}% chance to fly {}'.format( + lines.append('{}% chance to fly {}m'.format( formatAmount((1 - higherChance) * 100, prec=3, lowest=0, highest=0), - formatAmount(lowerRange, prec=3, lowest=0, highest=3, unitName='m'))) - lines.append('{}% chance to fly {}'.format( + formatAmount(lowerRange, prec=3, lowest=0, highest=3))) + lines.append('{}% chance to fly {}m'.format( formatAmount(higherChance * 100, prec=3, lowest=0, highest=0), - formatAmount(higherRange, prec=3, lowest=0, highest=3, unitName='m'))) + formatAmount(higherRange, prec=3, lowest=0, highest=3))) else: lines.append("Optimal + Falloff") return '\n'.join(lines) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 9af767acc..7025b1a2a 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -34,7 +34,7 @@ from wx.lib.agw.floatspin import FloatSpin import config import gui.globalEvents as GE -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.bitmap_loader import BitmapLoader from gui.builtinViews.entityEditor import BaseValidator, EntityEditor, TextEntryValidatedDialog from gui.builtinViews.implantEditor import BaseImplantEditorView @@ -428,6 +428,31 @@ class SkillTreeView(wx.Panel): # This cuases issues with GTK, see #1866 # self.Layout() + # For level keyboard shortcuts + self.ChangeLevelEvent, CHANGE_LEVEL_EVENT = wx.lib.newevent.NewEvent() + self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent) + self.Bind(CHANGE_LEVEL_EVENT, self.changeLevel) + + def kbEvent(self, event): + keyLevelMap = { + # Regular number keys + 48: 0, 49: 1, 50: 2, 51: 3, 52: 4, 53: 5, + # Numpad keys + wx.WXK_NUMPAD0: 0, wx.WXK_NUMPAD1: 1, wx.WXK_NUMPAD2: 2, + wx.WXK_NUMPAD3: 3, wx.WXK_NUMPAD4: 4, wx.WXK_NUMPAD5: 5} + keycode = event.GetKeyCode() + if keycode in keyLevelMap and event.GetModifiers() == wx.MOD_NONE: + level = keyLevelMap[keycode] + selection = self.skillTreeListCtrl.GetSelection() + if selection: + dataType, skillID = self.skillTreeListCtrl.GetItemData(selection) + if dataType == 'skill': + event = self.ChangeLevelEvent() + event.SetId(self.idLevels[level]) + wx.PostEvent(self, event) + return + event.Skip() + def importSkills(self, evt): with wx.MessageDialog( @@ -611,6 +636,8 @@ class SkillTreeView(wx.Panel): sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() + if char.name in ("All 0", "All 5"): + return selection = self.skillTreeListCtrl.GetSelection() dataType, skillID = self.skillTreeListCtrl.GetItemData(selection) diff --git a/gui/devTools.py b/gui/devTools.py index 3e5ac725e..2fb6abb4a 100644 --- a/gui/devTools.py +++ b/gui/devTools.py @@ -26,7 +26,7 @@ import wx from logbook import Logger import eos.db -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.builtinShipBrowser.events import FitSelected diff --git a/gui/errorDialog.py b/gui/errorDialog.py index 1e50a17bc..1825e2e5b 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -26,7 +26,7 @@ import wx from logbook import Logger import config -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from service.prereqsCheck import version_block diff --git a/gui/esiFittings.py b/gui/esiFittings.py index 22dec7a4f..61da04d78 100644 --- a/gui/esiFittings.py +++ b/gui/esiFittings.py @@ -8,7 +8,7 @@ from logbook import Logger import gui.globalEvents as GE from eos.db import getItem from eos.saveddata.cargo import Cargo -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.display import Display from service.esi import Esi from service.esiAccess import APIException diff --git a/gui/itemStats.py b/gui/itemStats.py index 284121ecc..dda8e68ad 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -23,7 +23,7 @@ import wx import config import gui.mainFrame from eos.saveddata.module import Module -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.bitmap_loader import BitmapLoader from gui.builtinItemStatsViews.itemAffectedBy import ItemAffectedBy from gui.builtinItemStatsViews.itemAttributes import ItemParams diff --git a/gui/patternEditor.py b/gui/patternEditor.py index ee22d3fd1..4c30c8c90 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -21,7 +21,7 @@ import wx from logbook import Logger -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.bitmap_loader import BitmapLoader from gui.builtinViews.entityEditor import BaseValidator, EntityEditor from gui.utils.clipboard import fromClipboard, toClipboard diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index 2292c5ac5..06ae4bf20 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -10,7 +10,7 @@ import gui.builtinMarketBrowser.pfSearchBox as SBox import gui.display as d import gui.globalEvents as GE from eos.db.gamedata.queries import getAttributeInfo, getItem -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.bitmap_loader import BitmapLoader from gui.marketBrowser import SearchBox from service.market import Market diff --git a/gui/setEditor.py b/gui/setEditor.py index 578cf18fd..8ba2213d5 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -21,7 +21,7 @@ import wx from logbook import Logger -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.builtinViews.entityEditor import BaseValidator, EntityEditor from gui.builtinViews.implantEditor import BaseImplantEditorView from gui.utils.clipboard import fromClipboard, toClipboard diff --git a/gui/targetProfileEditor.py b/gui/targetProfileEditor.py index 244aec20b..e2d977a66 100644 --- a/gui/targetProfileEditor.py +++ b/gui/targetProfileEditor.py @@ -27,7 +27,7 @@ from logbook import Logger import gui.mainFrame import gui.globalEvents as GE -from gui.auxFrame import AuxiliaryFrame +from gui.auxWindow import AuxiliaryFrame from gui.bitmap_loader import BitmapLoader from gui.builtinViews.entityEditor import EntityEditor, BaseValidator from gui.utils.clipboard import toClipboard, fromClipboard diff --git a/service/port/port.py b/service/port/port.py index 2fc4d879a..3c10d9abe 100644 --- a/service/port/port.py +++ b/service/port/port.py @@ -47,7 +47,7 @@ from service.port.muta import parseMutant pyfalog = Logger(__name__) # 2017/04/05 NOTE: simple validation, for xml file -RE_XML_START = r'<\?xml\s+version="1.0"\s*\?>' +RE_XML_START = r'<\?xml\s+version="1.0"[^<>]*\?>' class Port: @@ -182,7 +182,8 @@ class Port: pyfalog.critical(e) # TypeError: not all arguments converted during string formatting # return False, "Unknown Error while processing {0}" % path - return False, "Unknown error while processing %s\n\n Error: %s" % (path, e.message) + return False, "Unknown error while processing {}\n\n Error: {} {}".format( + path, type(e).__name__, getattr(e, 'message', '')) return True, fit_list @@ -321,4 +322,4 @@ class Port: @staticmethod def exportFitStats(fit, callback=None): - return exportFitStats(fit, callback=callback) \ No newline at end of file + return exportFitStats(fit, callback=callback) diff --git a/service/port/shared.py b/service/port/shared.py index 214a7f3fe..5f3988723 100644 --- a/service/port/shared.py +++ b/service/port/shared.py @@ -82,9 +82,13 @@ def fetchItem(typeName, eagerCat=False): eager = 'group.category' if eagerCat else None try: item = sMkt.getItem(typeName, eager=eager) + except (KeyboardInterrupt, SystemExit): + raise except: pyfalog.warning('service.port.shared: unable to fetch item "{}"'.format(typeName)) return None + if item is None: + return None if sMkt.getPublicityByItem(item): return item else: diff --git a/staticdata/bulkdata/dogmatypeattributes.json b/staticdata/bulkdata/dogmatypeattributes.json index 109ae83ef..27dc3e12d 100644 --- a/staticdata/bulkdata/dogmatypeattributes.json +++ b/staticdata/bulkdata/dogmatypeattributes.json @@ -215747,7 +215747,7 @@ { "attributeID": 9, "typeID": 4308, - "value": 2170.0 + "value": 2180.0 }, { "attributeID": 11, @@ -2361009,6 +2361009,11 @@ "typeID": 32880, "value": 1.0 }, + { + "attributeID": 1296, + "typeID": 32880, + "value": -50.0 + }, { "attributeID": 1547, "typeID": 32880, @@ -2890019,6 +2890024,11 @@ "typeID": 52694, "value": 380.0 }, + { + "attributeID": 1302, + "typeID": 52694, + "value": 32880.0 + }, { "attributeID": 1333, "typeID": 52694, diff --git a/staticdata/bulkdata/dogmatypeeffects.json b/staticdata/bulkdata/dogmatypeeffects.json index e98f063b6..59de1b316 100644 --- a/staticdata/bulkdata/dogmatypeeffects.json +++ b/staticdata/bulkdata/dogmatypeeffects.json @@ -167469,6 +167469,11 @@ "isDefault": false, "typeID": 32878 }, + { + "effectID": 3526, + "isDefault": false, + "typeID": 32880 + }, { "effectID": 5058, "isDefault": false, diff --git a/staticdata/phobos/metadata.json b/staticdata/phobos/metadata.json index f61529931..f8463aba1 100644 --- a/staticdata/phobos/metadata.json +++ b/staticdata/phobos/metadata.json @@ -1,10 +1,10 @@ [ { "field_name": "client_build", - "field_value": 1604553 + "field_value": 1610407 }, { "field_name": "dump_time", - "field_value": 1573560935 + "field_value": 1574329773 } ] \ No newline at end of file diff --git a/staticdata/phobos/traits.json b/staticdata/phobos/traits.json index 06b07ed64..e8c548d5d 100644 --- a/staticdata/phobos/traits.json +++ b/staticdata/phobos/traits.json @@ -733,7 +733,7 @@ "text": "reduction in Small Energy Turret activation cost" }, { - "number": "5%", + "number": "10%", "text": "bonus to Small Energy Turret damage" } ], @@ -10287,6 +10287,10 @@ { "number": "2+", "text": "bonus to ship warp core strength" + }, + { + "number": "50%", + "text": "reduction in Industrial Cynosural Field Generator liquid ozone consumption" } ], "header": "Role Bonus:" @@ -10405,7 +10409,7 @@ { "bonuses": [ { - "number": "10%", + "number": "15%", "text": "bonus to Small Hybrid Turret damage" } ], @@ -14922,7 +14926,7 @@ "text": "reduction in Microwarpdrive signature radius penalty" }, { - "number": "5%", + "number": "10%", "text": "bonus to Small Projectile Turret damage" } ], @@ -18810,7 +18814,7 @@ { "bonuses": [ { - "number": "5%", + "number": "10%", "text": "bonus to Small Hybrid Turret damage" }, { diff --git a/version.yml b/version.yml index 44dc1322f..7b5140866 100644 --- a/version.yml +++ b/version.yml @@ -1 +1 @@ -version: v2.14.2 +version: v2.14.3