Implement drone mutation support with some exceptions

This commit is contained in:
DarkPhoenix
2021-10-25 23:34:08 +03:00
parent 056685ded5
commit ea7a5b3c70
24 changed files with 494 additions and 167 deletions

View File

@@ -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

View File

@@ -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<mutagrade>\S+) (?P<dronetype>\S+) Drone (?P<mutatype>\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()

View File

@@ -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}'

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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"))