Merge branch 'master' into singularity
This commit is contained in:
21
config.py
21
config.py
@@ -1,6 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
# TODO: move all logging back to pyfa.py main loop
|
||||||
|
# We moved it here just to avoid rebuilding windows skeleton for now (any change to pyfa.py needs it)
|
||||||
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
|
||||||
# Load variable overrides specific to distribution type
|
# Load variable overrides specific to distribution type
|
||||||
try:
|
try:
|
||||||
import configforced
|
import configforced
|
||||||
@@ -12,6 +17,8 @@ debug = False
|
|||||||
# Defines if our saveddata will be in pyfa root or not
|
# Defines if our saveddata will be in pyfa root or not
|
||||||
saveInRoot = False
|
saveInRoot = False
|
||||||
|
|
||||||
|
logLevel = logging.WARN
|
||||||
|
|
||||||
# Version data
|
# Version data
|
||||||
version = "1.12.1"
|
version = "1.12.1"
|
||||||
tag = "git"
|
tag = "git"
|
||||||
@@ -25,11 +32,6 @@ staticPath = None
|
|||||||
saveDB = None
|
saveDB = None
|
||||||
gameDB = None
|
gameDB = None
|
||||||
|
|
||||||
# TODO: move back to pyfa.py main loop
|
|
||||||
# We moved it here just to avoid rebuilding windows skeleton for now (any change to pyfa.py needs it)
|
|
||||||
import logging
|
|
||||||
logging.basicConfig()
|
|
||||||
|
|
||||||
def defPaths():
|
def defPaths():
|
||||||
global pyfaPath
|
global pyfaPath
|
||||||
global savePath
|
global savePath
|
||||||
@@ -55,6 +57,15 @@ def defPaths():
|
|||||||
savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")),
|
savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")),
|
||||||
sys.getfilesystemencoding())
|
sys.getfilesystemencoding())
|
||||||
|
|
||||||
|
format = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s'
|
||||||
|
logging.basicConfig(format=format, level=logLevel)
|
||||||
|
handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000, backupCount=3)
|
||||||
|
formatter = logging.Formatter(format)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
logging.getLogger('').addHandler(handler)
|
||||||
|
|
||||||
|
logging.info("Starting pyfa")
|
||||||
|
|
||||||
# Redirect stderr to file if we're requested to do so
|
# Redirect stderr to file if we're requested to do so
|
||||||
stderrToFile = getattr(configforced, "stderrToFile", None)
|
stderrToFile = getattr(configforced, "stderrToFile", None)
|
||||||
if stderrToFile is True:
|
if stderrToFile is True:
|
||||||
|
|||||||
23
eos/db/migrations/upgrade9.py
Normal file
23
eos/db/migrations/upgrade9.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
Migration 9
|
||||||
|
|
||||||
|
Effectively drops UNIQUE constraint from boosters table. SQLite does not support
|
||||||
|
this, so we have to copy the table to the updated schema and then rename it
|
||||||
|
"""
|
||||||
|
|
||||||
|
tmpTable = """
|
||||||
|
CREATE TABLE boostersTemp (
|
||||||
|
'ID' INTEGER NOT NULL,
|
||||||
|
'itemID' INTEGER,
|
||||||
|
'fitID' INTEGER NOT NULL,
|
||||||
|
'active' BOOLEAN,
|
||||||
|
PRIMARY KEY(ID),
|
||||||
|
FOREIGN KEY('fitID') REFERENCES fits ('ID')
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
saveddata_engine.execute(tmpTable)
|
||||||
|
saveddata_engine.execute("INSERT INTO boostersTemp (ID, itemID, fitID, active) SELECT ID, itemID, fitID, active FROM boosters")
|
||||||
|
saveddata_engine.execute("DROP TABLE boosters")
|
||||||
|
saveddata_engine.execute("ALTER TABLE boostersTemp RENAME TO boosters")
|
||||||
@@ -29,7 +29,7 @@ boosters_table = Table("boosters", saveddata_meta,
|
|||||||
Column("itemID", Integer),
|
Column("itemID", Integer),
|
||||||
Column("fitID", Integer, ForeignKey("fits.ID"), nullable = False),
|
Column("fitID", Integer, ForeignKey("fits.ID"), nullable = False),
|
||||||
Column("active", Boolean),
|
Column("active", Boolean),
|
||||||
UniqueConstraint("itemID", "fitID"))
|
)
|
||||||
|
|
||||||
activeSideEffects_table = Table("boostersActiveSideEffects", saveddata_meta,
|
activeSideEffects_table = Table("boostersActiveSideEffects", saveddata_meta,
|
||||||
Column("boosterID", ForeignKey("boosters.ID"), primary_key = True),
|
Column("boosterID", ForeignKey("boosters.ID"), primary_key = True),
|
||||||
|
|||||||
@@ -27,9 +27,7 @@ from eos.db.saveddata.drone import drones_table
|
|||||||
from eos.db.saveddata.cargo import cargo_table
|
from eos.db.saveddata.cargo import cargo_table
|
||||||
from eos.db.saveddata.implant import fitImplants_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, Cargo, Implant, Character, DamagePattern, TargetResists
|
||||||
from eos.effectHandlerHelpers import HandledModuleList, HandledDroneList, \
|
from eos.effectHandlerHelpers import *
|
||||||
HandledImplantBoosterList, HandledProjectedModList, HandledProjectedDroneList, \
|
|
||||||
HandledProjectedFitList, HandledCargoList
|
|
||||||
|
|
||||||
fits_table = Table("fits", saveddata_meta,
|
fits_table = Table("fits", saveddata_meta,
|
||||||
Column("ID", Integer, primary_key = True),
|
Column("ID", Integer, primary_key = True),
|
||||||
@@ -55,14 +53,16 @@ mapper(Fit, fits_table,
|
|||||||
"_Fit__projectedModules" : relation(Module, collection_class = HandledProjectedModList, cascade='all, delete, delete-orphan', single_parent=True,
|
"_Fit__projectedModules" : relation(Module, collection_class = HandledProjectedModList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||||
primaryjoin = and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)),
|
primaryjoin = and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)),
|
||||||
"owner" : relation(User, backref = "fits"),
|
"owner" : relation(User, backref = "fits"),
|
||||||
|
"itemID" : fits_table.c.shipID,
|
||||||
|
"shipID" : fits_table.c.shipID,
|
||||||
"_Fit__boosters" : relation(Booster, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', single_parent=True),
|
"_Fit__boosters" : relation(Booster, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', single_parent=True),
|
||||||
"_Fit__drones" : relation(Drone, collection_class = HandledDroneList, cascade='all, delete, delete-orphan', single_parent=True,
|
"_Fit__drones" : relation(Drone, collection_class = HandledDroneCargoList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||||
primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)),
|
primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)),
|
||||||
"_Fit__cargo" : relation(Cargo, collection_class = HandledCargoList, cascade='all, delete, delete-orphan', single_parent=True,
|
"_Fit__cargo" : relation(Cargo, collection_class = HandledDroneCargoList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||||
primaryjoin = and_(cargo_table.c.fitID == fits_table.c.ID)),
|
primaryjoin = and_(cargo_table.c.fitID == fits_table.c.ID)),
|
||||||
"_Fit__projectedDrones" : relation(Drone, collection_class = HandledProjectedDroneList, cascade='all, delete, delete-orphan', single_parent=True,
|
"_Fit__projectedDrones" : relation(Drone, collection_class = HandledProjectedDroneList, cascade='all, delete, delete-orphan', single_parent=True,
|
||||||
primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)),
|
primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)),
|
||||||
"_Fit__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', single_parent=True,
|
"_Fit__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', backref='fit', single_parent=True,
|
||||||
primaryjoin = fitImplants_table.c.fitID == fits_table.c.ID,
|
primaryjoin = fitImplants_table.c.fitID == fits_table.c.ID,
|
||||||
secondaryjoin = fitImplants_table.c.implantID == Implant.ID,
|
secondaryjoin = fitImplants_table.c.implantID == Implant.ID,
|
||||||
secondary = fitImplants_table),
|
secondary = fitImplants_table),
|
||||||
|
|||||||
@@ -185,6 +185,12 @@ def getFit(lookfor, eager=None):
|
|||||||
fit = saveddata_session.query(Fit).options(*eager).filter(Fit.ID == fitID).first()
|
fit = saveddata_session.query(Fit).options(*eager).filter(Fit.ID == fitID).first()
|
||||||
else:
|
else:
|
||||||
raise TypeError("Need integer as argument")
|
raise TypeError("Need integer as argument")
|
||||||
|
|
||||||
|
if fit.isInvalid:
|
||||||
|
with sd_lock:
|
||||||
|
removeInvalid([fit])
|
||||||
|
return None
|
||||||
|
|
||||||
return fit
|
return fit
|
||||||
|
|
||||||
@cachedQuery(Fleet, 1, "fleetID")
|
@cachedQuery(Fleet, 1, "fleetID")
|
||||||
@@ -244,9 +250,10 @@ def getFitsWithShip(shipID, ownerID=None, where=None, eager=None):
|
|||||||
filter = processWhere(filter, where)
|
filter = processWhere(filter, where)
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
fits = saveddata_session.query(Fit).options(*eager).filter(filter).all()
|
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
|
||||||
else:
|
else:
|
||||||
raise TypeError("ShipID must be integer")
|
raise TypeError("ShipID must be integer")
|
||||||
|
|
||||||
return fits
|
return fits
|
||||||
|
|
||||||
def getBoosterFits(ownerID=None, where=None, eager=None):
|
def getBoosterFits(ownerID=None, where=None, eager=None):
|
||||||
@@ -264,7 +271,8 @@ def getBoosterFits(ownerID=None, where=None, eager=None):
|
|||||||
filter = processWhere(filter, where)
|
filter = processWhere(filter, where)
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
fits = saveddata_session.query(Fit).options(*eager).filter(filter).all()
|
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
|
||||||
|
|
||||||
return fits
|
return fits
|
||||||
|
|
||||||
def countAllFits():
|
def countAllFits():
|
||||||
@@ -295,7 +303,8 @@ def countFitsWithShip(shipID, ownerID=None, where=None, eager=None):
|
|||||||
def getFitList(eager=None):
|
def getFitList(eager=None):
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
fits = saveddata_session.query(Fit).options(*eager).all()
|
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).all())
|
||||||
|
|
||||||
return fits
|
return fits
|
||||||
|
|
||||||
def getFleetList(eager=None):
|
def getFleetList(eager=None):
|
||||||
@@ -385,7 +394,8 @@ def searchFits(nameLike, where=None, eager=None):
|
|||||||
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
|
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
|
||||||
eager = processEager(eager)
|
eager = processEager(eager)
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
fits = saveddata_session.query(Fit).options(*eager).filter(filter).all()
|
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
|
||||||
|
|
||||||
return fits
|
return fits
|
||||||
|
|
||||||
def getSquadsIDsWithFitID(fitID):
|
def getSquadsIDsWithFitID(fitID):
|
||||||
@@ -406,6 +416,16 @@ def getProjectedFits(fitID):
|
|||||||
else:
|
else:
|
||||||
raise TypeError("Need integer as argument")
|
raise TypeError("Need integer as argument")
|
||||||
|
|
||||||
|
def removeInvalid(fits):
|
||||||
|
invalids = [f for f in fits if f.isInvalid]
|
||||||
|
|
||||||
|
if invalids:
|
||||||
|
map(fits.remove, invalids)
|
||||||
|
map(saveddata_session.delete, invalids)
|
||||||
|
saveddata_session.commit()
|
||||||
|
|
||||||
|
return fits
|
||||||
|
|
||||||
def add(stuff):
|
def add(stuff):
|
||||||
with sd_lock:
|
with sd_lock:
|
||||||
saveddata_session.add(stuff)
|
saveddata_session.add(stuff)
|
||||||
|
|||||||
@@ -17,8 +17,12 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
|
|
||||||
|
from sqlalchemy.orm.attributes import flag_modified
|
||||||
import eos.db
|
import eos.db
|
||||||
import eos.types
|
import eos.types
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class HandledList(list):
|
class HandledList(list):
|
||||||
def filteredItemPreAssign(self, filter, *args, **kwargs):
|
def filteredItemPreAssign(self, filter, *args, **kwargs):
|
||||||
@@ -101,6 +105,11 @@ class HandledList(list):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def remove(self, thing):
|
||||||
|
# We must flag it as modified, otherwise it not be removed from the database
|
||||||
|
flag_modified(thing, "itemID")
|
||||||
|
list.remove(self, thing)
|
||||||
|
|
||||||
class HandledModuleList(HandledList):
|
class HandledModuleList(HandledList):
|
||||||
def append(self, mod):
|
def append(self, mod):
|
||||||
emptyPosition = float("Inf")
|
emptyPosition = float("Inf")
|
||||||
@@ -115,10 +124,14 @@ class HandledModuleList(HandledList):
|
|||||||
del self[emptyPosition]
|
del self[emptyPosition]
|
||||||
mod.position = emptyPosition
|
mod.position = emptyPosition
|
||||||
HandledList.insert(self, emptyPosition, mod)
|
HandledList.insert(self, emptyPosition, mod)
|
||||||
|
if mod.isInvalid:
|
||||||
|
self.remove(mod)
|
||||||
return
|
return
|
||||||
|
|
||||||
mod.position = len(self)
|
mod.position = len(self)
|
||||||
HandledList.append(self, mod)
|
HandledList.append(self, mod)
|
||||||
|
if mod.isInvalid:
|
||||||
|
self.remove(mod)
|
||||||
|
|
||||||
def insert(self, index, mod):
|
def insert(self, index, mod):
|
||||||
mod.position = index
|
mod.position = index
|
||||||
@@ -149,128 +162,71 @@ class HandledModuleList(HandledList):
|
|||||||
if mod.getModifiedItemAttr("subSystemSlot") == slot:
|
if mod.getModifiedItemAttr("subSystemSlot") == slot:
|
||||||
del self[i]
|
del self[i]
|
||||||
|
|
||||||
class HandledDroneList(HandledList):
|
class HandledDroneCargoList(HandledList):
|
||||||
def find(self, item):
|
def find(self, item):
|
||||||
for d in self:
|
for o in self:
|
||||||
if d.item == item:
|
if o.item == item:
|
||||||
yield d
|
yield o
|
||||||
|
|
||||||
def findFirst(self, item):
|
def findFirst(self, item):
|
||||||
for d in self.find(item):
|
for o in self.find(item):
|
||||||
return d
|
return o
|
||||||
|
|
||||||
def append(self, drone):
|
def append(self, thing):
|
||||||
list.append(self, drone)
|
HandledList.append(self, thing)
|
||||||
|
|
||||||
def remove(self, drone):
|
if thing.isInvalid:
|
||||||
HandledList.remove(self, drone)
|
self.remove(thing)
|
||||||
|
|
||||||
def appendItem(self, item, amount = 1):
|
|
||||||
if amount < 1: ValueError("Amount of drones to add should be >= 1")
|
|
||||||
d = self.findFirst(item)
|
|
||||||
|
|
||||||
if d is None:
|
|
||||||
d = eos.types.Drone(item)
|
|
||||||
self.append(d)
|
|
||||||
|
|
||||||
d.amount += amount
|
|
||||||
return d
|
|
||||||
|
|
||||||
def removeItem(self, item, amount):
|
|
||||||
if amount < 1: ValueError("Amount of drones to remove should be >= 1")
|
|
||||||
d = self.findFirst(item)
|
|
||||||
if d is None: return
|
|
||||||
d.amount -= amount
|
|
||||||
if d.amount <= 0:
|
|
||||||
self.remove(d)
|
|
||||||
return None
|
|
||||||
|
|
||||||
return d
|
|
||||||
|
|
||||||
class HandledCargoList(HandledList):
|
|
||||||
# shameless copy of HandledDroneList
|
|
||||||
# I have no idea what this does, but I needed it
|
|
||||||
# @todo: investigate this
|
|
||||||
def find(self, item):
|
|
||||||
for d in self:
|
|
||||||
if d.item == item:
|
|
||||||
yield d
|
|
||||||
|
|
||||||
def findFirst(self, item):
|
|
||||||
for d in self.find(item):
|
|
||||||
return d
|
|
||||||
|
|
||||||
def append(self, cargo):
|
|
||||||
list.append(self, cargo)
|
|
||||||
|
|
||||||
def remove(self, cargo):
|
|
||||||
HandledList.remove(self, cargo)
|
|
||||||
|
|
||||||
def appendItem(self, item, qty = 1):
|
|
||||||
if qty < 1: ValueError("Amount of cargo to add should be >= 1")
|
|
||||||
d = self.findFirst(item)
|
|
||||||
|
|
||||||
if d is None:
|
|
||||||
d = eos.types.Cargo(item)
|
|
||||||
self.append(d)
|
|
||||||
|
|
||||||
d.qty += qty
|
|
||||||
return d
|
|
||||||
|
|
||||||
def removeItem(self, item, qty):
|
|
||||||
if qty < 1: ValueError("Amount of cargo to remove should be >= 1")
|
|
||||||
d = self.findFirst(item)
|
|
||||||
if d is None: return
|
|
||||||
d.qty -= qty
|
|
||||||
if d.qty <= 0:
|
|
||||||
self.remove(d)
|
|
||||||
return None
|
|
||||||
|
|
||||||
return d
|
|
||||||
|
|
||||||
class HandledImplantBoosterList(HandledList):
|
class HandledImplantBoosterList(HandledList):
|
||||||
def __init__(self):
|
def append(self, thing):
|
||||||
self.__slotCache = {}
|
if thing.isInvalid:
|
||||||
|
HandledList.append(self, thing)
|
||||||
|
self.remove(thing)
|
||||||
|
return
|
||||||
|
|
||||||
def append(self, implant):
|
# if needed, remove booster that was occupying slot
|
||||||
if self.__slotCache.has_key(implant.slot):
|
oldObj = next((m for m in self if m.slot == thing.slot), None)
|
||||||
raise ValueError("Implant/Booster slot already in use, remove the old one first or set replace = True")
|
if oldObj:
|
||||||
self.__slotCache[implant.slot] = implant
|
logging.info("Slot %d occupied with %s, replacing with %s", thing.slot, oldObj.item.name, thing.item.name)
|
||||||
HandledList.append(self, implant)
|
self.remove(oldObj)
|
||||||
|
|
||||||
def remove(self, implant):
|
HandledList.append(self, thing)
|
||||||
HandledList.remove(self, implant)
|
|
||||||
del self.__slotCache[implant.slot]
|
|
||||||
# While we deleted this implant, in edge case seems like not all references
|
|
||||||
# to it are removed and object still lives in session; forcibly remove it,
|
|
||||||
# or otherwise when adding the same booster twice booster's table (typeID, fitID)
|
|
||||||
# constraint will report database integrity error
|
|
||||||
# TODO: make a proper fix, probably by adjusting fit-boosters sqlalchemy relationships
|
|
||||||
eos.db.remove(implant)
|
|
||||||
|
|
||||||
def freeSlot(self, slot):
|
|
||||||
if hasattr(slot, "slot"):
|
|
||||||
slot = slot.slot
|
|
||||||
|
|
||||||
try:
|
|
||||||
implant = self.__slotCache[slot]
|
|
||||||
except KeyError:
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
self.remove(implant)
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
class HandledProjectedModList(HandledList):
|
class HandledProjectedModList(HandledList):
|
||||||
|
def append(self, proj):
|
||||||
|
if proj.isInvalid:
|
||||||
|
# we must include it before we remove it. doing it this way ensures
|
||||||
|
# rows and relationships in database are removed as well
|
||||||
|
HandledList.append(self, proj)
|
||||||
|
self.remove(proj)
|
||||||
|
return
|
||||||
|
|
||||||
|
proj.projected = True
|
||||||
|
isSystemEffect = proj.item.group.name == "Effect Beacon"
|
||||||
|
|
||||||
|
if isSystemEffect:
|
||||||
|
# remove other system effects - only 1 per fit plz
|
||||||
|
oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None)
|
||||||
|
|
||||||
|
if oldEffect:
|
||||||
|
logging.info("System effect occupied with %s, replacing with %s", oldEffect.item.name, proj.item.name)
|
||||||
|
self.remove(oldEffect)
|
||||||
|
|
||||||
|
HandledList.append(self, proj)
|
||||||
|
|
||||||
|
# Remove non-projectable modules
|
||||||
|
if not proj.item.isType("projected") and not isSystemEffect:
|
||||||
|
self.remove(proj)
|
||||||
|
|
||||||
|
class HandledProjectedDroneList(HandledDroneCargoList):
|
||||||
def append(self, proj):
|
def append(self, proj):
|
||||||
proj.projected = True
|
proj.projected = True
|
||||||
HandledList.append(self, proj)
|
HandledList.append(self, proj)
|
||||||
|
|
||||||
class HandledProjectedDroneList(HandledDroneList):
|
# Remove invalid or non-projectable drones
|
||||||
def append(self, proj):
|
if proj.isInvalid or not proj.item.isType("projected"):
|
||||||
proj.projected = True
|
self.remove(proj)
|
||||||
list.append(self, proj)
|
|
||||||
|
|
||||||
class HandledProjectedFitList(HandledList):
|
class HandledProjectedFitList(HandledList):
|
||||||
def append(self, proj):
|
def append(self, proj):
|
||||||
|
|||||||
@@ -20,36 +20,53 @@
|
|||||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||||
from eos.effectHandlerHelpers import HandledItem
|
from eos.effectHandlerHelpers import HandledItem
|
||||||
from sqlalchemy.orm import reconstructor, validates
|
from sqlalchemy.orm import reconstructor, validates
|
||||||
|
import eos.db
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Booster(HandledItem, ItemAttrShortcut):
|
class Booster(HandledItem, ItemAttrShortcut):
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
self.__slot = self.__calculateSlot(item)
|
|
||||||
self.itemID = item.ID
|
|
||||||
self.__item = item
|
self.__item = item
|
||||||
|
|
||||||
|
if self.isInvalid:
|
||||||
|
raise ValueError("Passed item is not a Booster")
|
||||||
|
|
||||||
|
self.itemID = item.ID if item is not None else None
|
||||||
self.active = True
|
self.active = True
|
||||||
self.build()
|
self.build()
|
||||||
|
|
||||||
@reconstructor
|
@reconstructor
|
||||||
def init(self):
|
def init(self):
|
||||||
|
"""Initialize a booster 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:
|
||||||
|
logger.error("Item (id: %d) does not exist", self.itemID)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.isInvalid:
|
||||||
|
logger.error("Item (id: %d) is not a Booser", self.itemID)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.build()
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
""" Build object. Assumes proper and valid item already set """
|
||||||
|
self.__sideEffects = []
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||||
self.__sideEffects = []
|
self.__slot = self.__calculateSlot(self.__item)
|
||||||
for effect in self.item.effects.itervalues():
|
|
||||||
|
for effect in self.__item.effects.itervalues():
|
||||||
if effect.isType("boosterSideEffect"):
|
if effect.isType("boosterSideEffect"):
|
||||||
s = SideEffect(self)
|
s = SideEffect(self)
|
||||||
s.effect = effect
|
s.effect = effect
|
||||||
s.active = effect.ID in self.__activeSideEffectIDs
|
s.active = effect.ID in self.__activeSideEffectIDs
|
||||||
self.__sideEffects.append(s)
|
self.__sideEffects.append(s)
|
||||||
|
|
||||||
def __fetchItemInfo(self):
|
|
||||||
import eos.db
|
|
||||||
self.__item = eos.db.getItem(self.itemID)
|
|
||||||
self.__slot = self.__calculateSlot(self.__item)
|
|
||||||
self.build()
|
|
||||||
|
|
||||||
def iterSideEffects(self):
|
def iterSideEffects(self):
|
||||||
return self.__sideEffects.__iter__()
|
return self.__sideEffects.__iter__()
|
||||||
|
|
||||||
@@ -62,23 +79,18 @@ class Booster(HandledItem, ItemAttrShortcut):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def itemModifiedAttributes(self):
|
def itemModifiedAttributes(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__itemModifiedAttributes
|
return self.__itemModifiedAttributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def slot(self):
|
def isInvalid(self):
|
||||||
if self.__item is None:
|
return self.__item is None or self.__item.group.name != "Booster"
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def slot(self):
|
||||||
return self.__slot
|
return self.__slot
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def item(self):
|
def item(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__item
|
return self.__item
|
||||||
|
|
||||||
def __calculateSlot(self, item):
|
def __calculateSlot(self, item):
|
||||||
|
|||||||
@@ -20,40 +20,45 @@
|
|||||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
||||||
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
||||||
from sqlalchemy.orm import validates, reconstructor
|
from sqlalchemy.orm import validates, reconstructor
|
||||||
|
import eos.db
|
||||||
|
import logging
|
||||||
|
|
||||||
# Cargo class copied from Implant class and hacked to make work. \o/
|
logger = logging.getLogger(__name__)
|
||||||
# @todo: clean me up, Scotty
|
|
||||||
class Cargo(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
class Cargo(HandledItem, ItemAttrShortcut):
|
||||||
|
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
|
"""Initialize cargo from the program"""
|
||||||
self.__item = item
|
self.__item = item
|
||||||
self.itemID = item.ID
|
self.itemID = item.ID if item is not None else None
|
||||||
self.amount = 0
|
self.amount = 0
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
self.__itemModifiedAttributes.original = self.item.attributes
|
self.__itemModifiedAttributes.original = item.attributes
|
||||||
|
|
||||||
@reconstructor
|
@reconstructor
|
||||||
def init(self):
|
def init(self):
|
||||||
|
"""Initialize cargo from the database and validate"""
|
||||||
self.__item = None
|
self.__item = None
|
||||||
|
|
||||||
def __fetchItemInfo(self):
|
if self.itemID:
|
||||||
import eos.db
|
self.__item = eos.db.getItem(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
|
||||||
|
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def itemModifiedAttributes(self):
|
def itemModifiedAttributes(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__itemModifiedAttributes
|
return self.__itemModifiedAttributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def item(self):
|
def isInvalid(self):
|
||||||
if self.__item is None:
|
return self.__item is None
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def item(self):
|
||||||
return self.__item
|
return self.__item
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
|||||||
@@ -20,81 +20,80 @@
|
|||||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
|
||||||
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
||||||
from sqlalchemy.orm import validates, reconstructor
|
from sqlalchemy.orm import validates, reconstructor
|
||||||
|
import eos.db
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||||
DAMAGE_TYPES = ("em", "kinetic", "explosive", "thermal")
|
DAMAGE_TYPES = ("em", "kinetic", "explosive", "thermal")
|
||||||
MINING_ATTRIBUTES = ("miningAmount",)
|
MINING_ATTRIBUTES = ("miningAmount",)
|
||||||
|
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
if item.category.name != "Drone":
|
"""Initialize a drone from the program"""
|
||||||
raise ValueError("Passed item is not a drone")
|
|
||||||
|
|
||||||
self.__item = item
|
self.__item = item
|
||||||
self.__charge = None
|
|
||||||
self.itemID = item.ID
|
if self.isInvalid:
|
||||||
|
raise ValueError("Passed item is not a Drone")
|
||||||
|
|
||||||
|
self.itemID = item.ID if item is not None else None
|
||||||
self.amount = 0
|
self.amount = 0
|
||||||
self.amountActive = 0
|
self.amountActive = 0
|
||||||
self.__dps = None
|
|
||||||
self.__volley = None
|
|
||||||
self.__miningyield = None
|
|
||||||
self.projected = False
|
self.projected = False
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.build()
|
||||||
self.itemModifiedAttributes.original = self.item.attributes
|
|
||||||
|
|
||||||
@reconstructor
|
@reconstructor
|
||||||
def init(self):
|
def init(self):
|
||||||
|
"""Initialize a drone 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 Drone", self.itemID)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.build()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
""" Build object. Assumes proper and valid item already set """
|
||||||
|
self.__charge = None
|
||||||
self.__dps = None
|
self.__dps = None
|
||||||
self.__volley = None
|
self.__volley = None
|
||||||
self.__miningyield = None
|
self.__miningyield = None
|
||||||
self.__item = None
|
|
||||||
self.__charge = None
|
|
||||||
|
|
||||||
def __fetchItemInfo(self):
|
|
||||||
import eos.db
|
|
||||||
self.__item = eos.db.getItem(self.itemID)
|
|
||||||
self.__charge = None
|
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
self.__itemModifiedAttributes.original = self.item.attributes
|
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||||
|
|
||||||
def __fetchChargeInfo(self):
|
|
||||||
chargeID = self.getModifiedItemAttr("entityMissileTypeID")
|
|
||||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||||
|
chargeID = self.getModifiedItemAttr("entityMissileTypeID")
|
||||||
if chargeID is not None:
|
if chargeID is not None:
|
||||||
import eos.db
|
|
||||||
charge = eos.db.getItem(int(chargeID))
|
charge = eos.db.getItem(int(chargeID))
|
||||||
self.__charge = charge
|
self.__charge = charge
|
||||||
|
self.__chargeModifiedAttributes.original = charge.attributes
|
||||||
self.chargeModifiedAttributes.original = charge.attributes
|
|
||||||
else:
|
|
||||||
self.__charge = 0
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def itemModifiedAttributes(self):
|
def itemModifiedAttributes(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__itemModifiedAttributes
|
return self.__itemModifiedAttributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chargeModifiedAttributes(self):
|
def chargeModifiedAttributes(self):
|
||||||
if self.__charge is None:
|
|
||||||
self.__fetchChargeInfo()
|
|
||||||
|
|
||||||
return self.__chargeModifiedAttributes
|
return self.__chargeModifiedAttributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def item(self):
|
def isInvalid(self):
|
||||||
if self.__item is None:
|
return self.__item is None or self.__item.category.name != "Drone"
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def item(self):
|
||||||
return self.__item
|
return self.__item
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def charge(self):
|
def charge(self):
|
||||||
if self.__charge is None:
|
return self.__charge
|
||||||
self.__fetchChargeInfo()
|
|
||||||
|
|
||||||
return self.__charge if self.__charge != 0 else None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dealsDamage(self):
|
def dealsDamage(self):
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
|
|
||||||
from eos.effectHandlerHelpers import HandledList, HandledModuleList, HandledDroneList, HandledImplantBoosterList, \
|
from eos.effectHandlerHelpers import *
|
||||||
HandledProjectedFitList, HandledProjectedModList, HandledProjectedDroneList, HandledCargoList
|
|
||||||
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
||||||
from sqlalchemy.orm import validates, reconstructor
|
from sqlalchemy.orm import validates, reconstructor
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
@@ -28,7 +27,11 @@ from math import sqrt, log, asinh
|
|||||||
from eos.types import Drone, Cargo, Ship, Character, State, Slot, Module, Implant, Booster, Skill
|
from eos.types import Drone, Cargo, Ship, Character, State, Slot, Module, Implant, Booster, Skill
|
||||||
from eos.saveddata.module import State
|
from eos.saveddata.module import State
|
||||||
from eos.saveddata.mode import Mode
|
from eos.saveddata.mode import Mode
|
||||||
|
import eos.db
|
||||||
import time
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
@@ -48,10 +51,14 @@ class Fit(object):
|
|||||||
|
|
||||||
PEAK_RECHARGE = 0.25
|
PEAK_RECHARGE = 0.25
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, ship=None, name=""):
|
||||||
|
"""Initialize a fit from the program"""
|
||||||
|
# use @mode.setter's to set __attr and IDs. This will set mode as well
|
||||||
|
self.ship = ship
|
||||||
|
|
||||||
self.__modules = HandledModuleList()
|
self.__modules = HandledModuleList()
|
||||||
self.__drones = HandledDroneList()
|
self.__drones = HandledDroneCargoList()
|
||||||
self.__cargo = HandledCargoList()
|
self.__cargo = HandledDroneCargoList()
|
||||||
self.__implants = HandledImplantBoosterList()
|
self.__implants = HandledImplantBoosterList()
|
||||||
self.__boosters = HandledImplantBoosterList()
|
self.__boosters = HandledImplantBoosterList()
|
||||||
self.__projectedFits = HandledProjectedFitList()
|
self.__projectedFits = HandledProjectedFitList()
|
||||||
@@ -59,23 +66,40 @@ class Fit(object):
|
|||||||
self.__projectedDrones = HandledProjectedDroneList()
|
self.__projectedDrones = HandledProjectedDroneList()
|
||||||
self.__character = None
|
self.__character = None
|
||||||
self.__owner = None
|
self.__owner = None
|
||||||
self.shipID = None
|
|
||||||
self.projected = False
|
self.projected = False
|
||||||
self.name = ""
|
self.name = name
|
||||||
self.fleet = None
|
|
||||||
self.boostsFits = set()
|
|
||||||
self.gangBoosts = None
|
|
||||||
self.timestamp = time.time()
|
self.timestamp = time.time()
|
||||||
self.ecmProjectedStr = 1
|
|
||||||
self.modeID = None
|
self.modeID = None
|
||||||
|
|
||||||
self.build()
|
self.build()
|
||||||
|
|
||||||
@reconstructor
|
@reconstructor
|
||||||
def init(self):
|
def init(self):
|
||||||
|
"""Initialize a fit from the database and validate"""
|
||||||
|
self.__ship = None
|
||||||
|
self.__mode = None
|
||||||
|
|
||||||
|
if self.shipID:
|
||||||
|
item = eos.db.getItem(self.shipID)
|
||||||
|
if item is None:
|
||||||
|
logger.error("Item (id: %d) does not exist", self.shipID)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.__ship = Ship(item)
|
||||||
|
except ValueError:
|
||||||
|
logger.error("Item (id: %d) is not a Ship", self.shipID)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.modeID and self.__ship:
|
||||||
|
item = eos.db.getItem(self.modeID)
|
||||||
|
# Don't need to verify if it's a proper item, as validateModeItem assures this
|
||||||
|
self.__mode = self.ship.validateModeItem(item)
|
||||||
|
|
||||||
self.build()
|
self.build()
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
from eos import db
|
|
||||||
self.__extraDrains = []
|
self.__extraDrains = []
|
||||||
self.__ehp = None
|
self.__ehp = None
|
||||||
self.__weaponDPS = None
|
self.__weaponDPS = None
|
||||||
@@ -100,11 +124,6 @@ class Fit(object):
|
|||||||
self.ecmProjectedStr = 1
|
self.ecmProjectedStr = 1
|
||||||
self.extraAttributes = ModifiedAttributeDict(self)
|
self.extraAttributes = ModifiedAttributeDict(self)
|
||||||
self.extraAttributes.original = self.EXTRA_ATTRIBUTES
|
self.extraAttributes.original = self.EXTRA_ATTRIBUTES
|
||||||
self.ship = Ship(db.getItem(self.shipID)) if self.shipID is not None else None
|
|
||||||
if self.ship is not None:
|
|
||||||
self.mode = self.ship.checkModeItem(db.getItem(self.modeID) if self.modeID else None)
|
|
||||||
else:
|
|
||||||
self.mode = None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def targetResists(self):
|
def targetResists(self):
|
||||||
@@ -128,13 +147,17 @@ class Fit(object):
|
|||||||
self.__ehp = None
|
self.__ehp = None
|
||||||
self.__effectiveTank = None
|
self.__effectiveTank = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isInvalid(self):
|
||||||
|
return self.__ship is None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mode(self):
|
def mode(self):
|
||||||
return self._mode
|
return self.__mode
|
||||||
|
|
||||||
@mode.setter
|
@mode.setter
|
||||||
def mode(self, mode):
|
def mode(self, mode):
|
||||||
self._mode = mode
|
self.__mode = mode
|
||||||
self.modeID = mode.item.ID if mode is not None else None
|
self.modeID = mode.item.ID if mode is not None else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -154,7 +177,7 @@ class Fit(object):
|
|||||||
self.__ship = ship
|
self.__ship = ship
|
||||||
self.shipID = ship.item.ID if ship is not None else None
|
self.shipID = ship.item.ID if ship is not None else None
|
||||||
# set mode of new ship
|
# set mode of new ship
|
||||||
self.mode = self.ship.checkModeItem(None) if ship is not None else None
|
self.mode = self.ship.validateModeItem(None) if ship is not None else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def drones(self):
|
def drones(self):
|
||||||
|
|||||||
@@ -20,46 +20,58 @@
|
|||||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||||
from eos.effectHandlerHelpers import HandledItem
|
from eos.effectHandlerHelpers import HandledItem
|
||||||
from sqlalchemy.orm import validates, reconstructor
|
from sqlalchemy.orm import validates, reconstructor
|
||||||
|
import eos.db
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Implant(HandledItem, ItemAttrShortcut):
|
class Implant(HandledItem, ItemAttrShortcut):
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
self.__slot = self.__calculateSlot(item)
|
|
||||||
self.__item = item
|
self.__item = item
|
||||||
self.itemID = item.ID
|
|
||||||
|
if self.isInvalid:
|
||||||
|
raise ValueError("Passed item is not an Implant")
|
||||||
|
|
||||||
|
self.itemID = item.ID if item is not None else None
|
||||||
self.active = True
|
self.active = True
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.build()
|
||||||
self.__itemModifiedAttributes.original = self.item.attributes
|
|
||||||
|
|
||||||
@reconstructor
|
@reconstructor
|
||||||
def init(self):
|
def init(self):
|
||||||
self.__item = None
|
self.__item = None
|
||||||
|
|
||||||
def __fetchItemInfo(self):
|
if self.itemID:
|
||||||
import eos.db
|
self.__item = eos.db.getItem(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 an Implant", self.itemID)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.build()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
""" Build object. Assumes proper and valid item already set """
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||||
self.__slot = self.__calculateSlot(self.__item)
|
self.__slot = self.__calculateSlot(self.__item)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def itemModifiedAttributes(self):
|
def itemModifiedAttributes(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__itemModifiedAttributes
|
return self.__itemModifiedAttributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def slot(self):
|
def isInvalid(self):
|
||||||
if self.__item is None:
|
return self.__item is None or self.__item.category.name != "Implant"
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def slot(self):
|
||||||
return self.__slot
|
return self.__slot
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def item(self):
|
def item(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__item
|
return self.__item
|
||||||
|
|
||||||
def __calculateSlot(self, item):
|
def __calculateSlot(self, item):
|
||||||
|
|||||||
@@ -19,36 +19,24 @@
|
|||||||
|
|
||||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||||
from eos.effectHandlerHelpers import HandledItem
|
from eos.effectHandlerHelpers import HandledItem
|
||||||
|
import eos.db
|
||||||
|
|
||||||
class Mode(ItemAttrShortcut, HandledItem):
|
class Mode(ItemAttrShortcut, HandledItem):
|
||||||
|
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
|
|
||||||
|
if item.group.name != "Ship Modifiers":
|
||||||
|
raise ValueError('Passed item "%s" (category: (%s)) is not a Ship Modifier'%(item.name, item.category.name))
|
||||||
|
|
||||||
self.__item = item
|
self.__item = item
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
|
|
||||||
if not isinstance(item, int):
|
|
||||||
self.__buildOriginal()
|
|
||||||
|
|
||||||
def __fetchItemInfo(self):
|
|
||||||
import eos.db
|
|
||||||
self.__item = eos.db.getItem(self.__item)
|
|
||||||
self.__buildOriginal()
|
|
||||||
|
|
||||||
def __buildOriginal(self):
|
|
||||||
self.__itemModifiedAttributes.original = self.item.attributes
|
self.__itemModifiedAttributes.original = self.item.attributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def item(self):
|
def item(self):
|
||||||
if isinstance(self.__item, int):
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__item
|
return self.__item
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def itemModifiedAttributes(self):
|
def itemModifiedAttributes(self):
|
||||||
if isinstance(self.__item, int):
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__itemModifiedAttributes
|
return self.__itemModifiedAttributes
|
||||||
|
|
||||||
# @todo: rework to fit only on t3 dessy
|
# @todo: rework to fit only on t3 dessy
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, C
|
|||||||
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
from eos.effectHandlerHelpers import HandledItem, HandledCharge
|
||||||
from eos.enum import Enum
|
from eos.enum import Enum
|
||||||
from eos.mathUtils import floorFloat
|
from eos.mathUtils import floorFloat
|
||||||
|
import eos.db
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class State(Enum):
|
class State(Enum):
|
||||||
OFFLINE = -1
|
OFFLINE = -1
|
||||||
@@ -49,95 +53,78 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
MINING_ATTRIBUTES = ("miningAmount", )
|
MINING_ATTRIBUTES = ("miningAmount", )
|
||||||
|
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
self.__item = item if item != None else 0
|
"""Initialize a module from the program"""
|
||||||
|
self.__item = item
|
||||||
|
|
||||||
|
if item is not None and self.isInvalid:
|
||||||
|
raise ValueError("Passed item is not a Module")
|
||||||
|
|
||||||
|
self.__charge = None
|
||||||
self.itemID = item.ID if item is not None else None
|
self.itemID = item.ID if item is not None else None
|
||||||
self.__charge = 0
|
|
||||||
self.projected = False
|
self.projected = False
|
||||||
self.state = State.ONLINE
|
self.state = State.ONLINE
|
||||||
|
self.build()
|
||||||
|
|
||||||
|
@reconstructor
|
||||||
|
def init(self):
|
||||||
|
"""Initialize a module from the database and validate"""
|
||||||
|
self.__item = None
|
||||||
|
self.__charge = 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:
|
||||||
|
logger.error("Item (id: %d) does not exist", self.itemID)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.isInvalid:
|
||||||
|
logger.error("Item (id: %d) is not a Module", self.itemID)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.chargeID:
|
||||||
|
self.__charge = eos.db.getItem(self.chargeID)
|
||||||
|
|
||||||
|
self.build()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
""" Builds internal module variables from both init's """
|
||||||
|
|
||||||
|
if self.__charge and self.__charge.category.name != "Charge":
|
||||||
|
self.__charge = None
|
||||||
|
|
||||||
self.__dps = None
|
self.__dps = None
|
||||||
self.__miningyield = None
|
self.__miningyield = None
|
||||||
self.__volley = None
|
self.__volley = None
|
||||||
self.__reloadTime = None
|
self.__reloadTime = None
|
||||||
self.__reloadForce = None
|
self.__reloadForce = None
|
||||||
self.__chargeCycles = None
|
self.__chargeCycles = None
|
||||||
|
self.__hardpoint = Hardpoint.NONE
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
self.__slot = None
|
|
||||||
|
|
||||||
if item != None:
|
|
||||||
self.__itemModifiedAttributes.original = item.attributes
|
|
||||||
self.__hardpoint = self.__calculateHardpoint(item)
|
|
||||||
self.__slot = self.__calculateSlot(item)
|
|
||||||
|
|
||||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||||
|
self.__slot = self.dummySlot # defaults to None
|
||||||
|
|
||||||
@reconstructor
|
if self.__item:
|
||||||
def init(self):
|
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||||
if self.dummySlot is None:
|
self.__hardpoint = self.__calculateHardpoint(self.__item)
|
||||||
self.__item = None
|
self.__slot = self.__calculateSlot(self.__item)
|
||||||
self.__charge = None
|
if self.__charge:
|
||||||
self.__volley = None
|
self.__chargeModifiedAttributes.original = self.__charge.attributes
|
||||||
self.__dps = None
|
|
||||||
self.__miningyield = None
|
|
||||||
self.__reloadTime = None
|
|
||||||
self.__reloadForce = None
|
|
||||||
self.__chargeCycles = None
|
|
||||||
else:
|
|
||||||
self.__slot = self.dummySlot
|
|
||||||
self.__item = 0
|
|
||||||
self.__charge = 0
|
|
||||||
self.__dps = 0
|
|
||||||
self.__miningyield = 0
|
|
||||||
self.__volley = 0
|
|
||||||
self.__reloadTime = 0
|
|
||||||
self.__reloadForce = None
|
|
||||||
self.__chargeCycles = 0
|
|
||||||
self.__hardpoint = Hardpoint.NONE
|
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
|
|
||||||
def __fetchItemInfo(self):
|
|
||||||
import eos.db
|
|
||||||
item = eos.db.getItem(self.itemID)
|
|
||||||
self.__item = item
|
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
self.__itemModifiedAttributes.original = item.attributes
|
|
||||||
self.__hardpoint = self.__calculateHardpoint(item)
|
|
||||||
self.__slot = self.__calculateSlot(item)
|
|
||||||
|
|
||||||
def __fetchChargeInfo(self):
|
|
||||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
if self.chargeID is not None:
|
|
||||||
import eos.db
|
|
||||||
charge = eos.db.getItem(self.chargeID)
|
|
||||||
self.__charge = charge
|
|
||||||
self.__chargeModifiedAttributes.original = charge.attributes
|
|
||||||
else:
|
|
||||||
self.__charge = 0
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def buildEmpty(cls, slot):
|
def buildEmpty(cls, slot):
|
||||||
empty = Module(None)
|
empty = Module(None)
|
||||||
empty.__slot = slot
|
empty.__slot = slot
|
||||||
empty.__hardpoint = Hardpoint.NONE
|
|
||||||
empty.__item = 0
|
|
||||||
empty.__charge = 0
|
|
||||||
empty.dummySlot = slot
|
empty.dummySlot = slot
|
||||||
empty.__itemModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
empty.__chargeModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
|
|
||||||
return empty
|
return empty
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def buildRack(cls, slot):
|
def buildRack(cls, slot):
|
||||||
empty = Rack(None)
|
empty = Rack(None)
|
||||||
empty.__slot = slot
|
empty.__slot = slot
|
||||||
empty.__hardpoint = Hardpoint.NONE
|
|
||||||
empty.__item = 0
|
|
||||||
empty.__charge = 0
|
|
||||||
empty.dummySlot = slot
|
empty.dummySlot = slot
|
||||||
empty.__itemModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
empty.__chargeModifiedAttributes = ModifiedAttributeDict()
|
|
||||||
|
|
||||||
return empty
|
return empty
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -146,11 +133,14 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def hardpoint(self):
|
def hardpoint(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__hardpoint
|
return self.__hardpoint
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isInvalid(self):
|
||||||
|
if self.isEmpty:
|
||||||
|
return False
|
||||||
|
return self.__item is None or (self.__item.category.name not in ("Module", "Subsystem") and self.__item.group.name != "Effect Beacon")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def numCharges(self):
|
def numCharges(self):
|
||||||
if self.charge is None:
|
if self.charge is None:
|
||||||
@@ -263,38 +253,23 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def slot(self):
|
def slot(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__slot
|
return self.__slot
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def itemModifiedAttributes(self):
|
def itemModifiedAttributes(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__itemModifiedAttributes
|
return self.__itemModifiedAttributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chargeModifiedAttributes(self):
|
def chargeModifiedAttributes(self):
|
||||||
if self.__charge is None:
|
|
||||||
self.__fetchChargeInfo()
|
|
||||||
|
|
||||||
return self.__chargeModifiedAttributes
|
return self.__chargeModifiedAttributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def item(self):
|
def item(self):
|
||||||
if self.__item is None:
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__item if self.__item != 0 else None
|
return self.__item if self.__item != 0 else None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def charge(self):
|
def charge(self):
|
||||||
if self.__charge is None:
|
|
||||||
self.__fetchChargeInfo()
|
|
||||||
|
|
||||||
return self.__charge if self.__charge != 0 else None
|
return self.__charge if self.__charge != 0 else None
|
||||||
|
|
||||||
@charge.setter
|
@charge.setter
|
||||||
@@ -516,7 +491,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
|
|
||||||
def getValidCharges(self):
|
def getValidCharges(self):
|
||||||
validCharges = set()
|
validCharges = set()
|
||||||
import eos.db
|
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i))
|
itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i))
|
||||||
if itemChargeGroup is not None:
|
if itemChargeGroup is not None:
|
||||||
|
|||||||
@@ -20,6 +20,10 @@
|
|||||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||||
from eos.effectHandlerHelpers import HandledItem
|
from eos.effectHandlerHelpers import HandledItem
|
||||||
from eos.saveddata.mode import Mode
|
from eos.saveddata.mode import Mode
|
||||||
|
import eos.db
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Ship(ItemAttrShortcut, HandledItem):
|
class Ship(ItemAttrShortcut, HandledItem):
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
@@ -28,33 +32,18 @@ class Ship(ItemAttrShortcut, HandledItem):
|
|||||||
raise ValueError('Passed item "%s" (category: (%s)) is not under Ship category'%(item.name, item.category.name))
|
raise ValueError('Passed item "%s" (category: (%s)) is not under Ship category'%(item.name, item.category.name))
|
||||||
|
|
||||||
self.__item = item
|
self.__item = item
|
||||||
|
self.__modeItems = self.__getModeItems()
|
||||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||||
self.__modeItems = self._getModeItems()
|
self.__itemModifiedAttributes.original = self.item.attributes
|
||||||
if not isinstance(item, int):
|
|
||||||
self.__buildOriginal()
|
|
||||||
|
|
||||||
self.commandBonus = 0
|
self.commandBonus = 0
|
||||||
|
|
||||||
def __fetchItemInfo(self):
|
|
||||||
import eos.db
|
|
||||||
self.__item = eos.db.getItem(self.__item)
|
|
||||||
self.__buildOriginal()
|
|
||||||
|
|
||||||
def __buildOriginal(self):
|
|
||||||
self.__itemModifiedAttributes.original = self.item.attributes
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def item(self):
|
def item(self):
|
||||||
if isinstance(self.__item, int):
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__item
|
return self.__item
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def itemModifiedAttributes(self):
|
def itemModifiedAttributes(self):
|
||||||
if isinstance(self.__item, int):
|
|
||||||
self.__fetchItemInfo()
|
|
||||||
|
|
||||||
return self.__itemModifiedAttributes
|
return self.__itemModifiedAttributes
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
@@ -67,25 +56,16 @@ class Ship(ItemAttrShortcut, HandledItem):
|
|||||||
if effect.runTime == runTime and effect.isType("passive"):
|
if effect.runTime == runTime and effect.isType("passive"):
|
||||||
effect.handler(fit, self, ("ship",))
|
effect.handler(fit, self, ("ship",))
|
||||||
|
|
||||||
def checkModeItem(self, item):
|
def validateModeItem(self, item):
|
||||||
"""
|
""" Checks if provided item is a valid mode """
|
||||||
Checks if provided item is a valid mode.
|
|
||||||
|
|
||||||
If ship has modes, and current item is not valid, return forced mode
|
|
||||||
else if mode is valid, return Mode
|
|
||||||
else if ship does not have modes, return None
|
|
||||||
|
|
||||||
@todo: rename this
|
|
||||||
"""
|
|
||||||
items = self.__modeItems
|
items = self.__modeItems
|
||||||
|
|
||||||
if items != None:
|
if items is not None:
|
||||||
if item == None or item not in items:
|
# if we have items, then we are in a tactical destroyer and must have a mode
|
||||||
# We have a tact dessy, but mode is None or not valid. Force new mode
|
if item is None or item not in items:
|
||||||
|
# If provided item is invalid mode, force new one
|
||||||
return Mode(items[0])
|
return Mode(items[0])
|
||||||
elif item in items:
|
return Mode(item)
|
||||||
# We have a valid mode
|
|
||||||
return Mode(item)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -96,21 +76,17 @@ class Ship(ItemAttrShortcut, HandledItem):
|
|||||||
def modes(self):
|
def modes(self):
|
||||||
return [Mode(item) for item in self.__modeItems] if self.__modeItems else None
|
return [Mode(item) for item in self.__modeItems] if self.__modeItems else None
|
||||||
|
|
||||||
def _getModeItems(self):
|
def __getModeItems(self):
|
||||||
"""
|
"""
|
||||||
Returns a list of valid mode items for ship. Note that this returns the
|
Returns a list of valid mode items for ship. Note that this returns the
|
||||||
valid Item objects, not the Mode objects. Returns None if not a
|
valid Item objects, not the Mode objects. Returns None if not a
|
||||||
t3 dessy
|
t3 dessy
|
||||||
"""
|
"""
|
||||||
# @todo: is there a better way to determine this that isn't hardcoded groupIDs?
|
if self.item.group.name != "Tactical Destroyer":
|
||||||
if self.item.groupID != 1305:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
modeGroupID = 1306
|
|
||||||
import eos.db
|
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
g = eos.db.getGroup(modeGroupID, eager=("items.icon", "items.attributes"))
|
g = eos.db.getGroup("Ship Modifiers", eager=("items.icon", "items.attributes"))
|
||||||
for item in g.items:
|
for item in g.items:
|
||||||
# Rely on name detection because race is not reliable
|
# Rely on name detection because race is not reliable
|
||||||
if item.name.lower().startswith(self.item.name.lower()):
|
if item.name.lower().startswith(self.item.name.lower()):
|
||||||
|
|||||||
223
eos/slotFill.py
223
eos/slotFill.py
@@ -1,223 +0,0 @@
|
|||||||
#===============================================================================
|
|
||||||
# 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#===============================================================================
|
|
||||||
|
|
||||||
from eos.types import Slot, Fit, Module, State
|
|
||||||
import random
|
|
||||||
import copy
|
|
||||||
import math
|
|
||||||
import bisect
|
|
||||||
import itertools
|
|
||||||
import time
|
|
||||||
|
|
||||||
class SlotFill(object):
|
|
||||||
def __init__(self, original, modules, attributeWeights=None, propertyWeights=None, specificWeights=None, defaultState = State.ACTIVE):
|
|
||||||
self.original = original
|
|
||||||
self.attributeWeights = attributeWeights or {}
|
|
||||||
self.propertyWeights = propertyWeights or {}
|
|
||||||
self.specificWeights = specificWeights or []
|
|
||||||
self.state = State.ACTIVE
|
|
||||||
self.modules = map(self.__newModule, modules)
|
|
||||||
|
|
||||||
def __newModule(self, item):
|
|
||||||
m = Module(item)
|
|
||||||
m.state = self.state
|
|
||||||
return m
|
|
||||||
|
|
||||||
def __getMetaParent(self, item):
|
|
||||||
metaGroup = item.metaGroup
|
|
||||||
return item if metaGroup is None else metaGroup.parent
|
|
||||||
|
|
||||||
def fitness(self, fit, chromosome):
|
|
||||||
modList = fit.modules
|
|
||||||
modAttr = fit.ship.getModifiedItemAttr
|
|
||||||
|
|
||||||
modList.extend(chromosome)
|
|
||||||
fit.clear()
|
|
||||||
fit.calculateModifiedAttributes()
|
|
||||||
|
|
||||||
if not fit.fits:
|
|
||||||
del modList[-len(chromosome):]
|
|
||||||
return 0
|
|
||||||
|
|
||||||
weight = 0
|
|
||||||
for attr, value in self.attributeWeights.iteritems():
|
|
||||||
weight += modAttr(attr) * (value if value >= 0 else 1.0 / -value)
|
|
||||||
|
|
||||||
for prop, value in self.propertyWeights.iteritems():
|
|
||||||
weight += getattr(fit, prop) * (value if value >= 0 else 1.0 / -value)
|
|
||||||
|
|
||||||
for specific in self.specificWeights:
|
|
||||||
weight += specific(fit)
|
|
||||||
|
|
||||||
totalVars = (fit.ship.getModifiedItemAttr("powerOutput"),
|
|
||||||
fit.ship.getModifiedItemAttr("cpuOutput"),
|
|
||||||
fit.ship.getModifiedItemAttr('upgradeCapacity'))
|
|
||||||
|
|
||||||
usedVars = (fit.pgUsed, fit.cpuUsed, fit.calibrationUsed)
|
|
||||||
|
|
||||||
total = 0
|
|
||||||
used = 0
|
|
||||||
for tv, uv in zip(totalVars, usedVars):
|
|
||||||
if uv > tv:
|
|
||||||
del modList[-len(chromosome):]
|
|
||||||
return 0
|
|
||||||
|
|
||||||
del modList[-len(chromosome):]
|
|
||||||
|
|
||||||
|
|
||||||
return weight
|
|
||||||
|
|
||||||
|
|
||||||
def run(self, elite = 0.05, crossoverChance = 0.8, slotMutationChance = 0.5, typeMutationChance = 0.5):
|
|
||||||
#Use a copy of the original for all our calcs. We don't want to damage it
|
|
||||||
fit = copy.deepcopy(self.original)
|
|
||||||
fit.unfill()
|
|
||||||
|
|
||||||
#First of all, lets check the number of slots we got to play with
|
|
||||||
chromLength = -1
|
|
||||||
slotAmounts = {}
|
|
||||||
for type in Slot.getTypes():
|
|
||||||
slot = Slot.getValue(type)
|
|
||||||
amount = fit.getSlotsFree(slot)
|
|
||||||
if amount > 0:
|
|
||||||
slotAmounts[slot] = amount
|
|
||||||
|
|
||||||
chromLength += amount
|
|
||||||
|
|
||||||
if not slotAmounts:
|
|
||||||
#Nothing to do, joy
|
|
||||||
return
|
|
||||||
|
|
||||||
slotModules = {}
|
|
||||||
metaModules = {}
|
|
||||||
|
|
||||||
for slotType in slotAmounts:
|
|
||||||
slotModules[slotType] = modules = []
|
|
||||||
|
|
||||||
for module in self.modules:
|
|
||||||
#Store the variations of each base for ease and speed
|
|
||||||
metaParent = self.__getMetaParent(module.item)
|
|
||||||
metaList = metaModules.get(metaParent)
|
|
||||||
if metaList is None:
|
|
||||||
metaList = metaModules[metaParent] = []
|
|
||||||
metaList.append(module)
|
|
||||||
|
|
||||||
#Sort stuff by slotType for ease and speed
|
|
||||||
slot = module.slot
|
|
||||||
if slot in slotModules:
|
|
||||||
slotModules[slot].append(module)
|
|
||||||
|
|
||||||
for slotType, modules in slotModules.iteritems():
|
|
||||||
if len(modules) == 0:
|
|
||||||
chromLength -= slotAmounts[slotType]
|
|
||||||
del slotAmounts[slotType]
|
|
||||||
|
|
||||||
#Now, we need an initial set, first thing to do is decide how big that set will be
|
|
||||||
setSize = 10
|
|
||||||
|
|
||||||
#Grab some variables locally for performance improvements
|
|
||||||
rchoice = random.choice
|
|
||||||
rrandom = random.random
|
|
||||||
rrandint = random.randint
|
|
||||||
bbisect = bisect.bisect
|
|
||||||
ccopy = copy.copy
|
|
||||||
|
|
||||||
#Get our list for storage of our chromosomes
|
|
||||||
chromosomes = []
|
|
||||||
|
|
||||||
# Helpers
|
|
||||||
weigher = lambda chromosome: (self.fitness(fit, chromosome), chromosome)
|
|
||||||
keyer = lambda info: info[0]
|
|
||||||
|
|
||||||
eliteCutout = int(math.floor(setSize * (1 - elite)))
|
|
||||||
lastEl = setSize - 1
|
|
||||||
|
|
||||||
#Generate our initial set entirely randomly
|
|
||||||
#Subtelies to take in mind:
|
|
||||||
# * modules of the same slotType are kept together for easy cross-overing
|
|
||||||
state = self.state
|
|
||||||
for _ in xrange(setSize):
|
|
||||||
chrom = []
|
|
||||||
for type, amount in slotAmounts.iteritems():
|
|
||||||
for _ in xrange(amount):
|
|
||||||
chrom.append(rchoice(slotModules[type]))
|
|
||||||
|
|
||||||
chromosomes.append(weigher(chrom))
|
|
||||||
|
|
||||||
#Sort our initial set
|
|
||||||
chromosomes.sort(key=keyer)
|
|
||||||
currentGeneration = chromosomes
|
|
||||||
|
|
||||||
#Yield the best result from our initial set, this is gonna be pretty bad
|
|
||||||
yield currentGeneration[lastEl]
|
|
||||||
|
|
||||||
#Setup's done, now we can actualy apply our genetic algorithm to optimize all this
|
|
||||||
while True:
|
|
||||||
moo = time.time()
|
|
||||||
#First thing we do, we're gonna be elitair
|
|
||||||
#Grab the top x%, we'll put em in the next generation
|
|
||||||
nextGeneration = []
|
|
||||||
for i in xrange(lastEl, eliteCutout - 1, -1):
|
|
||||||
nextGeneration.append(currentGeneration[i])
|
|
||||||
|
|
||||||
#Figure out our ratios to do our roulette wheel
|
|
||||||
fitnessList = map(keyer, currentGeneration)
|
|
||||||
totalFitness = float(sum(fitnessList))
|
|
||||||
|
|
||||||
curr = 0
|
|
||||||
ratios = []
|
|
||||||
for fitness in fitnessList:
|
|
||||||
curr += fitness
|
|
||||||
ratios.append(curr / (totalFitness or 1))
|
|
||||||
|
|
||||||
t = 0
|
|
||||||
#Do our pairing
|
|
||||||
for _ in xrange(0, eliteCutout):
|
|
||||||
# Crossover chance
|
|
||||||
mother = currentGeneration[bbisect(ratios, rrandom())][1]
|
|
||||||
father = currentGeneration[bbisect(ratios, rrandom())][1]
|
|
||||||
if rrandom() <= crossoverChance:
|
|
||||||
crosspoint = rrandint(0, chromLength)
|
|
||||||
luke = mother[:crosspoint] + father[crosspoint:]
|
|
||||||
else:
|
|
||||||
luke = father
|
|
||||||
|
|
||||||
#Chance for slot mutation
|
|
||||||
if rrandom() <= slotMutationChance:
|
|
||||||
target = rrandint(0, chromLength)
|
|
||||||
mod = luke[target]
|
|
||||||
luke[target] = rchoice(slotModules[mod.slot])
|
|
||||||
|
|
||||||
if rrandom() <= typeMutationChance:
|
|
||||||
#Mutation of an item to another one of the same type
|
|
||||||
target = rrandint(0, chromLength)
|
|
||||||
mod = luke[target]
|
|
||||||
vars = metaModules[self.__getMetaParent(mod.item)]
|
|
||||||
luke[target] = rchoice(vars)
|
|
||||||
|
|
||||||
tt = time.time()
|
|
||||||
nextGeneration.append(weigher(luke))
|
|
||||||
t += time.time() - tt
|
|
||||||
|
|
||||||
print "time spent weighing: ", t
|
|
||||||
|
|
||||||
nextGeneration.sort(key=keyer)
|
|
||||||
currentGeneration = nextGeneration
|
|
||||||
print "total time spent this iteration:", time.time() - moo
|
|
||||||
yield currentGeneration[lastEl]
|
|
||||||
@@ -36,7 +36,8 @@ class DroneViewDrop(wx.PyDropTarget):
|
|||||||
|
|
||||||
def OnData(self, x, y, t):
|
def OnData(self, x, y, t):
|
||||||
if self.GetData():
|
if self.GetData():
|
||||||
self.dropFn(x, y, int(self.dropData.GetText()))
|
data = self.dropData.GetText().split(':')
|
||||||
|
self.dropFn(x, y, data)
|
||||||
return t
|
return t
|
||||||
|
|
||||||
class DroneView(d.Display):
|
class DroneView(d.Display):
|
||||||
@@ -72,7 +73,7 @@ class DroneView(d.Display):
|
|||||||
|
|
||||||
|
|
||||||
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
|
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
|
||||||
self.SetDropTarget(DroneViewDrop(self.mergeDrones))
|
self.SetDropTarget(DroneViewDrop(self.handleDragDrop))
|
||||||
|
|
||||||
def OnLeaveWindow(self, event):
|
def OnLeaveWindow(self, event):
|
||||||
self.SetToolTip(None)
|
self.SetToolTip(None)
|
||||||
@@ -117,17 +118,27 @@ class DroneView(d.Display):
|
|||||||
row = event.GetIndex()
|
row = event.GetIndex()
|
||||||
if row != -1:
|
if row != -1:
|
||||||
data = wx.PyTextDataObject()
|
data = wx.PyTextDataObject()
|
||||||
data.SetText(str(self.GetItemData(row)))
|
data.SetText("drone:"+str(row))
|
||||||
|
|
||||||
dropSource = wx.DropSource(self)
|
dropSource = wx.DropSource(self)
|
||||||
dropSource.SetData(data)
|
dropSource.SetData(data)
|
||||||
res = dropSource.DoDragDrop()
|
res = dropSource.DoDragDrop()
|
||||||
|
|
||||||
def mergeDrones(self, x, y, itemID):
|
def handleDragDrop(self, x, y, data):
|
||||||
srcRow = self.FindItemData(-1,itemID)
|
'''
|
||||||
dstRow, _ = self.HitTest((x, y))
|
Handles dragging of items from various pyfa displays which support it
|
||||||
if srcRow != -1 and dstRow != -1:
|
|
||||||
self._merge(srcRow, dstRow)
|
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] == "drone": # we want to merge drones
|
||||||
|
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):
|
def _merge(self, src, dst):
|
||||||
sFit = service.Fit.getInstance()
|
sFit = service.Fit.getInstance()
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ class MainFrame(wx.Frame):
|
|||||||
|
|
||||||
self.marketBrowser = MarketBrowser(self.notebookBrowsers)
|
self.marketBrowser = MarketBrowser(self.notebookBrowsers)
|
||||||
self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage = marketImg, showClose = False)
|
self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage = marketImg, showClose = False)
|
||||||
|
self.marketBrowser.splitter.SetSashPosition(self.marketHeight)
|
||||||
|
|
||||||
self.shipBrowser = ShipBrowser(self.notebookBrowsers)
|
self.shipBrowser = ShipBrowser(self.notebookBrowsers)
|
||||||
self.notebookBrowsers.AddPage(self.shipBrowser, "Ships", tabImage = shipBrowserImg, showClose = False)
|
self.notebookBrowsers.AddPage(self.shipBrowser, "Ships", tabImage = shipBrowserImg, showClose = False)
|
||||||
@@ -159,8 +160,8 @@ class MainFrame(wx.Frame):
|
|||||||
self.notebookBrowsers.SetSelection(1)
|
self.notebookBrowsers.SetSelection(1)
|
||||||
|
|
||||||
self.splitter.SplitVertically(self.notebookBrowsers, self.FitviewAdditionsPanel)
|
self.splitter.SplitVertically(self.notebookBrowsers, self.FitviewAdditionsPanel)
|
||||||
self.splitter.SetMinimumPaneSize(204)
|
self.splitter.SetMinimumPaneSize(220)
|
||||||
self.splitter.SetSashPosition(300)
|
self.splitter.SetSashPosition(self.browserWidth)
|
||||||
|
|
||||||
cstatsSizer = wx.BoxSizer(wx.VERTICAL)
|
cstatsSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
|
||||||
@@ -227,7 +228,7 @@ class MainFrame(wx.Frame):
|
|||||||
|
|
||||||
|
|
||||||
def LoadMainFrameAttribs(self):
|
def LoadMainFrameAttribs(self):
|
||||||
mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 680, "wnd_maximized": False}
|
mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 680, "wnd_maximized": False, "browser_width": 300, "market_height": 0}
|
||||||
self.mainFrameAttribs = service.SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", mainFrameDefaultAttribs)
|
self.mainFrameAttribs = service.SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", mainFrameDefaultAttribs)
|
||||||
|
|
||||||
if self.mainFrameAttribs["wnd_maximized"]:
|
if self.mainFrameAttribs["wnd_maximized"]:
|
||||||
@@ -241,6 +242,9 @@ class MainFrame(wx.Frame):
|
|||||||
self.SetSize((width, height))
|
self.SetSize((width, height))
|
||||||
self.SetMinSize((mainFrameDefaultAttribs["wnd_width"], mainFrameDefaultAttribs["wnd_height"]))
|
self.SetMinSize((mainFrameDefaultAttribs["wnd_width"], mainFrameDefaultAttribs["wnd_height"]))
|
||||||
|
|
||||||
|
self.browserWidth = self.mainFrameAttribs["browser_width"]
|
||||||
|
self.marketHeight = self.mainFrameAttribs["market_height"]
|
||||||
|
|
||||||
def UpdateMainFrameAttribs(self):
|
def UpdateMainFrameAttribs(self):
|
||||||
if self.IsIconized():
|
if self.IsIconized():
|
||||||
return
|
return
|
||||||
@@ -250,6 +254,9 @@ class MainFrame(wx.Frame):
|
|||||||
self.mainFrameAttribs["wnd_height"] = height
|
self.mainFrameAttribs["wnd_height"] = height
|
||||||
self.mainFrameAttribs["wnd_maximized"] = self.IsMaximized()
|
self.mainFrameAttribs["wnd_maximized"] = self.IsMaximized()
|
||||||
|
|
||||||
|
self.mainFrameAttribs["browser_width"] = self.notebookBrowsers.GetSize()[0]
|
||||||
|
self.mainFrameAttribs["market_height"] = self.marketBrowser.marketView.GetSize()[1]
|
||||||
|
|
||||||
def SetActiveStatsWindow(self, wnd):
|
def SetActiveStatsWindow(self, wnd):
|
||||||
self.activeStatsWnd = wnd
|
self.activeStatsWnd = wnd
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class MarketBrowser(wx.Panel):
|
|||||||
self.itemView = ItemView(self.splitter, self)
|
self.itemView = ItemView(self.splitter, self)
|
||||||
|
|
||||||
self.splitter.SplitHorizontally(self.marketView, self.itemView)
|
self.splitter.SplitHorizontally(self.marketView, self.itemView)
|
||||||
self.splitter.SetMinimumPaneSize(250)
|
self.splitter.SetMinimumPaneSize(150)
|
||||||
|
|
||||||
# Setup our buttons for metaGroup selection
|
# Setup our buttons for metaGroup selection
|
||||||
# Same fix as for search box on macs,
|
# Same fix as for search box on macs,
|
||||||
|
|||||||
@@ -342,7 +342,6 @@ class ResistsEditorDlg(wx.Dialog):
|
|||||||
self.patternChanged()
|
self.patternChanged()
|
||||||
|
|
||||||
def showInput(self, bool):
|
def showInput(self, bool):
|
||||||
print self.namePicker.IsShown(), bool
|
|
||||||
if bool and not self.namePicker.IsShown():
|
if bool and not self.namePicker.IsShown():
|
||||||
self.ccResists.Hide()
|
self.ccResists.Hide()
|
||||||
self.namePicker.Show()
|
self.namePicker.Show()
|
||||||
|
|||||||
@@ -150,8 +150,8 @@ class Fit(object):
|
|||||||
return fit.modules[pos]
|
return fit.modules[pos]
|
||||||
|
|
||||||
def newFit(self, shipID, name=None):
|
def newFit(self, shipID, name=None):
|
||||||
fit = eos.types.Fit()
|
ship = eos.types.Ship(eos.db.getItem(shipID))
|
||||||
fit.ship = eos.types.Ship(eos.db.getItem(shipID))
|
fit = eos.types.Fit(ship)
|
||||||
fit.name = name if name is not None else "New %s" % fit.ship.item.name
|
fit.name = name if name is not None else "New %s" % fit.ship.item.name
|
||||||
fit.damagePattern = self.pattern
|
fit.damagePattern = self.pattern
|
||||||
fit.targetResists = self.targetResists
|
fit.targetResists = self.targetResists
|
||||||
@@ -275,7 +275,6 @@ class Fit(object):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
fit.implants.freeSlot(implant)
|
|
||||||
fit.implants.append(implant)
|
fit.implants.append(implant)
|
||||||
self.recalc(fit)
|
self.recalc(fit)
|
||||||
return True
|
return True
|
||||||
@@ -301,7 +300,6 @@ class Fit(object):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
fit.boosters.freeSlot(booster)
|
|
||||||
fit.boosters.append(booster)
|
fit.boosters.append(booster)
|
||||||
self.recalc(fit)
|
self.recalc(fit)
|
||||||
return True
|
return True
|
||||||
|
|||||||
Reference in New Issue
Block a user