Merge branch 'ammo_graph' of github.com:pyfa-org/Pyfa into ammo_graph

This commit is contained in:
DarkPhoenix
2019-12-06 01:09:16 +03:00
23 changed files with 163 additions and 69 deletions

View File

@@ -10689,6 +10689,7 @@ class Effect3526(BaseEffect):
Used by:
Ships from group: Force Recon Ship (8 of 9)
Ship: Venture
Skill: Cynosural Field Theory
"""

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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():

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
return exportFitStats(fit, callback=callback)

View File

@@ -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:

View File

@@ -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,

View File

@@ -167469,6 +167469,11 @@
"isDefault": false,
"typeID": 32878
},
{
"effectID": 3526,
"isDefault": false,
"typeID": 32880
},
{
"effectID": 5058,
"isDefault": false,

View File

@@ -1,10 +1,10 @@
[
{
"field_name": "client_build",
"field_value": 1604553
"field_value": 1610407
},
{
"field_name": "dump_time",
"field_value": 1573560935
"field_value": 1574329773
}
]

View File

@@ -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"
},
{

View File

@@ -1 +1 @@
version: v2.14.2
version: v2.14.3