diff --git a/eos/db/saveddata/fighter.py b/eos/db/saveddata/fighter.py
new file mode 100644
index 000000000..0cc66d13f
--- /dev/null
+++ b/eos/db/saveddata/fighter.py
@@ -0,0 +1,33 @@
+#===============================================================================
+# 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 .
+#===============================================================================
+
+from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean
+from sqlalchemy.orm import mapper
+from eos.db import saveddata_meta
+from eos.types import Fighter
+
+fighters_table = Table("fighters", saveddata_meta,
+ Column("groupID", Integer, primary_key=True),
+ Column("fitID", Integer, ForeignKey("fits.ID"), nullable = False, index = True),
+ Column("itemID", Integer, nullable = False),
+ Column("amount", Integer, nullable = False),
+ Column("amountActive", Integer, nullable = False),
+ Column("projected", Boolean, default = False))
+
+mapper(Fighter, fighters_table)
diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py
index a40d8c7fb..77e6eb04b 100644
--- a/eos/db/saveddata/fit.py
+++ b/eos/db/saveddata/fit.py
@@ -26,9 +26,10 @@ from sqlalchemy.orm.collections import attribute_mapped_collection
from eos.db import saveddata_meta
from eos.db.saveddata.module import modules_table
from eos.db.saveddata.drone import drones_table
+from eos.db.saveddata.fighter import fighters_table
from eos.db.saveddata.cargo import cargo_table
from eos.db.saveddata.implant import fitImplants_table
-from eos.types import Fit, Module, User, Booster, Drone, Cargo, Implant, Character, DamagePattern, TargetResists
+from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, TargetResists
from eos.effectHandlerHelpers import *
fits_table = Table("fits", saveddata_meta,
@@ -117,6 +118,12 @@ mapper(Fit, fits_table,
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)),
+ "_Fit__fighters": relation(
+ Fighter,
+ collection_class=HandledDroneCargoList,
+ cascade='all, delete, delete-orphan',
+ single_parent=True,
+ primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)),
"_Fit__cargo": relation(
Cargo,
collection_class=HandledDroneCargoList,
diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py
new file mode 100644
index 000000000..4e16f2423
--- /dev/null
+++ b/eos/saveddata/fighter.py
@@ -0,0 +1,253 @@
+#===============================================================================
+# 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 .
+#===============================================================================
+
+from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
+from eos.effectHandlerHelpers import HandledItem, HandledCharge
+from sqlalchemy.orm import validates, reconstructor
+import eos.db
+import logging
+
+logger = logging.getLogger(__name__)
+
+class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
+ DAMAGE_TYPES = ("em", "kinetic", "explosive", "thermal")
+ MINING_ATTRIBUTES = ("miningAmount",)
+
+ def __init__(self, item):
+ """Initialize a fighter from the program"""
+ self.__item = item
+ print self.__item.category.name
+ if self.isInvalid:
+ raise ValueError("Passed item is not a Fighter")
+
+ self.itemID = item.ID if item is not None else None
+ self.amount = 0
+ self.amountActive = 0
+ self.projected = False
+ self.build()
+
+ @reconstructor
+ def init(self):
+ """Initialize a fighter from the database and validate"""
+ self.__item = None
+
+ if self.itemID:
+ self.__item = eos.db.getItem(self.itemID)
+ if self.__item is None:
+ logger.error("Item (id: %d) does not exist", self.itemID)
+ return
+
+ if self.isInvalid:
+ logger.error("Item (id: %d) is not a Fighter", self.itemID)
+ return
+
+ self.build()
+
+ def build(self):
+ """ Build object. Assumes proper and valid item already set """
+ self.__charge = None
+ self.__dps = None
+ self.__volley = None
+ self.__miningyield = None
+ self.__itemModifiedAttributes = ModifiedAttributeDict()
+ self.__itemModifiedAttributes.original = self.__item.attributes
+ self.__itemModifiedAttributes.overrides = self.__item.overrides
+
+ self.__chargeModifiedAttributes = ModifiedAttributeDict()
+ chargeID = self.getModifiedItemAttr("entityMissileTypeID")
+ if chargeID is not None:
+ charge = eos.db.getItem(int(chargeID))
+ self.__charge = charge
+ self.__chargeModifiedAttributes.original = charge.attributes
+ self.__chargeModifiedAttributes.overrides = charge.overrides
+
+ @property
+ def itemModifiedAttributes(self):
+ return self.__itemModifiedAttributes
+
+ @property
+ def chargeModifiedAttributes(self):
+ return self.__chargeModifiedAttributes
+
+ @property
+ def isInvalid(self):
+ return self.__item is None or self.__item.category.name != "Fighter"
+
+ @property
+ def item(self):
+ return self.__item
+
+ @property
+ def charge(self):
+ return self.__charge
+
+ @property
+ def dealsDamage(self):
+ for attr in ("emDamage", "kineticDamage", "explosiveDamage", "thermalDamage"):
+ if attr in self.itemModifiedAttributes or attr in self.chargeModifiedAttributes:
+ return True
+
+ @property
+ def mines(self):
+ if "miningAmount" in self.itemModifiedAttributes:
+ return True
+
+ @property
+ def hasAmmo(self):
+ return self.charge is not None
+
+ @property
+ def dps(self):
+ return self.damageStats()
+
+ def damageStats(self, targetResists = None):
+ if self.__dps == None:
+ self.__volley = 0
+ self.__dps = 0
+ if self.dealsDamage is True and self.amountActive > 0:
+ if self.hasAmmo:
+ attr = "missileLaunchDuration"
+ getter = self.getModifiedChargeAttr
+ else:
+ attr = "speed"
+ getter = self.getModifiedItemAttr
+
+ cycleTime = self.getModifiedItemAttr(attr)
+
+ volley = sum(map(lambda d: (getter("%sDamage"%d) or 0) * (1-getattr(targetResists, "%sAmount"%d, 0)), self.DAMAGE_TYPES))
+ volley *= self.amountActive
+ volley *= self.getModifiedItemAttr("damageMultiplier") or 1
+ self.__volley = volley
+ self.__dps = volley / (cycleTime / 1000.0)
+
+ return self.__dps, self.__volley
+
+ @property
+ def miningStats(self):
+ if self.__miningyield == None:
+ if self.mines is True and self.amountActive > 0:
+ attr = "duration"
+ getter = self.getModifiedItemAttr
+
+ cycleTime = self.getModifiedItemAttr(attr)
+ volley = sum(map(lambda d: getter(d), self.MINING_ATTRIBUTES)) * self.amountActive
+ self.__miningyield = volley / (cycleTime / 1000.0)
+ else:
+ self.__miningyield = 0
+
+ return self.__miningyield
+
+ @property
+ def maxRange(self):
+ attrs = ("shieldTransferRange", "powerTransferRange",
+ "energyDestabilizationRange", "empFieldRange",
+ "ecmBurstRange", "maxRange")
+ for attr in attrs:
+ maxRange = self.getModifiedItemAttr(attr)
+ if maxRange is not None: return maxRange
+ if self.charge is not None:
+ delay = self.getModifiedChargeAttr("explosionDelay")
+ speed = self.getModifiedChargeAttr("maxVelocity")
+ if delay is not None and speed is not None:
+ return delay / 1000.0 * speed
+
+ # Had to add this to match the falloff property in modules.py
+ # Fscking ship scanners. If you find any other falloff attributes,
+ # Put them in the attrs tuple.
+ @property
+ def falloff(self):
+ attrs = ("falloff", "falloffEffectiveness")
+ for attr in attrs:
+ falloff = self.getModifiedItemAttr(attr)
+ if falloff is not None: return falloff
+
+ @validates("ID", "itemID", "chargeID", "amount", "amountActive")
+ def validator(self, key, val):
+ map = {"ID": lambda val: isinstance(val, int),
+ "itemID" : lambda val: isinstance(val, int),
+ "chargeID" : lambda val: isinstance(val, int),
+ "amount" : lambda val: isinstance(val, int) and val >= 0,
+ "amountActive" : lambda val: isinstance(val, int) and val <= self.amount and val >= 0}
+
+ if map[key](val) == False: raise ValueError(str(val) + " is not a valid value for " + key)
+ else: return val
+
+ def clear(self):
+ self.__dps = None
+ self.__volley = None
+ self.__miningyield = None
+ self.itemModifiedAttributes.clear()
+ self.chargeModifiedAttributes.clear()
+
+ def canBeApplied(self, projectedOnto):
+ """Check if fighter can engage specific fitting"""
+ item = self.item
+ # Do not allow to apply offensive modules on ship with offensive module immunite, with few exceptions
+ # (all effects which apply instant modification are exception, generally speaking)
+ if item.offensive and projectedOnto.ship.getModifiedItemAttr("disallowOffensiveModifiers") == 1:
+ offensiveNonModifiers = set(("energyDestabilizationNew", "leech", "energyNosferatuFalloff", "energyNeutralizerFalloff"))
+ if not offensiveNonModifiers.intersection(set(item.effects)):
+ return False
+ # If assistive modules are not allowed, do not let to apply these altogether
+ if item.assistive and projectedOnto.ship.getModifiedItemAttr("disallowAssistance") == 1:
+ return False
+ else:
+ return True
+
+ def calculateModifiedAttributes(self, fit, runTime, forceProjected = False):
+ if self.projected or forceProjected:
+ context = "projected", "fighter"
+ projected = True
+ else:
+ context = ("fighter",)
+ projected = False
+
+ for effect in self.item.effects.itervalues():
+ if effect.runTime == runTime and \
+ ((projected == True and effect.isType("projected")) or \
+ projected == False and effect.isType("passive")):
+ i = 0
+ while i != self.amountActive:
+ effect.handler(fit, self, context)
+ i += 1
+
+ if self.charge:
+ for effect in self.charge.effects.itervalues():
+ if effect.runTime == runTime:
+ effect.handler(fit, self, ("fighterCharge",))
+
+ def __deepcopy__(self, memo):
+ copy = Fighter(self.item)
+ copy.amount = self.amount
+ copy.amountActive = self.amountActive
+ return copy
+
+ def fits(self, fit):
+ return True
+
+ fitDroneGroupLimits = set()
+ for i in xrange(1, 3):
+ groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i)
+ if groneGrp is not None:
+ fitDroneGroupLimits.add(int(groneGrp))
+ if len(fitDroneGroupLimits) == 0:
+ return True
+ if self.item.groupID in fitDroneGroupLimits:
+ return True
+ return False
diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py
index 611478a6d..50d7d8934 100644
--- a/eos/saveddata/fit.py
+++ b/eos/saveddata/fit.py
@@ -53,6 +53,7 @@ class Fit(object):
self.__modules = HandledModuleList()
self.__drones = HandledDroneCargoList()
+ self.__fighters = HandledDroneCargoList()
self.__cargo = HandledDroneCargoList()
self.__implants = HandledImplantBoosterList()
self.__boosters = HandledImplantBoosterList()
@@ -185,6 +186,10 @@ class Fit(object):
def drones(self):
return self.__drones
+ @property
+ def fighters(self):
+ return self.__fighters
+
@property
def cargo(self):
return self.__cargo
@@ -365,6 +370,7 @@ class Fit(object):
c = chain(
self.modules,
self.drones,
+ self.fighters,
self.boosters,
self.implants,
self.projectedDrones,
@@ -482,6 +488,7 @@ class Fit(object):
u = [
(self.character, self.ship),
self.drones,
+ self.fighters,
self.boosters,
self.appliedImplants,
self.modules
@@ -999,7 +1006,7 @@ class Fit(object):
copy.damagePattern = self.damagePattern
copy.targetResists = self.targetResists
- toCopy = ("modules", "drones", "cargo", "implants", "boosters", "projectedModules", "projectedDrones")
+ toCopy = ("modules", "drones", "fighters", "cargo", "implants", "boosters", "projectedModules", "projectedDrones")
for name in toCopy:
orig = getattr(self, name)
c = getattr(copy, name)
diff --git a/eos/types.py b/eos/types.py
index c1cafd1f5..26e36ef90 100644
--- a/eos/types.py
+++ b/eos/types.py
@@ -27,6 +27,7 @@ from eos.saveddata.targetResists import TargetResists
from eos.saveddata.character import Character, Skill
from eos.saveddata.module import Module, State, Slot, Hardpoint, Rack
from eos.saveddata.drone import Drone
+from eos.saveddata.fighter import Fighter
from eos.saveddata.cargo import Cargo
from eos.saveddata.implant import Implant
from eos.saveddata.booster import SideEffect
diff --git a/gui/additionsPane.py b/gui/additionsPane.py
index 61490671c..ec8617a4c 100644
--- a/gui/additionsPane.py
+++ b/gui/additionsPane.py
@@ -21,6 +21,7 @@ import wx
import gui.mainFrame
from gui.boosterView import BoosterView
from gui.droneView import DroneView
+from gui.fighterView import FighterView
from gui.cargoView import CargoView
from gui.implantView import ImplantView
from gui.projectedView import ProjectedView
@@ -57,6 +58,7 @@ class AdditionsPane(TogglePanel):
cargoImg = BitmapLoader.getImage("cargo_small", "gui")
self.notebook.AddPage(DroneView(self.notebook), "Drones", tabImage = droneImg, showClose = False)
+ self.notebook.AddPage(FighterView(self.notebook), "Fighters", tabImage = droneImg, showClose = False)
self.notebook.AddPage(CargoView(self.notebook), "Cargo", tabImage = cargoImg, showClose = False)
self.notebook.AddPage(ImplantView(self.notebook), "Implants", tabImage = implantImg, showClose = False)
self.notebook.AddPage(BoosterView(self.notebook), "Boosters", tabImage = boosterImg, showClose = False)
@@ -68,7 +70,7 @@ class AdditionsPane(TogglePanel):
self.notebook.AddPage(self.gangPage, "Fleet", tabImage = gangImg, showClose = False)
self.notebook.SetSelection(0)
- PANES = ["Drones", "Cargo", "Implants", "Boosters", "Projected", "Fleet"]
+ PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Fleet"]
def select(self, name):
self.notebook.SetSelection(self.PANES.index(name))
diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py
index 73ba66a56..a05064d24 100644
--- a/gui/builtinContextMenus/itemStats.py
+++ b/gui/builtinContextMenus/itemStats.py
@@ -16,7 +16,7 @@ class ItemStats(ContextMenu):
"implantItem", "boosterItem",
"skillItem", "projectedModule",
"projectedDrone", "projectedCharge",
- "itemStats")
+ "itemStats", "fighterItem")
def getText(self, itmContext, selection):
return "{0} Stats".format(itmContext if itmContext is not None else "Item")
diff --git a/gui/fighterView.py b/gui/fighterView.py
new file mode 100644
index 000000000..31a902b6b
--- /dev/null
+++ b/gui/fighterView.py
@@ -0,0 +1,251 @@
+#===============================================================================
+# Copyright (C) 2010 Diego Duclos
+#
+# This file is part of pyfa.
+#
+# pyfa is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pyfa 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with pyfa. If not, see .
+#===============================================================================
+
+import wx
+
+import service
+import gui.globalEvents as GE
+import gui.marketBrowser as mb
+import gui.display as d
+from gui.builtinViewColumns.state import State
+from gui.contextMenu import ContextMenu
+
+class FighterViewDrop(wx.PyDropTarget):
+ def __init__(self, dropFn):
+ wx.PyDropTarget.__init__(self)
+ self.dropFn = dropFn
+ # this is really transferring an EVE itemID
+ self.dropData = wx.PyTextDataObject()
+ self.SetDataObject(self.dropData)
+
+ def OnData(self, x, y, t):
+ if self.GetData():
+ data = self.dropData.GetText().split(':')
+ self.dropFn(x, y, data)
+ return t
+
+class FighterView(d.Display):
+ DEFAULT_COLS = [#"State",
+ #"Base Icon",
+ "Base Name",
+ # "prop:droneDps,droneBandwidth",
+ #"Max Range",
+ #"Miscellanea",
+ #"attr:maxVelocity",
+ "Price",]
+
+ def __init__(self, parent):
+ d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)
+
+ self.lastFitId = None
+
+ self.hoveredRow = None
+ self.hoveredColumn = None
+
+ self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
+ self.mainFrame.Bind(mb.ITEM_SELECTED, self.addItem)
+ self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
+ self.Bind(wx.EVT_LEFT_DOWN, self.click)
+ self.Bind(wx.EVT_KEY_UP, self.kbEvent)
+ self.Bind(wx.EVT_MOTION, self.OnMouseMove)
+ self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
+
+ if "__WXGTK__" in wx.PlatformInfo:
+ self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu)
+ else:
+ self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu)
+
+
+ self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
+ self.SetDropTarget(FighterViewDrop(self.handleDragDrop))
+
+ def OnLeaveWindow(self, event):
+ self.SetToolTip(None)
+ self.hoveredRow = None
+ self.hoveredColumn = None
+ event.Skip()
+
+ def OnMouseMove(self, event):
+ row, _, col = self.HitTestSubItem(event.Position)
+ if row != self.hoveredRow or col != self.hoveredColumn:
+ if self.ToolTip is not None:
+ self.SetToolTip(None)
+ else:
+ self.hoveredRow = row
+ self.hoveredColumn = col
+ if row != -1 and col != -1 and col < len(self.DEFAULT_COLS):
+ mod = self.fighters[self.GetItemData(row)]
+ if self.DEFAULT_COLS[col] == "Miscellanea":
+ tooltip = self.activeColumns[col].getToolTip(mod)
+ if tooltip is not None:
+ self.SetToolTipString(tooltip)
+ else:
+ self.SetToolTip(None)
+ else:
+ self.SetToolTip(None)
+ else:
+ self.SetToolTip(None)
+ event.Skip()
+
+ def kbEvent(self, event):
+ keycode = event.GetKeyCode()
+ if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE:
+ row = self.GetFirstSelected()
+ firstSel = row
+ if row != -1:
+ fighter = self.fighters[self.GetItemData(row)]
+ self.removeFighter(fighter)
+
+ event.Skip()
+
+ def startDrag(self, event):
+ row = event.GetIndex()
+ if row != -1:
+ data = wx.PyTextDataObject()
+ data.SetText("fighter:"+str(row))
+
+ dropSource = wx.DropSource(self)
+ dropSource.SetData(data)
+ res = dropSource.DoDragDrop()
+
+ def handleDragDrop(self, x, y, data):
+ '''
+ Handles dragging of items from various pyfa displays which support it
+
+ data is list with two indices:
+ data[0] is hard-coded str of originating source
+ data[1] is typeID or index of data we want to manipulate
+ '''
+ if data[0] == "fighter": # we want to merge fighters
+ srcRow = int(data[1])
+ dstRow, _ = self.HitTest((x, y))
+ if srcRow != -1 and dstRow != -1:
+ self._merge(srcRow, dstRow)
+ elif data[0] == "market":
+ wx.PostEvent(self.mainFrame, mb.ItemSelected(itemID=int(data[1])))
+
+ def _merge(self, src, dst):
+ print "merge fighters {} -> {}".format(src, dst)
+ raise NotImplementedError()
+
+ sFit = service.Fit.getInstance()
+ fitID = self.mainFrame.getActiveFit()
+ if sFit.mergeDrones(fitID, self.drones[src], self.drones[dst]):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+
+ '''
+ DRONE_ORDER = ('Light Scout Drones', 'Medium Scout Drones',
+ 'Heavy Attack Drones', 'Sentry Drones', 'Fighters',
+ 'Fighter Bombers', 'Combat Utility Drones',
+ 'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones',
+ 'Light Fighters', 'Heavy Fighters', 'Support Fighters')
+ def droneKey(self, drone):
+ sMkt = service.Market.getInstance()
+
+ groupName = sMkt.getMarketGroupByItem(drone.item).name
+ print groupName
+ return (self.DRONE_ORDER.index(groupName),
+ drone.item.name)
+ '''
+
+ def fitChanged(self, event):
+
+ #Clear list and get out if current fitId is None
+ if event.fitID is None and self.lastFitId is not None:
+ self.DeleteAllItems()
+ self.lastFitId = None
+ event.Skip()
+ return
+
+ sFit = service.Fit.getInstance()
+ fit = sFit.getFit(event.fitID)
+
+ self.original = fit.fighters if fit is not None else None
+ self.fighters = stuff = fit.fighters[:] if fit is not None else None
+
+ '''
+ if stuff is not None:
+ stuff.sort(key=self.droneKey)
+ '''
+
+ if event.fitID != self.lastFitId:
+ self.lastFitId = event.fitID
+
+ item = self.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_DONTCARE)
+
+ if item != -1:
+ self.EnsureVisible(item)
+
+ self.deselectItems()
+
+ self.update(stuff)
+ event.Skip()
+
+
+ def addItem(self, event):
+ sFit = service.Fit.getInstance()
+ fitID = self.mainFrame.getActiveFit()
+ trigger = sFit.addFighter(fitID, event.itemID)
+ if trigger:
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.additionsPane.select("Fighters")
+
+ event.Skip()
+
+ def removeItem(self, event):
+ row, _ = self.HitTest(event.Position)
+ if row != -1:
+ col = self.getColumn(event.Position)
+ if col != self.getColIndex(State):
+ fighter = self.fighters[self.GetItemData(row)]
+ self.removeFighter(fighter)
+
+ def removeFighter(self, fighter):
+ fitID = self.mainFrame.getActiveFit()
+ sFit = service.Fit.getInstance()
+ sFit.removeFighter(fitID, self.original.index(fighter))
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+
+ def click(self, event):
+ event.Skip()
+ row, _ = self.HitTest(event.Position)
+ if row != -1:
+ col = self.getColumn(event.Position)
+ if col == self.getColIndex(State):
+ fitID = self.mainFrame.getActiveFit()
+ sFit = service.Fit.getInstance()
+ fighter = self.fighters[row]
+ sFit.toggleFighter(fitID, self.original.index(fighter))
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+
+ def scheduleMenu(self, event):
+ event.Skip()
+ if self.getColumn(event.Position) != self.getColIndex(State):
+ wx.CallAfter(self.spawnMenu)
+
+ def spawnMenu(self):
+ sel = self.GetFirstSelected()
+ if sel != -1:
+ fighter = self.fighters[sel]
+
+ sMkt = service.Market.getInstance()
+ sourceContext = "fighterItem"
+ itemContext = sMkt.getCategoryByItem(fighter.item).name
+ menu = ContextMenu.getMenu((fighter,), (sourceContext, itemContext))
+ self.PopupMenu(menu)
diff --git a/service/fit.py b/service/fit.py
index 590651abe..e15faaf8a 100644
--- a/service/fit.py
+++ b/service/fit.py
@@ -615,6 +615,45 @@ class Fit(object):
self.recalc(fit)
return True
+ def addFighter(self, fitID, itemID):
+ if fitID is None:
+ return False
+
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+ if item.category.name == "Fighter":
+ fighter = None
+ '''
+ for d in fit.fighters.find(item):
+ if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]):
+ drone = d
+ break
+ '''
+ if fighter is None:
+ fighter = eos.types.Fighter(item)
+ if fighter.fits(fit) is True:
+ fit.fighters.append(fighter)
+ else:
+ return False
+ fighter.amount += 1
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+ else:
+ return False
+
+ def removeFighter(self, fitID, i):
+ fit = eos.db.getFit(fitID)
+ f = fit.fighters[i]
+ f.amount -= 1
+
+ if f.amount == 0:
+ del fit.fighters[i]
+
+ eos.db.commit()
+ self.recalc(fit)
+ return True
+
def addDrone(self, fitID, itemID):
if fitID is None:
return False
diff --git a/service/market.py b/service/market.py
index 39c700ce5..00323b78b 100644
--- a/service/market.py
+++ b/service/market.py
@@ -318,7 +318,7 @@ class Market():
("faction", frozenset((4, 3))),
("complex", frozenset((6,))),
("officer", frozenset((5,)))])
- self.SEARCH_CATEGORIES = ("Drone", "Module", "Subsystem", "Charge", "Implant", "Deployable")
+ self.SEARCH_CATEGORIES = ("Drone", "Module", "Subsystem", "Charge", "Implant", "Deployable", "Fighter")
self.ROOT_MARKET_GROUPS = (9, # Modules
1111, # Rigs
157, # Drones