Merge branch 'PreferencesPaneV2' of https://github.com/Ebag333/Pyfa into PreferencesPaneV2

This commit is contained in:
Ebag333
2017-02-26 11:29:52 -08:00
16 changed files with 560 additions and 70 deletions

View File

@@ -23,6 +23,7 @@ from sqlalchemy.sql import and_, or_, select
import eos.config
from eos.db import gamedata_session
from eos.db.gamedata.metaGroup import metatypes_table, items_table
from eos.db.gamedata.group import groups_table
from eos.db.util import processEager, processWhere
from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData
@@ -249,7 +250,7 @@ def searchItems(nameLike, where=None, join=None, eager=None):
@cachedQuery(2, "where", "itemids")
def getVariations(itemids, where=None, eager=None):
def getVariations(itemids, groupIDs=None, where=None, eager=None):
for itemid in itemids:
if not isinstance(itemid, int):
raise TypeError("All passed item IDs must be integers")
@@ -262,7 +263,17 @@ def getVariations(itemids, where=None, eager=None):
joinon = items_table.c.typeID == metatypes_table.c.typeID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((metatypes_table, joinon)).filter(
filter).all()
return vars
if vars:
return vars
elif groupIDs:
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
filter = processWhere(itemfilter, where)
joinon = items_table.c.groupID == groups_table.c.groupID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
filter).all()
return vars
@cachedQuery(1, "attr")

View File

@@ -1,25 +0,0 @@
# ===============================================================================
# Copyright (C) 2010 Anton Vorobyov
#
# This file is part of eos.
#
# eos is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# eos is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from math import floor
def floorFloat(value):
result = int(floor(value))
return result

View File

@@ -127,6 +127,12 @@ class Fit(object):
self.__capUsed = None
self.__capRecharge = None
self.__calculatedTargets = []
self.__remoteReps = {
"Armor": None,
"Shield": None,
"Hull": None,
"Capacitor": None,
}
self.factorReload = False
self.boostsFits = set()
self.gangBoosts = None
@@ -392,6 +398,9 @@ class Fit(object):
self.ecmProjectedStr = 1
self.commandBonuses = {}
for remoterep_type in self.__remoteReps:
self.__remoteReps[remoterep_type] = None
del self.__calculatedTargets[:]
del self.__extraDrains[:]
@@ -1151,6 +1160,67 @@ class Fit(object):
self.__capStable = True
self.__capState = 100
@property
def remoteReps(self):
force_recalc = False
for remote_type in self.__remoteReps:
if self.__remoteReps[remote_type] is None:
force_recalc = True
break
if force_recalc is False:
return self.__remoteReps
# We are rerunning the recalcs. Explicitly set to 0 to make sure we don't duplicate anything and correctly set all values to 0.
for remote_type in self.__remoteReps:
self.__remoteReps[remote_type] = 0
for module in self.modules:
# Skip empty and non-Active modules
if module.isEmpty or module.state < State.ACTIVE:
continue
# Covert cycleTime to seconds
duration = module.cycleTime / 1000
# Skip modules with no duration.
if not duration:
continue
fueledMultiplier = module.getModifiedItemAttr("chargedArmorDamageMultiplier", 1)
remote_module_groups = {
"Remote Armor Repairer" : "Armor",
"Ancillary Remote Armor Repairer": "Armor",
"Remote Hull Repairer" : "Hull",
"Remote Shield Booster" : "Shield",
"Ancillary Remote Shield Booster": "Shield",
"Remote Capacitor Transmitter" : "Capacitor",
}
module_group = module.item.group.name
if module_group in remote_module_groups:
remote_type = remote_module_groups[module_group]
else:
# Module isn't in our list of remote rep modules, bail
continue
if remote_type == "Hull":
hp = module.getModifiedItemAttr("structureDamageAmount", 0)
elif remote_type == "Armor":
hp = module.getModifiedItemAttr("armorDamageAmount", 0)
elif remote_type == "Shield":
hp = module.getModifiedItemAttr("shieldBonus", 0)
elif remote_type == "Capacitor":
hp = module.getModifiedItemAttr("powerTransferAmount", 0)
else:
hp = 0
self.__remoteReps[remote_type] += (hp * fueledMultiplier) / duration
return self.__remoteReps
@property
def hp(self):
hp = {}

View File

@@ -20,11 +20,11 @@
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
from math import floor
import eos.db
from eos.effectHandlerHelpers import HandledItem, HandledCharge
from eos.enum import Enum
from eos.mathUtils import floorFloat
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
from eos.saveddata.citadel import Citadel
@@ -172,7 +172,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if chargeVolume is None or containerCapacity is None:
charges = 0
else:
charges = floorFloat(float(containerCapacity) / chargeVolume)
charges = floor(containerCapacity / chargeVolume)
return charges
@property
@@ -216,9 +216,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def __calculateAmmoShots(self):
if self.charge is not None:
# Set number of cycles before reload is needed
# numcycles = math.floor(module_capacity / (module_volume * module_chargerate))
chargeRate = self.getModifiedItemAttr("chargeRate")
numCharges = self.numCharges
numShots = floorFloat(float(numCharges) / chargeRate)
numShots = floor(numCharges / chargeRate)
else:
numShots = None
return numShots
@@ -231,7 +232,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
chance = self.getModifiedChargeAttr("crystalVolatilityChance")
damage = self.getModifiedChargeAttr("crystalVolatilityDamage")
crystals = self.numCharges
numShots = floorFloat(float(crystals * hp) / (damage * chance))
numShots = floor((crystals * hp) / (damage * chance))
else:
# Set 0 (infinite) for permanent crystals like t1 laser crystals
numShots = 0
@@ -665,32 +666,68 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def cycleTime(self):
reactivation = (self.getModifiedItemAttr("moduleReactivationDelay") or 0)
# Reactivation time starts counting after end of module cycle
speed = self.rawCycleTime + reactivation
if self.charge:
reload = self.reloadTime
else:
reload = 0.0
# Determine if we'll take into account reload time or not
factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload
# If reactivation is longer than 10 seconds then module can be reloaded
# during reactivation time, thus we may ignore reload
if factorReload and reactivation < reload:
numShots = self.numShots
# Time it takes to reload module after end of reactivation time,
# given that we started when module cycle has just over
additionalReloadTime = (reload - reactivation)
# Speed here already takes into consideration reactivation time
speed = (speed * numShots + additionalReloadTime) / numShots if numShots > 0 else speed
numShots = self.numShots
speed = self.rawCycleTime
if factorReload and self.charge:
raw_reload_time = self.reloadTime
else:
raw_reload_time = 0.0
# Module can only fire one shot at a time, think bomb launchers or defender launchers
if self.disallowRepeatingAction:
if numShots > 1:
"""
The actual mechanics behind this is complex. Behavior will be (for 3 ammo):
fire, reactivation delay, fire, reactivation delay, fire, max(reactivation delay, reload)
so your effective reload time depends on where you are at in the cycle.
We can't do that, so instead we'll average it out.
Currently would apply to bomb launchers and defender missiles
"""
effective_reload_time = ((self.reactivationDelay * numShots) + raw_reload_time) / numShots
else:
"""
Applies to MJD/MJFG
"""
effective_reload_time = max(raw_reload_time, self.reactivationDelay, 0)
else:
"""
Currently no other modules would have a reactivation delay, so for sanities sake don't try and account for it.
Okay, technically cloaks do, but they also have 0 cycle time and cap usage so why do you care?
"""
effective_reload_time = raw_reload_time
if numShots > 0 and self.charge:
speed = (speed * numShots + effective_reload_time) / numShots
return speed
@property
def rawCycleTime(self):
speed = self.getModifiedItemAttr("speed") or self.getModifiedItemAttr("duration")
speed = max(
self.getModifiedItemAttr("speed"), # Most weapons
self.getModifiedItemAttr("duration"), # Most average modules
self.getModifiedItemAttr("durationSensorDampeningBurstProjector"),
self.getModifiedItemAttr("durationTargetIlluminationBurstProjector"),
self.getModifiedItemAttr("durationECMJammerBurstProjector"),
self.getModifiedItemAttr("durationWeaponDisruptionBurstProjector"),
0, # Return 0 if none of the above are valid
)
return speed
@property
def disallowRepeatingAction(self):
return self.getModifiedItemAttr("disallowRepeatingAction", 0)
@property
def reactivationDelay(self):
return self.getModifiedItemAttr("moduleReactivationDelay", 0)
@property
def capUse(self):
capNeed = self.getModifiedItemAttr("capacitorNeed")

View File

@@ -22,4 +22,6 @@ __all__ = [
"metaSwap",
"implantSets",
"fighterAbilities",
"cargoAmmo",
"droneStack"
]

View File

@@ -0,0 +1,35 @@
from gui.contextMenu import ContextMenu
import gui.mainFrame
import service
import gui.globalEvents as GE
import wx
class CargoAmmo(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, srcContext, selection):
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False
for selected_item in selection:
if selected_item.category.ID in (
8, # Charge
):
return True
def getText(self, itmContext, selection):
return "Add {0} to Cargo (x1000)".format(itmContext)
def activate(self, fullContext, selection, i):
sFit = service.Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
typeID = int(selection[0].ID)
sFit.addCargo(fitID, typeID, 1000)
self.mainFrame.additionsPane.select("Cargo")
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
CargoAmmo.register()

View File

@@ -0,0 +1,37 @@
from gui.contextMenu import ContextMenu
import gui.mainFrame
import service
import gui.globalEvents as GE
import wx
class CargoAmmo(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, srcContext, selection):
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False
for selected_item in selection:
if selected_item.category.ID in (
18, # Drones
):
return True
return False
def getText(self, itmContext, selection):
return "Add {0} to Drone Bay (x5)".format(itmContext)
def activate(self, fullContext, selection, i):
sFit = service.Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
typeID = int(selection[0].ID)
sFit.addDrone(fitID, typeID, 5)
self.mainFrame.additionsPane.select("Drones")
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
CargoAmmo.register()

View File

@@ -9,6 +9,11 @@ import gui.mainFrame
import gui.globalEvents as GE
from gui.contextMenu import ContextMenu
from service.settings import ContextMenuSettings
from eos.saveddata.booster import Booster
from eos.saveddata.module import Module
from eos.saveddata.drone import Drone
from eos.saveddata.fighter import Fighter
from eos.saveddata.implant import Implant
class MetaSwap(ContextMenu):
@@ -20,7 +25,13 @@ class MetaSwap(ContextMenu):
if not self.settings.get('metaSwap'):
return False
if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule",):
if self.mainFrame.getActiveFit() is None or srcContext not in (
"fittingModule",
"droneItem",
"fighterItem",
"boosterItem",
"implantItem",
):
return False
# Check if list of variations is same for all of selection
@@ -56,6 +67,17 @@ class MetaSwap(ContextMenu):
def get_metagroup(x):
return x.metaGroup.ID if x.metaGroup is not None else 0
def get_boosterrank(x):
# If we're returning a lot of items, sort my name
if len(self.variations) > 7:
return x.name
# Sort by booster chance to get some sort of pseudorank.
elif 'boosterEffectChance1' in x.attributes:
return x.attributes['boosterEffectChance1'].value
# the "first" rank (Synth) doesn't have boosterEffectChance1. If we're not pulling back all boosters, return 0 for proper sorting
else:
return 0
m = wx.Menu()
# If on Windows we need to bind out events into the root menu, on other
@@ -67,8 +89,17 @@ class MetaSwap(ContextMenu):
# Sort items by metalevel, and group within that metalevel
items = list(self.variations)
items.sort(key=get_metalevel)
items.sort(key=get_metagroup)
print context
if "implantItem" in context:
# sort implants based on name
items.sort(key=lambda x: x.name)
elif "boosterItem" in context:
# boosters don't have meta or anything concrete that we can rank by. Go by chance to inflict side effect
items.sort(key=get_boosterrank)
else:
# sort by group and meta level
items.sort(key=get_metalevel)
items.sort(key=get_metagroup)
group = None
for item in items:
@@ -78,7 +109,7 @@ class MetaSwap(ContextMenu):
else:
thisgroup = item.metaGroup.name
if thisgroup != group:
if thisgroup != group and context not in ("implantItem", "boosterItem"):
group = thisgroup
id = ContextMenu.nextID()
m.Append(id, u'%s' % group)
@@ -101,9 +132,56 @@ class MetaSwap(ContextMenu):
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
for mod in self.selection:
pos = fit.modules.index(mod)
sFit.changeModule(fitID, pos, item.ID)
for selected_item in self.selection:
if isinstance(selected_item, Module):
pos = fit.modules.index(selected_item)
sFit.changeModule(fitID, pos, item.ID)
elif isinstance(selected_item, Drone):
drone_count = None
for idx, drone_stack in enumerate(fit.drones):
if drone_stack is selected_item:
drone_count = drone_stack.amount
sFit.removeDrone(fitID, idx, drone_count)
break
if drone_count:
sFit.addDrone(fitID, item.ID, drone_count)
elif isinstance(selected_item, Fighter):
fighter_count = None
for idx, fighter_stack in enumerate(fit.fighters):
# Right now fighters always will have max stack size.
# Including this for future improvement, so if adjustable
# fighter stacks get added we're ready for it.
if fighter_stack is selected_item:
if fighter_stack.amount > 0:
fighter_count = fighter_stack.amount
elif fighter_stack.amount == -1:
fighter_count = fighter_stack.amountActive
else:
fighter_count.amount = 0
sFit.removeFighter(fitID, idx)
break
sFit.addFighter(fitID, item.ID)
elif isinstance(selected_item, Booster):
for idx, booster_stack in enumerate(fit.boosters):
if booster_stack is selected_item:
sFit.removeBooster(fitID, idx)
sFit.addBooster(fitID, item.ID)
break
elif isinstance(selected_item, Implant):
for idx, implant_stack in enumerate(fit.implants):
if implant_stack is selected_item:
sFit.removeImplant(fitID, idx)
sFit.addImplant(fitID, item.ID, False)
break
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))

View File

@@ -4,6 +4,7 @@ __all__ = [
"rechargeViewFull",
"firepowerViewFull",
"capacitorViewFull",
"outgoingViewFull",
"targetingMiscViewMinimal",
"priceViewFull",
]

View File

@@ -0,0 +1,106 @@
# ===============================================================================
# Copyright (C) 2014 Alexandros Kosiaris
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
# noinspection PyPackageRequirements
import wx
from gui.statsView import StatsView
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
class OutgoingViewFull(StatsView):
name = "outgoingViewFull"
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
self._cachedValues = []
def getHeaderText(self, fit):
return "Remote Reps"
def getTextExtentW(self, text):
width, height = self.parent.GetTextExtent(text)
return width
def populatePanel(self, contentPanel, headerPanel):
contentSizer = contentPanel.GetSizer()
parent = self.panel = contentPanel
self.headerPanel = headerPanel
sizerOutgoing = wx.GridSizer(1, 4)
contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0)
counter = 0
rr_list = [
("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."),
("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."),
("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."),
("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."),
]
for outgoingType, label, image, tooltip in rr_list:
baseBox = wx.BoxSizer(wx.VERTICAL)
baseBox.Add(BitmapLoader.getStaticBitmap("%s_big" % image, parent, "gui"), 0, wx.ALIGN_CENTER)
if "Capacitor" in outgoingType:
lbl = wx.StaticText(parent, wx.ID_ANY, u"0 GJ/s")
else:
lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s")
lbl.SetToolTip(wx.ToolTip(tooltip))
setattr(self, "label%s" % outgoingType, lbl)
baseBox.Add(lbl, 0, wx.ALIGN_CENTER)
self._cachedValues.append(0)
counter += 1
sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
# If we did anything intresting, we'd update our labels to reflect the new fit's stats here
stats = [
("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, u"%s HP/s", None),
("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, u"%s HP/s", None),
("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, u"%s HP/s", None),
("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, u"%s GJ/s", None),
]
counter = 0
for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
label = getattr(self, labelName)
value = value() if fit is not None else 0
value = value if value is not None else 0
if self._cachedValues[counter] != value:
valueStr = formatAmount(value, prec, lowest, highest)
label.SetLabel(valueFormat % valueStr)
tipStr = valueFormat % valueStr if altFormat is None else altFormat % value
label.SetToolTip(wx.ToolTip(tipStr))
self._cachedValues[counter] = value
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
OutgoingViewFull.register()

View File

@@ -0,0 +1,106 @@
# ===============================================================================
# Copyright (C) 2014 Alexandros Kosiaris
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
# noinspection PyPackageRequirements
import wx
from gui.statsView import StatsView
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
class OutgoingViewFull(StatsView):
name = "outgoingViewFull"
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
self._cachedValues = []
def getHeaderText(self, fit):
return "Remote Reps"
def getTextExtentW(self, text):
width, height = self.parent.GetTextExtent(text)
return width
def populatePanel(self, contentPanel, headerPanel):
contentSizer = contentPanel.GetSizer()
parent = self.panel = contentPanel
self.headerPanel = headerPanel
sizerOutgoing = wx.GridSizer(1, 4)
contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0)
counter = 0
rr_list = [
("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."),
("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."),
("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."),
("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."),
]
for outgoingType, label, image, tooltip in rr_list:
baseBox = wx.BoxSizer(wx.VERTICAL)
baseBox.Add(BitmapLoader.getStaticBitmap("%s_big" % image, parent, "gui"), 0, wx.ALIGN_CENTER)
if "Capacitor" in outgoingType:
lbl = wx.StaticText(parent, wx.ID_ANY, u"0 GJ/s")
else:
lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s")
lbl.SetToolTip(wx.ToolTip(tooltip))
setattr(self, "label%s" % outgoingType, lbl)
baseBox.Add(lbl, 0, wx.ALIGN_CENTER)
self._cachedValues.append(0)
counter += 1
sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
# If we did anything intresting, we'd update our labels to reflect the new fit's stats here
stats = [
("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, u"%s HP/s", None),
("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, u"%s HP/s", None),
("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, u"%s HP/s", None),
("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, u"%s GJ/s", None),
]
counter = 0
for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
label = getattr(self, labelName)
value = value() if fit is not None else 0
value = value if value is not None else 0
if self._cachedValues[counter] != value:
valueStr = formatAmount(value, prec, lowest, highest)
label.SetLabel(valueFormat % valueStr)
tipStr = valueFormat % valueStr if altFormat is None else altFormat % value
label.SetToolTip(wx.ToolTip(tipStr))
self._cachedValues[counter] = value
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
OutgoingViewFull.register()

View File

@@ -40,6 +40,7 @@ class StatsPane(wx.Panel):
"resistances",
"recharge",
"firepower",
"outgoingView",
"capacitor",
"targetingMisc",
"price",

View File

@@ -52,4 +52,5 @@ from gui.builtinStatsViews import ( # noqa: E402, F401
rechargeViewFull,
targetingMiscViewMinimal,
priceViewFull,
outgoingViewFull,
)

View File

@@ -688,7 +688,7 @@ class Fit(object):
self.recalc(fit)
return True
def addDrone(self, fitID, itemID):
def addDrone(self, fitID, itemID, numDronesToAdd=1):
if fitID is None:
return False
@@ -707,7 +707,7 @@ class Fit(object):
fit.drones.append(drone)
else:
return False
drone.amount += 1
drone.amount += numDronesToAdd
eos.db.commit()
self.recalc(fit)
return True

View File

@@ -590,7 +590,40 @@ class Market(object):
parents = set()
# Set-container for variables
variations = set()
variations_limiter = set()
for item in items:
if item.category.ID == 20: # Implants and Boosters
implant_remove_list = set()
implant_remove_list.add("Low-Grade ")
implant_remove_list.add("Low-grade ")
implant_remove_list.add("Mid-Grade ")
implant_remove_list.add("Mid-grade ")
implant_remove_list.add("High-Grade ")
implant_remove_list.add("High-grade ")
implant_remove_list.add("Limited ")
implant_remove_list.add(" - Advanced")
implant_remove_list.add(" - Basic")
implant_remove_list.add(" - Elite")
implant_remove_list.add(" - Improved")
implant_remove_list.add(" - Standard")
implant_remove_list.add("Copper ")
implant_remove_list.add("Gold ")
implant_remove_list.add("Silver ")
implant_remove_list.add("Advanced ")
implant_remove_list.add("Improved ")
implant_remove_list.add("Prototype ")
implant_remove_list.add("Standard ")
implant_remove_list.add("Strong ")
implant_remove_list.add("Synth ")
for implant_prefix in ("-6", "-7", "-8", "-9", "-10"):
for i in range(50):
implant_remove_list.add(implant_prefix + str("%02d" % i))
for text_to_remove in implant_remove_list:
if text_to_remove in item.name:
variations_limiter.add(item.name.replace(text_to_remove, ""))
# Get parent item
if alreadyparent is False:
parent = self.getParentItemByItem(item)
@@ -608,7 +641,16 @@ class Market(object):
variations.update(parents)
# Add all variations of parents to the set
parentids = tuple(item.ID for item in parents)
variations.update(eos.db.getVariations(parentids))
groupids = tuple(item.group.ID for item in parents)
variations_list = eos.db.getVariations(parentids, groupids)
if variations_limiter:
for limit in variations_limiter:
trimmed_variations_list = [variation_item for variation_item in variations_list if limit in variation_item.name]
if trimmed_variations_list:
variations_list = trimmed_variations_list
variations.update(variations_list)
return variations
def getGroupsByCategory(self, cat):

View File

@@ -1,12 +0,0 @@
from eos.mathUtils import floorFloat
def test_floorFloat():
assert type(floorFloat(1)) is not float
assert type(floorFloat(1)) is int
assert type(floorFloat(1.1)) is not float
assert type(floorFloat(1.1)) is int
assert floorFloat(1.1) == 1
assert floorFloat(1.9) == 1
assert floorFloat(1.5) == 1
assert floorFloat(-1.5) == -2