From 59a5b68152113a136e3d09962a641cb13db6ba96 Mon Sep 17 00:00:00 2001 From: burnsypet Date: Wed, 13 Jun 2018 22:52:36 +0100 Subject: [PATCH 01/13] Added reset and randomize buttons to mutaplasmid view --- gui/builtinItemStatsViews/itemMutator.py | 34 ++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py index 1803086a8..aa7c8dc06 100644 --- a/gui/builtinItemStatsViews/itemMutator.py +++ b/gui/builtinItemStatsViews/itemMutator.py @@ -9,6 +9,11 @@ from gui.contextMenu import ContextMenu from gui.bitmap_loader import BitmapLoader import gui.globalEvents as GE import gui.mainFrame +import random + +from logbook import Logger + +pyfalog = Logger(__name__) class ItemMutator(wx.Panel): @@ -91,8 +96,13 @@ class ItemMutator(wx.Panel): bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.saveBtn = wx.Button(self, wx.ID_ANY, "Save Attributes", wx.DefaultPosition, wx.DefaultSize, 0) - bSizer.Add(self.saveBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.refreshBtn = wx.Button(self, wx.ID_ANY, "Reset defaults", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.refreshBtn.Bind(wx.EVT_BUTTON, self.resetMutatedValues) + + self.randomBtn = wx.Button(self, wx.ID_ANY, "Random stats", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.randomBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.randomBtn.Bind(wx.EVT_BUTTON, self.randomMutatedValues) mainSizer.Add(bSizer, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 0) @@ -110,6 +120,26 @@ class ItemMutator(wx.Panel): self.timer = None self.timer = wx.CallLater(1000, self.callLater) + def resetMutatedValues(self, evt): + sFit = Fit.getInstance() + + for slider, m in self.event_mapping.items(): + value = m.baseValue + sFit.changeMutatedValue(m, value) + slider.SetValue(value) + + evt.Skip() + + def randomMutatedValues(self, evt): + sFit = Fit.getInstance() + + for slider, m in self.event_mapping.items(): + value = random.uniform(m.minValue, m.maxValue) + sFit.changeMutatedValue(m, value) + slider.SetValue(value) + + evt.Skip() + def callLater(self): self.timer = None print("recalc fit") From 154b07e0d69a82cc835ec19cb9d50f1bdc9dee84 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 13 Jun 2018 23:10:48 -0400 Subject: [PATCH 02/13] Move the attribute unit translations from the GUI to the Unit class and tweak the display of mutator ranges to use this. Additionally, develop inverse functions to the value translations for eventual use in the mutator slider --- eos/gamedata.py | 96 ++++++++++++++++++++- gui/builtinItemStatsViews/itemAttributes.py | 53 ++---------- gui/builtinItemStatsViews/itemMutator.py | 8 +- 3 files changed, 109 insertions(+), 48 deletions(-) diff --git a/eos/gamedata.py b/eos/gamedata.py index 717676915..f4ea61719 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -543,7 +543,101 @@ class MetaType(EqBase): class Unit(EqBase): - pass + + def __init__(self): + self.name = None + self.displayName = None + + @property + def translations(self): + """ This is a mapping of various tweaks that we have to do between the internal representation of an attribute + value and the display (for example, 'Millisecond' units have the display name of 's', so we have to convert value + from ms to s) """ + return { + "Inverse Absolute Percent": ( + lambda v: (1 - v) * 100, + lambda d: -1 * (d / 100) + 1, + lambda u: u), + "Inversed Modifier Percent": ( + lambda v: (1 - v) * 100, + lambda d: -1 * (d / 100) + 1, + lambda u: u), + "Modifier Percent": ( + lambda v: ("%+.2f" if ((v - 1) * 100) % 1 else "%+d") % ((v - 1) * 100), + lambda d: (d / 100) + 1, + lambda u: u), + "Volume": ( + lambda v: v, + lambda d: d, + lambda u: "m³"), + "Sizeclass": ( + lambda v: v, + lambda d: d, + lambda u: ""), + "Absolute Percent": ( + lambda v: (v * 100), + lambda d: d / 100, + lambda u: u), + "Milliseconds": ( + lambda v: v / 1000.0, + lambda d: d * 1000.0, + lambda u: u), + "Boolean": ( + lambda v, u: "Yes" if v == 1 else "No", + lambda d: 1.0 if d == "Yes" else 0.0, + lambda u: ""), + "typeID": ( + self.itemIDCallback, + None, # we could probably convert these back if we really tried hard enough + lambda u: ""), + "groupID": ( + self.groupIDCallback, + None, + lambda u: ""), + "attributeID": ( + self.attributeIDCallback, + None, + lambda u: ""), + } + + @staticmethod + def itemIDCallback(v): + v = int(v) + item = eos.db.getItem(int(v)) + return "%s (%d)" % (item.name, v) if item is not None else str(v) + + @staticmethod + def groupIDCallback(v): + v = int(v) + group = eos.db.getGroup(v) + return "%s (%d)" % (group.name, v) if group is not None else str(v) + + @staticmethod + def attributeIDCallback(v): + v = int(v) + if not v: # some attributes come through with a value of 0? See #1387 + return "%d" % (v) + attribute = eos.db.getAttributeInfo(v, eager=("unit")) + return "%s (%d)" % (attribute.name.capitalize(), v) + + def TranslateValue(self, value): + """Attributes have to be translated certain ways based on their unit (ex: decimals converting to percentages). + This allows us to get an easy representation of how the attribute should be printed """ + + override = self.translations.get(self.name) + if override is not None: + return override[0](value), override[2](self.displayName) + + return value, self.displayName + + def ComplicateValue(self, value): + """Takes the display value and turns it back into the internal representation of it""" + + override = self.translations.get(self.name) + if override is not None: + return override[1](value) + + return value class Traits(EqBase): diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index 5f413020d..1c9b05c4f 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -7,8 +7,6 @@ import wx from .helpers import AutoListCtrl from gui.bitmap_loader import BitmapLoader -from service.market import Market -from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount @@ -215,14 +213,14 @@ class ItemParams(wx.Panel): if self.toggleView != 1: valueUnit = str(value) elif info and info.unit: - valueUnit = self.TranslateValueUnit(value, info.unit.displayName, info.unit.name) + valueUnit = self.FormatValue(*info.unit.TranslateValue(value)) else: valueUnit = formatAmount(value, 3, 0, 0) if self.toggleView != 1: valueUnitDefault = str(valueDefault) elif info and info.unit: - valueUnitDefault = self.TranslateValueUnit(valueDefault, info.unit.displayName, info.unit.name) + valueUnitDefault = self.FormatValue(*info.unit.TranslateValue(valueDefault)) else: valueUnitDefault = formatAmount(valueDefault, 3, 0, 0) @@ -237,44 +235,11 @@ class ItemParams(wx.Panel): self.Layout() @staticmethod - def TranslateValueUnit(value, unitName, unitDisplayName): - def itemIDCallback(): - item = Market.getInstance().getItem(value) - return "%s (%d)" % (item.name, value) if item is not None else str(value) - - def groupIDCallback(): - group = Market.getInstance().getGroup(value) - return "%s (%d)" % (group.name, value) if group is not None else str(value) - - def attributeIDCallback(): - if not value: # some attributes come through with a value of 0? See #1387 - return "%d" % (value) - attribute = Attribute.getInstance().getAttributeInfo(value) - return "%s (%d)" % (attribute.name.capitalize(), value) - - trans = { - "Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName), - "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), - "Modifier Percent" : ( - lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), - "Volume" : (lambda: value, "m\u00B3"), - "Sizeclass" : (lambda: value, ""), - "Absolute Percent" : (lambda: (value * 100), unitName), - "Milliseconds" : (lambda: value / 1000.0, unitName), - "typeID" : (itemIDCallback, ""), - "groupID" : (groupIDCallback, ""), - "attributeID" : (attributeIDCallback, "") - } - - override = trans.get(unitDisplayName) - if override is not None: - v = override[0]() - if isinstance(v, str): - fvalue = v - elif isinstance(v, (int, float)): - fvalue = formatAmount(v, 3, 0, 0) - else: - fvalue = v - return "%s %s" % (fvalue, override[1]) + def FormatValue(value, unit): + """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) else: - return "%s %s" % (formatAmount(value, 3, 0), unitName) + fvalue = value + return "%s %s" % (fvalue, unit) diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py index 1803086a8..dcfcaf897 100644 --- a/gui/builtinItemStatsViews/itemMutator.py +++ b/gui/builtinItemStatsViews/itemMutator.py @@ -6,6 +6,7 @@ from .attributeSlider import AttributeSlider, EVT_VALUE_CHANGED import gui.mainFrame from gui.contextMenu import ContextMenu +from .itemAttributes import ItemParams from gui.bitmap_loader import BitmapLoader import gui.globalEvents as GE import gui.mainFrame @@ -69,14 +70,15 @@ class ItemMutator(wx.Panel): headingSizer.Add(displayName, 3, wx.ALL | wx.EXPAND, 0) - range_low = wx.StaticText(self, wx.ID_ANY, "{} {}".format(worse_range[0], m.attribute.unit.displayName)) + + 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) - range_high = wx.StaticText(self, wx.ID_ANY, "{} {}".format(better_range[0], m.attribute.unit.displayName)) + 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) headingSizer.Add(range_low, 0, wx.ALL | wx.EXPAND, 0) - headingSizer.Add(wx.StaticText(self, wx.ID_ANY, " ── "), 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 5) + 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) mainSizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5) From ed942945846dc24c7d0cb7ee06b8803901ef885d Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 15 Jun 2018 01:31:51 -0400 Subject: [PATCH 03/13] Finish implementing the translated values for the Mutators --- gui/builtinItemStatsViews/itemMutator.py | 14 +++++++++----- service/fit.py | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py index d2cf098d9..e5741c216 100644 --- a/gui/builtinItemStatsViews/itemMutator.py +++ b/gui/builtinItemStatsViews/itemMutator.py @@ -32,8 +32,10 @@ class ItemMutator(wx.Panel): self.event_mapping = {} for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName): - slider = AttributeSlider(self, m.baseValue, m.minMod, m.maxMod, not m.highIsGood) - slider.SetValue(m.value, False) + 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) @@ -114,6 +116,7 @@ class ItemMutator(wx.Panel): def changeMutatedValue(self, evt): m = self.event_mapping[evt.Object] value = evt.Value + value = m.attribute.unit.ComplicateValue(value) sFit = Fit.getInstance() sFit.changeMutatedValue(m, value) @@ -126,8 +129,8 @@ class ItemMutator(wx.Panel): sFit = Fit.getInstance() for slider, m in self.event_mapping.items(): - value = m.baseValue - sFit.changeMutatedValue(m, value) + value = sFit.changeMutatedValue(m, m.baseValue) + value = m.attribute.unit.TranslateValue(value)[0] slider.SetValue(value) evt.Skip() @@ -137,7 +140,8 @@ class ItemMutator(wx.Panel): for slider, m in self.event_mapping.items(): value = random.uniform(m.minValue, m.maxValue) - sFit.changeMutatedValue(m, value) + value = sFit.changeMutatedValue(m, value) + value = m.attribute.unit.TranslateValue(value)[0] slider.SetValue(value) evt.Skip() diff --git a/service/fit.py b/service/fit.py index 306887bfb..c356d98af 100644 --- a/service/fit.py +++ b/service/fit.py @@ -522,7 +522,7 @@ class Fit(object): mutator.value = value eos.db.commit() - #self.recalc(fit) + return mutator.value def appendModule(self, fitID, itemID): pyfalog.debug("Appending module for fit ({0}) using item: {1}", fitID, itemID) From 47016c682e1cc1eb96d1c3d455ca223f5add0ca7 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 15 Jun 2018 02:27:41 -0400 Subject: [PATCH 04/13] Don't destroy ItemStats window on close. Instead, Skip() the event so that it can finish processing events correctly. (#1650) --- gui/itemStats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/itemStats.py b/gui/itemStats.py index 1ad422c42..c01efdc76 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -149,7 +149,7 @@ class ItemStatsDialog(wx.Dialog): ItemStatsDialog.counter -= 1 self.parentWnd.UnregisterStatsWindow(self) - self.Destroy() + event.Skip() class ItemStatsContainer(wx.Panel): From 7e6fc4e6c2a89ae02644e0c0004f3aef828eb930 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 16 Jun 2018 15:58:30 -0400 Subject: [PATCH 05/13] Fix issue with closing item stats --- gui/builtinContextMenus/itemStats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 6221fa1e0..46b87226f 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -63,7 +63,7 @@ class ItemStats(ContextMenu): size = wx.DefaultSize pos = wx.DefaultPosition ItemStatsDialog(stuff, fullContext, pos, size, maximized) - lastWnd.closeEvent(None) + lastWnd.Close() else: ItemStatsDialog(stuff, fullContext) From 4e8e5d86c4082345f5efc0a50ec400f087930804 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 16 Jun 2018 17:04:29 -0400 Subject: [PATCH 06/13] Fix for #1647 --- gui/builtinItemStatsViews/attributeSlider.py | 2 +- gui/builtinItemStatsViews/itemMutator.py | 34 +++++++++++++------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/gui/builtinItemStatsViews/attributeSlider.py b/gui/builtinItemStatsViews/attributeSlider.py index f26be8492..49cb776fa 100644 --- a/gui/builtinItemStatsViews/attributeSlider.py +++ b/gui/builtinItemStatsViews/attributeSlider.py @@ -104,7 +104,7 @@ class AttributeSlider(wx.Panel): elif mod > 1: modEnd = self.UserMaxValue slider_percentage = ((mod-1)/(modEnd-1)) * 100 - print(slider_percentage) + # print(slider_percentage) if self.inverse: slider_percentage *= -1 self.slider.SetValue(slider_percentage) diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py index e5741c216..40326b607 100644 --- a/gui/builtinItemStatsViews/itemMutator.py +++ b/gui/builtinItemStatsViews/itemMutator.py @@ -58,14 +58,14 @@ 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) + # + # 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) font = parent.GetFont() font.SetWeight(wx.BOLD) @@ -148,9 +148,19 @@ class ItemMutator(wx.Panel): def callLater(self): self.timer = None - print("recalc fit") sFit = Fit.getInstance() - sFit.refreshFit(self.activeFit) - # todo BUG: if fit is not currently active, this causes the changed fit to show...? - wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitID=self.activeFit)) + + # recalc the fit that this module affects. This is not necessarily the currently active fit + sFit.refreshFit(self.activeFit) + + mainFrame = gui.mainFrame.MainFrame.getInstance() + activeFit = mainFrame.getActiveFit() + + if activeFit != self.activeFit: + # if we're no longer on the fit this module is affecting, simulate a "switch fit" so that the active fit + # can be recalculated (if needed) + sFit.switchFit(activeFit) + + # Send signal to GUI to update stats with current active fit + wx.PostEvent(mainFrame, GE.FitChanged(fitID=activeFit)) From 5c566bae730b178ef62984f97a204445b87dbbea Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 16 Jun 2018 21:50:03 -0400 Subject: [PATCH 07/13] Introduce special cases for cargo swapping when dealing with abyssal modules (#1649) --- eos/db/gamedata/queries.py | 4 ++++ eos/gamedata.py | 13 +++++++++++++ gui/builtinAdditionPanes/cargoView.py | 13 +++++++++++-- service/fit.py | 5 +++-- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index f1e9954c3..956ef054e 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -391,6 +391,10 @@ def directAttributeRequest(itemIDs, attrIDs): return result +def getAbyssalTypes(): + return set([r.resultingTypeID for r in gamedata_session.query(DynamicItem.resultingTypeID).distinct()]) + + def getRequiredFor(itemID, attrMapping): Attribute1 = aliased(Attribute) Attribute2 = aliased(Attribute) diff --git a/eos/gamedata.py b/eos/gamedata.py index f4ea61719..f337e3329 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -208,6 +208,8 @@ class Item(EqBase): MOVE_ATTR_INFO = None + ABYSSAL_TYPES = None + @classmethod def getMoveAttrInfo(cls): info = getattr(cls, "MOVE_ATTR_INFO", None) @@ -463,6 +465,17 @@ class Item(EqBase): return self.__price + @property + def isAbyssal(self): + if Item.ABYSSAL_TYPES is None: + Item.getAbyssalYypes() + + return self.ID in Item.ABYSSAL_TYPES + + @classmethod + def getAbyssalYypes(cls): + cls.ABYSSAL_TYPES = eos.db.getAbyssalTypes() + def __repr__(self): return "Item(ID={}, name={}) at {}".format( self.ID, self.name, hex(id(self)) diff --git a/gui/builtinAdditionPanes/cargoView.py b/gui/builtinAdditionPanes/cargoView.py index ed5687cfc..a5b94f31b 100644 --- a/gui/builtinAdditionPanes/cargoView.py +++ b/gui/builtinAdditionPanes/cargoView.py @@ -118,13 +118,22 @@ class CargoView(d.Display): # Gather module information to get position module = fit.modules[modIdx] + if module.item.isAbyssal: + dlg = wx.MessageDialog(self, + "Moving this Abyssal module to the cargo will convert it to the base module. Do you wish to proceed?", + "Confirm", wx.YES_NO | wx.ICON_QUESTION) + result = dlg.ShowModal() == wx.ID_YES + + if not result: + return + if dstRow != -1: # we're swapping with cargo if mstate.cmdDown: # if copying, append to cargo - sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) + sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID if not module.item.isAbyssal else module.baseItemID) else: # else, move / swap sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, dstRow) else: # dragging to blank spot, append - sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) + sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID if not module.item.isAbyssal else module.baseItemID) if not mstate.cmdDown: # if not copying, remove module sFit.removeModule(self.mainFrame.getActiveFit(), module.position) diff --git a/service/fit.py b/service/fit.py index c356d98af..fd9e8ea35 100644 --- a/service/fit.py +++ b/service/fit.py @@ -705,11 +705,12 @@ class Fit(object): cargo.amount -= 1 if not module.isEmpty: # if module is placeholder, we don't want to convert/add it - for x in fit.cargo.find(module.item): + moduleItem = module.item if not module.item.isAbyssal else module.baseItem + for x in fit.cargo.find(moduleItem ): x.amount += 1 break else: - moduleP = es_Cargo(module.item) + moduleP = es_Cargo(moduleItem ) moduleP.amount = 1 fit.cargo.insert(cargoIdx, moduleP) From 710739696a1f2d4757524d064cb7d2bb30c20334 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 16 Jun 2018 23:25:27 -0400 Subject: [PATCH 08/13] Incorporate mutaplasmids within module copy --- eos/saveddata/module.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 1efa44d7f..500ce0977 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -18,6 +18,7 @@ # =============================================================================== from logbook import Logger +from copy import deepcopy from sqlalchemy.orm import validates, reconstructor from math import floor @@ -110,6 +111,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__item = None self.__baseItem = None self.__charge = None + self.__mutaplasmid = None # we need this early if module is invalid and returns early self.__slot = self.dummySlot @@ -352,11 +354,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def baseItem(self): - return self.__baseItem if self.__baseItem != 0 else None # what? + return self.__baseItem @property def mutaplasmid(self): - return self.__mutaplasmid if self.__mutaplasmid != 0 else None + return self.__mutaplasmid @property def charge(self): @@ -838,9 +840,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if item is None: copy = Module.buildEmpty(self.slot) else: - copy = Module(self.item) + copy = Module(self.item, self.baseItem, self.mutaplasmid) copy.charge = self.charge copy.state = self.state + + for x in self.mutators.values(): + Mutator(copy, x.attribute, x.value) + return copy def __repr__(self): From d4dba99363ba9646353060ab3c797209d386e72a Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 17 Jun 2018 00:04:34 -0400 Subject: [PATCH 09/13] Properly handle projecting an abyssal module (we now copy modules instead of using the same item ID) --- gui/builtinAdditionPanes/projectedView.py | 2 +- service/fit.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gui/builtinAdditionPanes/projectedView.py b/gui/builtinAdditionPanes/projectedView.py index a5654d1a4..58642ee05 100644 --- a/gui/builtinAdditionPanes/projectedView.py +++ b/gui/builtinAdditionPanes/projectedView.py @@ -109,7 +109,7 @@ class ProjectedView(d.Display): dstRow, _ = self.HitTest((x, y)) # Gather module information to get position module = fit.modules[int(data[1])] - sFit.project(fit.ID, module.item.ID) + sFit.project(fit.ID, module) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID)) elif data[0] == "market": sFit = Fit.getInstance() diff --git a/service/fit.py b/service/fit.py index fd9e8ea35..e0d10e949 100644 --- a/service/fit.py +++ b/service/fit.py @@ -379,7 +379,10 @@ class Fit(object): thing = eos.db.getItem(thing, eager=("attributes", "group.category")) - if isinstance(thing, FitType): + if isinstance(thing, es_Module): + thing = copy.deepcopy(thing) + fit.projectedModules.append(thing) + elif isinstance(thing, FitType): if thing in fit.projectedFits: return From b7458396ac674486cc78d95f7bc689db246074fa Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 17 Jun 2018 01:48:51 -0400 Subject: [PATCH 10/13] Fix some rounding issues (#1651) --- eos/saveddata/mutator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eos/saveddata/mutator.py b/eos/saveddata/mutator.py index 84e3ddbc4..7b8affd07 100644 --- a/eos/saveddata/mutator.py +++ b/eos/saveddata/mutator.py @@ -81,7 +81,7 @@ class Mutator(EqBase): """ Validates values as properly falling within the range of the modules' Mutaplasmid """ mod = val/self.baseValue - if self.minMod < mod < self.maxMod: + if self.minMod <= mod <= self.maxMod: # sweet, all good returnVal = val else: @@ -105,11 +105,11 @@ class Mutator(EqBase): @property def minMod(self): - return self.dynamicAttribute.min + return round(self.dynamicAttribute.min, 3) @property def maxMod(self): - return self.dynamicAttribute.max + return round(self.dynamicAttribute.max, 3) @property def baseValue(self): From 8f5060cc3e5ffb0af25a8e5df0c29a61dfeb78f5 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 17 Jun 2018 12:10:33 -0400 Subject: [PATCH 11/13] Fix some issues capping at a certain range (#1651) --- eos/saveddata/mutator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eos/saveddata/mutator.py b/eos/saveddata/mutator.py index 7b8affd07..ff1473005 100644 --- a/eos/saveddata/mutator.py +++ b/eos/saveddata/mutator.py @@ -86,7 +86,10 @@ class Mutator(EqBase): returnVal = val else: # need to fudge the numbers a bit. Go with the value closest to base - returnVal = min(self.maxValue, max(self.minValue, val)) + if val >= 0: + returnVal = min(self.maxValue, max(self.minValue, val)) + else: + returnVal = max(self.maxValue, min(self.minValue, val)) return returnVal From 8bf9a9881fd01c5909a0ddd0b14cf74d90087242 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 17 Jun 2018 12:23:05 -0400 Subject: [PATCH 12/13] Add some invalidation logic to module for Abyysal modules without base or mutaplasmid --- eos/saveddata/module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 500ce0977..536be8614 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -208,7 +208,8 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return False return self.__item is None or \ (self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and - self.__item.group.name not in self.SYSTEM_GROUPS) + self.__item.group.name not in self.SYSTEM_GROUPS) or \ + (self.item.isAbyssal and (not self.baseItemID or not self.mutaplasmidID) ) @property def isMutated(self): From 15216453c934edaf32af487e317b56e0e95ac70f Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 17 Jun 2018 13:01:54 -0400 Subject: [PATCH 13/13] Update item attributes panel when mutation changes (#1644) --- gui/builtinItemStatsViews/itemAttributes.py | 3 ++- gui/builtinItemStatsViews/itemMutator.py | 5 +++++ gui/itemStats.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index 1c9b05c4f..278ceab20 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -84,7 +84,8 @@ class ItemParams(wx.Panel): def RefreshValues(self, event): self._fetchValues() self.UpdateList() - event.Skip() + if event: + event.Skip() def ToggleViewMode(self, event): self.toggleView *= -1 diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py index 40326b607..11f539261 100644 --- a/gui/builtinItemStatsViews/itemMutator.py +++ b/gui/builtinItemStatsViews/itemMutator.py @@ -123,6 +123,11 @@ class ItemMutator(wx.Panel): if self.timer: self.timer.Stop() self.timer = None + + for x in self.Parent.Children: + if isinstance(x, ItemParams): + x.RefreshValues(None) + break self.timer = wx.CallLater(1000, self.callLater) def resetMutatedValues(self, evt): diff --git a/gui/itemStats.py b/gui/itemStats.py index c01efdc76..b1ca79160 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -168,7 +168,7 @@ class ItemStatsContainer(wx.Panel): if isinstance(stuff, Module) and stuff.isMutated: self.mutator = ItemMutator(self.nbContainer, stuff, item) - self.nbContainer.AddPage(self.mutator, "Multiplasmid") + self.nbContainer.AddPage(self.mutator, "Mutations") self.desc = ItemDescription(self.nbContainer, stuff, item) self.nbContainer.AddPage(self.desc, "Description")