diff --git a/eos/db/__init__.py b/eos/db/__init__.py
index a471f83b7..3972c8f8f 100644
--- a/eos/db/__init__.py
+++ b/eos/db/__init__.py
@@ -118,7 +118,7 @@ from eos.db.gamedata import alphaClones, attribute, category, effect, group, ite
pyfalog.debug('Importing saveddata DB scheme')
# noinspection PyPep8
from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, \
- miscData, mutator, module, override, price, queries, skill, targetProfile, user
+ miscData, mutatorMod, mutatorDrone, module, override, price, queries, skill, targetProfile, user
pyfalog.debug('Importing gamedata queries')
# noinspection PyPep8
diff --git a/eos/db/gamedata/item.py b/eos/db/gamedata/item.py
index e11b072e2..fd6b11789 100644
--- a/eos/db/gamedata/item.py
+++ b/eos/db/gamedata/item.py
@@ -17,7 +17,7 @@
# along with eos. If not, see .
# ===============================================================================
-from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, Table
+from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Table
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import backref, deferred, mapper, relation, synonym
from sqlalchemy.orm.collections import attribute_mapped_collection
diff --git a/eos/db/migrations/upgrade45.py b/eos/db/migrations/upgrade45.py
new file mode 100644
index 000000000..ee8a6dc22
--- /dev/null
+++ b/eos/db/migrations/upgrade45.py
@@ -0,0 +1,18 @@
+"""
+Migration 45
+
+- Drone mutaplasmid support
+"""
+
+import sqlalchemy
+
+
+def upgrade(saveddata_engine):
+ try:
+ saveddata_engine.execute("SELECT baseItemID FROM drones LIMIT 1")
+ except sqlalchemy.exc.DatabaseError:
+ saveddata_engine.execute("ALTER TABLE drones ADD COLUMN baseItemID INTEGER;")
+ try:
+ saveddata_engine.execute("SELECT mutaplasmidID FROM drones LIMIT 1")
+ except sqlalchemy.exc.DatabaseError:
+ saveddata_engine.execute("ALTER TABLE drones ADD COLUMN mutaplasmidID INTEGER;")
diff --git a/eos/db/saveddata/__init__.py b/eos/db/saveddata/__init__.py
index 4de25d3cf..804188652 100644
--- a/eos/db/saveddata/__init__.py
+++ b/eos/db/saveddata/__init__.py
@@ -1,7 +1,8 @@
__all__ = [
"character",
"fit",
- "mutator",
+ "mutatorMod",
+ "mutatorDrone",
"module",
"user",
"skill",
diff --git a/eos/db/saveddata/drone.py b/eos/db/saveddata/drone.py
index 93434c5b9..dd7337191 100644
--- a/eos/db/saveddata/drone.py
+++ b/eos/db/saveddata/drone.py
@@ -18,27 +18,35 @@
# ===============================================================================
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, Boolean, DateTime
-from sqlalchemy.orm import mapper, relation
+from sqlalchemy.orm import mapper, relation, synonym
+from sqlalchemy.orm.collections import attribute_mapped_collection
import datetime
from eos.db import saveddata_meta
from eos.saveddata.drone import Drone
from eos.saveddata.fit import Fit
+from eos.saveddata.mutator import MutatorDrone
drones_table = Table("drones", saveddata_meta,
Column("groupID", Integer, primary_key=True),
Column("fitID", Integer, ForeignKey("fits.ID"), nullable=False, index=True),
Column("itemID", Integer, nullable=False),
+ Column("baseItemID", Integer, nullable=True),
+ Column("mutaplasmidID", Integer, nullable=True),
Column("amount", Integer, nullable=False),
Column("amountActive", Integer, nullable=False),
Column("projected", Boolean, default=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
- Column("projectionRange", Float, nullable=True)
- )
+ Column("projectionRange", Float, nullable=True))
+
mapper(Drone, drones_table,
properties={
- "owner": relation(Fit)
- }
-)
+ "ID": synonym("groupID"),
+ "owner": relation(Fit),
+ "mutators": relation(
+ MutatorDrone,
+ backref="item",
+ cascade="all,delete-orphan",
+ collection_class=attribute_mapped_collection('attrID'))})
diff --git a/eos/db/saveddata/module.py b/eos/db/saveddata/module.py
index 784eb6de0..83196f7af 100644
--- a/eos/db/saveddata/module.py
+++ b/eos/db/saveddata/module.py
@@ -18,14 +18,14 @@
# ===============================================================================
from sqlalchemy import Table, Column, Integer, Float, ForeignKey, CheckConstraint, Boolean, DateTime
-from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.orm import relation, mapper
+from sqlalchemy.orm.collections import attribute_mapped_collection
import datetime
from eos.db import saveddata_meta
from eos.saveddata.module import Module
-from eos.saveddata.mutator import Mutator
from eos.saveddata.fit import Fit
+from eos.saveddata.mutator import MutatorModule
modules_table = Table("modules", saveddata_meta,
Column("ID", Integer, primary_key=True),
@@ -45,13 +45,12 @@ modules_table = Table("modules", saveddata_meta,
Column("projectionRange", Float, nullable=True),
CheckConstraint('("dummySlot" = NULL OR "itemID" = NULL) AND "dummySlot" != "itemID"'))
+
mapper(Module, modules_table,
properties={
"owner": relation(Fit),
"mutators": relation(
- Mutator,
- backref="module",
+ MutatorModule,
+ backref="item",
cascade="all,delete-orphan",
- collection_class=attribute_mapped_collection('attrID')
- )
- })
+ collection_class=attribute_mapped_collection('attrID'))})
diff --git a/eos/db/saveddata/mutator.py b/eos/db/saveddata/mutatorDrone.py
similarity index 63%
rename from eos/db/saveddata/mutator.py
rename to eos/db/saveddata/mutatorDrone.py
index 1ba81e097..9aa20f89c 100644
--- a/eos/db/saveddata/mutator.py
+++ b/eos/db/saveddata/mutatorDrone.py
@@ -23,13 +23,14 @@ from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
from sqlalchemy.orm import mapper
from eos.db import saveddata_meta
-from eos.saveddata.mutator import Mutator
+from eos.saveddata.mutator import MutatorDrone
-mutator_table = Table("mutators", saveddata_meta,
- Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
- Column("attrID", Integer, primary_key=True, index=True),
- Column("value", Float, nullable=False),
- Column("created", DateTime, nullable=True, default=datetime.datetime.now),
- Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
+mutatorDrones_table = Table(
+ "mutatorsDrones", saveddata_meta,
+ Column("groupID", Integer, ForeignKey("drones.groupID"), primary_key=True, index=True),
+ Column("attrID", Integer, primary_key=True, index=True),
+ Column("value", Float, nullable=False),
+ Column("created", DateTime, nullable=True, default=datetime.datetime.now),
+ Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
-mapper(Mutator, mutator_table)
+mapper(MutatorDrone, mutatorDrones_table)
diff --git a/eos/db/saveddata/mutatorMod.py b/eos/db/saveddata/mutatorMod.py
new file mode 100644
index 000000000..a17e8a72d
--- /dev/null
+++ b/eos/db/saveddata/mutatorMod.py
@@ -0,0 +1,36 @@
+# ===============================================================================
+# Copyright (C) 2010 Diego Duclos
+#
+# 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 .
+# ===============================================================================
+
+import datetime
+
+from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer, Table
+from sqlalchemy.orm import mapper
+
+from eos.db import saveddata_meta
+from eos.saveddata.mutator import MutatorModule
+
+mutatorMods_table = Table(
+ "mutators", saveddata_meta,
+ Column("moduleID", Integer, ForeignKey("modules.ID"), primary_key=True, index=True),
+ Column("attrID", Integer, primary_key=True, index=True),
+ Column("value", Float, nullable=False),
+ Column("created", DateTime, nullable=True, default=datetime.datetime.now),
+ Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
+
+mapper(MutatorModule, mutatorMods_table)
diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py
index a5cbe1945..0acf5864f 100644
--- a/eos/saveddata/drone.py
+++ b/eos/saveddata/drone.py
@@ -25,6 +25,8 @@ from sqlalchemy.orm import reconstructor, validates
import eos.db
from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
+from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
+from eos.saveddata.mutator import MutatorDrone
from eos.utils.cycles import CycleInfo
from eos.utils.default import DEFAULT
from eos.utils.stats import DmgTypes, RRTypes
@@ -33,12 +35,13 @@ from eos.utils.stats import DmgTypes, RRTypes
pyfalog = Logger(__name__)
-class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
+class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
MINING_ATTRIBUTES = ("miningAmount",)
- def __init__(self, item):
+ def __init__(self, item, baseItem=None, mutaplasmid=None):
"""Initialize a drone from the program"""
- self.__item = item
+ self._item = item
+ self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
if self.isInvalid:
raise ValueError("Passed item is not a Drone")
@@ -53,14 +56,19 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@reconstructor
def init(self):
"""Initialize a drone from the database and validate"""
- self.__item = None
+ self._item = None
if self.itemID:
- self.__item = eos.db.getItem(self.itemID)
- if self.__item is None:
+ self._item = eos.db.getItem(self.itemID)
+ if self._item is None:
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
+ try:
+ self._mutaReconstruct()
+ except MutaError:
+ return
+
if self.isInvalid:
pyfalog.error("Item (id: {0}) is not a Drone", self.itemID)
return
@@ -74,10 +82,13 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__baseRRAmount = None
self.__miningyield = None
self.__itemModifiedAttributes = ModifiedAttributeDict()
- self.__itemModifiedAttributes.original = self.__item.attributes
- self.__itemModifiedAttributes.overrides = self.__item.overrides
-
+ self.__itemModifiedAttributes.original = self._item.attributes
+ self.__itemModifiedAttributes.overrides = self._item.overrides
self.__chargeModifiedAttributes = ModifiedAttributeDict()
+
+ self._mutaLoadMutators(mutatorClass=MutatorDrone)
+ self.__itemModifiedAttributes.mutators = self.mutators
+
# pheonix todo: check the attribute itself, not the modified. this will always return 0 now.
chargeID = self.getModifiedItemAttr("entityMissileTypeID", None)
if chargeID is not None:
@@ -96,11 +107,17 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def isInvalid(self):
- return self.__item is None or self.__item.category.name != "Drone"
+ if self._item is None:
+ return True
+ if self._item.category.name != "Drone":
+ return True
+ if self._mutaIsInvalid:
+ return True
+ return False
@property
def item(self):
- return self.__item
+ return self._item
@property
def charge(self):
@@ -337,10 +354,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
effect.handler(fit, self, ("droneCharge",), projectionRange, effect=effect)
def __deepcopy__(self, memo):
- copy = Drone(self.item)
+ copy = Drone(self.item, self.baseItem, self.mutaplasmid)
copy.amount = self.amount
copy.amountActive = self.amountActive
copy.projectionRange = self.projectionRange
+ self._mutaApplyMutators(mutatorClass=MutatorDrone, targetInstance=copy)
return copy
def rebase(self, item):
@@ -348,10 +366,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
amountActive = self.amountActive
projectionRange = self.projectionRange
- Drone.__init__(self, item)
+ Drone.__init__(self, item, self.baseItem, self.mutaplasmid)
self.amount = amount
self.amountActive = amountActive
self.projectionRange = projectionRange
+ self._mutaApplyMutators(mutatorClass=MutatorDrone)
def fits(self, fit):
fitDroneGroupLimits = set()
diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py
index 680ccd50d..e2602eef3 100644
--- a/eos/saveddata/module.py
+++ b/eos/saveddata/module.py
@@ -27,7 +27,8 @@ from eos.const import FittingHardpoint, FittingModuleState, FittingSlot
from eos.effectHandlerHelpers import HandledCharge, HandledItem
from eos.modifiedAttributeDict import ChargeAttrShortcut, ItemAttrShortcut, ModifiedAttributeDict
from eos.saveddata.citadel import Citadel
-from eos.saveddata.mutator import Mutator
+from eos.saveddata.mutatedMixin import MutatedMixin, MutaError
+from eos.saveddata.mutator import MutatorModule
from eos.utils.cycles import CycleInfo, CycleSequence
from eos.utils.default import DEFAULT
from eos.utils.float import floatUnerr
@@ -61,7 +62,7 @@ ProjectedSystem = {
}
-class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
+class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, MutatedMixin):
"""An instance of this class represents a module together with its charge and modified attributes"""
MINING_ATTRIBUTES = ("miningAmount",)
SYSTEM_GROUPS = (
@@ -72,21 +73,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
"""Initialize a module from the program"""
self.itemID = item.ID if item is not None else None
- self.baseItemID = baseItem.ID if baseItem is not None else None
- self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
- if baseItem is not None:
- # we're working with a mutated module, need to get abyssal module loaded with the base attributes
- # Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
- # will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
- # before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
- self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
- self.__baseItem = baseItem
- self.__mutaplasmid = mutaplasmid
- else:
- self.__item = item
- self.__baseItem = baseItem
- self.__mutaplasmid = mutaplasmid
+ self._item = item
+ self._mutaInit(baseItem=baseItem, mutaplasmid=mutaplasmid)
if item is not None and self.isInvalid:
raise ValueError("Passed item is not a Module")
@@ -101,27 +90,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@reconstructor
def init(self):
"""Initialize a module from the database and validate"""
- self.__item = None
- self.__baseItem = None
+ self._item = None
self.__charge = None
- self.__mutaplasmid = None
# we need this early if module is invalid and returns early
self.__slot = self.dummySlot
if self.itemID:
- self.__item = eos.db.getItem(self.itemID)
- if self.__item is None:
+ self._item = eos.db.getItem(self.itemID)
+ if self._item is None:
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
- if self.baseItemID:
- self.__item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
- self.__baseItem = eos.db.getItem(self.baseItemID)
- self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
- if self.__baseItem is None:
- pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
- return
+ try:
+ self._mutaReconstruct()
+ except MutaError:
+ return
if self.isInvalid:
pyfalog.error("Item (id: {0}) is not a Module", self.itemID)
@@ -149,21 +133,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self)
self.__slot = self.dummySlot # defaults to None
- if self.__item:
- self.__itemModifiedAttributes.original = self.__item.attributes
- self.__itemModifiedAttributes.overrides = self.__item.overrides
- self.__hardpoint = self.__calculateHardpoint(self.__item)
- self.__slot = self.calculateSlot(self.__item)
-
- # Instantiate / remove mutators if this is a mutated module
- if self.__baseItem:
- for x in self.mutaplasmid.attributes:
- attr = self.item.attributes[x.name]
- id = attr.ID
- if id not in self.mutators: # create the mutator
- Mutator(self, attr, attr.value)
- # @todo: remove attributes that are no longer part of the mutaplasmid.
+ if self._item:
+ self.__itemModifiedAttributes.original = self._item.attributes
+ self.__itemModifiedAttributes.overrides = self._item.overrides
+ self.__hardpoint = self.__calculateHardpoint(self._item)
+ self.__slot = self.calculateSlot(self._item)
+ self._mutaLoadMutators(mutatorClass=MutatorModule)
self.__itemModifiedAttributes.mutators = self.mutators
if self.__charge:
@@ -198,28 +174,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# todo: validate baseItem as well if it's set.
if self.isEmpty:
return False
- if self.__item is None:
+ if self._item is None:
return True
if (
- self.__item.category.name not in ("Module", "Subsystem", "Structure Module")
- and self.__item.group.name not in self.SYSTEM_GROUPS
+ self._item.category.name not in ("Module", "Subsystem", "Structure Module")
+ and self._item.group.name not in self.SYSTEM_GROUPS
):
return True
if (
- self.__item.category.name == "Structure Module"
- and self.__item.group.name == "Quantum Cores"
+ self._item.category.name == "Structure Module"
+ and self._item.group.name == "Quantum Cores"
):
return True
- if self.item.isAbyssal and not self.isMutated:
- return True
- if self.isMutated and not self.__mutaplasmid:
+ if self._mutaIsInvalid:
return True
return False
- @property
- def isMutated(self):
- return self.baseItemID and self.mutaplasmidID
-
@property
def numCharges(self):
return self.getNumCharges(self.charge)
@@ -419,15 +389,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def item(self):
- return self.__item if self.__item != 0 else None
-
- @property
- def baseItem(self):
- return self.__baseItem
-
- @property
- def mutaplasmid(self):
- return self.__mutaplasmid
+ return self._item if self._item != 0 else None
@property
def charge(self):
@@ -1098,9 +1060,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
copy.spoolType = self.spoolType
copy.spoolAmount = self.spoolAmount
copy.projectionRange = self.projectionRange
-
- for x in self.mutators.values():
- Mutator(copy, x.attribute, x.value)
+ self._mutaApplyMutators(mutatorClass=MutatorModule, targetInstance=copy)
return copy
@@ -1118,14 +1078,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.spoolType = spoolType
self.spoolAmount = spoolAmount
self.projectionRange = projectionRange
- for x in self.mutators.values():
- Mutator(self, x.attribute, x.value)
+ self._mutaApplyMutators(mutatorClass=MutatorModule)
def __repr__(self):
if self.item:
- return "Module(ID={}, name={}) at {}".format(
- self.item.ID, self.item.name, hex(id(self))
- )
+ return "Module(ID={}, name={}) at {}".format(self.item.ID, self.item.name, hex(id(self)))
else:
return "EmptyModule() at {}".format(hex(id(self)))
diff --git a/eos/saveddata/mutatedMixin.py b/eos/saveddata/mutatedMixin.py
new file mode 100644
index 000000000..a1cf270e0
--- /dev/null
+++ b/eos/saveddata/mutatedMixin.py
@@ -0,0 +1,96 @@
+# ===============================================================================
+# Copyright (C) 2010 Diego Duclos
+#
+# 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 .
+# ===============================================================================
+
+
+import eos.db
+
+from logbook import Logger
+
+
+pyfalog = Logger(__name__)
+
+
+class MutaError(Exception):
+ pass
+
+
+class MutatedMixin:
+
+ @property
+ def isMutated(self):
+ return self.baseItemID and self.mutaplasmidID
+
+ @property
+ def baseItem(self):
+ return self.__baseItem
+
+ @property
+ def mutaplasmid(self):
+ return self.__mutaplasmid
+
+ def _mutaInit(self, baseItem, mutaplasmid):
+ self.baseItemID = baseItem.ID if baseItem is not None else None
+ self.mutaplasmidID = mutaplasmid.ID if mutaplasmid is not None else None
+ if baseItem is not None:
+ # we're working with a mutated module, need to get abyssal module loaded with the base attributes
+ # Note: there may be a better way of doing this, such as a metho on this classe to convert(mutaplamid). This
+ # will require a bit more research though, considering there has never been a need to "swap" out the item of a Module
+ # before, and there may be assumptions taken with regards to the item never changing (pre-calculated / cached results, for example)
+ print('muta init', self, self._item.ID, self.baseItemID)
+ self._item = eos.db.getItemWithBaseItemAttribute(self._item.ID, self.baseItemID)
+ self.__baseItem = baseItem
+ self.__mutaplasmid = mutaplasmid
+ else:
+ self.__baseItem = None
+ self.__mutaplasmid = None
+
+ def _mutaReconstruct(self):
+ self.__baseItem = None
+ self.__mutaplasmid = None
+ if self.baseItemID:
+ self._item = eos.db.getItemWithBaseItemAttribute(self.itemID, self.baseItemID)
+ self.__baseItem = eos.db.getItem(self.baseItemID)
+ self.__mutaplasmid = eos.db.getMutaplasmid(self.mutaplasmidID)
+ if self.__baseItem is None:
+ pyfalog.error("Base Item (id: {0}) does not exist", self.itemID)
+ raise MutaError
+
+ def _mutaLoadMutators(self, mutatorClass):
+ # Instantiate / remove mutators if this is a mutated module
+ if self.__baseItem:
+ for x in self.mutaplasmid.attributes:
+ attr = self.item.attributes[x.name]
+ id = attr.ID
+ if id not in self.mutators: # create the mutator
+ mutatorClass(self, attr, attr.value)
+ # @todo: remove attributes that are no longer part of the mutaplasmid.
+
+ @property
+ def _mutaIsInvalid(self):
+ if self.item.isAbyssal and not self.isMutated:
+ return True
+ if self.isMutated and not self.__mutaplasmid:
+ return True
+ return False
+
+ def _mutaApplyMutators(self, mutatorClass, targetInstance=None):
+ if targetInstance is None:
+ targetInstance = self
+ for x in self.mutators.values():
+ mutatorClass(targetInstance, x.attribute, x.value)
diff --git a/eos/saveddata/mutator.py b/eos/saveddata/mutator.py
index ee1b116c2..a14af2d2c 100644
--- a/eos/saveddata/mutator.py
+++ b/eos/saveddata/mutator.py
@@ -27,10 +27,10 @@ from eos.eqBase import EqBase
pyfalog = Logger(__name__)
-class Mutator(EqBase):
- """ Mutators are the object that represent an attribute override on the module level, in conjunction with
- mutaplasmids. Each mutated module, when created, is instantiated with a list of these objects, dictated by the
- mutaplasmid that is used on the base module.
+class MutatorBase(EqBase):
+ """ Mutators are the object that represent an attribute override on the eos item level, in conjunction with
+ mutaplasmids. Each mutated item, when created, is instantiated with a list of these objects, dictated by the
+ mutaplasmid that is used on the base item.
A note on the different attributes on this object:
* attribute: points to the definition of the attribute from dgmattribs.
@@ -40,13 +40,13 @@ class Mutator(EqBase):
This could probably be cleaned up with smarter relationships, but whatever
"""
- def __init__(self, module, attr, value):
- # this needs to be above module assignment, as assigning the module will add it to the list and it via
+ def __init__(self, item, attr, value):
+ # this needs to be above item assignment, as assigning the item will add it to the list and it via
# relationship and needs this set 4correctly
self.attrID = attr.ID
- self.module = module
- self.moduleID = module.ID
+ self.item = item
+ self.itemID = item.ID
self.__attr = attr
self.build()
@@ -67,20 +67,20 @@ class Mutator(EqBase):
def build(self):
# try...except here to catch orphaned mutators. Pretty rare, only happens so far if hacking the database
- # But put it here to remove the module link if it happens, until a better solution can be developed
+ # But put it here to remove the eos item link if it happens, until a better solution can be developed
try:
# dynamic attribute links to the Mutaplasmids attribute definition for this mutated definition
- self.dynamicAttribute = next(a for a in self.module.mutaplasmid.attributes if a.attributeID == self.attrID)
+ self.dynamicAttribute = next(a for a in self.item.mutaplasmid.attributes if a.attributeID == self.attrID)
# base attribute links to the base ite's attribute for this mutated definition (contains original, base value)
- self.baseAttribute = self.module.item.attributes[self.dynamicAttribute.name]
+ self.baseAttribute = self.item.item.attributes[self.dynamicAttribute.name]
except (KeyboardInterrupt, SystemExit):
raise
except:
- self.module = None
+ self.item = None
@validates("value")
def validator(self, key, val):
- """ Validates values as properly falling within the range of the modules' Mutaplasmid """
+ """ Validates values as properly falling within the range of the items' Mutaplasmid """
if self.baseValue == 0:
return 0
mod = val / self.baseValue
@@ -99,7 +99,7 @@ class Mutator(EqBase):
# @todo: need to test what happens:
# 1) if an attribute is removed from the EVE database
# 2) if a mutaplasmid does not have the attribute anymore
- # 3) if a mutaplasmid does not exist (in eve or on the module's item)
+ # 3) if a mutaplasmid does not exist (in eve or on the pyfa item's item)
# Can remove invalid ones in a SQLAlchemy collection class... eventually
return self.__attr is None
@@ -139,3 +139,11 @@ class Mutator(EqBase):
@property
def attribute(self):
return self.__attr
+
+
+class MutatorModule(MutatorBase):
+ pass
+
+
+class MutatorDrone(MutatorBase):
+ pass
diff --git a/gui/builtinContextMenus/__init__.py b/gui/builtinContextMenus/__init__.py
index 0546e7a0f..9b3b1c0b5 100644
--- a/gui/builtinContextMenus/__init__.py
+++ b/gui/builtinContextMenus/__init__.py
@@ -26,7 +26,7 @@ from gui.builtinContextMenus import itemAmountChange
from gui.builtinContextMenus import itemProjectionRange
from gui.builtinContextMenus import droneSplitStack
from gui.builtinContextMenus import itemVariationChange
-from gui.builtinContextMenus import moduleMutations
+from gui.builtinContextMenus import itemMutations
from gui.builtinContextMenus import moduleFill
from gui.builtinContextMenus import moduleMutatedExport
from gui.builtinContextMenus import skillAffectors
diff --git a/gui/builtinContextMenus/moduleMutations.py b/gui/builtinContextMenus/itemMutations.py
similarity index 62%
rename from gui/builtinContextMenus/moduleMutations.py
rename to gui/builtinContextMenus/itemMutations.py
index 5a569e2b7..b401348d3 100644
--- a/gui/builtinContextMenus/moduleMutations.py
+++ b/gui/builtinContextMenus/itemMutations.py
@@ -1,16 +1,19 @@
-# noinspection PyPackageRequirements
+import re
+# noinspection PyPackageRequirements
import wx
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
-from gui.fitCommands import GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand
+from gui.fitCommands import (
+ GuiConvertMutatedLocalModuleCommand, GuiRevertMutatedLocalModuleCommand,
+ GuiConvertMutatedLocalDroneCommand, GuiRevertMutatedLocalDroneCommand)
from service.fit import Fit
_t = wx.GetTranslation
-class ChangeModuleMutation(ContextMenuSingle):
+class ChangeItemMutation(ContextMenuSingle):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
@@ -18,10 +21,10 @@ class ChangeModuleMutation(ContextMenuSingle):
def display(self, callingWindow, srcContext, mainItem):
- if srcContext != "fittingModule" or self.mainFrame.getActiveFit() is None:
+ if srcContext not in ("fittingModule", "droneItem") or self.mainFrame.getActiveFit() is None:
return False
- if mainItem is None or mainItem.isEmpty:
+ if mainItem is None or getattr(mainItem, 'isEmpty', False):
return False
if len(mainItem.item.mutaplasmids) == 0 and not mainItem.isMutated:
@@ -44,10 +47,13 @@ class ChangeModuleMutation(ContextMenuSingle):
for item in mainItem.item.mutaplasmids:
label = item.item.name
- keywords = ('Decayed', 'Gravid', 'Unstable', 'Exigent', 'Radical')
+ keywords = ('Decayed', 'Gravid', 'Unstable', 'Radical')
for kw in keywords:
if item.item.name.startswith(f'{kw} '):
label = kw
+ m = re.match('(?P\S+) (?P\S+) Drone (?P\S+) Mutaplasmid', label)
+ if m:
+ label = '{} {}'.format(m.group('mutagrade'), m.group('mutatype'))
id = ContextMenuSingle.nextID()
self.eventIDs[id] = (item, mainItem)
mItem = wx.MenuItem(menu, id, label)
@@ -57,13 +63,17 @@ class ChangeModuleMutation(ContextMenuSingle):
return sub
def handleMenu(self, event):
- mutaplasmid, mod = self.eventIDs[event.Id]
+ mutaplasmid, item = self.eventIDs[event.Id]
fitID = self.mainFrame.getActiveFit()
fit = Fit.getInstance().getFit(fitID)
- if mod in fit.modules:
- position = fit.modules.index(mod)
+ if item in fit.modules:
+ position = fit.modules.index(item)
self.mainFrame.command.Submit(GuiConvertMutatedLocalModuleCommand(
fitID=fitID, position=position, mutaplasmid=mutaplasmid))
+ elif item in fit.drones:
+ position = fit.drones.index(item)
+ self.mainFrame.command.Submit(GuiConvertMutatedLocalDroneCommand(
+ fitID=fitID, position=position, mutaplasmid=mutaplasmid))
def activate(self, callingWindow, fullContext, mainItem, i):
fitID = self.mainFrame.getActiveFit()
@@ -72,9 +82,13 @@ class ChangeModuleMutation(ContextMenuSingle):
position = fit.modules.index(mainItem)
self.mainFrame.command.Submit(GuiRevertMutatedLocalModuleCommand(
fitID=fitID, position=position))
+ elif mainItem in fit.drones:
+ position = fit.drones.index(mainItem)
+ self.mainFrame.command.Submit(GuiRevertMutatedLocalDroneCommand(
+ fitID=fitID, position=position))
def getBitmap(self, callingWindow, context, mainItem):
return None
-ChangeModuleMutation.register()
+ChangeItemMutation.register()
diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py
index d6dec97b4..872d10d51 100644
--- a/gui/builtinItemStatsViews/itemAttributes.py
+++ b/gui/builtinItemStatsViews/itemAttributes.py
@@ -355,4 +355,5 @@ class ItemParams(wx.Panel):
fvalue = roundDec(value, digits)
else:
fvalue = value
- return "%s %s" % (fvalue, unit)
+ unitSuffix = f' {unit}' if unit is not None else ''
+ return f'{fvalue}{unitSuffix}'
diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py
index 247d3953c..7125766f4 100644
--- a/gui/builtinItemStatsViews/itemMutator.py
+++ b/gui/builtinItemStatsViews/itemMutator.py
@@ -18,22 +18,22 @@ _t = wx.GetTranslation
class ItemMutatorPanel(wx.Panel):
- def __init__(self, parent, mod):
+ def __init__(self, parent, stuff):
wx.Panel.__init__(self, parent)
- self.stuff = mod
+ self.stuff = stuff
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
mainSizer = wx.BoxSizer(wx.VERTICAL)
headerSizer = wx.BoxSizer(wx.HORIZONTAL)
headerSizer.AddStretchSpacer()
- itemIcon = BitmapLoader.getStaticBitmap(mod.item.iconID, self, "icons")
+ itemIcon = BitmapLoader.getStaticBitmap(stuff.item.iconID, self, "icons")
if itemIcon is not None:
headerSizer.Add(itemIcon, 0, 0, 0)
- mutaIcon = BitmapLoader.getStaticBitmap(mod.mutaplasmid.item.iconID, self, "icons")
+ mutaIcon = BitmapLoader.getStaticBitmap(stuff.mutaplasmid.item.iconID, self, "icons")
if mutaIcon is not None:
headerSizer.Add(mutaIcon, 0, wx.LEFT, 0)
- sourceItemShort = "{} {}".format(mod.mutaplasmid.item.name.split(" ")[0], mod.baseItem.name)
+ sourceItemShort = "{} {}".format(stuff.mutaplasmid.item.name.split(" ")[0], stuff.baseItem.name)
sourceItemText = wx.StaticText(self, wx.ID_ANY, sourceItemShort)
font = parent.GetFont()
font.SetWeight(wx.BOLD)
@@ -43,7 +43,7 @@ class ItemMutatorPanel(wx.Panel):
mainSizer.Add(headerSizer, 0, wx.ALL | wx.EXPAND, 5)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
- self.mutaList = ItemMutatorList(self, mod)
+ self.mutaList = ItemMutatorList(self, stuff)
mainSizer.Add(self.mutaList, 1, wx.EXPAND | wx.ALL, 0)
mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 0)
@@ -68,13 +68,13 @@ class ItemMutatorPanel(wx.Panel):
class ItemMutatorList(wx.ScrolledWindow):
- def __init__(self, parent, mod):
+ def __init__(self, parent, stuff):
wx.ScrolledWindow.__init__(self, parent)
self.SetScrollRate(0, 15)
self.carryingFitID = gui.mainFrame.MainFrame.getInstance().getActiveFit()
self.initialMutations = {}
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
- self.mod = mod
+ self.stuff = stuff
self.timer = None
self.isModified = False
@@ -91,9 +91,8 @@ class ItemMutatorList(wx.ScrolledWindow):
('Damage Control', 'duration'): True,
('Siege Module', 'siegeLocalLogisticsDurationBonus'): False
}
-
first = True
- for m in sorted(mod.mutators.values(), key=lambda x: x.attribute.displayName):
+ for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName):
if m.baseValue == 0:
continue
if not first:
@@ -102,10 +101,10 @@ class ItemMutatorList(wx.ScrolledWindow):
self.initialMutations[m.attrID] = m.value
- highIsGood = higOverrides.get((mod.item.group.name, m.attribute.name), m.highIsGood)
+ highIsGood = higOverrides.get((stuff.item.group.name, m.attribute.name), m.highIsGood)
# Format: [raw value, modifier applied to base raw value, display value]
- range1 = (m.minValue, m.attribute.unit.SimplifyValue(m.minValue))
- range2 = (m.maxValue, m.attribute.unit.SimplifyValue(m.maxValue))
+ range1 = (m.minValue, self._simplifyValue(m, m.minValue))
+ range2 = (m.maxValue, self._simplifyValue(m, m.maxValue))
# minValue/maxValue do not always correspond to min/max, because these are
# just base value multiplied by minMod/maxMod, and in case base is negative
@@ -148,11 +147,11 @@ class ItemMutatorList(wx.ScrolledWindow):
headingSizer.Add(displayName, 3, wx.ALL | wx.EXPAND, 0)
- worseVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worseRange[0]), rounding='dec')
+ worseVal = ItemParams.FormatValue(*self._preformatValue(m, worseRange[0]), rounding='dec')
worseText = wx.StaticText(self, wx.ID_ANY, worseVal)
worseText.SetForegroundColour(badColor)
- betterVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(betterRange[0]), rounding='dec')
+ betterVal = ItemParams.FormatValue(*self._preformatValue(m, betterRange[0]), rounding='dec')
betterText = wx.StaticText(self, wx.ID_ANY, betterVal)
betterText.SetForegroundColour(goodColor)
@@ -163,23 +162,38 @@ class ItemMutatorList(wx.ScrolledWindow):
sizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
slider = AttributeSlider(parent=self,
- baseValue=m.attribute.unit.SimplifyValue(sliderBaseValue),
+ baseValue=self._simplifyValue(m, sliderBaseValue),
minValue=displayMinRange[1],
maxValue=displayMaxRange[1],
inverse=displayMaxRange is worseRange)
- slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False)
+ slider.SetValue(self._simplifyValue(m, m.value), False)
slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
self.event_mapping[slider] = m
sizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
self.SetSizer(sizer)
+ def _simplifyValue(self, mutator, value):
+ if mutator.attribute.unit is None:
+ return value
+ return mutator.attribute.unit.SimplifyValue(value)
+
+ def _complicateValue(self, mutator, value):
+ if mutator.attribute.unit is None:
+ return value
+ return mutator.attribute.unit.ComplicateValue(value)
+
+ def _preformatValue(self, mutator, value):
+ if mutator.attribute.unit is None:
+ return value, None
+ return mutator.attribute.unit.PreformatValue(value)
+
def changeMutatedValue(self, evt):
if evt.AffectsModifiedFlag:
self.isModified = True
m = self.event_mapping[evt.Object]
value = evt.Value
- value = m.attribute.unit.ComplicateValue(value)
+ value = self._complicateValue(m, value)
sFit = Fit.getInstance()
sFit.changeMutatedValuePrelim(m, value)
@@ -198,7 +212,7 @@ class ItemMutatorList(wx.ScrolledWindow):
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
value = sFit.changeMutatedValuePrelim(m, m.baseValue)
- value = m.attribute.unit.SimplifyValue(value)
+ value = self._simplifyValue(m, value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
@@ -208,7 +222,7 @@ class ItemMutatorList(wx.ScrolledWindow):
for slider, m in self.event_mapping.items():
value = random.uniform(m.minValue, m.maxValue)
value = sFit.changeMutatedValuePrelim(m, value)
- value = m.attribute.unit.SimplifyValue(value)
+ value = self._simplifyValue(m, value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
@@ -218,7 +232,7 @@ class ItemMutatorList(wx.ScrolledWindow):
for slider, m in self.event_mapping.items():
if m.attrID in self.initialMutations:
value = sFit.changeMutatedValuePrelim(m, self.initialMutations[m.attrID])
- value = m.attribute.unit.SimplifyValue(value)
+ value = self._simplifyValue(m, value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
@@ -226,14 +240,14 @@ class ItemMutatorList(wx.ScrolledWindow):
# Submit mutation changes
sFit = Fit.getInstance()
fit = sFit.getFit(self.carryingFitID)
- if self.mod in fit.modules:
+ if self.stuff in fit.modules:
if self.isModified:
currentMutation = {}
for slider, m in self.event_mapping.items():
# Sliders may have more up-to-date info than mutator in case we changed
# value in slider and without confirming it, decided to close window
value = slider.GetValue()
- value = m.attribute.unit.ComplicateValue(value)
+ value = self._complicateValue(m, value)
if value != m.value:
value = sFit.changeMutatedValuePrelim(m, value)
currentMutation[m.attrID] = value
@@ -242,7 +256,7 @@ class ItemMutatorList(wx.ScrolledWindow):
mainFrame = gui.mainFrame.MainFrame.getInstance()
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
fitID=self.carryingFitID,
- position=fit.modules.index(self.mod),
+ position=fit.modules.index(self.stuff),
mutation=currentMutation,
oldMutation=self.initialMutations))
for slider in self.event_mapping:
diff --git a/gui/fitCommands/__init__.py b/gui/fitCommands/__init__.py
index 6e8382ae8..16dcb480a 100644
--- a/gui/fitCommands/__init__.py
+++ b/gui/fitCommands/__init__.py
@@ -28,6 +28,8 @@ from .gui.localDrone.changeAmount import GuiChangeLocalDroneAmountCommand
from .gui.localDrone.changeMetas import GuiChangeLocalDroneMetasCommand
from .gui.localDrone.clone import GuiCloneLocalDroneCommand
from .gui.localDrone.imprt import GuiImportLocalDronesCommand
+from .gui.localDrone.mutatedConvert import GuiConvertMutatedLocalDroneCommand
+from .gui.localDrone.mutatedRevert import GuiRevertMutatedLocalDroneCommand
from .gui.localDrone.remove import GuiRemoveLocalDronesCommand
from .gui.localDrone.stackSplit import GuiSplitLocalDroneStackCommand
from .gui.localDrone.stacksMerge import GuiMergeLocalDroneStacksCommand
diff --git a/gui/fitCommands/gui/localDrone/mutatedConvert.py b/gui/fitCommands/gui/localDrone/mutatedConvert.py
new file mode 100644
index 000000000..bf23581b5
--- /dev/null
+++ b/gui/fitCommands/gui/localDrone/mutatedConvert.py
@@ -0,0 +1,65 @@
+import math
+
+import wx
+
+import eos.db
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
+from gui.fitCommands.calc.drone.localRemove import CalcRemoveLocalDroneCommand
+from gui.fitCommands.helpers import DroneInfo, InternalCommandHistory
+from service.fit import Fit
+
+
+class GuiConvertMutatedLocalDroneCommand(wx.Command):
+
+ def __init__(self, fitID, position, mutaplasmid):
+ wx.Command.__init__(self, True, 'Convert Local Drone to Mutated')
+ self.internalHistory = InternalCommandHistory()
+ self.fitID = fitID
+ self.position = position
+ self.itemID = mutaplasmid.resultingItem.ID
+ self.mutaplasmidID = mutaplasmid.ID
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fit = sFit.getFit(self.fitID)
+ try:
+ drone = fit.drones[self.position]
+ except IndexError:
+ return False
+ if drone.isMutated:
+ return False
+ info = DroneInfo(
+ amount=drone.amount,
+ amountActive=drone.amountActive,
+ itemID=self.itemID,
+ baseItemID=drone.item.ID,
+ mutaplasmidID=self.mutaplasmidID,
+ mutations={})
+ cmdRemove = CalcRemoveLocalDroneCommand(
+ fitID=self.fitID,
+ position=self.position,
+ amount=math.inf)
+ cmdAdd = CalcAddLocalDroneCommand(
+ fitID=self.fitID,
+ droneInfo=info,
+ forceNewStack=True,
+ ignoreRestrictions=True)
+ success = self.internalHistory.submitBatch(cmdRemove, cmdAdd)
+ eos.db.flush()
+ sFit.recalc(self.fitID)
+ sFit.fill(self.fitID)
+ eos.db.commit()
+ wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
+ return success
+
+ def Undo(self):
+ success = self.internalHistory.undoAll()
+ eos.db.flush()
+ sFit = Fit.getInstance()
+ sFit.recalc(self.fitID)
+ sFit.fill(self.fitID)
+ eos.db.commit()
+ wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
+ return success
diff --git a/gui/fitCommands/gui/localDrone/mutatedRevert.py b/gui/fitCommands/gui/localDrone/mutatedRevert.py
new file mode 100644
index 000000000..8a4836536
--- /dev/null
+++ b/gui/fitCommands/gui/localDrone/mutatedRevert.py
@@ -0,0 +1,60 @@
+import math
+
+import wx
+
+import eos.db
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.drone.localAdd import CalcAddLocalDroneCommand
+from gui.fitCommands.calc.drone.localRemove import CalcRemoveLocalDroneCommand
+from gui.fitCommands.helpers import DroneInfo, InternalCommandHistory
+from service.fit import Fit
+
+
+class GuiRevertMutatedLocalDroneCommand(wx.Command):
+
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, 'Revert Local Drone from Mutated')
+ self.internalHistory = InternalCommandHistory()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fit = sFit.getFit(self.fitID)
+ try:
+ drone = fit.drones[self.position]
+ except IndexError:
+ return False
+ if not drone.isMutated:
+ return False
+ info = DroneInfo(
+ amount=drone.amount,
+ amountActive=drone.amountActive,
+ itemID=drone.baseItemID)
+ cmdRemove = CalcRemoveLocalDroneCommand(
+ fitID=self.fitID,
+ position=self.position,
+ amount=math.inf)
+ cmdAdd = CalcAddLocalDroneCommand(
+ fitID=self.fitID,
+ droneInfo=info,
+ forceNewStack=True,
+ ignoreRestrictions=True)
+ success = self.internalHistory.submitBatch(cmdRemove, cmdAdd)
+ eos.db.flush()
+ sFit.recalc(self.fitID)
+ sFit.fill(self.fitID)
+ eos.db.commit()
+ wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
+ return success
+
+ def Undo(self):
+ success = self.internalHistory.undoAll()
+ eos.db.flush()
+ sFit = Fit.getInstance()
+ sFit.recalc(self.fitID)
+ sFit.fill(self.fitID)
+ eos.db.commit()
+ wx.PostEvent(gui.mainFrame.MainFrame.getInstance(), GE.FitChanged(fitIDs=(self.fitID,)))
+ return success
diff --git a/gui/fitCommands/gui/localModule/mutatedConvert.py b/gui/fitCommands/gui/localModule/mutatedConvert.py
index b8cb99909..e166a5d77 100644
--- a/gui/fitCommands/gui/localModule/mutatedConvert.py
+++ b/gui/fitCommands/gui/localModule/mutatedConvert.py
@@ -21,7 +21,10 @@ class GuiConvertMutatedLocalModuleCommand(wx.Command):
def Do(self):
sFit = Fit.getInstance()
fit = sFit.getFit(self.fitID)
- mod = fit.modules[self.position]
+ try:
+ mod = fit.modules[self.position]
+ except IndexError:
+ return False
if mod.isEmpty:
return False
if mod.isMutated:
diff --git a/gui/fitCommands/gui/localModule/mutatedRevert.py b/gui/fitCommands/gui/localModule/mutatedRevert.py
index d97439bd7..6dcb1a54f 100644
--- a/gui/fitCommands/gui/localModule/mutatedRevert.py
+++ b/gui/fitCommands/gui/localModule/mutatedRevert.py
@@ -19,7 +19,10 @@ class GuiRevertMutatedLocalModuleCommand(wx.Command):
def Do(self):
sFit = Fit.getInstance()
fit = sFit.getFit(self.fitID)
- mod = fit.modules[self.position]
+ try:
+ mod = fit.modules[self.position]
+ except IndexError:
+ return False
if mod.isEmpty:
return False
if not mod.isMutated:
diff --git a/gui/fitCommands/helpers.py b/gui/fitCommands/helpers.py
index 17291ad5f..0a7936542 100644
--- a/gui/fitCommands/helpers.py
+++ b/gui/fitCommands/helpers.py
@@ -158,8 +158,11 @@ class ModuleInfo:
class DroneInfo:
- def __init__(self, itemID, amount, amountActive):
+ def __init__(self, amount, amountActive, itemID, baseItemID=None, mutaplasmidID=None, mutations=None):
self.itemID = itemID
+ self.baseItemID = baseItemID
+ self.mutaplasmidID = mutaplasmidID
+ self.mutations = mutations
self.amount = amount
self.amountActive = amountActive
@@ -170,22 +173,40 @@ class DroneInfo:
info = cls(
itemID=drone.itemID,
amount=drone.amount,
- amountActive=drone.amountActive)
+ amountActive=drone.amountActive,
+ baseItemID=drone.baseItemID,
+ mutaplasmidID=drone.mutaplasmidID,
+ mutations={m.attrID: m.value for m in drone.mutators.values()})
return info
def toDrone(self):
- item = Market.getInstance().getItem(self.itemID, eager=('attributes', 'group.category'))
+ mkt = Market.getInstance()
+ item = mkt.getItem(self.itemID, eager=('attributes', 'group.category'))
+ if self.baseItemID and self.mutaplasmidID:
+ baseItem = mkt.getItem(self.baseItemID, eager=('attributes', 'group.category'))
+ mutaplasmid = eos.db.getDynamicItem(self.mutaplasmidID)
+ else:
+ baseItem = None
+ mutaplasmid = None
try:
- drone = Drone(item)
+ drone = Drone(item, baseItem=baseItem, mutaplasmid=mutaplasmid)
except ValueError:
pyfalog.warning('Invalid item: {}'.format(self.itemID))
return None
+
+ if self.mutations is not None:
+ for attrID, mutator in drone.mutators.items():
+ if attrID in self.mutations:
+ mutator.value = self.mutations[attrID]
+
drone.amount = self.amount
drone.amountActive = self.amountActive
return drone
def __repr__(self):
- return makeReprStr(self, ['itemID', 'amount', 'amountActive'])
+ return makeReprStr(self, [
+ 'itemID', 'amount', 'amountActive',
+ 'baseItemID', 'mutaplasmidID', 'mutations'])
class FighterInfo:
diff --git a/gui/itemStats.py b/gui/itemStats.py
index bb98cd3c0..040dba9a7 100644
--- a/gui/itemStats.py
+++ b/gui/itemStats.py
@@ -22,6 +22,7 @@ import wx
import config
import gui.mainFrame
+from eos.saveddata.drone import Drone
from eos.saveddata.module import Module
from gui.auxWindow import AuxiliaryFrame
from gui.bitmap_loader import BitmapLoader
@@ -165,7 +166,7 @@ class ItemStatsContainer(wx.Panel):
self.traits = ItemTraits(self.nbContainer, stuff, item)
self.nbContainer.AddPage(self.traits, _t("Traits"))
- if isinstance(stuff, Module) and stuff.isMutated:
+ if isinstance(stuff, (Module, Drone)) and stuff.isMutated:
self.mutator = ItemMutatorPanel(self.nbContainer, stuff)
self.nbContainer.AddPage(self.mutator, _t("Mutations"))
diff --git a/service/fit.py b/service/fit.py
index 2a7547a7a..778195b9d 100644
--- a/service/fit.py
+++ b/service/fit.py
@@ -379,7 +379,7 @@ class Fit:
return fits
def changeMutatedValuePrelim(self, mutator, value):
- pyfalog.debug("Changing mutated value for {} / {}: {} => {}".format(mutator.module, mutator.module.mutaplasmid, mutator.value, value))
+ pyfalog.debug("Changing mutated value for {} / {}: {} => {}".format(mutator.item, mutator.item.mutaplasmid, mutator.value, value))
if mutator.value != value:
mutator.value = value
eos.db.flush()