Merge branch 'master' into singularity

This commit is contained in:
blitzmann
2016-03-30 18:48:17 -04:00
103 changed files with 218 additions and 241 deletions

View File

@@ -9,31 +9,31 @@
pyfa, short for **py**thon **f**itting **a**ssistant, allows you to create, experiment with, and save ship fittings without being in game. Open source and written in Python, it is available on any platform where Python 2.x and wxWidgets are available, including Windows, Mac OS X, and Linux.
## Latest Version and Changelogs
The latest version along with release notes can always be found on the projects [Releases](https://github.com/DarkFenX/Pyfa/releases) page. pyfa will notify you if you are running an outdated version.
The latest version along with release notes can always be found on the project's [Releases](https://github.com/DarkFenX/Pyfa/releases) page. pyfa will notify you if you are running an outdated version.
## Installing
Windows and OS X users are supplied self-contained builds of pyfa that can be run without additional software. An `.exe` installer is also available for the Windows builds. There is no self-contained package for Linux users, which are expected to run pyfa through their distributions Python interpreter. However, there are a number of third-party packages available that handle the dependencies and updates for pyfa (for example, [pyfa for Arch Linux](https://aur.archlinux.org/packages/pyfa/)). Please check your distributions repositories.
## Installation
Windows and OS X users are supplied self-contained builds of pyfa on the [latest releases](https://github.com/pyfa-org/Pyfa/releases/latest) page. An `.exe` installer is also available for Windows builds. Linux users can run pyfa using their distribution's Python interpreter. There is no official self-contained package for Linux, however, there are a number of third-party packages available through distribution-specific repositories.
### Requirements
### Linux Distro-specific Packages
The following is a list of pyfa packages available for certain distributions. Please note that these packages are maintained by third-parties and are not evaluated by the pyfa developers.
* Debian/Ubuntu/derivitives: https://github.com/AdamMajer/Pyfa/releases
* Arch: https://aur.archlinux.org/packages/pyfa/
* openSUSE: https://build.opensuse.org/package/show/home:rmk2/pyfa
* FreeBSD: http://www.freshports.org/games/pyfa/ (see [#484](https://github.com/pyfa-org/Pyfa/issues/484) for instructions)
### Dependencies
If you wish to help with development or simply need to run pyfa through a Python interpreter, the following software is required:
* Python 2.7
* `wxPython` 2.8/3.0
* `sqlalchemy` >= 0.6
* `dateutil`
* `matplotlib` (for some Linux distributions, you may need to install separate wxPython bindings, such as `python-matplotlib-wx`)
* `matplotlib` (for some Linux distributions you may need to install separate wxPython bindings such as `python-matplotlib-wx`)
* `requests`
### Linux Distro-specific Packages
The following is a list of pyfa packages available for certain distros. Please note that these packages are maintained by third-parties and are not evaluated by the pyfa developers.
* Debian/Ubuntu/derivitives: https://github.com/AdamMajer/Pyfa/releases
* Arch: https://aur.archlinux.org/packages/pyfa/
* openSUSE: https://build.opensuse.org/package/show/home:rmk2/pyfa
* FreeBSD: http://www.freshports.org/games/pyfa/ (see #484 for instructions)
## Bug Reporting
The preferred method of reporting bugs is through the projects GitHub Issues interface. Alternatively, posting a report in the pyfa thread on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/Bug-Reporting).
The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/Bug-Reporting).
## License
pyfa is licensed under the GNU GPL v3.0, see LICENSE
@@ -46,12 +46,10 @@ pyfa is licensed under the GNU GPL v3.0, see LICENSE
* [EVE Online website](http://www.eveonline.com/)
## Contacts:
* Kadesh Priestess
* GitHub: @DarkFenX
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @kadesh
* Sable Blitzmann
* GitHub: @blitzmann
* [TweetFleet Slack](https://www.fuzzwork.co.uk/tweetfleet-slack-invites/): @blitzmann
* [Gitter chat](https://gitter.im/pyfa-org/Pyfa): @ blitzmann
* Email: sable.blitzmann@gmail.com
## CCP Copyright Notice

View File

@@ -18,10 +18,10 @@ debug = False
saveInRoot = False
# Version data
version = "1.19.2"
version = "1.20.2"
tag = "git"
expansionName = "February 2016"
expansionVersion = "1.1"
expansionName = "March 2016"
expansionVersion = "1.3"
evemonMinVersion = "4081"
pyfaPath = None
@@ -60,7 +60,7 @@ def __createDirs(path):
if not os.path.exists(path):
os.makedirs(path)
def defPaths():
def defPaths(customSavePath):
global debug
global pyfaPath
global savePath
@@ -87,8 +87,11 @@ def defPaths():
else:
savePath = getattr(configforced, "savePath", None)
if savePath is None:
savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")),
if customSavePath is None: # customSavePath is not overriden
savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")),
sys.getfilesystemencoding())
else:
savePath = customSavePath
__createDirs(savePath)

View File

@@ -131,6 +131,10 @@ class HandledModuleList(HandledList):
self.remove(mod)
return
# fix for #529, where a module may be in incorrect state after CCP changes mechanics of module
if not mod.isValidState(mod.state):
mod.state = eos.types.State.ONLINE
mod.position = len(self)
HandledList.append(self, mod)
if mod.isInvalid:

View File

@@ -1,7 +1,7 @@
# ammoInfluenceCapNeed
#
# Used by:
# Items from category: Charge (458 of 833)
# Items from category: Charge (458 of 851)
type = "passive"
def handler(fit, module, context):
# Dirty hack to work around cap charges setting cap booster

View File

@@ -1,7 +1,7 @@
# ammoInfluenceRange
#
# Used by:
# Items from category: Charge (559 of 833)
# Items from category: Charge (559 of 851)
type = "passive"
def handler(fit, module, context):
module.multiplyItemAttr("maxRange", module.getModifiedChargeAttr("weaponRangeMultiplier"))

View File

@@ -2,6 +2,7 @@
#
# Used by:
# Implants named like: Exile Booster (4 of 4)
# Implant: Antipharmakon Kosybo
type = "passive"
def handler(fit, booster, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems") or mod.item.requiresSkill("Capital Repair Systems"),

View File

@@ -1,7 +1,7 @@
# boosterArmorHpPenalty
#
# Used by:
# Implants from group: Booster (12 of 37)
# Implants from group: Booster (12 of 42)
type = "boosterSideEffect"
def handler(fit, booster, context):
fit.ship.boostItemAttr("armorHP", booster.getModifiedItemAttr("boosterArmorHPPenalty"))

View File

@@ -1,7 +1,7 @@
# boosterArmorRepairAmountPenalty
#
# Used by:
# Implants from group: Booster (9 of 37)
# Implants from group: Booster (9 of 42)
type = "boosterSideEffect"
def handler(fit, booster, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Armor Repair Unit",

View File

@@ -1,7 +1,7 @@
# boosterMaxVelocityPenalty
#
# Used by:
# Implants from group: Booster (12 of 37)
# Implants from group: Booster (12 of 42)
type = "boosterSideEffect"
def handler(fit, booster, context):
fit.ship.boostItemAttr("maxVelocity", booster.getModifiedItemAttr("boosterMaxVelocityPenalty"))

View File

@@ -1,7 +1,7 @@
# boosterShieldCapacityPenalty
#
# Used by:
# Implants from group: Booster (12 of 37)
# Implants from group: Booster (12 of 42)
type = "boosterSideEffect"
def handler(fit, booster, context):
fit.ship.boostItemAttr("shieldCapacity", booster.getModifiedItemAttr("boosterShieldCapacityPenalty"))

View File

@@ -1,7 +1,7 @@
# boosterTurretOptimalRangePenalty
#
# Used by:
# Implants from group: Booster (9 of 37)
# Implants from group: Booster (9 of 42)
type = "boosterSideEffect"
def handler(fit, booster, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),

View File

@@ -1,7 +1,7 @@
# capacitorCapacityBonus
#
# Used by:
# Modules from group: Capacitor Battery (27 of 27)
# Modules from group: Capacitor Battery (22 of 22)
type = "passive"
def handler(fit, ship, context):
fit.ship.increaseItemAttr("capacitorCapacity", ship.getModifiedItemAttr("capacitorBonus"))

View File

@@ -2,9 +2,9 @@
#
# Used by:
# Implants named like: Hardwiring Zainou 'Sharpshooter' ZMX (6 of 6)
# Skill: Citadel Torpedoes
# Skill: XL Torpedoes
type = "passive"
def handler(fit, container, context):
level = container.level if "skill" in context else 1
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Torpedoes"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Torpedoes"),
"emDamage", container.getModifiedItemAttr("damageMultiplierBonus") * level)

View File

@@ -2,9 +2,9 @@
#
# Used by:
# Implants named like: Hardwiring Zainou 'Sharpshooter' ZMX (6 of 6)
# Skill: Citadel Torpedoes
# Skill: XL Torpedoes
type = "passive"
def handler(fit, container, context):
level = container.level if "skill" in context else 1
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Torpedoes"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Torpedoes"),
"explosiveDamage", container.getModifiedItemAttr("damageMultiplierBonus") * level)

View File

@@ -2,9 +2,9 @@
#
# Used by:
# Implants named like: Hardwiring Zainou 'Sharpshooter' ZMX (6 of 6)
# Skill: Citadel Torpedoes
# Skill: XL Torpedoes
type = "passive"
def handler(fit, container, context):
level = container.level if "skill" in context else 1
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Torpedoes"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Torpedoes"),
"kineticDamage", container.getModifiedItemAttr("damageMultiplierBonus") * level)

View File

@@ -2,9 +2,9 @@
#
# Used by:
# Implants named like: Hardwiring Zainou 'Sharpshooter' ZMX (6 of 6)
# Skill: Citadel Torpedoes
# Skill: XL Torpedoes
type = "passive"
def handler(fit, container, context):
level = container.level if "skill" in context else 1
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Torpedoes"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Torpedoes"),
"thermalDamage", container.getModifiedItemAttr("damageMultiplierBonus") * level)

View File

@@ -1,8 +1,8 @@
# capitalLauncherSkillCruiseCitadelEmDamage1
#
# Used by:
# Skill: Citadel Cruise Missiles
# Skill: XL Cruise Missiles
type = "passive"
def handler(fit, skill, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Cruise Missiles"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Cruise Missiles"),
"emDamage", skill.getModifiedItemAttr("damageMultiplierBonus") * skill.level)

View File

@@ -1,8 +1,8 @@
# capitalLauncherSkillCruiseCitadelExplosiveDamage1
#
# Used by:
# Skill: Citadel Cruise Missiles
# Skill: XL Cruise Missiles
type = "passive"
def handler(fit, skill, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Cruise Missiles"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Cruise Missiles"),
"explosiveDamage", skill.getModifiedItemAttr("damageMultiplierBonus") * skill.level)

View File

@@ -1,8 +1,8 @@
# capitalLauncherSkillCruiseCitadelKineticDamage1
#
# Used by:
# Skill: Citadel Cruise Missiles
# Skill: XL Cruise Missiles
type = "passive"
def handler(fit, skill, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Cruise Missiles"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Cruise Missiles"),
"kineticDamage", skill.getModifiedItemAttr("damageMultiplierBonus") * skill.level)

View File

@@ -1,8 +1,8 @@
# capitalLauncherSkillCruiseCitadelThermalDamage1
#
# Used by:
# Skill: Citadel Cruise Missiles
# Skill: XL Cruise Missiles
type = "passive"
def handler(fit, skill, context):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Cruise Missiles"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Cruise Missiles"),
"thermalDamage", skill.getModifiedItemAttr("damageMultiplierBonus") * skill.level)

View File

@@ -1,7 +1,7 @@
# damageControl
#
# Used by:
# Modules from group: Damage Control (14 of 14)
# Modules from group: Damage Control (17 of 17)
type = "passive"
def handler(fit, module, context):
for layer, attrPrefix in (('shield', 'shield'), ('armor', 'armor'), ('hull', '')):

View File

@@ -2,7 +2,6 @@
#
# Used by:
# Drones from group: Stasis Webifying Drone (3 of 3)
# Modules from group: Stasis Web (19 of 19)
type = "active", "projected"
def handler(fit, module, context):
if "projected" not in context:

View File

@@ -1,7 +1,7 @@
# droneDamageBonusOnline
#
# Used by:
# Modules from group: Drone Damage Modules (10 of 10)
# Modules from group: Drone Damage Modules (11 of 11)
type = "passive"
def handler(fit, module, context):
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"),

View File

@@ -1,7 +1,7 @@
# droneTrackingComputerBonus
#
# Used by:
# Modules from group: Drone Tracking Modules (8 of 8)
# Modules from group: Drone Tracking Modules (10 of 10)
type = "active"
def handler(fit, module, context):
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"),

View File

@@ -1,7 +1,7 @@
# droneTrackingEnhancerBonus
#
# Used by:
# Modules from group: Drone Tracking Enhancer (9 of 9)
# Modules from group: Drone Tracking Enhancer (10 of 10)
type = "passive"
def handler(fit, module, context):
fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"),

View File

@@ -1,7 +1,7 @@
# ecmBurst
#
# Used by:
# Modules from group: ECM Burst (7 of 7)
# Modules from group: Burst Jammer (11 of 11)
type = "active"
def handler(fit, module, context):
pass

View File

@@ -4,6 +4,7 @@
# Implants named like: Inherent Implants 'Squire' Capacitor Management EM (6 of 6)
# Implants named like: Mindflood Booster (4 of 4)
# Modules named like: Semiconductor Memory Cell (8 of 8)
# Implant: Antipharmakon Aeolis
# Implant: Genolution Core Augmentation CA-1
# Skill: Capacitor Management
type = "passive"

View File

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

View File

@@ -1,7 +1,7 @@
# energyWeaponSpeedMultiply
#
# Used by:
# Modules from group: Heat Sink (25 of 25)
# Modules from group: Heat Sink (18 of 18)
type = "passive"
def handler(fit, module, context):
fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == "Energy Weapon",

View File

@@ -1,8 +0,0 @@
# ewSkillEcmBurstFalloffBonus
#
# Used by:
# Skill: Frequency Modulation
type = "passive"
def handler(fit, skill, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Burst Jammer",
"falloffEffectiveness", skill.getModifiedItemAttr("falloffBonus") * skill.level)

View File

@@ -1,7 +1,6 @@
# ewTargetPaint
#
# Used by:
# Modules from group: Target Painter (9 of 9)
# Drones named like: TP (3 of 3)
type = "projected", "active"
def handler(fit, container, context):

View File

@@ -1,7 +1,6 @@
# ewTestEffectJam
#
# Used by:
# Modules from group: ECM (44 of 44)
# Drones named like: EC (3 of 3)
type = "projected", "active"
def handler(fit, module, context):

View File

@@ -1,7 +1,7 @@
# gunneryFalloffBonusOnline
#
# Used by:
# Modules from group: Tracking Enhancer (17 of 17)
# Modules from group: Tracking Enhancer (10 of 10)
# Module: QA Damage Module
type = "passive"
def handler(fit, module, context):

View File

@@ -1,7 +1,7 @@
# gunneryMaxRangeBonusOnline
#
# Used by:
# Modules from group: Tracking Enhancer (17 of 17)
# Modules from group: Tracking Enhancer (10 of 10)
# Module: QA Damage Module
type = "passive"
def handler(fit, module, context):

View File

@@ -1,7 +1,7 @@
# gunneryMaxRangeFalloffTrackingSpeedBonus
#
# Used by:
# Modules from group: Tracking Computer (14 of 14)
# Modules from group: Tracking Computer (11 of 11)
type = "active"
def handler(fit, module, context):
for attr in ("maxRange", "falloff", "trackingSpeed"):

View File

@@ -1,7 +1,7 @@
# gunneryTrackingSpeedBonusOnline
#
# Used by:
# Modules from group: Tracking Enhancer (17 of 17)
# Modules from group: Tracking Enhancer (10 of 10)
# Module: QA Damage Module
type = "passive"
def handler(fit, module, context):

View File

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

View File

@@ -1,7 +1,7 @@
# hybridWeaponSpeedMultiply
#
# Used by:
# Modules from group: Magnetic Field Stabilizer (20 of 20)
# Modules from group: Magnetic Field Stabilizer (12 of 12)
type = "passive"
def handler(fit, module, context):
fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == "Hybrid Weapon",

View File

@@ -3,6 +3,7 @@
# Used by:
# Implants named like: Zainou 'Deadeye' Missile Bombardment MB (6 of 6)
# Modules named like: Rocket Fuel Cache Partition (8 of 8)
# Implant: Antipharmakon Toxot
# Skill: Missile Bombardment
type = "passive"
def handler(fit, container, context):

View File

@@ -1,7 +1,7 @@
# missileDMGBonus
#
# Used by:
# Modules from group: Ballistic Control system (21 of 21)
# Modules from group: Ballistic Control system (17 of 17)
# Modules named like: QA Multiship Module Players (4 of 4)
type = "passive"
def handler(fit, container, context):

View File

@@ -1,7 +1,7 @@
# missileLauncherSpeedMultiplier
#
# Used by:
# Modules from group: Ballistic Control system (21 of 21)
# Modules from group: Ballistic Control system (17 of 17)
type = "passive"
def handler(fit, module, context):
fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Missile Launcher Operation"),

View File

@@ -1,3 +1,7 @@
# modifyEnergyWarfareResistance
#
# Used by:
# Modules from group: Capacitor Battery (22 of 22)
type = "passive"
def handler(fit, module, context):
fit.ship.boostItemAttr("energyWarfareResistance",

View File

@@ -1,8 +0,0 @@
# neutAttackReflect
#
# Used by:
# Modules from group: Capacitor Battery (27 of 27)
type = "passive"
def handler(fit, module, context):
fit.ship.boostItemAttr("neutReflector", module.getModifiedItemAttr("capAttackReflector"),
stackingPenalties = True)

View File

@@ -1,7 +0,0 @@
# neutReflectAmount
#
# Used by:
# Modules from group: Capacitor Battery (27 of 27)
type = "passive"
def handler(fit, module, context):
fit.ship.boostItemAttr("neutReflectAmount", module.getModifiedItemAttr("neutReflectAmountBonus"))

View File

@@ -1,8 +0,0 @@
# nosAttackReflect
#
# Used by:
# Modules from group: Capacitor Battery (27 of 27)
type = "passive"
def handler(fit, module, context):
fit.ship.boostItemAttr("nosReflector", module.getModifiedItemAttr("capAttackReflector"),
stackingPenalties = True)

View File

@@ -1,7 +0,0 @@
# nosReflectAmount
#
# Used by:
# Modules from group: Capacitor Battery (27 of 27)
type = "passive"
def handler(fit, module, context):
fit.ship.boostItemAttr("nosReflectAmount", module.getModifiedItemAttr("nosReflectAmountBonus"))

View File

@@ -1,13 +1,9 @@
# overloadRofBonus
#
# Used by:
# Modules from group: Energy Weapon (100 of 187)
# Modules from group: Hybrid Weapon (110 of 202)
# Modules from group: Missile Launcher Citadel (4 of 4)
# Modules from group: Missile Launcher Heavy (12 of 12)
# Modules from group: Missile Launcher Rocket (15 of 15)
# Modules from group: Projectile Weapon (60 of 146)
# Modules named like: Launcher (125 of 138)
# Modules from group: Missile Launcher Torpedo (22 of 22)
# Items from market group: Ship Equipment > Turrets & Bays (397 of 767)
# Module: Interdiction Sphere Launcher I
type = "overheat"
def handler(fit, module, context):
module.boostItemAttr("speed", module.getModifiedItemAttr("overloadRofBonus"))

View File

@@ -1,10 +0,0 @@
# overloadSelfECCMStrenghtBonus
#
# Used by:
# Modules from group: ECCM (44 of 44)
# Modules from group: Projected ECCM (7 of 7)
type = "overheat"
def handler(fit, module, context):
for scanType in ("Gravimetric", "Magnetometric", "Radar", "Ladar"):
module.boostItemAttr("scan%sStrengthPercent" % scanType,
module.getModifiedItemAttr("overloadECCMStrenghtBonus"))

View File

@@ -1,8 +1,8 @@
# overloadSelfECMStrenghtBonus
#
# Used by:
# Modules from group: ECM (44 of 44)
# Modules from group: ECM Burst (7 of 7)
# Modules from group: Burst Jammer (11 of 11)
# Modules from group: ECM (39 of 39)
type = "overheat"
def handler(fit, module, context):
if "projected" not in context:

View File

@@ -1,7 +1,7 @@
# overloadSelfPainterBonus
#
# Used by:
# Modules from group: Target Painter (9 of 9)
# Modules from group: Target Painter (8 of 8)
type = "overheat"
def handler(fit, module, context):
module.boostItemAttr("signatureRadiusBonus", module.getModifiedItemAttr("overloadPainterStrengthBonus") or 0)

View File

@@ -1,7 +1,8 @@
# overloadSelfRangeBonus
#
# Used by:
# Modules from group: Stasis Web (19 of 19)
# Modules from group: Stasis Grappler (7 of 7)
# Modules from group: Stasis Web (18 of 18)
# Modules from group: Warp Scrambler (38 of 39)
type = "overheat"
def handler(fit, module, context):

View File

@@ -2,8 +2,8 @@
#
# Used by:
# Modules from group: Remote Sensor Booster (8 of 8)
# Modules from group: Remote Sensor Damper (8 of 8)
# Modules from group: Sensor Booster (12 of 12)
# Modules from group: Sensor Booster (16 of 16)
# Modules from group: Sensor Dampener (6 of 6)
type = "overheat"
def handler(fit, module, context):
module.boostItemAttr("maxTargetRangeBonus", module.getModifiedItemAttr("overloadSensorModuleStrengthBonus"))

View File

@@ -1,9 +1,8 @@
# overloadSelfTrackingModuleBonus
#
# Used by:
# Modules from group: Drone Tracking Modules (8 of 8)
# Modules from group: Remote Tracking Computer (10 of 10)
# Modules from group: Tracking Computer (14 of 14)
# Modules from group: Drone Tracking Modules (10 of 10)
# Modules named like: Tracking Computer (19 of 19)
# Variations of module: Tracking Disruptor I (6 of 6)
type = "overheat"
def handler(fit, module, context):

View File

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

View File

@@ -1,7 +1,7 @@
# projectileWeaponSpeedMultiply
#
# Used by:
# Modules from group: Gyrostabilizer (20 of 20)
# Modules from group: Gyrostabilizer (12 of 12)
type = "passive"
def handler(fit, module, context):
fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name == "Projectile Weapon",

View File

@@ -1,3 +1,7 @@
# remoteECMFalloff
#
# Used by:
# Modules from group: ECM (39 of 39)
type = "projected", "active"
def handler(fit, module, context):
if "projected" in context:

View File

@@ -1,4 +1,4 @@
# targetMissileDisruptorHostile
# remoteGuidanceDisruptFalloff
#
# Used by:
# Variations of module: Guidance Disruptor I (6 of 6)

View File

@@ -1,3 +1,7 @@
# remoteSensorBoostFalloff
#
# Used by:
# Modules from group: Remote Sensor Booster (8 of 8)
type= "projected", "active"
def handler(fit, module, context):
if "projected" not in context:

View File

@@ -1,3 +1,7 @@
# remoteSensorDampFalloff
#
# Used by:
# Modules from group: Sensor Dampener (6 of 6)
type= "projected", "active"
def handler(fit, module, context):
if "projected" not in context:

View File

@@ -1,3 +1,7 @@
# remoteTargetPaintFalloff
#
# Used by:
# Modules from group: Target Painter (8 of 8)
type = "projected", "active"
def handler(fit, container, context):
if "projected" in context:

View File

@@ -1,3 +1,7 @@
# remoteTrackingAssistFalloff
#
# Used by:
# Modules from group: Remote Tracking Computer (8 of 8)
type= "projected", "active"
def handler(fit, module, context):
if "projected" in context:

View File

@@ -1,4 +1,4 @@
# targetGunneryMaxRangeAndTrackingSpeedAndFalloffBonusHostile
# remoteTrackingDisruptFalloff
#
# Used by:
# Variations of module: Tracking Disruptor I (6 of 6)

View File

@@ -1,3 +1,8 @@
# remoteWebifierFalloff
#
# Used by:
# Modules from group: Stasis Grappler (7 of 7)
# Modules from group: Stasis Web (18 of 18)
type = "active", "projected"
def handler(fit, module, context):
if "projected" not in context: return

View File

@@ -1,7 +1,6 @@
# scanStrengthBonusPercentActivate
#
# Used by:
# Modules from group: ECCM (44 of 44)
# Module: QA ECCM
type = "active"
def handler(fit, module, context):

View File

@@ -1,7 +1,7 @@
# scanStrengthBonusPercentOnline
#
# Used by:
# Modules from group: Sensor Backup Array (72 of 72)
# Modules from group: Signal Amplifier (7 of 7)
type = "passive"
def handler(fit, module, context):
for type in ("Gravimetric", "Magnetometric", "Radar", "Ladar"):

View File

@@ -1,11 +0,0 @@
# scanStrengthTargetPercentBonus
#
# Used by:
# Modules from group: Projected ECCM (7 of 7)
type = "projected", "active"
def handler(fit, module, context):
if "projected" not in context: return
for type in ("Gravimetric", "Magnetometric", "Radar", "Ladar"):
fit.ship.boostItemAttr("scan%sStrength" % type,
module.getModifiedItemAttr("scan%sStrengthPercent" % type),
stackingPenalties = True)

View File

@@ -1,7 +1,7 @@
# scriptSensorBoosterMaxTargetRangeBonusBonus
#
# Used by:
# Charges from group: Sensor Booster Script (2 of 2)
# Charges from group: Sensor Booster Script (3 of 3)
# Charges from group: Sensor Dampener Script (2 of 2)
type = "passive"
def handler(fit, module, context):

View File

@@ -1,7 +1,7 @@
# scriptSensorBoosterScanResolutionBonusBonus
#
# Used by:
# Charges from group: Sensor Booster Script (2 of 2)
# Charges from group: Sensor Booster Script (3 of 3)
# Charges from group: Sensor Dampener Script (2 of 2)
type = "passive"
def handler(fit, module, context):

View File

@@ -1,3 +1,7 @@
# scriptSensorBoosterSensorStrengthBonusBonus
#
# Used by:
# Charges from group: Sensor Booster Script (3 of 3)
type = "active"
def handler(fit, module, context):
for scanType in ("Gravimetric", "Magnetometric", "Radar", "Ladar"):

View File

@@ -1,7 +1,7 @@
# sensorBoosterActivePercentage
#
# Used by:
# Modules from group: Sensor Booster (12 of 12)
# Modules from group: Sensor Booster (16 of 16)
type = "active"
def handler(fit, module, context):
fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("maxTargetRangeBonus"),

View File

@@ -2,6 +2,7 @@
#
# Used by:
# Implants named like: Blue Pill Booster (5 of 5)
# Implant: Antipharmakon Thureo
type = "passive"
def handler(fit, container, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill("Capital Shield Operation"),

View File

@@ -4,5 +4,5 @@
# Ship: Phoenix
type = "passive"
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Citadel Cruise Missiles"),
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("XL Cruise Missiles"),
"speed", ship.getModifiedItemAttr("dreadnoughtShipBonusC1"), skill="Caldari Dreadnought")

View File

@@ -4,5 +4,5 @@
# Ship: Phoenix
type = "passive"
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Citadel Torpedoes"),
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("XL Torpedoes"),
"speed", ship.getModifiedItemAttr("dreadnoughtShipBonusC1"), skill="Caldari Dreadnought")

View File

@@ -1,3 +1,7 @@
# shipBonusRemoteTrackingComputerFalloffGC2
#
# Used by:
# Ship: Oneiros
type = "passive"
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Remote Tracking Computer",

View File

@@ -1,3 +1,7 @@
# shipBonusRemoteTrackingComputerFalloffMC
#
# Used by:
# Ship: Scimitar
type = "passive"
def handler(fit, ship, context):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Remote Tracking Computer",

View File

@@ -1,7 +1,7 @@
# shipMaxLockedTargetsBonusAddOnline
#
# Used by:
# Modules from group: Signal Amplifier (11 of 11)
# Modules from group: Signal Amplifier (7 of 7)
type = "passive"
def handler(fit, module, context):
fit.ship.increaseItemAttr("maxLockedTargets", module.getModifiedItemAttr("maxLockedTargetsBonus"))

View File

@@ -1,7 +1,7 @@
# shipMaxTargetRangeBonusOnline
#
# Used by:
# Modules from group: Signal Amplifier (11 of 11)
# Modules from group: Signal Amplifier (7 of 7)
type = "passive"
def handler(fit, module, context):
fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("maxTargetRangeBonus"),

View File

@@ -1,7 +1,7 @@
# shipScanResolutionBonusOnline
#
# Used by:
# Modules from group: Signal Amplifier (11 of 11)
# Modules from group: Signal Amplifier (7 of 7)
# Module: QA Damage Module
type = "passive"
def handler(fit, module, context):

View File

@@ -18,12 +18,12 @@ def handler(fit, module, context):
#Missiles
for type in ("kinetic", "thermal", "explosive", "em"):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Torpedoes") or \
mod.charge.requiresSkill("Citadel Cruise Missiles"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Torpedoes") or \
mod.charge.requiresSkill("XL Cruise Missiles"),
"%sDamage" % type, module.getModifiedItemAttr("damageMultiplierBonus"))
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Citadel Torpedoes") or \
mod.charge.requiresSkill("Citadel Cruise Missiles"),
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("XL Torpedoes") or \
mod.charge.requiresSkill("XL Cruise Missiles"),
"aoeVelocity", module.getModifiedItemAttr("aoeVelocityBonus"))
#Shield Boosters

View File

@@ -1,16 +0,0 @@
# targetGunneryMaxRangeFalloffTrackingSpeedBonusAssistance
#
# Used by:
# Modules from group: Remote Tracking Computer (10 of 10)
type= "projected", "active"
def handler(fit, module, context):
if "projected" in context:
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
"trackingSpeed", module.getModifiedItemAttr("trackingSpeedBonus"),
stackingPenalties = True)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
"maxRange", module.getModifiedItemAttr("maxRangeBonus"),
stackingPenalties = True)
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
"falloff", module.getModifiedItemAttr("falloffBonus"),
stackingPenalties = True)

View File

@@ -1,13 +0,0 @@
# targetMaxTargetRangeAndScanResolutionBonusAssistance
#
# Used by:
# Modules from group: Remote Sensor Booster (8 of 8)
type= "projected", "active"
def handler(fit, module, context):
if "projected" not in context:
return
fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("maxTargetRangeBonus"),
stackingPenalties = True)
fit.ship.boostItemAttr("scanResolution", module.getModifiedItemAttr("scanResolutionBonus"),
stackingPenalties = True)

View File

@@ -1,12 +0,0 @@
# targetMaxTargetRangeAndScanResolutionBonusHostile
#
# Used by:
# Modules from group: Remote Sensor Damper (8 of 8)
type= "projected", "active"
def handler(fit, module, context):
if "projected" not in context:
return
fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("maxTargetRangeBonus"),
stackingPenalties = True)
fit.ship.boostItemAttr("scanResolution", module.getModifiedItemAttr("scanResolutionBonus"),
stackingPenalties = True)

View File

@@ -3,6 +3,7 @@
# Used by:
# Implants named like: Drop Booster (4 of 4)
# Implants named like: Eifyr and Co. 'Gunslinger' Motion Prediction MR (6 of 6)
# Implant: Antipharmakon Iokira
# Implant: Ogdin's Eye Coordination Enhancer
# Skill: Motion Prediction
type = "passive"

View File

@@ -1,9 +1,9 @@
# useMissiles
#
# Used by:
# Modules from group: Missile Launcher Citadel (4 of 4)
# Modules from group: Missile Launcher Heavy (12 of 12)
# Modules from group: Missile Launcher Rocket (15 of 15)
# Modules from group: XL Missile Launcher (4 of 4)
# Modules named like: Launcher (138 of 138)
type = 'active'
def handler(fit, module, context):

View File

@@ -823,7 +823,8 @@ class Fit(object):
return 10 / rechargeRate * sqrt(percent) * (1 - sqrt(percent)) * capacity
def addDrain(self, cycleTime, capNeed, clipSize=0):
resistance = self.ship.getModifiedItemAttr("energyWarfareResistance") or 1
""" Used for both cap drains and cap fills (fills have negative capNeed) """
resistance = self.ship.getModifiedItemAttr("energyWarfareResistance") or 1 if capNeed > 0 else 1
self.__extraDrains.append((cycleTime, capNeed * resistance, clipSize))
def removeDrain(self, i):

View File

@@ -416,10 +416,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
# Check this only if we're told to do so
if hardpointLimit:
if self.hardpoint == Hardpoint.TURRET:
if fit.ship.getModifiedItemAttr('turretSlotsLeft') - fit.getHardpointsUsed(Hardpoint.TURRET) < 1:
if (fit.ship.getModifiedItemAttr('turretSlotsLeft') or 0) - fit.getHardpointsUsed(Hardpoint.TURRET) < 1:
return False
elif self.hardpoint == Hardpoint.MISSILE:
if fit.ship.getModifiedItemAttr('launcherSlotsLeft') - fit.getHardpointsUsed(Hardpoint.MISSILE) < 1:
if (fit.ship.getModifiedItemAttr('launcherSlotsLeft')or 0) - fit.getHardpointsUsed(Hardpoint.MISSILE) < 1:
return False
return True

BIN
eve.db

Binary file not shown.

View File

@@ -28,10 +28,12 @@ class ChangeAffectingSkills(ContextMenu):
fitID = self.mainFrame.getActiveFit()
sFit = service.Fit.getInstance()
self.stuff = sFit.getFit(fitID).ship
cont = sFit.getFit(fitID).ship.itemModifiedAttributes
elif srcContext == "fittingCharge":
cont = selection[0].chargeModifiedAttributes
else:
self.stuff = selection[0]
cont = selection[0].itemModifiedAttributes
cont = self.stuff.itemModifiedAttributes
skills = set()
for attrName in cont.iterAfflictions():

View File

@@ -23,7 +23,6 @@ from gui import builtinStatsViews
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
import service
import locale
class PriceViewFull(StatsView):
name = "priceViewFull"
@@ -107,15 +106,15 @@ class PriceViewFull(StatsView):
if self._cachedShip != shipPrice:
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(shipPrice, 3, 3, 9, currency=True))
self.labelPriceShip.SetToolTip(wx.ToolTip(locale.format('%.2f', shipPrice, 1)))
self.labelPriceShip.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice)))
self._cachedShip = shipPrice
if self._cachedFittings != modPrice:
self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(modPrice, 3, 3, 9, currency=True))
self.labelPriceFittings.SetToolTip(wx.ToolTip(locale.format('%.2f', modPrice, 1)))
self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(modPrice)))
self._cachedFittings = modPrice
if self._cachedTotal != (shipPrice+modPrice):
self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(shipPrice + modPrice, 3, 3, 9, currency=True))
self.labelPriceTotal.SetToolTip(wx.ToolTip(locale.format('%.2f', (shipPrice + modPrice), 1)))
self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice + modPrice)))
self._cachedTotal = shipPrice + modPrice
self.panel.Layout()

View File

@@ -22,7 +22,6 @@ from gui.statsView import StatsView
from gui import builtinStatsViews
from gui.utils.numberFormatter import formatAmount
import locale
try:
from collections import OrderedDict
except ImportError:
@@ -201,17 +200,15 @@ class TargetingMiscViewFull(StatsView):
label.SetToolTip(wx.ToolTip("Type: %s" % (fit.scanType)))
elif labelName == "labelFullAlignTime":
alignTime = "Align:\t%.3fs"%mainValue
mass = "Mass:\t%skg"%locale.format('%d', fit.ship.getModifiedItemAttr("mass"), 1)
mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass"))
agility = "Agility:\t%.3fx"%fit.ship.getModifiedItemAttr("agility")
label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility)))
elif labelName == "labelFullCargo":
tipLines = []
tipLines.append(u"Cargohold: %sm\u00B3 / %sm\u00B3"% (
locale.format('%.2f', fit.cargoBayUsed, 1),
locale.format('%.2f', newValues["main"], 1)))
tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"]))
for attrName, tipAlias in cargoNamesOrder.items():
if newValues[attrName] > 0:
tipLines.append(u"%s: %sm\u00B3"% (tipAlias, locale.format('%.2f', newValues[attrName], 1)))
tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName]))
label.SetToolTip(wx.ToolTip(u"\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip("%.1f" % mainValue))
@@ -237,12 +234,10 @@ class TargetingMiscViewFull(StatsView):
# if you add stuff to cargo, the capacity doesn't change and thus it is still cached
# This assures us that we force refresh of cargo tooltip
tipLines = []
tipLines.append(u"Cargohold: %sm\u00B3 / %sm\u00B3"% (
locale.format('%.2f', fit.cargoBayUsed, 1),
locale.format('%.2f', cachedCargo["main"], 1)))
tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"]))
for attrName, tipAlias in cargoNamesOrder.items():
if cachedCargo[attrName] > 0:
tipLines.append(u"%s: %sm\u00B3"% (tipAlias, locale.format('%.2f', cachedCargo[attrName], 1)))
tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName]))
label.SetToolTip(wx.ToolTip(u"\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip(""))

View File

@@ -116,12 +116,10 @@ class MainFrame(wx.Frame):
def getInstance(cls):
return cls.__instance if cls.__instance is not None else MainFrame()
def __init__(self):
self.title="pyfa %s%s - Python Fitting Assistant"%(config.version, "" if config.tag.lower() != 'git' else " (git)")
def __init__(self, title):
self.title=title
wx.Frame.__init__(self, None, wx.ID_ANY, self.title)
self.locale = wx.Locale(wx.LANGUAGE_ENGLISH)
MainFrame.__instance = self
#Load stored settings (width/height/maximized..)

View File

@@ -52,9 +52,17 @@ class exportHtmlThread(threading.Thread):
<head>
<title>Pyfa Fittings</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" />
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" />
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script>
// http://stackoverflow.com/questions/32453806/uncaught-securityerror-failed-to-execute-replacestate-on-history-cannot-be
$(document).bind('mobileinit',function(){
$.mobile.changePage.defaults.changeHash = false;
$.mobile.hashListeningEnabled = false;
$.mobile.pushStateEnabled = false;
});
</script>
<script src="https://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
<style>
/* Basic settings */
.ui-li-static.ui-collapsible {

BIN
imgs/icons/118_10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

BIN
imgs/icons/118_11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

BIN
imgs/icons/118_13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

BIN
imgs/icons/118_14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

BIN
imgs/icons/118_9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

BIN
imgs/icons/125_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

BIN
imgs/icons/126_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

13
pyfa.py
View File

@@ -43,6 +43,8 @@ parser = PassThroughOptionParser(usage=usage)
parser.add_option("-r", "--root", action="store_true", dest="rootsavedata", help="if you want pyfa to store its data in root folder, use this option", default=False)
parser.add_option("-w", "--wx28", action="store_true", dest="force28", help="Force usage of wxPython 2.8", default=False)
parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Set logger to debug level.", default=False)
parser.add_option("-t", "--title", action="store", dest="title", help="Set Window Title", default=None)
parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set the folder for savedata", default=None)
(options, args) = parser.parse_args()
@@ -99,9 +101,16 @@ if __name__ == "__main__":
# Configure paths
if options.rootsavedata is True:
config.saveInRoot = True
# set title if it wasn't supplied by argument
if options.title == None:
options.title = "pyfa %s%s - Python Fitting Assistant"%(config.version, "" if config.tag.lower() != 'git' else " (git)")
config.debug = options.debug
config.defPaths()
# convert to unicode if it is set
if options.savepath is not None:
options.savepath = unicode(options.savepath)
config.defPaths(options.savepath)
# Basic logging initialization
import logging
@@ -123,5 +132,5 @@ if __name__ == "__main__":
eos.db.saveddata_meta.create_all()
pyfa = wx.App(False)
MainFrame()
MainFrame(options.title)
pyfa.MainLoop()

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