Merge branch 'test-3' into esi
# Conflicts: # eos/saveddata/character.py # service/character.py # service/eveapi.py # service/pycrest/eve.py
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -119,3 +119,6 @@ ENV/
|
||||
eos.iml
|
||||
gitversion
|
||||
.version
|
||||
/.version
|
||||
*.swp
|
||||
|
||||
|
||||
22
config.py
22
config.py
@@ -23,10 +23,11 @@ debug = False
|
||||
saveInRoot = False
|
||||
|
||||
# Version data
|
||||
version = "1.34.0"
|
||||
tag = "Stable"
|
||||
expansionName = " Arms Race"
|
||||
expansionVersion = "1.3"
|
||||
|
||||
version = "2.0.0b4"
|
||||
tag = "git"
|
||||
expansionName = "YC120.2"
|
||||
expansionVersion = "1.2"
|
||||
evemonMinVersion = "4081"
|
||||
|
||||
pyfaPath = None
|
||||
@@ -76,10 +77,13 @@ def getPyfaRoot():
|
||||
return root
|
||||
|
||||
|
||||
def getGitVersion():
|
||||
with open(os.path.join(pyfaPath, '.version')) as f:
|
||||
version = f.readline()
|
||||
return version
|
||||
def getVersion():
|
||||
if os.path.isfile(os.path.join(pyfaPath, '.version')):
|
||||
with open(os.path.join(pyfaPath, '.version')) as f:
|
||||
gitVersion = f.readline()
|
||||
return gitVersion
|
||||
# if no version file exists, then user is running from source or not an official build
|
||||
return version + " (git)"
|
||||
|
||||
|
||||
def getDefaultSave():
|
||||
@@ -136,7 +140,7 @@ def defPaths(customSavePath=None):
|
||||
# os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem")
|
||||
|
||||
# The database where we store all the fits etc
|
||||
saveDB = os.path.join(savePath, "saveddata.db")
|
||||
saveDB = os.path.join(savePath, "saveddata-py3-dev.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
|
||||
|
||||
@@ -62,7 +62,7 @@ exe = EXE(pyz,
|
||||
a.scripts,
|
||||
exclude_binaries=True,
|
||||
debug=False,
|
||||
console=True,
|
||||
console=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
name='pyfa',
|
||||
|
||||
45
dist_assets/win/version_resource.py
Normal file
45
dist_assets/win/version_resource.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# UTF-8
|
||||
#
|
||||
# For more details about fixed file info 'ffi' see:
|
||||
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
|
||||
VSVersionInfo(
|
||||
ffi=FixedFileInfo(
|
||||
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
|
||||
# Set not needed items to zero 0.
|
||||
filevers=(1, 15, 1, 0),
|
||||
prodvers=(1, 15, 1, 0),
|
||||
# Contains a bitmask that specifies the valid bits 'flags'r
|
||||
mask=0x3f,
|
||||
# Contains a bitmask that specifies the Boolean attributes of the file.
|
||||
flags=0x0,
|
||||
# The operating system for which this file was designed.
|
||||
# 0x4 - NT and there is no need to change it.
|
||||
OS=0x40004,
|
||||
# The general type of file.
|
||||
# 0x1 - the file is an application.
|
||||
fileType=0x1,
|
||||
# The function of the file.
|
||||
# 0x0 - the function is not defined for this fileType
|
||||
subtype=0x0,
|
||||
# Creation date and time stamp.
|
||||
date=(0, 0)
|
||||
),
|
||||
kids=[
|
||||
StringFileInfo(
|
||||
[
|
||||
StringTable(
|
||||
u'040904E4',
|
||||
[StringStruct(u'LegalCopyright', u''),
|
||||
StringStruct(u'InternalName', u'pyfa.exe'),
|
||||
StringStruct(u'FileVersion', u'1.15.1.0'),
|
||||
StringStruct(u'CompanyName', u''),
|
||||
StringStruct(u'OriginalFilename', u'pyfa.exe'),
|
||||
StringStruct(u'ProductVersion', u'1.15.1.0'),
|
||||
StringStruct(u'FileDescription', u'Python fitting assistant'),
|
||||
StringStruct(u'LegalTrademarks', u''),
|
||||
StringStruct(u'Comments', u''),
|
||||
StringStruct(u'ProductName', u'pyfa')])
|
||||
]),
|
||||
VarFileInfo([VarStruct(u'Translation', [1033, 1252])])
|
||||
]
|
||||
)
|
||||
@@ -18,7 +18,7 @@ if istravis is True or hasattr(sys, '_called_from_test'):
|
||||
# Running in Travis. Run saveddata database in memory.
|
||||
saveddata_connectionstring = 'sqlite:///:memory:'
|
||||
else:
|
||||
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db"))
|
||||
saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata-py3-db.db"))
|
||||
|
||||
pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring)
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class DefaultDatabaseValues(object):
|
||||
["[Hybrid Charges]Uranium", "0", "38.4", "57.6", "0"],
|
||||
["[Missiles]Mjolnir", "100", "0", "0", "0"], ["[Missiles]Inferno", "0", "100", "0", "0"],
|
||||
["[Missiles]Scourge", "0", "0", "100", "0"], ["[Missiles]Nova", "0", "0", "0", "100"],
|
||||
["[Missiles][Structure) Standup Missile", "100", "100", "100", "100"],
|
||||
["[Missiles][Structure] Standup Missile", "100", "100", "100", "100"],
|
||||
["[Projectile Ammo][T2] Tremor", "0", "0", "24", "40"],
|
||||
["[Projectile Ammo][T2] Quake", "0", "0", "40", "72"],
|
||||
["[Projectile Ammo][T2] Hail", "0", "0", "26.4", "96.8"],
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Modules named like: Memetic Algorithm Bank (8 of 8)
|
||||
# Implant: Neural Lace 'Blackglass' Net Intrusion 920-40
|
||||
# Implant: Poteque 'Prospector' Environmental Analysis EY-1005
|
||||
# Implant: Poteque 'Prospector' Hacking HC-905
|
||||
type = "passive"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ammoInfluenceCapNeed
|
||||
#
|
||||
# Used by:
|
||||
# Items from category: Charge (478 of 924)
|
||||
# Items from category: Charge (478 of 925)
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ammoInfluenceRange
|
||||
#
|
||||
# Used by:
|
||||
# Items from category: Charge (572 of 924)
|
||||
# Items from category: Charge (572 of 925)
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ammoSpeedMultiplier
|
||||
#
|
||||
# Used by:
|
||||
# Charges from group: Festival Charges (22 of 22)
|
||||
# Charges from group: Festival Charges (23 of 23)
|
||||
# Charges from group: Interdiction Probe (2 of 2)
|
||||
type = "passive"
|
||||
|
||||
|
||||
21
eos/effects/citadelrigbonus.py
Normal file
21
eos/effects/citadelrigbonus.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
|
||||
for attr in [
|
||||
"structureRigDoomsdayDamageLossTargetBonus",
|
||||
"structureRigScanResBonus",
|
||||
"structureRigPDRangeBonus",
|
||||
"structureRigPDCapUseBonus",
|
||||
"structureRigMissileExploVeloBonus",
|
||||
"structureRigMissileVelocityBonus",
|
||||
"structureRigEwarOptimalBonus",
|
||||
"structureRigEwarFalloffBonus",
|
||||
"structureRigEwarCapUseBonus",
|
||||
"structureRigMissileExplosionRadiusBonus"
|
||||
]:
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Jury Rigging"),
|
||||
attr, src.getModifiedItemAttr("structureRoleBonus"))
|
||||
@@ -1,8 +1,7 @@
|
||||
# damageControl
|
||||
#
|
||||
# Used by:
|
||||
# Variations of module: Damage Control I (16 of 16)
|
||||
# Module: Civilian Damage Control
|
||||
# Modules from group: Damage Control (22 of 27)
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# doHacking
|
||||
#
|
||||
# Used by:
|
||||
# Modules from group: Data Miners (17 of 18)
|
||||
# Modules from group: Data Miners (9 of 9)
|
||||
type = "active"
|
||||
|
||||
|
||||
|
||||
9
eos/effects/doomsdayaoebubble.py
Normal file
9
eos/effects/doomsdayaoebubble.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# doomsdayAOEBubble
|
||||
#
|
||||
# Used by:
|
||||
# Module: Standup Warp Disruption Burst Projector
|
||||
type = "projected", "active"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
return
|
||||
16
eos/effects/doomsdayaoedamp.py
Normal file
16
eos/effects/doomsdayaoedamp.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# doomsdayAOEDamp
|
||||
#
|
||||
# Used by:
|
||||
# Module: Sensor Dampening Burst Projector
|
||||
type = "projected", "active"
|
||||
|
||||
|
||||
def handler(fit, module, context, *args, **kwargs):
|
||||
if "projected" not in context:
|
||||
return
|
||||
|
||||
fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("maxTargetRangeBonus"),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
|
||||
fit.ship.boostItemAttr("scanResolution", module.getModifiedItemAttr("scanResolutionBonus"),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
21
eos/effects/doomsdayaoeneut.py
Normal file
21
eos/effects/doomsdayaoeneut.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# doomsdayAOENeut
|
||||
#
|
||||
# Used by:
|
||||
# Module: Energy Neutralization Burst Projector
|
||||
from eos.saveddata.module import State
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
||||
|
||||
type = "active", "projected"
|
||||
|
||||
|
||||
def handler(fit, src, context, **kwargs):
|
||||
if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or
|
||||
hasattr(src, "amountActive")):
|
||||
amount = src.getModifiedItemAttr("energyNeutralizerAmount")
|
||||
|
||||
if 'effect' in kwargs:
|
||||
amount *= ModifiedAttributeDict.getResistance(fit, kwargs['effect'])
|
||||
|
||||
time = src.getModifiedItemAttr("duration")
|
||||
|
||||
fit.addDrain(src, time, amount, 0)
|
||||
11
eos/effects/doomsdayaoepaint.py
Normal file
11
eos/effects/doomsdayaoepaint.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# doomsdayAOEPaint
|
||||
#
|
||||
# Used by:
|
||||
# Module: Target Illumination Burst Projector
|
||||
type = "projected", "active"
|
||||
|
||||
|
||||
def handler(fit, container, context, *args, **kwargs):
|
||||
if "projected" in context:
|
||||
fit.ship.boostItemAttr("signatureRadius", container.getModifiedItemAttr("signatureRadiusBonus"),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
29
eos/effects/doomsdayaoetrack.py
Normal file
29
eos/effects/doomsdayaoetrack.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# doomsdayAOETrack
|
||||
#
|
||||
# Used by:
|
||||
# Module: Weapon Disruption Burst Projector
|
||||
|
||||
type = "active", "projected"
|
||||
|
||||
|
||||
def handler(fit, module, context, *args, **kwargs):
|
||||
if "projected" in context:
|
||||
for srcAttr, tgtAttr in (
|
||||
("aoeCloudSizeBonus", "aoeCloudSize"),
|
||||
("aoeVelocityBonus", "aoeVelocity"),
|
||||
("missileVelocityBonus", "maxVelocity"),
|
||||
("explosionDelayBonus", "explosionDelay"),
|
||||
):
|
||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"),
|
||||
tgtAttr, module.getModifiedItemAttr(srcAttr),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||
"trackingSpeed", module.getModifiedItemAttr("trackingSpeedBonus"),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||
"maxRange", module.getModifiedItemAttr("maxRangeBonus"),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||
"falloff", module.getModifiedItemAttr("falloffBonus"),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
12
eos/effects/doomsdayaoeweb.py
Normal file
12
eos/effects/doomsdayaoeweb.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# doomsdayAOEWeb
|
||||
#
|
||||
# Used by:
|
||||
# Module: Stasis Webification Burst Projector
|
||||
type = "active", "projected"
|
||||
|
||||
|
||||
def handler(fit, module, context, *args, **kwargs):
|
||||
if "projected" not in context:
|
||||
return
|
||||
fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"),
|
||||
stackingPenalties=True, *args, **kwargs)
|
||||
@@ -1,7 +1,4 @@
|
||||
# eliteBonusGunshipDroneCapacity2
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Ishkur
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
7
eos/effects/elitebonusgunshipdronetracking2.py
Normal file
7
eos/effects/elitebonusgunshipdronetracking2.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "trackingSpeed",
|
||||
src.getModifiedItemAttr("eliteBonusGunship2"), stackingPenalties=True, skill="Assault Frigates")
|
||||
10
eos/effects/elitebonusgunshipemmissiledamage1.py
Normal file
10
eos/effects/elitebonusgunshipemmissiledamage1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# eliteBonusGunshipEMMissileDamage1
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Jaguar
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), "emDamage",
|
||||
src.getModifiedItemAttr("eliteBonusGunship1"), skill="Assault Frigates")
|
||||
10
eos/effects/elitebonusgunshipexplosionvelocity2.py
Normal file
10
eos/effects/elitebonusgunshipexplosionvelocity2.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# eliteBonusGunshipExplosionVelocity2
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Jaguar
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), "aoeVelocity",
|
||||
src.getModifiedItemAttr("eliteBonusGunship2"), stackingPenalties=True, skill="Assault Frigates")
|
||||
10
eos/effects/elitebonusgunshipexplosivemissiledamage1.py
Normal file
10
eos/effects/elitebonusgunshipexplosivemissiledamage1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# eliteBonusGunshipExplosiveMissileDamage1
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Jaguar
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), "explosiveDamage",
|
||||
src.getModifiedItemAttr("eliteBonusGunship1"), skill="Assault Frigates")
|
||||
10
eos/effects/elitebonusgunshipkineticmissiledamage1.py
Normal file
10
eos/effects/elitebonusgunshipkineticmissiledamage1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# eliteBonusGunshipKineticMissileDamage1
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Jaguar
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), "kineticDamage",
|
||||
src.getModifiedItemAttr("eliteBonusGunship1"), skill="Assault Frigates")
|
||||
@@ -1,7 +1,4 @@
|
||||
# eliteBonusGunshipProjectileDamage2
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Jaguar
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
# eliteBonusGunshipProjectileOptimal1
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Jaguar
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
10
eos/effects/elitebonusgunshipthermalmissiledamage1.py
Normal file
10
eos/effects/elitebonusgunshipthermalmissiledamage1.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# eliteBonusGunshipThermalMissileDamage1
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Jaguar
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), "thermalDamage",
|
||||
src.getModifiedItemAttr("eliteBonusGunship1"), skill="Assault Frigates")
|
||||
@@ -14,4 +14,5 @@ grouped = True
|
||||
def handler(fit, src, context):
|
||||
if "projected" not in context:
|
||||
return
|
||||
fit.ship.boostItemAttr("maxVelocity", src.getModifiedItemAttr("{}SpeedPenalty".format(prefix)) * src.amountActive)
|
||||
fit.ship.boostItemAttr("maxVelocity", src.getModifiedItemAttr("{}SpeedPenalty".format(prefix)) * src.amountActive,
|
||||
stackingPenalties=True)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Modules named like: Memetic Algorithm Bank (8 of 8)
|
||||
# Implant: Neural Lace 'Blackglass' Net Intrusion 920-40
|
||||
# Implant: Poteque 'Prospector' Environmental Analysis EY-1005
|
||||
# Implant: Poteque 'Prospector' Hacking HC-905
|
||||
# Skill: Hacking
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# Not used by any item
|
||||
# hackingVirusStrengthBonus
|
||||
#
|
||||
# Used by:
|
||||
# Implant: Neural Lace 'Blackglass' Net Intrusion 920-40
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
16
eos/effects/modulebonusassaultdamagecontrol.py
Normal file
16
eos/effects/modulebonusassaultdamagecontrol.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# moduleBonusAssaultDamageControl
|
||||
#
|
||||
# Used by:
|
||||
# Variations of module: Assault Damage Control I (5 of 5)
|
||||
type = "active"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
for layer, attrPrefix in (('shield', 'shield'), ('armor', 'armor'), ('hull', '')):
|
||||
for damageType in ('Kinetic', 'Thermal', 'Explosive', 'Em'):
|
||||
bonus = "%s%sDamageResonance" % (attrPrefix, damageType)
|
||||
bonus = "%s%s" % (bonus[0].lower(), bonus[1:])
|
||||
booster = "%s%sDamageResonance" % (layer, damageType)
|
||||
|
||||
src.forceItemAttr(booster, src.getModifiedItemAttr("resistanceMultiplier"))
|
||||
10
eos/effects/remotewebifiermaxrangebonus.py
Normal file
10
eos/effects/remotewebifiermaxrangebonus.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# remoteWebifierMaxRangeBonus
|
||||
#
|
||||
# Used by:
|
||||
# Implants named like: Inquest 'Eros' Stasis Webifier MR (3 of 3)
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Stasis Web", "maxRange",
|
||||
src.getModifiedItemAttr("stasisWebRangeBonus"), stackingPenalties=True)
|
||||
10
eos/effects/scriptscangravimetricstrengthbonusbonus.py
Normal file
10
eos/effects/scriptscangravimetricstrengthbonusbonus.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# scriptscanGravimetricStrengthBonusBonus
|
||||
#
|
||||
# Used by:
|
||||
# Charges from group: Structure ECM script (4 of 4)
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context, *args, **kwargs):
|
||||
src.boostItemAttr("scanGravimetricStrengthBonus", src.getModifiedChargeAttr("scanGravimetricStrengthBonusBonus"))
|
||||
10
eos/effects/scriptscanladarstrengthbonusbonus.py
Normal file
10
eos/effects/scriptscanladarstrengthbonusbonus.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# scriptscanLadarStrengthBonusBonus
|
||||
#
|
||||
# Used by:
|
||||
# Charges from group: Structure ECM script (4 of 4)
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context, *args, **kwargs):
|
||||
src.boostItemAttr("scanLadarStrengthBonus", src.getModifiedChargeAttr("scanLadarStrengthBonusBonus"))
|
||||
10
eos/effects/scriptscanmagnetometricstrengthbonusbonus.py
Normal file
10
eos/effects/scriptscanmagnetometricstrengthbonusbonus.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# scriptscanMagnetometricStrengthBonusBonus
|
||||
#
|
||||
# Used by:
|
||||
# Charges from group: Structure ECM script (4 of 4)
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context, *args, **kwargs):
|
||||
src.boostItemAttr("scanMagnetometricStrengthBonus", src.getModifiedChargeAttr("scanMagnetometricStrengthBonusBonus"))
|
||||
10
eos/effects/scriptscanradarstrengthbonusbonus.py
Normal file
10
eos/effects/scriptscanradarstrengthbonusbonus.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# scriptscanRadarStrengthBonusBonus
|
||||
#
|
||||
# Used by:
|
||||
# Charges from group: Structure ECM script (4 of 4)
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context, *args, **kwargs):
|
||||
src.boostItemAttr("scanRadarStrengthBonus", src.getModifiedChargeAttr("scanRadarStrengthBonusBonus"))
|
||||
11
eos/effects/scriptstandupwarpscram.py
Normal file
11
eos/effects/scriptstandupwarpscram.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# scriptStandupWarpScram
|
||||
#
|
||||
# Used by:
|
||||
# Charge: Standup Focused Warp Scrambling Script
|
||||
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context, *args, **kwargs):
|
||||
src.boostItemAttr("maxRange", src.getModifiedChargeAttr("warpScrambleRangeBonus"))
|
||||
7
eos/effects/servicemodulefullpowerhitpointpostassign.py
Normal file
7
eos/effects/servicemodulefullpowerhitpointpostassign.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.ship.forceItemAttr("structureFullPowerStateHitpointMultiplier", src.getModifiedItemAttr("serviceModuleFullPowerStateHitpointMultiplier"))
|
||||
10
eos/effects/shipbonusdronetrackingelitegunship2.py
Normal file
10
eos/effects/shipbonusdronetrackingelitegunship2.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# shipBonusDroneTrackingEliteGunship2
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Ishkur
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "trackingSpeed",
|
||||
src.getModifiedItemAttr("eliteBonusGunship2"), skill="Assault Frigates")
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Breacher
|
||||
# Ship: Jaguar
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# shipPDmgBonusMF
|
||||
#
|
||||
# Used by:
|
||||
# Variations of ship: Rifter (3 of 3)
|
||||
# Variations of ship: Slasher (3 of 3)
|
||||
# Ship: Cheetah
|
||||
# Ship: Freki
|
||||
# Ship: Republic Fleet Firetail
|
||||
# Ship: Rifter
|
||||
# Ship: Wolf
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#
|
||||
# Used by:
|
||||
# Variations of ship: Slasher (3 of 3)
|
||||
# Ship: Jaguar
|
||||
# Ship: Republic Fleet Firetail
|
||||
# Ship: Wolf
|
||||
type = "passive"
|
||||
|
||||
10
eos/effects/shipsetrofaf.py
Normal file
10
eos/effects/shipsetrofaf.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# shipSETROFAF
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Retribution
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Energy Turret"), "speed",
|
||||
src.getModifiedItemAttr("shipBonusAF"), stackingPenalties=False, skill="Amarr Frigate")
|
||||
@@ -1,7 +1,4 @@
|
||||
# shipSETTrackingBonusAF
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Retribution
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# Used by:
|
||||
# Ship: Breacher
|
||||
# Ship: Jaguar
|
||||
type = "passive"
|
||||
|
||||
|
||||
|
||||
11
eos/effects/structureaoerofrolebonus.py
Normal file
11
eos/effects/structureaoerofrolebonus.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, ship, context):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Structure Guided Bomb Launcher",
|
||||
"speed", ship.getModifiedItemAttr("structureAoERoFRoleBonus"))
|
||||
for attr in ["duration", "durationTargetIlluminationBurstProjector", "durationWeaponDisruptionBurstProjector",
|
||||
"durationECMJammerBurstProjector", "durationSensorDampeningBurstProjector", "capacitorNeed"]:
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Structure Burst Projector",
|
||||
attr, ship.getModifiedItemAttr("structureAoERoFRoleBonus"))
|
||||
7
eos/effects/structurearmorhpmultiply.py
Normal file
7
eos/effects/structurearmorhpmultiply.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.ship.multiplyItemAttr("hiddenArmorHPMultiplier", src.getModifiedItemAttr("armorHPMultiplier"))
|
||||
@@ -11,7 +11,7 @@ def handler(fit, module, context):
|
||||
module.getModifiedItemAttr("missileDamageMultiplierBonus"),
|
||||
stackingPenalties=True)
|
||||
|
||||
launcherGroups = ("Structure AXL Missile Launcher", "Structure ASML Missile Launcher")
|
||||
launcherGroups = ("Structure XL Missile Launcher", "Structure Multirole Missile Launcher")
|
||||
fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name in launcherGroups,
|
||||
"speed", module.getModifiedItemAttr("speedMultiplier"),
|
||||
stackingPenalties=True)
|
||||
|
||||
6
eos/effects/structurecapacitorcapacitybonus.py
Normal file
6
eos/effects/structurecapacitorcapacitybonus.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, ship, context):
|
||||
fit.ship.increaseItemAttr("capacitorCapacity", ship.getModifiedItemAttr("capacitorBonus"))
|
||||
7
eos/effects/structurefullpowerstatehitpointmodifier.py
Normal file
7
eos/effects/structurefullpowerstatehitpointmodifier.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.ship.multiplyItemAttr("shieldCapacity", src.getModifiedItemAttr("structureFullPowerStateHitpointMultiplier") or 0)
|
||||
fit.ship.multiplyItemAttr("armorHP", src.getModifiedItemAttr("structureFullPowerStateHitpointMultiplier") or 0)
|
||||
6
eos/effects/structurehiddenarmorhpmultiplier.py
Normal file
6
eos/effects/structurehiddenarmorhpmultiplier.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
fit.ship.multiplyItemAttr("armorHP", src.getModifiedItemAttr("hiddenArmorHPMultiplier") or 0)
|
||||
10
eos/effects/structurehiddenmissiledamagemultiplier.py
Normal file
10
eos/effects/structurehiddenmissiledamagemultiplier.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, src, context):
|
||||
groups = ("Structure Anti-Subcapital Missile", "Structure Anti-Capital Missile")
|
||||
for dmgType in ("em", "kinetic", "explosive", "thermal"):
|
||||
fit.modules.filteredChargeMultiply(lambda mod: mod.item.group.name in groups,
|
||||
"%sDamage" % dmgType,
|
||||
src.getModifiedItemAttr("hiddenMissileDamageMultiplier"))
|
||||
15
eos/effects/structuremissileguidanceenhancer.py
Normal file
15
eos/effects/structuremissileguidanceenhancer.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, container, context):
|
||||
missileGroups = ("Structure Anti-Capital Missile", "Structure Anti-Subcapital Missile")
|
||||
for srcAttr, tgtAttr in (
|
||||
("aoeCloudSizeBonus", "aoeCloudSize"),
|
||||
("aoeVelocityBonus", "aoeVelocity"),
|
||||
("missileVelocityBonus", "maxVelocity"),
|
||||
("explosionDelayBonus", "explosionDelay"),
|
||||
):
|
||||
fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name in missileGroups,
|
||||
tgtAttr, container.getModifiedItemAttr(srcAttr),
|
||||
stackingPenalties=True)
|
||||
6
eos/effects/structuremodifypowerrechargerate.py
Normal file
6
eos/effects/structuremodifypowerrechargerate.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
fit.ship.multiplyItemAttr("rechargeRate", module.getModifiedItemAttr("capacitorRechargeRateMultiplier"))
|
||||
6
eos/effects/structurerigmaxtargetrange.py
Normal file
6
eos/effects/structurerigmaxtargetrange.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# Not used by any item
|
||||
type = "passive"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("structureRigMaxTargetRangeBonus"))
|
||||
@@ -7,12 +7,12 @@ type = "projected", "active"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
if "projected" not in context:
|
||||
return
|
||||
|
||||
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
|
||||
|
||||
# this is such a dirty hack
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
if "projected" in context:
|
||||
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
|
||||
if module.charge is not None and module.charge.ID == 47336:
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.state > State.ONLINE and (
|
||||
mod.item.requiresSkill("High Speed Maneuvering")
|
||||
or mod.item.requiresSkill("Micro Jump Drive Operation")
|
||||
):
|
||||
mod.state = State.ONLINE
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
# warpDisruptSphere
|
||||
#
|
||||
# Used by:
|
||||
@@ -9,18 +10,23 @@ runTime = "early"
|
||||
|
||||
|
||||
def handler(fit, module, context):
|
||||
fit.ship.boostItemAttr("mass", module.getModifiedItemAttr("massBonusPercentage"))
|
||||
fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
|
||||
"speedBoostFactor", module.getModifiedItemAttr("speedBoostFactorBonus"))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
|
||||
"speedFactor", module.getModifiedItemAttr("speedFactorBonus"))
|
||||
fit.ship.forceItemAttr("disallowAssistance", 1)
|
||||
|
||||
|
||||
if "projected" in context:
|
||||
fit.ship.increaseItemAttr("warpScrambleStatus", module.getModifiedItemAttr("warpScrambleStrength"))
|
||||
|
||||
if module.charge is not None and module.charge.ID == 45010:
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE:
|
||||
mod.state = State.ONLINE
|
||||
else:
|
||||
if module.charge is None:
|
||||
fit.ship.boostItemAttr("mass", module.getModifiedItemAttr("massBonusPercentage"))
|
||||
fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
|
||||
"speedBoostFactor", module.getModifiedItemAttr("speedBoostFactorBonus"))
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Propulsion Module",
|
||||
"speedFactor", module.getModifiedItemAttr("speedFactorBonus"))
|
||||
|
||||
fit.ship.forceItemAttr("disallowAssistance", 1)
|
||||
|
||||
@@ -16,5 +16,8 @@ def handler(fit, module, context):
|
||||
|
||||
# this is such a dirty hack
|
||||
for mod in fit.modules:
|
||||
if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE:
|
||||
if not mod.isEmpty and mod.state > State.ONLINE and (
|
||||
mod.item.requiresSkill("Micro Jump Drive Operation")
|
||||
or mod.item.requiresSkill("High Speed Maneuvering")
|
||||
):
|
||||
mod.state = State.ONLINE
|
||||
|
||||
@@ -81,7 +81,7 @@ class FitDpsGraph(Graph):
|
||||
total += dps * self.calculateTurretMultiplier(mod, data)
|
||||
|
||||
elif mod.hardpoint == Hardpoint.MISSILE:
|
||||
if mod.state >= State.ACTIVE and mod.maxRange >= distance:
|
||||
if mod.state >= State.ACTIVE and mod.maxRange is not None and mod.maxRange >= distance:
|
||||
total += dps * self.calculateMissileMultiplier(mod, data)
|
||||
|
||||
if distance <= fit.extraAttributes["droneControlRange"]:
|
||||
|
||||
@@ -18,12 +18,16 @@
|
||||
# ===============================================================================
|
||||
|
||||
import re
|
||||
import eos.db
|
||||
|
||||
|
||||
class DamagePattern(object):
|
||||
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
|
||||
|
||||
def __init__(self, emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.update(*args, **kwargs)
|
||||
|
||||
def update(self, emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25):
|
||||
self.emAmount = emAmount
|
||||
self.thermalAmount = thermalAmount
|
||||
self.kineticAmount = kineticAmount
|
||||
@@ -74,6 +78,14 @@ class DamagePattern(object):
|
||||
lines = re.split('[\n\r]+', text)
|
||||
patterns = []
|
||||
numPatterns = 0
|
||||
|
||||
# When we import damage profiles, we create new ones and update old ones. To do this, get a list of current
|
||||
# patterns to allow lookup
|
||||
lookup = {}
|
||||
current = eos.db.getDamagePatternList()
|
||||
for pattern in current:
|
||||
lookup[pattern.name] = pattern
|
||||
|
||||
for line in lines:
|
||||
try:
|
||||
if line.strip()[0] == "#": # comments
|
||||
@@ -99,10 +111,18 @@ class DamagePattern(object):
|
||||
continue
|
||||
|
||||
if len(fields) == 4: # Avoid possible blank lines
|
||||
pattern = DamagePattern(**fields)
|
||||
pattern.name = name.strip()
|
||||
if name.strip() in lookup:
|
||||
pattern = lookup[name.strip()]
|
||||
pattern.update(**fields)
|
||||
eos.db.save(pattern)
|
||||
else:
|
||||
pattern = DamagePattern(**fields)
|
||||
pattern.name = name.strip()
|
||||
eos.db.save(pattern)
|
||||
patterns.append(pattern)
|
||||
|
||||
eos.db.commit()
|
||||
|
||||
return patterns, numPatterns
|
||||
|
||||
EXPORT_FORMAT = "DamageProfile = %s,%d,%d,%d,%d\n"
|
||||
|
||||
@@ -264,7 +264,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
def fits(self, fit):
|
||||
fitDroneGroupLimits = set()
|
||||
for i in range(1, 3):
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i)
|
||||
groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i, None)
|
||||
if groneGrp is not None:
|
||||
fitDroneGroupLimits.add(int(groneGrp))
|
||||
if len(fitDroneGroupLimits) == 0:
|
||||
|
||||
@@ -89,8 +89,8 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
self.__slot = self.__calculateSlot(self.__item)
|
||||
|
||||
chargeID = self.getModifiedItemAttr("fighterAbilityLaunchBombType", None)
|
||||
if chargeID is not None:
|
||||
chargeID = self.getModifiedItemAttr("fighterAbilityLaunchBombType")
|
||||
if chargeID:
|
||||
charge = eos.db.getItem(int(chargeID))
|
||||
self.__charge = charge
|
||||
self.__chargeModifiedAttributes.original = charge.attributes
|
||||
@@ -104,7 +104,10 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
types = {
|
||||
"Light" : Slot.F_LIGHT,
|
||||
"Support": Slot.F_SUPPORT,
|
||||
"Heavy" : Slot.F_HEAVY
|
||||
"Heavy" : Slot.F_HEAVY,
|
||||
"StandupLight": Slot.FS_LIGHT,
|
||||
"StandupSupport": Slot.FS_SUPPORT,
|
||||
"StandupHeavy": Slot.FS_HEAVY
|
||||
}
|
||||
|
||||
for t, slot in types.items():
|
||||
|
||||
@@ -917,7 +917,7 @@ class Fit(object):
|
||||
|
||||
for mod in chain(self.modules, self.fighters):
|
||||
if mod.slot is type and (not getattr(mod, "isEmpty", False) or countDummies):
|
||||
if type in (Slot.F_HEAVY, Slot.F_SUPPORT, Slot.F_LIGHT) and not mod.active:
|
||||
if type in (Slot.F_HEAVY, Slot.F_SUPPORT, Slot.F_LIGHT, Slot.FS_HEAVY, Slot.FS_LIGHT, Slot.FS_SUPPORT) and not mod.active:
|
||||
continue
|
||||
amount += 1
|
||||
|
||||
@@ -932,7 +932,10 @@ class Fit(object):
|
||||
Slot.SERVICE : "serviceSlots",
|
||||
Slot.F_LIGHT : "fighterLightSlots",
|
||||
Slot.F_SUPPORT: "fighterSupportSlots",
|
||||
Slot.F_HEAVY : "fighterHeavySlots"
|
||||
Slot.F_HEAVY : "fighterHeavySlots",
|
||||
Slot.FS_LIGHT: "fighterStandupLightSlots",
|
||||
Slot.FS_SUPPORT: "fighterStandupSupportSlots",
|
||||
Slot.FS_HEAVY: "fighterStandupHeavySlots",
|
||||
}
|
||||
|
||||
def getSlotsFree(self, type, countDummies=False):
|
||||
|
||||
@@ -56,6 +56,10 @@ class Slot(Enum):
|
||||
F_LIGHT = 10
|
||||
F_SUPPORT = 11
|
||||
F_HEAVY = 12
|
||||
# fighter 'slots' (for structures)
|
||||
FS_LIGHT = 13
|
||||
FS_SUPPORT = 14
|
||||
FS_HEAVY = 15
|
||||
|
||||
|
||||
class Hardpoint(Enum):
|
||||
@@ -178,7 +182,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
@property
|
||||
def numShots(self):
|
||||
if self.charge is None:
|
||||
return -1
|
||||
return 0
|
||||
if self.__chargeCycles is None and self.charge:
|
||||
numCharges = self.numCharges
|
||||
# Usual ammo like projectiles and missiles
|
||||
@@ -430,9 +434,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
and fit.ship.item.ID not in fitsOnType:
|
||||
return False
|
||||
|
||||
# AFAIK Citadel modules will always be restricted based on canFitShipType/Group. If we are fitting to a Citadel
|
||||
# and the module does not have these properties, return false to prevent regular ship modules from being used
|
||||
if isinstance(fit.ship, Citadel) and len(fitsOnGroup) == 0 and len(fitsOnType) == 0:
|
||||
# Citadel modules are now under a new category, so we can check this to ensure only structure modules can fit on a citadel
|
||||
if isinstance(fit.ship, Citadel) and self.item.category.name != "Structure Module" or \
|
||||
not isinstance(fit.ship, Citadel) and self.item.category.name == "Structure Module":
|
||||
return False
|
||||
|
||||
# EVE doesn't let capital modules be fit onto subcapital hulls. Confirmed by CCP Larrikin that this is dictated
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import re
|
||||
from logbook import Logger
|
||||
import eos.db
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -27,7 +28,10 @@ class TargetResists(object):
|
||||
# also determined import/export order - VERY IMPORTANT
|
||||
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
|
||||
|
||||
def __init__(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.update(*args, **kwargs)
|
||||
|
||||
def update(self, emAmount=0, thermalAmount=0, kineticAmount=0, explosiveAmount=0):
|
||||
self.emAmount = emAmount
|
||||
self.thermalAmount = thermalAmount
|
||||
self.kineticAmount = kineticAmount
|
||||
@@ -38,6 +42,14 @@ class TargetResists(object):
|
||||
lines = re.split('[\n\r]+', text)
|
||||
patterns = []
|
||||
numPatterns = 0
|
||||
|
||||
# When we import damage profiles, we create new ones and update old ones. To do this, get a list of current
|
||||
# patterns to allow lookup
|
||||
lookup = {}
|
||||
current = eos.db.getTargetResistsList()
|
||||
for pattern in current:
|
||||
lookup[pattern.name] = pattern
|
||||
|
||||
for line in lines:
|
||||
try:
|
||||
if line.strip()[0] == "#": # comments
|
||||
@@ -66,10 +78,18 @@ class TargetResists(object):
|
||||
continue
|
||||
|
||||
if len(fields) == 4: # Avoid possible blank lines
|
||||
pattern = TargetResists(**fields)
|
||||
pattern.name = name.strip()
|
||||
if name.strip() in lookup:
|
||||
pattern = lookup[name.strip()]
|
||||
pattern.update(**fields)
|
||||
eos.db.save(pattern)
|
||||
else:
|
||||
pattern = TargetResists(**fields)
|
||||
pattern.name = name.strip()
|
||||
eos.db.save(pattern)
|
||||
patterns.append(pattern)
|
||||
|
||||
eos.db.commit()
|
||||
|
||||
return patterns, numPatterns
|
||||
|
||||
EXPORT_FORMAT = "TargetResists = %s,%.1f,%.1f,%.1f,%.1f\n"
|
||||
|
||||
@@ -51,6 +51,7 @@ class BoosterView(d.Display):
|
||||
"Base Name",
|
||||
"Side Effects",
|
||||
"Price",
|
||||
"Miscellanea",
|
||||
]
|
||||
|
||||
def __init__(self, parent):
|
||||
@@ -130,6 +131,7 @@ class BoosterView(d.Display):
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
if not fit or fit.isStructure:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
trigger = sFit.addBooster(fitID, event.itemID)
|
||||
|
||||
@@ -210,6 +210,7 @@ class DroneView(Display):
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
if not fit or fit.isStructure:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
trigger = sFit.addDrone(fitID, event.itemID)
|
||||
|
||||
@@ -91,7 +91,10 @@ class FighterView(wx.Panel):
|
||||
|
||||
if fit:
|
||||
for x in self.labels:
|
||||
slot = getattr(Slot, "F_{}".format(x.upper()))
|
||||
if fit.isStructure:
|
||||
slot = getattr(Slot, "FS_{}".format(x.upper()))
|
||||
else:
|
||||
slot = getattr(Slot, "F_{}".format(x.upper()))
|
||||
used = fit.getSlotsUsed(slot)
|
||||
total = fit.getNumSlots(slot)
|
||||
color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings.GetColour(
|
||||
|
||||
@@ -152,6 +152,7 @@ class ImplantDisplay(d.Display):
|
||||
fit = sFit.getFit(fitID)
|
||||
|
||||
if not fit or fit.isStructure:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
trigger = sFit.addImplant(fitID, event.itemID)
|
||||
|
||||
@@ -99,15 +99,22 @@ class ProjectedView(d.Display):
|
||||
data[0] is hard-coded str of originating source
|
||||
data[1] is typeID or index of data we want to manipulate
|
||||
"""
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(self.mainFrame.getActiveFit())
|
||||
|
||||
if data[0] == "projected":
|
||||
# if source is coming from projected, we are trying to combine drones.
|
||||
self.mergeDrones(x, y, int(data[1]))
|
||||
elif data[0] == "fitting":
|
||||
dstRow, _ = self.HitTest((x, y))
|
||||
# Gather module information to get position
|
||||
module = fit.modules[int(data[1])]
|
||||
sFit.project(fit.ID, module.item.ID)
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
|
||||
elif data[0] == "market":
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit.project(fitID, int(data[1]))
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
|
||||
sFit.project(fit.ID, int(data[1]))
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit.ID))
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
|
||||
@@ -26,57 +26,71 @@ class ChangeAmount(ContextMenu):
|
||||
return u"Change {0} Quantity".format(itmContext)
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
srcContext = fullContext[0]
|
||||
dlg = AmountChanger(self.mainFrame, selection[0], srcContext)
|
||||
dlg.ShowModal()
|
||||
thing = selection[0]
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
fitID = mainFrame.getActiveFit()
|
||||
|
||||
if isinstance(thing, es_Fit):
|
||||
value = thing.getProjectionInfo(fitID).amount
|
||||
else:
|
||||
value = thing.amount
|
||||
|
||||
dlg = AmountChanger(self.mainFrame, value)
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
if dlg.input.GetLineText(0).strip() == '':
|
||||
return
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
cleanInput = re.sub(r'[^0-9.]', '', dlg.input.GetLineText(0).strip())
|
||||
|
||||
if isinstance(thing, es_Cargo):
|
||||
sFit.addCargo(fitID, thing.item.ID, int(float(cleanInput)), replace=True)
|
||||
elif isinstance(thing, es_Fit):
|
||||
sFit.changeAmount(fitID, thing, int(float(cleanInput)))
|
||||
elif isinstance(thing, es_Fighter):
|
||||
sFit.changeActiveFighters(fitID, thing, int(float(cleanInput)))
|
||||
|
||||
wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
|
||||
ChangeAmount.register()
|
||||
|
||||
|
||||
class AmountChanger(wx.Dialog):
|
||||
def __init__(self, parent, thing, context):
|
||||
wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60))
|
||||
self.thing = thing
|
||||
self.context = context
|
||||
def __init__(self, parent, value):
|
||||
wx.Dialog.__init__(self, parent, title="Change Amount")
|
||||
self.SetMinSize((346, 156))
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
bSizer2 = wx.BoxSizer(wx.VERTICAL)
|
||||
text = wx.StaticText(self, wx.ID_ANY, "New Amount:")
|
||||
bSizer2.Add(text, 0)
|
||||
|
||||
bSizer1.Add(bSizer2, 0, wx.ALL, 10)
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.input.SetValue(str(value))
|
||||
|
||||
bSizer1.Add(self.input, 1, wx.ALL, 5)
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
bSizer3 = wx.BoxSizer(wx.VERTICAL)
|
||||
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 15)
|
||||
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.input.SetFocus()
|
||||
self.input.SetInsertionPointEnd()
|
||||
self.input.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.change)
|
||||
self.button = wx.Button(self, wx.ID_OK, "Done")
|
||||
bSizer1.Add(self.button, 0, wx.ALL, 5)
|
||||
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.Layout()
|
||||
self.Centre(wx.BOTH)
|
||||
self.button.Bind(wx.EVT_BUTTON, self.change)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
|
||||
def change(self, event):
|
||||
if self.input.GetLineText(0).strip() == '':
|
||||
event.Skip()
|
||||
self.Close()
|
||||
return
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
cleanInput = re.sub(r'[^0-9.]', '', self.input.GetLineText(0).strip())
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
fitID = mainFrame.getActiveFit()
|
||||
|
||||
if isinstance(self.thing, es_Cargo):
|
||||
sFit.addCargo(fitID, self.thing.item.ID, int(float(cleanInput)), replace=True)
|
||||
elif isinstance(self.thing, es_Fit):
|
||||
sFit.changeAmount(fitID, self.thing, int(float(cleanInput)))
|
||||
elif isinstance(self.thing, es_Fighter):
|
||||
sFit.changeActiveFighters(fitID, self.thing, int(float(cleanInput)))
|
||||
|
||||
wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
event.Skip()
|
||||
self.Close()
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
# checks to make sure it's valid number
|
||||
@staticmethod
|
||||
|
||||
@@ -31,9 +31,7 @@ class FactorReload(ContextMenu):
|
||||
|
||||
def getBitmap(self, context, selection):
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit.factorReload:
|
||||
if sFit.serviceFittingOptions["useGlobalForceReload"]:
|
||||
return BitmapLoader.getBitmap("state_active_small", "gui")
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -243,6 +243,8 @@ class ItemParams(wx.Panel):
|
||||
return "%s (%d)" % (group.name, value) if group is not None else str(value)
|
||||
|
||||
def attributeIDCallback():
|
||||
if not value: # some attributes come through with a value of 0? See #1387
|
||||
return "%d" % (value)
|
||||
attribute = Attribute.getInstance().getAttributeInfo(value)
|
||||
return "%s (%d)" % (attribute.name.capitalize(), value)
|
||||
|
||||
|
||||
@@ -42,6 +42,15 @@ class PFGeneralPref(PreferenceView):
|
||||
self.inputLogPath.SetBackgroundColour((200, 200, 200))
|
||||
mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
|
||||
|
||||
import requests
|
||||
self.certPath = wx.StaticText(panel, wx.ID_ANY, "Cert Path:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.certPath .Wrap(-1)
|
||||
mainSizer.Add(self.certPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
self.certPathCtrl = wx.TextCtrl(panel, wx.ID_ANY, requests.certs.where(), wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.certPathCtrl.SetEditable(False)
|
||||
self.certPathCtrl.SetBackgroundColour((200, 200, 200))
|
||||
mainSizer.Add(self.certPathCtrl, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
|
||||
|
||||
# Debug Logging
|
||||
self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, "Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
mainSizer.Add(self.cbdebugLogging, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
@@ -121,11 +121,11 @@ class PFNetworkPref(PreferenceView):
|
||||
self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, "Username:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stPSetLogin.Wrap(-1)
|
||||
self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
0)
|
||||
self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, "Password:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stPSetPassword.Wrap(-1)
|
||||
self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition,
|
||||
wx.DefaultSize, wx.TE_PASSWORD)
|
||||
wx.DefaultSize, wx.TE_PASSWORD)
|
||||
pAuthSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
pAuthSizer.Add(self.stPSetLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
pAuthSizer.Add(self.editProxySettingsLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
@@ -134,7 +134,7 @@ class PFNetworkPref(PreferenceView):
|
||||
mainSizer.Add(pAuthSizer, 0, wx.EXPAND, 5)
|
||||
|
||||
self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, "Auto-detected: ", wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
0)
|
||||
self.stPSAutoDetected.Wrap(-1)
|
||||
mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
@@ -150,10 +150,10 @@ class PFNetworkPref(PreferenceView):
|
||||
proxy = self.settings.autodetect()
|
||||
|
||||
if proxy is not None:
|
||||
addr, port = proxy
|
||||
txt = addr + ":" + str(port)
|
||||
addr, port = proxy
|
||||
txt = addr + ":" + str(port)
|
||||
else:
|
||||
txt = "None"
|
||||
txt = "None"
|
||||
|
||||
self.stPSAutoDetected.SetLabel("Auto-detected: " + txt)
|
||||
self.stPSAutoDetected.Disable()
|
||||
|
||||
@@ -289,6 +289,7 @@ class FitItem(SFItem.SFBrowserItem):
|
||||
def editLostFocus(self, event):
|
||||
self.RestoreEditButton()
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
def editCheckEsc(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_ESCAPE:
|
||||
|
||||
@@ -51,9 +51,13 @@ class PFBitmapFrame(wx.Frame):
|
||||
pass
|
||||
|
||||
def OnWindowPaint(self, event):
|
||||
# todo: evaluate wx.DragImage, might make this class obsolete, however might also lose our customizations
|
||||
# (like the sexy fade-in animation)
|
||||
rect = self.GetRect()
|
||||
canvas = wx.Bitmap(rect.width, rect.height)
|
||||
mdc = wx.AutoBufferedPaintDC(self)
|
||||
# todo: convert to context manager after updating to wxPython >v4.0.1 (4.0.1 has a bug, see #1421)
|
||||
# See #1418 for discussion
|
||||
mdc = wx.BufferedPaintDC(self)
|
||||
mdc.SelectObject(canvas)
|
||||
mdc.DrawBitmap(self.bitmap, 0, 0)
|
||||
mdc.SetPen(wx.Pen("#000000", width=1))
|
||||
|
||||
@@ -72,6 +72,10 @@ class Miscellanea(ViewColumn):
|
||||
|
||||
if itemGroup == "Ship Modifiers":
|
||||
return "", None
|
||||
elif itemGroup == "Booster":
|
||||
stuff.getModifiedItemAttr("boosterDuration")
|
||||
text = "{0} min".format(formatAmount(stuff.getModifiedItemAttr("boosterDuration") / 1000 / 60, 3, 0, 3))
|
||||
return text, "Booster Duration"
|
||||
elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"):
|
||||
trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed")
|
||||
if not trackingSpeed:
|
||||
|
||||
@@ -69,11 +69,12 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
|
||||
pyfalog.critical(e)
|
||||
if count < 0:
|
||||
startup = getattr(event, "startup", False) # see OpenFitsThread in gui.mainFrame
|
||||
from_import = getattr(event, "from_import", False) # always open imported into a new tab
|
||||
sFit = Fit.getInstance()
|
||||
openFitInNew = sFit.serviceFittingOptions["openFitInNew"]
|
||||
mstate = wx.GetMouseState()
|
||||
|
||||
if (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()):
|
||||
if from_import or (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()):
|
||||
self.multiSwitch.AddPage()
|
||||
|
||||
view = self.multiSwitch.GetSelectedPage()
|
||||
@@ -286,6 +287,7 @@ class FittingView(d.Display):
|
||||
If fit is removed and active, the page is deleted.
|
||||
We also refresh the fit of the new current page in case
|
||||
delete fit caused change in stats (projected)
|
||||
todo: move this to the notebook, not the page. We don't want the page being responsible for deleting itself
|
||||
"""
|
||||
print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
|
||||
pyfalog.debug("FittingView::fitRemoved")
|
||||
@@ -692,28 +694,26 @@ class FittingView(d.Display):
|
||||
# pyfalog.critical(e)
|
||||
|
||||
def OnShow(self, event):
|
||||
pass
|
||||
# if event.Show():
|
||||
# try:
|
||||
# self.MakeSnapshot()
|
||||
# except Exception as e:
|
||||
# pyfalog.critical("Failed to make snapshot")
|
||||
# pyfalog.critical(e)
|
||||
# event.Skip()
|
||||
if self and not self.IsShown():
|
||||
try:
|
||||
self.MakeSnapshot()
|
||||
except Exception as e:
|
||||
pyfalog.critical("Failed to make snapshot")
|
||||
pyfalog.critical(e)
|
||||
event.Skip()
|
||||
|
||||
def Snapshot(self):
|
||||
return self.FVsnapshot
|
||||
|
||||
# noinspection PyPropertyAccess
|
||||
def MakeSnapshot(self, maxColumns=1337):
|
||||
|
||||
if self.FVsnapshot:
|
||||
del self.FVsnapshot
|
||||
|
||||
tbmp = wx.Bitmap(16, 16)
|
||||
tdc = wx.MemoryDC()
|
||||
tdc.SelectObject(tbmp)
|
||||
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
||||
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
||||
tdc.SetFont(font)
|
||||
|
||||
columnsWidths = []
|
||||
@@ -726,6 +726,7 @@ class FittingView(d.Display):
|
||||
except Exception as e:
|
||||
pyfalog.critical("Failed to get fit")
|
||||
pyfalog.critical(e)
|
||||
return
|
||||
|
||||
if fit is None:
|
||||
return
|
||||
|
||||
@@ -30,7 +30,7 @@ from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenu
|
||||
import gui.globalEvents as GE
|
||||
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
||||
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
|
||||
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator, TextEntryValidatedDialog
|
||||
from service.fit import Fit
|
||||
from service.character import Character
|
||||
from service.network import AuthenticationError, TimeoutError
|
||||
@@ -42,9 +42,21 @@ from wx.lib.agw.floatspin import FloatSpin
|
||||
|
||||
from gui.utils.clipboard import toClipboard, fromClipboard
|
||||
|
||||
import roman
|
||||
import re
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
def arabicOrRomanToInt(s):
|
||||
m = re.match(r'\d+$', s)
|
||||
if m:
|
||||
i = int(s)
|
||||
else:
|
||||
i = roman.fromRoman(s)
|
||||
return i
|
||||
|
||||
|
||||
class CharacterTextValidor(BaseValidator):
|
||||
def __init__(self):
|
||||
BaseValidator.__init__(self)
|
||||
@@ -53,14 +65,14 @@ class CharacterTextValidor(BaseValidator):
|
||||
return CharacterTextValidor()
|
||||
|
||||
def Validate(self, win):
|
||||
entityEditor = win.parent
|
||||
textCtrl = self.GetWindow()
|
||||
text = textCtrl.GetValue().strip()
|
||||
sChar = Character.getInstance()
|
||||
|
||||
try:
|
||||
if len(text) == 0:
|
||||
raise ValueError("You must supply a name for the Character!")
|
||||
elif text in [x.name for x in entityEditor.choices]:
|
||||
elif text in [x.name for x in sChar.getCharacterList()]:
|
||||
raise ValueError("Character name already in use, please choose another.")
|
||||
|
||||
return True
|
||||
@@ -230,8 +242,8 @@ class CharacterEditor(wx.Frame):
|
||||
|
||||
def saveCharAs(self, event):
|
||||
char = self.entityEditor.getActiveEntity()
|
||||
dlg = SaveCharacterAs(self, char.ID)
|
||||
dlg.ShowModal()
|
||||
self.SaveCharacterAs(self, char.ID)
|
||||
wx.PostEvent(self, GE.CharListUpdated())
|
||||
|
||||
def revertChar(self, event):
|
||||
sChr = Character.getInstance()
|
||||
@@ -273,6 +285,22 @@ class CharacterEditor(wx.Frame):
|
||||
|
||||
wx.Frame.Destroy(self)
|
||||
|
||||
@staticmethod
|
||||
def SaveCharacterAs(parent, charID):
|
||||
sChar = Character.getInstance()
|
||||
name = sChar.getCharName(charID)
|
||||
|
||||
dlg = TextEntryValidatedDialog(parent, CharacterTextValidor,
|
||||
"Enter a name for your new Character:",
|
||||
"Save Character As...")
|
||||
dlg.SetValue("{} Copy".format(name))
|
||||
dlg.txtctrl.SetInsertionPointEnd()
|
||||
dlg.CenterOnParent()
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
sChar = Character.getInstance()
|
||||
return sChar.saveCharacterAs(charID, dlg.txtctrl.GetValue().strip())
|
||||
|
||||
|
||||
class SkillTreeView(wx.Panel):
|
||||
def __init__(self, parent):
|
||||
@@ -319,8 +347,8 @@ class SkillTreeView(wx.Panel):
|
||||
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
tree.SetImageList(self.imageList)
|
||||
self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui"))
|
||||
self.skillBookDirtyImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small_red", "gui"))
|
||||
self.skillBookImageId = self.imageList.Add(wx.Icon(BitmapLoader.getBitmap("skill_small", "gui")))
|
||||
self.skillBookDirtyImageId = self.imageList.Add(wx.Icon(BitmapLoader.getBitmap("skill_small_red", "gui")))
|
||||
|
||||
tree.AppendColumn("Skill")
|
||||
tree.AppendColumn("Level")
|
||||
@@ -414,7 +442,8 @@ class SkillTreeView(wx.Panel):
|
||||
lines = text.splitlines()
|
||||
|
||||
for l in lines:
|
||||
skill, level = l.strip()[:-1].strip(), int(l.strip()[-1])
|
||||
s = l.strip()
|
||||
skill, level = s.rsplit(None, 1)[0], arabicOrRomanToInt(s.rsplit(None, 1)[1])
|
||||
skill = char.getSkill(skill)
|
||||
if skill:
|
||||
skill.setLevel(level, ignoreRestrict=True)
|
||||
@@ -868,36 +897,6 @@ class APIView(wx.Panel):
|
||||
self.stStatus.SetLabel("Unable to retrieve {}\'s skills. Error message:\n{}".format(charName, exc_obj))
|
||||
|
||||
|
||||
class SaveCharacterAs(wx.Dialog):
|
||||
def __init__(self, parent, charID):
|
||||
wx.Dialog.__init__(self, parent, title="Save Character As...", size=wx.Size(300, 60))
|
||||
self.charID = charID
|
||||
self.parent = parent
|
||||
sChar = Character.getInstance()
|
||||
name = sChar.getCharName(charID)
|
||||
bSizer1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, name, style=wx.TE_PROCESS_ENTER)
|
||||
|
||||
bSizer1.Add(self.input, 1, wx.ALL, 5)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.change)
|
||||
self.button = wx.Button(self, wx.ID_OK, "Save")
|
||||
bSizer1.Add(self.button, 0, wx.ALL, 5)
|
||||
|
||||
self.SetSizer(bSizer1)
|
||||
self.Layout()
|
||||
self.Centre(wx.BOTH)
|
||||
self.button.Bind(wx.EVT_BUTTON, self.change)
|
||||
|
||||
def change(self, event):
|
||||
sChar = Character.getInstance()
|
||||
sChar.saveCharacterAs(self.charID, self.input.GetLineText(0))
|
||||
wx.PostEvent(self.parent, GE.CharListUpdated())
|
||||
|
||||
event.Skip()
|
||||
self.Close()
|
||||
|
||||
|
||||
class SecStatusDialog(wx.Dialog):
|
||||
|
||||
def __init__(self, parent, sec):
|
||||
|
||||
@@ -97,7 +97,7 @@ class CharacterSelection(wx.Panel):
|
||||
grantItem = menu.Append(wx.ID_ANY, "Grant Missing Skills")
|
||||
self.Bind(wx.EVT_MENU, self.grantMissingSkills, grantItem)
|
||||
|
||||
exportItem = menu.Append(wx.ID_ANY, "Export Missing Skills")
|
||||
exportItem = menu.Append(wx.ID_ANY, "Copy Missing Skills")
|
||||
self.Bind(wx.EVT_MENU, self.exportSkills, exportItem)
|
||||
|
||||
self.PopupMenu(menu, pos)
|
||||
|
||||
@@ -19,7 +19,7 @@ import wx.lib.newevent
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.utils import draw
|
||||
from gui.utils import color as color_utils
|
||||
|
||||
from service.fit import Fit
|
||||
|
||||
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
|
||||
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
|
||||
@@ -1057,6 +1057,10 @@ class _TabsContainer(wx.Panel):
|
||||
Checks to see if we have a tab preview and sets up the timer for it
|
||||
to display
|
||||
"""
|
||||
sFit = Fit.getInstance()
|
||||
if not sFit.serviceFittingOptions["showTooltip"] or False:
|
||||
return
|
||||
|
||||
if self.preview_timer:
|
||||
if self.preview_timer.IsRunning():
|
||||
if self.preview_wnd:
|
||||
@@ -1286,6 +1290,7 @@ class _TabsContainer(wx.Panel):
|
||||
if not self.preview_tab.GetSelected():
|
||||
page = self.Parent.GetPage(self.GetTabIndex(self.preview_tab))
|
||||
if page.Snapshot():
|
||||
|
||||
self.preview_wnd = PFNotebookPagePreview(
|
||||
self,
|
||||
(mposx + 3, mposy + 3),
|
||||
@@ -1373,7 +1378,7 @@ class PFNotebookPagePreview(wx.Frame):
|
||||
def OnWindowPaint(self, event):
|
||||
rect = self.GetRect()
|
||||
canvas = wx.Bitmap(rect.width, rect.height)
|
||||
mdc = wx.AudoBufferedPaintDC(self)
|
||||
mdc = wx.BufferedPaintDC(self)
|
||||
mdc.SelectObject(canvas)
|
||||
color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)
|
||||
mdc.SetBackground(wx.Brush(color))
|
||||
|
||||
@@ -170,8 +170,8 @@ class GraphFrame(wx.Frame):
|
||||
self.AppendFitToList(fitID)
|
||||
|
||||
def close(self, event):
|
||||
self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK)
|
||||
self.mainFrame.Unbind(GE.FIT_CHANGED)
|
||||
self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem)
|
||||
self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw)
|
||||
event.Skip()
|
||||
|
||||
def getView(self):
|
||||
@@ -229,6 +229,15 @@ class GraphFrame(wx.Frame):
|
||||
def draw(self, event=None):
|
||||
global mpl_version
|
||||
|
||||
if event is not None:
|
||||
event.Skip()
|
||||
|
||||
# todo: FIX THIS, see #1430. draw() is not being unbound properly when the window closes, this is an easy fix,
|
||||
# but not a proper solution
|
||||
if not self:
|
||||
pyfalog.warning("GraphFrame handled event, however GraphFrame no longer exists. Ignoring event")
|
||||
return
|
||||
|
||||
values = self.getValues()
|
||||
view = self.getView()
|
||||
self.subplot.clear()
|
||||
@@ -247,7 +256,7 @@ class GraphFrame(wx.Frame):
|
||||
|
||||
self.subplot.plot(x, y)
|
||||
legend.append(fit.name)
|
||||
except:
|
||||
except Exception as ex:
|
||||
pyfalog.warning("Invalid values in '{0}'", fit.name)
|
||||
self.SetStatusText("Invalid values in '%s'" % fit.name)
|
||||
self.canvas.draw()
|
||||
@@ -299,8 +308,6 @@ class GraphFrame(wx.Frame):
|
||||
|
||||
self.canvas.draw()
|
||||
self.SetStatusText("")
|
||||
if event is not None:
|
||||
event.Skip()
|
||||
|
||||
def onFieldChanged(self, event):
|
||||
self.draw()
|
||||
|
||||
@@ -49,7 +49,7 @@ from gui.multiSwitch import MultiSwitch
|
||||
from gui.statsPane import StatsPane
|
||||
from gui.shipBrowser import ShipBrowser
|
||||
from gui.builtinShipBrowser.events import FitSelected, ImportSelected, Stage3Selected
|
||||
from gui.characterEditor import CharacterEditor, SaveCharacterAs
|
||||
from gui.characterEditor import CharacterEditor
|
||||
from gui.characterSelection import CharacterSelection
|
||||
from gui.patternEditor import DmgPatternEditorDlg
|
||||
from gui.resistsEditor import ResistsEditorDlg
|
||||
@@ -358,7 +358,7 @@ class MainFrame(wx.Frame):
|
||||
def ShowAboutBox(self, evt):
|
||||
info = wx.adv.AboutDialogInfo()
|
||||
info.Name = "pyfa"
|
||||
info.Version = config.getGitVersion() # gui.aboutData.versionString
|
||||
info.Version = config.getVersion() # gui.aboutData.versionString
|
||||
#
|
||||
# try:
|
||||
# import matplotlib
|
||||
@@ -683,8 +683,8 @@ class MainFrame(wx.Frame):
|
||||
|
||||
def saveCharAs(self, event):
|
||||
charID = self.charSelection.getActiveCharacter()
|
||||
dlg = SaveCharacterAs(self, charID)
|
||||
dlg.ShowModal()
|
||||
CharacterEditor.SaveCharacterAs(self, charID)
|
||||
wx.PostEvent(self, GE.CharListUpdated())
|
||||
|
||||
def revertChar(self, event):
|
||||
sChr = Character.getInstance()
|
||||
@@ -960,7 +960,7 @@ class MainFrame(wx.Frame):
|
||||
if len(fits) > 0:
|
||||
if len(fits) == 1:
|
||||
fit = fits[0]
|
||||
wx.PostEvent(self, FitSelected(fitID=fit.ID))
|
||||
wx.PostEvent(self, FitSelected(fitID=fit.ID, from_import=True))
|
||||
wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True))
|
||||
else:
|
||||
fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name))
|
||||
|
||||
@@ -38,7 +38,7 @@ class PreferenceDialog(wx.Dialog):
|
||||
# self.listview.SetSize((500, -1))
|
||||
|
||||
self.imageList = wx.ImageList(32, 32)
|
||||
self.listbook.SetImageList(self.imageList)
|
||||
self.listbook.AssignImageList(self.imageList)
|
||||
|
||||
mainSizer.Add(self.listbook, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 5)
|
||||
|
||||
@@ -55,17 +55,17 @@ class PreferenceDialog(wx.Dialog):
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
for prefView in PreferenceView.views:
|
||||
page = wx.Panel(self.listbook)
|
||||
page = wx.ScrolledWindow(self.listbook)
|
||||
page.SetScrollbars(1, 1, 20, 20)
|
||||
bmp = prefView.getImage()
|
||||
if bmp:
|
||||
imgID = self.imageList.Add(bmp)
|
||||
else:
|
||||
imgID = -1
|
||||
prefView.populatePanel(page)
|
||||
|
||||
self.listbook.AddPage(page, prefView.title, imageId=imgID)
|
||||
|
||||
# Set the height based on a condition. Can all the panels fit in the current height?
|
||||
# If not, use the .GetBestVirtualSize() to ensure that all content is available.
|
||||
minHeight = 550
|
||||
bestFit = self.GetBestVirtualSize()
|
||||
if minHeight > bestFit[1]:
|
||||
|
||||
@@ -102,9 +102,11 @@ class AttributeEditor(wx.Frame):
|
||||
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
path = dlg.GetPath()
|
||||
with open(path, 'rb') as csvfile:
|
||||
with open(path, 'r') as csvfile:
|
||||
spamreader = csv.reader(csvfile)
|
||||
for row in spamreader:
|
||||
if len(row) == 0: # csvwriter seems to added blank lines to the end sometimes
|
||||
continue
|
||||
itemID, attrID, value = row
|
||||
item = getItem(int(itemID))
|
||||
attr = getAttributeInfo(int(attrID))
|
||||
@@ -123,7 +125,7 @@ class AttributeEditor(wx.Frame):
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
path = dlg.GetPath()
|
||||
with open(path, 'wb') as csvfile:
|
||||
with open(path, 'w', encoding='utf-8') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
for item in items:
|
||||
for key, override in item.overrides.items():
|
||||
|
||||
@@ -18,6 +18,7 @@ import wx
|
||||
|
||||
from gui.utils import color as color_utils
|
||||
from gui.utils import draw, anim_effects
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class PyGauge(wx.Window):
|
||||
@@ -129,11 +130,16 @@ class PyGauge(wx.Window):
|
||||
return self._max_range
|
||||
|
||||
def Animate(self):
|
||||
if not self._timer:
|
||||
self._timer = wx.Timer(self, self._timer_id)
|
||||
sFit = Fit.getInstance()
|
||||
if sFit.serviceFittingOptions["enableGaugeAnimation"]:
|
||||
if not self._timer:
|
||||
self._timer = wx.Timer(self, self._timer_id)
|
||||
|
||||
self._anim_step = 0
|
||||
self._timer.Start(self._period)
|
||||
self._anim_step = 0
|
||||
self._timer.Start(self._period)
|
||||
else:
|
||||
self._anim_value = self._percentage
|
||||
self.Refresh()
|
||||
|
||||
def SetRange(self, range, reinit=False, animate=True):
|
||||
"""
|
||||
|
||||
@@ -62,6 +62,7 @@ class UpdateDialog(wx.Dialog):
|
||||
self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow)
|
||||
|
||||
link_patterns = [
|
||||
(re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"),
|
||||
(re.compile("#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"),
|
||||
(re.compile("@(\w+)", re.I), r"https://github.com/\1")
|
||||
]
|
||||
|
||||
@@ -63,12 +63,14 @@ class exportHtmlThread(threading.Thread):
|
||||
HTML = self.generateFullHTML(sMkt, sFit, dnaUrl)
|
||||
|
||||
try:
|
||||
FILE = open(settings.getPath(), "w")
|
||||
FILE.write(HTML.encode('utf-8'))
|
||||
FILE = open(settings.getPath(), "w", encoding='utf-8')
|
||||
FILE.write(HTML)
|
||||
FILE.close()
|
||||
except IOError:
|
||||
except IOError as ex:
|
||||
print(("Failed to write to " + settings.getPath()))
|
||||
pass
|
||||
except Exception as ex:
|
||||
pass
|
||||
|
||||
if self.callback:
|
||||
wx.CallAfter(self.callback, -1)
|
||||
@@ -84,6 +86,7 @@ class exportHtmlThread(threading.Thread):
|
||||
<head>
|
||||
<title>Pyfa Fittings</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8" />
|
||||
<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>
|
||||
|
||||
BIN
imgs/icons/101_4.png
Normal file
BIN
imgs/icons/101_4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 878 B |
Binary file not shown.
|
Before Width: | Height: | Size: 783 B After Width: | Height: | Size: 791 B |
Binary file not shown.
|
Before Width: | Height: | Size: 800 B After Width: | Height: | Size: 800 B |
BIN
imgs/icons/assaultdamagecontrol.png
Normal file
BIN
imgs/icons/assaultdamagecontrol.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 767 B |
8
pyfa.py
8
pyfa.py
@@ -95,10 +95,6 @@ if __name__ == "__main__":
|
||||
if options.rootsavedata is True:
|
||||
config.saveInRoot = True
|
||||
|
||||
# set title if it wasn't supplied by argument
|
||||
if options.title is None:
|
||||
options.title = "pyfa %s%s - Python Fitting Assistant" % (config.version, "" if config.tag.lower() != 'git' else " (git)")
|
||||
|
||||
config.debug = options.debug
|
||||
config.loggingLevel = config.LOGLEVEL_MAP.get(options.logginglevel.lower(), config.LOGLEVEL_MAP['error'])
|
||||
config.defPaths(options.savepath)
|
||||
@@ -131,6 +127,10 @@ if __name__ == "__main__":
|
||||
|
||||
from gui.mainFrame import MainFrame
|
||||
|
||||
# set title if it wasn't supplied by argument
|
||||
if options.title is None:
|
||||
options.title = "pyfa %s - Python Fitting Assistant" % (config.getVersion())
|
||||
|
||||
pyfa = wx.App(False)
|
||||
mf = MainFrame(options.title)
|
||||
ErrorHandler.SetParent(mf)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
wxPython >= 4.0.1
|
||||
wxPython == 4.0.0b2
|
||||
logbook >= 1.0.0
|
||||
matplotlib >= 2.0.0
|
||||
python-dateutil
|
||||
@@ -6,4 +6,6 @@ requests >= 2.0.0
|
||||
sqlalchemy >= 1.0.5
|
||||
esipy == 0.3.0
|
||||
markdown2
|
||||
packaging
|
||||
packaging
|
||||
roman
|
||||
beautifulsoup4
|
||||
@@ -208,7 +208,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True):
|
||||
|
||||
for cursor, dictionary in ((old_cursor, old_itmdata), (new_cursor, new_itmdata)):
|
||||
# Compose list of items we're interested in, filtered by category
|
||||
query = 'SELECT it.typeID, it.groupID FROM invtypes AS it INNER JOIN invgroups AS ig ON it.groupID = ig.groupID INNER JOIN invcategories AS ic ON ig.categoryID = ic.categoryID WHERE it.published = 1 AND ic.categoryName IN ("Ship", "Module", "Charge", "Skill", "Drone", "Implant", "Subsystem")'
|
||||
query = 'SELECT it.typeID, it.groupID FROM invtypes AS it INNER JOIN invgroups AS ig ON it.groupID = ig.groupID INNER JOIN invcategories AS ic ON ig.categoryID = ic.categoryID WHERE it.published = 1 AND ic.categoryName IN ("Ship", "Module", "Charge", "Skill", "Drone", "Implant", "Subsystem", "Structure", "Structure Module", "Fighter")'
|
||||
cursor.execute(query)
|
||||
for row in cursor:
|
||||
itemid = row[0]
|
||||
|
||||
@@ -108,6 +108,7 @@ class SkillBackupThread(threading.Thread):
|
||||
def run(self):
|
||||
path = self.path
|
||||
sCharacter = Character.getInstance()
|
||||
|
||||
if self.saveFmt == "xml" or self.saveFmt == "emp":
|
||||
backupData = sCharacter.exportXml()
|
||||
else:
|
||||
@@ -115,14 +116,13 @@ class SkillBackupThread(threading.Thread):
|
||||
|
||||
if self.saveFmt == "emp":
|
||||
with gzip.open(path, mode='wb') as backupFile:
|
||||
backupFile.write(backupData)
|
||||
backupFile.write(backupData.encode())
|
||||
else:
|
||||
with open(path, mode='w', encoding='utf-8') as backupFile:
|
||||
backupFile.write(backupData)
|
||||
|
||||
wx.CallAfter(self.callback)
|
||||
|
||||
|
||||
class Character(object):
|
||||
instance = None
|
||||
skillReqsDict = {}
|
||||
@@ -246,6 +246,7 @@ class Character(object):
|
||||
|
||||
# revert old char
|
||||
char.revertLevels()
|
||||
return newChar.ID
|
||||
|
||||
@staticmethod
|
||||
def revertCharacter(charID):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user