Merge branch 'ammo_graph' into singularity
# Conflicts: # gui/targetProfileEditor.py
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
import wx
|
||||
|
||||
|
||||
class AuxiliaryFrame(wx.Frame):
|
||||
class AuxiliaryMixin:
|
||||
|
||||
_instance = None
|
||||
|
||||
@@ -55,14 +55,26 @@ class AuxiliaryFrame(wx.Frame):
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
|
||||
@classmethod
|
||||
def openOne(cls, parent, *args, **kwargs):
|
||||
def openOne(cls, parent, *args, forceReopen=False, **kwargs):
|
||||
"""If window is open and alive - raise it, open otherwise"""
|
||||
if not cls._instance:
|
||||
if not cls._instance or forceReopen:
|
||||
if cls._instance:
|
||||
cls._instance.Close()
|
||||
frame = cls(parent, *args, **kwargs)
|
||||
cls._instance = frame
|
||||
frame.Show()
|
||||
else:
|
||||
cls._instance.Raise()
|
||||
return cls._instance
|
||||
|
||||
|
||||
def OnSuppressedAction(self, event):
|
||||
return
|
||||
|
||||
|
||||
class AuxiliaryFrame(AuxiliaryMixin, wx.Frame):
|
||||
pass
|
||||
|
||||
|
||||
class AuxiliaryDialog(AuxiliaryMixin, wx.Dialog):
|
||||
pass
|
||||
@@ -36,6 +36,11 @@ import gui.fitCommands as cmd
|
||||
from gui.fitCommands.helpers import droneStackLimit
|
||||
|
||||
|
||||
DRONE_ORDER = ('Light Scout Drones', 'Medium Scout Drones',
|
||||
'Heavy Attack Drones', 'Sentry Drones', 'Combat Utility Drones',
|
||||
'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones')
|
||||
|
||||
|
||||
class DroneViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(DroneViewDrop, self).__init__(*args, **kwargs)
|
||||
@@ -186,17 +191,13 @@ class DroneView(Display):
|
||||
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
|
||||
fitID=fitID, srcPosition=srcPosition, dstPosition=dstPosition))
|
||||
|
||||
DRONE_ORDER = ('Light Scout Drones', 'Medium Scout Drones',
|
||||
'Heavy Attack Drones', 'Sentry Drones', 'Combat Utility Drones',
|
||||
'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones')
|
||||
|
||||
def droneKey(self, drone):
|
||||
@staticmethod
|
||||
def droneKey(drone):
|
||||
sMkt = Market.getInstance()
|
||||
|
||||
groupName = sMkt.getMarketGroupByItem(drone.item).name
|
||||
|
||||
return (self.DRONE_ORDER.index(groupName),
|
||||
drone.item.name)
|
||||
return (DRONE_ORDER.index(groupName), drone.item.name)
|
||||
|
||||
def fitChanged(self, event):
|
||||
event.Skip()
|
||||
|
||||
@@ -34,6 +34,9 @@ from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
||||
|
||||
|
||||
class FighterViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(FighterViewDrop, self).__init__(*args, **kwargs)
|
||||
@@ -250,11 +253,10 @@ class FighterDisplay(d.Display):
|
||||
def _merge(src, dst):
|
||||
return
|
||||
|
||||
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
||||
|
||||
def fighterKey(self, fighter):
|
||||
@staticmethod
|
||||
def fighterKey(fighter):
|
||||
groupName = Market.getInstance().getGroupByItem(fighter.item).name
|
||||
orderPos = self.FIGHTER_ORDER.index(groupName)
|
||||
orderPos = FIGHTER_ORDER.index(groupName)
|
||||
# Sort support fighters by name, ignore their abilities
|
||||
if groupName == 'Support Fighter':
|
||||
abilityEffectIDs = ()
|
||||
|
||||
@@ -6,6 +6,7 @@ from gui.builtinContextMenus import fitAddCurrentlyOpen
|
||||
from gui.builtinContextMenus import envEffectAdd
|
||||
from gui.builtinContextMenus import commandFitAdd
|
||||
from gui.builtinContextMenus.targetProfile import adder
|
||||
from gui.builtinContextMenus import graphFitAmmoPicker
|
||||
# Often-used item manipulations
|
||||
from gui.builtinContextMenus import shipModeChange
|
||||
from gui.builtinContextMenus import moduleAmmoChange
|
||||
|
||||
241
gui/builtinContextMenus/graphFitAmmoPicker.py
Normal file
241
gui/builtinContextMenus/graphFitAmmoPicker.py
Normal file
@@ -0,0 +1,241 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.auxWindow import AuxiliaryDialog
|
||||
from gui.contextMenu import ContextMenuSingle
|
||||
from service.ammo import Ammo
|
||||
from service.market import Market
|
||||
|
||||
|
||||
class GraphFitAmmoPicker(ContextMenuSingle):
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem):
|
||||
if srcContext != 'graphFitList':
|
||||
return False
|
||||
if mainItem is None or not mainItem.isFit:
|
||||
return False
|
||||
if callingWindow.graphFrame.getView().internalName != 'dmgStatsGraph':
|
||||
return False
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem):
|
||||
return 'Plot with Different Ammo...'
|
||||
|
||||
def activate(self, callingWindow, fullContext, mainItem, i):
|
||||
AmmoPickerFrame.openOne(callingWindow, mainItem.item, forceReopen=True)
|
||||
|
||||
|
||||
GraphFitAmmoPicker.register()
|
||||
|
||||
|
||||
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 = 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()
|
||||
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
|
||||
|
||||
def kbEvent(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||
self.Close()
|
||||
return
|
||||
event.Skip()
|
||||
|
||||
|
||||
class AmmoPickerContents(wx.ScrolledCanvas):
|
||||
|
||||
indent = 15
|
||||
|
||||
def __init__(self, parent, fit):
|
||||
wx.ScrolledCanvas.__init__(self, parent)
|
||||
self.SetScrollRate(0, 15)
|
||||
|
||||
mods = self.getMods(fit)
|
||||
drones = self.getDrones(fit)
|
||||
fighters = self.getFighters(fit)
|
||||
self.rbLabelMap = {}
|
||||
self.rbCheckboxMap = {}
|
||||
|
||||
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
|
||||
|
||||
for modInfo, modAmmo in mods:
|
||||
text = '\n'.join('{}x {}'.format(amount, item.name) for item, amount in modInfo)
|
||||
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:
|
||||
self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=1)
|
||||
else:
|
||||
for ammoCatName, ammos in ammoTree.items():
|
||||
if len(ammos) == 1:
|
||||
ammo = next(iter(ammos))
|
||||
self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=1)
|
||||
else:
|
||||
self.addLabel(moduleSizer, '{}:'.format(ammoCatName), modRb, indentLvl=1)
|
||||
for ammo in ammos:
|
||||
self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=2)
|
||||
if drones:
|
||||
droneRb = self.addRadioButton(self.droneSizer, 'Drones', firstRadio)
|
||||
from gui.builtinAdditionPanes.droneView import DroneView
|
||||
for drone in sorted(drones, key=DroneView.droneKey):
|
||||
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:
|
||||
fighterRb = self.addRadioButton(fighterSizer, 'Fighters', firstRadio)
|
||||
from gui.builtinAdditionPanes.fighterView import FighterDisplay
|
||||
for fighter in sorted(fighters, key=FighterDisplay.fighterKey):
|
||||
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()
|
||||
loadableChargesCache = {}
|
||||
# Modules, format: {frozenset(ammo): {item: count}}
|
||||
modsPrelim = {}
|
||||
if fit is not None:
|
||||
for mod in fit.modules:
|
||||
if not mod.canDealDamage():
|
||||
continue
|
||||
typeID = mod.item.ID
|
||||
if typeID not in loadableChargesCache:
|
||||
loadableChargesCache[typeID] = sAmmo.getModuleFlatAmmo(mod)
|
||||
charges = loadableChargesCache[typeID]
|
||||
# We're not interested in modules which contain no charges
|
||||
if charges:
|
||||
data = modsPrelim.setdefault(frozenset(charges), {})
|
||||
if mod.item not in data:
|
||||
data[mod.item] = 0
|
||||
data[mod.item] += 1
|
||||
# Format: [([(item, count), ...], frozenset(ammo)), ...]
|
||||
modsFinal = []
|
||||
for charges, itemCounts in modsPrelim.items():
|
||||
modsFinal.append((
|
||||
# Sort items within group
|
||||
sorted(itemCounts.items(), key=lambda i: sMkt.itemSort(i[0], reverseMktGrp=True), reverse=True),
|
||||
charges))
|
||||
# Sort item groups
|
||||
modsFinal.sort(key=lambda i: sMkt.itemSort(i[0][0][0], reverseMktGrp=True), reverse=True)
|
||||
return modsFinal
|
||||
|
||||
def getDrones(self, fit):
|
||||
drones = []
|
||||
if fit is not None:
|
||||
for drone in fit.drones:
|
||||
if drone.item is None:
|
||||
continue
|
||||
# Drones are our "ammo", so we want to pick even those which are inactive
|
||||
if drone.canDealDamage(ignoreState=True):
|
||||
drones.append(drone)
|
||||
continue
|
||||
if {'remoteWebifierEntity', 'remoteTargetPaintEntity'}.intersection(drone.item.effects):
|
||||
drones.append(drone)
|
||||
continue
|
||||
return drones
|
||||
|
||||
def getFighters(self, fit):
|
||||
fighters = []
|
||||
if fit is not None:
|
||||
for fighter in fit.fighters:
|
||||
if fighter.item is None:
|
||||
continue
|
||||
# Fighters are our "ammo" as well
|
||||
if fighter.canDealDamage(ignoreState=True):
|
||||
fighters.append(fighter)
|
||||
continue
|
||||
for ability in fighter.abilities:
|
||||
if not ability.active:
|
||||
continue
|
||||
if ability.effect.name == 'fighterAbilityStasisWebifier':
|
||||
fighters.append(fighter)
|
||||
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():
|
||||
for item in items:
|
||||
item.Enable(rb.GetValue())
|
||||
|
||||
def rbSelected(self, event):
|
||||
event.Skip()
|
||||
self.refreshStatus()
|
||||
@@ -3,33 +3,28 @@ import wx
|
||||
|
||||
import gui.fitCommands as cmd
|
||||
import gui.mainFrame
|
||||
from eos.const import FittingHardpoint
|
||||
from eos.saveddata.module import Module
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from gui.fitCommands.helpers import getSimilarModPositions
|
||||
from service.ammo import Ammo
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
class ChangeModuleAmmo(ContextMenuCombined):
|
||||
|
||||
DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal")
|
||||
MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed")
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
# Format: {type ID: set(loadable, charges)}
|
||||
self.loadableCharges = {}
|
||||
self.loadableChargesCache = {}
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem, selection):
|
||||
if srcContext not in ("fittingModule", "projectedModule"):
|
||||
if srcContext not in ('fittingModule', 'projectedModule'):
|
||||
return False
|
||||
|
||||
if self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
self.mainCharges = self.getChargesForMod(mainItem)
|
||||
self.mainCharges = self._getAmmo(mainItem)
|
||||
if not self.mainCharges:
|
||||
return False
|
||||
|
||||
@@ -39,186 +34,81 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem, selection):
|
||||
return "Charge"
|
||||
return 'Charge'
|
||||
|
||||
def getChargesForMod(self, mod):
|
||||
sMkt = Market.getInstance()
|
||||
if mod is None or mod.isEmpty:
|
||||
def _getAmmo(self, mod):
|
||||
if mod.itemID is None:
|
||||
return set()
|
||||
typeID = mod.item.ID
|
||||
if typeID in self.loadableCharges:
|
||||
return self.loadableCharges[typeID]
|
||||
chargeSet = self.loadableCharges.setdefault(typeID, set())
|
||||
# Do not try to grab it for modes which can also be passed as part of selection
|
||||
if isinstance(mod, Module):
|
||||
for charge in mod.getValidCharges():
|
||||
if sMkt.getPublicityByItem(charge):
|
||||
chargeSet.add(charge)
|
||||
return chargeSet
|
||||
if mod.itemID not in self.loadableChargesCache:
|
||||
self.loadableChargesCache[mod.itemID] = Ammo.getInstance().getModuleFlatAmmo(mod)
|
||||
return self.loadableChargesCache[mod.itemID]
|
||||
|
||||
def turretSorter(self, charge):
|
||||
damage = 0
|
||||
range_ = (self.module.item.getAttribute("maxRange")) * \
|
||||
(charge.getAttribute("weaponRangeMultiplier") or 1)
|
||||
falloff = (self.module.item.getAttribute("falloff") or 0) * \
|
||||
(charge.getAttribute("fallofMultiplier") or 1)
|
||||
for type_ in self.DAMAGE_TYPES:
|
||||
d = charge.getAttribute("%sDamage" % type_)
|
||||
if d > 0:
|
||||
damage += d
|
||||
|
||||
# Take optimal and falloff as range factor
|
||||
rangeFactor = range_ + falloff
|
||||
|
||||
return - rangeFactor, charge.name.rsplit()[-2:], damage, charge.name
|
||||
|
||||
def missileSorter(self, charge):
|
||||
# Get charge damage type and total damage
|
||||
chargeDamageType, totalDamage = self.damageInfo(charge)
|
||||
# Find its position in sort list
|
||||
position = self.MISSILE_ORDER.index(chargeDamageType)
|
||||
return position, totalDamage, charge.name
|
||||
|
||||
def damageInfo(self, charge):
|
||||
# Set up data storage for missile damage stuff
|
||||
damageMap = {}
|
||||
totalDamage = 0
|
||||
# Fill them with the data about charge
|
||||
for damageType in self.DAMAGE_TYPES:
|
||||
currentDamage = charge.getAttribute("{0}Damage".format(damageType)) or 0
|
||||
damageMap[damageType] = currentDamage
|
||||
totalDamage += currentDamage
|
||||
# Detect type of ammo
|
||||
chargeDamageType = None
|
||||
for damageType in damageMap:
|
||||
# If all damage belongs to certain type purely, set appropriate
|
||||
# ammoType
|
||||
if damageMap[damageType] == totalDamage:
|
||||
chargeDamageType = damageType
|
||||
break
|
||||
# Else consider ammo as mixed damage
|
||||
if chargeDamageType is None:
|
||||
chargeDamageType = "mixed"
|
||||
|
||||
return chargeDamageType, totalDamage
|
||||
|
||||
@staticmethod
|
||||
def numericConverter(string):
|
||||
return int(string) if string.isdigit() else string
|
||||
|
||||
def nameSorter(self, charge):
|
||||
parts = charge.name.split(" ")
|
||||
return list(map(self.numericConverter, parts))
|
||||
|
||||
def addCharge(self, menu, charge):
|
||||
def _addCharge(self, menu, charge):
|
||||
id_ = ContextMenuCombined.nextID()
|
||||
name = charge.name if charge is not None else "Empty"
|
||||
self.chargeIds[id_] = charge
|
||||
name = charge.name if charge is not None else 'Empty'
|
||||
self.chargeEventMap[id_] = charge
|
||||
item = wx.MenuItem(menu, id_, name)
|
||||
menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item)
|
||||
item.charge = charge
|
||||
if charge is not None and charge.iconID is not None:
|
||||
bitmap = BitmapLoader.getBitmap(charge.iconID, "icons")
|
||||
bitmap = BitmapLoader.getBitmap(charge.iconID, 'icons')
|
||||
if bitmap is not None:
|
||||
item.SetBitmap(bitmap)
|
||||
|
||||
return item
|
||||
|
||||
@staticmethod
|
||||
def addSeperator(m, text):
|
||||
def _addSeparator(m, text):
|
||||
id_ = ContextMenuCombined.nextID()
|
||||
m.Append(id_, '─ %s ─' % text)
|
||||
m.Enable(id_, False)
|
||||
|
||||
def getSubMenu(self, callingWindow, context, mainItem, selection, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
m = wx.Menu()
|
||||
self.chargeIds = {}
|
||||
hardpoint = self.module.hardpoint
|
||||
moduleName = self.module.item.name
|
||||
# Make sure we do not consider mining turrets as combat turrets
|
||||
if hardpoint == FittingHardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount", None) is None:
|
||||
self.addSeperator(m, "Long Range")
|
||||
items = []
|
||||
range_ = None
|
||||
nameBase = None
|
||||
sub = None
|
||||
chargesSorted = sorted(self.mainCharges, key=self.turretSorter)
|
||||
for charge in chargesSorted:
|
||||
if "civilian" in charge.name.lower():
|
||||
continue
|
||||
currBase = charge.name.rsplit()[-2:]
|
||||
currRange = charge.getAttribute("weaponRangeMultiplier")
|
||||
if nameBase is None or range_ != currRange or nameBase != currBase:
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
|
||||
sub = None
|
||||
base = charge
|
||||
nameBase = currBase
|
||||
range_ = currRange
|
||||
item = self.addCharge(rootMenu if msw else m, charge)
|
||||
items.append(item)
|
||||
msw = True if 'wxMSW' in wx.PlatformInfo else False
|
||||
menu = wx.Menu()
|
||||
self.chargeEventMap = {}
|
||||
modType, chargeDict = Ammo.getInstance().getModuleStructuredAmmo(self.module, ammo=self.mainCharges)
|
||||
if modType == 'ddTurret':
|
||||
self._addSeparator(menu, 'Long Range')
|
||||
menuItems = []
|
||||
for charges in chargeDict.values():
|
||||
if len(charges) == 1:
|
||||
menuItems.append(self._addCharge(rootMenu if msw else menu, charges[0]))
|
||||
else:
|
||||
if sub is None and item and base:
|
||||
sub = wx.Menu()
|
||||
sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
self.addSeperator(sub, "Less Damage")
|
||||
item.SetSubMenu(sub)
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, base))
|
||||
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, charge))
|
||||
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
|
||||
for item in items:
|
||||
m.Append(item)
|
||||
|
||||
self.addSeperator(m, "Short Range")
|
||||
elif hardpoint == FittingHardpoint.MISSILE and moduleName != 'Festival Launcher':
|
||||
type_ = None
|
||||
sub = None
|
||||
defender = None
|
||||
chargesSorted = sorted(self.mainCharges, key=self.missileSorter)
|
||||
for charge in chargesSorted:
|
||||
currType = self.damageInfo(charge)[0]
|
||||
|
||||
if currType != type_ or type_ is None:
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
|
||||
type_ = currType
|
||||
item = wx.MenuItem(m, wx.ID_ANY, type_.capitalize())
|
||||
bitmap = BitmapLoader.getBitmap("%s_small" % type_, "gui")
|
||||
if bitmap is not None:
|
||||
item.SetBitmap(bitmap)
|
||||
|
||||
sub = wx.Menu()
|
||||
sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
self.addSeperator(sub, "Less Damage")
|
||||
item.SetSubMenu(sub)
|
||||
m.Append(item)
|
||||
|
||||
if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"):
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, charge))
|
||||
else:
|
||||
defender = charge
|
||||
|
||||
if defender is not None:
|
||||
m.Append(self.addCharge(rootMenu if msw else m, defender))
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
else:
|
||||
chargesSorted = sorted(self.mainCharges, key=self.nameSorter)
|
||||
for charge in chargesSorted:
|
||||
m.Append(self.addCharge(rootMenu if msw else m, charge))
|
||||
|
||||
m.Append(self.addCharge(rootMenu if msw else m, None))
|
||||
return m
|
||||
baseCharge = charges[0]
|
||||
menuItem = self._addCharge(rootMenu if msw else menu, baseCharge)
|
||||
menuItems.append(menuItem)
|
||||
subMenu = wx.Menu()
|
||||
subMenu.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
menuItem.SetSubMenu(subMenu)
|
||||
self._addSeparator(subMenu, 'Less Damage')
|
||||
for charge in charges:
|
||||
subMenu.Append(self._addCharge(rootMenu if msw else subMenu, charge))
|
||||
self._addSeparator(subMenu, 'More Damage')
|
||||
for menuItem in menuItems:
|
||||
menu.Append(menuItem)
|
||||
self._addSeparator(menu, 'Short Range')
|
||||
elif modType == 'ddMissile':
|
||||
menuItems = []
|
||||
for chargeCatName, charges in chargeDict.items():
|
||||
menuItem = wx.MenuItem(menu, wx.ID_ANY, chargeCatName.capitalize())
|
||||
menuItems.append(menuItem)
|
||||
subMenu = wx.Menu()
|
||||
subMenu.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
menuItem.SetSubMenu(subMenu)
|
||||
self._addSeparator(subMenu, 'Less Damage')
|
||||
for charge in charges:
|
||||
subMenu.Append(self._addCharge(rootMenu if msw else subMenu, charge))
|
||||
self._addSeparator(subMenu, 'More Damage')
|
||||
for menuItem in menuItems:
|
||||
menu.Append(menuItem)
|
||||
elif modType == 'general':
|
||||
for charge in chargeDict['general']:
|
||||
menu.Append(self._addCharge(rootMenu if msw else menu, charge))
|
||||
menu.Append(self._addCharge(rootMenu if msw else menu, None))
|
||||
return menu
|
||||
|
||||
def handleAmmoSwitch(self, event):
|
||||
charge = self.chargeIds.get(event.Id, False)
|
||||
charge = self.chargeEventMap.get(event.Id, False)
|
||||
if charge is False:
|
||||
event.Skip()
|
||||
return
|
||||
@@ -254,7 +144,7 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
||||
positions = []
|
||||
for position, mod in enumerate(modContainer):
|
||||
if mod in self.selection:
|
||||
modCharges = self.getChargesForMod(mod)
|
||||
modCharges = self._getAmmo(mod)
|
||||
if modCharges.issubset(self.mainCharges):
|
||||
positions.append(position)
|
||||
self.mainFrame.command.Submit(command(
|
||||
|
||||
@@ -35,7 +35,7 @@ class ItemDescription(wx.Panel):
|
||||
self.Layout()
|
||||
|
||||
self.description.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
|
||||
self.description.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
|
||||
self.description.Bind(wx.EVT_KEY_UP, self.onKeyUp)
|
||||
|
||||
self.popupMenu = wx.Menu()
|
||||
copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
|
||||
@@ -50,7 +50,7 @@ class ItemDescription(wx.Panel):
|
||||
if selectedMenuItem == 1: # Copy was chosen
|
||||
self.copySelectionToClipboard()
|
||||
|
||||
def onKeyDown(self, event):
|
||||
def onKeyUp(self, event):
|
||||
keyCode = event.GetKeyCode()
|
||||
# Ctrl + C
|
||||
if keyCode == 67 and event.ControlDown():
|
||||
|
||||
@@ -14,7 +14,7 @@ class ItemTraits(wx.Panel):
|
||||
self.traits.SetPage(item.traits.traitText)
|
||||
|
||||
self.traits.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
|
||||
self.traits.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
|
||||
self.traits.Bind(wx.EVT_KEY_UP, self.onKeyUp)
|
||||
|
||||
mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.Layout()
|
||||
@@ -32,7 +32,7 @@ class ItemTraits(wx.Panel):
|
||||
if selectedMenuItem == 1: # Copy was chosen
|
||||
self.copySelectionToClipboard()
|
||||
|
||||
def onKeyDown(self, event):
|
||||
def onKeyUp(self, event):
|
||||
keyCode = event.GetKeyCode()
|
||||
# Ctrl + C
|
||||
if keyCode == 67 and event.ControlDown():
|
||||
|
||||
@@ -203,22 +203,6 @@ class ItemView(Display):
|
||||
self.setToggles()
|
||||
self.filterItemStore()
|
||||
|
||||
def itemSort(self, item):
|
||||
sMkt = self.sMkt
|
||||
catname = sMkt.getCategoryByItem(item).name
|
||||
try:
|
||||
mktgrpid = sMkt.getMarketGroupByItem(item).ID
|
||||
except AttributeError:
|
||||
mktgrpid = -1
|
||||
pyfalog.warning("unable to find market group for {}".format(item.name))
|
||||
parentname = sMkt.getParentItemByItem(item).name
|
||||
# Get position of market group
|
||||
metagrpid = sMkt.getMetaGroupIdByItem(item)
|
||||
metatab = sMkt.META_MAP_REVERSE_INDICES.get(metagrpid)
|
||||
metalvl = item.metaLevel or 0
|
||||
|
||||
return catname, mktgrpid, parentname, metatab, metalvl, item.name
|
||||
|
||||
def contextMenu(self, event):
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
self.ensureSelection(clickedPos)
|
||||
@@ -241,7 +225,7 @@ class ItemView(Display):
|
||||
self.unselectAll()
|
||||
# Perform sorting, using item's meta levels besides other stuff
|
||||
if self.marketBrowser.mode != 'recent':
|
||||
items.sort(key=self.itemSort)
|
||||
items.sort(key=self.sMkt.itemSort)
|
||||
# Mark current item list as active
|
||||
self.active = items
|
||||
# Show them
|
||||
@@ -251,12 +235,10 @@ class ItemView(Display):
|
||||
if len(items) > 1:
|
||||
# Re-sort stuff
|
||||
if self.marketBrowser.mode != 'recent':
|
||||
items.sort(key=self.itemSort)
|
||||
|
||||
items.sort(key=self.sMkt.itemSort)
|
||||
for i, item in enumerate(items[:9]):
|
||||
# set shortcut info for first 9 modules
|
||||
item.marketShortcut = i + 1
|
||||
|
||||
Display.refresh(self, items)
|
||||
|
||||
def columnBackground(self, colItem, item):
|
||||
|
||||
@@ -86,7 +86,7 @@ class PFSearchBox(wx.Window):
|
||||
|
||||
def OnKeyPress(self, event):
|
||||
if event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
||||
HandleCtrlBackspace(self.EditBox)
|
||||
HandleCtrlBackspace(self.EditBox)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ class FittingView(d.Display):
|
||||
self.hoveredRow = None
|
||||
self.hoveredColumn = None
|
||||
|
||||
self.Bind(wx.EVT_KEY_DOWN, self.kbEvent)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_MIDDLE_DOWN, self.click)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import config
|
||||
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 gui.characterEditor import APIView
|
||||
from service.character import Character
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.fit import Fit
|
||||
@@ -213,19 +213,7 @@ class ItemView(d.Display):
|
||||
def itemSort(self, item):
|
||||
sMkt = Market.getInstance()
|
||||
isFittable = item.group.name in sMkt.FIT_GROUPS or item.category.name in sMkt.FIT_CATEGORIES
|
||||
catname = sMkt.getCategoryByItem(item).name
|
||||
try:
|
||||
mktgrpid = sMkt.getMarketGroupByItem(item).ID
|
||||
except AttributeError:
|
||||
mktgrpid = -1
|
||||
pyfalog.warning("unable to find market group for {}".format(item.name))
|
||||
parentname = sMkt.getParentItemByItem(item).name
|
||||
# Get position of market group
|
||||
metagrpid = sMkt.getMetaGroupIdByItem(item)
|
||||
metatab = sMkt.META_MAP_REVERSE_INDICES.get(metagrpid)
|
||||
metalvl = item.metaLevel or 0
|
||||
|
||||
return not isFittable, catname, mktgrpid, parentname, metatab, metalvl, item.name
|
||||
return (not isFittable, *sMkt.itemSort(item))
|
||||
|
||||
def populateSearch(self, itemIDs):
|
||||
items = Market.getItems(itemIDs)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user