Merge branch 'master' into attrGroup

# Conflicts:
#	eos/gamedata.py
#	eve.db
#	gui/builtinItemStatsViews/itemAttributes.py
This commit is contained in:
blitzmann
2018-12-01 23:15:35 -05:00
188 changed files with 3787 additions and 362 deletions

View File

@@ -167,8 +167,6 @@ class AttributeGauge(wx.Window):
def SetValue(self, value, animate=True):
""" Sets the current position of the gauge. """
print("=" * 20, self._percentage)
if self._value == value:
return

View File

@@ -151,7 +151,7 @@ class CargoView(d.Display):
self.original = fit.cargo if fit is not None else None
self.cargo = stuff = fit.cargo if fit is not None else None
if stuff is not None:
stuff.sort(key=lambda cargo: cargo.itemID)
stuff.sort(key=lambda c: (c.item.group.category.name, c.item.group.name, c.item.name))
if event.fitID != self.lastFitId:
self.lastFitId = event.fitID

View File

@@ -23,6 +23,7 @@ import gui.display as d
from gui.builtinMarketBrowser.events import ITEM_SELECTED
import gui.mainFrame
from gui.builtinViewColumns.state import State
from gui.utils.staticHelpers import DragDropHelper
from gui.contextMenu import ContextMenu
import gui.globalEvents as GE
from eos.saveddata.fit import ImplantLocation
@@ -31,6 +32,22 @@ from service.market import Market
import gui.fitCommands as cmd
class ImplantViewDrop(wx.DropTarget):
def __init__(self, dropFn, *args, **kwargs):
super(ImplantViewDrop, self).__init__(*args, **kwargs)
self.dropFn = dropFn
# this is really transferring an EVE itemID
self.dropData = wx.TextDataObject()
self.SetDataObject(self.dropData)
def OnData(self, x, y, t):
if self.GetData():
dragged_data = DragDropHelper.data
data = dragged_data.split(':')
self.dropFn(x, y, data)
return t
class ImplantView(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL)
@@ -77,10 +94,7 @@ class ImplantView(wx.Panel):
def OnRadioSelect(self, event):
fitID = self.mainFrame.getActiveFit()
if fitID is not None:
sFit = Fit.getInstance()
sFit.toggleImplantSource(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
self.mainFrame.command.Submit(cmd.GuiChangeImplantLocation(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER))
class ImplantDisplay(d.Display):
@@ -102,12 +116,27 @@ class ImplantDisplay(d.Display):
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LEFT_DOWN, self.click)
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
self.SetDropTarget(ImplantViewDrop(self.handleListDrag))
if "__WXGTK__" in wx.PlatformInfo:
self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu)
else:
self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu)
def handleListDrag(self, x, y, data):
"""
Handles dragging of items from various pyfa displays which support it
data is list with two indices:
data[0] is hard-coded str of originating source
data[1] is typeID or index of data we want to manipulate
"""
if data[0] == "market":
if self.mainFrame.command.Submit(cmd.GuiAddImplantCommand(self.mainFrame.getActiveFit(), int(data[1]))):
self.mainFrame.additionsPane.select("Implants")
def kbEvent(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:

View File

@@ -30,8 +30,9 @@ class ModuleGlobalAmmoPicker(ModuleAmmoPicker):
fit = db_getFit(fitID)
selectedModule = self.modules[0]
source = fit.modules if not selectedModule.isProjected else fit.projectedModules
allModules = []
for mod in fit.modules:
for mod in source:
if mod.itemID is None:
continue
if mod.itemID == selectedModule.itemID:

View File

@@ -15,7 +15,7 @@ class WhProjector(ContextMenu):
# CCP doesn't currently provide a mapping between the general Environment, and the specific environment effect
# (which can be random when going into Abyssal space). This is how we currently define it:
# environment type: specific type name previx
# environment type: specific type name prefix
abyssal_mapping = {
'caustic_toxin_weather': 47862, # Exotic Particle Storm
'darkness_weather': 47863, # Dark Matter Field
@@ -50,13 +50,13 @@ class WhProjector(ContextMenu):
wormhole_item.SetSubMenu(wormhole_menu)
sub.Append(wormhole_item)
effdata = self.getEffectBeacons()
self.buildMenu(effdata, wormhole_menu, rootMenu, msw)
grouped_data, flat_data = self.getEffectBeacons()
self.buildMenu(grouped_data, flat_data, wormhole_menu, rootMenu, msw)
# Incursions
effdata = self.getEffectBeacons(incursions=True)
self.buildMenu(effdata, sub, rootMenu, msw)
grouped_data, flat_data = self.getEffectBeacons(incursions=True)
self.buildMenu(grouped_data, flat_data, sub, rootMenu, msw)
# Abyssal Weather
@@ -65,8 +65,8 @@ class WhProjector(ContextMenu):
abyssal_item.SetSubMenu(abyssal_menu)
sub.Append(abyssal_item)
effdata = self.getAbyssalWeather()
self.buildMenu(effdata, abyssal_menu, rootMenu, msw)
grouped_data, flat_data = self.getAbyssalWeather()
self.buildMenu(grouped_data, flat_data, abyssal_menu, rootMenu, msw)
# Localized Weather
@@ -75,8 +75,8 @@ class WhProjector(ContextMenu):
local_item.SetSubMenu(local_menu)
sub.Append(local_item)
effdata = self.getLocalizedEnvironments()
self.buildMenu(effdata, local_menu, rootMenu, msw)
grouped_data, flat_data = self.getLocalizedEnvironments()
self.buildMenu(grouped_data, flat_data, local_menu, rootMenu, msw)
return sub
@@ -91,33 +91,38 @@ class WhProjector(ContextMenu):
fitID = self.mainFrame.getActiveFit()
self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(fitID, swObj.ID, 'item'))
def buildMenu(self, data, local_menu, rootMenu, msw):
for swType in sorted(data):
def buildMenu(self, grouped_data, flat_data, local_menu, rootMenu, msw):
def processFlat(data, root, sub):
for swData in sorted(data, key=lambda tpl: tpl[2]):
wxid = ContextMenu.nextID()
swObj, swName, swClass = swData
self.idmap[wxid] = (swObj, swName)
subItem = wx.MenuItem(sub, wxid, swClass)
if msw:
root.Bind(wx.EVT_MENU, self.handleSelection, subItem)
else:
sub.Bind(wx.EVT_MENU, self.handleSelection, subItem)
sub.Append(subItem)
for swType in sorted(grouped_data):
subItem = wx.MenuItem(local_menu, wx.ID_ANY, swType)
grandSub = wx.Menu()
subItem.SetSubMenu(grandSub)
local_menu.Append(subItem)
processFlat(grouped_data[swType], rootMenu, grandSub)
for swData in sorted(data[swType], key=lambda tpl: tpl[2]):
wxid = ContextMenu.nextID()
swObj, swName, swClass = swData
self.idmap[wxid] = (swObj, swName)
grandSubItem = wx.MenuItem(grandSub, wxid, swClass)
if msw:
rootMenu.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
else:
grandSub.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
grandSub.Append(grandSubItem)
processFlat(flat_data, rootMenu, local_menu)
def getEffectBeacons(self, incursions=False):
"""
Get dictionary with system-wide effects
Get dictionary with wormhole system-wide effects
"""
sMkt = Market.getInstance()
# todo: rework this
# Container for system-wide effects
effects = {}
grouped = {}
# Expressions for matching when detecting effects we're looking for
if incursions:
@@ -158,13 +163,13 @@ class WhProjector(ContextMenu):
groupname = re.sub(garbage, "", groupname)
groupname = re.sub(" {2,}", " ", groupname).strip()
# Add stuff to dictionary
if groupname not in effects:
effects[groupname] = set()
effects[groupname].add((beacon, beaconname, shortname))
if groupname not in grouped:
grouped[groupname] = set()
grouped[groupname].add((beacon, beaconname, shortname))
# Break loop on 1st result
break
return effects
return grouped, ()
def getAbyssalWeather(self):
sMkt = Market.getInstance()
@@ -172,7 +177,8 @@ class WhProjector(ContextMenu):
environments = {x.ID: x for x in sMkt.getGroup("Abyssal Environment").items}
items = chain(sMkt.getGroup("MassiveEnvironments").items, sMkt.getGroup("Non-Interactable Object").items)
effects = {}
grouped = {}
flat = set()
for beacon in items:
if not beacon.isType('projected'):
@@ -183,20 +189,23 @@ class WhProjector(ContextMenu):
if type is None:
continue
if type.name not in effects:
effects[type.name] = set()
if type.name not in grouped:
grouped[type.name] = set()
display_name = "{} {}".format(type.name, beacon.name[-1:])
effects[type.name].add((beacon, display_name, display_name))
grouped[type.name].add((beacon, display_name, display_name))
return effects
# PVP weather
flat.add((sMkt.getItem(49766), 'PvP Weather', 'PvP Weather'))
return grouped, flat
def getLocalizedEnvironments(self):
sMkt = Market.getInstance()
grp = sMkt.getGroup("Abyssal Hazards")
effects = dict()
grouped = dict()
for beacon in grp.items:
if not beacon.isType('projected'):
@@ -206,12 +215,12 @@ class WhProjector(ContextMenu):
name_parts = beacon.name.split(" ")
key = name_parts[1].strip()
if key not in effects:
effects[key] = set()
if key not in grouped:
grouped[key] = set()
effects[key].add((beacon, beacon.name, beacon.name))
grouped[key].add((beacon, beacon.name, beacon.name))
return effects
return grouped, ()
WhProjector.register()

View File

@@ -46,17 +46,17 @@ class AttributeSlider(wx.Panel):
# Slider which abstracts users values from internal values (because the built in slider does not deal with floats
# and the like), based on http://wxpython-users.wxwidgets.narkive.com/ekgBzA7u/anyone-ever-thought-of-a-floating-point-slider
def __init__(self, parent, baseValue, minMod, maxMod, inverse=False, id=-1):
def __init__(self, parent, baseValue, minValue, maxValue, inverse=False, id=-1):
wx.Panel.__init__(self, parent, id=id)
self.parent = parent
self.inverse = inverse
self.base_value = baseValue
self.UserMinValue = minMod
self.UserMaxValue = maxMod
self.UserMinValue = minValue
self.UserMaxValue = maxValue
self.inverse = inverse
# The internal slider basically represents the percentage towards the end of the range. It has to be normalized
# in this way, otherwise when we start off with a base, if the range is skewed to one side, the base value won't
@@ -65,12 +65,12 @@ class AttributeSlider(wx.Panel):
# Additionally, since we want the slider to be accurate to 3 decimal places, we need to blow out the two ends here
# (if we have a slider that needs to land on 66.66% towards the right, it will actually be converted to 66%. Se we need it to support 6,666)
#
# self.SliderMinValue = -100
# self.SliderMaxValue = 100
# self.SliderValue = 0
self.SliderMinValue = -100
self.SliderMaxValue = 100
self.SliderValue = 0
range = [(self.UserMinValue * self.base_value), (self.UserMaxValue * self.base_value)]
range = [self.UserMinValue, self.UserMaxValue]
self.ctrl = wx.SpinCtrlDouble(self, min=min(range), max=max(range))
self.ctrl.SetDigits(3)
@@ -92,19 +92,12 @@ class AttributeSlider(wx.Panel):
evt.Skip()
def SetValue(self, value, post_event=True):
# todo: check this against values that might be 2.5x and whatnot
mod = value / self.base_value
self.ctrl.SetValue(value)
slider_percentage = 0
if mod < 1:
modEnd = self.UserMinValue
slider_percentage = (1 - mod) / (1 - modEnd) * -100
elif mod > 1:
modEnd = self.UserMaxValue
slider_percentage = ((mod - 1) / (modEnd - 1)) * 100
# print(slider_percentage)
if self.inverse:
slider_percentage *= -1
invert_factor = -1 if self.inverse else 1
if value >= self.base_value:
slider_percentage = (value - self.base_value) / (self.UserMaxValue - self.base_value) * 100 * invert_factor
else:
slider_percentage = (value - self.base_value) / (self.base_value - self.UserMinValue) * 100 * invert_factor
self.slider.SetValue(slider_percentage)
if post_event:
wx.PostEvent(self, ValueChanged(self, None, value, None, slider_percentage))

View File

@@ -7,7 +7,7 @@ import wx.lib.agw.hypertreelist
from gui.builtinItemStatsViews.helpers import AutoListCtrl
from gui.bitmap_loader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
from gui.utils.numberFormatter import formatAmount, roundDec
from enum import IntEnum
from gui.builtinItemStatsViews.attributeGrouping import *
@@ -294,14 +294,14 @@ class ItemParams(wx.Panel):
if self.toggleView != 1:
valueUnit = str(value)
elif info and info.unit:
valueUnit = self.FormatValue(*info.unit.TranslateValue(value))
valueUnit = self.FormatValue(*info.unit.PreformatValue(value))
else:
valueUnit = formatAmount(value, 3, 0, 0)
if self.toggleView != 1:
valueUnitDefault = str(valueDefault)
elif info and info.unit:
valueUnitDefault = self.FormatValue(*info.unit.TranslateValue(valueDefault))
valueUnitDefault = self.FormatValue(*info.unit.PreformatValue(valueDefault))
else:
valueUnitDefault = formatAmount(valueDefault, 3, 0, 0)
@@ -314,11 +314,13 @@ class ItemParams(wx.Panel):
# self.paramList.SetItemImage(index, attrIcon, which=wx.TreeItemIcon_Normal)
@staticmethod
def FormatValue(value, unit):
def FormatValue(value, unit, rounding='prec', digits=3):
"""Formats a value / unit combination into a string
@todo: move this to a more central location, since this is also used in the item mutator panel"""
if isinstance(value, (int, float)):
fvalue = formatAmount(value, 3, 0, 0)
if isinstance(value, (int, float)) and rounding == 'prec':
fvalue = formatAmount(value, digits, 0, 0)
elif isinstance(value, (int, float)) and rounding == 'dec':
fvalue = roundDec(value, digits)
else:
fvalue = value
return "%s %s" % (fvalue, unit)

View File

@@ -30,17 +30,9 @@ class ItemMutator(wx.Panel):
self.event_mapping = {}
for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName):
baseValueFormated = m.attribute.unit.TranslateValue(m.baseValue)[0]
valueFormated = m.attribute.unit.TranslateValue(m.value)[0]
slider = AttributeSlider(self, baseValueFormated, m.minMod, m.maxMod, not m.highIsGood)
slider.SetValue(valueFormated, False)
slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
self.event_mapping[slider] = m
headingSizer = wx.BoxSizer(wx.HORIZONTAL)
# create array for the two ranges
min_t = [round(m.minValue, 3), m.minMod, None]
max_t = [round(m.maxValue, 3), m.maxMod, None]
min_t = [m.minValue, m.minMod, None]
max_t = [m.maxValue, m.maxMod, None]
# Then we need to determine if it's better than original, which will be the color
min_t[2] = min_t[1] < 1 if not m.highIsGood else 1 < min_t[1]
@@ -56,14 +48,8 @@ class ItemMutator(wx.Panel):
worse_range = max_t
else:
worse_range = min_t
#
# print("{}: \nHigh is good: {}".format(m.attribute.displayName, m.attribute.highIsGood))
# print("Value {}".format(m.baseValue))
#
# print(min_t)
# print(max_t)
# print(better_range)
# print(worse_range)
headingSizer = wx.BoxSizer(wx.HORIZONTAL)
font = parent.GetFont()
font.SetWeight(wx.BOLD)
@@ -75,19 +61,30 @@ class ItemMutator(wx.Panel):
headingSizer.Add(displayName, 3, wx.ALL | wx.EXPAND, 0)
range_low = wx.StaticText(self, wx.ID_ANY, ItemParams.FormatValue(*m.attribute.unit.TranslateValue(worse_range[0])))
range_low.SetForegroundColour(self.goodColor if worse_range[2] else self.badColor)
worst_val = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worse_range[0]), rounding='dec')
worst_text = wx.StaticText(self, wx.ID_ANY, worst_val)
worst_text.SetForegroundColour(self.goodColor if worse_range[2] else self.badColor)
range_high = wx.StaticText(self, wx.ID_ANY, ItemParams.FormatValue(*m.attribute.unit.TranslateValue(better_range[0])))
range_high.SetForegroundColour(self.goodColor if better_range[2] else self.badColor)
best_val = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(better_range[0]), rounding='dec')
best_text = wx.StaticText(self, wx.ID_ANY, best_val)
best_text.SetForegroundColour(self.goodColor if better_range[2] else self.badColor)
headingSizer.Add(range_low, 0, wx.ALL | wx.EXPAND, 0)
headingSizer.Add(worst_text, 0, wx.ALL | wx.EXPAND, 0)
headingSizer.Add(wx.StaticText(self, wx.ID_ANY, ""), 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 5)
headingSizer.Add(range_high, 0, wx.RIGHT | wx.EXPAND, 10)
headingSizer.Add(best_text, 0, wx.RIGHT | wx.EXPAND, 10)
mainSizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
slider = AttributeSlider(parent=self,
baseValue=m.attribute.unit.SimplifyValue(m.baseValue),
minValue=m.attribute.unit.SimplifyValue(min_t[0]),
maxValue=m.attribute.unit.SimplifyValue(max_t[0]),
inverse=better_range is min_t)
slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False)
slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
self.event_mapping[slider] = m
mainSizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
mainSizer.AddStretchSpacer()
@@ -132,7 +129,7 @@ class ItemMutator(wx.Panel):
for slider, m in self.event_mapping.items():
value = sFit.changeMutatedValue(m, m.baseValue)
value = m.attribute.unit.TranslateValue(value)[0]
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
evt.Skip()
@@ -143,7 +140,7 @@ class ItemMutator(wx.Panel):
for slider, m in self.event_mapping.items():
value = random.uniform(m.minValue, m.maxValue)
value = sFit.changeMutatedValue(m, value)
value = m.attribute.unit.TranslateValue(value)[0]
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
evt.Skip()

View File

@@ -736,6 +736,7 @@ class _TabsContainer(wx.Panel):
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged)
@@ -776,6 +777,29 @@ class _TabsContainer(wx.Panel):
self.dragged_tab = tab
def OnMiddleUp(self, event):
mposx, mposy = event.GetPosition()
tab = self.FindTabAtPos(mposx, mposy)
if tab is None or not tab.closeable: # if not able to close, return False
return False
index = self.tabs.index(tab)
ev = PageClosing(index)
wx.PostEvent(self.Parent, ev)
if ev.isVetoed():
return False
index = self.GetTabIndex(tab)
self.Parent.DeletePage(index)
wx.PostEvent(self.Parent, PageClosed(index=index))
sel = self.GetSelected()
if sel is not None:
wx.PostEvent(self.Parent, PageChanged(-1, sel))
def OnMotion(self, event):
"""
Determines what happens when the mouse moves. This handles primarily

View File

@@ -131,7 +131,7 @@ class EveFittings(wx.Frame):
return
data = self.fitTree.fittingsTreeCtrl.GetItemData(selection)
sPort = Port.getInstance()
fits = sPort.importFitFromBuffer(data)
import_type, fits = sPort.importFitFromBuffer(data)
self.mainFrame._openAfterImport(fits)
def deleteFitting(self, event):

View File

@@ -31,4 +31,6 @@ from .guiChangeProjectedFitQty import GuiChangeProjectedFitQty
from .guiChangeDroneQty import GuiChangeDroneQty
from .guiChangeProjectedDroneQty import GuiChangeProjectedDroneQty
from .guiToggleDrone import GuiToggleDroneCommand
from .guiFitRename import GuiFitRenameCommand
from .guiFitRename import GuiFitRenameCommand
from .guiChangeImplantLocation import GuiChangeImplantLocation
from .guiImportMutatedModule import GuiImportMutatedModuleCommand

View File

@@ -28,11 +28,12 @@ class FitAddProjectedEnvCommand(wx.Command):
# todo: thing to check for existing environmental effects
self.old_item = fit.projectedModules.makeRoom(module)
module.state = State.ONLINE
fit.projectedModules.append(module)
if module.isExclusiveSystemEffect:
# if this is an exclusive system effect, we need to cache the old one. We make room for the new one here, which returns the old one
self.old_item = fit.projectedModules.makeRoom(module)
fit.projectedModules.append(module)
eos.db.commit()
self.new_index = fit.projectedModules.index(module)
return True

View File

@@ -0,0 +1,25 @@
import wx
import eos.db
from logbook import Logger
pyfalog = Logger(__name__)
class FitChangeImplantLocation(wx.Command):
def __init__(self, fitID, source):
wx.Command.__init__(self, True, "Drone add")
self.fitID = fitID
self.source = source
self.old_source = None
def Do(self):
pyfalog.debug("Toggling implant source for fit ID: {0}", self.fitID)
fit = eos.db.getFit(self.fitID)
self.old_source = fit.implantSource
fit.implantSource = self.source
eos.db.commit()
return True
def Undo(self):
cmd = FitChangeImplantLocation(self.fitID, self.old_source)
return cmd.Do()

View File

@@ -0,0 +1,93 @@
import wx
from eos.saveddata.module import Module, State
import eos.db
from eos.db.gamedata.queries import getDynamicItem
from logbook import Logger
from service.fit import Fit
pyfalog = Logger(__name__)
class FitImportMutatedCommand(wx.Command):
""""
Fitting command that takes info about mutated module, composes it and adds it to a fit
"""
def __init__(self, fitID, baseItem, mutaItem, attrMap):
wx.Command.__init__(self, True)
self.fitID = fitID
self.baseItem = baseItem
self.mutaItem = mutaItem
self.attrMap = attrMap
self.new_position = None
self.change = None
self.replace_cmd = None
def Do(self):
sFit = Fit.getInstance()
fitID = self.fitID
fit = eos.db.getFit(fitID)
if self.baseItem is None:
pyfalog.warning("Unable to build non-mutated module: no base item to build from")
return False
try:
mutaTypeID = self.mutaItem.ID
except AttributeError:
mutaplasmid = None
else:
mutaplasmid = getDynamicItem(mutaTypeID)
# Try to build simple item even though no mutaplasmid found
if mutaplasmid is None:
try:
module = Module(self.baseItem)
except ValueError:
pyfalog.warning("Unable to build non-mutated module: {}", self.baseItem)
return False
# Build mutated module otherwise
else:
try:
module = Module(mutaplasmid.resultingItem, self.baseItem, mutaplasmid)
except ValueError:
pyfalog.warning("Unable to build mutated module: {} {}", self.baseItem, self.mutaItem)
return False
else:
for attrID, mutator in module.mutators.items():
if attrID in self.attrMap:
mutator.value = self.attrMap[attrID]
# this is essentially the same as the FitAddModule command. possibly look into centralizing this functionality somewhere?
if module.fits(fit):
pyfalog.debug("Adding {} as module for fit {}", module, fit)
module.owner = fit
numSlots = len(fit.modules)
fit.modules.append(module)
if module.isValidState(State.ACTIVE):
module.state = State.ACTIVE
# todo: fix these
# As some items may affect state-limiting attributes of the ship, calculate new attributes first
# self.recalc(fit)
# Then, check states of all modules and change where needed. This will recalc if needed
sFit.checkStates(fit, module)
# fit.fill()
eos.db.commit()
self.change = numSlots != len(fit.modules)
self.new_position = module.modPosition
else:
return False
return True
def Undo(self):
# We added a subsystem module, which actually ran the replace command. Run the undo for that guy instead
if self.replace_cmd:
return self.replace_cmd.Undo()
from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import
if self.new_position is not None:
cmd = FitRemoveModuleCommand(self.fitID, [self.new_position])
cmd.Do()
return True

View File

@@ -23,6 +23,13 @@ class FitReplaceModuleCommand(wx.Command):
self.old_module = None
def Do(self):
fit = eos.db.getFit(self.fitID)
mod = fit.modules[self.position]
if not mod.isEmpty:
self.old_module = ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID,
mod.mutaplasmidID)
return self.change_module(self.fitID, self.position, self.itemID)
def Undo(self):
@@ -30,7 +37,6 @@ class FitReplaceModuleCommand(wx.Command):
fit = eos.db.getFit(self.fitID)
fit.modules.toDummy(self.position)
return True
self.change_module(self.fitID, self.position, self.old_module.itemID)
self.module.state = self.old_module.state
self.module.charge = self.old_module.charge
@@ -52,10 +58,7 @@ class FitReplaceModuleCommand(wx.Command):
pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", self.position, fitID)
item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
mod = fit.modules[self.position]
if not mod.isEmpty:
self.old_module = ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID)
try:
self.module = Module(item)
@@ -75,6 +78,9 @@ class FitReplaceModuleCommand(wx.Command):
if self.module.isValidState(State.ACTIVE):
self.module.state = State.ACTIVE
if self.old_module.charge and self.module.isValidCharge(self.old_module.charge):
self.module.charge = self.old_module.charge
# Then, check states of all modules and change where needed. This will recalc if needed
# self.checkStates(fit, m)

View File

@@ -9,7 +9,7 @@ pyfalog = Logger(__name__)
class FitSetChargeCommand(wx.Command):
def __init__(self, fitID, positions, chargeID=None):
def __init__(self, fitID, positions, chargeID=None, projected=False):
# todo: determine if this command really should be used with a group of modules, or a simple per module basis
wx.Command.__init__(self, True, "Module Charge Add")
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
@@ -17,6 +17,7 @@ class FitSetChargeCommand(wx.Command):
self.fitID = fitID
self.chargeID = chargeID
self.positions = positions
self.projected = projected
self.cache = None
def Do(self):
@@ -29,7 +30,8 @@ class FitSetChargeCommand(wx.Command):
def __setAmmo(self, positions, chargeID):
fit = eos.db.getFit(self.fitID)
self.cache = {fit.modules[i].modPosition: fit.modules[i].chargeID for i in positions}
source = fit.modules if not self.projected else fit.projectedModules
self.cache = {source[i].modPosition: source[i].chargeID for i in positions}
ammo = eos.db.getItem(chargeID) if chargeID else None
if ammo is not None and not ammo.isCharge:
@@ -37,7 +39,7 @@ class FitSetChargeCommand(wx.Command):
result = False
for pos in positions:
mod = fit.modules[pos]
mod = source[pos]
if not mod.isEmpty and mod.isValidCharge(ammo):
pyfalog.debug("Set ammo {} for {} on fit {}", ammo, mod, self.fitID)
result = True

View File

@@ -15,9 +15,10 @@ class GuiModuleAddChargeCommand(wx.Command):
self.fitID = fitID
self.itemID = itemID
self.positions = [mod.modPosition for mod in modules]
self.projected = modules[0].isProjected
def Do(self):
if self.internal_history.Submit(FitSetChargeCommand(self.fitID, self.positions, self.itemID)):
if self.internal_history.Submit(FitSetChargeCommand(self.fitID, self.positions, self.itemID, self.projected)):
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
return True

View File

@@ -3,7 +3,9 @@ from service.fit import Fit
import gui.mainFrame
from gui import globalEvents as GE
from eos.saveddata.fit import ImplantLocation
from .calc.fitAddImplant import FitAddImplantCommand
from .calc.fitChangeImplantLocation import FitChangeImplantLocation
class GuiAddImplantCommand(wx.Command):
@@ -16,7 +18,7 @@ class GuiAddImplantCommand(wx.Command):
self.itemID = itemID
def Do(self):
if self.internal_history.Submit(FitAddImplantCommand(self.fitID, self.itemID)):
if self.internal_history.Submit(FitAddImplantCommand(self.fitID, self.itemID)) and self.internal_history.Submit(FitChangeImplantLocation(self.fitID, ImplantLocation.FIT)):
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
return True

View File

@@ -0,0 +1,30 @@
import wx
from service.fit import Fit
import gui.mainFrame
from gui import globalEvents as GE
from .calc.fitChangeImplantLocation import FitChangeImplantLocation
class GuiChangeImplantLocation(wx.Command):
def __init__(self, fitID, source):
wx.Command.__init__(self, True, "Implant Source Change")
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.sFit = Fit.getInstance()
self.internal_history = wx.CommandProcessor()
self.fitID = fitID
self.source = source
def Do(self):
if self.internal_history.Submit(FitChangeImplantLocation(self.fitID, self.source)):
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
return True
return False
def Undo(self):
for _ in self.internal_history.Commands:
self.internal_history.Undo()
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
return True

View File

@@ -0,0 +1,38 @@
import wx
import eos.db
import gui.mainFrame
from gui import globalEvents as GE
from .calc.fitImportMutatedModule import FitImportMutatedCommand
from service.fit import Fit
from logbook import Logger
pyfalog = Logger(__name__)
class GuiImportMutatedModuleCommand(wx.Command):
def __init__(self, fitID, baseItem, mutaItem, attrMap):
wx.Command.__init__(self, True, "Mutated Module Import: {} {} {}".format(baseItem, mutaItem, attrMap))
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.sFit = Fit.getInstance()
self.fitID = fitID
self.baseItem = baseItem
self.mutaItem = mutaItem
self.attrMap = attrMap
self.internal_history = wx.CommandProcessor()
def Do(self):
pyfalog.debug("{} Do()".format(self))
if self.internal_history.Submit(FitImportMutatedCommand(self.fitID, self.baseItem, self.mutaItem, self.attrMap)):
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd"))
return True
return False
def Undo(self):
pyfalog.debug("{} Undo()".format(self))
for _ in self.internal_history.Commands:
self.internal_history.Undo()
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel"))
return True

View File

@@ -17,6 +17,7 @@ class GuiRemoveImplantCommand(wx.Command):
def Do(self):
if self.internal_history.Submit(FitRemoveImplantCommand(self.fitID, self.position)):
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
return True
return False
@@ -24,5 +25,6 @@ class GuiRemoveImplantCommand(wx.Command):
def Undo(self):
for _ in self.internal_history.Commands:
self.internal_history.Undo()
self.sFit.recalc(self.fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
return True

View File

@@ -73,6 +73,7 @@ from service.fit import Fit
from service.port import EfsPort, IPortUser, Port
from service.settings import HTMLExportSettings, SettingsProvider
from service.update import Update
import gui.fitCommands as cmd
disableOverrideEditor = False
@@ -728,12 +729,18 @@ class MainFrame(wx.Frame):
def importFromClipboard(self, event):
clipboard = fromClipboard()
activeFit = self.getActiveFit()
try:
fits = Port().importFitFromBuffer(clipboard, self.getActiveFit())
importType, importData = Port().importFitFromBuffer(clipboard, activeFit)
# If it's mutated item - make sure there's at least base item specified
if importType == "MutatedItem":
# we've imported an Abyssal module, need to fire off the command to add it to the fit
self.command.Submit(cmd.GuiImportMutatedModuleCommand(activeFit, *importData[0]))
return # no need to do anything else
except:
pyfalog.error("Attempt to import failed:\n{0}", clipboard)
else:
self._openAfterImport(fits)
self._openAfterImport(importData)
def exportToClipboard(self, event):
CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft,

View File

@@ -110,3 +110,9 @@ def roundToPrec(val, prec):
if int(val) == val:
val = int(val)
return val
def roundDec(val, prec):
if int(val) == val:
return int(val)
return round(val, prec)