Merge branch 'master' into feature/alphaclones

Conflicts:
	eve.db
This commit is contained in:
blitzmann
2017-01-26 19:39:13 -05:00
383 changed files with 890 additions and 2072 deletions

View File

@@ -18,10 +18,10 @@ debug = False
saveInRoot = False
# Version data
version = "1.25.2"
version = "1.26.1"
tag = "git"
expansionName = "Ascension"
expansionVersion = "1.9"
expansionName = "YC118.10"
expansionVersion = "1.2"
evemonMinVersion = "4081"
pyfaPath = None

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.
@@ -306,24 +258,31 @@ def countAllFits():
return count
def countFitsWithShip(shipID, ownerID=None, where=None, eager=None):
def countFitsWithShip(lookfor, ownerID=None, where=None, eager=None):
"""
Get all the fits using a certain ship.
If no user is passed, do this for all users.
"""
if isinstance(shipID, int):
if ownerID is not None and not isinstance(ownerID, int):
raise TypeError("OwnerID must be integer")
filter = Fit.shipID == shipID
if ownerID is not None:
filter = and_(filter, Fit.ownerID == ownerID)
if ownerID is not None and not isinstance(ownerID, int):
raise TypeError("OwnerID must be integer")
filter = processWhere(filter, where)
eager = processEager(eager)
with sd_lock:
count = saveddata_session.query(Fit).options(*eager).filter(filter).count()
if isinstance(lookfor, int):
filter = Fit.shipID == lookfor
elif isinstance(lookfor, list):
if len(lookfor) == 0:
return 0
filter = Fit.shipID.in_(lookfor)
else:
raise TypeError("ShipID must be integer")
raise TypeError("You must supply either an integer or ShipID must be integer")
if ownerID is not None:
filter = and_(filter, Fit.ownerID == ownerID)
filter = processWhere(filter, where)
eager = processEager(eager)
with sd_lock:
count = saveddata_session.query(Fit).options(*eager).filter(filter).count()
return count
@@ -334,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):
@@ -465,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

@@ -172,10 +172,11 @@ class HandledModuleList(HandledList):
self[index] = mod
def freeSlot(self, slot):
for i in range(len(self) - 1, -1, -1):
for i in range(len(self)):
mod = self[i]
if mod.getModifiedItemAttr("subSystemSlot") == slot:
del self[i]
self.toDummy(i)
break
class HandledDroneCargoList(HandledList):

View File

@@ -1,7 +1,7 @@
# ammoSpeedMultiplier
#
# Used by:
# Charges from group: Festival Charges (8 of 8)
# Charges from group: Festival Charges (9 of 9)
# Charges from group: Interdiction Probe (2 of 2)
# Charges from group: Survey Probe (3 of 3)
type = "passive"

View File

@@ -1,7 +1,7 @@
# boosterArmorHpPenalty
#
# Used by:
# Implants from group: Booster (12 of 47)
# Implants from group: Booster (12 of 48)
type = "boosterSideEffect"
activeByDefault = False

View File

@@ -1,7 +1,7 @@
# boosterMaxVelocityPenalty
#
# Used by:
# Implants from group: Booster (12 of 47)
# Implants from group: Booster (12 of 48)
type = "boosterSideEffect"
activeByDefault = False

View File

@@ -1,7 +1,7 @@
# boosterShieldCapacityPenalty
#
# Used by:
# Implants from group: Booster (12 of 47)
# Implants from group: Booster (12 of 48)
type = "boosterSideEffect"
activeByDefault = False

View File

@@ -2,111 +2,9 @@
#
# Used by:
# Items from market group: Ammunition & Charges > Command Burst Charges (15 of 15)
type = "active"
'''
Some documentation:
When the fit is calculated, we gather up all the gang effects and stick them onto the fit. We don't run the actual
effect yet, only give the fit details so that it can run the effect at a later time. We need to do this so that we can
only run the strongest effect. When we are done, one of the last things that we do with the fit is to loop through those
bonuses and actually run the effect. To do this, we have a special argument passed into the effect handler that tells it
which warfareBuffID to run (shouldn't need this right now, but better safe than sorry)
'''
type = "active", "gang"
def handler(fit, module, context, **kwargs):
print "In chargeBonusWarfareEffect, context: ", context
def runEffect(id, value):
print "RUN EFFECT: ", fit,
if id == 10: # Shield Burst: Shield Harmonizing: Shield Resistance
for damageType in ("Em", "Explosive", "Thermal", "Kinetic"):
fit.ship.boostItemAttr("shield%sDamageResonance" % damageType, value)
if id == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill("Shield Emission Systems"), "capacitorNeed", value)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill("Shield Emission Systems"), "duration", value)
if id == 12: # Shield Burst: Shield Extension: Shield HP
fit.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
if id == 13: # Armor Burst: Armor Energizing: Armor Resistance
for damageType in ("Em", "Thermal", "Explosive", "Kinetic"):
fit.ship.boostItemAttr("armor%sDamageResonance" % damageType, value)
if id == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), "capacitorNeed", value)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), "duration", value)
if id == 15: # Armor Burst: Armor Reinforcement: Armor HP
fit.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
if id == 16: # Information Burst: Sensor Optimization: Scan Resolution
fit.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
if id == 17: # Information Burst: Electronic Superiority: EWAR Range and Strength
groups = ("ECM", "Sensor Dampener", "Weapon Disruptor", "Target Painter")
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True)
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "falloffEffectiveness", value, stackingPenalties=True)
for scanType in ("Magnetometric", "Radar", "Ladar", "Gravimetric"):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.nam == "ECM", "scan%sStrengthBonus" % scanType, value, stackingPenalties=True)
for attr in ("missileVelocityBonus", "explosionDelayBonus", "aoeVelocityBonus", "falloffBonus",
"maxRangeBonus", "aoeCloudSizeBonus", "trackingSpeedBonus"):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Weapon Disruptor", attr, value)
for attr in ("maxTargetRangeBonus", "scanResolutionBonus"):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Sensor Dampener", attr, value)
fit.modules.filteredItemBoost(lambda mod: mod.item.gorup.name == "Target Painter", "signatureRadiusBonus", value, stackingPenalties=True)
if id == 18: # Information Burst: Electronic Hardening: Scan Strength
for scanType in ("Gravimetric", "Radar", "Ladar", "Magnetometric"):
fit.ship.boostItemAttr("scan%sStrength" % scanType, value, stackingPenalties=True)
if id == 19: # Information Burst: Electronic Hardening: RSD/RWD Resistance
fit.ship.boostItemAttr("sensorDampenerResistance", value)
fit.ship.boostItemAttr("weaponDisruptionResistance", value)
if id == 26: # Information Burst: Sensor Optimization: Targeting Range
fit.ship.boostItemAttr("maxTargetRange", value)
if id == 20: # Skirmish Burst: Evasive Maneuvers: Signature Radius
fit.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
if id == 21: # Skirmish Burst: Interdiction Maneuvers: Tackle Range
groups = ("Stasis Web", "Warp Scrambler")
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True)
if id == 22: # Skirmish Burst: Rapid Deployment: AB/MWD Speed Increase
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Afterburner") or mod.item.requiresSkill("High Speed Maneuvering"), "speedFactor", value, stackingPenalties=True)
if id == 23: # Mining Burst: Mining Laser Field Enhancement: Mining/Survey Range
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill("Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "maxRange", value, stackingPenalties=True)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"), "surveyScanRange", value, stackingPenalties=True)
if id == 24: # Mining Burst: Mining Laser Optimization: Mining Capacitor/Duration
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill("Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "capacitorNeed", value, stackingPenalties=True)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill("Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "duration", value, stackingPenalties = True)
if id == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"), "crystalVolatilityChance", value, stackingPenalties=True)
if id == 60: # Skirmish Burst: Evasive Maneuvers: Agility
fit.ship.boostItemAttr("agility", value, stackingPenalties=True)
def handler(fit, module, context):
for x in xrange(1, 4):
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x))
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
print "Buff ID: ",id," value: ",value
if id:
if 'commandRun' not in context:
print "Add buffID", id, " to ", fit
fit.addCommandBonus(id, value, module, kwargs['effect'])
elif kwargs['warfareBuffID'] is not None and kwargs['warfareBuffID'] == id:
print "Running buffID ", kwargs['warfareBuffID'], " on ", fit
runEffect(kwargs['warfareBuffID'], value)
value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x))
module.multiplyItemAttr("warfareBuff{}Value".format(x), value)

View File

@@ -10,6 +10,7 @@
# Ships named like: Stratios (2 of 2)
# Subsystems named like: Offensive Covert Reconfiguration (4 of 4)
# Ship: Astero
# Ship: Rabisu
type = "passive"

View File

@@ -4,5 +4,5 @@
# Ship: Rabisu
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusLogistics1"), stackingPenalties=True, skill="Logistics Cruisers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("eliteBonusLogistics1"), stackingPenalties=True, skill="Logistics Cruisers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusLogistics1"), skill="Logistics Cruisers")
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("eliteBonusLogistics1"), skill="Logistics Cruisers")

View File

@@ -1,7 +1,7 @@
# energyWeaponDamageMultiply
#
# Used by:
# Modules from group: Heat Sink (17 of 17)
# Modules from group: Heat Sink (18 of 18)
# Modules named like: QA Multiship Module Players (4 of 4)
# Module: QA Damage Module
type = "passive"

View File

@@ -1,7 +1,7 @@
# energyWeaponSpeedMultiply
#
# Used by:
# Modules from group: Heat Sink (17 of 17)
# Modules from group: Heat Sink (18 of 18)
type = "passive"

View File

@@ -1,7 +1,7 @@
# hybridWeaponDamageMultiply
#
# Used by:
# Modules from group: Magnetic Field Stabilizer (12 of 12)
# Modules from group: Magnetic Field Stabilizer (14 of 14)
# Modules named like: QA Multiship Module Players (4 of 4)
# Module: QA Damage Module
type = "passive"

View File

@@ -1,7 +1,7 @@
# hybridWeaponSpeedMultiply
#
# Used by:
# Modules from group: Magnetic Field Stabilizer (12 of 12)
# Modules from group: Magnetic Field Stabilizer (14 of 14)
type = "passive"

View File

@@ -5,7 +5,7 @@
type = "passive"
def handler(fit, src, context):
lvl = src.level
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Value", src.getModifiedItemAttr("commandStrengthBonus") * lvl)

View File

@@ -6,16 +6,16 @@ type = "passive"
def handler(fit, src, context):
fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), "shieldCapacity",
fit.fighters.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Fighters"), "shieldCapacity",
src.getModifiedItemAttr("fighterBonusShieldCapacityPercent"))
fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), "maxVelocity",
fit.fighters.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Fighters"), "maxVelocity",
src.getModifiedItemAttr("fighterBonusVelocityPercent"), stackingPenalties=True)
fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"),
fit.fighters.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Fighters"),
"fighterAbilityAttackMissileDuration",
src.getModifiedItemAttr("fighterBonusROFPercent"), stackingPenalties=True)
fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), "fighterAbilityAttackTurretDuration",
fit.fighters.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Fighters"), "fighterAbilityAttackTurretDuration",
src.getModifiedItemAttr("fighterBonusROFPercent"), stackingPenalties=True)
fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), "fighterAbilityMissilesDuration",
fit.fighters.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Fighters"), "fighterAbilityMissilesDuration",
src.getModifiedItemAttr("fighterBonusROFPercent"), stackingPenalties=True)
fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"), "shieldRechargeRate",
fit.fighters.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Fighters"), "shieldRechargeRate",
src.getModifiedItemAttr("fighterBonusShieldRechargePercent"))

View File

@@ -2,10 +2,24 @@
#
# Used by:
# Variations of module: Armor Command Burst I (2 of 2)
type = "active"
runTime = "early"
def handler(fit, module, context):
for x in xrange(1, 4):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
module.multiplyChargeAttr("warfareBuff{}Multiplier".format(x), value)
'''
Some documentation:
When the fit is calculated, we gather up all the gang effects and stick them onto the fit. We don't run the actual
effect yet, only give the fit details so that it can run the effect at a later time. We need to do this so that we can
only run the strongest effect. When we are done, one of the last things that we do with the fit is to loop through those
bonuses and actually run the effect. To do this, we have a special argument passed into the effect handler that tells it
which warfareBuffID to run (shouldn't need this right now, but better safe than sorry)
'''
type = "active", "gang"
def handler(fit, module, context, **kwargs):
for x in xrange(1, 5):
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
if id:
fit.addCommandBonus(id, value, module, kwargs['effect'])

View File

@@ -2,10 +2,13 @@
#
# Used by:
# Variations of module: Information Command Burst I (2 of 2)
type = "active"
runTime = "early"
def handler(fit, module, context):
for x in xrange(1, 4):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
module.multiplyChargeAttr("warfareBuff{}Multiplier".format(x), value)
type = "active", "gang"
def handler(fit, module, context, **kwargs):
for x in xrange(1, 5):
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
if id:
fit.addCommandBonus(id, value, module, kwargs['effect'])

View File

@@ -2,10 +2,13 @@
#
# Used by:
# Variations of module: Mining Foreman Burst I (2 of 2)
type = "active"
runTime = "late"
def handler(fit, module, context):
for x in xrange(1, 4):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
module.multiplyChargeAttr("warfareBuff{}Multiplier".format(x), value)
type = "active", "gang"
def handler(fit, module, context, **kwargs):
for x in xrange(1, 5):
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
if id:
fit.addCommandBonus(id, value, module, kwargs['effect'])

View File

@@ -2,10 +2,13 @@
#
# Used by:
# Variations of module: Shield Command Burst I (2 of 2)
type = "active"
runTime = "early"
def handler(fit, module, context):
for x in xrange(1, 4):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
module.multiplyChargeAttr("warfareBuff{}Multiplier".format(x), value)
type = "active", "gang"
def handler(fit, module, context, **kwargs):
for x in xrange(1, 5):
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
if id:
fit.addCommandBonus(id, value, module, kwargs['effect'])

View File

@@ -2,10 +2,13 @@
#
# Used by:
# Variations of module: Skirmish Command Burst I (2 of 2)
type = "active"
runTime = "early"
def handler(fit, module, context):
for x in xrange(1, 4):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
module.multiplyChargeAttr("warfareBuff{}Multiplier".format(x), value)
type = "active", "gang"
def handler(fit, module, context, **kwargs):
for x in xrange(1, 5):
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
if id:
fit.addCommandBonus(id, value, module, kwargs['effect'])

View File

@@ -2,74 +2,16 @@
#
# Used by:
# Modules from group: Titan Phenomena Generator (4 of 4)
type = "active", "gang"
def handler(fit, module, context, **kwargs):
def runEffect(id, value):
if id == 39: # Avatar Effect Generator : Capacitor Recharge bonus
fit.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True)
if id == 40: # Avatar Effect Generator : Kinetic resistance bonus
for attr in ("armorKineticDamageResonance", "shieldKineticDamageResonance", "hullKineticDamageResonance"):
fit.ship.boostItemAttr(attr, value, stackingPenalties=True)
if id == 41: # Erebus Effect Generator : Armor HP bonus
fit.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
if id == 42: # Leviathan Effect Generator : Shield HP bonus
fit.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
if id == 43: # Erebus Effect Generator : Explosive resistance bonus
for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "hullExplosiveDamageResonance"):
fit.ship.boostItemAttr(attr, value, stackingPenalties=True)
if id == 44: # Erebus Effect Generator : Thermal resistance penalty
for attr in ("armorThermalDamageResonance", "shieldThermalDamageResonance", "hullThermalDamageResonance"):
fit.ship.boostItemAttr(attr, value, stackingPenalties=True)
if id == 45: # Ragnarok Effect Generator : Signature Radius bonus
fit.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
if id == 46: # Ragnarok Effect Generator : Thermal resistance bonus
for attr in ("armorThermalDamageResonance", "shieldThermalDamageResonance", "hullThermalDamageResonance"):
fit.ship.boostItemAttr(attr, value, stackingPenalties=True)
if id == 47: # Ragnarok Effect Generator : Explosive resistance penaly
for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "hullExplosiveDamageResonance"):
fit.ship.boostItemAttr(attr, value, stackingPenalties=True)
if id == 48: # Leviathan Effect Generator : Shield HP bonus
fit.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
if id == 49: # Leviathan Effect Generator : EM resistance bonus
for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "hullEmDamageResonance"):
fit.ship.boostItemAttr(attr, value, stackingPenalties=True)
if id == 50: # Leviathan Effect Generator : Kinetic resistance penalty
for attr in ("armorKineticDamageResonance", "shieldKineticDamageResonance", "hullKineticDamageResonance"):
fit.ship.boostItemAttr(attr, value, stackingPenalties=True)
if id == 51: # Avatar Effect Generator : Velocity penalty
fit.ship.boostItemAttr("maxVelocity", value, stackingPenalties=True)
if id == 52: # Erebus Effect Generator : Shield RR penalty
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", value, stackingPenalties=True)
if id == 53: # Leviathan Effect Generator : Armor RR penalty
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", value, stackingPenalties=True)
if id == 54: # Ragnarok Effect Generator : Laser and Hybrid Optimal penalty
groups = ("Energy Weapon", "Hybrid Weapon")
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True)
for x in xrange(1, 4):
if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x))
id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x))
for x in xrange(1, 5):
if module.getModifiedItemAttr("warfareBuff{}ID".format(x)):
value = module.getModifiedItemAttr("warfareBuff{}Value".format(x))
id = module.getModifiedItemAttr("warfareBuff{}ID".format(x))
if id:
if 'commandRun' not in context:
fit.addCommandBonus(id, value, module, kwargs['effect'])
elif kwargs['warfareBuffID'] is not None and kwargs['warfareBuffID'] == id:
runEffect(kwargs['warfareBuffID'], value)
fit.addCommandBonus(id, value, module, kwargs['effect'])

View File

@@ -2,7 +2,7 @@
#
# Used by:
# Modules from group: Missile Launcher Torpedo (22 of 22)
# Items from market group: Ship Equipment > Turrets & Bays (428 of 854)
# Items from market group: Ship Equipment > Turrets & Bays (428 of 859)
# Module: Interdiction Sphere Launcher I
type = "overheat"

View File

@@ -1,7 +1,7 @@
# projectileWeaponDamageMultiply
#
# Used by:
# Modules from group: Gyrostabilizer (12 of 12)
# Modules from group: Gyrostabilizer (13 of 13)
# Modules named like: QA Multiship Module Players (4 of 4)
# Module: QA Damage Module
type = "passive"

View File

@@ -1,7 +1,7 @@
# projectileWeaponSpeedMultiply
#
# Used by:
# Modules from group: Gyrostabilizer (12 of 12)
# Modules from group: Gyrostabilizer (13 of 13)
type = "passive"

View File

@@ -4,5 +4,5 @@
# Ship: Rabisu
type = "passive"
def handler(fit, src, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("roleBonusRepairRange"), stackingPenalties=True)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("roleBonusRepairRange"), stackingPenalties=True)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("roleBonusRepairRange"))
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("roleBonusRepairRange"))

View File

@@ -1,8 +1,8 @@
# setBonusAsklepian
#
# Used by:
# Implants named like: Asklepian Omega (3 of 3)
# Implants named like: Grade Asklepian (16 of 16)
# Implants named like: grade Asklepian Omega (2 of 2)
runTime = "early"
type = "passive"

View File

@@ -0,0 +1,10 @@
# shipBonusCommandDestroyerRole1DefenderBonus
#
# Used by:
# Ships from group: Command Destroyer (4 of 4)
type = "passive"
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Defender Missiles"),
"moduleReactivationDelay", ship.getModifiedItemAttr("shipBonusRole1"))

View File

@@ -18,5 +18,5 @@ def handler(fit, src, context):
fit.fighters.filteredItemBoost(lambda mod: mod.item.requiresSkill("Fighters"),
"fighterAbilityMissilesDamageMultiplier",
src.getModifiedItemAttr("damageMultiplierBonus") * lvl)
fit.drones.filteredItemBoost(lambda drone: drone.item.group.name == "Mining Drone", "miningDroneAmountPercent",
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Mining Drone Operation"), "miningDroneAmountPercent",
src.getModifiedItemAttr("miningAmountBonus") * lvl)

View File

@@ -0,0 +1,10 @@
# skillMultiplierDefenderMissileVelocity
#
# Used by:
# Skill: Defender Missiles
type = "passive"
def handler(fit, skill, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Defender Missiles"),
"maxVelocity", skill.getModifiedItemAttr("missileVelocityBonus") * skill.level)

View File

@@ -6,5 +6,28 @@ type = "passive"
def handler(fit, skill, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Burst Projectors",
"duration", skill.getModifiedItemAttr("projECMDurationBonus") * skill.level)
# We need to make sure that the attribute exists, otherwise we add attributes that don't belong. See #927
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and
mod.item.getAttribute("duration"),
"duration",
skill.getModifiedItemAttr("projECMDurationBonus") * skill.level)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and
mod.item.getAttribute("durationECMJammerBurstProjector"),
"durationECMJammerBurstProjector",
skill.getModifiedItemAttr("projECMDurationBonus") * skill.level)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and
mod.item.getAttribute("durationTargetIlluminationBurstProjector"),
"durationTargetIlluminationBurstProjector",
skill.getModifiedItemAttr("projECMDurationBonus") * skill.level)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and
mod.item.getAttribute("durationSensorDampeningBurstProjector"),
"durationSensorDampeningBurstProjector",
skill.getModifiedItemAttr("projECMDurationBonus") * skill.level)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Burst Projector Operation") and
mod.item.getAttribute("durationWeaponDisruptionBurstProjector"),
"durationWeaponDisruptionBurstProjector",
skill.getModifiedItemAttr("projECMDurationBonus") * skill.level)

View File

@@ -10,4 +10,4 @@ def handler(fit, container, context):
and container.state >= State.ACTIVE) or hasattr(container, "amountActive")):
amount = container.getModifiedItemAttr("energyNeutralizerAmount")
time = container.getModifiedItemAttr("duration")
fit.addDrain(time, amount, 0)
fit.addDrain(container, time, amount, 0)

View File

@@ -3,4 +3,5 @@ type = "passive"
def handler(fit, src, context):
fit.ship.increaseItemAttr("maxLockedTargets", src.getModifiedItemAttr("structureRigMaxTargetBonus"))
fit.extraAttributes.increase("maxTargetsLockedFromSkills", src.getModifiedItemAttr("structureRigMaxTargetBonus"))

View File

@@ -4,9 +4,27 @@
# Modules from group: Missile Launcher Heavy (12 of 12)
# Modules from group: Missile Launcher Rocket (15 of 15)
# Modules named like: Launcher (151 of 151)
type = 'active'
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
@@ -449,9 +448,7 @@ class Fit(object):
# oh fuck this is so janky
# @todo should we pass in min/max to this function, or is abs okay?
# (abs is old method, ccp now provides the aggregate function in their data)
print "Add command bonus: ", warfareBuffID, " - value: ", value
if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][0]) < abs(value):
if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][1]) < abs(value):
self.commandBonuses[warfareBuffID] = (runTime, value, module, effect)
def __runCommandBoosts(self, runTime="normal"):
@@ -463,31 +460,176 @@ class Fit(object):
if runTime != effect_runTime:
continue
context = ("commandRun", thing.__class__.__name__.lower())
if isinstance(thing, Module):
# This should always be a gang effect, otherwise it wouldn't be added to commandBonuses
# @todo: Check this
if effect.isType("gang"):
# todo: ensure that these are run with the module is active only
context += ("commandRun",)
self.register(thing)
effect.handler(self, thing, context, warfareBuffID = warfareBuffID)
# This should always be a gang effect, otherwise it wouldn't be added to commandBonuses
# @todo: Check this
if effect.isType("gang"):
self.register(thing)
# 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:
self.register(thing)
effect.handler(self, thing, context)
except:
pass
if warfareBuffID == 10: # Shield Burst: Shield Harmonizing: Shield Resistance
for damageType in ("Em", "Explosive", "Thermal", "Kinetic"):
self.ship.boostItemAttr("shield%sDamageResonance" % damageType, value)
if warfareBuffID == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
"Shield Emission Systems"), "capacitorNeed", value)
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill(
"Shield Emission Systems"), "duration", value)
if warfareBuffID == 12: # Shield Burst: Shield Extension: Shield HP
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
if warfareBuffID == 13: # Armor Burst: Armor Energizing: Armor Resistance
for damageType in ("Em", "Thermal", "Explosive", "Kinetic"):
self.ship.boostItemAttr("armor%sDamageResonance" % damageType, value)
if warfareBuffID == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(
"Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"),
"capacitorNeed", value)
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(
"Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), "duration",
value)
if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP
self.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
if warfareBuffID == 16: # Information Burst: Sensor Optimization: Scan Resolution
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
if warfareBuffID == 17: # Information Burst: Electronic Superiority: EWAR Range and Strength
groups = ("ECM", "Sensor Dampener", "Weapon Disruptor", "Target Painter")
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value,
stackingPenalties=True)
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups,
"falloffEffectiveness", value, stackingPenalties=True)
for scanType in ("Magnetometric", "Radar", "Ladar", "Gravimetric"):
self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "ECM",
"scan%sStrengthBonus" % scanType, value,
stackingPenalties=True)
for attr in (
"missileVelocityBonus", "explosionDelayBonus", "aoeVelocityBonus", "falloffBonus",
"maxRangeBonus", "aoeCloudSizeBonus", "trackingSpeedBonus"):
self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Weapon Disruptor",
attr, value)
for attr in ("maxTargetRangeBonus", "scanResolutionBonus"):
self.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Sensor Dampener",
attr, value)
self.modules.filteredItemBoost(lambda mod: mod.item.gorup.name == "Target Painter",
"signatureRadiusBonus", value, stackingPenalties=True)
if warfareBuffID == 18: # Information Burst: Electronic Hardening: Scan Strength
for scanType in ("Gravimetric", "Radar", "Ladar", "Magnetometric"):
self.ship.boostItemAttr("scan%sStrength" % scanType, value, stackingPenalties=True)
if warfareBuffID == 19: # Information Burst: Electronic Hardening: RSD/RWD Resistance
self.ship.boostItemAttr("sensorDampenerResistance", value)
self.ship.boostItemAttr("weaponDisruptionResistance", value)
if warfareBuffID == 26: # Information Burst: Sensor Optimization: Targeting Range
self.ship.boostItemAttr("maxTargetRange", value)
if warfareBuffID == 20: # Skirmish Burst: Evasive Maneuvers: Signature Radius
self.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
if warfareBuffID == 21: # Skirmish Burst: Interdiction Maneuvers: Tackle Range
groups = ("Stasis Web", "Warp Scrambler")
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value,
stackingPenalties=True)
if warfareBuffID == 22: # Skirmish Burst: Rapid Deployment: AB/MWD Speed Increase
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Afterburner") or mod.item.requiresSkill(
"High Speed Maneuvering"), "speedFactor", value, stackingPenalties=True)
if warfareBuffID == 23: # Mining Burst: Mining Laser Field Enhancement: Mining/Survey Range
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "maxRange",
value, stackingPenalties=True)
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"),
"surveyScanRange", value, stackingPenalties=True)
if warfareBuffID == 24: # Mining Burst: Mining Laser Optimization: Mining Capacitor/Duration
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"),
"capacitorNeed", value, stackingPenalties=True)
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "duration",
value, stackingPenalties=True)
if warfareBuffID == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"),
"crystalVolatilityChance", value, stackingPenalties=True)
if warfareBuffID == 60: # Skirmish Burst: Evasive Maneuvers: Agility
self.ship.boostItemAttr("agility", value, stackingPenalties=True)
# Titan effects
if warfareBuffID == 39: # Avatar Effect Generator : Capacitor Recharge bonus
self.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True)
if warfareBuffID == 40: # Avatar Effect Generator : Kinetic resistance bonus
for attr in ("armorKineticDamageResonance", "shieldKineticDamageResonance", "kineticDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 41: # Avatar Effect Generator : EM resistance penalty
for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "emDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 42: # Erebus Effect Generator : Armor HP bonus
self.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
if warfareBuffID == 43: # Erebus Effect Generator : Explosive resistance bonus
for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "explosiveDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 44: # Erebus Effect Generator : Thermal resistance penalty
for attr in ("armorThermalDamageResonance", "shieldThermalDamageResonance", "thermalDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 45: # Ragnarok Effect Generator : Signature Radius bonus
self.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
if warfareBuffID == 46: # Ragnarok Effect Generator : Thermal resistance bonus
for attr in ("armorThermalDamageResonance", "shieldThermalDamageResonance", "thermalDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 47: # Ragnarok Effect Generator : Explosive resistance penaly
for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "explosiveDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 48: # Leviathan Effect Generator : Shield HP bonus
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
if warfareBuffID == 49: # Leviathan Effect Generator : EM resistance bonus
for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "emDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 50: # Leviathan Effect Generator : Kinetic resistance penalty
for attr in ("armorKineticDamageResonance", "shieldKineticDamageResonance", "kineticDamageResonance"):
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 51: # Avatar Effect Generator : Velocity penalty
self.ship.boostItemAttr("maxVelocity", value, stackingPenalties=True)
if warfareBuffID == 52: # Erebus Effect Generator : Shield RR penalty
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", value, stackingPenalties=True)
if warfareBuffID == 53: # Leviathan Effect Generator : Armor RR penalty
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", value, stackingPenalties=True)
if warfareBuffID == 54: # Ragnarok Effect Generator : Laser and Hybrid Optimal penalty
groups = ("Energy Weapon", "Hybrid Weapon")
self.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True)
del self.commandBonuses[warfareBuffID]
@@ -506,7 +648,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
@@ -514,31 +655,11 @@ class Fit(object):
eos.db.saveddata_session.delete(self)
if self.commandFits and not withBoosters:
print "Calculatate command fits and apply to fit"
for fit in self.commandFits:
if self == fit:
print "nope"
continue
print "calculating ", fit
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
@@ -625,7 +746,7 @@ class Fit(object):
self.__calculated = True
# Only apply projected fits if fit it not projected itself.
if not projected:
if not projected and not withBoosters:
for fit in self.projectedFits:
if fit.getProjectionInfo(self.ID).active:
fit.calculateModifiedAttributes(self, withBoosters=withBoosters, dirtyStorage=dirtyStorage)

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

@@ -201,6 +201,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
else:
return self.__chargeCycles
@property
def modPosition(self):
if self.owner:
return self.owner.modules.index(self)
@property
def hpBeforeReload(self):
"""
@@ -265,7 +270,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
flightTime = self.getModifiedChargeAttr("explosionDelay") / 1000.0
mass = self.getModifiedChargeAttr("mass")
agility = self.getModifiedChargeAttr("agility")
if maxVelocity and flightTime and mass and agility:
if maxVelocity and (flightTime or mass or agility):
accelTime = min(flightTime, mass * agility / 1000000)
# Average distance done during acceleration
duringAcceleration = maxVelocity / 2 * accelTime
@@ -655,10 +660,14 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
effect.activeByDefault and \
(effect.isType("offline") or
(effect.isType("passive") and self.state >= State.ONLINE) or
(effect.isType("active") and self.state >= State.ACTIVE))\
and ((projected and effect.isType("projected")) or not projected)\
and ((gang and effect.isType("gang")) or not gang):
effect.handler(fit, self, context)
try:
effect.handler(fit, self, context, effect=effect)
except:
effect.handler(fit, self, context)
@property
def cycleTime(self):

View File

@@ -40,7 +40,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

5
fun.py Normal file
View File

@@ -0,0 +1,5 @@
problem 15
number of moves: XxY
each block added introduces two possible moves

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

@@ -224,7 +224,7 @@ class FittingView(d.Display):
if row != -1 and row not in self.blanks:
data = wx.PyTextDataObject()
data.SetText("fitting:"+str(self.mods[row].position))
data.SetText("fitting:"+str(self.mods[row].modPosition))
dropSource = wx.DropSource(self)
dropSource.SetData(data)
@@ -356,7 +356,7 @@ class FittingView(d.Display):
if dstRow != -1 and dstRow not in self.blanks:
sFit = service.Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].position, srcIdx)
moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].modPosition, srcIdx)
if moduleChanged is None:
# the new module doesn't fit in specified slot, try to simply append it
wx.PostEvent(self.mainFrame, gui.marketBrowser.ItemSelected(itemID=srcIdx))
@@ -371,7 +371,7 @@ class FittingView(d.Display):
module = self.mods[dstRow]
sFit = service.Fit.getInstance()
sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, srcIdx, mstate.CmdDown() and module.isEmpty)
sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.modPosition, srcIdx, mstate.CmdDown() and module.isEmpty)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
@@ -389,6 +389,7 @@ class FittingView(d.Display):
dstRow, _ = self.HitTest((x, y))
if dstRow != -1 and dstRow not in self.blanks:
mod1 = fit.modules[srcIdx]
mod2 = self.mods[dstRow]
@@ -397,9 +398,9 @@ class FittingView(d.Display):
return
if clone and mod2.isEmpty:
sFit.cloneModule(self.mainFrame.getActiveFit(), mod1.position, mod2.position)
sFit.cloneModule(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
else:
sFit.swapModules(self.mainFrame.getActiveFit(), mod1.position, mod2.position)
sFit.swapModules(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))

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

@@ -34,6 +34,7 @@ Stage3Selected, EVT_SB_STAGE3_SEL = wx.lib.newevent.NewEvent()
SearchSelected, EVT_SB_SEARCH_SEL = wx.lib.newevent.NewEvent()
ImportSelected, EVT_SB_IMPORT_SEL = wx.lib.newevent.NewEvent()
class PFWidgetsContainer(PFListPane):
def __init__(self,parent):
PFListPane.__init__(self,parent)
@@ -685,7 +686,8 @@ class ShipBrowser(wx.Panel):
# set map & cache of fittings per category
for cat in self.categoryList:
self.categoryFitCache[cat.ID] = sFit.groupHasFits(cat.ID)
itemIDs = [x.ID for x in cat.items]
self.categoryFitCache[cat.ID] = sFit.countFitsWithShip(itemIDs) > 1
for ship in self.categoryList:
if self.filterShipsWithNoFits and not self.categoryFitCache[ship.ID]:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Some files were not shown because too many files have changed in this diff Show More