Merge branch 'development'

Conflicts:
	eos/db/saveddata/queries.py
This commit is contained in:
blitzman
2017-01-10 22:42:01 -05:00
20 changed files with 179 additions and 1766 deletions

View File

@@ -8,7 +8,6 @@ __all__ = [
"booster",
"drone",
"implant",
"fleet",
"damagePattern",
"miscData",
"targetResists",

View File

@@ -0,0 +1,104 @@
# ===============================================================================
# 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 <http://www.gnu.org/licenses/>.
# ===============================================================================
import sqlalchemy
import logging
logger = logging.getLogger(__name__)
class DatabaseCleanup:
def __init__(self):
pass
@staticmethod
def OrphanedCharacterSkills(saveddata_engine):
# Finds and fixes database corruption issues.
logger.debug("Start databsae validation and cleanup.")
# Find orphaned character skills.
# This solves an issue where the character doesn't exist, but skills for that character do.
# See issue #917
try:
logger.debug("Running database cleanup for character skills.")
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills "
"WHERE characterID NOT IN (SELECT ID from characters)")
row = results.first()
if row and row['num']:
delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)")
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
except sqlalchemy.exc.DatabaseError:
logger.error("Failed to connect to database.")
@staticmethod
def OrphanedFitDamagePatterns(saveddata_engine):
# Find orphaned damage patterns.
# This solves an issue where the damage pattern doesn't exist, but fits reference the pattern.
# See issue #777
try:
logger.debug("Running database cleanup for orphaned damage patterns attached to fits.")
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL")
row = results.first()
if row and row['num']:
# Get Uniform damage pattern ID
query = saveddata_engine.execute("SELECT ID FROM damagePatterns WHERE name = 'Uniform'")
rows = query.fetchall()
if len(rows) == 0:
logger.error("Missing uniform damage pattern.")
elif len(rows) > 1:
logger.error("More than one uniform damage pattern found.")
else:
uniform_damage_pattern_id = rows[0]['ID']
update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? "
"WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL",
uniform_damage_pattern_id)
logger.error("Database corruption found. Cleaning up %d records.", update.rowcount)
except sqlalchemy.exc.DatabaseError:
logger.error("Failed to connect to database.")
@staticmethod
def OrphanedFitCharacterIDs(saveddata_engine):
# Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern.
try:
logger.debug("Running database cleanup for orphaned characters attached to fits.")
results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL")
row = results.first()
if row['num']:
# Get All 5 character ID
query = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'")
rows = query.fetchall()
if len(rows) == 0:
logger.error("Missing 'All 5' character.")
elif len(rows) > 1:
logger.error("More than one 'All 5' character found.")
else:
all5_id = rows[0]['ID']
update = saveddata_engine.execute("UPDATE 'fits' SET 'characterID' = ? "
"WHERE characterID not in (select ID from characters) OR characterID IS NULL",
all5_id)
logger.error("Database corruption found. Cleaning up %d records.", update.rowcount)
except sqlalchemy.exc.DatabaseError:
logger.error("Failed to connect to database.")

View File

@@ -1,65 +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 sqlalchemy import Table, Column, Integer, ForeignKey, String
from sqlalchemy.orm import mapper, relation
from eos.db import saveddata_meta
from eos.db.saveddata.fit import fits_table
from eos.types import Fleet, Wing, Squad, Fit
gangs_table = Table("gangs", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("leaderID", ForeignKey("fits.ID")),
Column("boosterID", ForeignKey("fits.ID")),
Column("name", String))
wings_table = Table("wings", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("gangID", ForeignKey("gangs.ID")),
Column("boosterID", ForeignKey("fits.ID")),
Column("leaderID", ForeignKey("fits.ID")))
squads_table = Table("squads", saveddata_meta,
Column("ID", Integer, primary_key=True),
Column("wingID", ForeignKey("wings.ID")),
Column("leaderID", ForeignKey("fits.ID")),
Column("boosterID", ForeignKey("fits.ID")))
squadmembers_table = Table("squadmembers", saveddata_meta,
Column("squadID", ForeignKey("squads.ID"), primary_key=True),
Column("memberID", ForeignKey("fits.ID"), primary_key=True))
mapper(Fleet, gangs_table,
properties={"wings": relation(Wing, backref="gang"),
"leader": relation(Fit, primaryjoin=gangs_table.c.leaderID == fits_table.c.ID),
"booster": relation(Fit, primaryjoin=gangs_table.c.boosterID == fits_table.c.ID)})
mapper(Wing, wings_table,
properties={"squads": relation(Squad, backref="wing"),
"leader": relation(Fit, primaryjoin=wings_table.c.leaderID == fits_table.c.ID),
"booster": relation(Fit, primaryjoin=wings_table.c.boosterID == fits_table.c.ID)})
mapper(Squad, squads_table,
properties={"leader": relation(Fit, primaryjoin=squads_table.c.leaderID == fits_table.c.ID),
"booster": relation(Fit, primaryjoin=squads_table.c.boosterID == fits_table.c.ID),
"members": relation(Fit,
primaryjoin=squads_table.c.ID == squadmembers_table.c.squadID,
secondaryjoin=squadmembers_table.c.memberID == fits_table.c.ID,
secondary=squadmembers_table)})

View File

@@ -17,14 +17,12 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
from sqlalchemy.sql import and_
from eos.db import saveddata_session, sd_lock
from eos.db.saveddata.fit import projectedFits_table
from eos.db.saveddata.fleet import squadmembers_table
from eos.db.util import processEager, processWhere
from eos.types import *
from eos.db import saveddata_session, sd_lock
from eos.types import *
from eos.db.saveddata.fit import projectedFits_table
from sqlalchemy.sql import and_
import eos.config
configVal = getattr(eos.config, "saveddataCache", None)
@@ -212,52 +210,6 @@ def getFit(lookfor, eager=None):
return fit
@cachedQuery(Fleet, 1, "fleetID")
def getFleet(fleetID, eager=None):
if isinstance(fleetID, int):
if eager is None:
with sd_lock:
fleet = saveddata_session.query(Fleet).get(fleetID)
else:
eager = processEager(eager)
with sd_lock:
fleet = saveddata_session.query(Fleet).options(*eager).filter(Fleet.ID == fleetID).first()
else:
raise TypeError("Need integer as argument")
return fleet
@cachedQuery(Wing, 1, "wingID")
def getWing(wingID, eager=None):
if isinstance(wingID, int):
if eager is None:
with sd_lock:
wing = saveddata_session.query(Wing).get(wingID)
else:
eager = processEager(eager)
with sd_lock:
wing = saveddata_session.query(Wing).options(*eager).filter(Wing.ID == wingID).first()
else:
raise TypeError("Need integer as argument")
return wing
@cachedQuery(Squad, 1, "squadID")
def getSquad(squadID, eager=None):
if isinstance(squadID, int):
if eager is None:
with sd_lock:
squad = saveddata_session.query(Squad).get(squadID)
else:
eager = processEager(eager)
with sd_lock:
squad = saveddata_session.query(Squad).options(*eager).filter(Fleet.ID == squadID).first()
else:
raise TypeError("Need integer as argument")
return squad
def getFitsWithShip(shipID, ownerID=None, where=None, eager=None):
"""
Get all the fits using a certain ship.
@@ -341,14 +293,6 @@ def getFitList(eager=None):
return fits
def getFleetList(eager=None):
eager = processEager(eager)
with sd_lock:
fleets = saveddata_session.query(Fleet).options(*eager).all()
return fleets
@cachedQuery(Price, 1, "typeID")
def getPrice(typeID):
if isinstance(typeID, int):
@@ -472,18 +416,6 @@ def searchFits(nameLike, where=None, eager=None):
return fits
def getSquadsIDsWithFitID(fitID):
if isinstance(fitID, int):
with sd_lock:
squads = saveddata_session.query(squadmembers_table.c.squadID).filter(
squadmembers_table.c.memberID == fitID).all()
squads = tuple(entry[0] for entry in squads)
return squads
else:
raise TypeError("Need integer as argument")
def getProjectedFits(fitID):
if isinstance(fitID, int):
with sd_lock:

View File

@@ -3,10 +3,28 @@
# Used by:
# Modules from group: Missile Launcher Heavy (12 of 12)
# Modules from group: Missile Launcher Rocket (15 of 15)
# Modules named like: Launcher (153 of 153)
type = 'active'
# Modules named like: Launcher (151 of 151)
type = 'active', "projected"
def handler(fit, module, context):
def handler(fit, src, context):
# Set reload time to 10 seconds
module.reloadTime = 10000
src.reloadTime = 10000
if "projected" in context:
if src.item.group.name == 'Missile Launcher Bomb':
# Bomb Launcher Cooldown Timer
moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay")
# Void and Focused Void Bombs
neutAmount = src.getModifiedChargeAttr("energyNeutralizerAmount")
if moduleReactivationDelay and neutAmount:
fit.addDrain(src, moduleReactivationDelay, neutAmount, 0)
# Lockbreaker Bombs
ecmStrengthBonus = src.getModifiedChargeAttr("scan{0}StrengthBonus".format(fit.scanType))
if ecmStrengthBonus:
strModifier = 1 - ecmStrengthBonus / fit.scanStrength
fit.ecmProjectedStr *= strModifier

View File

@@ -134,7 +134,6 @@ class Fit(object):
self.__capRecharge = None
self.__calculatedTargets = []
self.factorReload = False
self.fleet = None
self.boostsFits = set()
self.gangBoosts = None
self.ecmProjectedStr = 1
@@ -632,14 +631,6 @@ class Fit(object):
groups = ("Energy Weapon", "Hybrid Weapon")
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True)
# if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \
# (effect.isType("active") and thing.state >= State.ACTIVE):
# # Run effect, and get proper bonuses applied
# try:
# self.register(thing)
# effect.handler(self, thing, context)
# except:
# pass
else:
# Run effect, and get proper bonuses applied
try:
@@ -665,7 +656,6 @@ class Fit(object):
# Don't inspect this, we genuinely want to reassign self
# noinspection PyMethodFirstArgAssignment
self = copy.deepcopy(self)
self.fleet = copied.fleet
logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self)
# we delete the fit because when we copy a fit, flush() is
# called to properly handle projection updates. However, we do
@@ -678,24 +668,6 @@ class Fit(object):
continue
fit.calculateModifiedAttributes(self, True)
#
# for thing in chain(fit.modules, fit.implants, fit.character.skills, (fit.ship,)):
# if thing.item is None:
# continue
# for effect in thing.item.effects.itervalues():
# # And check if it actually has gang boosting effects
# if effect.isType("gang"):
# effect.handler(self, thing, ("commandRun"))
# if self.fleet is not None and withBoosters is True:
# logger.debug("Fleet is set, gathering gang boosts")
#
# self.gangBoosts = self.fleet.recalculateLinear(withBoosters=withBoosters)
#
# timer.checkpoint("Done calculating gang boosts for %r"%self)
# elif self.fleet is None:
# self.gangBoosts = None
# If we're not explicitly asked to project fit onto something,
# set self as target fit

View File

@@ -1,341 +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 copy import deepcopy
from itertools import chain
from eos.types import Skill, Module, Ship
class Fleet(object):
def calculateModifiedAttributes(self):
# Make sure ALL fits in the gang have been calculated
for c in chain(self.wings, (self.leader,)):
if c is not None:
c.calculateModifiedAttributes()
leader = self.leader
self.booster = booster = self.booster if self.booster is not None else leader
self.broken = False
self.store = store = Store()
store.set(booster, "fleet")
# Go all the way down for each subtree we have.
for wing in self.wings:
wing.calculateGangBonusses(store)
# Check skill requirements and wing amount to see if we break or not
if len(self.wings) == 0 or leader is None or leader.character is None or leader.character.getSkill(
"Fleet Command").level < len(self.wings):
self.broken = True
# Now calculate our own if we aren't broken
if not self.broken:
# We only get our own bonuses *Sadface*
store.apply(leader, "fleet")
def recalculateLinear(self, withBoosters=True, dirtyStorage=None):
self.store = Store()
self.linearBoosts = {}
if withBoosters is True:
if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill(
"Fleet Command").level >= 1:
self.leader.boostsFits.add(self.wings[0].squads[0].members[0].ID)
self.leader.calculateModifiedAttributes()
self.store.set(self.leader, "squad", clearingUpdate=True)
else:
self.store = Store()
if self.leader is not None:
try:
self.leader.boostsFits.remove(self.wings[0].squads[0].members[0].ID)
except KeyError:
pass
self.wings[0].recalculateLinear(self.store, withBoosters=withBoosters, dirtyStorage=dirtyStorage)
return self.linearBoosts
def count(self):
total = 0
for wing in self.wings:
total += wing.count()
return total
def extend(self):
self.wings.append(Wing())
def __deepcopy__(self, memo):
copy = Fleet()
copy.name = self.name
copy.booster = deepcopy(self.booster)
copy.leader = deepcopy(self.leader)
for wing in self.wings:
copy.wings.append(deepcopy(wing))
return copy
class Wing(object):
def calculateModifiedAttributes(self):
for c in chain(self.squads, (self.leader,)):
if c is not None:
c.calculateModifiedAttributes()
def calculateGangBonusses(self, store):
self.broken = False
leader = self.leader
self.booster = booster = self.booster if self.booster is not None else leader
store.set(booster, "wing")
# ALWAYS move down
for squad in self.squads:
squad.calculateGangBonusses(store)
# Check skill requirements and squad amount to see if we break or not
if len(self.squads) == 0 or leader is None or leader.character is None or leader.character.getSkill(
"Wing Command").level < len(self.squads):
self.broken = True
# Check if we aren't broken, if we aren't, boost
if not self.broken:
store.apply(leader, "wing")
else:
# We broke, don't go up
self.gang.broken = True
def recalculateLinear(self, store, withBoosters=True, dirtyStorage=None):
if withBoosters is True:
if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill(
"Wing Command").level >= 1:
self.leader.boostsFits.add(self.squads[0].members[0].ID)
self.leader.calculateModifiedAttributes()
store.set(self.leader, "squad", clearingUpdate=False)
else:
store = Store()
if self.gang.leader is not None:
try:
self.gang.leader.boostsFits.remove(self.squads[0].members[0].ID)
except KeyError:
pass
if self.leader is not None:
try:
self.leader.boostsFits.remove(self.squads[0].members[0].ID)
except KeyError:
pass
self.squads[0].recalculateLinear(store, withBoosters=withBoosters, dirtyStorage=dirtyStorage)
def count(self):
total = 0 if self.leader is None else 1
for squad in self.squads:
total += squad.count()
return total
def extend(self):
self.squads.append(Squad())
def __deepcopy__(self, memo):
copy = Wing()
copy.booster = deepcopy(self.booster)
copy.leader = deepcopy(self.leader)
for squad in self.squads:
copy.squads.append(deepcopy(squad))
return copy
class Squad(object):
def calculateModifiedAttributes(self):
for member in self.members:
member.calculateModifiedAttributes()
def calculateGangBonusses(self, store):
self.broken = False
leader = self.leader
self.booster = booster = self.booster if self.booster is not None else leader
store.set(booster, "squad")
# Check skill requirements and squad size to see if we break or not
if len(self.members) <= 0 or leader is None or leader.character is None or leader.character.getSkill(
"Leadership").level * 2 < len(self.members):
self.broken = True
if not self.broken:
for member in self.members:
store.apply(member, "squad")
else:
self.wing.broken = True
def recalculateLinear(self, store, withBoosters=True, dirtyStorage=None):
if withBoosters is True:
if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill(
"Leadership").level >= 1:
self.leader.boostsFits.add(self.members[0].ID)
self.leader.calculateModifiedAttributes(dirtyStorage=dirtyStorage)
store.set(self.leader, "squad", clearingUpdate=False)
else:
store = Store()
if self.leader is not None:
try:
self.leader.boostsFits.remove(self.members[0].ID)
except KeyError:
pass
if self.wing.leader is not None:
try:
self.wing.leader.boostsFits.remove(self.members[0].ID)
except KeyError:
pass
if self.wing.gang.leader is not None:
try:
self.wing.gang.leader.boostsFits.remove(self.members[0].ID)
except KeyError:
pass
if getattr(self.wing.gang, "linearBoosts", None) is None:
self.wing.gang.linearBoosts = {}
dict = store.bonuses["squad"]
for boostedAttr, boostInfoList in dict.iteritems():
for boostInfo in boostInfoList:
effect, thing = boostInfo
# Get current boost value for given attribute, use 0 as fallback if
# no boosts applied yet
currBoostAmount = self.wing.gang.linearBoosts.get(boostedAttr, (0,))[0]
# Attribute name which is used to get boost value
newBoostAttr = effect.getattr("gangBonus") or "commandBonus"
# Get boost amount for current boost
newBoostAmount = thing.getModifiedItemAttr(newBoostAttr) or 0
# Skill used to modify the gang bonus (for purposes of comparing old vs new)
newBoostSkill = effect.getattr("gangBonusSkill")
# If skill takes part in gang boosting, multiply by skill level
if type(thing) == Skill:
newBoostAmount *= thing.level
# boost the gang bonus based on skill noted in effect file
if newBoostSkill:
newBoostAmount *= thing.parent.character.getSkill(newBoostSkill).level
# If new boost is more powerful, replace older one with it
if abs(newBoostAmount) > abs(currBoostAmount):
self.wing.gang.linearBoosts[boostedAttr] = (newBoostAmount, boostInfo)
def count(self):
return len(self.members)
def __deepcopy__(self, memo):
copy = Squad()
copy.booster = deepcopy(self.booster)
copy.leader = deepcopy(self.leader)
for member in self.members:
copy.members.append(deepcopy(member))
return copy
class Store(object):
def __init__(self):
# Container for gang boosters and their respective bonuses, three-layered
self.bonuses = {}
for dictType in ("fleet", "wing", "squad"):
self.bonuses[dictType] = {}
# Container for boosted fits and corresponding boosts applied onto them
self.boosts = {}
def set(self, fitBooster, layer, clearingUpdate=True):
"""Add all gang boosts of given fit for given layer to boost store"""
if fitBooster is None:
return
# This dict contains all bonuses for specified layer
dict = self.bonuses[layer]
if clearingUpdate is True:
# Clear existing bonuses
dict.clear()
# Go through everything which can be used as gang booster
for thing in chain(fitBooster.modules, fitBooster.implants, fitBooster.character.skills, (fitBooster.ship,)):
if thing.item is None:
continue
for effect in thing.item.effects.itervalues():
# And check if it actually has gang boosting effects
if effect.isType("gang"):
# Attribute which is boosted
boostedAttr = effect.getattr("gangBoost")
# List which contains all bonuses for given attribute for given layer
l = dict.get(boostedAttr)
# If there was no list, create it
if l is None:
l = dict[boostedAttr] = []
# And append effect which is used to boost stuff and carrier of this effect
l.append((effect, thing))
contextMap = {Skill: "skill",
Ship: "ship",
Module: "module"}
def apply(self, fitBoosted, layer):
"""Applies all boosts onto given fit for given layer"""
if fitBoosted is None:
return
# Boosts dict contains all bonuses applied onto given fit
self.boosts[fitBoosted] = boosts = {}
# Go through all bonuses for given layer, and find highest one per boosted attribute
for currLayer in ("fleet", "wing", "squad"):
# Dictionary with boosts for given layer
dict = self.bonuses[currLayer]
for boostedAttr, boostInfoList in dict.iteritems():
for boostInfo in boostInfoList:
effect, thing = boostInfo
# Get current boost value for given attribute, use 0 as fallback if
# no boosts applied yet
currBoostAmount = boosts.get(boostedAttr, (0,))[0]
# Attribute name which is used to get boost value
newBoostAttr = effect.getattr("gangBonus") or "commandBonus"
# Get boost amount for current boost
newBoostAmount = thing.getModifiedItemAttr(newBoostAttr) or 0
# Skill used to modify the gang bonus (for purposes of comparing old vs new)
newBoostSkill = effect.getattr("gangBonusSkill")
# If skill takes part in gang boosting, multiply by skill level
if type(thing) == Skill:
newBoostAmount *= thing.level
# boost the gang bonus based on skill noted in effect file
if newBoostSkill:
newBoostAmount *= thing.parent.character.getSkill(newBoostSkill).level
# If new boost is more powerful, replace older one with it
if abs(newBoostAmount) > abs(currBoostAmount):
boosts[boostedAttr] = (newBoostAmount, boostInfo)
# Don't look further down then current layer, wing commanders don't get squad bonuses and all that
if layer == currLayer:
break
self.modify(fitBoosted)
def getBoosts(self, fit):
"""Return all boosts applied onto given fit"""
return self.boosts.get(fit)
def modify(self, fitBoosted):
# Get all boosts which should be applied onto current fit
boosts = self.getBoosts(fitBoosted)
# Now we got it all figured out, actually do the useful part of all this
for name, info in boosts.iteritems():
# Unpack all data required to run effect properly
effect, thing = info[1]
context = ("gang", self.contextMap[type(thing)])
# Run effect, and get proper bonuses applied
try:
effect.handler(fitBoosted, thing, context)
except:
pass

View File

@@ -39,7 +39,6 @@ from eos.saveddata.implantSet import ImplantSet
from eos.saveddata.booster import Booster
from eos.saveddata.fit import Fit, ImplantLocation
from eos.saveddata.mode import Mode
from eos.saveddata.fleet import Fleet, Wing, Squad
from eos.saveddata.miscData import MiscData
from eos.saveddata.override import Override

View File

@@ -29,7 +29,6 @@ from gui.projectedView import ProjectedView
from gui.commandView import CommandView
from gui.notesView import NotesView
from gui.pyfatogglepanel import TogglePanel
from gui.gangView import GangView
from gui.bitmapLoader import BitmapLoader
import gui.chromeTabs

View File

@@ -1 +1 @@
__all__ = ["fittingView", "fleetView", "implantEditor"]
__all__ = ["fittingView", "implantEditor"]

View File

@@ -1,140 +0,0 @@
import wx.gizmos
import gui.fleetBrowser
import service
from gui.bitmapLoader import BitmapLoader
#Tab spawning handler
class FleetSpawner(gui.multiSwitch.TabSpawner):
def __init__(self, multiSwitch):
self.multiSwitch = multiSwitch
mainFrame = gui.mainFrame.MainFrame.getInstance()
mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_SELECTED, self.fleetSelected)
def fleetSelected(self, event):
if self.multiSwitch.GetPageCount() == 0:
self.multiSwitch.AddPage(wx.Panel(self.multiSwitch, size = (0,0)), "Empty Tab")
view = FleetView(self.multiSwitch)
self.multiSwitch.ReplaceActivePage(view)
view.populate(event.fleetID)
view.Show()
FleetSpawner.register()
class FleetView(wx.gizmos.TreeListCtrl):
def __init__(self, parent, size = (0,0)):
wx.gizmos.TreeListCtrl.__init__(self, parent, size = size)
self.tabManager = parent
self.fleetId = None
self.fleetImg = BitmapLoader.getImage("53_16", "icons")
self.imageList = wx.ImageList(16, 16)
self.SetImageList(self.imageList)
for col in ("", "Fit", "Shiptype", "Character", "Bonusses"):
self.AddColumn(col)
self.SetMainColumn(1)
self.icons = {}
self.addImage = self.imageList.Add(BitmapLoader.getBitmap("add_small", "gui"))
for icon in ("fb", "fc", "sb", "sc", "wb", "wc"):
self.icons[icon] = self.imageList.Add(BitmapLoader.getBitmap("fleet_%s_small" % icon, "gui"))
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.checkNew)
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_RENAMED, self.fleetRenamed)
self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_REMOVED, self.fleetRemoved)
def Destroy(self):
self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_REMOVED, handler = self.fleetRemoved)
self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_RENAMED, handler = self.fleetRenamed)
wx.gizmos.TreeListCtrl.Destroy(self)
def fleetRenamed(self, event):
if event.fleetID == self.fleetId:
sFleet = service.Fleet.getInstance()
f = sFleet.getFleetByID(event.fleetID)
self.UpdateTab(f.name, self.fleetImg)
event.Skip()
def fleetRemoved(self, event):
if event.fleetID == self.fleetId:
self.tabManager.DeletePage(self.tabManager.GetPageIndex(self))
event.Skip()
def checkNew(self, event):
data = self.GetPyData(event.Item)
if data and isinstance(data, tuple) and data[0] == "add":
layer = data[1]
def UpdateTab(self, name, img):
self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img)
def populate(self, fleetID):
sFleet = service.Fleet.getInstance()
f = sFleet.getFleetByID(fleetID)
self.fleetId = fleetID
self.UpdateTab( f.name, self.fleetImg)
self.fleet = f
self.DeleteAllItems()
root = self.AddRoot("")
self.setEntry(root, f.leader, "fleet", f)
for wing in f.wings:
wingId = self.AppendItem(root, "")
self.setEntry(wingId, wing.leader, "wing", wing)
for squad in wing.squads:
for member in squad.members:
memberId = self.AppendItem(wingId, "")
self.setEntry(memberId, member, "squad", squad)
self.addAdder(wingId, "squad")
self.addAdder(root, "wing")
self.ExpandAll(root)
self.SetColumnWidth(0, 16)
for i in xrange(1, 5):
self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER)
headerWidth = self.GetColumnWidth(i) + 5
self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
baseWidth = self.GetColumnWidth(i)
if baseWidth < headerWidth:
self.SetColumnWidth(i, headerWidth)
else:
self.SetColumnWidth(i, baseWidth)
def addAdder(self, treeItemId, layer):
id = self.AppendItem(treeItemId, "Add new %s" % layer.capitalize())
self.SetPyData(id, ("add", layer))
self.SetItemImage(id, self.addImage, 1)
def setEntry(self, treeItemId, fit, layer, info):
self.SetPyData(treeItemId, info)
if fit is None:
self.SetItemText(treeItemId, "%s Commander" % layer.capitalize(), 1)
else:
fleet = self.fleet
if fit == info.booster:
self.SetItemImage(treeItemId, self.icons["%sb" % layer[0]], 0)
elif fit == info.leader:
self.SetItemImage(treeItemId, self.icons["%sc" % layer[0]], 1)
self.SetItemText(treeItemId, fit.name, 1)
self.SetItemText(treeItemId, fit.ship.item.name, 2)
self.SetItemText(treeItemId, fit.character.name, 3)
boosts = fleet.store.getBoosts(fit)
if boosts:
bonusses = []
for name, info in boosts.iteritems():
bonusses.append("%s: %.2g" % (name, info[0]))
self.SetItemText(treeItemId, ", ".join(bonusses), 3)

View File

@@ -1,456 +0,0 @@
import wx
import copy
from gui.bitmapLoader import BitmapLoader
import gui.mainFrame
from gui.PFListPane import PFListPane
import service.fleet
from gui.utils.drawUtils import GetPartialText
from wx.lib.buttons import GenBitmapButton
import gui.utils.colorUtils as colorUtils
import gui.utils.drawUtils as drawUtils
import gui.sfBrowserItem as SFItem
FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent()
FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent()
FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent()
FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent()
FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent()
FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent()
FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent()
FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent()
class FleetBrowser(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.sFleet = service.fleet.Fleet.getInstance()
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.hpane = FleetBrowserHeader(self)
mainSizer.Add(self.hpane, 0, wx.EXPAND)
self.m_sl2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
mainSizer.Add( self.m_sl2, 0, wx.EXPAND, 0 )
self.fleetItemContainer = PFFleetItemContainer(self)
mainSizer.Add(self.fleetItemContainer, 1, wx.EXPAND)
self.SetSizer(mainSizer)
self.Layout()
self.filter = ""
self.fleetIDMustEditName = -1
self.Bind(wx.EVT_SIZE, self.SizeRefreshList)
self.Bind(EVT_FLEET_ITEM_NEW, self.AddNewFleetItem)
self.Bind(EVT_FLEET_ITEM_SELECT, self.SelectFleetItem)
self.Bind(EVT_FLEET_ITEM_DELETE, self.DeleteFleetItem)
self.Bind(EVT_FLEET_ITEM_COPY, self.CopyFleetItem)
self.Bind(EVT_FLEET_ITEM_RENAME, self.RenameFleetItem)
self.PopulateFleetList()
def AddNewFleetItem(self, event):
fleetName = event.fleetName
newFleet = self.sFleet.addFleet()
self.sFleet.renameFleet(newFleet, fleetName)
self.fleetIDMustEditName = newFleet.ID
self.AddItem(newFleet.ID, newFleet.name, newFleet.count())
def SelectFleetItem(self, event):
fleetID = event.fleetID
self.fleetItemContainer.SelectWidgetByFleetID(fleetID)
wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleetID))
def CopyFleetItem(self, event):
fleetID = event.fleetID
fleet = self.sFleet.copyFleetByID(fleetID)
fleetName = fleet.name + " Copy"
self.sFleet.renameFleet(fleet,fleetName)
self.fleetIDMustEditName = fleet.ID
self.AddItem(fleet.ID, fleet.name, fleet.count())
self.fleetItemContainer.SelectWidgetByFleetID(fleet.ID)
wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleet.ID))
def RenameFleetItem(self, event):
fleetID = event.fleetID
fleet = self.sFleet.getFleetByID(fleetID)
newFleetName = event.fleetName
self.sFleet.renameFleet(fleet, newFleetName)
wx.PostEvent(self.mainFrame, FleetRenamed(fleetID = fleet.ID))
def DeleteFleetItem(self, event):
self.sFleet.deleteFleetByID(event.fleetID)
self.PopulateFleetList()
wx.PostEvent(self.mainFrame, FleetRemoved(fleetID = event.fleetID))
def AddItem (self, ID, name, count):
self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count))
widget = self.fleetItemContainer.GetWidgetByFleetID(ID)
self.fleetItemContainer.RefreshList(True)
self.fleetItemContainer.ScrollChildIntoView(widget)
wx.PostEvent(self, FleetItemSelect(fleetID = ID))
def PopulateFleetList(self):
self.Freeze()
filter = self.filter
self.fleetItemContainer.RemoveAllChildren()
fleetList = self.sFleet.getFleetList()
for fleetID, fleetName, fleetCount in fleetList:
if fleetName.lower().find(filter.lower()) != -1:
self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount))
self.fleetItemContainer.RefreshList()
self.Thaw()
def SetFilter(self, filter):
self.filter = filter
def SizeRefreshList(self, event):
ewidth, eheight = event.GetSize()
self.Layout()
self.fleetItemContainer.Layout()
self.fleetItemContainer.RefreshList(True)
event.Skip()
class FleetBrowserHeader (wx.Panel):
def __init__(self, parent):
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), style=wx.TAB_TRAVERSAL)
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self.newBmp = BitmapLoader.getBitmap("fit_add_small","gui")
bmpSize = (16,16)
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
if 'wxMac' in wx.PlatformInfo:
bgcolour = wx.Colour(0, 0, 0, 0)
else:
bgcolour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE )
self.fbNewFleet = PFGenBitmapButton( self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE )
mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL , 5)
self.fbNewFleet.SetBackgroundColour( bgcolour )
self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL )
mainSizer.Add( self.sl1, 0, wx.EXPAND |wx.LEFT, 5 )
self.tcFilter = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 )
self.stStatus = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 )
self.stStatus.Wrap( -1 )
mainSizer.Add( self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 )
self.SetSizer(mainSizer)
self.Layout()
self.fbNewFleet.Bind(wx.EVT_ENTER_WINDOW, self.fbNewEnterWindow)
self.fbNewFleet.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow)
self.fbNewFleet.Bind(wx.EVT_BUTTON, self.OnNewFleetItem)
self.tcFilter.Bind(wx.EVT_TEXT, self.OnFilterText)
self.tcFilter.Bind(wx.EVT_ENTER_WINDOW, self.fbFilterEnterWindow)
self.tcFilter.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow)
def OnFilterText(self, event):
filter = self.tcFilter.GetValue()
self.Parent.SetFilter(filter)
self.Parent.PopulateFleetList()
event.Skip()
def OnNewFleetItem(self, event):
wx.PostEvent(self.Parent, FleetItemNew(fleetName = "New Fleet"))
def fbNewEnterWindow(self, event):
self.stStatus.SetLabel("New fleet")
self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
event.Skip()
def fbHItemLeaveWindow(self, event):
self.stStatus.SetLabel("")
self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
event.Skip()
def fbFilterEnterWindow(self, event):
self.stStatus.SetLabel("Filter list")
event.Skip()
class PFFleetItemContainer(PFListPane):
def __init__(self,parent):
PFListPane.__init__(self,parent)
self.selectedWidget = -1
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
def IsWidgetSelectedByContext(self, widget):
if self.GetWidgetList()[widget].IsSelected():
return True
return False
def GetWidgetIndex(self, widgetWnd):
return self.GetWidgetList().index(widgetWnd)
def GetWidgetByFleetID(self, fleetID):
for widget in self.GetWidgetList():
if widget.fleetID == fleetID:
return widget
return None
def SelectWidget(self, widgetWnd):
wlist = self.GetWidgetList()
if self.selectedWidget != -1:
wlist[self.selectedWidget].SetSelected(False)
wlist[self.selectedWidget].Refresh()
windex = self.GetWidgetIndex(widgetWnd)
wlist[windex].SetSelected(True)
wlist[windex].Refresh()
self.selectedWidget = windex
def SelectWidgetByFleetID(self, fleetID):
widgetWnd = self.GetWidgetByFleetID(fleetID)
if widgetWnd:
self.SelectWidget(widgetWnd)
def RemoveWidget(self, child):
child.Destroy()
self.selectedWidget = -1
self._wList.remove(child)
def RemoveAllChildren(self):
for widget in self._wList:
widget.Destroy()
self.selectedWidget = -1
self._wList = []
def OnLeftUp(self, event):
event.Skip()
class FleetItem(SFItem.SFBrowserItem):
def __init__(self, parent, fleetID, fleetName, fleetCount,
id=wx.ID_ANY, pos=wx.DefaultPosition,
size=(0,40), style=0):
SFItem.SFBrowserItem.__init__(self, parent, size = size)
self.fleetBrowser = self.Parent
self.fleetID = fleetID
self.fleetName = fleetName
self.fleetCount = fleetCount
self.padding = 4
self.fontBig = wx.FontFromPixelSize((0,15),wx.SWISS, wx.NORMAL, wx.BOLD, False)
self.fontNormal = wx.FontFromPixelSize((0,14),wx.SWISS, wx.NORMAL, wx.NORMAL, False)
self.fontSmall = wx.FontFromPixelSize((0,12),wx.SWISS, wx.NORMAL, wx.NORMAL, False)
self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui")
self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui")
self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small","gui")
self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui")
self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui")
fleetImg = self.fleetBmp.ConvertToImage()
fleetImg = fleetImg.Blur(2)
if not fleetImg.HasAlpha():
fleetImg.InitAlpha()
fleetImg = fleetImg.AdjustChannels(1, 1, 1, 0.5)
self.fleetEffBmp = wx.BitmapFromImage(fleetImg)
self.toolbar.AddButton(self.copyBmp, "Copy", self.CopyFleetCB)
self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.RenameFleetCB)
self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB)
self.editWidth = 150
self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth,-1), wx.TE_PROCESS_ENTER)
if self.fleetBrowser.fleetIDMustEditName != self.fleetID:
self.tcFleetName.Show(False)
else:
self.tcFleetName.SetFocus()
self.tcFleetName.SelectAll()
self.fleetBrowser.fleetIDMustEditName = -1
self.renameBtn.SetBitmap(self.acceptBmp)
self.selected = True
self.tcFleetName.Bind(wx.EVT_KILL_FOCUS, self.OnEditLostFocus)
self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet)
self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc)
self.animCount = 0
def MouseLeftUp(self, event):
if self.tcFleetName.IsShown():
self.RestoreEditButton()
else:
wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID = self.fleetID))
def CopyFleetCB(self):
if self.tcFleetName.IsShown():
self.RestoreEditButton()
return
wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID = self.fleetID))
def RenameFleetCB(self):
if self.tcFleetName.IsShown():
self.RenameFleet(None)
self.RestoreEditButton()
else:
self.tcFleetName.SetValue(self.fleetName)
self.tcFleetName.Show()
self.renameBtn.SetBitmap(self.acceptBmp)
self.Refresh()
self.tcFleetName.SetFocus()
self.tcFleetName.SelectAll()
self.Refresh()
def RenameFleet(self, event):
newFleetName = self.tcFleetName.GetValue()
self.fleetName = newFleetName
self.tcFleetName.Show(False)
wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID = self.fleetID, fleetName = self.fleetName))
self.Refresh()
def DeleteFleetCB(self):
if self.tcFleetName.IsShown():
self.RestoreEditButton()
return
wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID = self.fleetID))
def RestoreEditButton(self):
self.tcFleetName.Show(False)
self.renameBtn.SetBitmap(self.renameBmp)
self.Refresh()
def OnEditLostFocus(self, event):
self.RestoreEditButton()
self.Refresh()
def EditCheckEsc(self, event):
if event.GetKeyCode() == wx.WXK_ESCAPE:
self.RestoreEditButton()
else:
event.Skip()
def IsSelected(self):
return self.selected
def UpdateElementsPos(self, mdc):
rect = self.GetRect()
self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding
self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2
self.toolbarx = self.toolbarx + self.animCount
self.fleetBmpx = self.padding + (rect.height - self.fleetBmp.GetWidth()) / 2
self.fleetBmpy = (rect.height - self.fleetBmp.GetHeight()) / 2
self.fleetBmpx -= self.animCount
self.textStartx = self.fleetBmpx + self.fleetBmp.GetWidth() + self.padding
self.fleetNamey = (rect.height - self.fleetBmp.GetHeight()) / 2
mdc.SetFont(self.fontBig)
wtext, htext = mdc.GetTextExtent(self.fleetName)
self.fleetCounty = self.fleetNamey + htext
mdc.SetFont(self.fontSmall)
wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel)
self.thoverx = self.toolbarx - self.padding - wlabel
self.thovery = (rect.height - hlabel)/2
self.thoverw = wlabel
def DrawItem(self, mdc):
rect = self.GetRect()
windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
textColor = colorUtils.GetSuitableColor(windowColor, 1)
mdc.SetTextForeground(textColor)
self.UpdateElementsPos(mdc)
self.toolbar.SetPosition((self.toolbarx, self.toolbary))
mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2)
mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx,self.fleetBmpy)
mdc.SetFont(self.fontNormal)
suffix = "%d ships" % self.fleetCount if self.fleetCount >1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships"
fleetCount = "Fleet size: %s" % suffix
fleetCount = drawUtils.GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty)
mdc.SetFont(self.fontSmall)
mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery)
mdc.SetFont(self.fontBig)
pfname = drawUtils.GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
mdc.DrawText(pfname, self.textStartx, self.fleetNamey)
if self.tcFleetName.IsShown():
self.AdjustControlSizePos(self.tcFleetName, self.textStartx, self.toolbarx - self.editWidth - self.padding)
def AdjustControlSizePos(self, editCtl, start, end):
fnEditSize = editCtl.GetSize()
wSize = self.GetSize()
fnEditPosX = end
fnEditPosY = (wSize.height - fnEditSize.height)/2
if fnEditPosX < start:
editCtl.SetSize((self.editWidth + fnEditPosX - start,-1))
editCtl.SetPosition((start,fnEditPosY))
else:
editCtl.SetSize((self.editWidth,-1))
editCtl.SetPosition((fnEditPosX,fnEditPosY))
class PFGenBitmapButton(GenBitmapButton):
def __init__(self, parent, id, bitmap, pos, size, style):
GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style)
self.bgcolor = wx.Brush(wx.WHITE)
def SetBackgroundColour(self, color):
self.bgcolor = wx.Brush(color)
def GetBackgroundBrush(self, dc):
return self.bgcolor

View File

@@ -1,412 +0,0 @@
#===============================================================================
# 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 <http://www.gnu.org/licenses/>.
#===============================================================================
import wx
from wx.lib.scrolledpanel import ScrolledPanel
import service
import gui.mainFrame
import gui.shipBrowser
import gui.globalEvents as GE
from gui import characterEditor as CharEditor
class GangView ( ScrolledPanel ):
def __init__( self, parent ):
ScrolledPanel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 100,20 ), style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL )
mainSizer = wx.BoxSizer( wx.VERTICAL )
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.draggedFitID = None
help = '''Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.'''
helpSizer = wx.BoxSizer( wx.HORIZONTAL )
self.helpText = wx.StaticText( self, wx.ID_ANY, help, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE )
helpSizer.Add( self.helpText, 1, wx.ALL, 5 )
self.options = ["Fleet", "Wing", "Squad"]
self.fleet = {}
for id, option in enumerate(self.options):
# set content for each commander
self.fleet[id] = {}
self.fleet[id]['stLabel'] = wx.StaticText( self, wx.ID_ANY, self.options[id]+':', wx.DefaultPosition, wx.DefaultSize, 0 )
self.fleet[id]['stText'] = wx.StaticText( self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0 )
self.fleet[id]['chFit'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] )
self.fleet[id]['chChar'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] )
self.fleet[id]['fitSizer'] = wx.BoxSizer( wx.VERTICAL )
self.FitDNDPopupMenu = self.buildBoostermenu()
contentFGSizer = wx.FlexGridSizer( 5, 3, 0, 0 )
contentFGSizer.AddGrowableCol( 1 )
contentFGSizer.SetFlexibleDirection( wx.BOTH )
contentFGSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )
### Header
self.stBooster = wx.StaticText( self, wx.ID_ANY, u"Booster", wx.DefaultPosition, wx.DefaultSize, 0 )
self.stBooster.Wrap( -1 )
self.stBooster.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
contentFGSizer.Add( self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5 )
self.stFits = wx.StaticText( self, wx.ID_ANY, u"Fits", wx.DefaultPosition, wx.DefaultSize, 0 )
self.stFits.Wrap( -1 )
self.stFits.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
contentFGSizer.Add( self.stFits, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 )
self.stCharacters = wx.StaticText( self, wx.ID_ANY, u"Characters", wx.DefaultPosition, wx.DefaultSize, 0 )
self.stCharacters.Wrap( -1 )
self.stCharacters.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
contentFGSizer.Add( self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 )
self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
contentFGSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 )
self.m_staticline3 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
contentFGSizer.Add( self.m_staticline3, 0, wx.EXPAND, 5 )
self.m_staticline4 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
contentFGSizer.Add( self.m_staticline4, 0, wx.EXPAND, 5 )
### Content
for id in self.fleet:
# set various properties
self.fleet[id]['stLabel'].Wrap( -1 )
self.fleet[id]['stLabel'].SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
self.fleet[id]['stText'].Wrap( -1 )
# bind text and choice events
self.fleet[id]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster)
self.fleet[id]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow)
self.fleet[id]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
self.fleet[id]['stText'].SetToolTip(wx.ToolTip("Double click to remove booster"))
self.fleet[id]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged)
self.fleet[id]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected)
# add fit text and choice to the fit sizer
self.fleet[id]['fitSizer'].Add( self.fleet[id]['stText'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
self.fleet[id]['fitSizer'].Add( self.fleet[id]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 1 )
# add everything to the content sizer
contentFGSizer.Add( self.fleet[id]['stLabel'], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
contentFGSizer.Add( self.fleet[id]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 )
contentFGSizer.Add( self.fleet[id]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
mainSizer.Add( contentFGSizer, 1, wx.EXPAND, 0 )
mainSizer.Add( helpSizer, 0, wx.EXPAND, 0 )
self.SetSizer( mainSizer )
self.SetAutoLayout(True)
self.SetupScrolling()
self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.RefreshCharacterList)
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitSelected)
self.mainFrame.Bind(gui.shipBrowser.EVT_FIT_RENAMED, self.fitRenamed)
self.mainFrame.Bind(gui.shipBrowser.BOOSTER_LIST_UPDATED, self.RefreshBoosterFits)
self.RefreshBoosterFits()
self.RefreshCharacterList()
def buildBoostermenu(self):
menu = wx.Menu()
for id, option in enumerate(self.options):
item = menu.Append(-1, option)
# We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser
self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item)
return menu
def OnEnterWindow(self, event):
obj = event.GetEventObject()
obj.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
event.Skip()
def OnLeaveWindow(self, event):
obj = event.GetEventObject()
obj.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
event.Skip()
def CharChanged(self, event):
''' Change booster character '''
chBooster = event.GetEventObject()
type = -1
for id in self.fleet:
if chBooster == self.fleet[id]['chChar']: type = id
if type == -1:
event.Skip()
return
sFit = service.Fit.getInstance()
fleetSrv = service.Fleet.getInstance()
activeFitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(activeFitID)
sChar = service.Character.getInstance()
charList = sChar.getCharacterList()
if activeFitID:
commanders = fleetSrv.loadLinearFleet(fit)
if commanders is None:
fleetCom, wingCom, squadCom = (None, None, None)
else:
fleetCom, wingCom, squadCom = commanders
if type == 0:
if fleetCom:
charID = chBooster.GetClientData(chBooster.GetSelection())
sFit.changeChar(fleetCom.ID, charID)
else:
chBooster.SetSelection(0)
if type == 1:
if wingCom:
charID = chBooster.GetClientData(chBooster.GetSelection())
sFit.changeChar(wingCom.ID, charID)
else:
chBooster.SetSelection(0)
if type == 2:
if squadCom:
charID = chBooster.GetClientData(chBooster.GetSelection())
sFit.changeChar(squadCom.ID, charID)
else:
chBooster.SetSelection(0)
sFit.recalc(fit, withBoosters=True)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID))
else:
chBooster.SetSelection(0)
def RemoveBooster(self, event):
activeFitID = self.mainFrame.getActiveFit()
if not activeFitID:
return
location = event.GetEventObject()
for id in self.fleet:
if location == self.fleet[id]['stText']: type = id
sFit = service.Fit.getInstance()
boostee = sFit.getFit(activeFitID)
booster = None
fleetSrv = service.Fleet.getInstance()
if type == 0: fleetSrv.setLinearFleetCom(boostee, booster)
if type == 1: fleetSrv.setLinearWingCom(boostee, booster)
if type == 2: fleetSrv.setLinearSquadCom(boostee, booster)
# Hide stText and, default fit selection, and enable it
location.Hide()
choice = self.fleet[type]['chFit']
choice.SetSelection(0)
choice.Show()
sFit.recalc(boostee, withBoosters=True)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID))
def fitRenamed(self, event):
fleetSrv = service.Fleet.getInstance()
activeFitID = self.mainFrame.getActiveFit()
if activeFitID:
ev = event
ev.fitID = activeFitID
self.fitSelected(ev)
def fitSelected(self, event):
''' Fires when active fit is selected and when booster is saved to fit. Update the UI to reflect changes '''
fleetSrv = service.Fleet.getInstance()
activeFitID = self.mainFrame.getActiveFit()
sFit = service.Fit.getInstance()
fit = sFit.getFit(event.fitID or activeFitID)
self.Parent.Parent.DisablePage(self, not fit or fit.isStructure)
commanders = (None, None, None)
if activeFitID:
commanders = fleetSrv.loadLinearFleet(fit)
for id in self.fleet:
# try...except here as we're trying 2 different criteria and want to fall back on the same code
try:
commander = commanders[id]
if not activeFitID or commander is None:
raise Exception()
self.fleet[id]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name)
self.fleet[id]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0")
self.fleet[id]['chChar'].Enable()
self.fleet[id]['chFit'].Hide()
self.fleet[id]['stText'].Show()
except:
#set defaults, disable char selection, and enable fit selection
self.fleet[id]['stText'].SetLabel("None")
self.fleet[id]['chChar'].SetStringSelection("All 0")
self.fleet[id]['chChar'].Disable()
self.fleet[id]['chFit'].SetSelection(0)
self.fleet[id]['chFit'].Show()
self.fleet[id]['stText'].Hide()
if activeFitID:
self.Enable()
else:
self.Disable()
self.Layout()
self.SendSizeEvent()
def AddCommander(self, fitID, type = None):
''' Adds booster to a fit, then recalculates active fit '''
if type is None:
return
activeFitID = self.mainFrame.getActiveFit()
if activeFitID:
sFit = service.Fit.getInstance()
boostee = sFit.getFit(activeFitID)
booster = sFit.getFit(fitID)
fleetSrv = service.Fleet.getInstance()
if type == 0: fleetSrv.setLinearFleetCom(boostee, booster)
if type == 1: fleetSrv.setLinearWingCom(boostee, booster)
if type == 2: fleetSrv.setLinearSquadCom(boostee, booster)
sFit.recalc(boostee)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID))
def RefreshBoosterFits(self, event = None):
sFit = service.Fit.getInstance()
sMkt = service.Market.getInstance()
fitList = sFit.getBoosterFits()
for id in self.fleet:
choice = self.fleet[id]['chFit']
chCurrSelection = choice.GetSelection()
chCurrData = -1
if chCurrSelection != -1:
chCurrData = choice.GetClientData(chCurrSelection)
chCurrSelString = choice.GetString(chCurrSelection)
choice.Clear()
currSelFound = False
choice.Append("None", -1)
for fit in fitList:
id,name,type = fit
ship = sMkt.getItem(type)
choice.Append(ship.name+': '+name, id)
if chCurrData == id:
currSelFound = True
if chCurrSelection == -1:
choice.SetSelection(0)
else:
if currSelFound:
choice.SetStringSelection(chCurrSelString)
else:
choice.SetSelection(0)
def RefreshCharacterList(self, event = None):
sChar = service.Character.getInstance()
charList = sChar.getCharacterList()
for id in self.fleet:
choice = self.fleet[id]['chChar']
chCurrSelection = choice.GetSelection()
chCurrData = -1
if chCurrSelection != -1:
chCurrData = choice.GetClientData(chCurrSelection)
chCurrSelString = choice.GetString(chCurrSelection)
choice.Clear()
currSelFound = False
for char in charList:
choice.Append(char.name, char.ID)
if chCurrData == char.ID:
currSelFound = True
if chCurrSelection == -1:
choice.SetSelection(1)
else:
if currSelFound:
choice.SetStringSelection(chCurrSelString)
else:
choice.SetSelection(1)
def handleDrag(self, type, fitID):
''' Handle dragging of fit to fleet interface '''
#Those are drags coming from pyfa sources, NOT builtin wx drags
self.draggedFitID = None
if type == "fit":
sFit = service.Fit.getInstance()
fit = sFit.getFit(self.mainFrame.getActiveFit())
if fit and not fit.isStructure:
self.draggedFitID = fitID
pos = wx.GetMousePosition()
pos = self.ScreenToClient(pos)
self.PopupMenu(self.FitDNDPopupMenu, pos)
def OnPopupItemSelected(self, event):
''' Fired when booster popup item is selected '''
# Get menu selection ID via self.options
menuItem = event.EventObject.FindItemById(event.GetId())
type = self.options.index(menuItem.GetText())
if self.draggedFitID:
sFit = service.Fit.getInstance()
draggedFit = sFit.getFit(self.draggedFitID)
self.AddCommander(draggedFit.ID, type)
self.mainFrame.additionsPane.select("Fleet")
def OnFitChoiceSelected(self, event):
''' Fired when booster choice is selected '''
sFit = service.Fit.getInstance()
# set type via choice box used
chFit = event.GetEventObject()
fitID = chFit.GetClientData(chFit.GetSelection())
type = -1
for id in self.fleet:
if chFit == self.fleet[id]['chFit']: type = id
if type == -1 or fitID == -1:
event.Skip()
return
fit = sFit.getFit(fitID)
self.AddCommander(fit.ID, type)
self.mainFrame.additionsPane.select("Fleet")

View File

@@ -55,7 +55,6 @@ from gui.preferenceDialog import PreferenceDialog
from gui.graphFrame import GraphFrame
from gui.copySelectDialog import CopySelectDialog
from gui.utils.clipboard import toClipboard, fromClipboard
from gui.fleetBrowser import FleetBrowser
from gui.updateDialog import UpdateDialog
from gui.builtinViews import *
@@ -158,12 +157,6 @@ class MainFrame(wx.Frame):
self.shipBrowser = ShipBrowser(self.notebookBrowsers)
self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage = shipBrowserImg, showClose = False)
#=======================================================================
# DISABLED FOR RC2 RELEASE
#self.fleetBrowser = FleetBrowser(self.notebookBrowsers)
#self.notebookBrowsers.AddPage(self.fleetBrowser, "Fleets", showClose = False)
#=======================================================================
self.notebookBrowsers.SetSelection(1)
self.browser_fitting_split.SplitVertically(self.notebookBrowsers, self.fitting_additions_split)

View File

@@ -5,7 +5,6 @@ from service.character import Character
from service.damagePattern import DamagePattern
from service.targetResists import TargetResists
from service.settings import SettingsProvider
from service.fleet import Fleet
from service.update import Update
from service.price import Price
from service.network import Network

View File

@@ -34,6 +34,8 @@ import service
import config
import logging
from eos.saveddata.character import Character as es_Character
logger = logging.getLogger(__name__)
class CharacterImportThread(threading.Thread):
@@ -45,6 +47,12 @@ class CharacterImportThread(threading.Thread):
def run(self):
paths = self.paths
sCharacter = Character.getInstance()
all5_character = es_Character("All 5", 5)
all_skill_ids = []
for skill in all5_character.skills:
# Parse out the skill item IDs to make searching it easier later on
all_skill_ids.append(skill.itemID)
for path in paths:
try:
# we try to parse api XML data first
@@ -59,19 +67,28 @@ class CharacterImportThread(threading.Thread):
charFile = open(path, mode='r').read()
doc = minidom.parseString(charFile)
if doc.documentElement.tagName not in ("SerializableCCPCharacter", "SerializableUriCharacter"):
logger.error("Incorrect EVEMon XML sheet")
raise RuntimeError("Incorrect EVEMon XML sheet")
name = doc.getElementsByTagName("name")[0].firstChild.nodeValue
skill_els = doc.getElementsByTagName("skill")
skills = []
for skill in skill_els:
skills.append({
"typeID": int(skill.getAttribute("typeID")),
"level": int(skill.getAttribute("level")),
})
if int(skill.getAttribute("typeID")) in all_skill_ids and (0 <= int(skill.getAttribute("level")) <= 5):
skills.append({
"typeID": int(skill.getAttribute("typeID")),
"level": int(skill.getAttribute("level")),
})
else:
logger.error("Attempted to import unknown skill %s (ID: %s) (Level: %s)",
skill.getAttribute("name"),
skill.getAttribute("typeID"),
skill.getAttribute("level"),
)
char = sCharacter.new(name+" (EVEMon)")
sCharacter.apiUpdateCharSheet(char.ID, skills)
except Exception, e:
print e.message
logger.error("Exception on character import:")
logger.error(e)
continue
wx.CallAfter(self.callback)
@@ -255,8 +272,11 @@ class Character(object):
return char
def rename(self, char, newName):
char.name = newName
eos.db.commit()
if char.name in ("All 0", "All 5"):
logger.info("Cannot rename built in characters.")
else:
char.name = newName
eos.db.commit()
def copy(self, char):
newChar = copy.deepcopy(char)

View File

@@ -34,7 +34,6 @@ from eos.types import State, Slot
from service.market import Market
from service.damagePattern import DamagePattern
from service.character import Character
from service.fleet import Fleet
from service.settings import SettingsProvider
from service.port import Port
@@ -177,8 +176,6 @@ class Fit(object):
def deleteFit(self, fitID):
fit = eos.db.getFit(fitID)
sFleet = Fleet.getInstance()
sFleet.removeAssociatedFleetData(fit)
eos.db.remove(fit)
@@ -229,7 +226,8 @@ class Fit(object):
self.recalc(fit, withBoosters=True)
def getFit(self, fitID, projected=False, basic=False):
''' Gets fit from database, and populates fleet data.
''' Gets fit from database
Projected is a recursion flag that is set to reduce recursions into projected fits
Basic is a flag to simply return the fit without any other processing
'''
@@ -243,14 +241,6 @@ class Fit(object):
inited = getattr(fit, "inited", None)
if inited is None or inited is False:
sFleet = Fleet.getInstance()
f = sFleet.getLinearFleet(fit)
if f is None:
sFleet.removeAssociatedFleetData(fit)
fit.fleet = None
else:
fit.fleet = f
if not projected:
for fitP in fit.projectedFits:
self.getFit(fitP.ID, projected=True)

View File

@@ -1,215 +0,0 @@
#===============================================================================
# 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 <http://www.gnu.org/licenses/>.
#===============================================================================
import eos.db
from eos.types import Fleet as Fleet_, Wing, Squad
import copy
class Fleet(object):
instance = None
@classmethod
def getInstance(cls):
if cls.instance is None:
cls.instance = Fleet()
return cls.instance
def __init__(self):
pass
def getFleetList(self):
fleetList = []
fleets = eos.db.getFleetList()
for fleet in fleets:
fleetList.append((fleet.ID, fleet.name, fleet.count()))
return fleetList
def getFleetByID(self, ID):
f = eos.db.getFleet(ID)
return f
def addFleet(self):
f = Fleet_()
eos.db.save(f)
return f
def renameFleet(self, fleet, newName):
fleet.name = newName
eos.db.commit()
def copyFleet(self, fleet):
newFleet = copy.deepcopy(fleet)
eos.db.save(newFleet)
return newFleet
def copyFleetByID(self, ID):
fleet = self.getFleetByID(ID)
return self.copyFleet(fleet)
def deleteFleet(self, fleet):
eos.db.remove(fleet)
def deleteFleetByID(self, ID):
fleet = self.getFleetByID(ID)
self.deleteFleet(fleet)
def makeLinearFleet(self, fit):
f = Fleet_()
w = Wing()
f.wings.append(w)
s = Squad()
w.squads.append(s)
s.members.append(fit)
fit.fleet = f
eos.db.save(f)
def setLinearFleetCom(self, boostee, booster):
#if boostee == booster:
# return
if self.getLinearFleet(boostee) is None:
self.removeAssociatedFleetData(boostee)
self.makeLinearFleet(boostee)
squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID))
squad = eos.db.getSquad(squadIDs.pop())
if squad.wing.gang.leader is not None and booster is None:
try:
squad.wing.gang.leader.boostsFits.remove(boostee.ID)
except KeyError:
pass
squad.wing.gang.leader = booster
if self.anyBoosters(squad) is False:
self.removeAssociatedFleetData(boostee)
from service.fit import Fit
sFit = Fit.getInstance()
sFit.recalc(boostee, withBoosters=True)
def setLinearWingCom(self, boostee, booster):
#if boostee == booster:
# return
if self.getLinearFleet(boostee) is None:
self.removeAssociatedFleetData(boostee)
self.makeLinearFleet(boostee)
squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID))
squad = eos.db.getSquad(squadIDs.pop())
if squad.wing.leader is not None and booster is None:
try:
squad.wing.leader.boostsFits.remove(boostee.ID)
except KeyError:
pass
squad.wing.leader = booster
if self.anyBoosters(squad) is False:
self.removeAssociatedFleetData(boostee)
from service.fit import Fit
sFit = Fit.getInstance()
sFit.recalc(boostee, withBoosters=True)
def setLinearSquadCom(self, boostee, booster):
#if boostee == booster:
# return
if self.getLinearFleet(boostee) is None:
self.removeAssociatedFleetData(boostee)
self.makeLinearFleet(boostee)
squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID))
squad = eos.db.getSquad(squadIDs.pop())
if squad.leader is not None and booster is None:
try:
squad.leader.boostsFits.remove(boostee.ID)
except KeyError:
pass
squad.leader = booster
if self.anyBoosters(squad) is False:
self.removeAssociatedFleetData(boostee)
from service.fit import Fit
sFit = Fit.getInstance()
sFit.recalc(boostee, withBoosters=True)
def getLinearFleet(self, fit):
sqIDs = eos.db.getSquadsIDsWithFitID(fit.ID)
if len(sqIDs) != 1:
return None
s = eos.db.getSquad(sqIDs[0])
if len(s.members) != 1:
return None
w = s.wing
if len(w.squads) != 1:
return None
f = w.gang
if len(f.wings) != 1:
return None
return f
def removeAssociatedFleetData(self, fit):
squadIDs = set(eos.db.getSquadsIDsWithFitID(fit.ID))
if len(squadIDs) == 0:
return
squads = list(eos.db.getSquad(sqID) for sqID in squadIDs)
wingIDs = set(squad.wing.ID for squad in squads)
fleetIDs = set(squad.wing.gang.ID for squad in squads)
for fleetID in fleetIDs:
fleet = eos.db.getFleet(fleetID)
for wing in fleet.wings:
wingIDs.add(wing.ID)
for wingID in wingIDs:
wing = eos.db.getWing(wingID)
for squad in wing.squads:
squadIDs.add(squad.ID)
for squadID in squadIDs:
squad = eos.db.getSquad(squadID)
if squad.leader is not None:
try:
squad.leader.boostsFits.remove(fit.ID)
except KeyError:
pass
eos.db.remove(squad)
for wingID in wingIDs:
wing = eos.db.getWing(wingID)
if wing.leader is not None:
try:
wing.leader.boostsFits.remove(fit.ID)
except KeyError:
pass
eos.db.remove(wing)
for fleetID in fleetIDs:
fleet = eos.db.getFleet(fleetID)
if fleet.leader is not None:
try:
fleet.leader.boostsFits.remove(fit.ID)
except KeyError:
pass
eos.db.remove(fleet)
fit.fleet = None
return
def anyBoosters(self, squad):
wing = squad.wing
fleet = wing.gang
if squad.leader is None and wing.leader is None and fleet.leader is None:
return False
return True
def loadLinearFleet(self, fit):
if self.getLinearFleet(fit) is None:
return None
squadID = eos.db.getSquadsIDsWithFitID(fit.ID)[0]
s = eos.db.getSquad(squadID)
w = s.wing
f = w.gang
return (f.leader, w.leader, s.leader)

View File

@@ -406,7 +406,12 @@ class Port(object):
cargoMap[modName] = 0
cargoMap[modName] += extraAmount
elif item.category.name == "Implant":
fit.implants.append(Implant(item))
if "implantness" in item.attributes:
fit.implants.append(Implant(item))
elif "boosterness" in item.attributes:
fit.boosters.append(Booster(item))
else:
logger.error("Failed to import implant: %s", line)
# elif item.category.name == "Subsystem":
# try:
# subsystem = Module(item)

View File

@@ -23,6 +23,11 @@ import os
import eos.types
import eos.db.migration as migration
from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues
from eos.db.saveddata.databaseRepair import DatabaseCleanup
import logging
logger = logging.getLogger(__name__)
class PrefetchThread(threading.Thread):
def run(self):
@@ -55,6 +60,14 @@ if os.path.isfile(config.saveDB):
# Import default database values
# Import values that must exist otherwise Pyfa breaks
DefaultDatabaseValues.importRequiredDefaults()
logging.debug("Starting database validation.")
database_cleanup_instance = DatabaseCleanup()
database_cleanup_instance.OrphanedCharacterSkills(eos.db.saveddata_engine)
database_cleanup_instance.OrphanedFitCharacterIDs(eos.db.saveddata_engine)
database_cleanup_instance.OrphanedFitDamagePatterns(eos.db.saveddata_engine)
logging.debug("Completed database validation.")
else:
# If database does not exist, do not worry about migration. Simply
# create and set version
@@ -67,4 +80,3 @@ else:
DefaultDatabaseValues.importDamageProfileDefaults()
# Import default values for target resist profiles
DefaultDatabaseValues.importResistProfileDefaults()