Merge branch 'master' into singularity

This commit is contained in:
DarkPhoenix
2019-08-27 11:34:17 +03:00
19 changed files with 111 additions and 50 deletions

View File

@@ -148,6 +148,7 @@ class CapSimulator:
stability_precision = self.stability_precision
period = self.period
activation = None
iterations = 0
capCapacity = self.capacitorCapacity
@@ -162,7 +163,12 @@ class CapSimulator:
t_max = self.t_max
while 1:
activation = pop(state)
# Nothing to pop - might happen when no mods are activated, or when
# only cap injectors are active (and are postponed by code below)
try:
activation = pop(state)
except IndexError:
break
t_now, duration, capNeed, shot, clipSize, reloadTime, isInjector = activation
# Max time reached, stop simulation - we're stable
@@ -275,7 +281,8 @@ class CapSimulator:
activation[3] = shot
push(state, activation)
push(state, activation)
if activation is not None:
push(state, activation)
# update instance with relevant results.
self.t = t_last

View File

@@ -4261,7 +4261,7 @@ class Effect1434(BaseEffect):
for sensorType in ('Gravimetric', 'Ladar', 'Magnetometric', 'Radar'):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Electronic Warfare'),
'scan{0}StrengthBonus'.format(sensorType),
ship.getModifiedItemAttr('shipBonusCB'), stackingPenalties=True,
ship.getModifiedItemAttr('shipBonusCB'),
skill='Caldari Battleship', **kwargs)
@@ -5967,8 +5967,10 @@ class Effect2019(BaseEffect):
@staticmethod
def handler(fit, container, context, **kwargs):
level = container.level if 'skill' in context else 1
penalized = False if 'skill' in context else True
fit.drones.filteredItemBoost(lambda drone: drone.item.group.name == 'Logistic Drone',
'shieldBonus', container.getModifiedItemAttr('damageHP') * level, **kwargs)
'shieldBonus', container.getModifiedItemAttr('damageHP') * level,
stackingPenalties=penalized, **kwargs)
class Effect2020(BaseEffect):
@@ -6721,10 +6723,10 @@ class Effect2251(BaseEffect):
@staticmethod
def handler(fit, src, context, **kwargs):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupActive',
src.getModifiedItemAttr('maxGangModules'), **kwargs)
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupOnline',
src.getModifiedItemAttr('maxGangModules'), **kwargs)
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupActive',
src.getModifiedItemAttr('maxGangModules'), **kwargs)
class Effect2252(BaseEffect):
@@ -29503,6 +29505,8 @@ class Effect6613(BaseEffect):
@staticmethod
def handler(fit, src, context, **kwargs):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupOnline',
src.getModifiedItemAttr('shipBonusRole1'), **kwargs)
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupActive',
src.getModifiedItemAttr('shipBonusRole1'), **kwargs)
@@ -29605,6 +29609,8 @@ class Effect6619(BaseEffect):
@staticmethod
def handler(fit, src, context, **kwargs):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupOnline',
src.getModifiedItemAttr('shipBonusRole1'), **kwargs)
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupActive',
src.getModifiedItemAttr('shipBonusRole1'), **kwargs)
@@ -29917,6 +29923,8 @@ class Effect6640(BaseEffect):
@staticmethod
def handler(fit, src, context, **kwargs):
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupOnline',
src.getModifiedItemAttr('shipBonusRole1'), **kwargs)
fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill('Leadership'), 'maxGroupActive',
src.getModifiedItemAttr('shipBonusRole1'), **kwargs)

View File

@@ -29,6 +29,7 @@ from eos.db.gamedata.queries import getAttributeInfo
defaultValuesCache = {}
cappingAttrKeyCache = {}
resistanceCache = {}
def getAttrDefault(key, fallback=None):
@@ -46,19 +47,23 @@ def getAttrDefault(key, fallback=None):
def getResistanceAttrID(modifyingItem, effect):
# If it doesn't exist on the effect, check the modifying modules attributes. If it's there, set it on the
# effect for this session so that we don't have to look here again (won't always work when it's None, but
# will catch most)
if not effect.getattr('resistanceCalculated'):
# If it doesn't exist on the effect, check the modifying module's attributes.
# If it's there, cache it and return
if effect.resistanceID:
return effect.resistanceID
cacheKey = (modifyingItem.item.ID, effect.ID)
try:
return resistanceCache[cacheKey]
except KeyError:
attrPrefix = effect.getattr('prefix')
if attrPrefix:
effect.resistanceID = int(modifyingItem.getModifiedItemAttr('{}ResistanceID'.format(attrPrefix))) or None
if not effect.resistanceID:
effect.resistanceID = int(modifyingItem.getModifiedItemAttr('{}RemoteResistanceID'.format(attrPrefix))) or None
resistanceID = int(modifyingItem.getModifiedItemAttr('{}ResistanceID'.format(attrPrefix))) or None
if not resistanceID:
resistanceID = int(modifyingItem.getModifiedItemAttr('{}RemoteResistanceID'.format(attrPrefix))) or None
else:
effect.resistanceID = int(modifyingItem.getModifiedItemAttr("remoteResistanceID")) or None
effect.resistanceCalculated = True
return effect.resistanceID
resistanceID = int(modifyingItem.getModifiedItemAttr("remoteResistanceID")) or None
resistanceCache[cacheKey] = resistanceID
return resistanceID
class ItemAttrShortcut:
@@ -553,10 +558,12 @@ class ModifiedAttributeDict(collections.MutableMapping):
if 'projected' not in effectType:
return 1
remoteResistID = getResistanceAttrID(modifyingItem=fit.getModifier(), effect=effect)
if not remoteResistID:
return 1
attrInfo = getAttributeInfo(remoteResistID)
# Get the attribute of the resist
resist = fit.ship.itemModifiedAttributes[attrInfo.attributeName] or None
return resist or 1.0
return resist or 1
class Affliction:

View File

@@ -188,6 +188,7 @@ class GraphCanvasPanel(wx.Panel):
if minX is not None and maxX is not None:
minY = min(allYs, default=None)
maxY = max(allYs, default=None)
yDiff = (maxY or 0) - (minY or 0)
xMark = max(min(self.xMark, maxX), minX)
# If in top 10% of X coordinates, align labels differently
if xMark > canvasMinX + 0.9 * (canvasMaxX - canvasMinX):
@@ -214,7 +215,12 @@ class GraphCanvasPanel(wx.Panel):
def addYMark(val):
if val is None:
return
rounded = roundToPrec(val, 4)
# Round according to shown Y range - the bigger the range,
# the rougher the rounding
if yDiff != 0:
rounded = roundToPrec(val, 4, nsValue=yDiff)
else:
rounded = val
# If due to some bug or insufficient plot density we're
# out of bounds, do not add anything
if minY <= val <= maxY or minY <= rounded <= maxY:

View File

@@ -38,6 +38,9 @@ from .ctrlPanel import GraphControlPanel
pyfalog = Logger(__name__)
REDRAW_DELAY = 500
class GraphFrame(AuxiliaryFrame):
def __init__(self, parent):
@@ -45,7 +48,7 @@ class GraphFrame(AuxiliaryFrame):
pyfalog.warning('Matplotlib is not enabled. Skipping initialization.')
return
super().__init__(parent, title='Graphs', style=wx.RESIZE_BORDER, size=(520, 390))
super().__init__(parent, title='Graphs', size=(520, 390), resizeable=True)
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.SetIcon(wx.Icon(BitmapLoader.getBitmap('graphs_small', 'gui')))
@@ -90,6 +93,9 @@ class GraphFrame(AuxiliaryFrame):
self.mainFrame.Bind(GE.GRAPH_OPTION_CHANGED, self.OnGraphOptionChanged)
self.mainFrame.Bind(GE.EFFECTIVE_HP_TOGGLED, self.OnEffectiveHpToggled)
self.drawTimer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnDrawTimer, self.drawTimer)
self.Layout()
self.UpdateWindowSize()
self.draw()
@@ -128,7 +134,10 @@ class GraphFrame(AuxiliaryFrame):
for fitID in event.fitIDs:
self.clearCache(reason=GraphCacheCleanupReason.fitChanged, extraData=fitID)
self.ctrlPanel.OnFitChanged(event)
self.draw()
# Data has to be recalculated - delay redraw
# to give time to finish UI update in main window
self.drawTimer.Stop()
self.drawTimer.Start(REDRAW_DELAY, True)
def OnFitRemoved(self, event):
event.Skip()
@@ -184,7 +193,10 @@ class GraphFrame(AuxiliaryFrame):
self.ctrlPanel.refreshAxeLabels(restoreSelection=True)
self.Layout()
self.clearCache(reason=GraphCacheCleanupReason.hpEffectivityChanged)
self.draw()
# Data has to be recalculated - delay redraw
# to give time to finish UI update in main window
self.drawTimer.Stop()
self.drawTimer.Start(REDRAW_DELAY, True)
# Even if graph is not selected, keep it updated
for idx in range(self.graphSelection.GetCount()):
view = self.getView(idx=idx)
@@ -202,6 +214,10 @@ class GraphFrame(AuxiliaryFrame):
self.draw()
event.Skip()
def OnDrawTimer(self, event):
event.Skip()
self.draw()
def OnClose(self, event):
self.mainFrame.Unbind(GE.FIT_RENAMED, handler=self.OnFitRenamed)
self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.OnFitChanged)

View File

@@ -26,8 +26,10 @@ class AuxiliaryFrame(wx.Frame):
_instance = None
def __init__(self, parent, id=None, title=None, pos=None, size=None, style=None, name=None):
def __init__(self, parent, id=None, title=None, pos=None, size=None, style=None, name=None, resizeable=False):
baseStyle = wx.FRAME_NO_TASKBAR | wx.FRAME_FLOAT_ON_PARENT | wx.CAPTION | wx.CLOSE_BOX | wx.SYSTEM_MENU
if resizeable:
baseStyle = baseStyle | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX
kwargs = {
'parent': parent,
'style': baseStyle if style is None else baseStyle | style}

View File

@@ -51,6 +51,7 @@ class ChangeItemToVariation(ContextMenuCombined):
def getSubMenu(self, callingWindow, context, mainItem, selection, rootMenu, i, pitem):
self.moduleLookup = {}
sFit = Fit.getInstance()
sMkt = Market.getInstance()
fit = sFit.getFit(self.mainFrame.getActiveFit())
def get_metalevel(x):
@@ -61,7 +62,8 @@ class ChangeItemToVariation(ContextMenuCombined):
def get_metagroup(x):
# We want deadspace before officer mods
remap = {5: 6, 6: 5}
return remap.get(x.metaGroup.ID, x.metaGroup.ID) if x.metaGroup is not None else 0
metaGroup = sMkt.getMetaGroupByItem(x)
return remap.get(metaGroup.ID, metaGroup.ID) if metaGroup is not None else 0
def get_boosterrank(x):
# If we're returning a lot of items, sort my name
@@ -84,7 +86,9 @@ class ChangeItemToVariation(ContextMenuCombined):
bindmenu = m
# Do not show abyssal items
items = list(i for i in self.mainVariations if i.metaGroup is None or i.metaGroup.ID != 15)
items = list(
i for i in self.mainVariations
if sMkt.getMetaGroupByItem(i) is None or sMkt.getMetaGroupByItem(i).ID != 15)
# Sort items by metalevel, and group within that metalevel
# Sort all items by name first
items.sort(key=lambda x: x.name)
@@ -102,12 +106,13 @@ class ChangeItemToVariation(ContextMenuCombined):
group = None
for item in items:
# Apparently no metaGroup for the Tech I variant:
metaGroup = sMkt.getMetaGroupByItem(item)
if 'subSystem' in item.effects:
thisgroup = item.marketGroup.marketGroupName
elif item.metaGroup is None:
elif metaGroup is None:
thisgroup = 'Tech I'
else:
thisgroup = item.metaGroup.name
thisgroup = metaGroup.name
if thisgroup != group and context not in ('implantItem', 'boosterItem'):
group = thisgroup

View File

@@ -84,11 +84,12 @@ class AttributeDisplay(ViewColumn):
return ""
if self.info.name == "volume":
str_ = (formatAmount(attr, 3, 0, 3))
if hasattr(mod, "amount"):
str_ += "m\u00B3 (%s m\u00B3)" % (formatAmount(attr * mod.amount, 3, 0, 3))
attr = str_
if getattr(mod, "amount", 1) != 1:
attr = "{} m\u00B3 ({} m\u00B3)".format(
formatAmount(attr, 3, 0, 6),
formatAmount(attr * mod.amount, 3, 0, 6))
else:
attr = "{} m\u00B3".format(formatAmount(attr, 3, 0, 6))
if isinstance(attr, (float, int)):
attr = (formatAmount(attr, 3, 0, 3))

View File

@@ -33,6 +33,7 @@ from eos.saveddata.module import Module, Rack
from eos.saveddata.targetProfile import TargetProfile
from graphs.wrapper import BaseWrapper
from gui.builtinContextMenus.envEffectAdd import AddEnvironmentEffect
from gui.utils.numberFormatter import formatAmount
from gui.viewColumn import ViewColumn
from service.fit import Fit as FitSvc
from service.market import Market
@@ -64,7 +65,11 @@ class BaseName(ViewColumn):
return "%d/%d %s" % \
(stuff.amount, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name)
elif isinstance(stuff, Cargo):
return "%dx %s" % (stuff.amount, stuff.item.name)
if stuff.item.group.name in ("Cargo Container", "Secure Cargo Container", "Audit Log Secure Container", "Freight Container"):
capacity = stuff.item.getAttribute('capacity')
if capacity:
return "{:d}x {} ({} m\u00B3)".format(stuff.amount, stuff.item.name, formatAmount(capacity, 3, 0, 6))
return "{:d}x {}".format(stuff.amount, stuff.item.name)
elif isinstance(stuff, Fit):
if self.projectedView:
# we need a little more information for the projected view

View File

@@ -154,7 +154,9 @@ class CharacterEntityEditor(EntityEditor):
class CharacterEditor(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(parent, id=wx.ID_ANY, title="Character Editor", pos=wx.DefaultPosition, size=wx.Size(640, 600))
super().__init__(
parent, id=wx.ID_ANY, title="Character Editor", resizeable=True,
pos=wx.DefaultPosition, size=wx.Size(640, 600))
i = wx.Icon(BitmapLoader.getBitmap("character_small", "gui"))
self.SetIcon(i)

View File

@@ -39,7 +39,7 @@ class DevTools(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title="Development Tools", style=wx.RESIZE_BORDER,
parent, id=wx.ID_ANY, title="Development Tools", resizeable=True,
size=wx.Size(400, 320) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240))
self.mainFrame = parent
self.block = False

View File

@@ -25,7 +25,7 @@ class EveFittings(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title="Browse EVE Fittings", pos=wx.DefaultPosition,
size=wx.Size(750, 450), style=wx.RESIZE_BORDER)
size=wx.Size(750, 450), resizeable=True)
self.mainFrame = parent
mainSizer = wx.BoxSizer(wx.VERTICAL)
@@ -204,7 +204,7 @@ class ExportToEve(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition,
size=wx.Size(400, 120) if "wxGTK" in wx.PlatformInfo else wx.Size(350, 100), style=wx.RESIZE_BORDER)
size=wx.Size(400, 120) if "wxGTK" in wx.PlatformInfo else wx.Size(350, 100), resizeable=True)
self.mainFrame = parent
@@ -317,7 +317,7 @@ class SsoCharacterMgmt(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title="SSO Character Management", pos=wx.DefaultPosition,
size=wx.Size(550, 250), style=wx.RESIZE_BORDER)
size=wx.Size(550, 250), resizeable=True)
self.mainFrame = parent
mainSizer = wx.BoxSizer(wx.HORIZONTAL)

View File

@@ -56,7 +56,7 @@ class ItemStatsFrame(AuxiliaryFrame):
title="Item stats",
pos=pos,
size=size,
style=wx.RESIZE_BORDER)
resizeable=True)
empty = getattr(victim, "isEmpty", False)

View File

@@ -94,7 +94,7 @@ class DmgPatternEditor(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title="Damage Pattern Editor", style=wx.RESIZE_BORDER,
parent, id=wx.ID_ANY, title="Damage Pattern Editor", resizeable=True,
# Dropdown list widget is scaled to its longest content line on GTK, adapt to that
size=wx.Size(500, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240))

View File

@@ -24,7 +24,7 @@ class AttributeEditor(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, wx.ID_ANY, title="Attribute Editor", pos=wx.DefaultPosition,
size=wx.Size(650, 600), style=wx.RESIZE_BORDER)
size=wx.Size(650, 600), resizeable=True)
i = wx.Icon(BitmapLoader.getBitmap("fit_rename_small", "gui"))
self.SetIcon(i)

View File

@@ -119,7 +119,7 @@ class ImplantSetEditor(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title="Implant Set Editor", style=wx.RESIZE_BORDER,
parent, id=wx.ID_ANY, title="Implant Set Editor", resizeable=True,
size=wx.Size(950, 500) if "wxGTK" in wx.PlatformInfo else wx.Size(850, 420))
self.block = False

View File

@@ -126,7 +126,7 @@ class TargetProfileEditor(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title="Target Profile Editor", style=wx.RESIZE_BORDER,
parent, id=wx.ID_ANY, title="Target Profile Editor", resizeable=True,
# Dropdown list widget is scaled to its longest content line on GTK, adapt to that
size=wx.Size(500, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(350, 240))

View File

@@ -99,14 +99,15 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal
return result
def roundToPrec(val, prec):
def roundToPrec(val, prec, nsValue=None):
"""
nsValue: custom value which should be used to determine normalization shift
"""
# We're not rounding integers anyway
# Also make sure that we do not ask to calculate logarithm of zero
if int(val) == val:
return int(val)
# Find round factor, taking into consideration that we want to keep at least prec
# positions for fractions with zero integer part (e.g. 0.0000354 for prec=3)
roundFactor = int(prec - math.ceil(math.log10(abs(val))))
roundFactor = int(prec - math.floor(math.log10(abs(val if nsValue is None else nsValue))) - 1)
# But we don't want to round integers
if roundFactor < 0:
roundFactor = 0

View File

@@ -305,9 +305,10 @@ class Market:
self.ITEMS_FORCEDMARKETGROUP_R = self.__makeRevDict(self.ITEMS_FORCEDMARKETGROUP)
self.FORCEDMARKETGROUP = {
685: False, # Ship Equipment > Electronic Warfare > ECCM
681: False, # Ship Equipment > Electronic Warfare > Sensor Backup Arrays
1639: False # Ship Equipment > Fleet Assistance > Command Processors
685: False, # Ship Equipment > Electronic Warfare > ECCM
681: False, # Ship Equipment > Electronic Warfare > Sensor Backup Arrays
1639: False, # Ship Equipment > Fleet Assistance > Command Processors
2527: True, # Ship Equipment > Hull & Armor > Mutadaptive Remote Armor Repairers - has hasTypes set to 1 while actually having no types
}
# Misc definitions
@@ -328,7 +329,7 @@ class Market:
"Structure",
"Structure Module",
)
self.SEARCH_GROUPS = ("Ice Product",)
self.SEARCH_GROUPS = ("Ice Product", "Cargo Container", "Secure Cargo Container", "Audit Log Secure Container", "Freight Container")
self.ROOT_MARKET_GROUPS = (9, # Ship Equipment
1111, # Rigs
157, # Drones