diff --git a/config.py b/config.py index 495bc7fe6..7e5cf7694 100644 --- a/config.py +++ b/config.py @@ -1,90 +1,90 @@ -import os -import sys - -# Load variable overrides specific to distribution type -try: - import configforced -except ImportError: - configforced = None - -# Turns on debug mode -debug = False -# Defines if our saveddata will be in pyfa root or not -saveInRoot = False - -# Version data -version = "1.6.2" -tag = "git" -expansionName = "Phoebe" -expansionVersion = "1.0" -evemonMinVersion = "4081" - -# Database version (int ONLY) -# Increment every time we need to flag for user database upgrade/modification -dbversion = 3 - -pyfaPath = None -savePath = None -staticPath = None -saveDB = None -gameDB = None - -def defPaths(): - global pyfaPath - global savePath - global staticPath - global saveDB - global gameDB - global saveInRoot - # The main pyfa directory which contains run.py - # Python 2.X uses ANSI by default, so we need to convert the character encoding - pyfaPath = getattr(configforced, "pyfaPath", pyfaPath) - if pyfaPath is None: - pyfaPath = unicode(os.path.dirname(os.path.realpath(os.path.abspath( - sys.modules['__main__'].__file__))), sys.getfilesystemencoding()) - - # Where we store the saved fits etc, default is the current users home directory - if saveInRoot is True: - savePath = getattr(configforced, "savePath", None) - if savePath is None: - savePath = os.path.join(pyfaPath, "saveddata") - else: - savePath = getattr(configforced, "savePath", None) - if savePath is None: - savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")), - sys.getfilesystemencoding()) - - # Redirect stderr to file if we're requested to do so - stderrToFile = getattr(configforced, "stderrToFile", None) - if stderrToFile is True: - if not os.path.exists(savePath): - os.mkdir(savePath) - sys.stderr = open(os.path.join(savePath, "error_log.txt"), "w") - - # Same for stdout - stdoutToFile = getattr(configforced, "stdoutToFile", None) - if stdoutToFile is True: - if not os.path.exists(savePath): - os.mkdir(savePath) - sys.stdout = open(os.path.join(savePath, "output_log.txt"), "w") - - # Static EVE Data from the staticdata repository, should be in the staticdata - # directory in our pyfa directory - staticPath = os.path.join(pyfaPath, "staticdata") - - # The database where we store all the fits etc - saveDB = os.path.join(savePath, "saveddata.db") - - # The database where the static EVE data from the datadump is kept. - # This is not the standard sqlite datadump but a modified version created by eos - # maintenance script - gameDB = os.path.join(staticPath, "eve.db") - - ## DON'T MODIFY ANYTHING BELOW ## - import eos.config - - #Caching modifiers, disable all gamedata caching, its unneeded. - eos.config.gamedataCache = False - # saveddata db location modifier, shouldn't ever need to touch this - eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" - eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" +import os +import sys + +# Load variable overrides specific to distribution type +try: + import configforced +except ImportError: + configforced = None + +# Turns on debug mode +debug = False +# Defines if our saveddata will be in pyfa root or not +saveInRoot = False + +# Version data +version = "1.8.2" +tag = "git" +expansionName = "Proteus" +expansionVersion = "1.0" +evemonMinVersion = "4081" + +# Database version (int ONLY) +# Increment every time we need to flag for user database upgrade/modification +dbversion = 5 + +pyfaPath = None +savePath = None +staticPath = None +saveDB = None +gameDB = None + +def defPaths(): + global pyfaPath + global savePath + global staticPath + global saveDB + global gameDB + global saveInRoot + # The main pyfa directory which contains run.py + # Python 2.X uses ANSI by default, so we need to convert the character encoding + pyfaPath = getattr(configforced, "pyfaPath", pyfaPath) + if pyfaPath is None: + pyfaPath = unicode(os.path.dirname(os.path.realpath(os.path.abspath( + sys.modules['__main__'].__file__))), sys.getfilesystemencoding()) + + # Where we store the saved fits etc, default is the current users home directory + if saveInRoot is True: + savePath = getattr(configforced, "savePath", None) + if savePath is None: + savePath = os.path.join(pyfaPath, "saveddata") + else: + savePath = getattr(configforced, "savePath", None) + if savePath is None: + savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")), + sys.getfilesystemencoding()) + + # Redirect stderr to file if we're requested to do so + stderrToFile = getattr(configforced, "stderrToFile", None) + if stderrToFile is True: + if not os.path.exists(savePath): + os.mkdir(savePath) + sys.stderr = open(os.path.join(savePath, "error_log.txt"), "w") + + # Same for stdout + stdoutToFile = getattr(configforced, "stdoutToFile", None) + if stdoutToFile is True: + if not os.path.exists(savePath): + os.mkdir(savePath) + sys.stdout = open(os.path.join(savePath, "output_log.txt"), "w") + + # Static EVE Data from the staticdata repository, should be in the staticdata + # directory in our pyfa directory + staticPath = os.path.join(pyfaPath, "staticdata") + + # The database where we store all the fits etc + saveDB = os.path.join(savePath, "saveddata.db") + + # The database where the static EVE data from the datadump is kept. + # This is not the standard sqlite datadump but a modified version created by eos + # maintenance script + gameDB = os.path.join(staticPath, "eve.db") + + ## DON'T MODIFY ANYTHING BELOW ## + import eos.config + + #Caching modifiers, disable all gamedata caching, its unneeded. + eos.config.gamedataCache = False + # saveddata db location modifier, shouldn't ever need to touch this + eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" + eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" diff --git a/eos/config.py b/eos/config.py index 192982e1f..35e2fe0dc 100644 --- a/eos/config.py +++ b/eos/config.py @@ -1,11 +1,11 @@ -import os.path +from os.path import realpath, join, dirname, abspath import sys debug = False gamedataCache = True saveddataCache = True -gamedata_connectionstring = 'sqlite:///' + os.path.expanduser(os.path.join("~", ".pyfa","eve.db")) +gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "staticdata", "eve.db")), sys.getfilesystemencoding()) saveddata_connectionstring = 'sqlite:///:memory:' #Autodetect path, only change if the autodetection bugs out. -path = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) +path = dirname(unicode(__file__, sys.getfilesystemencoding())) diff --git a/eos/db/__init__.py b/eos/db/__init__.py index bedb917d6..8867d63c5 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -75,7 +75,7 @@ from eos.db.saveddata.queries import getUser, getCharacter, getFit, getFitsWithS getFitList, getFleetList, getFleet, save, remove, commit, add, \ getCharactersForUser, getMiscData, getSquadsIDsWithFitID, getWing, \ getSquad, getBoosterFits, getProjectedFits, getTargetResistsList, getTargetResists,\ - clearPrices + clearPrices, countAllFits #If using in memory saveddata, you'll want to reflect it so the data structure is good. if config.saveddata_connectionstring == "sqlite:///:memory:": diff --git a/eos/db/gamedata/item.py b/eos/db/gamedata/item.py index 761921906..34a1a6189 100644 --- a/eos/db/gamedata/item.py +++ b/eos/db/gamedata/item.py @@ -30,6 +30,7 @@ items_table = Table("invtypes", gamedata_meta, Column("typeName", String, index=True), Column("description", String), Column("raceID", Integer), + Column("factionID", Integer), Column("volume", Float), Column("mass", Float), Column("capacity", Float), diff --git a/eos/db/migrations/upgrade3.py b/eos/db/migrations/upgrade3.py index 5467fdf2d..0350ded72 100644 --- a/eos/db/migrations/upgrade3.py +++ b/eos/db/migrations/upgrade3.py @@ -8,6 +8,6 @@ import sqlalchemy def upgrade(saveddata_engine): try: - saveddata_engine.execute("SELECT mode FROM fits LIMIT 1") + saveddata_engine.execute("SELECT modeID FROM fits LIMIT 1") except sqlalchemy.exc.DatabaseError: saveddata_engine.execute("ALTER TABLE fits ADD COLUMN modeID INTEGER") diff --git a/eos/db/migrations/upgrade4.py b/eos/db/migrations/upgrade4.py new file mode 100644 index 000000000..87906cffc --- /dev/null +++ b/eos/db/migrations/upgrade4.py @@ -0,0 +1,141 @@ +""" +Migration 4 + +- Converts modules based on Proteus Module Tiericide + Some modules have been unpublished (and unpublished module attributes are removed + from database), which causes pyfa to crash. We therefore replace these + modules with their new replacements + + Based on http://community.eveonline.com/news/patch-notes/patch-notes-for-proteus/ + and output of itemDiff.py +""" + + +CONVERSIONS = { + 506: ( # 'Basic' Capacitor Power Relay + 8205, # Alpha Reactor Control: Capacitor Power Relay + 8209, # Marked Generator Refitting: Capacitor Power Relay + 8203, # Partial Power Plant Manager: Capacity Power Relay + 8207, # Type-E Power Core Modification: Capacitor Power Relay + ), + 8177: ( # Mark I Compact Capacitor Power Relay + 8173, # Beta Reactor Control: Capacitor Power Relay I + ), + 8175: ( # Type-D Restrained Capacitor Power Relay + 8171, # Local Power Plant Manager: Capacity Power Relay I + ), + + 421: ( # 'Basic' Capacitor Recharger + 4425, # AGM Capacitor Charge Array, + 4421, # F-a10 Buffer Capacitor Regenerator + 4423, # Industrial Capacitor Recharger + 4427, # Secondary Parallel Link-Capacitor + ), + 4435: ( # Eutectic Compact Cap Recharger + 4433, # Barton Reactor Capacitor Recharger I + 4431, # F-b10 Nominal Capacitor Regenerator + 4437, # Fixed Parallel Link-Capacitor I + ), + + 1315: ( # 'Basic' Expanded Cargohold + 5483, # Alpha Hull Mod Expanded Cargo + 5479, # Marked Modified SS Expanded Cargo + 5481, # Partial Hull Conversion Expanded Cargo + 5485, # Type-E Altered SS Expanded Cargo + ), + 5493: ( # Type-D Restrained Expanded Cargo + 5491, # Beta Hull Mod Expanded Cargo + 5489, # Local Hull Conversion Expanded Cargo I + 5487, # Mark I Modified SS Expanded Cargo + ), + + 1401: ( # 'Basic' Inertial Stabilizers + 5523, # Alpha Hull Mod Inertial Stabilizers + 5521, # Partial Hull Conversion Inertial Stabilizers + 5525, # Type-E Altered SS Inertial Stabilizers + ), + 5533: ( # Type-D Restrained Inertial Stabilizers + 5531, # Beta Hull Mod Inertial Stabilizers + 5529, # Local Hull Conversion Inertial Stabilizers I + 5527, # Mark I Modified SS Inertial Stabilizers + 5519, # Marked Modified SS Inertial Stabilizers + ), + + 5239: ( # EP-S Gaussian Scoped Mining Laser + 5241, # Dual Diode Mining Laser I + ), + 5233: ( # Single Diode Basic Mining Laser + 5231, # EP-R Argon Ion Basic Excavation Pulse + 5237, # Rubin Basic Particle Bore Stream + 5235, # Xenon Basic Drilling Beam + ), + 5245: ( # Particle Bore Compact Mining Laser + 5243, # XeCl Drilling Beam I + ), + + 22619: ( # Frigoris Restrained Ice Harvester Upgrade + 22617, # Crisium Ice Harvester Upgrade + ), + 22611: ( # Elara Restrained Mining Laser Upgrade + 22609, # Erin Mining Laser Upgrade + ), + + 1242: ( # 'Basic' Nanofiber Internal Structure + 5591, # Alpha Hull Mod Nanofiber Structure + 5595, # Marked Modified SS Nanofiber Structure + 5559, # Partial Hull Conversion Nanofiber Structure + 5593, # Type-E Altered SS Nanofiber Structure + ), + 5599: ( # Type-D Restrained Nanofiber Structure + 5597, # Beta Hull Mod Nanofiber Structure + 5561, # Local Hull Conversion Nanofiber Structure I + 5601, # Mark I Modified SS Nanofiber Structure + ), + + 1192: ( # 'Basic' Overdrive Injector System + 5613, # Alpha Hull Mod Overdrive Injector + 5617, # Marked Modified SS Overdrive Injector + 5611, # Partial Hull Conversion Overdrive Injector + 5615, # Type-E Altered SS Overdrive Injector + ), + 5631: ( # Type-D Restrained Overdrive Injector + 5629, # Beta Hull Mod Overdrive Injector + 5627, # Local Hull Conversion Overdrive Injector I + 5633, # Mark I Modified SS Overdrive Injector + ), + + 1537: ( # 'Basic' Power Diagnostic System + 8213, # Alpha Reactor Control: Diagnostic System + 8217, # Marked Generator Refitting: Diagnostic System + 8211, # Partial Power Plant Manager: Diagnostic System + 8215, # Type-E Power Core Modification: Diagnostic System + 8255, # Type-E Power Core Modification: Reaction Control + ), + 8225: ( # Mark I Compact Power Diagnostic System + 8221, # Beta Reactor Control: Diagnostic System I + 8219, # Local Power Plant Manager: Diagnostic System I + 8223, # Type-D Power Core Modification: Diagnostic System + ), + + 1240: ( # 'Basic' Reinforced Bulkheads + 5677, # Alpha Hull Mod Reinforced Bulkheads + 5681, # Marked Modified SS Reinforced Bulkheads + 5675, # Partial Hull Conversion Reinforced Bulkheads + 5679, # Type-E Altered SS Reinforced Bulkheads + ), + 5649: ( # Mark I Compact Reinforced Bulkheads + 5645, # Beta Hull Mod Reinforced Bulkheads + ), + 5647: ( # Type-D Restrained Reinforced Bulkheads + 5643, # Local Hull Conversion Reinforced Bulkheads I + ), +} + +def upgrade(saveddata_engine): + + # Convert modules + for replacement_item, list in CONVERSIONS.iteritems(): + for retired_item in list: + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) + diff --git a/eos/db/migrations/upgrade5.py b/eos/db/migrations/upgrade5.py new file mode 100644 index 000000000..cf6a3385d --- /dev/null +++ b/eos/db/migrations/upgrade5.py @@ -0,0 +1,8 @@ +""" +Migration 5 + +Simply deletes damage profiles with a blank name. See GH issue #256 +""" + +def upgrade(saveddata_engine): + saveddata_engine.execute('DELETE FROM damagePatterns WHERE name LIKE ?', ("",)) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 9fee22561..be7c078fc 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -267,6 +267,11 @@ def getBoosterFits(ownerID=None, where=None, eager=None): fits = saveddata_session.query(Fit).options(*eager).filter(filter).all() return fits +def countAllFits(): + with sd_lock: + count = saveddata_session.query(Fit).count() + return count + def countFitsWithShip(shipID, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. diff --git a/eos/effects/agilitymultipliereffect.py b/eos/effects/agilitymultipliereffect.py index 9478201f3..11306be36 100644 --- a/eos/effects/agilitymultipliereffect.py +++ b/eos/effects/agilitymultipliereffect.py @@ -1,9 +1,9 @@ # agilityMultiplierEffect # # Used by: -# Modules from group: Inertia Stabilizer (12 of 12) -# Modules from group: Nanofiber Internal Structure (14 of 14) -# Modules from group: Reinforced Bulkhead (12 of 12) +# Modules from group: Inertial Stabilizer (7 of 7) +# Modules from group: Nanofiber Internal Structure (7 of 7) +# Modules from group: Reinforced Bulkhead (8 of 8) type = "passive" def handler(fit, module, context): fit.ship.boostItemAttr("agility", diff --git a/eos/effects/capacitorcapacitymultiply.py b/eos/effects/capacitorcapacitymultiply.py index d761c502c..17e9d1da4 100644 --- a/eos/effects/capacitorcapacitymultiply.py +++ b/eos/effects/capacitorcapacitymultiply.py @@ -2,8 +2,8 @@ # # Used by: # Modules from group: Capacitor Flux Coil (6 of 6) -# Modules from group: Capacitor Power Relay (26 of 26) -# Modules from group: Power Diagnostic System (31 of 31) +# Modules from group: Capacitor Power Relay (20 of 20) +# Modules from group: Power Diagnostic System (23 of 23) # Modules from group: Propulsion Module (107 of 107) # Modules from group: Reactor Control Unit (22 of 22) # Modules from group: Shield Flux Coil (11 of 11) diff --git a/eos/effects/cargocapacitymultiply.py b/eos/effects/cargocapacitymultiply.py index c98c31387..c476f9b79 100644 --- a/eos/effects/cargocapacitymultiply.py +++ b/eos/effects/cargocapacitymultiply.py @@ -1,9 +1,9 @@ # cargoCapacityMultiply # # Used by: -# Modules from group: Expanded Cargohold (13 of 13) -# Modules from group: Overdrive Injector System (14 of 14) -# Modules from group: Reinforced Bulkhead (12 of 12) +# Modules from group: Expanded Cargohold (7 of 7) +# Modules from group: Overdrive Injector System (7 of 7) +# Modules from group: Reinforced Bulkhead (8 of 8) type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("capacity", module.getModifiedItemAttr("cargoCapacityMultiplier")) diff --git a/eos/effects/drawbackarmorhp.py b/eos/effects/drawbackarmorhp.py index d60b1369a..28d24015e 100644 --- a/eos/effects/drawbackarmorhp.py +++ b/eos/effects/drawbackarmorhp.py @@ -1,7 +1,7 @@ # drawbackArmorHP # # Used by: -# Modules from group: Rig Navigation (48 of 68) +# Modules from group: Rig Navigation (48 of 64) type = "passive" def handler(fit, module, context): fit.ship.boostItemAttr("armorHP", module.getModifiedItemAttr("drawback")) \ No newline at end of file diff --git a/eos/effects/drawbackwarpspeed.py b/eos/effects/drawbackwarpspeed.py index 70ae85e3b..229a6cd14 100644 --- a/eos/effects/drawbackwarpspeed.py +++ b/eos/effects/drawbackwarpspeed.py @@ -1,7 +1,7 @@ # drawbackWarpSpeed # # Used by: -# Modules named like: Higgs Anchor I (4 of 4) +# Modules from group: Rig Anchor (4 of 4) type = "passive" def handler(fit, module, context): fit.ship.boostItemAttr("warpSpeedMultiplier", module.getModifiedItemAttr("drawback"), stackingPenalties=True) diff --git a/eos/effects/dreadnoughtmd1projdmgbonus.py b/eos/effects/dreadnoughtmd1projdmgbonus.py index 303d8b87f..4f3d7f264 100644 --- a/eos/effects/dreadnoughtmd1projdmgbonus.py +++ b/eos/effects/dreadnoughtmd1projdmgbonus.py @@ -1,7 +1,7 @@ # dreadnoughtMD1ProjDmgBonus # # Used by: -# Ship: Naglfar +# Ships named like: Naglfar (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Minmatar Dreadnought").level diff --git a/eos/effects/dreadnoughtmd3projrofbonus.py b/eos/effects/dreadnoughtmd3projrofbonus.py index 639985a44..6ecbc500d 100644 --- a/eos/effects/dreadnoughtmd3projrofbonus.py +++ b/eos/effects/dreadnoughtmd3projrofbonus.py @@ -1,7 +1,7 @@ # dreadnoughtMD3ProjRoFBonus # # Used by: -# Ship: Naglfar +# Ships named like: Naglfar (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Minmatar Dreadnought").level diff --git a/eos/effects/dreadnoughtshipbonushybriddmgg1.py b/eos/effects/dreadnoughtshipbonushybriddmgg1.py index c0921b5d3..75e111b79 100644 --- a/eos/effects/dreadnoughtshipbonushybriddmgg1.py +++ b/eos/effects/dreadnoughtshipbonushybriddmgg1.py @@ -1,7 +1,7 @@ # dreadnoughtShipBonusHybridDmgG1 # # Used by: -# Ship: Moros +# Ships named like: Moros (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Dreadnought").level diff --git a/eos/effects/dreadnoughtshipbonushybridrofg2.py b/eos/effects/dreadnoughtshipbonushybridrofg2.py index ca1a8325e..f28c752a1 100644 --- a/eos/effects/dreadnoughtshipbonushybridrofg2.py +++ b/eos/effects/dreadnoughtshipbonushybridrofg2.py @@ -1,7 +1,7 @@ # dreadnoughtShipBonusHybridRoFG2 # # Used by: -# Ship: Moros +# Ships named like: Moros (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Dreadnought").level diff --git a/eos/effects/dreadnoughtshipbonuslasercapneeda1.py b/eos/effects/dreadnoughtshipbonuslasercapneeda1.py index 53912cfb1..d5cce7464 100644 --- a/eos/effects/dreadnoughtshipbonuslasercapneeda1.py +++ b/eos/effects/dreadnoughtshipbonuslasercapneeda1.py @@ -1,7 +1,7 @@ # dreadnoughtShipBonusLaserCapNeedA1 # # Used by: -# Ship: Revelation +# Ships named like: Revelation (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Amarr Dreadnought").level diff --git a/eos/effects/dreadnoughtshipbonuslaserrofa2.py b/eos/effects/dreadnoughtshipbonuslaserrofa2.py index d01246d24..dee4b663b 100644 --- a/eos/effects/dreadnoughtshipbonuslaserrofa2.py +++ b/eos/effects/dreadnoughtshipbonuslaserrofa2.py @@ -1,7 +1,7 @@ # dreadnoughtShipBonusLaserRofA2 # # Used by: -# Ship: Revelation +# Ships named like: Revelation (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Amarr Dreadnought").level diff --git a/eos/effects/dreadnoughtshipbonusshieldresistancesc2.py b/eos/effects/dreadnoughtshipbonusshieldresistancesc2.py index a837c7a36..3f1ef0feb 100644 --- a/eos/effects/dreadnoughtshipbonusshieldresistancesc2.py +++ b/eos/effects/dreadnoughtshipbonusshieldresistancesc2.py @@ -1,7 +1,7 @@ # dreadnoughtShipBonusShieldResistancesC2 # # Used by: -# Ship: Phoenix +# Ships named like: Phoenix (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Caldari Dreadnought").level diff --git a/eos/effects/elitereconbonusassaultlauncherrof1.py b/eos/effects/elitereconbonusassaultlauncherrof1.py deleted file mode 100644 index b4237469e..000000000 --- a/eos/effects/elitereconbonusassaultlauncherrof1.py +++ /dev/null @@ -1,10 +0,0 @@ -# eliteReconBonusAssaultLauncherROF1 -# -# Used by: -# Ship: Huginn -# Ship: Lachesis -type = "passive" -def handler(fit, ship, context): - level = fit.character.getSkill("Recon Ships").level - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Rapid Light", - "speed", ship.getModifiedItemAttr("eliteBonusReconShip1") * level) diff --git a/eos/effects/elitereconbonusheavyassaultlauncherrof1.py b/eos/effects/elitereconbonusheavyassaultlauncherrof1.py deleted file mode 100644 index 15c7c8e29..000000000 --- a/eos/effects/elitereconbonusheavyassaultlauncherrof1.py +++ /dev/null @@ -1,9 +0,0 @@ -# eliteReconBonusHeavyAssaultLauncherROF1 -# -# Used by: -# Ship: Huginn -type = "passive" -def handler(fit, ship, context): - level = fit.character.getSkill("Recon Ships").level - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy Assault", - "speed", ship.getModifiedItemAttr("eliteBonusReconShip1") * level) diff --git a/eos/effects/elitereconbonusheavylauncherrof1.py b/eos/effects/elitereconbonusheavylauncherrof1.py deleted file mode 100644 index bbce42793..000000000 --- a/eos/effects/elitereconbonusheavylauncherrof1.py +++ /dev/null @@ -1,10 +0,0 @@ -# eliteReconBonusHeavyLauncherROF1 -# -# Used by: -# Ship: Huginn -# Ship: Lachesis -type = "passive" -def handler(fit, ship, context): - level = fit.character.getSkill("Recon Ships").level - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy", - "speed", ship.getModifiedItemAttr("eliteBonusReconShip1") * level) diff --git a/eos/effects/elitereconbonusmhtoptimalrange1.py b/eos/effects/elitereconbonusmhtoptimalrange1.py new file mode 100644 index 000000000..4f9ad6048 --- /dev/null +++ b/eos/effects/elitereconbonusmhtoptimalrange1.py @@ -0,0 +1,9 @@ +# eliteReconBonusMHTOptimalRange1 +# +# Used by: +# Ship: Lachesis +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Recon Ships").level + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Hybrid Turret"), + "maxRange", ship.getModifiedItemAttr("eliteBonusReconShip1") * level) diff --git a/eos/effects/elitereconbonusmptdamage1.py b/eos/effects/elitereconbonusmptdamage1.py new file mode 100644 index 000000000..1a8b12924 --- /dev/null +++ b/eos/effects/elitereconbonusmptdamage1.py @@ -0,0 +1,9 @@ +# eliteReconBonusMPTdamage1 +# +# Used by: +# Ship: Huginn +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Recon Ships").level + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Medium Projectile Turret"), + "damageMultiplier", ship.getModifiedItemAttr("eliteBonusReconShip1") * level) diff --git a/eos/effects/elitereconbonusneutrange3.py b/eos/effects/elitereconbonusneutrange3.py new file mode 100644 index 000000000..88b6ecb03 --- /dev/null +++ b/eos/effects/elitereconbonusneutrange3.py @@ -0,0 +1,9 @@ +# eliteReconBonusNeutRange3 +# +# Used by: +# Ship: Pilgrim +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Recon Ships").level + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Destabilizer", + "energyDestabilizationRange", ship.getModifiedItemAttr("eliteBonusReconShip3") * level) diff --git a/eos/effects/elitereconbonusvamprange3.py b/eos/effects/elitereconbonusvamprange3.py new file mode 100644 index 000000000..dfafdd3a3 --- /dev/null +++ b/eos/effects/elitereconbonusvamprange3.py @@ -0,0 +1,9 @@ +# eliteReconBonusVampRange3 +# +# Used by: +# Ship: Pilgrim +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Recon Ships").level + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Vampire", + "powerTransferRange", ship.getModifiedItemAttr("eliteBonusReconShip3") * level) diff --git a/eos/effects/evasivemaneuveringagilitybonuspostpercentagilityship.py b/eos/effects/evasivemaneuveringagilitybonuspostpercentagilityship.py index febd7cf0d..ebb16ac65 100644 --- a/eos/effects/evasivemaneuveringagilitybonuspostpercentagilityship.py +++ b/eos/effects/evasivemaneuveringagilitybonuspostpercentagilityship.py @@ -1,9 +1,9 @@ # evasiveManeuveringAgilityBonusPostPercentAgilityShip # # Used by: +# Modules from group: Rig Anchor (4 of 4) # Implants named like: Eifyr and Co. 'Rogue' Evasive Maneuvering EM (6 of 6) # Implants named like: grade Nomad (10 of 12) -# Modules named like: Higgs Anchor I (4 of 4) # Modules named like: Low Friction Nozzle Joints (8 of 8) # Implant: Genolution Core Augmentation CA-4 # Skill: Evasive Maneuvering diff --git a/eos/effects/freighteragilitybonus2o2.py b/eos/effects/freighteragilitybonus2o2.py index 7364d2015..2472ae51f 100644 --- a/eos/effects/freighteragilitybonus2o2.py +++ b/eos/effects/freighteragilitybonus2o2.py @@ -1,3 +1,7 @@ +# freighterAgilityBonus2O2 +# +# Used by: +# Ship: Bowhead type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("ORE Freighter").level diff --git a/eos/effects/freightersmacapacitybonuso1.py b/eos/effects/freightersmacapacitybonuso1.py index 4b1e591e8..ff5861137 100644 --- a/eos/effects/freightersmacapacitybonuso1.py +++ b/eos/effects/freightersmacapacitybonuso1.py @@ -1,3 +1,7 @@ +# freighterSMACapacityBonusO1 +# +# Used by: +# Ship: Bowhead type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("ORE Freighter").level diff --git a/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py b/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py index 501ab9bd4..b495ccaaf 100644 --- a/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py +++ b/eos/effects/iceharvestcycletimemodulesrequiringiceharvestingonline.py @@ -1,7 +1,7 @@ # iceHarvestCycleTimeModulesRequiringIceHarvestingOnline # # Used by: -# Variations of module: Ice Harvester Upgrade I (6 of 6) +# Variations of module: Ice Harvester Upgrade I (5 of 5) type = "passive" def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting"), diff --git a/eos/effects/iceminercpuusagepercent.py b/eos/effects/iceminercpuusagepercent.py index d14d89589..9a3f04694 100644 --- a/eos/effects/iceminercpuusagepercent.py +++ b/eos/effects/iceminercpuusagepercent.py @@ -1,7 +1,7 @@ # iceMinerCpuUsagePercent # # Used by: -# Variations of module: Ice Harvester Upgrade I (6 of 6) +# Variations of module: Ice Harvester Upgrade I (5 of 5) type = "passive" def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting"), diff --git a/eos/effects/increasesignatureradiusonline.py b/eos/effects/increasesignatureradiusonline.py index a4bd284b4..a67dc074f 100644 --- a/eos/effects/increasesignatureradiusonline.py +++ b/eos/effects/increasesignatureradiusonline.py @@ -1,7 +1,7 @@ # increaseSignatureRadiusOnline # # Used by: -# Modules from group: Inertia Stabilizer (12 of 12) +# Modules from group: Inertial Stabilizer (7 of 7) type = "passive" def handler(fit, module, context): fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus")) \ No newline at end of file diff --git a/eos/effects/massreductionbonuspassive.py b/eos/effects/massreductionbonuspassive.py index f3d5c8f6f..13e72f726 100644 --- a/eos/effects/massreductionbonuspassive.py +++ b/eos/effects/massreductionbonuspassive.py @@ -1,7 +1,7 @@ # massReductionBonusPassive # # Used by: -# Modules named like: Higgs Anchor I (4 of 4) +# Modules from group: Rig Anchor (4 of 4) type = "passive" def handler(fit, module, context): fit.ship.boostItemAttr("mass", module.getModifiedItemAttr("massBonusPercentage"), stackingPenalties=True) diff --git a/eos/effects/minercpuusagemultiplypercent2.py b/eos/effects/minercpuusagemultiplypercent2.py index 6f1b9aa33..f76b5323b 100644 --- a/eos/effects/minercpuusagemultiplypercent2.py +++ b/eos/effects/minercpuusagemultiplypercent2.py @@ -1,7 +1,7 @@ # minerCpuUsageMultiplyPercent2 # # Used by: -# Variations of module: Mining Laser Upgrade I (6 of 6) +# Variations of module: Mining Laser Upgrade I (5 of 5) type = "passive" def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"), diff --git a/eos/effects/mininglaser.py b/eos/effects/mininglaser.py index a691775fa..a4095f4ed 100644 --- a/eos/effects/mininglaser.py +++ b/eos/effects/mininglaser.py @@ -2,7 +2,7 @@ # # Used by: # Modules from group: Frequency Mining Laser (3 of 3) -# Modules from group: Mining Laser (17 of 17) +# Modules from group: Mining Laser (12 of 12) # Modules from group: Strip Miner (5 of 5) type = 'active' def handler(fit, module, context): diff --git a/eos/effects/miningyieldmultiplypercent.py b/eos/effects/miningyieldmultiplypercent.py index 0ca493d0b..d00198ab4 100644 --- a/eos/effects/miningyieldmultiplypercent.py +++ b/eos/effects/miningyieldmultiplypercent.py @@ -1,7 +1,7 @@ # miningYieldMultiplyPercent # # Used by: -# Variations of module: Mining Laser Upgrade I (6 of 6) +# Variations of module: Mining Laser Upgrade I (5 of 5) type = "passive" def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"), diff --git a/eos/effects/modeagilitypostdiv.py b/eos/effects/modeagilitypostdiv.py index 8251e2852..022951098 100644 --- a/eos/effects/modeagilitypostdiv.py +++ b/eos/effects/modeagilitypostdiv.py @@ -1,3 +1,7 @@ +# modeAgilityPostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Propulsion Mode type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("agility", 1/module.getModifiedItemAttr("modeAgilityPostDiv"), diff --git a/eos/effects/modearmorresonancepostdiv.py b/eos/effects/modearmorresonancepostdiv.py index 56e4bef1e..af767b63b 100644 --- a/eos/effects/modearmorresonancepostdiv.py +++ b/eos/effects/modearmorresonancepostdiv.py @@ -1,3 +1,7 @@ +# modeArmorResonancePostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Defense Mode type = "passive" def handler(fit, module, context): for resType in ("Em", "Explosive", "Kinetic"): diff --git a/eos/effects/modesigradiuspostdiv.py b/eos/effects/modesigradiuspostdiv.py index 20e530328..3cec5bb76 100644 --- a/eos/effects/modesigradiuspostdiv.py +++ b/eos/effects/modesigradiuspostdiv.py @@ -1,5 +1,8 @@ +# modeSigRadiusPostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Defense Mode type = "passive" def handler(fit, module, context): - level = fit.character.getSkill("Minmatar Destroyer").level fit.ship.multiplyItemAttr("signatureRadius", 1/module.getModifiedItemAttr("modeSignatureRadiusPostDiv"), stackingPenalties = True, penaltyGroup="postDiv") diff --git a/eos/effects/modevelocitypostdiv.py b/eos/effects/modevelocitypostdiv.py index 91dc341cd..a38c9ddb8 100644 --- a/eos/effects/modevelocitypostdiv.py +++ b/eos/effects/modevelocitypostdiv.py @@ -1,3 +1,7 @@ +# modeVelocityPostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Propulsion Mode type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("maxVelocity", 1/module.getModifiedItemAttr("modeVelocityPostDiv"), diff --git a/eos/effects/modifymaxvelocityofshippassive.py b/eos/effects/modifymaxvelocityofshippassive.py index 3c134f937..62670476a 100644 --- a/eos/effects/modifymaxvelocityofshippassive.py +++ b/eos/effects/modifymaxvelocityofshippassive.py @@ -1,7 +1,7 @@ # modifyMaxVelocityOfShipPassive # # Used by: -# Modules from group: Expanded Cargohold (13 of 13) +# Modules from group: Expanded Cargohold (7 of 7) type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("maxVelocity", module.getModifiedItemAttr("maxVelocityBonus"), diff --git a/eos/effects/modifypowerrechargerate.py b/eos/effects/modifypowerrechargerate.py index 9dcea33b5..4f1e35a4b 100644 --- a/eos/effects/modifypowerrechargerate.py +++ b/eos/effects/modifypowerrechargerate.py @@ -2,9 +2,9 @@ # # Used by: # Modules from group: Capacitor Flux Coil (6 of 6) -# Modules from group: Capacitor Power Relay (26 of 26) -# Modules from group: Capacitor Recharger (25 of 25) -# Modules from group: Power Diagnostic System (31 of 31) +# Modules from group: Capacitor Power Relay (20 of 20) +# Modules from group: Capacitor Recharger (18 of 18) +# Modules from group: Power Diagnostic System (23 of 23) # Modules from group: Reactor Control Unit (22 of 22) # Modules from group: Shield Flux Coil (11 of 11) # Modules from group: Shield Power Relay (11 of 11) diff --git a/eos/effects/modifyshieldrechargerate.py b/eos/effects/modifyshieldrechargerate.py index 1e1dbc192..1986cbb09 100644 --- a/eos/effects/modifyshieldrechargerate.py +++ b/eos/effects/modifyshieldrechargerate.py @@ -2,8 +2,8 @@ # # Used by: # Modules from group: Capacitor Flux Coil (6 of 6) -# Modules from group: Capacitor Power Relay (26 of 26) -# Modules from group: Power Diagnostic System (31 of 31) +# Modules from group: Capacitor Power Relay (20 of 20) +# Modules from group: Power Diagnostic System (23 of 23) # Modules from group: Reactor Control Unit (22 of 22) # Modules from group: Shield Flux Coil (11 of 11) # Modules from group: Shield Recharger (6 of 6) diff --git a/eos/effects/mwdsignatureradiusrolebonus.py b/eos/effects/mwdsignatureradiusrolebonus.py index b160c5b28..92f757399 100644 --- a/eos/effects/mwdsignatureradiusrolebonus.py +++ b/eos/effects/mwdsignatureradiusrolebonus.py @@ -2,7 +2,7 @@ # # Used by: # Ships from group: Assault Frigate (8 of 12) -# Ships from group: Heavy Assault Cruiser (8 of 11) +# Ships from group: Heavy Assault Cruiser (10 of 13) type = "passive" def handler(fit, ship, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("High Speed Maneuvering"), diff --git a/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py b/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py index 188a237fd..19cb4e928 100644 --- a/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py +++ b/eos/effects/navigationvelocitybonuspostpercentmaxvelocityship.py @@ -1,9 +1,9 @@ # navigationVelocityBonusPostPercentMaxVelocityShip # # Used by: +# Modules from group: Rig Anchor (4 of 4) # Implants named like: grade Snake (16 of 18) # Modules named like: Auxiliary Thrusters (8 of 8) -# Modules named like: Higgs Anchor I (4 of 4) # Implant: Quafe Zero # Skill: Navigation type = "passive" diff --git a/eos/effects/poweroutputmultiply.py b/eos/effects/poweroutputmultiply.py index e4f988ff8..e0ac4fe57 100644 --- a/eos/effects/poweroutputmultiply.py +++ b/eos/effects/poweroutputmultiply.py @@ -2,8 +2,8 @@ # # Used by: # Modules from group: Capacitor Flux Coil (6 of 6) -# Modules from group: Capacitor Power Relay (26 of 26) -# Modules from group: Power Diagnostic System (31 of 31) +# Modules from group: Capacitor Power Relay (20 of 20) +# Modules from group: Power Diagnostic System (23 of 23) # Modules from group: Reactor Control Unit (22 of 22) # Modules from group: Shield Flux Coil (11 of 11) # Modules from group: Shield Power Relay (11 of 11) diff --git a/eos/effects/probelaunchercpupercentbonustacticaldestroyer.py b/eos/effects/probelaunchercpupercentbonustacticaldestroyer.py index 026fb0818..1efb4913c 100644 --- a/eos/effects/probelaunchercpupercentbonustacticaldestroyer.py +++ b/eos/effects/probelaunchercpupercentbonustacticaldestroyer.py @@ -1,3 +1,7 @@ +# probeLauncherCPUPercentBonusTacticalDestroyer +# +# Used by: +# Ship: Confessor type = "passive" def handler(fit, ship, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Astrometrics"), diff --git a/eos/effects/rolebonusbulkheadcpu.py b/eos/effects/rolebonusbulkheadcpu.py index 31cae7b4e..c6cbac5c5 100644 --- a/eos/effects/rolebonusbulkheadcpu.py +++ b/eos/effects/rolebonusbulkheadcpu.py @@ -1,7 +1,7 @@ # roleBonusBulkheadCPU # # Used by: -# Ships from group: Freighter (4 of 4) +# Ships from group: Freighter (4 of 5) # Ships from group: Jump Freighter (4 of 4) type = "passive" def handler(fit, ship, context): diff --git a/eos/effects/shieldboostamplifier.py b/eos/effects/shieldboostamplifier.py index 164c39319..137df0af1 100644 --- a/eos/effects/shieldboostamplifier.py +++ b/eos/effects/shieldboostamplifier.py @@ -1,7 +1,7 @@ # shieldBoostAmplifier # # Used by: -# Modules from group: Capacitor Power Relay (26 of 26) +# Modules from group: Capacitor Power Relay (20 of 20) # Modules from group: Shield Boost Amplifier (25 of 25) type = "passive" def handler(fit, module, context): diff --git a/eos/effects/shieldcapacitymultiply.py b/eos/effects/shieldcapacitymultiply.py index 4f536da11..9cf482cc7 100644 --- a/eos/effects/shieldcapacitymultiply.py +++ b/eos/effects/shieldcapacitymultiply.py @@ -2,8 +2,8 @@ # # Used by: # Modules from group: Capacitor Flux Coil (6 of 6) -# Modules from group: Capacitor Power Relay (26 of 26) -# Modules from group: Power Diagnostic System (31 of 31) +# Modules from group: Capacitor Power Relay (20 of 20) +# Modules from group: Power Diagnostic System (23 of 23) # Modules from group: Reactor Control Unit (22 of 22) # Modules from group: Shield Flux Coil (11 of 11) # Modules from group: Shield Power Relay (11 of 11) diff --git a/eos/effects/shipadvancedspaceshipcommandagilitybonus.py b/eos/effects/shipadvancedspaceshipcommandagilitybonus.py index 7e2acb881..bbc33a174 100644 --- a/eos/effects/shipadvancedspaceshipcommandagilitybonus.py +++ b/eos/effects/shipadvancedspaceshipcommandagilitybonus.py @@ -1,7 +1,7 @@ # shipAdvancedSpaceshipCommandAgilityBonus # # Used by: -# Items from market group: Ships > Capital Ships (27 of 29) +# Items from market group: Ships > Capital Ships (32 of 34) type = "passive" def handler(fit, ship, context): skill = fit.character.getSkill("Advanced Spaceship Command") diff --git a/eos/effects/shipbonusdreadcitadelcruiserofc1.py b/eos/effects/shipbonusdreadcitadelcruiserofc1.py index d97580dbc..92427cdc7 100644 --- a/eos/effects/shipbonusdreadcitadelcruiserofc1.py +++ b/eos/effects/shipbonusdreadcitadelcruiserofc1.py @@ -1,7 +1,7 @@ # shipBonusDreadCitadelCruiseRofC1 # # Used by: -# Ship: Phoenix +# Ships named like: Phoenix (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Caldari Dreadnought").level diff --git a/eos/effects/shipbonusdreadcitadeltorprofc1.py b/eos/effects/shipbonusdreadcitadeltorprofc1.py index ae624dbd5..1371a87e5 100644 --- a/eos/effects/shipbonusdreadcitadeltorprofc1.py +++ b/eos/effects/shipbonusdreadcitadeltorprofc1.py @@ -1,7 +1,7 @@ # shipBonusDreadCitadelTorpRofC1 # # Used by: -# Ship: Phoenix +# Ships named like: Phoenix (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Caldari Dreadnought").level diff --git a/eos/effects/shipbonusdronedamagemultipliergb2.py b/eos/effects/shipbonusdronedamagemultipliergb2.py index c65feca5a..80fe20844 100644 --- a/eos/effects/shipbonusdronedamagemultipliergb2.py +++ b/eos/effects/shipbonusdronedamagemultipliergb2.py @@ -2,6 +2,7 @@ # # Used by: # Variations of ship: Dominix (3 of 3) +# Ship: Dominix Quafe Edition # Ship: Nestor type = "passive" def handler(fit, ship, context): diff --git a/eos/effects/shipbonusdronedamagemultipliergc2.py b/eos/effects/shipbonusdronedamagemultipliergc2.py index 8c3aa4af0..c251fdda9 100644 --- a/eos/effects/shipbonusdronedamagemultipliergc2.py +++ b/eos/effects/shipbonusdronedamagemultipliergc2.py @@ -3,6 +3,7 @@ # Used by: # Ships named like: Stratios (2 of 2) # Variations of ship: Vexor (3 of 4) +# Ship: Vexor Quafe Edition type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Cruiser").level diff --git a/eos/effects/shipbonusdronehitpointsgb2.py b/eos/effects/shipbonusdronehitpointsgb2.py index 0a526d7e1..aa6c3958a 100644 --- a/eos/effects/shipbonusdronehitpointsgb2.py +++ b/eos/effects/shipbonusdronehitpointsgb2.py @@ -2,6 +2,7 @@ # # Used by: # Variations of ship: Dominix (3 of 3) +# Ship: Dominix Quafe Edition # Ship: Nestor type = "passive" def handler(fit, ship, context): diff --git a/eos/effects/shipbonusdronehitpointsgc2.py b/eos/effects/shipbonusdronehitpointsgc2.py index 5e36004ae..fc008fa87 100644 --- a/eos/effects/shipbonusdronehitpointsgc2.py +++ b/eos/effects/shipbonusdronehitpointsgc2.py @@ -3,6 +3,7 @@ # Used by: # Ships named like: Stratios (2 of 2) # Variations of ship: Vexor (3 of 4) +# Ship: Vexor Quafe Edition type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Cruiser").level diff --git a/eos/effects/shipbonusdronehitpointsgf.py b/eos/effects/shipbonusdronehitpointsgf.py index d1892d7c8..b5d838c4b 100644 --- a/eos/effects/shipbonusdronehitpointsgf.py +++ b/eos/effects/shipbonusdronehitpointsgf.py @@ -1,8 +1,8 @@ # shipBonusDroneHitpointsGF # # Used by: +# Ships named like: Tristan (2 of 2) # Ship: Astero -# Ship: Tristan type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Frigate").level diff --git a/eos/effects/shipbonusdroneminingamountgc2.py b/eos/effects/shipbonusdroneminingamountgc2.py index 60a66e282..643e7f522 100644 --- a/eos/effects/shipbonusdroneminingamountgc2.py +++ b/eos/effects/shipbonusdroneminingamountgc2.py @@ -1,8 +1,7 @@ # shipBonusDroneMiningAmountGC2 # # Used by: -# Ship: Vexor -# Ship: Vexor Navy Issue +# Ships named like: Vexor (3 of 4) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Cruiser").level diff --git a/eos/effects/shipbonusdroneoptimalrangegb.py b/eos/effects/shipbonusdroneoptimalrangegb.py index 746b74c2a..c8e8f2346 100644 --- a/eos/effects/shipbonusdroneoptimalrangegb.py +++ b/eos/effects/shipbonusdroneoptimalrangegb.py @@ -2,6 +2,7 @@ # # Used by: # Ship: Dominix +# Ship: Dominix Quafe Edition type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Battleship").level diff --git a/eos/effects/shipbonusdronetrackinggb.py b/eos/effects/shipbonusdronetrackinggb.py index 192608c97..553479ae0 100644 --- a/eos/effects/shipbonusdronetrackinggb.py +++ b/eos/effects/shipbonusdronetrackinggb.py @@ -2,6 +2,7 @@ # # Used by: # Ship: Dominix +# Ship: Dominix Quafe Edition type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Battleship").level diff --git a/eos/effects/shipbonusdronetrackinggf.py b/eos/effects/shipbonusdronetrackinggf.py index 9f666dfe4..8e172b68b 100644 --- a/eos/effects/shipbonusdronetrackinggf.py +++ b/eos/effects/shipbonusdronetrackinggf.py @@ -1,7 +1,7 @@ # shipBonusDroneTrackingGF # # Used by: -# Ship: Tristan +# Ships named like: Tristan (2 of 2) type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Frigate").level diff --git a/eos/effects/shipbonusheavyassaultlauncherrateoffirecc2.py b/eos/effects/shipbonusheavyassaultlauncherrateoffirecc2.py deleted file mode 100644 index 7edf68e60..000000000 --- a/eos/effects/shipbonusheavyassaultlauncherrateoffirecc2.py +++ /dev/null @@ -1,9 +0,0 @@ -# shipBonusHeavyAssaultLauncherRateOfFireCC2 -# -# Used by: -# Ship: Rook -type = "passive" -def handler(fit, ship, context): - level = fit.character.getSkill("Caldari Cruiser").level - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy Assault", - "speed", ship.getModifiedItemAttr("shipBonusCC2") * level) diff --git a/eos/effects/shipbonusheavylauncherrateoffirecc2.py b/eos/effects/shipbonusheavylauncherrateoffirecc2.py deleted file mode 100644 index 5ca8c9393..000000000 --- a/eos/effects/shipbonusheavylauncherrateoffirecc2.py +++ /dev/null @@ -1,9 +0,0 @@ -# shipBonusHeavyLauncherRateOfFireCC2 -# -# Used by: -# Ship: Rook -type = "passive" -def handler(fit, ship, context): - level = fit.character.getSkill("Caldari Cruiser").level - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Missile Launcher Heavy", - "speed", ship.getModifiedItemAttr("shipBonusCC2") * level) diff --git a/eos/effects/shipbonushybridtrackinggf2.py b/eos/effects/shipbonushybridtrackinggf2.py index e0aabdeba..d50ab59b4 100644 --- a/eos/effects/shipbonushybridtrackinggf2.py +++ b/eos/effects/shipbonushybridtrackinggf2.py @@ -1,10 +1,10 @@ # shipBonusHybridTrackingGF2 # # Used by: +# Ships named like: Tristan (2 of 2) # Ship: Ares # Ship: Federation Navy Comet # Ship: Police Pursuit Comet -# Ship: Tristan type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Frigate").level diff --git a/eos/effects/shipbonusthermalmissiledamagegc2.py b/eos/effects/shipbonusthermalmissiledamagegc2.py index 05e602e20..19fefc9c7 100644 --- a/eos/effects/shipbonusthermalmissiledamagegc2.py +++ b/eos/effects/shipbonusthermalmissiledamagegc2.py @@ -3,6 +3,7 @@ # Used by: # Ship: Chameleon # Ship: Gila + type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Cruiser").level diff --git a/eos/effects/shipheatdamageamarrtacticaldestroyer3.py b/eos/effects/shipheatdamageamarrtacticaldestroyer3.py index 9585d71c7..b8fb82149 100644 --- a/eos/effects/shipheatdamageamarrtacticaldestroyer3.py +++ b/eos/effects/shipheatdamageamarrtacticaldestroyer3.py @@ -1,3 +1,7 @@ +# shipHeatDamageAmarrTacticalDestroyer3 +# +# Used by: +# Ship: Confessor type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Amarr Tactical Destroyer").level diff --git a/eos/effects/shiphtdmgbonusfixedgc.py b/eos/effects/shiphtdmgbonusfixedgc.py index c0fac0f09..33ad961e3 100644 --- a/eos/effects/shiphtdmgbonusfixedgc.py +++ b/eos/effects/shiphtdmgbonusfixedgc.py @@ -2,13 +2,11 @@ # # Used by: # Ships named like: Thorax (3 of 3) +# Ships named like: Vexor (3 of 4) # Ship: Adrestia # Ship: Arazu # Ship: Deimos # Ship: Exequror Navy Issue -# Ship: Guardian-Vexor -# Ship: Lachesis -# Ship: Vexor type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Cruiser").level diff --git a/eos/effects/shiphybridtrackinggc.py b/eos/effects/shiphybridtrackinggc.py index f99cea0b0..02215246a 100644 --- a/eos/effects/shiphybridtrackinggc.py +++ b/eos/effects/shiphybridtrackinggc.py @@ -1,6 +1,7 @@ # shipHybridTrackingGC # # Used by: +# Ship: Lachesis # Ship: Phobos type = "passive" def handler(fit, ship, context): diff --git a/eos/effects/shipmissilebonusemdmgmc.py b/eos/effects/shipmissilebonusemdmgmc.py new file mode 100644 index 000000000..1230c3f94 --- /dev/null +++ b/eos/effects/shipmissilebonusemdmgmc.py @@ -0,0 +1,9 @@ +# shipMissileBonusEMdmgMC +# +# Used by: +# Ship: Rapier +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Minmatar Cruiser").level + fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), + "emDamage", ship.getModifiedItemAttr("shipBonusMC") * level) diff --git a/eos/effects/shipmissilebonusexpdmgmc.py b/eos/effects/shipmissilebonusexpdmgmc.py new file mode 100644 index 000000000..cdaa2637d --- /dev/null +++ b/eos/effects/shipmissilebonusexpdmgmc.py @@ -0,0 +1,9 @@ +# shipMissileBonusExpdmgMC +# +# Used by: +# Ship: Rapier +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Minmatar Cruiser").level + fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), + "explosiveDamage", ship.getModifiedItemAttr("shipBonusMC") * level) diff --git a/eos/effects/shipmissilebonuskindmgmc.py b/eos/effects/shipmissilebonuskindmgmc.py new file mode 100644 index 000000000..c4173ca36 --- /dev/null +++ b/eos/effects/shipmissilebonuskindmgmc.py @@ -0,0 +1,9 @@ +# shipMissileBonusKindmgMC +# +# Used by: +# Ship: Rapier +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Minmatar Cruiser").level + fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), + "kineticDamage", ship.getModifiedItemAttr("shipBonusMC") * level) diff --git a/eos/effects/shipmissilebonusthermdmgmc.py b/eos/effects/shipmissilebonusthermdmgmc.py new file mode 100644 index 000000000..90a8c3daa --- /dev/null +++ b/eos/effects/shipmissilebonusthermdmgmc.py @@ -0,0 +1,9 @@ +# shipMissileBonusThermdmgMC +# +# Used by: +# Ship: Rapier +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Minmatar Cruiser").level + fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), + "thermalDamage", ship.getModifiedItemAttr("shipBonusMC") * level) diff --git a/eos/effects/shipmissilekindamagecc2.py b/eos/effects/shipmissilekindamagecc2.py index eee63477c..97ff60cc1 100644 --- a/eos/effects/shipmissilekindamagecc2.py +++ b/eos/effects/shipmissilekindamagecc2.py @@ -2,6 +2,7 @@ # # Used by: # Ship: Osprey Navy Issue +# Ship: Rook type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Caldari Cruiser").level diff --git a/eos/effects/shipmissilelauncherrofad1fixed.py b/eos/effects/shipmissilelauncherrofad1fixed.py new file mode 100644 index 000000000..fa26f38eb --- /dev/null +++ b/eos/effects/shipmissilelauncherrofad1fixed.py @@ -0,0 +1,9 @@ +# shipMissileLauncherRoFAD1Fixed +# +# Used by: +# Ship: Heretic +type = "passive" +def handler(fit, ship, context): + level = fit.character.getSkill("Amarr Destroyer").level + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Missile Launcher Operation"), + "speed", ship.getModifiedItemAttr("shipBonusAD1") * level) \ No newline at end of file diff --git a/eos/effects/shipmodemaxtargetrangepostdiv.py b/eos/effects/shipmodemaxtargetrangepostdiv.py index 0a7d0304a..d9cb50254 100644 --- a/eos/effects/shipmodemaxtargetrangepostdiv.py +++ b/eos/effects/shipmodemaxtargetrangepostdiv.py @@ -1,3 +1,7 @@ +# shipModeMaxTargetRangePostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Sharpshooter Mode type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("maxTargetRange", 1/module.getModifiedItemAttr("modeMaxTargetRangePostDiv"), diff --git a/eos/effects/shipmodescanrespostdiv.py b/eos/effects/shipmodescanrespostdiv.py index 2df22198c..07db84ad5 100644 --- a/eos/effects/shipmodescanrespostdiv.py +++ b/eos/effects/shipmodescanrespostdiv.py @@ -1,3 +1,7 @@ +# shipModeScanResPostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Sharpshooter Mode type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("scanResolution", 1/module.getModifiedItemAttr("modeScanResPostDiv"), diff --git a/eos/effects/shipmodescanstrengthpostdiv.py b/eos/effects/shipmodescanstrengthpostdiv.py index 3dbc07954..cdf2b20f3 100644 --- a/eos/effects/shipmodescanstrengthpostdiv.py +++ b/eos/effects/shipmodescanstrengthpostdiv.py @@ -1,3 +1,7 @@ +# shipModeScanStrengthPostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Sharpshooter Mode type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("scanRadarStrength", 1/module.getModifiedItemAttr("modeRadarStrengthPostDiv"), diff --git a/eos/effects/shipmodesetoptimalrangepostdiv.py b/eos/effects/shipmodesetoptimalrangepostdiv.py index baf3f6864..bafceea86 100644 --- a/eos/effects/shipmodesetoptimalrangepostdiv.py +++ b/eos/effects/shipmodesetoptimalrangepostdiv.py @@ -1,3 +1,7 @@ +# shipModeSETOptimalRangePostDiv +# +# Used by: +# Module: Amarr Tactical Destroyer Sharpshooter Mode type = "passive" def handler(fit, module, context): fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Small Energy Turret"), diff --git a/eos/effects/shippturretfalloffbonusgb.py b/eos/effects/shippturretfalloffbonusgb.py index 7088999a4..4286e1bd6 100644 --- a/eos/effects/shippturretfalloffbonusgb.py +++ b/eos/effects/shippturretfalloffbonusgb.py @@ -6,4 +6,4 @@ type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Gallente Battleship").level fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Projectile Turret"), - "falloff", ship.getModifiedItemAttr("shipBonusGB") * level) \ No newline at end of file + "falloff", ship.getModifiedItemAttr("shipBonusGB") * level) diff --git a/eos/effects/shippturretspeedbonusmc.py b/eos/effects/shippturretspeedbonusmc.py index 6a5ad4d5b..c561afc47 100644 --- a/eos/effects/shippturretspeedbonusmc.py +++ b/eos/effects/shippturretspeedbonusmc.py @@ -5,7 +5,6 @@ # Variations of ship: Rupture (3 of 3) # Variations of ship: Stabber (3 of 3) # Ship: Huginn -# Ship: Rapier # Ship: Scythe Fleet Issue type = "passive" def handler(fit, ship, context): diff --git a/eos/effects/shipsetcapneedamarrtacticaldestroyer2.py b/eos/effects/shipsetcapneedamarrtacticaldestroyer2.py index 048c17a26..4f015f818 100644 --- a/eos/effects/shipsetcapneedamarrtacticaldestroyer2.py +++ b/eos/effects/shipsetcapneedamarrtacticaldestroyer2.py @@ -1,3 +1,7 @@ +# shipSETCapNeedAmarrTacticalDestroyer2 +# +# Used by: +# Ship: Confessor type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Amarr Tactical Destroyer").level diff --git a/eos/effects/shipsetdamageamarrtacticaldestroyer1.py b/eos/effects/shipsetdamageamarrtacticaldestroyer1.py index 3bca59b8e..a959549d3 100644 --- a/eos/effects/shipsetdamageamarrtacticaldestroyer1.py +++ b/eos/effects/shipsetdamageamarrtacticaldestroyer1.py @@ -1,3 +1,7 @@ +# shipSETDamageAmarrTacticalDestroyer1 +# +# Used by: +# Ship: Confessor type = "passive" def handler(fit, ship, context): level = fit.character.getSkill("Amarr Tactical Destroyer").level diff --git a/eos/effects/shipxlprojectiledamagerole.py b/eos/effects/shipxlprojectiledamagerole.py index fbbb39202..00d66ce05 100644 --- a/eos/effects/shipxlprojectiledamagerole.py +++ b/eos/effects/shipxlprojectiledamagerole.py @@ -1,7 +1,7 @@ # shipXLProjectileDamageRole # # Used by: -# Ship: Naglfar +# Ships named like: Naglfar (2 of 2) type = "passive" def handler(fit, ship, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Projectile Turret"), diff --git a/eos/effects/structurehpmultiply.py b/eos/effects/structurehpmultiply.py index 502d67636..aa7d43c7c 100644 --- a/eos/effects/structurehpmultiply.py +++ b/eos/effects/structurehpmultiply.py @@ -1,8 +1,8 @@ # structureHPMultiply # # Used by: -# Modules from group: Nanofiber Internal Structure (14 of 14) -# Modules from group: Reinforced Bulkhead (12 of 12) +# Modules from group: Nanofiber Internal Structure (7 of 7) +# Modules from group: Reinforced Bulkhead (8 of 8) # Modules named like: QA Multiship Module Players (4 of 4) type = "passive" def handler(fit, module, context): diff --git a/eos/effects/structurehpmultiplypassive.py b/eos/effects/structurehpmultiplypassive.py index 60159c104..89ab600df 100644 --- a/eos/effects/structurehpmultiplypassive.py +++ b/eos/effects/structurehpmultiplypassive.py @@ -1,7 +1,7 @@ # structureHPMultiplyPassive # # Used by: -# Modules from group: Expanded Cargohold (13 of 13) +# Modules from group: Expanded Cargohold (7 of 7) type = "passive" def handler(fit, module, context): fit.ship.multiplyItemAttr("hp", module.getModifiedItemAttr("structureHPMultiplier")) \ No newline at end of file diff --git a/eos/effects/velocitybonusonline.py b/eos/effects/velocitybonusonline.py index ef6a5e458..1af6d4411 100644 --- a/eos/effects/velocitybonusonline.py +++ b/eos/effects/velocitybonusonline.py @@ -1,8 +1,8 @@ # velocityBonusOnline # # Used by: -# Modules from group: Nanofiber Internal Structure (14 of 14) -# Modules from group: Overdrive Injector System (14 of 14) +# Modules from group: Nanofiber Internal Structure (7 of 7) +# Modules from group: Overdrive Injector System (7 of 7) type = "passive" def handler(fit, module, context): fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("implantBonusVelocity"), diff --git a/eos/gamedata.py b/eos/gamedata.py index f1a970bdd..d5691da46 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -120,7 +120,12 @@ class Effect(EqBase): ''' try: self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True) - self.__handler = getattr(effectModule, "handler") + try: + self.__handler = getattr(effectModule, "handler") + except AttributeError: + print "effect {} exists, but no handler".format(self.handlerName) + raise + try: self.__runTime = getattr(effectModule, "runTime") or "normal" except AttributeError: @@ -133,7 +138,7 @@ class Effect(EqBase): t = t if isinstance(t, tuple) or t is None else (t,) self.__type = t - except ImportError as e: + except (ImportError, AttributeError) as e: self.__handler = effectDummy self.__runTime = "normal" self.__type = None @@ -235,47 +240,63 @@ class Item(EqBase): requiredSkills[item] = skillLvl return self.__requiredSkills + factionMap = { + 500001: "caldari", + 500002: "minmatar", + 500003: "amarr", + 500004: "gallente", + 500005: "jove", + 500010: "guristas", + 500011: "angel", + 500012: "blood", + 500014: "ore", + 500016: "sisters", + 500018: "mordu", + 500019: "sansha", + 500020: "serpentis" + } + @property def race(self): if self.__race is None: - # Define race map - map = {1: "caldari", - 2: "minmatar", - 4: "amarr", - 5: "sansha", # Caldari + Amarr - 6: "blood", # Minmatar + Amarr - 8: "gallente", - 9: "guristas", # Caldari + Gallente - 10: "angelserp", # Minmatar + Gallente, final race depends on the order of skills - 12: "sisters", # Amarr + Gallente - 16: "jove", - 32: "sansha", # Incrusion Sansha - 128: "ore"} - # Race is None by default - race = None - # Check primary and secondary required skills' races - if race is None: - # Currently Assault Frigates skill has raceID set, which is actually - # EVE's bug - ignoredSkills = ('Assault Frigates',) - skills = tuple(filter(lambda i: i.name not in ignoredSkills, self.requiredSkills.keys())) - skillRaces = tuple(filter(lambda rid: rid, (s.raceID for s in skills))) - if sum(skillRaces) in map: - race = map[sum(skillRaces)] - if race == "angelserp": - if skillRaces == (2, 8): - race = "angel" - else: - race = "serpentis" - - # Rely on item's own raceID as last resort - if race is None: - race = map.get(self.raceID, None) - - # Store our final value - self.__race = race + try: + self.__race = self.factionMap[self.factionID] + # Some ships (like few limited issue ships) do not have factionID set, + # thus keep old mechanism for now + except KeyError: + # Define race map + map = {1: "caldari", + 2: "minmatar", + 4: "amarr", + 5: "sansha", # Caldari + Amarr + 6: "blood", # Minmatar + Amarr + 8: "gallente", + 9: "guristas", # Caldari + Gallente + 10: "angelserp", # Minmatar + Gallente, final race depends on the order of skills + 12: "sisters", # Amarr + Gallente + 16: "jove", + 32: "sansha", # Incrusion Sansha + 128: "ore"} + # Race is None by default + race = None + # Check primary and secondary required skills' races + if race is None: + skillRaces = tuple(filter(lambda rid: rid, (s.raceID for s in tuple(self.requiredSkills.keys())))) + if sum(skillRaces) in map: + race = map[sum(skillRaces)] + if race == "angelserp": + if skillRaces == (2, 8): + race = "angel" + else: + race = "serpentis" + # Rely on item's own raceID as last resort + if race is None: + race = map.get(self.raceID, None) + # Store our final value + self.__race = race return self.__race + @property def assistive(self): """Detects if item can be used as assistance""" diff --git a/eos/graph/fitDps.py b/eos/graph/fitDps.py index d10d9f2b3..cbcb07996 100644 --- a/eos/graph/fitDps.py +++ b/eos/graph/fitDps.py @@ -71,7 +71,7 @@ class FitDpsGraph(Graph): if distance <= fit.extraAttributes["droneControlRange"]: for drone in fit.drones: multiplier = 1 if drone.getModifiedItemAttr("maxVelocity") > 0 else self.calculateTurretMultiplier(drone, data) - dps = drone.damageStats(fit.targetResists) + dps, _ = drone.damageStats(fit.targetResists) total += dps * multiplier return total diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index 3e959bec3..289674f1e 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -35,6 +35,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.amount = 0 self.amountActive = 0 self.__dps = None + self.__volley = None self.__miningyield = None self.projected = False self.__itemModifiedAttributes = ModifiedAttributeDict() @@ -43,6 +44,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @reconstructor def init(self): self.__dps = None + self.__volley = None self.__miningyield = None self.__item = None self.__charge = None @@ -115,6 +117,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def damageStats(self, targetResists = None): if self.__dps == None: + self.__volley = 0 + self.__dps = 0 if self.dealsDamage is True and self.amountActive > 0: if self.hasAmmo: attr = "missileLaunchDuration" @@ -128,11 +132,10 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): volley = sum(map(lambda d: (getter("%sDamage"%d) or 0) * (1-getattr(targetResists, "%sAmount"%d, 0)), self.DAMAGE_TYPES)) volley *= self.amountActive volley *= self.getModifiedItemAttr("damageMultiplier") or 1 + self.__volley = volley self.__dps = volley / (cycleTime / 1000.0) - else: - self.__dps = 0 - return self.__dps + return self.__dps, self.__volley @property def miningStats(self): @@ -186,6 +189,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def clear(self): self.__dps = None + self.__volley = None self.__miningyield = None self.itemModifiedAttributes.clear() self.chargeModifiedAttributes.clear() diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index dc35fa5da..a382ae01d 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -82,6 +82,7 @@ class Fit(object): self.__minerYield = None self.__weaponVolley = None self.__droneDPS = None + self.__droneVolley = None self.__droneYield = None self.__sustainableTank = None self.__effectiveSustainableTank = None @@ -115,6 +116,7 @@ class Fit(object): self.__weaponDPS = None self.__weaponVolley = None self.__droneDPS = None + self.__droneVolley = None @property def damagePattern(self): @@ -151,6 +153,8 @@ class Fit(object): def ship(self, ship): self.__ship = ship self.shipID = ship.item.ID if ship is not None else None + # set mode of new ship + self.mode = self.ship.checkModeItem(None) if ship is not None else None @property def drones(self): @@ -205,10 +209,21 @@ class Fit(object): return self.__droneDPS + @property + def droneVolley(self): + if self.__droneVolley is None: + self.calculateWeaponStats() + + return self.__droneVolley + @property def totalDPS(self): return self.droneDPS + self.weaponDPS + @property + def totalVolley(self): + return self.droneVolley + self.weaponVolley + @property def minerYield(self): if self.__minerYield is None: @@ -294,6 +309,7 @@ class Fit(object): self.__effectiveSustainableTank = None self.__sustainableTank = None self.__droneDPS = None + self.__droneVolley = None self.__droneYield = None self.__ehp = None self.__calculated = False @@ -390,12 +406,14 @@ class Fit(object): (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 @@ -840,6 +858,7 @@ class Fit(object): weaponDPS = 0 droneDPS = 0 weaponVolley = 0 + droneVolley = 0 for mod in self.modules: dps, volley = mod.damageStats(self.targetResists) @@ -847,11 +866,14 @@ class Fit(object): weaponVolley += volley for drone in self.drones: - droneDPS += drone.damageStats(self.targetResists) + dps, volley = drone.damageStats(self.targetResists) + droneDPS += dps + droneVolley += volley self.__weaponDPS = weaponDPS self.__weaponVolley = weaponVolley self.__droneDPS = droneDPS + self.__droneVolley = droneVolley @property def fits(self): diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py index 49c3a3f58..f9585e1ab 100644 --- a/eos/saveddata/ship.py +++ b/eos/saveddata/ship.py @@ -29,6 +29,7 @@ class Ship(ItemAttrShortcut, HandledItem): self.__item = item self.__itemModifiedAttributes = ModifiedAttributeDict() + self.__modeItems = self._getModeItems() if not isinstance(item, int): self.__buildOriginal() @@ -76,7 +77,7 @@ class Ship(ItemAttrShortcut, HandledItem): @todo: rename this """ - items = self.getModeItems() + items = self.__modeItems if items != None: if item == None or item not in items: @@ -87,11 +88,15 @@ class Ship(ItemAttrShortcut, HandledItem): return Mode(item) return None - def getModes(self): - items = self.getModeItems() - return [Mode(item) for item in items] if items else None + @property + def modeItems(self): + return self.__modeItems - def getModeItems(self): + @property + def modes(self): + return [Mode(item) for item in self.__modeItems] if self.__modeItems else None + + def _getModeItems(self): """ Returns a list of valid mode items for ship. Note that this returns the valid Item objects, not the Mode objects. Returns None if not a diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 352340204..29a029389 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -24,7 +24,7 @@ class DamagePattern(ContextMenu): self.fit = sFit.getFit(fitID) self.patterns = sDP.getDamagePatternList() - self.patterns.sort(key=lambda p: (p.name not in ["Uniform","Selected Ammo"], p.name)) + self.patterns.sort(key=lambda p: (p.name not in ["Uniform", "Selected Ammo"], p.name)) self.patternIds = {} self.subMenus = OrderedDict() @@ -71,7 +71,6 @@ class DamagePattern(ContextMenu): def getSubMenu(self, context, selection, rootMenu, i, pitem): msw = True if "wxMSW" in wx.PlatformInfo else False - rootMenu.Bind(wx.EVT_MENU, self.handlePatternSwitch) # this bit is required for some reason if self.m[i] not in self.subMenus: # if we're trying to get submenu to something that shouldn't have one, @@ -79,10 +78,11 @@ class DamagePattern(ContextMenu): # our patternIds mapping, then return None for no submenu id = pitem.GetId() self.patternIds[id] = self.singles[i] + rootMenu.Bind(wx.EVT_MENU, self.handlePatternSwitch, pitem) if self.patternIds[id] == self.fit.damagePattern: bitmap = bitmapLoader.getBitmap("state_active_small", "icons") pitem.SetBitmap(bitmap) - return None + return False sub = wx.Menu() diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index 31025b397..3469017a8 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -16,7 +16,7 @@ class TacticalMode(ContextMenu): fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) - self.modes = fit.ship.getModes() + self.modes = fit.ship.modes self.currMode = fit.mode return srcContext == "fittingShip" and self.modes is not None diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 98190fcde..60468ec9a 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -53,6 +53,9 @@ class PFGeneralPref ( PreferenceView): labelSizer.Add( self.cbRackLabels, 0, wx.ALL|wx.EXPAND, 5 ) mainSizer.Add( labelSizer, 0, wx.LEFT|wx.EXPAND, 30 ) + self.cbShowTooltip = wx.CheckBox( panel, wx.ID_ANY, u"Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0 ) + mainSizer.Add( self.cbShowTooltip, 0, wx.ALL|wx.EXPAND, 5 ) + defCharSizer = wx.BoxSizer( wx.HORIZONTAL ) self.sFit = service.Fit.getInstance() @@ -65,6 +68,7 @@ class PFGeneralPref ( PreferenceView): self.cbRackLabels.SetValue(self.sFit.serviceFittingOptions["rackLabels"] or False) self.cbCompactSkills.SetValue(self.sFit.serviceFittingOptions["compactSkills"] or False) self.cbReopenFits.SetValue(self.openFitsSettings["enabled"]) + self.cbShowTooltip.SetValue(self.sFit.serviceFittingOptions["showTooltip"] or False) self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange) self.cbGlobalDmgPattern.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalDmgPatternStateChange) @@ -74,6 +78,7 @@ class PFGeneralPref ( PreferenceView): self.cbRackLabels.Bind(wx.EVT_CHECKBOX, self.onCBGlobalRackLabels) self.cbCompactSkills.Bind(wx.EVT_CHECKBOX, self.onCBCompactSkills) self.cbReopenFits.Bind(wx.EVT_CHECKBOX, self.onCBReopenFits) + self.cbShowTooltip.Bind(wx.EVT_CHECKBOX, self.onCBShowTooltip) self.cbRackLabels.Enable(self.sFit.serviceFittingOptions["rackSlots"] or False) @@ -127,6 +132,9 @@ class PFGeneralPref ( PreferenceView): def onCBReopenFits(self, event): self.openFitsSettings["enabled"] = self.cbReopenFits.GetValue() + def onCBShowTooltip(self, event): + self.sFit.serviceFittingOptions["showTooltip"] = self.cbShowTooltip.GetValue() + def getImage(self): return bitmapLoader.getBitmap("prefs_settings", "icons") diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 7f18b6a0c..a7180252e 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -30,6 +30,7 @@ class FirepowerViewFull(StatsView): StatsView.__init__(self) self.parent = parent self._cachedValues = [] + def getHeaderText(self, fit): return "Firepower" @@ -42,23 +43,21 @@ class FirepowerViewFull(StatsView): parent = self.panel = contentPanel self.headerPanel = headerPanel - headerContentSizer = wx.BoxSizer(wx.HORIZONTAL) - hsizer = headerPanel.GetSizer() - hsizer.Add(headerContentSizer,0,0,0) - self.stEff = wx.StaticText(headerPanel, wx.ID_ANY, "( Effective )") - headerContentSizer.Add(self.stEff) - headerPanel.GetParent().AddToggleItem(self.stEff) + hsizer = self.headerPanel.GetSizer() + self.stEff = wx.StaticText(self.headerPanel, wx.ID_ANY, "( Effective )") + hsizer.Add(self.stEff) + self.headerPanel.GetParent().AddToggleItem(self.stEff) panel = "full" sizerFirepower = wx.FlexGridSizer(1, 4) sizerFirepower.AddGrowableCol(1) - contentSizer.Add( sizerFirepower, 0, wx.EXPAND, 0) + contentSizer.Add(sizerFirepower, 0, wx.EXPAND, 0) counter = 0 - for damageType, image in (("weapon", "turret") , ("drone", "droneDPS")): + for damageType, image in (("weapon", "turret"), ("drone", "droneDPS")): baseBox = wx.BoxSizer(wx.HORIZONTAL) sizerFirepower.Add(baseBox, 1, wx.ALIGN_LEFT if counter == 0 else wx.ALIGN_CENTER_HORIZONTAL) @@ -73,10 +72,9 @@ class FirepowerViewFull(StatsView): box.Add(hbox, 1, wx.ALIGN_CENTER) lbl = wx.StaticText(parent, wx.ID_ANY, "0.0 DPS") - setattr(self, "label%sDps%s" % (panel.capitalize() ,damageType.capitalize()), lbl) + setattr(self, "label%sDps%s" % (panel.capitalize(), damageType.capitalize()), lbl) hbox.Add(lbl, 0, wx.ALIGN_CENTER) -# hbox.Add(wx.StaticText(parent, wx.ID_ANY, " DPS"), 0, wx.ALIGN_CENTER) self._cachedValues.append(0) counter += 1 targetSizer = sizerFirepower @@ -124,6 +122,12 @@ class FirepowerViewFull(StatsView): # And no longer display us self.panel.GetSizer().Clear(True) self.panel.GetSizer().Layout() + + # Remove effective label + hsizer = self.headerPanel.GetSizer() + hsizer.Remove(self.stEff) + self.stEff.Destroy() + # Get the new view view = StatsView.getView("miningyieldViewFull")(self.parent) view.populatePanel(self.panel, self.headerPanel) @@ -143,7 +147,7 @@ class FirepowerViewFull(StatsView): stats = (("labelFullDpsWeapon", lambda: fit.weaponDPS, 3, 0, 0, "%s DPS",None), ("labelFullDpsDrone", lambda: fit.droneDPS, 3, 0, 0, "%s DPS", None), - ("labelFullVolleyTotal", lambda: fit.weaponVolley, 3, 0, 0, "%s", "Volley: %.1f"), + ("labelFullVolleyTotal", lambda: fit.totalVolley, 3, 0, 0, "%s", "Volley: %.1f"), ("labelFullDpsTotal", lambda: fit.totalDPS, 3, 0, 0, "%s", None)) # See GH issue # #if fit is not None and fit.totalYield > 0: diff --git a/gui/builtinViewColumns/ammo.py b/gui/builtinViewColumns/ammo.py index cb3234ccc..fe50c6fbd 100644 --- a/gui/builtinViewColumns/ammo.py +++ b/gui/builtinViewColumns/ammo.py @@ -42,7 +42,6 @@ class Ammo(ViewColumn): return text - def getImageId(self, mod): return -1 diff --git a/gui/builtinViewColumns/ammoIcon.py b/gui/builtinViewColumns/ammoIcon.py index 4378e2458..1dfddf791 100644 --- a/gui/builtinViewColumns/ammoIcon.py +++ b/gui/builtinViewColumns/ammoIcon.py @@ -48,4 +48,8 @@ class AmmoIcon(ViewColumn): else: return -1 + def getToolTip(self, mod): + if isinstance(mod, Module) and mod.charge is not None: + return mod.charge.name + AmmoIcon.register() diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index a55a50407..86966574b 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -89,6 +89,12 @@ class AttributeDisplay(ViewColumn): def getImageId(self, mod): return -1 + def getToolTip(self, stuff): + if self.info.name == "cpu": + return "CPU" + else: + return self.info.name.title() + @staticmethod def getParameters(): return (("attribute", str, None), diff --git a/gui/builtinViewColumns/capacitorUse.py b/gui/builtinViewColumns/capacitorUse.py index 9e4916f2b..497869a66 100644 --- a/gui/builtinViewColumns/capacitorUse.py +++ b/gui/builtinViewColumns/capacitorUse.py @@ -34,9 +34,8 @@ class CapacitorUse(ViewColumn): sAttr = service.Attribute.getInstance() info = sAttr.getAttributeInfo("capacitorNeed") - self.imageId = fittingView.imageList.GetImageIndex(info.icon.iconFile, "pack") - self.bitmap = bitmapLoader.getBitmap(info.icon.iconFile, "pack") - + self.imageId = fittingView.imageList.GetImageIndex("capacitorRecharge_small", "icons") + self.bitmap = bitmapLoader.getBitmap("capacitorRecharge_small", "icons") def getText(self, mod): if isinstance(mod, Mode): @@ -51,4 +50,7 @@ class CapacitorUse(ViewColumn): def getImageId(self, mod): return -1 + def getToolTip(self, mod): + return self.name + CapacitorUse.register() diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index be6be2a86..17d27ef5d 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -74,4 +74,7 @@ class MaxRange(ViewColumn): return (("displayName", bool, False), ("showIcon", bool, True)) + def getToolTip(self, mod): + return "Optimal + Falloff" + MaxRange.register() diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index dd83ea22c..75b127bbd 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -23,7 +23,7 @@ from gui.viewColumn import ViewColumn from gui import bitmapLoader from gui.utils.numberFormatter import formatAmount from gui.utils.listFormatter import formatList -from service.fit import Fit +from service.fit import Fit, Market import wx @@ -424,6 +424,15 @@ class Miscellanea(ViewColumn): text = "{0}s".format(cycleTime) tooltip = "Spoolup time" return text, tooltip + elif itemGroup in ("Siege Module", "Cynosural Field"): + amt = stuff.getModifiedItemAttr("consumptionQuantity") + if amt: + typeID = stuff.getModifiedItemAttr("consumptionType") + item = Market.getInstance().getItem(typeID) + text = "{0} units".format(formatAmount(amt, 3, 0, 3)) + return text, item.name + else: + return "", None elif itemGroup in ("Fueled Armor Repairer", "Fueled Shield Booster"): hp = stuff.hpBeforeReload cycles = stuff.numShots diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 0afcb22c0..5fe6ef954 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -60,4 +60,7 @@ class Price(ViewColumn): def getImageId(self, mod): return -1 + def getToolTip(self, mod): + return self.name + Price.register() diff --git a/gui/builtinViewColumns/state.py b/gui/builtinViewColumns/state.py index 01e93e4f6..d26c46443 100644 --- a/gui/builtinViewColumns/state.py +++ b/gui/builtinViewColumns/state.py @@ -44,6 +44,10 @@ class State(ViewColumn): def getText(self, mod): return "" + def getToolTip(self, mod): + if isinstance(mod, Module) and not mod.isEmpty: + return State_.getName(mod.state).title() + def getImageId(self, stuff): if isinstance(stuff, Drone): return self.checkedId if stuff.amountActive > 0 else self.uncheckedId diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index c889f36cb..76c291cdd 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -26,7 +26,7 @@ import gui.display as d from gui.contextMenu import ContextMenu import gui.shipBrowser import gui.multiSwitch -from eos.types import Slot, Rack +from eos.types import Slot, Rack, Module from gui.builtinViewColumns.state import State from gui import bitmapLoader import gui.builtinViews.emptyView @@ -141,6 +141,7 @@ class FittingView(d.Display): self.Bind(wx.EVT_KEY_UP, self.kbEvent) self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_RIGHT_DOWN, self.click) + self.Bind(wx.EVT_MIDDLE_DOWN, self.click) self.Bind(wx.EVT_SHOW, self.OnShow) self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) @@ -162,12 +163,9 @@ class FittingView(d.Display): self.hoveredColumn = col if row != -1 and row not in self.blanks and col != -1 and col < len(self.DEFAULT_COLS): mod = self.mods[self.GetItemData(row)] - if self.DEFAULT_COLS[col] == "Miscellanea": - tooltip = self.activeColumns[col].getToolTip(mod) - if tooltip is not None: - self.SetToolTipString(tooltip) - else: - self.SetToolTip(None) + tooltip = self.activeColumns[col].getToolTip(mod) + if tooltip is not None: + self.SetToolTipString(tooltip) else: self.SetToolTip(None) else: @@ -424,14 +422,16 @@ class FittingView(d.Display): self.blanks[i] = x+i # modify blanks with actual index self.mods.insert(x+i, Rack.buildRack(slot)) - if fit.mode: - # Modes are special snowflakes and need a little manual loving - # We basically append the Mode rack and Mode to the modules - # while also marking their positions in the Blanks list + if fit.mode: + # Modes are special snowflakes and need a little manual loving + # We basically append the Mode rack and Mode to the modules + # while also marking their positions in the Blanks list + if sFit.serviceFittingOptions["rackSlots"]: self.blanks.append(len(self.mods)) self.mods.append(Rack.buildRack(Slot.MODE)) - self.blanks.append(len(self.mods)) - self.mods.append(fit.mode) + + self.blanks.append(len(self.mods)) + self.mods.append(fit.mode) else: self.mods = None @@ -520,16 +520,21 @@ class FittingView(d.Display): sFit = service.Fit.getInstance() fitID = self.mainFrame.getActiveFit() - ctrl = wx.GetMouseState().CmdDown() + ctrl = wx.GetMouseState().CmdDown() or wx.GetMouseState().MiddleDown() click = "ctrl" if ctrl is True else "right" if event.Button == 3 else "left" sFit.toggleModulesState(fitID, self.mods[self.GetItemData(row)], mods, click) + + # update state tooltip + tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)]) + self.SetToolTipString(tooltip) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) else: event.Skip() - slotColourMap = {1: wx.Colour(250, 235, 204), # yellow = low slots - 2: wx.Colour(188,215,241), # blue = mid slots - 3: wx.Colour(235,204,209), # red = high slots + slotColourMap = {1: wx.Colour(250, 235, 204), # yellow = low slots + 2: wx.Colour(188, 215, 241), # blue = mid slots + 3: wx.Colour(235, 204, 209), # red = high slots 4: '', 5: ''} @@ -557,12 +562,14 @@ class FittingView(d.Display): font = (self.GetClassDefaultAttributes()).font for i, mod in enumerate(self.mods): - if hasattr(mod,"slot") and slotMap[mod.slot]: - self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51)) - elif sFit.serviceFittingOptions["colorFitBySlot"] and not isinstance(mod, Rack): - self.SetItemBackgroundColour(i, self.slotColour(mod.slot)) - else: - self.SetItemBackgroundColour(i, self.GetBackgroundColour()) + self.SetItemBackgroundColour(i, self.GetBackgroundColour()) + + # only consider changing color if we're dealing with a Module + if type(mod) is Module: + if slotMap[mod.slot]: # Color too many modules as red + self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51)) + elif sFit.serviceFittingOptions["colorFitBySlot"]: # Color by slot it enabled + self.SetItemBackgroundColour(i, self.slotColour(mod.slot)) # Set rack face to bold if isinstance(mod, Rack) and \ diff --git a/gui/chromeTabs.py b/gui/chromeTabs.py index 91be9ed60..3a0c7b11c 100644 --- a/gui/chromeTabs.py +++ b/gui/chromeTabs.py @@ -22,6 +22,9 @@ import wx.lib.newevent import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils from gui import bitmapLoader +import gui.utils.fonts as fonts + +import service _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() @@ -321,7 +324,7 @@ class PFNotebook(wx.Panel): class PFTabRenderer: - def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6 , closeButton=True, fontSize=14): + def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6 , closeButton=True): """ Renders a new tab @@ -352,14 +355,13 @@ class PFTabRenderer: self.text = text self.tabSize = (width, height) self.closeButton = closeButton - self.fontSize = fontSize self.selected = False self.closeBtnHovering = False self.tabBitmap = None self.tabBackBitmap = None self.cbSize = 5 self.padding = 4 - self.font = wx.FontFromPixelSize((0, self.fontSize), wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.tabImg = img self.position = (0, 0) # Not used internally for rendering - helper for tab container @@ -677,6 +679,7 @@ class PFTabsContainer(wx.Panel): self.containerHeight = height self.startDrag = False self.dragging = False + self.sFit = service.Fit.getInstance() self.inclination = 7 if canAdd: @@ -1010,6 +1013,9 @@ class PFTabsContainer(wx.Panel): Checks to see if we have a tab preview and sets up the timer for it to display """ + if not self.sFit.serviceFittingOptions["showTooltip"] or False: + return + if self.previewTimer: if self.previewTimer.IsRunning(): if self.previewWnd: @@ -1275,7 +1281,7 @@ class PFNotebookPagePreview(wx.Frame): self.padding = 15 self.transp = 0 - hfont = wx.FontFromPixelSize((0, 14), wx.SWISS, wx.NORMAL, wx.NORMAL, False) + hfont = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.SetFont(hfont) tx, ty = self.GetTextExtent(self.title) @@ -1338,7 +1344,7 @@ class PFNotebookPagePreview(wx.Frame): mdc.SetBackground(wx.Brush(color)) mdc.Clear() - font = wx.FontFromPixelSize((0, 14), wx.SWISS, wx.NORMAL,wx.NORMAL, False) + font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL,wx.NORMAL, False) mdc.SetFont(font) x,y = mdc.GetTextExtent(self.title) diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 41bbca392..4ed09f9c1 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -73,18 +73,25 @@ class ContextMenu(object): rootMenu.info[id] = (m, fullContext, it) sub = m.getSubMenu(srcContext, selection, rootMenu, it, rootItem) + if sub is None: # if there is no sub menu, bind the handler to the rootItem rootMenu.Bind(wx.EVT_MENU, cls.handler, rootItem) - else: + elif sub: + # If sub exists and is not False, set submenu. + # Sub might return False when we have a mix of + # single menu items and submenus (see: damage profile + # context menu) + # # If there is a submenu, it is expected that the sub - # logic take care of it's own binding. No binding is - # done here + # logic take care of it's own bindings, including for + # any single root items. No binding is done here # # It is important to remember that when binding sub - # menu items, bind them to the rootMenu for proper - # event handling, eg: - # rootMenu.Bind(wx.EVE_MENU, self.handle, menuItem) + # menu items, the menu to bind to depends on platform. + # Windows should bind to rootMenu, and all other + # platforms should bind to sub menu. See existing + # implementations for examples. rootItem.SetSubMenu(sub) if bitmap is not None: diff --git a/gui/itemStats.py b/gui/itemStats.py index c727c170b..a26406af5 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -523,10 +523,24 @@ class ItemEffects (wx.Panel): self.Layout() def OnClick(self, event): + """ + Debug use: open effect file with default application. + If effect file does not exist, create it + """ + import os - file = os.path.join(config.pyfaPath,"eos","effects","%s.py"%event.GetText().lower()) + file = os.path.join(config.pyfaPath, "eos", "effects", "%s.py"%event.GetText().lower()) + + if not os.path.isfile(file): + open(file, 'a').close() + if 'wxMSW' in wx.PlatformInfo: os.startfile(file) + elif 'wxMac' in wx.PlatformInfo: + os.system("open "+file) + else: + import subprocess + subprocess.call(["xdg-open", file]) ########################################################################### diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 2d71da48a..634a22f01 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -42,7 +42,7 @@ from gui.additionsPane import AdditionsPane from gui.marketBrowser import MarketBrowser from gui.multiSwitch import MultiSwitch from gui.statsPane import StatsPane -from gui.shipBrowser import ShipBrowser, FitSelected +from gui.shipBrowser import ShipBrowser, FitSelected, ImportSelected, Stage3Selected from gui.characterEditor import CharacterEditor from gui.characterSelection import CharacterSelection from gui.patternEditor import DmgPatternEditorDlg @@ -54,6 +54,9 @@ from gui.utils.clipboard import toClipboard, fromClipboard from gui.fleetBrowser import FleetBrowser from gui.updateDialog import UpdateDialog from gui.builtinViews import * + +from time import gmtime, strftime + import locale locale.setlocale(locale.LC_ALL, '') @@ -201,9 +204,18 @@ class MainFrame(wx.Frame): dlg.Destroy() def LoadPreviousOpenFits(self): + sFit = service.Fit.getInstance() + self.prevOpenFits = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) fits = self.prevOpenFits['pyfaOpenFits'] + # Remove any fits that cause exception when fetching (non-existent fits) + for id in fits[:]: + try: + sFit.getFit(id) + except: + fits.remove(id) + if not self.prevOpenFits['enabled'] or len(fits) is 0: # add blank page if there are no fits to be loaded self.fitMultiSwitch.AddPage() @@ -327,47 +339,21 @@ class MainFrame(wx.Frame): dlg.ShowModal() dlg.Destroy() - def showImportDialog(self, event): - fits = [] - sFit = service.Fit.getInstance() - dlg=wx.FileDialog( - self, - "Open One Or More Fitting Files", - wildcard = "EFT text fitting files (*.cfg)|*.cfg|" \ - "EVE XML fitting files (*.xml)|*.xml|" \ - "All Files (*)|*", - style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) - if (dlg.ShowModal() == wx.ID_OK): - self.waitDialog = animUtils.WaitDialog(self, title = "Importing") - sFit.importFitsThreaded(dlg.GetPaths(), self.importCallback) - dlg.Destroy() - self.waitDialog.ShowModal() - - def importCallback(self, fits): - self.waitDialog.Destroy() - sFit = service.Fit.getInstance() - IDs = sFit.saveImportedFits(fits) - self._openAfterImport(len(fits), IDs) - - def _openAfterImport(self, importCount, fitIDs): - if importCount == 1: - wx.PostEvent(self, FitSelected(fitID=fitIDs[0])) - - self.shipBrowser.RefreshContent() - def showExportDialog(self, event): - dlg=wx.FileDialog( - self, - "Save Fitting As...", - wildcard = "EVE XML fitting files (*.xml)|*.xml", - style = wx.FD_SAVE) - if (dlg.ShowModal() == wx.ID_OK): - sFit = service.Fit.getInstance() + """ Export active fit """ + sFit = service.Fit.getInstance() + fit = sFit.getFit(self.getActiveFit()) + defaultFile = "%s - %s.xml"%(fit.ship.item.name, fit.name) if fit else None + + dlg = wx.FileDialog(self, "Save Fitting As...", + wildcard = "EVE XML fitting files (*.xml)|*.xml", + style = wx.FD_SAVE, + defaultFile=defaultFile) + if dlg.ShowModal() == wx.ID_OK: format = dlg.GetFilterIndex() - output = "" path = dlg.GetPath() - if (format == 0): - output = sFit.exportXml(self.getActiveFit()) + if format == 0: + output = sFit.exportXml(None, self.getActiveFit()) if '.' not in os.path.basename(path): path += ".xml" else: @@ -406,7 +392,7 @@ class MainFrame(wx.Frame): # Target Resists editor self.Bind(wx.EVT_MENU, self.showTargetResistsEditor, id=menuBar.targetResistsEditorId) # Import dialog - self.Bind(wx.EVT_MENU, self.showImportDialog, id=wx.ID_OPEN) + self.Bind(wx.EVT_MENU, self.fileImportDialog, id=wx.ID_OPEN) # Export dialog self.Bind(wx.EVT_MENU, self.showExportDialog, id=wx.ID_SAVEAS) # Import from Clipboard @@ -545,17 +531,16 @@ class MainFrame(wx.Frame): def clipboardXml(self): sFit = service.Fit.getInstance() - toClipboard(sFit.exportXml(self.getActiveFit())) + toClipboard(sFit.exportXml(None, self.getActiveFit())) def importFromClipboard(self, event): sFit = service.Fit.getInstance() try: fits = sFit.importFitFromBuffer(fromClipboard(), self.getActiveFit()) - IDs = sFit.saveImportedFits(fits) - self._openAfterImport(len(fits), IDs) except: pass - + else: + self._openAfterImport(fits) def exportToClipboard(self, event): CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft, @@ -571,44 +556,109 @@ class MainFrame(wx.Frame): pass dlg.Destroy() - def backupToXml(self, event): + def fileImportDialog(self, event): + """Handles importing single/multiple EVE XML / EFT cfg fit files""" sFit = service.Fit.getInstance() - saveDialog = wx.FileDialog( - self, - "Save Backup As...", - wildcard = "EVE XML fitting file (*.xml)|*.xml", - style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) - if (saveDialog.ShowModal() == wx.ID_OK): + dlg = wx.FileDialog(self, "Open One Or More Fitting Files", + wildcard = "EVE XML fitting files (*.xml)|*.xml|" \ + "EFT text fitting files (*.cfg)|*.cfg|" \ + "All Files (*)|*", + style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) + if (dlg.ShowModal() == wx.ID_OK): + self.progressDialog = wx.ProgressDialog( + "Importing fits", + " "*100, # set some arbitrary spacing to create wifth in window + parent=self, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + self.progressDialog.message = None + sFit.importFitsThreaded(dlg.GetPaths(), self.fileImportCallback) + self.progressDialog.ShowModal() + dlg.Destroy() + + def fileImportCallback(self, info, fits=None): + """ + While importing fits from file, the logic calls back to this function to + update progress bar to show activity. XML files can contain multiple + ships with multiple fits, whereas EFT cfg files contain many fits of + a single ship. When iterating through the files, we update the message + when we start a new file, and then Pulse the progress bar with every fit + that is processed. + """ + + if info == -1: + # Done processing + self.progressDialog.Hide() + self._openAfterImport(fits) + elif info != self.progressDialog.message and info is not None: + # New message, overwrite cached message and update + self.progressDialog.message = info + self.progressDialog.Pulse(info) + else: + # Simply Pulse() if we don't have anything else to do + self.progressDialog.Pulse() + + def _openAfterImport(self, fits): + if len(fits) > 0: + if len(fits) == 1: + fit = fits[0] + wx.PostEvent(self, FitSelected(fitID=fit.ID)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True)) + else: + wx.PostEvent(self.shipBrowser, ImportSelected(fits=fits, back=True)) + + def backupToXml(self, event): + """ Back up all fits to EVE XML file """ + defaultFile = "pyfa-fits-%s.xml"%strftime("%Y%m%d_%H%M%S", gmtime()) + + saveDialog = wx.FileDialog(self, "Save Backup As...", + wildcard = "EVE XML fitting file (*.xml)|*.xml", + style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + defaultFile=defaultFile) + + if saveDialog.ShowModal() == wx.ID_OK: filePath = saveDialog.GetPath() if '.' not in os.path.basename(filePath): filePath += ".xml" - self.waitDialog = animUtils.WaitDialog(self) - sFit.backupFits(filePath, self.closeWaitDialog) - self.waitDialog.ShowModal() - saveDialog.Destroy() + sFit = service.Fit.getInstance() + max = sFit.countAllFits() + + self.progressDialog = wx.ProgressDialog("Backup fits", + "Backing up %d fits to: %s"%(max, filePath), + maximum=max, parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + + sFit.backupFits(filePath, self.backupCallback) + self.progressDialog.ShowModal() + + def backupCallback(self, info): + if info == -1: + self.progressDialog.Hide() + else: + self.progressDialog.Update(info) def exportSkillsNeeded(self, event): + """ Exports skills needed for active fit and active character """ sCharacter = service.Character.getInstance() - saveDialog = wx.FileDialog( - self, - "Export Skills Needed As...", - wildcard = "EVEMon skills training file (*.emp)|*.emp|" \ - "EVEMon skills training XML file (*.xml)|*.xml|" \ - "Text skills training file (*.txt)|*.txt", - style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) - if (saveDialog.ShowModal() == wx.ID_OK): + saveDialog = wx.FileDialog(self, "Export Skills Needed As...", + wildcard = "EVEMon skills training file (*.emp)|*.emp|" \ + "EVEMon skills training XML file (*.xml)|*.xml|" \ + "Text skills training file (*.txt)|*.txt", + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + + if saveDialog.ShowModal() == wx.ID_OK: saveFmtInt = saveDialog.GetFilterIndex() - saveFmt = "" + if saveFmtInt == 0: # Per ordering of wildcards above saveFmt = "emp" elif saveFmtInt == 1: saveFmt = "xml" else: saveFmt = "txt" + filePath = saveDialog.GetPath() if '.' not in os.path.basename(filePath): filePath += ".{0}".format(saveFmt) + self.waitDialog = animUtils.WaitDialog(self) sCharacter.backupSkills(filePath, saveFmt, self.getActiveFit(), self.closeWaitDialog) self.waitDialog.ShowModal() @@ -616,29 +666,38 @@ class MainFrame(wx.Frame): saveDialog.Destroy() def importCharacter(self, event): - sCharacter = service.Character.getInstance() - dlg=wx.FileDialog( - self, - "Open One Or More Character Files", - wildcard = "EVE CCP API XML character files (*.xml)|*.xml|" \ - "All Files (*)|*", - style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) - if (dlg.ShowModal() == wx.ID_OK): - self.waitDialog = animUtils.WaitDialog(self, title = "Importing Character") + """ Imports character XML file from EVE API """ + dlg = wx.FileDialog(self, "Open One Or More Character Files", + wildcard="EVE API XML character files (*.xml)|*.xml|" \ + "All Files (*)|*", + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) + + if dlg.ShowModal() == wx.ID_OK: + self.waitDialog = animUtils.WaitDialog(self, title="Importing Character") + sCharacter = service.Character.getInstance() sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) dlg.Destroy() self.waitDialog.ShowModal() - def exportHtml(self, event): - from gui.utils.exportHtml import exportHtml - self.waitDialog = animUtils.WaitDialog(self) - exportHtml.getInstance().refreshFittingHtml(True, self.closeWaitDialog) - self.waitDialog.ShowModal() - def importCharacterCallback(self): self.waitDialog.Destroy() wx.PostEvent(self, GE.CharListUpdated()) + def exportHtml(self, event): + from gui.utils.exportHtml import exportHtml + sFit = service.Fit.getInstance() + settings = service.settings.HTMLExportSettings.getInstance() + + max = sFit.countAllFits() + path = settings.getPath() + self.progressDialog = wx.ProgressDialog("Backup fits", + "Generating HTML file at: %s"%path, + maximum=max, parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + + exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback) + self.progressDialog.ShowModal() + def closeWaitDialog(self): self.waitDialog.Destroy() @@ -650,7 +709,7 @@ class MainFrame(wx.Frame): else: self.graphFrame.SetFocus() - def openWXInspectTool(self,event): + def openWXInspectTool(self, event): from wx.lib.inspection import InspectionTool if not InspectionTool().initialized: InspectionTool().Init() diff --git a/gui/patternEditor.py b/gui/patternEditor.py index f3b8da855..07181ea9f 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -28,11 +28,11 @@ from service.damagePattern import ImportError ## Class DmgPatternEditorDlg ########################################################################### -class DmgPatternEditorDlg (wx.Dialog): +class DmgPatternEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__ (self, parent, id = wx.ID_ANY, title = u"Damage Pattern Editor", size = wx.Size( 400,240 )) + wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Damage Pattern Editor", size = wx.Size( 400,240 )) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -125,7 +125,7 @@ class DmgPatternEditorDlg (wx.Dialog): editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) editObj.SetLimited(True) editObj.SetMin(0) - editObj.SetMax(99999) + editObj.SetMax(2000000) contentSizer.Add(dmgeditSizer, 1, wx.EXPAND | wx.ALL, 5) self.slfooter = wx.StaticLine(self) @@ -244,13 +244,7 @@ class DmgPatternEditorDlg (wx.Dialog): self.block = False self.ValuesUpdated() - def newPattern(self,event): - sDP = service.DamagePattern.getInstance() - p = sDP.newPattern() - self.choices.append(p) - id = self.ccDmgPattern.Append(p.name) - self.ccDmgPattern.SetSelection(id) - + def newPattern(self, event): self.restrict() # reset values for type in self.DAMAGE_TYPES: @@ -258,9 +252,10 @@ class DmgPatternEditorDlg (wx.Dialog): editObj.SetValue(0) self.btnSave.SetLabel("Create") + self.Refresh() self.renamePattern() - def renamePattern(self,event=None): + def renamePattern(self, event=None): if event is not None: self.btnSave.SetLabel("Rename") @@ -268,7 +263,12 @@ class DmgPatternEditorDlg (wx.Dialog): self.namePicker.Show() self.headerSizer.Replace(self.ccDmgPattern, self.namePicker) self.namePicker.SetFocus() - self.namePicker.SetValue(self.getActivePattern().name) + + if event is not None: # Rename mode + self.btnSave.SetLabel("Rename") + self.namePicker.SetValue(self.getActivePattern().name) + else: # Create mode + self.namePicker.SetValue("") for btn in (self.new, self.rename, self.delete, self.copy): btn.Hide() @@ -284,19 +284,25 @@ class DmgPatternEditorDlg (wx.Dialog): newName = self.namePicker.GetLineText(0) self.stNotice.SetLabel("") - p = self.getActivePattern() - for pattern in self.choices: - if pattern.name == newName and p != pattern: - self.stNotice.SetLabel("Name already used, please choose another") - return - if newName == "": self.stNotice.SetLabel("Invalid name.") return sDP = service.DamagePattern.getInstance() + if event.EventObject.Label == "Create": + p = sDP.newPattern() + else: + # we are renaming, so get the current selection + p = self.getActivePattern() + + for pattern in self.choices: + if pattern.name == newName and p != pattern: + self.stNotice.SetLabel("Name already used, please choose another") + return + sDP.renamePattern(p, newName) + self.updateChoices(newName) self.headerSizer.Replace(self.namePicker, self.ccDmgPattern) self.ccDmgPattern.Show() self.namePicker.Hide() @@ -335,7 +341,7 @@ class DmgPatternEditorDlg (wx.Dialog): def __del__( self ): pass - def updateChoices(self): + def updateChoices(self, select=None): "Gathers list of patterns and updates choice selections" sDP = service.DamagePattern.getInstance() self.choices = sDP.getDamagePatternList() @@ -348,10 +354,14 @@ class DmgPatternEditorDlg (wx.Dialog): self.choices.sort(key=lambda p: p.name) self.ccDmgPattern.Clear() - for choice in map(lambda p: p.name, self.choices): + for i, choice in enumerate(map(lambda p: p.name, self.choices)): self.ccDmgPattern.Append(choice) - self.ccDmgPattern.SetSelection(0) + if select is not None and choice == select: + self.ccDmgPattern.SetSelection(i) + + if select is None: + self.ccDmgPattern.SetSelection(0) self.patternChanged() def importPatterns(self, event): diff --git a/gui/pygauge.py b/gui/pygauge.py index d9f92d5e2..f06a78958 100644 --- a/gui/pygauge.py +++ b/gui/pygauge.py @@ -18,6 +18,7 @@ import math from gui.utils import colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.animEffects as animEffects +import gui.utils.fonts as fonts class PyGauge(wx.PyWindow): """ @@ -74,7 +75,7 @@ class PyGauge(wx.PyWindow): self._oldPercentage = 0 self._showRemaining = False - self.font = wx.FontFromPixelSize((0,14),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.SetBarGradient((wx.Colour(119,119,119),wx.Colour(153,153,153))) self.SetBackgroundColour(wx.Colour(51,51,51)) @@ -378,9 +379,15 @@ class PyGauge(wx.PyWindow): dc.DrawLabel(formatStr, rect, wx.ALIGN_CENTER) else: if self.GetBarGradient() and self._showRemaining: - formatStr = "{0:." + str(self._fractionDigits) + "f} left" range = self._range if self._range > 0.01 else 0 - value = max( range - self._value , 0) + value = range - self._value + if value < 0: + label = "over" + value = -value + else: + label = "left" + formatStr = "{0:." + str(self._fractionDigits) + "f} " + label + else: formatStr = "{0:." + str(self._fractionDigits) + "f}%" diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index a5764a74a..259f34213 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -23,12 +23,12 @@ import service from gui.utils.clipboard import toClipboard, fromClipboard from service.targetResists import ImportError -class ResistsEditorDlg (wx.Dialog): +class ResistsEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__ (self, parent, id = wx.ID_ANY, title = u"Target Resists Editor", size = wx.Size( 350,240 )) + wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Target Resists Editor", size = wx.Size( 350,240 )) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) diff --git a/gui/sfBrowserItem.py b/gui/sfBrowserItem.py index 0a2da9090..4067707c3 100644 --- a/gui/sfBrowserItem.py +++ b/gui/sfBrowserItem.py @@ -413,4 +413,4 @@ class SFBrowserItem(wx.Window): self.bkBitmap.state = state self.bkBitmap.sFactor = sFactor self.bkBitmap.eFactor = eFactor - self.bkBitmap.mFactor = mFactor \ No newline at end of file + self.bkBitmap.mFactor = mFactor diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index ac98c8e31..20552aa55 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -5,7 +5,6 @@ import gui.mainFrame import gui.globalEvents as GE import time from gui.PFListPane import PFListPane -import service from wx.lib.buttons import GenBitmapButton @@ -18,6 +17,7 @@ import gui.sfBrowserItem as SFItem from gui.contextMenu import ContextMenu import service +import gui.utils.fonts as fonts FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent() FitSelected, EVT_FIT_SELECTED = wx.lib.newevent.NewEvent() @@ -29,6 +29,7 @@ Stage1Selected, EVT_SB_STAGE1_SEL = wx.lib.newevent.NewEvent() Stage2Selected, EVT_SB_STAGE2_SEL = wx.lib.newevent.NewEvent() 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): @@ -323,7 +324,7 @@ class RaceSelector(wx.Window): event.Skip() class NavigationPanel(SFItem.SFBrowserItem): - def __init__(self,parent, size = (-1,24)): + def __init__(self,parent, size = (-1, 24)): SFItem.SFBrowserItem.__init__(self,parent,size = size) self.rewBmpH = bitmapLoader.getBitmap("frewind_small","icons") @@ -351,10 +352,10 @@ class NavigationPanel(SFItem.SFBrowserItem): self.padding = 4 self.lastSearch = "" - self.recentSearches = [] + self.recentSearches = [] # not used? self.inSearch = False - self.fontSmall = wx.FontFromPixelSize((0,12),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL) w,h = size self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1 ), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) self.BrowserSearchBox.Show(False) @@ -370,23 +371,14 @@ class NavigationPanel(SFItem.SFBrowserItem): self.Bind(wx.EVT_SIZE, self.OnResize) - def OnScheduleSearch(self, event): search = self.BrowserSearchBox.GetValue() # Make sure we do not count wildcard as search symbol realsearch = search.replace("*", "") - if len(realsearch) < 3 and len(realsearch) >= 0: - if self.inSearch == True: - self.inSearch = False - if len(self.shipBrowser.browseHist) > 0: - stage,data = self.shipBrowser.browseHist.pop() - self.gotoStage(stage,data) - else: - if search: - wx.PostEvent(self.shipBrowser,SearchSelected(text=search, back = False)) - self.inSearch = True - else: - self.inSearch = False + if len(realsearch) >= 3: + self.lastSearch = search + wx.PostEvent(self.shipBrowser,SearchSelected(text=search, back = False)) + def ToggleSearchBox(self): if self.BrowserSearchBox.IsShown(): @@ -400,8 +392,6 @@ class NavigationPanel(SFItem.SFBrowserItem): self.OnBrowserSearchBoxLostFocus(None) def OnBrowserSearchBoxLostFocus(self, event): - self.lastSearch = self.BrowserSearchBox.GetValue() - self.BrowserSearchBox.ChangeValue("") self.BrowserSearchBox.Show(False) def OnBrowserSearchBoxEsc(self, event): @@ -410,7 +400,6 @@ class NavigationPanel(SFItem.SFBrowserItem): else: event.Skip() - def OnResize(self, event): self.Refresh() @@ -448,7 +437,7 @@ class NavigationPanel(SFItem.SFBrowserItem): sFit = service.Fit.getInstance() fitID = sFit.newFit(shipID, "%s fit" %shipName) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.Parent,Stage3Selected(shipID=shipID, back = True)) + wx.PostEvent(self.Parent,Stage3Selected(shipID=shipID)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def OnHistoryReset(self): @@ -459,11 +448,11 @@ class NavigationPanel(SFItem.SFBrowserItem): def OnHistoryBack(self): if len(self.shipBrowser.browseHist) > 0: stage,data = self.shipBrowser.browseHist.pop() - self.gotoStage(stage,data) + self.gotoStage(stage, data) def AdjustChannels(self, bitmap): img = wx.ImageFromBitmap(bitmap) - img = img.AdjustChannels(1.05,1.05,1.05,1) + img = img.AdjustChannels(1.05, 1.05, 1.05, 1) return wx.BitmapFromImage(img) def UpdateElementsPos(self, mdc): @@ -535,20 +524,20 @@ class NavigationPanel(SFItem.SFBrowserItem): self.bkBitmap.mFactor = mFactor - def gotoStage(self,stage, data = None): + def gotoStage(self, stage, data=None): if stage == 1: - wx.PostEvent(self.Parent,Stage1Selected()) + wx.PostEvent(self.Parent, Stage1Selected()) elif stage == 2: - wx.PostEvent(self.Parent,Stage2Selected(categoryID=data, back = True)) + wx.PostEvent(self.Parent, Stage2Selected(categoryID=data, back=True)) elif stage == 3: - wx.PostEvent(self.Parent,Stage3Selected(shipID=data, back = 1)) + wx.PostEvent(self.Parent, Stage3Selected(shipID=data)) elif stage == 4: self.shipBrowser._activeStage = 4 - self.stStatus.SetLabel("Search: %s" % data.capitalize()) - self.Layout() - wx.PostEvent(self.Parent,SearchSelected(text=data, back = True)) + wx.PostEvent(self.Parent, SearchSelected(text=data, back=True)) + elif stage == 5: + wx.PostEvent(self.Parent, ImportSelected(fits=data)) else: - wx.PostEvent(self.Parent,Stage1Selected()) + wx.PostEvent(self.Parent, Stage1Selected()) class ShipBrowser(wx.Panel): @@ -609,6 +598,7 @@ class ShipBrowser(wx.Panel): self.Bind(EVT_SB_STAGE1_SEL, self.stage1) self.Bind(EVT_SB_STAGE3_SEL, self.stage3) self.Bind(EVT_SB_SEARCH_SEL, self.searchStage) + self.Bind(EVT_SB_IMPORT_SEL, self.importStage) self.mainFrame.Bind(GE.FIT_CHANGED, self.RefreshList) @@ -653,6 +643,8 @@ class ShipBrowser(wx.Panel): return self._stage2Data if stage == 3: return self._stage3Data + if stage == 4: + return self.navpanel.lastSearch return -1 def GetStage3ShipName(self): @@ -707,7 +699,12 @@ class ShipBrowser(wx.Panel): self.raceselect.Show(False) self.Layout() - RACE_ORDER = ["amarr", "caldari", "gallente", "minmatar", "sisters", "ore", "serpentis", "angel", "blood", "sansha", "guristas", "jove", None] + RACE_ORDER = [ + "amarr", "caldari", "gallente", "minmatar", + "sisters", "ore", + "serpentis", "angel", "blood", "sansha", "guristas", "mordu", + "jove", None + ] def raceNameKey(self, ship): return self.RACE_ORDER.index(ship.race), ship.name @@ -803,13 +800,15 @@ class ShipBrowser(wx.Panel): self.lpane.ShowLoading(False) - if event.back == 0: - self.browseHist.append( (2,self._stage2Data) ) - elif event.back == -1: - if len(self.navpanel.recentSearches)>0: + # If back is False, do not append to history. This could be us calling + # the stage from previous history, creating / copying fit, etc. + # We also have to use conditional for search stage since it's last data + # is kept elsewhere + if getattr(event, "back", False): + if self._activeStage == 4 and self.navpanel.lastSearch != "": self.browseHist.append((4, self.navpanel.lastSearch)) - elif event.back > 0: - self.browseHist.append( (2,event.back) ) + else: + self.browseHist.append((self._activeStage, self.lastdata)) shipID = event.shipID self.lastdata = shipID @@ -862,11 +861,11 @@ class ShipBrowser(wx.Panel): self.navpanel.ShowSwitchEmptyGroupsButton(False) if not event.back: - if self._activeStage !=4: - if len(self.browseHist) >0: - self.browseHist.append( (self._activeStage, self.lastdata) ) + if self._activeStage != 4: + if len(self.browseHist) > 0: + self.browseHist.append((self._activeStage, self.lastdata)) else: - self.browseHist.append((1,0)) + self.browseHist.append((1, 0)) self._lastStage = self._activeStage self._activeStage = 4 @@ -897,6 +896,46 @@ class ShipBrowser(wx.Panel): self.raceselect.Show(False) self.Layout() + def importStage(self, event): + self.lpane.ShowLoading(False) + + self.navpanel.ShowNewFitButton(False) + self.navpanel.ShowSwitchEmptyGroupsButton(False) + + if getattr(event, "back", False): + self.browseHist.append((self._activeStage, self.lastdata)) + + self._lastStage = self._activeStage + self._activeStage = 5 + + fits = event.fits + + # sort by ship name, then fit name + fits.sort(key=lambda fit: (fit.ship.item.name, fit.name)) + + self.lastdata = fits + self.lpane.Freeze() + self.lpane.RemoveAllChildren() + + if fits: + for fit in fits: + self.lpane.AddWidget(FitItem( + self.lpane, + fit.ID, ( + fit.ship.item.name, + fit.name, + fit.booster, + fit.timestamp), + fit.ship.item.ID)) + self.lpane.RefreshList(doFocus=False) + self.lpane.Thaw() + + self.raceselect.RebuildRaces(self.RACE_ORDER) + + if self.showRacesFilterInStage2Only: + self.raceselect.Show(False) + self.Layout() + class PFStaticText(wx.Panel): def __init__(self, parent, label=wx.EmptyString): wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size = parent.GetSize()) @@ -938,7 +977,7 @@ class CategoryItem(SFItem.SFBrowserItem): self.padding = 4 - self.fontBig = wx.FontFromPixelSize((0,15),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.fontBig = wx.Font(fonts.BIG, wx.SWISS, wx.NORMAL, wx.NORMAL) self.animTimerId = wx.NewId() @@ -1052,9 +1091,9 @@ class ShipItem(SFItem.SFBrowserItem): self.shipID = shipID - 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.fontBig = wx.Font(fonts.BIG, wx.SWISS, wx.NORMAL, wx.BOLD) + self.fontNormal = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL) + self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL) self.shipBmp = None if shipID: @@ -1159,7 +1198,7 @@ class ShipItem(SFItem.SFBrowserItem): else: shipName, fittings = self.shipFittingInfo if fittings > 0: - wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID, back = -1 if self.shipBrowser.GetActiveStage() == 4 else 0)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID, back=True)) else: self.newBtnCB() @@ -1440,9 +1479,9 @@ class FitItem(SFItem.SFBrowserItem): self.dragMotionTrigger = self.dragMotionTrail self.dragWindow = None - 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.fontBig = wx.Font(fonts.BIG, wx.SWISS, wx.NORMAL, wx.BOLD) + self.fontNormal = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL) + self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL) self.SetDraggable() @@ -1571,7 +1610,7 @@ class FitItem(SFItem.SFBrowserItem): sFit = service.Fit.getInstance() fitID = sFit.copyFit(self.fitID) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID, back=True)) + wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def renameBtnCB(self): @@ -1614,18 +1653,22 @@ class FitItem(SFItem.SFBrowserItem): self.deleted = True sFit = service.Fit.getInstance() + fit = sFit.getFit(self.fitID) sFit.deleteFit(self.fitID) - if self.shipBrowser.GetActiveStage() == 4: - wx.PostEvent(self.shipBrowser,SearchSelected(text=self.shipBrowser.navpanel.lastSearch,back=True)) + if self.shipBrowser.GetActiveStage() == 5: + if fit in self.shipBrowser.lastdata: # remove fit from import cache + self.shipBrowser.lastdata.remove(fit) + wx.PostEvent(self.shipBrowser, ImportSelected(fits=self.shipBrowser.lastdata)) + elif self.shipBrowser.GetActiveStage() == 4: + wx.PostEvent(self.shipBrowser, SearchSelected(text=self.shipBrowser.navpanel.lastSearch, back=True)) else: - wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID, back=True)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID)) wx.PostEvent(self.mainFrame, FitRemoved(fitID=self.fitID)) def MouseLeftUp(self, event): - if self.dragging and self.dragged: self.dragging = False self.dragged = False diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index c61c2444a..726bec265 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -7,7 +7,7 @@ class exportHtml(): _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = exportHtml() return cls._instance @@ -15,17 +15,17 @@ class exportHtml(): def __init__(self): self.thread = exportHtmlThread() - def refreshFittingHtml(self, force = False, callback = False): + def refreshFittingHtml(self, force=False, callback=False): settings = service.settings.HTMLExportSettings.getInstance() - if (force or settings.getEnabled()): + if force or settings.getEnabled(): self.thread.stop() self.thread = exportHtmlThread(callback) self.thread.start() class exportHtmlThread(threading.Thread): - def __init__(self, callback = False): + def __init__(self, callback=False): threading.Thread.__init__(self) self.callback = callback self.stopRunning = False @@ -40,7 +40,7 @@ class exportHtmlThread(threading.Thread): return sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() + sFit = service.Fit.getInstance() settings = service.settings.HTMLExportSettings.getInstance() timestamp = time.localtime(time.time()) @@ -135,6 +135,9 @@ class exportHtmlThread(threading.Thread): HTML += ' \n' ' \n') + if groupFits > 0: # Market group header HTML += ( @@ -197,5 +214,5 @@ class exportHtmlThread(threading.Thread): pass if self.callback: - wx.CallAfter(self.callback) + wx.CallAfter(self.callback, -1) diff --git a/gui/utils/fonts.py b/gui/utils/fonts.py new file mode 100644 index 000000000..08f858f21 --- /dev/null +++ b/gui/utils/fonts.py @@ -0,0 +1,8 @@ +import wx + +if 'wxMac' in wx.PlatformInfo: + sizes = (10, 11, 12) +else: + sizes = (7, 8, 9) + +SMALL, NORMAL, BIG = sizes diff --git a/gui/viewColumn.py b/gui/viewColumn.py index 948d09074..c2ffff612 100644 --- a/gui/viewColumn.py +++ b/gui/viewColumn.py @@ -50,7 +50,7 @@ class ViewColumn(object): return "" def getToolTip(self, mod): - return "" + return None def getImageId(self, mod): return -1 diff --git a/icons/capacitorRecharge_small.png b/icons/capacitorRecharge_small.png new file mode 100644 index 000000000..d1c662dd8 Binary files /dev/null and b/icons/capacitorRecharge_small.png differ diff --git a/icons/race_mordu_small.png b/icons/race_mordu_small.png new file mode 100644 index 000000000..6804d7660 Binary files /dev/null and b/icons/race_mordu_small.png differ diff --git a/scripts/conversion.py b/scripts/conversion.py new file mode 100644 index 000000000..5120791d4 --- /dev/null +++ b/scripts/conversion.py @@ -0,0 +1,65 @@ +# Developed for module tiericide, this script will quickly print out a market +# conversion map based on database conversions / renamed modules between two +# eve databases. Correct database conversions must be implemented in upgrade +# script in eos.db.migrations + +import argparse +import os.path +import sqlite3 +import sys + +# Add eos root path to sys.path so we can import ourselves +path = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) +sys.path.append(os.path.realpath(os.path.join(path, ".."))) + +# change to correct conversion +from eos.db.migrations.upgrade4 import CONVERSIONS + +def main(old, new): + # Open both databases and get their cursors + old_db = sqlite3.connect(os.path.expanduser(old)) + old_cursor = old_db.cursor() + new_db = sqlite3.connect(os.path.expanduser(new)) + new_cursor = new_db.cursor() + + print "# Renamed items" + + # find renames (stolen from itemDiff) + old_namedata = {} + new_namedata = {} + + for cursor, dictionary in ((old_cursor, old_namedata), (new_cursor, new_namedata)): + cursor.execute("SELECT typeID, typeName FROM invtypes") + for row in cursor: + id = row[0] + name = row[1] + dictionary[id] = name + + for id in set(old_namedata.keys()).intersection(new_namedata.keys()): + oldname = old_namedata[id] + newname = new_namedata[id] + if oldname != newname: + print '"%s": "%s",' % (oldname.encode('utf-8'), newname.encode('utf-8')) + + # Convert modules + print "\n# Converted items" + for replacement_item, list in CONVERSIONS.iteritems(): + new_cursor.execute('SELECT "typeName" FROM "invtypes" WHERE "typeID" = ?', (replacement_item,)) + for row in new_cursor: + new_item = row[0] + break + + for retired_item in list: + old_cursor.execute('SELECT "typeName" FROM "invtypes" WHERE "typeID" = ?', (retired_item,)) + for row in old_cursor: + old_item = row[0] + break + print '"%s": "%s",' % (old_item, new_item) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-o", "--old", type=str) + parser.add_argument("-n", "--new", type=str) + args = parser.parse_args() + + main(args.old, args.new) diff --git a/scripts/effectUsedBy.py b/scripts/effectUsedBy.py index ad949af9e..b9ee818b9 100755 --- a/scripts/effectUsedBy.py +++ b/scripts/effectUsedBy.py @@ -57,7 +57,7 @@ import re import sqlite3 from optparse import OptionParser -script_dir = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) +script_dir = os.path.dirname(__file__) # Form list of effects for processing effects_path = os.path.join(script_dir, "..", "eos", "effects") @@ -92,6 +92,7 @@ TYPENAMECOMBS_WEIGHT = 1.0 # If score drops below this value, remaining items will be listed # without any grouping LOWEST_SCORE = 0.7 + # Adjust scoring formulae def calc_innerscore(affected_decribed, affected_undescribed, total, pereffect_totalaffected, weight=1.0): @@ -109,6 +110,7 @@ def calc_innerscore(affected_decribed, affected_undescribed, total, innerscore = (coverage_total ** 0.23) * coverage_additionalfactor * \ affected_total_factor * weight return innerscore + def calc_outerscore(innerscore_dict, pereffect_totalaffected, weight): """Outer score calculation formula""" # Return just max of the inner scores, including weight factor @@ -118,6 +120,16 @@ def calc_outerscore(innerscore_dict, pereffect_totalaffected, weight): return outerscore else: return 0.0 +def validate_string(s): + try: + s.encode('ascii') + except KeyboardInterrupt: + raise + except Exception: + return False + else: + return True + # Connect to database and set up cursor db = sqlite3.connect(os.path.expanduser(options.database)) cursor = db.cursor() @@ -127,6 +139,8 @@ FORCEPUB_TYPES = ("Ibis", "Impairor", "Velator", "Reaper") OVERRIDES_TYPEPUB = 'UPDATE invtypes SET published = 1 WHERE typeName = ?' for typename in FORCEPUB_TYPES: cursor.execute(OVERRIDES_TYPEPUB, (typename,)) +# Publish t3 Dessy Modes +cursor.execute("UPDATE invtypes SET published = 1 WHERE groupID = ?", (1306,)) # Queries to get raw data QUERY_ALLEFFECTS = 'SELECT effectID, effectName FROM dgmeffects' @@ -965,8 +979,9 @@ inner score: {5:.3})" # Append line for printing to list catname = type[2] typename = type[1] - printstr = "# {0}: {1}" - printing_typelines.append(printstr.format(catname, typename)) + printstr = "# {0}: {1}".format(catname, typename) + if validate_string(printstr): + printing_typelines.append(printstr) # Do the same for groups printing_grouplines = [] printing_groups = sorted(printing_groups, key=lambda tuple: tuple[1]) @@ -976,9 +991,9 @@ inner score: {5:.3})" groupname = group[1] described = len(effectmap_groupid_typeid[group[0]][0]) total = len(globalmap_groupid_typeid[group[0]]) - printstr = "# {0}s from group: {1} ({2} of {3})" - printing_grouplines.append(printstr.format(catname, groupname, - described, total)) + printstr = "# {0}s from group: {1} ({2} of {3})".format(catname, groupname, described, total) + if validate_string(printstr): + printing_grouplines.append(printstr) # Process categories printing_categorylines = [] printing_categories = sorted(printing_categories, @@ -987,9 +1002,9 @@ inner score: {5:.3})" catname = category[1] described = len(effectmap_categoryid_typeid[category[0]][0]) total = len(globalmap_categoryid_typeid[category[0]]) - printstr = "# Items from category: {0} ({1} of {2})" - printing_categorylines.append(printstr.format(catname, described, - total)) + printstr = "# Items from category: {0} ({1} of {2})".format(catname, described, total) + if validate_string(printstr): + printing_categorylines.append(printstr) # Process variations printing_basetypelines = [] printing_basetypes = sorted(printing_basetypes, @@ -1001,9 +1016,9 @@ inner score: {5:.3})" basename = basetype[1] described = len(effectmap_basetypeid_typeid[basetype[0]][0]) total = len(globalmap_basetypeid_typeid[basetype[0]]) - printstr = "# Variations of {0}: {1} ({2} of {3})" - printing_basetypelines.append(printstr.format(catname, basename, - described, total)) + printstr = "# Variations of {0}: {1} ({2} of {3})".format(catname, basename, described, total) + if validate_string(printstr): + printing_basetypelines.append(printstr) # Process market groups with variations printing_marketgroupwithvarslines = [] printing_marketgroupswithvars = sorted(printing_marketgroupswithvars, @@ -1014,9 +1029,9 @@ inner score: {5:.3})" [marketgroup[0]][0]) total = len(globalmap_marketgroupid_typeidwithvariations [marketgroup[0]]) - printstr = "# Items from market group: {0} ({1} of {2})" - printing_marketgroupwithvarslines.append(printstr. - format(marketgroupname, described, total)) + printstr = "# Items from market group: {0} ({1} of {2})".format(marketgroupname, described, total) + if validate_string(printstr): + printing_marketgroupwithvarslines.append(printstr) # Process type name combinations printing_typenamecombtuplelines = [] printing_typenamecombtuples = sorted(printing_typenamecombtuples, @@ -1029,9 +1044,9 @@ inner score: {5:.3})" described = len(effectmap_typenamecombtuple_typeid [typenamecomb[0]][0]) total = len(globalmap_typenamecombtuple_typeid[typenamecomb[0]]) - printstr = "# {0}s named like: {1} ({2} of {3})" - printing_typenamecombtuplelines.append(printstr.format(catname, - namedlike, described, total)) + printstr = "# {0}s named like: {1} ({2} of {3})".format(catname, namedlike, described, total) + if validate_string(printstr): + printing_typenamecombtuplelines.append(printstr) # Compose single list of lines using custom sorting commentlines = printing_categorylines + printing_grouplines + \ diff --git a/scripts/jsonToSql.py b/scripts/jsonToSql.py index eb1678fd2..23f037db6 100755 --- a/scripts/jsonToSql.py +++ b/scripts/jsonToSql.py @@ -131,6 +131,21 @@ def main(db, json_path): newData.append(newRow) return newData + def convertTypes(typesData): + """ + Add factionID column to invtypes table. + """ + factionMap = {} + with open(os.path.join(jsonPath, "fsdTypeOverrides.json")) as f: + overridesData = json.load(f) + for typeID, typeData in overridesData.items(): + factionID = typeData.get("factionID") + if factionID is not None: + factionMap[int(typeID)] = factionID + for row in typesData: + row['factionID'] = factionMap.get(int(row['typeID'])) + return typesData + data = {} # Dump all data to memory so we can easely cross check ignored rows @@ -141,27 +156,21 @@ def main(db, json_path): tableData = convertIcons(tableData) if jsonName == "phbtraits": tableData = convertTraits(tableData) + if jsonName == "invtypes": + tableData = convertTypes(tableData) data[jsonName] = tableData - # 1306 - group Ship Modifiers, for items like tactical t3 ship modes - - # Do some preprocessing to make our job easier + # Set with typeIDs which we will have in our database invTypes = set() for row in data["invtypes"]: + # 1306 - group Ship Modifiers, for items like tactical t3 ship modes if (row["published"] or row['groupID'] == 1306): invTypes.add(row["typeID"]) # ignore checker def isIgnored(file, row): - if file == "invtypes" and not (row["published"] or row['groupID'] == 1306): + if file in ("invtypes", "dgmtypeeffects", "dgmtypeattribs", "invmetatypes") and row['typeID'] not in invTypes: return True - elif file == "dgmtypeeffects" and not row["typeID"] in invTypes: - return True - elif file == "dgmtypeattribs" and not row["typeID"] in invTypes: - return True - elif file == "invmetatypes" and not row["typeID"] in invTypes: - return True - return False # Loop through each json file and write it away, checking ignored rows diff --git a/scripts/prep_data.py b/scripts/prep_data.py index e13415a74..df98489d9 100644 --- a/scripts/prep_data.py +++ b/scripts/prep_data.py @@ -65,7 +65,7 @@ if not args.nojson: list = "dgmexpressions,dgmattribs,dgmeffects,dgmtypeattribs,dgmtypeeffects,"\ "dgmunits,icons,invcategories,invgroups,invmetagroups,invmetatypes,"\ - "invtypes,mapbulk_marketGroups,phbmetadata,phbtraits" + "invtypes,mapbulk_marketGroups,phbmetadata,phbtraits,fsdTypeOverrides" FlowManager(miners, writers).run(list, "multi") @@ -91,4 +91,4 @@ sys.stdout = open(diff_file, 'w') itemDiff.main(old=old_db, new=db_file) sys.stdout = old_stdout -print "\nAll done." \ No newline at end of file +print "\nAll done." diff --git a/service/character.py b/service/character.py index e78ec990d..af4b43106 100644 --- a/service/character.py +++ b/service/character.py @@ -44,12 +44,14 @@ class CharacterImportThread(threading.Thread): paths = self.paths sCharacter = Character.getInstance() for path in paths: - with open(path, mode='r') as charFile: - sheet = service.ParseXML(charFile) - charID = sCharacter.new() - sCharacter.rename(charID, sheet.name+" (imported)") - sCharacter.apiUpdateCharSheet(charID, sheet) - + try: + with open(path, mode='r') as charFile: + sheet = service.ParseXML(charFile) + charID = sCharacter.new() + sCharacter.rename(charID, sheet.name+" (imported)") + sCharacter.apiUpdateCharSheet(charID, sheet) + except: + continue wx.CallAfter(self.callback) class SkillBackupThread(threading.Thread): diff --git a/service/conversions/releaseProteus.py b/service/conversions/releaseProteus.py new file mode 100644 index 000000000..9a964cb94 --- /dev/null +++ b/service/conversions/releaseProteus.py @@ -0,0 +1,104 @@ +""" +Conversion pack for Proteus Module Tiericide +""" + +CONVERSIONS = { + # Renamed items + "Basic Capacitor Recharger": "'Basic' Capacitor Recharger", + "Basic Capacitor Power Relay": "'Basic' Capacitor Power Relay", + "Basic Overdrive Injector System": "'Basic' Overdrive Injector System", + "Basic Reinforced Bulkheads": "'Basic' Reinforced Bulkheads", + "Basic Nanofiber Internal Structure": "'Basic' Nanofiber Internal Structure", + "Basic Expanded Cargohold": "'Basic' Expanded Cargohold", + "Basic Inertia Stabilizers": "'Basic' Inertial Stabilizers", + "Inertia Stabilizers I": "Inertial Stabilizers I", + "Inertia Stabilizers II": "Inertial Stabilizers II", + "Basic Power Diagnostic System": "'Basic' Power Diagnostic System", + "Eutectic Capacitor Charge Array": "Eutectic Compact Cap Recharger", + "Cu Vapor Particle Bore Stream I": "Particle Bore Compact Mining Laser", + "Type-D Altered SS Expanded Cargo": "Type-D Restrained Expanded Cargo", + "Type-D Altered SS Inertial Stabilizers": "Type-D Restrained Inertial Stabilizers", + "Type-D Altered SS Nanofiber Structure": "Type-D Restrained Nanofiber Structure", + "Type-D Altered SS Overdrive Injector": "Type-D Restrained Overdrive Injector", + "Type-D Altered SS Reinforced Bulkheads": "Type-D Restrained Reinforced Bulkheads", + "Mark I Modified SS Reinforced Bulkheads": "Mark I Compact Reinforced Bulkheads", + "Type-D Power Core Modification: Capacitor Power Relay": "Type-D Restrained Capacitor Power Relay", + "Mark I Generator Refitting: Capacitor Power Relay": "Mark I Compact Capacitor Power Relay", + "Mark I Generator Refitting: Diagnostic System": "Mark I Compact Power Diagnostic System", + "Synthetic Hull Conversion Overdrive Injector I": "Synthetic Hull Conversion Overdrive Injector", + "Synthetic Hull Conversion Reinforced Bulkheads I": "Synthetic Hull Conversion Reinforced Bulkheads", + "Synthetic Hull Conversion Inertia Stabilizers I": "Synthetic Hull Conversion Inertia Stabilizers", + "Synthetic Hull Conversion Nanofiber Structure I": "Synthetic Hull Conversion Nanofiber Structure", + "Elara Mining Laser Upgrade": "Elara Restrained Mining Laser Upgrade", + "Carpo Mining Laser Upgrade": "'Carpo' Mining Laser Upgrade", + "Aoede Mining Laser Upgrade": "'Aoede' Mining Laser Upgrade", + "Frigoris Ice Harvester Upgrade": "Frigoris Restrained Ice Harvester Upgrade", + "Anguis Ice Harvester Upgrade": "'Anguis' Ice Harvester Upgrade", + "Ingenii Ice Harvester Upgrade": "'Ingenii' Ice Harvester Upgrade", + "Limited Expanded 'Archiver' Cargo I": "Limited Expanded 'Archiver' Cargo", + "EP-S Gaussian Excavation Pulse": "EP-S Gaussian Scoped Mining Laser", + + # Converted items + "Alpha Reactor Control: Diagnostic System": "'Basic' Power Diagnostic System", + "Marked Generator Refitting: Diagnostic System": "'Basic' Power Diagnostic System", + "Partial Power Plant Manager: Diagnostic System": "'Basic' Power Diagnostic System", + "Type-E Power Core Modification: Diagnostic System": "'Basic' Power Diagnostic System", + "Type-E Power Core Modification: Reaction Control": "'Basic' Power Diagnostic System", + "Local Hull Conversion Reinforced Bulkheads I": "Type-D Restrained Reinforced Bulkheads", + "Beta Hull Mod Reinforced Bulkheads": "Mark I Compact Reinforced Bulkheads", + "Beta Hull Mod Inertial Stabilizers": "Type-D Restrained Inertial Stabilizers", + "Local Hull Conversion Inertial Stabilizers I": "Type-D Restrained Inertial Stabilizers", + "Mark I Modified SS Inertial Stabilizers": "Type-D Restrained Inertial Stabilizers", + "Marked Modified SS Inertial Stabilizers": "Type-D Restrained Inertial Stabilizers", + "Beta Reactor Control: Diagnostic System I": "Mark I Compact Power Diagnostic System", + "Local Power Plant Manager: Diagnostic System I": "Mark I Compact Power Diagnostic System", + "Type-D Power Core Modification: Diagnostic System": "Mark I Compact Power Diagnostic System", + "Alpha Hull Mod Expanded Cargo": "'Basic' Expanded Cargohold", + "Marked Modified SS Expanded Cargo": "'Basic' Expanded Cargohold", + "Partial Hull Conversion Expanded Cargo": "'Basic' Expanded Cargohold", + "Type-E Altered SS Expanded Cargo": "'Basic' Expanded Cargohold", + "AGM Capacitor Charge Array": "'Basic' Capacitor Recharger", + "F-a10 Buffer Capacitor Regenerator": "'Basic' Capacitor Recharger", + "Industrial Capacitor Recharger": "'Basic' Capacitor Recharger", + "Secondary Parallel Link-Capacitor": "'Basic' Capacitor Recharger", + "Beta Reactor Control: Capacitor Power Relay I": "Mark I Compact Capacitor Power Relay", + "Alpha Hull Mod Overdrive Injector": "'Basic' Overdrive Injector System", + "Marked Modified SS Overdrive Injector": "'Basic' Overdrive Injector System", + "Partial Hull Conversion Overdrive Injector": "'Basic' Overdrive Injector System", + "Type-E Altered SS Overdrive Injector": "'Basic' Overdrive Injector System", + "Erin Mining Laser Upgrade": "Elara Restrained Mining Laser Upgrade", + "Alpha Hull Mod Reinforced Bulkheads": "'Basic' Reinforced Bulkheads", + "Marked Modified SS Reinforced Bulkheads": "'Basic' Reinforced Bulkheads", + "Partial Hull Conversion Reinforced Bulkheads": "'Basic' Reinforced Bulkheads", + "Type-E Altered SS Reinforced Bulkheads": "'Basic' Reinforced Bulkheads", + "Alpha Hull Mod Nanofiber Structure": "'Basic' Nanofiber Internal Structure", + "Marked Modified SS Nanofiber Structure": "'Basic' Nanofiber Internal Structure", + "Partial Hull Conversion Nanofiber Structure": "'Basic' Nanofiber Internal Structure", + "Type-E Altered SS Nanofiber Structure": "'Basic' Nanofiber Internal Structure", + "Crisium Ice Harvester Upgrade": "Frigoris Restrained Ice Harvester Upgrade", + "Beta Hull Mod Nanofiber Structure": "Type-D Restrained Nanofiber Structure", + "Local Hull Conversion Nanofiber Structure I": "Type-D Restrained Nanofiber Structure", + "Mark I Modified SS Nanofiber Structure": "Type-D Restrained Nanofiber Structure", + "Local Power Plant Manager: Capacity Power Relay I": "Type-D Restrained Capacitor Power Relay", + "EP-R Argon Ion Basic Excavation Pulse": "Single Diode Basic Mining Laser", + "Rubin Basic Particle Bore Stream": "Single Diode Basic Mining Laser", + "Xenon Basic Drilling Beam": "Single Diode Basic Mining Laser", + "Barton Reactor Capacitor Recharger I": "Eutectic Compact Cap Recharger", + "F-b10 Nominal Capacitor Regenerator": "Eutectic Compact Cap Recharger", + "Fixed Parallel Link-Capacitor I": "Eutectic Compact Cap Recharger", + "Beta Hull Mod Expanded Cargo": "Type-D Restrained Expanded Cargo", + "Local Hull Conversion Expanded Cargo I": "Type-D Restrained Expanded Cargo", + "Mark I Modified SS Expanded Cargo": "Type-D Restrained Expanded Cargo", + "Dual Diode Mining Laser I": "EP-S Gaussian Scoped Mining Laser", + "Alpha Hull Mod Inertial Stabilizers": "'Basic' Inertial Stabilizers", + "Partial Hull Conversion Inertial Stabilizers": "'Basic' Inertial Stabilizers", + "Type-E Altered SS Inertial Stabilizers": "'Basic' Inertial Stabilizers", + "Alpha Reactor Control: Capacitor Power Relay": "'Basic' Capacitor Power Relay", + "Marked Generator Refitting: Capacitor Power Relay": "'Basic' Capacitor Power Relay", + "Partial Power Plant Manager: Capacity Power Relay": "'Basic' Capacitor Power Relay", + "Type-E Power Core Modification: Capacitor Power Relay": "'Basic' Capacitor Power Relay", + "XeCl Drilling Beam I": "Particle Bore Compact Mining Laser", + "Beta Hull Mod Overdrive Injector": "Type-D Restrained Overdrive Injector", + "Local Hull Conversion Overdrive Injector I": "Type-D Restrained Overdrive Injector", + "Mark I Modified SS Overdrive Injector": "Type-D Restrained Overdrive Injector", +} diff --git a/service/conversions/skinnedShips.py b/service/conversions/skinnedShips.py index 2b170dcc7..d2e0a8d85 100644 --- a/service/conversions/skinnedShips.py +++ b/service/conversions/skinnedShips.py @@ -21,20 +21,24 @@ CONVERSIONS = { "Coercer Blood Raiders Edition": "Coercer", "Cormorant Guristas Edition": "Cormorant", "Cyclone Thukker Tribe Edition": "Cyclone", + "Dominix Quafe Edition": "Dominix", "Ferox Guristas Edition": "Ferox", "Golem Guristas Edition": "Golem", "Golem Kaalakiota Edition": "Golem", "Golem Nugoeihuvi Edition": "Golem", "Hyperion Aliastra Edition": "Hyperion", "Hyperion Innerzone Shipping Edition": "Hyperion", + "Hyperion Inner Zone Shipping Edition": "Hyperion", "Incursus Aliastra Edition": "Incursus", "Incursus Innerzone Shipping Edition": "Incursus", + "Incursus Inner Zone Shipping Edition": "Incursus", "Inner Zone Shipping Catalyst": "Catalyst", "Inner Zone Shipping Imicus": "Imicus", "Intaki Syndicate Catalyst": "Catalyst", "InterBus Catalyst": "Catalyst", "Iteron Inner Zone Shipping Edition": "Iteron Mark V", "Kronos Innerzone Shipping Edition": "Kronos", + "Kronos Inner Zone Shipping Edition": "Kronos", "Kronos Police Edition": "Kronos", "Kronos Quafe Edition": "Kronos", "Mackinaw ORE Development Edition": "Mackinaw", @@ -42,6 +46,7 @@ CONVERSIONS = { "Maelstrom Nefantar Edition": "Maelstrom", "Mammoth Nefantar Edition": "Mammoth", "Megathron Innerzone Shipping Edition": "Megathron", + "Megathron Inner Zone Shipping Edition": "Megathron", "Megathron Police Edition": "Megathron", "Megathron Quafe Edition": "Megathron", "Merlin Nugoeihuvi Edition": "Merlin", @@ -84,9 +89,12 @@ CONVERSIONS = { "Tempest Nefantar Edition": "Tempest", "Thorax Aliastra Edition": "Thorax", "Thorax Innerzone Shipping Edition": "Thorax", + "Thorax Inner Zone Shipping Edition": "Thorax", "Thrasher Thukker Tribe Edition": "Thrasher", + "Tristan Quafe Edition": "Tristan", "Vargur Justice Edition": "Vargur", "Vargur Krusual Edition": "Vargur", "Vargur Nefantar Edition": "Vargur", + "Vexor Quafe Edition": "Vexor", "Vherokior Probe": "Probe" } diff --git a/service/fit.py b/service/fit.py index 52c0e48cb..fac02bb5c 100644 --- a/service/fit.py +++ b/service/fit.py @@ -17,7 +17,6 @@ # along with pyfa. If not, see . #=============================================================================== -import os.path import locale import copy import threading @@ -47,11 +46,13 @@ class FitBackupThread(threading.Thread): path = self.path sFit = Fit.getInstance() allFits = map(lambda x: x[0], sFit.getAllFits()) - backedUpFits = sFit.exportXml(*allFits) + backedUpFits = sFit.exportXml(self.callback, *allFits) backupFile = open(path, "w", encoding="utf-8") backupFile.write(backedUpFits) backupFile.close() - wx.CallAfter(self.callback) + + # Send done signal to GUI + wx.CallAfter(self.callback, -1) class FitImportThread(threading.Thread): @@ -61,14 +62,11 @@ class FitImportThread(threading.Thread): self.callback = callback def run(self): - importedFits = [] - paths = self.paths sFit = Fit.getInstance() - for path in paths: - pathImported = sFit.importFit(path) - if pathImported is not None: - importedFits += pathImported - wx.CallAfter(self.callback, importedFits) + fits = sFit.importFitFromFiles(self.paths, self.callback) + + # Send done signal to GUI + wx.CallAfter(self.callback, -1, fits) class Fit(object): @@ -96,7 +94,8 @@ class Fit(object): "colorFitBySlot": False, "rackSlots": True, "rackLabels": True, - "compactSkills": True} + "compactSkills": True, + "showTooltip": True} self.serviceFittingOptions = SettingsProvider.getInstance().getSettings( "pyfaServiceFittingOptions", serviceFittingDefaultOptions) @@ -127,6 +126,9 @@ class Fit(object): return names + def countAllFits(self): + return eos.db.countAllFits() + def countFitsWithShip(self, shipID): count = eos.db.countFitsWithShip(shipID) return count @@ -147,7 +149,6 @@ class Fit(object): def newFit(self, shipID, name=None): fit = eos.types.Fit() fit.ship = eos.types.Ship(eos.db.getItem(shipID)) - fit.mode = fit.ship.checkModeItem(None) fit.name = name if name is not None else "New %s" % fit.ship.item.name fit.damagePattern = self.pattern fit.targetResists = self.targetResists @@ -742,7 +743,7 @@ class Fit(object): fit = eos.db.getFit(fitID) for attr in ("em", "thermal", "kinetic", "explosive"): - setattr(dp, "%sAmount" % attr, ammo.getAttribute("%sDamage" % attr)) + setattr(dp, "%sAmount" % attr, ammo.getAttribute("%sDamage" % attr) or 0) fit.damagePattern = dp self.recalc(fit) @@ -759,9 +760,9 @@ class Fit(object): fit = eos.db.getFit(fitID) return Port.exportDna(fit) - def exportXml(self, *fitIDs): + def exportXml(self, callback=None, *fitIDs): fits = map(lambda fitID: eos.db.getFit(fitID), fitIDs) - return Port.exportXml(*fits) + return Port.exportXml(callback, *fits) def backupFits(self, path, callback): thread = FitBackupThread(path, callback) @@ -771,26 +772,50 @@ class Fit(object): thread = FitImportThread(paths, callback) thread.start() - def importFit(self, path): - filename = os.path.split(path)[1] + def importFitFromFiles(self, paths, callback=None): + """ + Imports fits from file(s). First processes all provided paths and stores + assembled fits into a list. This allows us to call back to the GUI as + fits are processed as well as when fits are being saved. + returns + """ defcodepage = locale.getpreferredencoding() - file = open(path, "r") - srcString = file.read() - # If file had ANSI encoding, convert it to unicode using system - # default codepage, or use fallback cp1252 on any encoding errors - if isinstance(srcString, str): - try: - srcString = unicode(srcString, defcodepage) - except UnicodeDecodeError: - srcString = unicode(srcString, "cp1252") + fits = [] + for path in paths: + if callback: # Pulse + wx.CallAfter(callback, "Processing file:\n%s"%path) - _, fits = Port.importAuto(srcString, filename) - for fit in fits: + file = open(path, "r") + srcString = file.read() + # If file had ANSI encoding, convert it to unicode using system + # default codepage, or use fallback cp1252 on any encoding errors + if isinstance(srcString, str): + try: + srcString = unicode(srcString, defcodepage) + except UnicodeDecodeError: + srcString = unicode(srcString, "cp1252") + + _, fitsImport = Port.importAuto(srcString, path, callback=callback) + fits += fitsImport + + IDs = [] + numFits = len(fits) + for i, fit in enumerate(fits): + # Set some more fit attributes and save fit.character = self.character fit.damagePattern = self.pattern fit.targetResists = self.targetResists + eos.db.save(fit) + IDs.append(fit.ID) + if callback: # Pulse + wx.CallAfter( + callback, + "Processing complete, saving fits to database\n(%d/%d)" % + (i+1, numFits) + ) + return fits def importFitFromBuffer(self, bufferStr, activeFit=None): @@ -799,15 +824,8 @@ class Fit(object): fit.character = self.character fit.damagePattern = self.pattern fit.targetResists = self.targetResists - return fits - - def saveImportedFits(self, fits): - IDs = [] - for fit in fits: eos.db.save(fit) - IDs.append(fit.ID) - - return IDs + return fits def checkStates(self, fit, base): changed = False diff --git a/service/market.py b/service/market.py index fdde3c598..c3c463494 100644 --- a/service/market.py +++ b/service/market.py @@ -23,6 +23,7 @@ import wx import Queue +import config import eos.db import eos.types from service.settings import SettingsProvider, NetworkSettings @@ -214,13 +215,34 @@ class Market(): "Goru's Shuttle": False, "Guristas Shuttle": False, "Mobile Decoy Unit": False, # Seems to be left over test mod for deployables - "Tournament Micro Jump Unit": False} # Normally seen only on tournament arenas - + "Tournament Micro Jump Unit": False, # Normally seen only on tournament arenas + u"\u4e07\u738b\u5b9d\u5ea7\u7ea7YC117\u5e74\u7279\u522b\u7248": False, # Proteus added shitton of chinese-specific ships + u"\u4e4c\u9e26\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u54cd\u5c3e\u86c7\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u5730\u72f1\u5929\u4f7f\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u591a\u7c73\u5c3c\u514b\u65af\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u672b\u65e5\u6c99\u573a\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u707e\u96be\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u9a6c\u514b\u745e\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u5e7c\u9f99\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u6bd2\u8725\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u4f0a\u4ec0\u5854\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + u"\u94f6\u9e70\u7ea7YC117\u5e74\u7279\u522b\u7248": False, + } # do not publish ships that we convert for name in conversions.packs['skinnedShips']: self.ITEMS_FORCEPUBLISHED[name] = False + if config.debug: + # Publish Tactical Dessy Modes if in debug + # Cannot use GROUPS_FORCEPUBLISHED as this does not force items + # within group to be published, but rather for the group itself + # to show up on ship list + group = self.getGroup("Ship Modifiers", eager="items") + for item in group.items: + self.ITEMS_FORCEPUBLISHED[item.name] = True + # List of groups which are forcibly published self.GROUPS_FORCEPUBLISHED = { "Prototype Exploration Ship": False } # We moved the only ship from this group to other group anyway diff --git a/service/port.py b/service/port.py index 7303d09af..e3bfe9dd7 100644 --- a/service/port.py +++ b/service/port.py @@ -18,60 +18,39 @@ #=============================================================================== import re +import os import xml.dom -import json from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster import service +import wx try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict -FIT_WIN_HEADINGS = ["High power", "Medium power", "Low power", "Rig Slot", "Sub System", "Charges"] EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM] - class Port(object): """Service which houses all import/export format functions""" @classmethod - def importAuto(cls, string, sourceFileName=None, activeFit=None): + def importAuto(cls, string, path=None, activeFit=None, callback=None): # Get first line and strip space symbols of it to avoid possible detection errors firstLine = re.split("[\n\r]+", string.strip(), maxsplit=1)[0] firstLine = firstLine.strip() - # If string is from in-game copy of fitting window - # We match " power" instead of "High power" in case a fit has no high modules - if firstLine in FIT_WIN_HEADINGS and activeFit is not None: - return "FIT", (cls.importFittingWindow(string, activeFit),) - - # If we have ".*", firstLine) - if chatDna: - return "DNA", (cls.importDna(chatDna.group(1)),) - - # If we have a CREST kill link - killLink = re.search("http://public-crest.eveonline.com/killmails/(.*)/", firstLine) - if killLink: - return "CREST", (cls.importCrest(tuple(killLink.group(1).split("/"))),) - - # If we have ".*", firstLine) - if killReport: - return "CREST", (cls.importCrest(tuple(killReport.group(1).split(":"))),) - # If XML-style start of tag encountered, detect as XML if re.match("<", firstLine): - return "XML", cls.importXml(string) + return "XML", cls.importXml(string, callback) # If we've got source file name which is used to describe ship name # and first line contains something like [setup name], detect as eft config file - if re.match("\[.*\]", firstLine) and sourceFileName is not None: - shipName = sourceFileName.rsplit('.')[0] - return "EFT Config", cls.importEftCfg(shipName, string) + if re.match("\[.*\]", firstLine) and path is not None: + filename = os.path.split(path)[1] + shipName = filename.rsplit('.')[0] + return "EFT Config", cls.importEftCfg(shipName, string, callback) # If no file is specified and there's comma between brackets, # consider that we have [ship, setup name] and detect like eft export format @@ -81,138 +60,6 @@ class Port(object): # Use DNA format for all other cases return "DNA", (cls.importDna(string),) - @staticmethod - def importFittingWindow(string, activeFit): - sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() - - activeFit = sFit.getFit(activeFit) - - # if the current fit has mods, do not mess with it. Instead, make new fit - if activeFit.modCount > 0: - fit = Fit() - fit.ship = Ship(sMkt.getItem(activeFit.ship.item.ID)) - fit.name = "%s (copy)" % activeFit.name - else: - fit = activeFit - lines = re.split('[\n\r]+', string) - - droneMap = {} - cargoMap = {} - modules = [] - - for i in range(1, len(lines)): - line = lines[i].strip() - if not line: - continue - - try: - amount, modName = line.split("x ") - amount = int(amount) - item = sMkt.getItem(modName, eager="group.category") - except: - # if no data can be found (old names) - continue - - if item.category.name == "Drone": - if not modName in droneMap: - droneMap[modName] = 0 - droneMap[modName] += amount - elif item.category.name == "Charge": - if not modName in cargoMap: - cargoMap[modName] = 0 - cargoMap[modName] += amount - else: - for _ in xrange(amount): - try: - m = Module(item) - except ValueError: - continue - # If we are importing T3 ship, we must apply subsystems first, then - # calcModAttr() to get the ship slots - if m.slot == Slot.SUBSYSTEM and m.fits(fit): - fit.modules.append(m) - else: - modules.append(m) - - fit.clear() - fit.calculateModifiedAttributes() - - for m in modules: - # we check to see if module fits as a basic sanity check - # if it doesn't then the imported fit is most likely invalid - # (ie: user tried to import Legion fit to a Rifter) - if m.fits(fit): - fit.modules.append(m) - if m.isValidState(State.ACTIVE): - m.state = State.ACTIVE - m.owner = fit # not sure why this is required when it's not for other import methods, but whatever - else: - return - - for droneName in droneMap: - d = Drone(sMkt.getItem(droneName)) - d.amount = droneMap[droneName] - fit.drones.append(d) - - for cargoName in cargoMap: - c = Cargo(sMkt.getItem(cargoName)) - c.amount = cargoMap[cargoName] - fit.cargo.append(c) - - return fit - - @staticmethod - def importCrest(info): - sMkt = service.Market.getInstance() - network = service.Network.getInstance() - try: - response = network.request("https://public-crest.eveonline.com/killmails/%s/%s/" % info, network.EVE) - except: - return - - kill = (json.loads(response.read()))['victim'] - - fit = Fit() - fit.ship = Ship(sMkt.getItem(kill['shipType']['name'])) - fit.name = "CREST: %s's %s" % (kill['character']['name'], kill['shipType']['name']) - - # sort based on flag to get proper rack position - items = sorted(kill['items'], key=lambda k: k['flag']) - - # We create a relation between module flag and module position on fit at time of append: - # this allows us to know which module to apply charges to if need be (see below) - flagMap = {} - - # Charges may show up before or after the module. We process modules first, - # storing any charges that are fitted in a dict and noting their flag (module). - charges = {} - - for mod in items: - if mod['flag'] == 5: # throw out cargo - continue - - item = sMkt.getItem(mod['itemType']['name'], eager="group.category") - - if item.category.name == "Drone": - d = Drone(item) - d.amount = mod['quantityDropped'] if 'quantityDropped' in mod else mod['quantityDestroyed'] - fit.drones.append(d) - elif item.category.name == "Charge": - charges[mod['flag']] = item - else: - m = Module(item) - if m.isValidState(State.ACTIVE): - m.state = State.ACTIVE - fit.modules.append(m) - flagMap[mod['flag']] = fit.modules.index(m) - - for flag, item in charges.items(): - # we do not need to verify valid charge as it comes directly from CCP - fit.modules[flagMap[flag]].charge = item - - return fit - @staticmethod def importDna(string): sMkt = service.Market.getInstance() @@ -355,7 +202,7 @@ class Port(object): return fit @staticmethod - def importEftCfg(shipname, contents): + def importEftCfg(shipname, contents, callback=None): """Handle import from EFT config store file""" # Check if we have such ship in database, bail if we don't @@ -363,7 +210,7 @@ class Port(object): try: sMkt.getItem(shipname) except: - return + return [] # empty list is expected # If client didn't take care of encoding file contents into Unicode, # do it using fallback encoding ourselves @@ -501,6 +348,9 @@ class Port(object): f.modules.append(m) # Append fit to list of fits fits.append(f) + + if callback: + wx.CallAfter(callback, None) # Skip fit silently if we get an exception except Exception: pass @@ -508,14 +358,15 @@ class Port(object): return fits @staticmethod - def importXml(text): + def importXml(text, callback=None): sMkt = service.Market.getInstance() doc = xml.dom.minidom.parseString(text.encode("utf-8")) fittings = doc.getElementsByTagName("fittings").item(0) fittings = fittings.getElementsByTagName("fitting") fits = [] - for fitting in fittings: + + for i, fitting in enumerate(fittings): f = Fit() f.name = fitting.getAttribute("name") # Maelstrom @@ -557,6 +408,8 @@ class Port(object): except KeyboardInterrupt: continue fits.append(f) + if callback: + wx.CallAfter(callback, None) return fits @@ -658,69 +511,76 @@ class Port(object): return dna + "::" @classmethod - def exportXml(cls, *fits): + def exportXml(cls, callback=None, *fits): doc = xml.dom.minidom.Document() fittings = doc.createElement("fittings") doc.appendChild(fittings) - for fit in fits: - fitting = doc.createElement("fitting") - fitting.setAttribute("name", fit.name) - fittings.appendChild(fitting) - description = doc.createElement("description") - description.setAttribute("value", "") - fitting.appendChild(description) - shipType = doc.createElement("shipType") - shipType.setAttribute("value", fit.ship.item.name) - fitting.appendChild(shipType) + for i, fit in enumerate(fits): + try: + fitting = doc.createElement("fitting") + fitting.setAttribute("name", fit.name) + fittings.appendChild(fitting) + description = doc.createElement("description") + description.setAttribute("value", "") + fitting.appendChild(description) + shipType = doc.createElement("shipType") + shipType.setAttribute("value", fit.ship.item.name) + fitting.appendChild(shipType) - charges = {} - slotNum = {} - for module in fit.modules: - if module.isEmpty: - continue + charges = {} + slotNum = {} + for module in fit.modules: + if module.isEmpty: + continue - slot = module.slot + slot = module.slot - if slot == Slot.SUBSYSTEM: - # Order of subsystem matters based on this attr. See GH issue #130 - slotId = module.getModifiedItemAttr("subSystemSlot") - 124 - else: - if not slot in slotNum: - slotNum[slot] = 0 + if slot == Slot.SUBSYSTEM: + # Order of subsystem matters based on this attr. See GH issue #130 + slotId = module.getModifiedItemAttr("subSystemSlot") - 124 + else: + if not slot in slotNum: + slotNum[slot] = 0 - slotId = slotNum[slot] - slotNum[slot] += 1 + slotId = slotNum[slot] + slotNum[slot] += 1 - hardware = doc.createElement("hardware") - hardware.setAttribute("type", module.item.name) - slotName = Slot.getName(slot).lower() - slotName = slotName if slotName != "high" else "hi" - hardware.setAttribute("slot", "%s slot %d" % (slotName, slotId)) - fitting.appendChild(hardware) + hardware = doc.createElement("hardware") + hardware.setAttribute("type", module.item.name) + slotName = Slot.getName(slot).lower() + slotName = slotName if slotName != "high" else "hi" + hardware.setAttribute("slot", "%s slot %d" % (slotName, slotId)) + fitting.appendChild(hardware) - if module.charge: - if not module.charge.name in charges: - charges[module.charge.name] = 0 - # `or 1` because some charges (ie scripts) are without qty - charges[module.charge.name] += module.numCharges or 1 + if module.charge: + if not module.charge.name in charges: + charges[module.charge.name] = 0 + # `or 1` because some charges (ie scripts) are without qty + charges[module.charge.name] += module.numCharges or 1 - for drone in fit.drones: - hardware = doc.createElement("hardware") - hardware.setAttribute("qty", "%d" % drone.amount) - hardware.setAttribute("slot", "drone bay") - hardware.setAttribute("type", drone.item.name) - fitting.appendChild(hardware) + for drone in fit.drones: + hardware = doc.createElement("hardware") + hardware.setAttribute("qty", "%d" % drone.amount) + hardware.setAttribute("slot", "drone bay") + hardware.setAttribute("type", drone.item.name) + fitting.appendChild(hardware) - for cargo in fit.cargo: - if not cargo.item.name in charges: - charges[cargo.item.name] = 0 - charges[cargo.item.name] += cargo.amount + for cargo in fit.cargo: + if not cargo.item.name in charges: + charges[cargo.item.name] = 0 + charges[cargo.item.name] += cargo.amount - for name, qty in charges.items(): - hardware = doc.createElement("hardware") - hardware.setAttribute("qty", "%d" % qty) - hardware.setAttribute("slot", "cargo") - hardware.setAttribute("type", name) - fitting.appendChild(hardware) + for name, qty in charges.items(): + hardware = doc.createElement("hardware") + hardware.setAttribute("qty", "%d" % qty) + hardware.setAttribute("slot", "cargo") + hardware.setAttribute("type", name) + fitting.appendChild(hardware) + except: + print "Failed on fitID: %d"%fit.ID + continue + finally: + if callback: + wx.CallAfter(callback, i) return doc.toprettyxml() diff --git a/service/price.py b/service/price.py index fafdf3297..aefd1b14f 100644 --- a/service/price.py +++ b/service/price.py @@ -63,7 +63,7 @@ class Price(): data = [] # Base request URL - baseurl = "https://api.eve-central.com/api/marketstat" + baseurl = "https://eve-central.com/api/marketstat" data.append(("usesystem", 30000142)) # Use Jita for market for typeID in toRequest: # Add all typeID arguments diff --git a/staticdata/eve.db b/staticdata/eve.db index 0fc75bf9b..b29208ba5 100644 Binary files a/staticdata/eve.db and b/staticdata/eve.db differ