Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b5c95213f | ||
|
|
1312177e0e | ||
|
|
5b0df0a9c3 | ||
|
|
1ea6136cb2 | ||
|
|
da2b6bbb6d | ||
|
|
87007af5f8 | ||
|
|
dd5ab872b1 | ||
|
|
65fb46885a | ||
|
|
fad1e401b2 | ||
|
|
d59f8afd8a | ||
|
|
7c42d00219 | ||
|
|
777ba69ac4 | ||
|
|
9a3bde872b | ||
|
|
01354a0a83 | ||
|
|
8c3b8589d5 | ||
|
|
451b5d4312 | ||
|
|
d204e70afc | ||
|
|
ce9ce17ad2 | ||
|
|
6a7cdda91a | ||
|
|
f38c61da51 | ||
|
|
735827a25b | ||
|
|
d3fcdcbe47 | ||
|
|
a3dce73663 | ||
|
|
594b58388f | ||
|
|
fa6be2edfb | ||
|
|
1177968b02 | ||
|
|
c04b370410 | ||
|
|
038b95531d | ||
|
|
56bc5c3376 | ||
|
|
f5d8be7861 | ||
|
|
5366c23db2 | ||
|
|
d147db22f1 | ||
|
|
28ebcd2739 | ||
|
|
2f57eb6ea6 | ||
|
|
3a7ee4699e | ||
|
|
fa7f991fae | ||
|
|
0be8b429aa | ||
|
|
7d94e2de7d | ||
|
|
6d32db5827 | ||
|
|
0fbb318d8a | ||
|
|
cd013e8287 | ||
|
|
e667453c1e | ||
|
|
24e2db0f88 | ||
|
|
3c47f8c6bb | ||
|
|
42c2b3f253 | ||
|
|
71d6830ac0 | ||
|
|
ac2fb01a7c | ||
|
|
fc66c212fc | ||
|
|
9625fafda7 | ||
|
|
6b1aca4306 | ||
|
|
fd5a094304 | ||
|
|
a3142ff62f | ||
|
|
58ebfd3643 | ||
|
|
e272267842 | ||
|
|
b0256542bc | ||
|
|
9b8fd24987 | ||
|
|
9c68889a6d | ||
|
|
a4ed6e8066 | ||
|
|
9fe8163a69 | ||
|
|
9f9b496726 | ||
|
|
3b8fa68a4c | ||
|
|
9f5a649b04 | ||
|
|
fb45736c14 | ||
|
|
b8a58fc7a6 | ||
|
|
b04c521805 | ||
|
|
526695fa9a | ||
|
|
d6be77d107 | ||
|
|
907da343b1 | ||
|
|
3fadccc715 | ||
|
|
82a912858f | ||
|
|
cc1fdddc0a | ||
|
|
e314ecf887 | ||
|
|
94b1c8b029 | ||
|
|
6e3b7ff132 | ||
|
|
ff14855808 | ||
|
|
4761857b84 | ||
|
|
bb53e75bfe | ||
|
|
be85080d8d | ||
|
|
27101732ca | ||
|
|
ed48a8b5d0 | ||
|
|
5a6fe373f1 | ||
|
|
3c6eb6d054 | ||
|
|
dc997f0dc4 | ||
|
|
14afc83e86 | ||
|
|
216dd2a787 |
@@ -2,7 +2,7 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.7
|
||||
- Python 3.11
|
||||
- Git CLI installed
|
||||
- Python, pip and git are all available as command-line commands (add to the path if needed)
|
||||
|
||||
|
||||
12
config.py
@@ -9,6 +9,7 @@ import hashlib
|
||||
from eos.const import FittingSlot
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
from collections import namedtuple
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -44,9 +45,16 @@ experimentalFeatures = None
|
||||
version = None
|
||||
language = None
|
||||
|
||||
API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746'
|
||||
ApiServer = namedtuple('ApiBase', ['name', 'sso', 'esi', 'client_id', 'callback', 'supports_auto_login'])
|
||||
supported_servers = {
|
||||
"Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback', True),
|
||||
# No point having SISI: https://developers.eveonline.com/blog/article/removing-datasource-singularity
|
||||
# "Singularity": ApiServer("Singularity", "sisilogin.testeveonline.com", "esi.evetech.net", 'b9c3cc79448f449ab17f3aebd018842e', 'https://pyfa-org.github.io/Pyfa/callback'),
|
||||
"Serenity": ApiServer("Serenity", "login.evepc.163.com", "ali-esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://ali-esi.evepc.163.com/ui/oauth2-redirect.html', False)
|
||||
}
|
||||
|
||||
SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff'
|
||||
ESI_CACHE = 'esi_cache'
|
||||
SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback'
|
||||
|
||||
LOGLEVEL_MAP = {
|
||||
"critical": CRITICAL,
|
||||
|
||||
@@ -33,6 +33,8 @@ AppDir:
|
||||
- libwebkit2gtk-4.0-37 # Needed for wx's HTML lib
|
||||
# Unknown
|
||||
- libpcre2-32-0 # https://github.com/pyfa-org/Pyfa/issues/2572
|
||||
- libnotify4 # https://github.com/pyfa-org/Pyfa/issues/2598
|
||||
- libwayland-client0 # https://github.com/pyfa-org/Pyfa/issues/2600
|
||||
exclude:
|
||||
- hicolor-icon-theme
|
||||
- humanity-icon-theme
|
||||
|
||||
@@ -10,10 +10,15 @@
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
36
eos/db/migrations/upgrade47.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
Migration 46
|
||||
|
||||
- add support for server selection for SSO characters
|
||||
"""
|
||||
import sqlalchemy
|
||||
|
||||
tmpTable = """
|
||||
CREATE TABLE ssoCharacterTemp (
|
||||
ID INTEGER NOT NULL,
|
||||
client VARCHAR NOT NULL,
|
||||
characterID INTEGER NOT NULL,
|
||||
characterName VARCHAR NOT NULL,
|
||||
refreshToken VARCHAR NOT NULL,
|
||||
accessToken VARCHAR NOT NULL,
|
||||
accessTokenExpires DATETIME NOT NULL,
|
||||
created DATETIME,
|
||||
modified DATETIME,
|
||||
server VARCHAR,
|
||||
PRIMARY KEY (ID),
|
||||
CONSTRAINT "uix_client_server_characterID" UNIQUE (client, server, characterID),
|
||||
CONSTRAINT "uix_client_server_characterName" UNIQUE (client, server, characterName)
|
||||
)
|
||||
"""
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
try:
|
||||
saveddata_engine.execute("SELECT server FROM ssoCharacter LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute(tmpTable)
|
||||
saveddata_engine.execute(
|
||||
"INSERT INTO ssoCharacterTemp (ID, client, characterID, characterName, refreshToken, accessToken, accessTokenExpires, created, modified, server) "
|
||||
"SELECT ID, client, characterID, characterName, refreshToken, accessToken, accessTokenExpires, created, modified, 'Tranquility' "
|
||||
"FROM ssoCharacter")
|
||||
saveddata_engine.execute("DROP TABLE ssoCharacter")
|
||||
saveddata_engine.execute("ALTER TABLE ssoCharacterTemp RENAME TO ssoCharacter")
|
||||
@@ -40,18 +40,18 @@ characters_table = Table("characters", saveddata_meta,
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
|
||||
|
||||
sso_table = Table("ssoCharacter", saveddata_meta,
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("client", String, nullable=False),
|
||||
Column("characterID", Integer, nullable=False),
|
||||
Column("characterName", String, nullable=False),
|
||||
Column("refreshToken", String, nullable=False),
|
||||
Column("accessToken", String, nullable=False),
|
||||
Column("accessTokenExpires", DateTime, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
UniqueConstraint('client', 'characterID', name='uix_client_characterID'),
|
||||
UniqueConstraint('client', 'characterName', name='uix_client_characterName')
|
||||
)
|
||||
Column("ID", Integer, primary_key=True),
|
||||
Column("client", String, nullable=False),
|
||||
Column("characterID", Integer, nullable=False),
|
||||
Column("characterName", String, nullable=False),
|
||||
Column("server", String, nullable=False),
|
||||
Column("refreshToken", String, nullable=False),
|
||||
Column("accessToken", String, nullable=False),
|
||||
Column("accessTokenExpires", DateTime, nullable=False),
|
||||
Column("created", DateTime, nullable=True, default=datetime.datetime.now),
|
||||
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
|
||||
UniqueConstraint('client', 'server', 'characterID', name='uix_client_server_characterID'),
|
||||
UniqueConstraint('client', 'server', 'characterName', name='uix_client_server_characterName'))
|
||||
|
||||
sso_character_map_table = Table("ssoCharacterMap", saveddata_meta,
|
||||
Column("characterID", ForeignKey("characters.ID"), primary_key=True),
|
||||
|
||||
@@ -493,9 +493,12 @@ def getSsoCharacters(clientHash, eager=None):
|
||||
|
||||
|
||||
@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash")
|
||||
def getSsoCharacter(lookfor, clientHash, eager=None):
|
||||
def getSsoCharacter(lookfor, clientHash, server=None, eager=None):
|
||||
filter = SsoCharacter.client == clientHash
|
||||
|
||||
if server is not None:
|
||||
filter = and_(filter, SsoCharacter.server == server)
|
||||
|
||||
if isinstance(lookfor, int):
|
||||
filter = and_(filter, SsoCharacter.ID == lookfor)
|
||||
elif isinstance(lookfor, str):
|
||||
|
||||
493
eos/effects.py
@@ -396,7 +396,7 @@ class Effect59(BaseEffect):
|
||||
cargoCapacityMultiply
|
||||
|
||||
Used by:
|
||||
Modules from group: Expanded Cargohold (7 of 7)
|
||||
Modules from group: Expanded Cargohold (8 of 8)
|
||||
Modules from group: Overdrive Injector System (7 of 7)
|
||||
Modules from group: Reinforced Bulkhead (8 of 8)
|
||||
"""
|
||||
@@ -966,8 +966,8 @@ class Effect272(BaseEffect):
|
||||
|
||||
Used by:
|
||||
Implants named like: Inherent Implants 'Noble' Repair Systems RS (6 of 6)
|
||||
Implants named like: Repairer Booster (4 of 4)
|
||||
Modules named like: Nanobot Accelerator (8 of 8)
|
||||
Implant: AIR Repairer Booster III
|
||||
Implant: Numon Family Heirloom
|
||||
Skill: Repair Systems
|
||||
"""
|
||||
@@ -1060,6 +1060,7 @@ class Effect290(BaseEffect):
|
||||
Used by:
|
||||
Implants named like: Frentix Booster (4 of 4)
|
||||
Implants named like: Halcyon B Booster (5 of 5)
|
||||
Implants named like: SoCT Turret Booster (3 of 3)
|
||||
Implants named like: Zainou 'Deadeye' Sharpshooter ST (6 of 6)
|
||||
Skill: Sharpshooter
|
||||
"""
|
||||
@@ -1169,6 +1170,7 @@ class Effect394(BaseEffect):
|
||||
Implant: AIR Overclocker Booster II
|
||||
Implant: Quafe Zero Classic
|
||||
Implant: Serenity YC122.9 Season Booster - Max Velocity
|
||||
Implant: Starsi Blast! Classic
|
||||
Implant: Wisdom of Gheinok
|
||||
Skill: Navigation
|
||||
"""
|
||||
@@ -1190,17 +1192,18 @@ class Effect395(BaseEffect):
|
||||
|
||||
Used by:
|
||||
Modules from group: Rig Anchor (4 of 4)
|
||||
Implants named like: Agility Booster (4 of 4)
|
||||
Implants named like: Eifyr and Co. 'Rogue' Evasive Maneuvering EM (6 of 6)
|
||||
Implants named like: Grand Prix Booster (4 of 6)
|
||||
Implants named like: Halcyon G Booster (5 of 5)
|
||||
Implants named like: Halcyon Y Booster (5 of 5)
|
||||
Implants named like: grade Nomad (10 of 12)
|
||||
Modules named like: Low Friction Nozzle Joints (8 of 8)
|
||||
Implant: AIR Agility Booster II
|
||||
Implant: AIR Overclocker Booster III
|
||||
Implant: Genolution Core Augmentation CA-4
|
||||
Implant: Quafe Zero Green Apple
|
||||
Implant: Serenity YC122.9 Season Booster - Agility
|
||||
Implant: Starsi Blast! Classic
|
||||
Skill: Evasive Maneuvering
|
||||
Skill: Spaceship Command
|
||||
"""
|
||||
@@ -1335,6 +1338,7 @@ class Effect485(BaseEffect):
|
||||
Implant: Basic Capsuleer Engineering Augmentation Chip
|
||||
Implant: Genolution Core Augmentation CA-2
|
||||
Implant: Quafe Zero Green Apple
|
||||
Implant: Starsi Blast! Orange
|
||||
Skill: Capacitor Systems Operation
|
||||
"""
|
||||
|
||||
@@ -1771,6 +1775,7 @@ class Effect584(BaseEffect):
|
||||
Implants named like: 'Pyrolancea' Dose (7 of 7)
|
||||
Implants named like: Eifyr and Co. 'Gunslinger' Surgical Strike SS (6 of 6)
|
||||
Implants named like: Halcyon Y Booster (5 of 5)
|
||||
Implants named like: SoCT Turret Booster (3 of 3)
|
||||
Implant: AIR Pyrolancea Booster II
|
||||
Implant: Standard Cerebral Accelerator
|
||||
"""
|
||||
@@ -2153,9 +2158,11 @@ class Effect699(BaseEffect):
|
||||
Used by:
|
||||
Implants named like: Halcyon B Booster (5 of 5)
|
||||
Implants named like: Halcyon R Booster (5 of 5)
|
||||
Implants named like: SoCT Scan Booster (3 of 3)
|
||||
Implants named like: Zainou 'Gypsy' Signature Analysis SA (6 of 6)
|
||||
Modules named like: Targeting System Subcontroller (8 of 8)
|
||||
Implant: Quafe Zero Classic
|
||||
Implant: Starsi Blast! Orange
|
||||
Skill: Signature Analysis
|
||||
"""
|
||||
|
||||
@@ -2502,6 +2509,7 @@ class Effect856(BaseEffect):
|
||||
Implants named like: Grand Prix Booster (5 of 6)
|
||||
Implants named like: Halcyon B Booster (5 of 5)
|
||||
Implants named like: Serenity Limited 'Overclocker' Dose (3 of 3)
|
||||
Implants named like: SoCT Agility Booster (3 of 3)
|
||||
Implants named like: grade Ascendancy (10 of 12)
|
||||
Modules named like: Hyperspatial Velocity Optimizer (8 of 8)
|
||||
Implant: Serenity YC122.9 Season Booster - Warp Speed
|
||||
@@ -4817,7 +4825,7 @@ class Effect1615(BaseEffect):
|
||||
shipAdvancedSpaceshipCommandAgilityBonus
|
||||
|
||||
Used by:
|
||||
Items from market group: Ships > Capital Ships (50 of 50)
|
||||
Items from market group: Ships > Capital Ships (51 of 51)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -4890,8 +4898,8 @@ class Effect1635(BaseEffect):
|
||||
capitalRepairSystemsSkillDurationBonus
|
||||
|
||||
Used by:
|
||||
Implants named like: Repairer Booster (4 of 4)
|
||||
Modules named like: Nanobot Accelerator (8 of 8)
|
||||
Implant: AIR Repairer Booster III
|
||||
Skill: Capital Repair Systems
|
||||
"""
|
||||
|
||||
@@ -4955,9 +4963,7 @@ class Effect1644(BaseEffect):
|
||||
skirmishCommandMindlink
|
||||
|
||||
Used by:
|
||||
Implant: Federation Navy Command Mindlink
|
||||
Implant: Republic Fleet Command Mindlink
|
||||
Implant: Skirmish Command Mindlink
|
||||
Implants from group: Cyber Leadership (4 of 11)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -4981,7 +4987,7 @@ class Effect1645(BaseEffect):
|
||||
shieldCommandMindlink
|
||||
|
||||
Used by:
|
||||
Implants from group: Cyber Leadership (4 of 10)
|
||||
Implants from group: Cyber Leadership (4 of 11)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -5005,9 +5011,7 @@ class Effect1646(BaseEffect):
|
||||
informationCommandMindlink
|
||||
|
||||
Used by:
|
||||
Implant: Caldari Navy Command Mindlink
|
||||
Implant: Imperial Navy Command Mindlink
|
||||
Implant: Information Command Mindlink
|
||||
Implants from group: Cyber Leadership (4 of 11)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -6737,7 +6741,7 @@ class Effect2252(BaseEffect):
|
||||
|
||||
Used by:
|
||||
Ships from group: Black Ops (5 of 5)
|
||||
Ships from group: Blockade Runner (4 of 4)
|
||||
Ships from group: Blockade Runner (5 of 5)
|
||||
Ships from group: Covert Ops (8 of 8)
|
||||
Ships from group: Expedition Frigate (2 of 2)
|
||||
Ships from group: Force Recon Ship (9 of 9)
|
||||
@@ -6786,8 +6790,8 @@ class Effect2255(BaseEffect):
|
||||
tractorBeamCan
|
||||
|
||||
Used by:
|
||||
Deployables from group: Mobile Tractor Unit (4 of 4)
|
||||
Modules from group: Tractor Beam (4 of 4)
|
||||
Deployables from group: Mobile Tractor Unit (5 of 5)
|
||||
Modules from group: Tractor Beam (6 of 6)
|
||||
"""
|
||||
|
||||
type = 'active'
|
||||
@@ -6996,6 +7000,7 @@ class Effect2432(BaseEffect):
|
||||
Implants named like: Halcyon Y Booster (5 of 5)
|
||||
Implants named like: Inherent Implants 'Squire' Capacitor Management EM (6 of 6)
|
||||
Implants named like: Mindflood Booster (4 of 4)
|
||||
Implants named like: SoCT Capacitor Booster (3 of 3)
|
||||
Modules named like: Semiconductor Memory Cell (8 of 8)
|
||||
Implant: Antipharmakon Aeolis
|
||||
Implant: Basic Capsuleer Engineering Augmentation Chip
|
||||
@@ -9490,7 +9495,7 @@ class Effect3046(BaseEffect):
|
||||
modifyMaxVelocityOfShipPassive
|
||||
|
||||
Used by:
|
||||
Modules from group: Expanded Cargohold (7 of 7)
|
||||
Modules from group: Expanded Cargohold (8 of 8)
|
||||
"""
|
||||
|
||||
type = 'offline'
|
||||
@@ -9505,7 +9510,7 @@ class Effect3047(BaseEffect):
|
||||
structureHPMultiplyPassive
|
||||
|
||||
Used by:
|
||||
Modules from group: Expanded Cargohold (7 of 7)
|
||||
Modules from group: Expanded Cargohold (8 of 8)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -13484,6 +13489,7 @@ class Effect4162(BaseEffect):
|
||||
Implants named like: Poteque 'Prospector' Astrometric Rangefinding AR (3 of 3)
|
||||
Implants named like: Poteque 'Prospector' Sharpeye (2 of 2)
|
||||
Implants named like: Serenity Limited 'Sharpeye' Dose (3 of 3)
|
||||
Implants named like: SoCT Scan Booster (3 of 3)
|
||||
Implants named like: grade Virtue (10 of 12)
|
||||
Modules named like: Gravity Capacitor Upgrade (8 of 8)
|
||||
Implant: AIR Astro-Rangefinding II Booster
|
||||
@@ -16344,7 +16350,7 @@ class Effect4921(BaseEffect):
|
||||
microJumpDrive
|
||||
|
||||
Used by:
|
||||
Modules from group: Micro Jump Drive (2 of 2)
|
||||
Modules named like: Micro Jump Drive (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'active'
|
||||
@@ -16627,8 +16633,8 @@ class Effect4967(BaseEffect):
|
||||
shieldBoosterDurationBonusShieldSkills
|
||||
|
||||
Used by:
|
||||
Implants named like: Repairer Booster (4 of 4)
|
||||
Modules named like: Core Defense Operational Solidifier (8 of 8)
|
||||
Implant: AIR Repairer Booster III
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -18657,7 +18663,7 @@ class Effect5266(BaseEffect):
|
||||
blockadeRunnerCloakCpuPercentBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Blockade Runner (4 of 4)
|
||||
Ships from group: Blockade Runner (5 of 5)
|
||||
"""
|
||||
|
||||
runTime = 'early'
|
||||
@@ -20175,6 +20181,7 @@ class Effect5437(BaseEffect):
|
||||
|
||||
Used by:
|
||||
Implants named like: Halcyon Y Booster (5 of 5)
|
||||
Implants named like: SoCT Relic Coherence Booster (3 of 3)
|
||||
Modules named like: Emission Scope Sharpener (8 of 8)
|
||||
Implant: Poteque 'Prospector' Archaeology AC-905
|
||||
Implant: Poteque 'Prospector' Environmental Analysis EY-1005
|
||||
@@ -22423,7 +22430,7 @@ class Effect5869(BaseEffect):
|
||||
eliteIndustrialWarpSpeedBonus1
|
||||
|
||||
Used by:
|
||||
Ships from group: Blockade Runner (4 of 4)
|
||||
Ships from group: Blockade Runner (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22505,7 +22512,7 @@ class Effect5874(BaseEffect):
|
||||
eliteIndustrialFleetCapacity1
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22521,8 +22528,7 @@ class Effect5881(BaseEffect):
|
||||
eliteIndustrialShieldResists2
|
||||
|
||||
Used by:
|
||||
Ship: Bustard
|
||||
Ship: Mastodon
|
||||
Ships from group: Deep Space Transport (3 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22557,7 +22563,7 @@ class Effect5889(BaseEffect):
|
||||
eliteIndustrialABHeatBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
Ships from group: Interceptor (4 of 10)
|
||||
"""
|
||||
|
||||
@@ -22574,7 +22580,7 @@ class Effect5890(BaseEffect):
|
||||
eliteIndustrialMWDHeatBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
Ships from group: Interceptor (4 of 10)
|
||||
"""
|
||||
|
||||
@@ -22591,7 +22597,7 @@ class Effect5891(BaseEffect):
|
||||
eliteIndustrialArmorHardenerHeatBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22607,7 +22613,7 @@ class Effect5892(BaseEffect):
|
||||
eliteIndustrialReactiveArmorHardenerHeatBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22623,7 +22629,7 @@ class Effect5893(BaseEffect):
|
||||
eliteIndustrialShieldHardenerHeatBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22639,7 +22645,7 @@ class Effect5896(BaseEffect):
|
||||
eliteIndustrialShieldBoosterHeatBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22657,7 +22663,7 @@ class Effect5899(BaseEffect):
|
||||
eliteIndustrialArmorRepairHeatBonus
|
||||
|
||||
Used by:
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -22690,7 +22696,7 @@ class Effect5901(BaseEffect):
|
||||
roleBonusBulkheadCPU
|
||||
|
||||
Used by:
|
||||
Ships from group: Freighter (4 of 5)
|
||||
Ships from group: Freighter (4 of 6)
|
||||
Ships from group: Jump Freighter (4 of 4)
|
||||
"""
|
||||
|
||||
@@ -26912,8 +26918,7 @@ class Effect6431(BaseEffect):
|
||||
fighterAbilityMissiles
|
||||
|
||||
Used by:
|
||||
Items from category: Fighter (48 of 82)
|
||||
Fighters from group: Light Fighter (32 of 32)
|
||||
Items from category: Fighter (56 of 94)
|
||||
"""
|
||||
|
||||
dealsDamage = True
|
||||
@@ -26928,7 +26933,7 @@ class Effect6434(BaseEffect):
|
||||
fighterAbilityEnergyNeutralizer
|
||||
|
||||
Used by:
|
||||
Fighters named like: Cenobite (4 of 4)
|
||||
Fighters named like: Cenobite (5 of 5)
|
||||
"""
|
||||
|
||||
displayName = 'Energy Neutralizer'
|
||||
@@ -26957,7 +26962,7 @@ class Effect6435(BaseEffect):
|
||||
fighterAbilityStasisWebifier
|
||||
|
||||
Used by:
|
||||
Fighters named like: Dromi (4 of 4)
|
||||
Fighters named like: Dromi (5 of 5)
|
||||
"""
|
||||
|
||||
displayName = 'Stasis Webifier'
|
||||
@@ -26984,7 +26989,7 @@ class Effect6436(BaseEffect):
|
||||
fighterAbilityWarpDisruption
|
||||
|
||||
Used by:
|
||||
Fighters named like: Siren (4 of 4)
|
||||
Fighters named like: Siren (5 of 5)
|
||||
"""
|
||||
|
||||
displayName = 'Warp Disruption'
|
||||
@@ -27011,7 +27016,7 @@ class Effect6437(BaseEffect):
|
||||
fighterAbilityECM
|
||||
|
||||
Used by:
|
||||
Fighters named like: Scarab (4 of 4)
|
||||
Fighters named like: Scarab (5 of 5)
|
||||
"""
|
||||
|
||||
displayName = 'ECM'
|
||||
@@ -27041,7 +27046,8 @@ class Effect6439(BaseEffect):
|
||||
fighterAbilityEvasiveManeuvers
|
||||
|
||||
Used by:
|
||||
Fighters from group: Light Fighter (16 of 32)
|
||||
Fighters from group: Light Fighter (12 of 24)
|
||||
Fighters from group: Structure Light Fighter (8 of 16)
|
||||
"""
|
||||
|
||||
displayName = 'Evasive Maneuvers'
|
||||
@@ -27079,7 +27085,7 @@ class Effect6440(BaseEffect):
|
||||
|
||||
Used by:
|
||||
Fighters named like: Shadow (2 of 2)
|
||||
Fighters named like: Siren (4 of 4)
|
||||
Fighters named like: Siren (5 of 5)
|
||||
"""
|
||||
|
||||
displayName = 'Afterburner'
|
||||
@@ -27098,7 +27104,7 @@ class Effect6441(BaseEffect):
|
||||
fighterAbilityMicroWarpDrive
|
||||
|
||||
Used by:
|
||||
Items from category: Fighter (44 of 82)
|
||||
Items from category: Fighter (51 of 94)
|
||||
"""
|
||||
|
||||
displayName = 'Microwarpdrive'
|
||||
@@ -27192,8 +27198,7 @@ class Effect6465(BaseEffect):
|
||||
fighterAbilityAttackM
|
||||
|
||||
Used by:
|
||||
Items from category: Fighter (50 of 82)
|
||||
Fighters from group: Heavy Fighter (34 of 34)
|
||||
Items from category: Fighter (54 of 94)
|
||||
"""
|
||||
|
||||
dealsDamage = True
|
||||
@@ -27466,7 +27471,8 @@ class Effect6485(BaseEffect):
|
||||
fighterAbilityLaunchBomb
|
||||
|
||||
Used by:
|
||||
Fighters from group: Heavy Fighter (16 of 34)
|
||||
Fighters from group: Heavy Fighter (8 of 17)
|
||||
Fighters from group: Structure Heavy Fighter (8 of 17)
|
||||
"""
|
||||
|
||||
dealsDamage = True
|
||||
@@ -28352,6 +28358,9 @@ class Effect6567(BaseEffect):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups or
|
||||
mod.item.requiresSkill('Propulsion Jamming'),
|
||||
'capacitorNeed', src.getModifiedItemAttr('ewCapacitorNeedBonus'), **kwargs)
|
||||
fit.modules.filteredItemIncrease(
|
||||
lambda mod: mod.item.requiresSkill('Capital Micro Jump Drive Operation'), 'activationBlocked',
|
||||
src.getModifiedItemAttr('networkedSensorArrayDisallowCapitalMicroJump'), **kwargs)
|
||||
|
||||
|
||||
class Effect6570(BaseEffect):
|
||||
@@ -31617,9 +31626,9 @@ class Effect6789(BaseEffect):
|
||||
industrialBonusDroneDamage
|
||||
|
||||
Used by:
|
||||
Ships from group: Blockade Runner (4 of 4)
|
||||
Ships from group: Deep Space Transport (4 of 4)
|
||||
Ships from group: Hauler (17 of 17)
|
||||
Ships from group: Blockade Runner (5 of 5)
|
||||
Ships from group: Deep Space Transport (5 of 5)
|
||||
Ships from group: Hauler (18 of 18)
|
||||
Ships from group: Industrial Command Ship (2 of 2)
|
||||
Ship: Hulk
|
||||
Ship: Mackinaw
|
||||
@@ -32204,7 +32213,7 @@ class Effect6879(BaseEffect):
|
||||
@staticmethod
|
||||
def handler(fit, src, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Stasis Web', 'maxRange',
|
||||
src.getModifiedItemAttr('eliteBonusBlackOps3'), stackingPenalties=True, skill='Black Ops', **kwargs)
|
||||
src.getModifiedItemAttr('eliteBonusBlackOps3'), skill='Black Ops', **kwargs)
|
||||
|
||||
|
||||
class Effect6880(BaseEffect):
|
||||
@@ -33538,7 +33547,7 @@ class Effect7008(BaseEffect):
|
||||
structureFullPowerStateHitpointModifier
|
||||
|
||||
Used by:
|
||||
Items from category: Structure (17 of 17)
|
||||
Items from category: Structure (18 of 18)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -33556,7 +33565,7 @@ class Effect7009(BaseEffect):
|
||||
Used by:
|
||||
Structure Modules from group: Structure Citadel Service Module (2 of 2)
|
||||
Structure Modules from group: Structure Engineering Service Module (6 of 6)
|
||||
Structure Modules from group: Structure Navigation Service Module (3 of 3)
|
||||
Structure Modules from group: Structure FLEX Service Module (3 of 3)
|
||||
Structure Modules from group: Structure Resource Processing Service Module (4 of 4)
|
||||
Structure Module: Standup Moon Drill I
|
||||
"""
|
||||
@@ -33789,7 +33798,7 @@ class Effect7030(BaseEffect):
|
||||
structureAoERoFRoleBonus
|
||||
|
||||
Used by:
|
||||
Items from category: Structure (11 of 17)
|
||||
Items from category: Structure (11 of 18)
|
||||
Structures from group: Citadel (8 of 9)
|
||||
"""
|
||||
|
||||
@@ -33938,7 +33947,7 @@ class Effect7039(BaseEffect):
|
||||
structureHiddenMissileDamageMultiplier
|
||||
|
||||
Used by:
|
||||
Items from category: Structure (14 of 17)
|
||||
Items from category: Structure (14 of 18)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -33957,7 +33966,7 @@ class Effect7040(BaseEffect):
|
||||
structureHiddenArmorHPMultiplier
|
||||
|
||||
Used by:
|
||||
Items from category: Structure (17 of 17)
|
||||
Items from category: Structure (18 of 18)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -36668,7 +36677,7 @@ class Effect8108(BaseEffect):
|
||||
signatureRadiusBonusOnline
|
||||
|
||||
Used by:
|
||||
Module: Signature Radius Suppressor I
|
||||
Modules from group: Signature Suppressor (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
@@ -36684,7 +36693,7 @@ class Effect8109(BaseEffect):
|
||||
targetSpectrumBreakerBonus
|
||||
|
||||
Used by:
|
||||
Module: Signature Radius Suppressor I
|
||||
Modules from group: Signature Suppressor (5 of 5)
|
||||
"""
|
||||
|
||||
type = 'active'
|
||||
@@ -38004,6 +38013,40 @@ class Effect8323(BaseEffect):
|
||||
skill='Gallente Hauler', **kwargs)
|
||||
|
||||
|
||||
class Effect8327(BaseEffect):
|
||||
"""
|
||||
relicAnalyzerRangeBonusPassive
|
||||
|
||||
Used by:
|
||||
Implants named like: SoCT Relic Range Booster (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill('Archaeology'), 'maxRange',
|
||||
container.getModifiedItemAttr('rangeSkillBonus'), **kwargs)
|
||||
|
||||
|
||||
class Effect8328(BaseEffect):
|
||||
"""
|
||||
relicVirusStrengthBonusPassive
|
||||
|
||||
Used by:
|
||||
Implants named like: SoCT Relic Strength Booster (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredItemIncrease(
|
||||
lambda mod: mod.item.requiresSkill('Archaeology'), 'virusStrength',
|
||||
container.getModifiedItemAttr('virusStrengthBonus'), **kwargs)
|
||||
|
||||
|
||||
class Effect8360(BaseEffect):
|
||||
"""
|
||||
shipBonusMissileReloadTimeGC2
|
||||
@@ -38222,6 +38265,23 @@ class Effect8479(BaseEffect):
|
||||
container.getModifiedItemAttr('falloffBonus'), **kwargs)
|
||||
|
||||
|
||||
class Effect8594(BaseEffect):
|
||||
"""
|
||||
modifyArmorDamageResistanceBonusPostPercentPassive
|
||||
|
||||
Used by:
|
||||
Implants named like: SoCT Armor Booster (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, booster, context, projectionRange, **kwargs):
|
||||
for type in ('Em', 'Explosive', 'Kinetic', 'Thermal'):
|
||||
fit.ship.boostItemAttr(f'armor{type}DamageResonance',
|
||||
booster.getModifiedItemAttr('armorDamageResistanceBonus'), **kwargs)
|
||||
|
||||
|
||||
class Effect11055(BaseEffect):
|
||||
"""
|
||||
shipBonusBattlecruiserHeavyMissileAoeVelocityMBC1
|
||||
@@ -40443,3 +40503,326 @@ class Effect12038(BaseEffect):
|
||||
fit.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill('Small Projectile Turret'), 'falloff',
|
||||
ship.getModifiedItemAttr('shipBonus3MF'), skill='Minmatar Frigate', **kwargs)
|
||||
|
||||
|
||||
class Effect12050(BaseEffect):
|
||||
"""
|
||||
shipBonusColonyResourcesHoldCapacityUH1
|
||||
|
||||
Used by:
|
||||
Variations of ship: Squall (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, src, context, projectionRange, **kwargs):
|
||||
fit.ship.boostItemAttr(
|
||||
'specialColonyResourcesHoldCapacity', src.getModifiedItemAttr('shipBonusUH1'),
|
||||
skill='Upwell Hauler', **kwargs)
|
||||
|
||||
|
||||
class Effect12051(BaseEffect):
|
||||
"""
|
||||
shipMissileEMDamageUH2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Squall (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'emDamage', container.getModifiedItemAttr('shipBonusUH2'),
|
||||
skill='Upwell Hauler', **kwargs)
|
||||
|
||||
|
||||
class Effect12052(BaseEffect):
|
||||
"""
|
||||
shipMissileThermalDamageUH2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Squall (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'thermalDamage', container.getModifiedItemAttr('shipBonusUH2'),
|
||||
skill='Upwell Hauler', **kwargs)
|
||||
|
||||
|
||||
class Effect12053(BaseEffect):
|
||||
"""
|
||||
shipMissileExplosiveDamageUH2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Squall (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'explosiveDamage', container.getModifiedItemAttr('shipBonusUH2'),
|
||||
skill='Upwell Hauler', **kwargs)
|
||||
|
||||
|
||||
class Effect12054(BaseEffect):
|
||||
"""
|
||||
shipMissileKineticDamageUH2
|
||||
|
||||
Used by:
|
||||
Variations of ship: Squall (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'kineticDamage', container.getModifiedItemAttr('shipBonusUH2'),
|
||||
skill='Upwell Hauler', **kwargs)
|
||||
|
||||
|
||||
class Effect12057(BaseEffect):
|
||||
"""
|
||||
shipBonusColonyResourcesHoldCapacityUFreighter1
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, src, context, projectionRange, **kwargs):
|
||||
fit.ship.boostItemAttr(
|
||||
'specialColonyResourcesHoldCapacity', src.getModifiedItemAttr('shipBonusUFreighter1'),
|
||||
skill='Upwell Freighter', **kwargs)
|
||||
|
||||
|
||||
class Effect12058(BaseEffect):
|
||||
"""
|
||||
shipMissileEMDamageUFreighter2
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'emDamage', container.getModifiedItemAttr('shipBonusUFreighter2'),
|
||||
skill='Upwell Freighter', **kwargs)
|
||||
|
||||
|
||||
class Effect12060(BaseEffect):
|
||||
"""
|
||||
shipMissileThermalDamageUFreighter2
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'thermalDamage', container.getModifiedItemAttr('shipBonusUFreighter2'),
|
||||
skill='Upwell Freighter', **kwargs)
|
||||
|
||||
|
||||
class Effect12061(BaseEffect):
|
||||
"""
|
||||
shipMissileExplosiveDamageUFreighter2
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'explosiveDamage', container.getModifiedItemAttr('shipBonusUFreighter2'),
|
||||
skill='Upwell Freighter', **kwargs)
|
||||
|
||||
|
||||
class Effect12062(BaseEffect):
|
||||
"""
|
||||
shipMissileKineticDamageUFreighter2
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Missile Launcher Operation'),
|
||||
'kineticDamage', container.getModifiedItemAttr('shipBonusUFreighter2'),
|
||||
skill='Upwell Freighter', **kwargs)
|
||||
|
||||
|
||||
class Effect12063(BaseEffect):
|
||||
"""
|
||||
shipRoleBonusUpwellFreighterCloakCPUPenalty
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredItemIncrease(
|
||||
lambda mod: mod.item.group.name == 'Cloaking Device',
|
||||
'cpu', ship.getModifiedItemAttr('upwellFreightercloakCPUPenalty'), **kwargs)
|
||||
|
||||
|
||||
class Effect12069(BaseEffect):
|
||||
"""
|
||||
shipBonusAutoTargetingMissilesUFreighter3
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, container, context, projectionRange, **kwargs):
|
||||
damageTypes = ('em', 'explosive', 'kinetic', 'thermal')
|
||||
for dmgType in damageTypes:
|
||||
fit.modules.filteredChargeBoost(
|
||||
lambda mod: mod.charge.requiresSkill('Auto-Targeting Missiles'),
|
||||
f'{dmgType}Damage', container.getModifiedItemAttr('shipBonusUFreighter3'),
|
||||
skill='Upwell Freighter', **kwargs)
|
||||
|
||||
|
||||
class Effect12071(BaseEffect):
|
||||
"""
|
||||
shipRoleBonusUpwellFreighterCapitalFlexHardenerFittingCapBonus
|
||||
|
||||
Used by:
|
||||
Ship: Avalanche
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredItemMultiply(
|
||||
lambda mod: mod.item.group.name == 'Flex Shield Hardener',
|
||||
'power', ship.getModifiedItemAttr('upwellFreighterCapitalFlexHardenerFittingCapBonus'), **kwargs)
|
||||
fit.modules.filteredItemMultiply(
|
||||
lambda mod: mod.item.group.name == 'Flex Shield Hardener',
|
||||
'capacitorNeed', ship.getModifiedItemAttr('upwellFreighterCapitalFlexHardenerFittingCapBonus'), **kwargs)
|
||||
|
||||
|
||||
class Effect12072(BaseEffect):
|
||||
"""
|
||||
eliteIndustrialUpwellNeutNosHeatBonus
|
||||
|
||||
Used by:
|
||||
Ship: Torrent
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.group.name in ('Energy Neutralizer', 'Energy Nosferatu'),
|
||||
'overloadSelfDurationBonus', ship.getModifiedItemAttr('roleBonusOverheatDST'), **kwargs)
|
||||
|
||||
|
||||
class Effect12098(BaseEffect):
|
||||
"""
|
||||
jumpPortalPassengerBonusPercentSkill
|
||||
|
||||
Used by:
|
||||
Skill: Capital Jump Portal Generation
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, skill, context, projectionRange, **kwargs):
|
||||
fit.ship.boostItemAttr(
|
||||
'conduitJumpPassengerCount',
|
||||
skill.getModifiedItemAttr('conduitPassengerBonusPercent') * skill.level, **kwargs)
|
||||
|
||||
|
||||
class Effect12102(BaseEffect):
|
||||
"""
|
||||
capitalMJDSkillCapReductionBonus
|
||||
|
||||
Used by:
|
||||
Skill: Capital Micro Jump Drive Operation
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, skill, context, projectionRange, **kwargs):
|
||||
fit.modules.filteredItemBoost(
|
||||
lambda mod: mod.item.requiresSkill('Capital Micro Jump Drive Operation'), 'capacitorNeed',
|
||||
skill.getModifiedItemAttr('capitalMJDCapReductionBonus') * skill.level, **kwargs)
|
||||
|
||||
|
||||
class Effect12126(BaseEffect):
|
||||
"""
|
||||
microJumpPortalDriveCapital
|
||||
|
||||
Used by:
|
||||
Module: Capital Micro Jump Field Generator
|
||||
"""
|
||||
|
||||
type = 'active'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, module, context, projectionRange, **kwargs):
|
||||
fit.ship.boostItemAttr('signatureRadius', module.getModifiedItemAttr('signatureRadiusBonusPercent'),
|
||||
stackingPenalties=True, **kwargs)
|
||||
|
||||
|
||||
class Effect12127(BaseEffect):
|
||||
"""
|
||||
shipRoleBonusUpwellHaulersMediumMissileFittingBonus
|
||||
|
||||
Used by:
|
||||
Variations of ship: Squall (3 of 3)
|
||||
"""
|
||||
|
||||
type = 'passive'
|
||||
|
||||
@staticmethod
|
||||
def handler(fit, ship, context, projectionRange, **kwargs):
|
||||
for attr in ('cpu', 'power'):
|
||||
fit.modules.filteredItemMultiply(
|
||||
lambda mod: mod.item.group.name in (
|
||||
'Missile Launcher Heavy Assault',
|
||||
'Missile Launcher Heavy',
|
||||
'Missile Launcher Rapid Light'),
|
||||
attr, ship.getModifiedItemAttr('upwellHaulersMediumMissileFittingBonus'), **kwargs)
|
||||
|
||||
@@ -343,7 +343,8 @@ class Item(EqBase):
|
||||
500018: "mordu",
|
||||
500019: "sansha",
|
||||
500020: "serpentis",
|
||||
500026: "triglavian"
|
||||
500026: "triglavian",
|
||||
500027: "upwell",
|
||||
}
|
||||
|
||||
@property
|
||||
@@ -351,11 +352,7 @@ class Item(EqBase):
|
||||
if self.__race is None:
|
||||
|
||||
try:
|
||||
if (
|
||||
self.category.name == 'Structure' or
|
||||
# Here until CCP puts their shit together
|
||||
self.name in ("Thunderchild", "Stormbringer", "Skybreaker")
|
||||
):
|
||||
if self.category.name == 'Structure':
|
||||
self.__race = "upwell"
|
||||
else:
|
||||
self.__race = self.factionMap[self.factionID]
|
||||
@@ -377,7 +374,8 @@ class Item(EqBase):
|
||||
16 : "jove",
|
||||
32 : "sansha", # Incrusion Sansha
|
||||
128: "ore",
|
||||
135: "triglavian"
|
||||
135: "triglavian",
|
||||
168: "upwell",
|
||||
}
|
||||
# Race is None by default
|
||||
race = None
|
||||
|
||||
@@ -25,10 +25,11 @@ import time
|
||||
|
||||
|
||||
class SsoCharacter:
|
||||
def __init__(self, charID, name, client, accessToken=None, refreshToken=None):
|
||||
def __init__(self, charID, name, client, server, accessToken=None, refreshToken=None):
|
||||
self.characterID = charID
|
||||
self.characterName = name
|
||||
self.client = client
|
||||
self.server = server
|
||||
self.accessToken = accessToken
|
||||
self.refreshToken = refreshToken
|
||||
self.accessTokenExpires = None
|
||||
@@ -37,6 +38,9 @@ class SsoCharacter:
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def characterDisplay(self):
|
||||
return "{} [{}]".format(self.characterName, self.server)
|
||||
def is_token_expired(self):
|
||||
if self.accessTokenExpires is None:
|
||||
return True
|
||||
|
||||
@@ -25,7 +25,7 @@ class AddToCargoAmmo(ContextMenuSingle):
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem):
|
||||
if mainItem.marketGroup.name == "Scan Probes":
|
||||
if mainItem.marketGroup and mainItem.marketGroup.name == "Scan Probes":
|
||||
return _t("Add {0} to Cargo (x8)").format(itmContext)
|
||||
|
||||
return _t("Add {0} to Cargo (x1000)").format(itmContext)
|
||||
@@ -34,7 +34,7 @@ class AddToCargoAmmo(ContextMenuSingle):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
typeID = int(mainItem.ID)
|
||||
|
||||
if mainItem.marketGroup.name == "Scan Probes":
|
||||
if mainItem.marketGroup and mainItem.marketGroup.name == "Scan Probes":
|
||||
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=8)
|
||||
else:
|
||||
command = cmd.GuiAddCargoCommand(fitID=fitID, itemID=typeID, amount=1000)
|
||||
|
||||
@@ -65,7 +65,6 @@ class DroneStackSplit(wx.Dialog):
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.input.SetValue(str(value))
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -75,12 +74,13 @@ class DroneStackSplit(wx.Dialog):
|
||||
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.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
@@ -59,7 +59,6 @@ class NameDialog(wx.Dialog):
|
||||
else:
|
||||
value = str(value)
|
||||
self.input.SetValue(value)
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -69,11 +68,12 @@ class NameDialog(wx.Dialog):
|
||||
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.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
@@ -108,7 +108,6 @@ class AmountChanger(wx.Dialog):
|
||||
|
||||
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.input.SetValue(str(value))
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -118,12 +117,13 @@ class AmountChanger(wx.Dialog):
|
||||
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.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
@@ -94,7 +94,6 @@ class RangeChanger(wx.Dialog):
|
||||
value = int(value)
|
||||
value = str(value)
|
||||
self.input.SetValue(value)
|
||||
self.input.SelectAll()
|
||||
|
||||
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
|
||||
|
||||
@@ -104,12 +103,13 @@ class RangeChanger(wx.Dialog):
|
||||
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.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
|
||||
self.SetSizer(bSizer1)
|
||||
self.CenterOnParent()
|
||||
self.Fit()
|
||||
self.CenterOnParent()
|
||||
self.input.SetFocus()
|
||||
self.input.SelectAll()
|
||||
|
||||
def processEnter(self, evt):
|
||||
self.EndModal(wx.ID_OK)
|
||||
|
||||
@@ -72,6 +72,7 @@ AttrGroupDict = {
|
||||
"specialAmmoHoldCapacity",
|
||||
"specialCommandCenterHoldCapacity",
|
||||
"specialPlanetaryCommoditiesHoldCapacity",
|
||||
"specialColonyResourcesHoldCapacity",
|
||||
"structureDamageLimit",
|
||||
"specialSubsystemHoldCapacity",
|
||||
"emDamageResonance",
|
||||
|
||||
@@ -5,3 +5,5 @@ import wx.lib.newevent
|
||||
ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent()
|
||||
|
||||
RECENTLY_USED_MODULES = -2
|
||||
|
||||
CHARGES_FOR_FIT = -3
|
||||
|
||||
@@ -2,14 +2,17 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import gui.builtinMarketBrowser.pfSearchBox as SBox
|
||||
from config import slotColourMap
|
||||
import gui.globalEvents as GE
|
||||
from config import slotColourMap, slotColourMapDark
|
||||
from eos.saveddata.module import Module
|
||||
from gui.builtinMarketBrowser.events import ItemSelected, RECENTLY_USED_MODULES
|
||||
from gui.builtinMarketBrowser.events import ItemSelected, RECENTLY_USED_MODULES, CHARGES_FOR_FIT
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.display import Display
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from gui.utils.dark import isDark
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
from service.ammo import Ammo
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -31,6 +34,7 @@ class ItemView(Display):
|
||||
self.filteredStore = set()
|
||||
self.sMkt = marketBrowser.sMkt
|
||||
self.sFit = Fit.getInstance()
|
||||
self.sAmmo = Ammo.getInstance()
|
||||
|
||||
self.marketBrowser = marketBrowser
|
||||
self.marketView = marketBrowser.marketView
|
||||
@@ -50,6 +54,9 @@ class ItemView(Display):
|
||||
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated)
|
||||
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
|
||||
|
||||
# the "charges for active fitting" needs to listen to fitting changes
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
|
||||
self.active = []
|
||||
|
||||
def delaySearch(self, evt):
|
||||
@@ -90,7 +97,11 @@ class ItemView(Display):
|
||||
if sel.IsOk():
|
||||
# Get data field of the selected item (which is a marketGroup ID if anything was selected)
|
||||
seldata = self.marketView.GetItemData(sel)
|
||||
if seldata is not None and seldata != RECENTLY_USED_MODULES:
|
||||
if seldata == RECENTLY_USED_MODULES:
|
||||
items = self.sMkt.getRecentlyUsed()
|
||||
elif seldata == CHARGES_FOR_FIT:
|
||||
items = self.getChargesForActiveFit()
|
||||
elif seldata is not None:
|
||||
# If market group treeview item doesn't have children (other market groups or dummies),
|
||||
# then it should have items in it and we want to request them
|
||||
if self.marketView.ItemHasChildren(sel) is False:
|
||||
@@ -102,11 +113,7 @@ class ItemView(Display):
|
||||
else:
|
||||
items = set()
|
||||
else:
|
||||
# If method was called but selection wasn't actually made or we have a hit on recently used modules
|
||||
if seldata == RECENTLY_USED_MODULES:
|
||||
items = self.sMkt.getRecentlyUsed()
|
||||
else:
|
||||
items = set()
|
||||
items = set()
|
||||
|
||||
# Fill store
|
||||
self.updateItemStore(items)
|
||||
@@ -114,6 +121,9 @@ class ItemView(Display):
|
||||
# Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered)
|
||||
if seldata == RECENTLY_USED_MODULES:
|
||||
self.marketBrowser.mode = 'recent'
|
||||
|
||||
if seldata == CHARGES_FOR_FIT:
|
||||
self.marketBrowser.mode = 'charges'
|
||||
|
||||
self.setToggles()
|
||||
if context == 'tree' and self.marketBrowser.settings.get('marketMGMarketSelectMode') == 1:
|
||||
@@ -122,6 +132,41 @@ class ItemView(Display):
|
||||
btn.setUserSelection(True)
|
||||
self.filterItemStore()
|
||||
|
||||
def getChargesForActiveFit(self):
|
||||
fitId = self.mainFrame.getActiveFit()
|
||||
|
||||
# no active fit => no charges
|
||||
if fitId is None:
|
||||
return set()
|
||||
|
||||
fit = self.sFit.getFit(fitId)
|
||||
|
||||
# use a set so we only add one entry for each charge
|
||||
items = set()
|
||||
for mod in fit.modules:
|
||||
charges = self.sAmmo.getModuleFlatAmmo(mod)
|
||||
for charge in charges:
|
||||
items.add(charge)
|
||||
return items
|
||||
|
||||
def fitChanged(self, event):
|
||||
# skip the event so the other handlers also get called
|
||||
event.Skip()
|
||||
|
||||
if self.marketBrowser.mode != 'charges':
|
||||
return
|
||||
|
||||
activeFitID = self.mainFrame.getActiveFit()
|
||||
# if it was not the active fitting that was changed, do not do anything
|
||||
if activeFitID is not None and activeFitID not in event.fitIDs:
|
||||
return
|
||||
|
||||
items = self.getChargesForActiveFit()
|
||||
|
||||
# update the UI
|
||||
self.updateItemStore(items)
|
||||
self.filterItemStore()
|
||||
|
||||
def updateItemStore(self, items):
|
||||
self.unfilteredStore = items
|
||||
|
||||
@@ -243,6 +288,7 @@ class ItemView(Display):
|
||||
|
||||
def columnBackground(self, colItem, item):
|
||||
if self.sFit.serviceFittingOptions["colorFitBySlot"]:
|
||||
return slotColourMap.get(Module.calculateSlot(item)) or self.GetBackgroundColour()
|
||||
colorMap = slotColourMapDark if isDark() else slotColourMap
|
||||
return colorMap.get(Module.calculateSlot(item)) or self.GetBackgroundColour()
|
||||
else:
|
||||
return self.GetBackgroundColour()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import wx
|
||||
|
||||
from gui.cachingImageList import CachingImageList
|
||||
from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES
|
||||
from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES, CHARGES_FOR_FIT
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
@@ -35,6 +35,9 @@ class MarketTree(wx.TreeCtrl):
|
||||
# Add recently used modules node
|
||||
rumIconId = self.addImage("market_small", "gui")
|
||||
self.AppendItem(self.root, _t("Recently Used Items"), rumIconId, data=RECENTLY_USED_MODULES)
|
||||
# Add charges for active fitting node
|
||||
cffIconId = self.addImage("damagePattern_small", "gui")
|
||||
self.AppendItem(self.root, _t("Charges For Active Fit"), cffIconId, data=CHARGES_FOR_FIT)
|
||||
|
||||
# Bind our lookup method to when the tree gets expanded
|
||||
self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
import config
|
||||
import gui.mainFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.preferenceView import PreferenceView
|
||||
from service.esi import Esi
|
||||
from service.settings import EsiSettings
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
@@ -41,38 +43,68 @@ class PFEsiPref(PreferenceView):
|
||||
"due to 'Signature has expired' error")))
|
||||
mainSizer.Add(self.enforceJwtExpiration, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.ssoServer = wx.CheckBox(panel, wx.ID_ANY, _t("Auto-login (starts local server)"), wx.DefaultPosition,
|
||||
wx.DefaultSize,
|
||||
0)
|
||||
self.ssoServer.SetToolTip(wx.ToolTip(_t("This allows the EVE SSO to callback to your local pyfa instance and complete the authentication process without manual intervention.")))
|
||||
mainSizer.Add(self.ssoServer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.rbMode = wx.RadioBox(panel, -1, _t("Login Authentication Method"), wx.DefaultPosition, wx.DefaultSize,
|
||||
[_t('Local Server'), _t('Manual')], 1, wx.RA_SPECIFY_COLS)
|
||||
self.rbMode.SetItemToolTip(0, _t("This option starts a local webserver that EVE SSO Server will call back to"
|
||||
" with information about the character login."))
|
||||
self.rbMode.SetItemToolTip(1, _t("This option prompts users to copy and paste information to allow for"
|
||||
" character login. Use this if having issues with the local server."))
|
||||
|
||||
self.rbMode.SetSelection(self.settings.get('loginMode'))
|
||||
self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration" or True))
|
||||
self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration") or True)
|
||||
self.ssoServer.SetValue(True if self.settings.get("loginMode") == 0 else False)
|
||||
|
||||
rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5)
|
||||
mainSizer.Add(rbSizer, 0, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
|
||||
esiSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
|
||||
self.esiServer.Wrap(-1)
|
||||
|
||||
esiSizer.Add(self.esiServer, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.esiServer.SetToolTip(wx.ToolTip(_t('The source you choose will be used on connection.')))
|
||||
|
||||
self.chESIserver = wx.Choice(panel, choices=list(self.settings.keys()))
|
||||
|
||||
self.chESIserver.SetStringSelection(self.settings.get("server"))
|
||||
|
||||
esiSizer.Add(self.chESIserver, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
|
||||
|
||||
mainSizer.Add(esiSizer, 0, wx.TOP | wx.RIGHT, 10)
|
||||
|
||||
self.chESIserver.Bind(wx.EVT_CHOICE, self.OnServerChange)
|
||||
self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange)
|
||||
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.ssoServer.Bind(wx.EVT_CHECKBOX, self.OnModeChange)
|
||||
|
||||
panel.SetSizer(mainSizer)
|
||||
|
||||
panel.Layout()
|
||||
|
||||
def OnTimeoutChange(self, event):
|
||||
self.settings.set('timeout', event.GetEventObject().GetValue())
|
||||
event.Skip()
|
||||
|
||||
def OnModeChange(self, event):
|
||||
self.settings.set('loginMode', event.GetInt())
|
||||
self.settings.set('loginMode', 0 if self.ssoServer.GetValue() else 1)
|
||||
event.Skip()
|
||||
|
||||
def OnEnforceChange(self, event):
|
||||
self.settings.set('enforceJwtExpiration', self.enforceJwtExpiration.GetValue())
|
||||
event.Skip()
|
||||
|
||||
def OnServerChange(self, event):
|
||||
# pass
|
||||
source = self.chESIserver.GetString(self.chESIserver.GetSelection())
|
||||
esiService = Esi.getInstance()
|
||||
# init servers
|
||||
esiService.init(config.supported_servers[source])
|
||||
self.settings.set("server", source)
|
||||
event.Skip()
|
||||
|
||||
def getImage(self):
|
||||
return BitmapLoader.getBitmap("eve", "gui")
|
||||
|
||||
|
||||
PFEsiPref.register()
|
||||
PFEsiPref.register()
|
||||
@@ -112,6 +112,7 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
cargoNamesOrder = OrderedDict((
|
||||
("fleetHangarCapacity", _t("Fleet hangar")),
|
||||
("shipMaintenanceBayCapacity", _t("Maintenance bay")),
|
||||
("specialColonyResourcesHoldCapacity", _t("Infrastructure hold")),
|
||||
("specialAmmoHoldCapacity", _t("Ammo hold")),
|
||||
("specialFuelBayCapacity", _t("Fuel bay")),
|
||||
("specialShipHoldCapacity", _t("Ship hold")),
|
||||
@@ -134,6 +135,7 @@ class TargetingMiscViewMinimal(StatsView):
|
||||
cargoValues = {
|
||||
"main": lambda: fit.ship.getModifiedItemAttr("capacity"),
|
||||
"fleetHangarCapacity": lambda: fit.ship.getModifiedItemAttr("fleetHangarCapacity"),
|
||||
"specialColonyResourcesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialColonyResourcesHoldCapacity"),
|
||||
"shipMaintenanceBayCapacity": lambda: fit.ship.getModifiedItemAttr("shipMaintenanceBayCapacity"),
|
||||
"specialAmmoHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialAmmoHoldCapacity"),
|
||||
"specialFuelBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialFuelBayCapacity"),
|
||||
|
||||
@@ -590,7 +590,7 @@ class Miscellanea(ViewColumn):
|
||||
text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3))
|
||||
tooltip = "Energy neutralization per second"
|
||||
return text, tooltip
|
||||
elif itemGroup in ("Micro Jump Drive", "Micro Jump Field Generators"):
|
||||
elif itemGroup in ("Micro Jump Drive", "Micro Jump Field Generators", "Capital Mobility Modules"):
|
||||
cycleTime = stuff.getModifiedItemAttr("duration") / 1000
|
||||
text = "{0}s".format(formatAmount(cycleTime, 3, 0, 3))
|
||||
tooltip = "Spoolup time"
|
||||
|
||||
@@ -807,7 +807,12 @@ class APIView(wx.Panel):
|
||||
|
||||
self.SetSizer(pmainSizer)
|
||||
self.Layout()
|
||||
self.ssoListChanged(None)
|
||||
try:
|
||||
self.ssoListChanged(None)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
pass
|
||||
|
||||
def ssoCharChanged(self, event):
|
||||
sChar = Character.getInstance()
|
||||
@@ -859,7 +864,7 @@ class APIView(wx.Panel):
|
||||
noneID = self.charChoice.Append(_t("None"), None)
|
||||
|
||||
for char in ssoChars:
|
||||
currId = self.charChoice.Append(char.characterName, char.ID)
|
||||
currId = self.charChoice.Append(char.characterDisplay, char.ID)
|
||||
|
||||
if sso is not None and char.ID == sso.ID:
|
||||
self.charChoice.SetSelection(currId)
|
||||
|
||||
@@ -96,7 +96,7 @@ class EveFittings(AuxiliaryFrame):
|
||||
|
||||
self.charChoice.Clear()
|
||||
for char in chars:
|
||||
self.charChoice.Append(char.characterName, char.ID)
|
||||
self.charChoice.Append(char.characterDisplay, char.ID)
|
||||
if len(chars) > 0:
|
||||
self.charChoice.SetSelection(0)
|
||||
|
||||
@@ -227,21 +227,6 @@ class EveFittings(AuxiliaryFrame):
|
||||
self.fitView.update([])
|
||||
|
||||
|
||||
class ESIServerExceptionHandler:
|
||||
def __init__(self, parentWindow, ex):
|
||||
pyfalog.error(ex)
|
||||
with wx.MessageDialog(
|
||||
parentWindow,
|
||||
_t("There was an issue starting up the localized server, try setting "
|
||||
"Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> "
|
||||
"Login Authentication Method. If this doesn't fix the problem please file an "
|
||||
"issue on Github."),
|
||||
_t("Add Character Error"),
|
||||
wx.OK | wx.ICON_ERROR
|
||||
) as dlg:
|
||||
dlg.ShowModal()
|
||||
|
||||
|
||||
class ESIExceptionHandler:
|
||||
# todo: make this a generate excetpion handler for all calls
|
||||
def __init__(self, ex):
|
||||
@@ -348,7 +333,7 @@ class ExportToEve(AuxiliaryFrame):
|
||||
|
||||
self.charChoice.Clear()
|
||||
for char in chars:
|
||||
self.charChoice.Append(char.characterName, char.ID)
|
||||
self.charChoice.Append(char.characterDisplay, char.ID)
|
||||
|
||||
if len(chars) > 0:
|
||||
self.charChoice.SetSelection(0)
|
||||
@@ -434,6 +419,7 @@ class SsoCharacterMgmt(AuxiliaryFrame):
|
||||
|
||||
self.lcCharacters.InsertColumn(0, heading=_t('Character'))
|
||||
self.lcCharacters.InsertColumn(1, heading=_t('Character ID'))
|
||||
self.lcCharacters.InsertColumn(2, heading=_t('Server'))
|
||||
|
||||
self.popCharList()
|
||||
|
||||
@@ -496,9 +482,11 @@ class SsoCharacterMgmt(AuxiliaryFrame):
|
||||
self.lcCharacters.InsertItem(index, char.characterName)
|
||||
self.lcCharacters.SetItem(index, 1, str(char.characterID))
|
||||
self.lcCharacters.SetItemData(index, char.ID)
|
||||
self.lcCharacters.SetItem(index, 2, char.server or "<unknown>")
|
||||
|
||||
self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE)
|
||||
self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE)
|
||||
self.lcCharacters.SetColumnWidth(2, wx.LIST_AUTOSIZE)
|
||||
|
||||
def addChar(self, event):
|
||||
try:
|
||||
@@ -506,8 +494,6 @@ class SsoCharacterMgmt(AuxiliaryFrame):
|
||||
sEsi.login()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as ex:
|
||||
ESIServerExceptionHandler(self, ex)
|
||||
|
||||
def delChar(self, event):
|
||||
item = self.lcCharacters.GetFirstSelected()
|
||||
|
||||
@@ -112,7 +112,7 @@ class FitBrowserLiteDialog(wx.Dialog):
|
||||
return True
|
||||
|
||||
matches = []
|
||||
searchTokens = [t.lower() for t in re.split('\s+', searchPattern)]
|
||||
searchTokens = [t.lower() for t in re.split(r'\s+', searchPattern)]
|
||||
for fit in self.allFits:
|
||||
if isMatch(fit, searchTokens):
|
||||
matches.append(fit)
|
||||
|
||||
164
gui/mainFrame.py
@@ -61,10 +61,11 @@ from gui.statsPane import StatsPane
|
||||
from gui.targetProfileEditor import TargetProfileEditor
|
||||
from gui.updateDialog import UpdateDialog
|
||||
from gui.utils.clipboard import fromClipboard
|
||||
from gui.utils.progressHelper import ProgressHelper
|
||||
from service.character import Character
|
||||
from service.esi import Esi
|
||||
from service.fit import Fit
|
||||
from service.port import IPortUser, Port
|
||||
from service.port import Port
|
||||
from service.price import Price
|
||||
from service.settings import HTMLExportSettings, SettingsProvider
|
||||
from service.update import Update
|
||||
@@ -130,7 +131,6 @@ class OpenFitsThread(threading.Thread):
|
||||
self.running = False
|
||||
|
||||
|
||||
# todo: include IPortUser again
|
||||
class MainFrame(wx.Frame):
|
||||
__instance = None
|
||||
|
||||
@@ -845,14 +845,15 @@ class MainFrame(wx.Frame):
|
||||
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
self.progressDialog = wx.ProgressDialog(
|
||||
_t("Importing fits"),
|
||||
" " * 100, # set some arbitrary spacing to create width in window
|
||||
parent=self,
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL
|
||||
)
|
||||
Port.importFitsThreaded(dlg.GetPaths(), self)
|
||||
self.progressDialog.ShowModal()
|
||||
# set some arbitrary spacing to create width in window
|
||||
progress = ProgressHelper(message=" " * 100, callback=self._openAfterImport)
|
||||
call = (Port.importFitsThreaded, [dlg.GetPaths(), progress], {})
|
||||
self.handleProgress(
|
||||
title=_t("Importing fits"),
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
|
||||
call=call,
|
||||
progress=progress,
|
||||
errMsgLbl=_t("Import Error"))
|
||||
|
||||
def backupToXml(self, event):
|
||||
""" Back up all fits to EVE XML file """
|
||||
@@ -863,32 +864,30 @@ class MainFrame(wx.Frame):
|
||||
_t("Save Backup As..."),
|
||||
wildcard=_t("EVE XML fitting file") + " (*.xml)|*.xml",
|
||||
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
|
||||
defaultFile=defaultFile,
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
filePath = dlg.GetPath()
|
||||
defaultFile=defaultFile) as fileDlg:
|
||||
if fileDlg.ShowModal() == wx.ID_OK:
|
||||
filePath = fileDlg.GetPath()
|
||||
if '.' not in os.path.basename(filePath):
|
||||
filePath += ".xml"
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
max_ = sFit.countAllFits()
|
||||
|
||||
self.progressDialog = wx.ProgressDialog(
|
||||
_t("Backup fits"),
|
||||
_t("Backing up {} fits to: {}").format(max_, filePath),
|
||||
maximum=max_,
|
||||
parent=self,
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL
|
||||
)
|
||||
Port.backupFits(filePath, self)
|
||||
self.progressDialog.ShowModal()
|
||||
fitAmount = Fit.getInstance().countAllFits()
|
||||
progress = ProgressHelper(
|
||||
message=_t("Backing up {} fits to: {}").format(fitAmount, filePath),
|
||||
maximum=fitAmount + 1)
|
||||
call = (Port.backupFits, [filePath, progress], {})
|
||||
self.handleProgress(
|
||||
title=_t("Backup fits"),
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
|
||||
call=call,
|
||||
progress=progress,
|
||||
errMsgLbl=_t("Export Error"))
|
||||
|
||||
def exportHtml(self, event):
|
||||
from gui.utils.exportHtml import exportHtml
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
settings = HTMLExportSettings.getInstance()
|
||||
|
||||
max_ = sFit.countAllFits()
|
||||
path = settings.getPath()
|
||||
|
||||
if not os.path.isdir(os.path.dirname(path)):
|
||||
@@ -903,82 +902,44 @@ class MainFrame(wx.Frame):
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
return
|
||||
progress = ProgressHelper(
|
||||
message=_t("Generating HTML file at: {}").format(path),
|
||||
maximum=sFit.countAllFits() + 1)
|
||||
call = (exportHtml.getInstance().refreshFittingHtml, [True, progress], {})
|
||||
self.handleProgress(
|
||||
title=_t("Backup fits"),
|
||||
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME,
|
||||
call=call,
|
||||
progress=progress)
|
||||
|
||||
self.progressDialog = wx.ProgressDialog(
|
||||
_t("Backup fits"),
|
||||
_t("Generating HTML file at: {}").format(path),
|
||||
maximum=max_, parent=self,
|
||||
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME)
|
||||
|
||||
exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback)
|
||||
self.progressDialog.ShowModal()
|
||||
|
||||
def backupCallback(self, info):
|
||||
if info == -1:
|
||||
self.closeProgressDialog()
|
||||
else:
|
||||
self.progressDialog.Update(info)
|
||||
|
||||
def on_port_process_start(self):
|
||||
# flag for progress dialog.
|
||||
self.__progress_flag = True
|
||||
|
||||
def on_port_processing(self, action, data=None):
|
||||
# 2017/03/29 NOTE: implementation like interface
|
||||
wx.CallAfter(
|
||||
self._on_port_processing, action, data
|
||||
)
|
||||
|
||||
return self.__progress_flag
|
||||
|
||||
def _on_port_processing(self, action, data):
|
||||
"""
|
||||
While importing fits from file, the logic calls back to this function to
|
||||
update progress bar to show activity. XML files can contain multiple
|
||||
ships with multiple fits, whereas EFT cfg files contain many fits of
|
||||
a single ship. When iterating through the files, we update the message
|
||||
when we start a new file, and then Pulse the progress bar with every fit
|
||||
that is processed.
|
||||
|
||||
action : a flag that lets us know how to deal with :data
|
||||
None: Pulse the progress bar
|
||||
1: Replace message with data
|
||||
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
|
||||
"""
|
||||
_message = None
|
||||
if action & IPortUser.ID_ERROR:
|
||||
self.closeProgressDialog()
|
||||
_message = _t("Import Error") if action & IPortUser.PROCESS_IMPORT else _t("Export Error")
|
||||
def handleProgress(self, title, style, call, progress, errMsgLbl=None):
|
||||
extraArgs = {}
|
||||
if progress.maximum is not None:
|
||||
extraArgs['maximum'] = progress.maximum
|
||||
with wx.ProgressDialog(
|
||||
parent=self,
|
||||
title=title,
|
||||
message=progress.message,
|
||||
style=style,
|
||||
**extraArgs
|
||||
) as dlg:
|
||||
func, args, kwargs = call
|
||||
func(*args, **kwargs)
|
||||
while progress.working:
|
||||
wx.MilliSleep(250)
|
||||
wx.Yield()
|
||||
(progress.dlgWorking, skip) = dlg.Update(progress.current, progress.message)
|
||||
if progress.error and errMsgLbl:
|
||||
with wx.MessageDialog(
|
||||
self,
|
||||
_t("The following error was generated") +
|
||||
f"\n\n{data}\n\n" +
|
||||
f"\n\n{progress.error}\n\n" +
|
||||
_t("Be aware that already processed fits were not saved"),
|
||||
_message, wx.OK | wx.ICON_ERROR
|
||||
errMsgLbl, wx.OK | wx.ICON_ERROR
|
||||
) as dlg:
|
||||
dlg.ShowModal()
|
||||
return
|
||||
|
||||
# data is str
|
||||
if action & IPortUser.PROCESS_IMPORT:
|
||||
if action & IPortUser.ID_PULSE:
|
||||
_message = ()
|
||||
# update message
|
||||
elif action & IPortUser.ID_UPDATE: # and data != self.progressDialog.message:
|
||||
_message = data
|
||||
|
||||
if _message is not None:
|
||||
self.__progress_flag, _unuse = self.progressDialog.Pulse(_message)
|
||||
else:
|
||||
self.closeProgressDialog()
|
||||
if action & IPortUser.ID_DONE:
|
||||
self._openAfterImport(data)
|
||||
# data is tuple(int, str)
|
||||
elif action & IPortUser.PROCESS_EXPORT:
|
||||
if action & IPortUser.ID_DONE:
|
||||
self.closeProgressDialog()
|
||||
else:
|
||||
self.__progress_flag, _unuse = self.progressDialog.Update(data[0], data[1])
|
||||
elif progress.callback:
|
||||
progress.callback(*progress.cbArgs)
|
||||
|
||||
def _openAfterImport(self, fits):
|
||||
if len(fits) > 0:
|
||||
@@ -988,6 +949,8 @@ class MainFrame(wx.Frame):
|
||||
wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True))
|
||||
else:
|
||||
fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name))
|
||||
# Show 100 fits max
|
||||
fits = fits[:100]
|
||||
results = []
|
||||
for fit in fits:
|
||||
results.append((
|
||||
@@ -999,15 +962,6 @@ class MainFrame(wx.Frame):
|
||||
))
|
||||
wx.PostEvent(self.shipBrowser, ImportSelected(fits=results, back=True))
|
||||
|
||||
def closeProgressDialog(self):
|
||||
# Windows apparently handles ProgressDialogs differently. We can
|
||||
# simply Destroy it here, but for other platforms we must Close it
|
||||
if 'wxMSW' in wx.PlatformInfo:
|
||||
self.progressDialog.Destroy()
|
||||
else:
|
||||
self.progressDialog.EndModal(wx.ID_OK)
|
||||
self.progressDialog.Close()
|
||||
|
||||
def importCharacter(self, event):
|
||||
""" Imports character XML file from EVE API """
|
||||
with wx.FileDialog(
|
||||
|
||||
@@ -146,7 +146,7 @@ class MarketBrowser(wx.Panel):
|
||||
setting = self.settings.get('marketMGSearchMode')
|
||||
# We turn on all meta buttons for the duration of search/recents
|
||||
if setting == 1:
|
||||
if newMode in ('search', 'recent'):
|
||||
if newMode in ('search', 'recent', 'charges'):
|
||||
for btn in self.metaButtons:
|
||||
btn.setUserSelection(True)
|
||||
if newMode == 'normal':
|
||||
|
||||
@@ -2,30 +2,48 @@ import wx
|
||||
import gui.mainFrame
|
||||
import webbrowser
|
||||
import gui.globalEvents as GE
|
||||
import config
|
||||
import time
|
||||
|
||||
from service.settings import EsiSettings
|
||||
|
||||
_t = wx.GetTranslation
|
||||
|
||||
|
||||
class SsoLogin(wx.Dialog):
|
||||
|
||||
def __init__(self):
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def __init__(self, server: config.ApiServer, start_local_server=True):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
from service.esi import Esi
|
||||
super().__init__(
|
||||
mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE,
|
||||
self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE,
|
||||
size=wx.Size(450, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240))
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
text = wx.StaticText(self, wx.ID_ANY, _t("Copy and paste the block of text provided by pyfa.io"))
|
||||
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
|
||||
if start_local_server:
|
||||
text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On."))
|
||||
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
|
||||
bSizer1.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.EXPAND, 15)
|
||||
text = wx.StaticText(self, wx.ID_ANY, _t("If auto-login fails, copy and paste the token provided by pyfa.io"))
|
||||
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
|
||||
elif server.name == "Serenity":
|
||||
text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the url when your authorization is completed"))
|
||||
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
else:
|
||||
text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the token provided by pyfa.io"))
|
||||
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
self.ssoInfoCtrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, (-1, -1), style=wx.TE_MULTILINE)
|
||||
self.ssoInfoCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
|
||||
self.ssoInfoCtrl.Layout()
|
||||
self.ssoInfoCtrl.Bind(wx.EVT_TEXT, self.OnTextEnter)
|
||||
|
||||
bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10)
|
||||
|
||||
self.Esisettings = EsiSettings.getInstance()
|
||||
|
||||
bSizer3 = wx.BoxSizer(wx.VERTICAL)
|
||||
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)
|
||||
|
||||
@@ -34,51 +52,43 @@ class SsoLogin(wx.Dialog):
|
||||
|
||||
self.SetSizer(bSizer1)
|
||||
self.Center()
|
||||
|
||||
from service.esi import Esi
|
||||
|
||||
self.sEsi = Esi.getInstance()
|
||||
uri = self.sEsi.get_login_uri(None)
|
||||
webbrowser.open(uri)
|
||||
|
||||
|
||||
class SsoLoginServer(wx.Dialog):
|
||||
|
||||
def __init__(self, port):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
super().__init__(self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE)
|
||||
|
||||
from service.esi import Esi
|
||||
|
||||
self.sEsi = Esi.getInstance()
|
||||
serverAddr = self.sEsi.startServer(port)
|
||||
|
||||
serverAddr = self.sEsi.startServer(0) if start_local_server else None
|
||||
uri = self.sEsi.get_login_uri(serverAddr)
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
|
||||
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
|
||||
if server.name == "Serenity":
|
||||
webbrowser.open(config.SSO_LOGOFF_SERENITY)
|
||||
time.sleep(1)
|
||||
|
||||
text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On."))
|
||||
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
bSizer3 = wx.BoxSizer(wx.VERTICAL)
|
||||
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)
|
||||
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.BOTTOM | wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
|
||||
|
||||
self.SetSizer(bSizer1)
|
||||
self.Fit()
|
||||
self.Center()
|
||||
self.okBtn = self.FindWindow(wx.ID_OK)
|
||||
self.okBtn.Enable(False)
|
||||
# Ensure we clean up once they hit the "OK" button
|
||||
self.okBtn.Bind(wx.EVT_BUTTON, self.OnDestroy)
|
||||
|
||||
webbrowser.open(uri)
|
||||
|
||||
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
|
||||
# Ensure we clean up if ESC is pressed
|
||||
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
|
||||
|
||||
def OnTextEnter(self, event):
|
||||
t = event.String.strip()
|
||||
if t == "":
|
||||
self.okBtn.Enable(False)
|
||||
else:
|
||||
self.okBtn.Enable(True)
|
||||
event.Skip()
|
||||
|
||||
def OnLogin(self, event):
|
||||
self.EndModal(wx.ID_OK)
|
||||
# This would normally happen if it was logged in via server auto-login. In this case, the modal is done, we effectively want to cancel out
|
||||
self.EndModal(wx.ID_CANCEL)
|
||||
event.Skip()
|
||||
|
||||
def OnDestroy(self, event):
|
||||
# Clean up by unbinding some events and stopping the server
|
||||
self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.OnLogin)
|
||||
if self:
|
||||
self.Unbind(wx.EVT_WINDOW_DESTROY, handler=self.OnDestroy)
|
||||
self.sEsi.stopServer()
|
||||
event.Skip()
|
||||
|
||||
@@ -2,6 +2,8 @@ import wx
|
||||
|
||||
|
||||
def isDark():
|
||||
if 'wxMSW' in wx.PlatformInfo:
|
||||
return False
|
||||
try:
|
||||
return wx.SystemSettings.GetAppearance().IsDark()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
|
||||
@@ -26,20 +26,20 @@ class exportHtml:
|
||||
def __init__(self):
|
||||
self.thread = exportHtmlThread()
|
||||
|
||||
def refreshFittingHtml(self, force=False, callback=False):
|
||||
def refreshFittingHtml(self, force=False, progress=None):
|
||||
settings = HTMLExportSettings.getInstance()
|
||||
|
||||
if force or settings.getEnabled():
|
||||
self.thread.stop()
|
||||
self.thread = exportHtmlThread(callback)
|
||||
self.thread = exportHtmlThread(progress)
|
||||
self.thread.start()
|
||||
|
||||
|
||||
class exportHtmlThread(threading.Thread):
|
||||
def __init__(self, callback=False):
|
||||
def __init__(self, progress=False):
|
||||
threading.Thread.__init__(self)
|
||||
self.name = "HTMLExport"
|
||||
self.callback = callback
|
||||
self.progress = progress
|
||||
self.stopRunning = False
|
||||
|
||||
def stop(self):
|
||||
@@ -72,11 +72,13 @@ class exportHtmlThread(threading.Thread):
|
||||
pass
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as ex:
|
||||
pass
|
||||
|
||||
if self.callback:
|
||||
wx.CallAfter(self.callback, -1)
|
||||
except Exception as e:
|
||||
if self.progress:
|
||||
self.progress.error = f'{e}'
|
||||
finally:
|
||||
if self.progress:
|
||||
self.progress.current += 1
|
||||
self.progress.workerWorking = False
|
||||
|
||||
def generateFullHTML(self, sMkt, sFit, dnaUrl):
|
||||
""" Generate the complete HTML with styling and javascript """
|
||||
@@ -171,13 +173,13 @@ class exportHtmlThread(threading.Thread):
|
||||
</head>
|
||||
<body>
|
||||
<div id="canvas" data-role="page">
|
||||
<div style="text-align: center;"><strong>Last updated:</strong> %s <small>(<span class="timer"></span>)</small></div>
|
||||
<div data-role="header">
|
||||
<h1>Pyfa fits</h1>
|
||||
<h1>Pyfa fits by Group</h1>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div style="text-align: center;"><strong>Last updated:</strong> %s <small>(<span class="timer"></span>)</small></div>
|
||||
|
||||
""" % (time.time(), dnaUrl, localDate)
|
||||
|
||||
HTML += ' <ul data-role="listview" class="ui-listview-outer" data-inset="true" data-filter="true">\n'
|
||||
categoryList = list(sMkt.getShipRoot())
|
||||
categoryList.sort(key=lambda _ship: _ship.name)
|
||||
@@ -214,7 +216,9 @@ class exportHtmlThread(threading.Thread):
|
||||
eftFit = Port.exportEft(getFit(fit[0]), options={
|
||||
PortEftOptions.IMPLANTS: True,
|
||||
PortEftOptions.MUTATIONS: True,
|
||||
PortEftOptions.LOADED_CHARGES: True})
|
||||
PortEftOptions.LOADED_CHARGES: True,
|
||||
PortEftOptions.BOOSTERS: True,
|
||||
PortEftOptions.CARGO: True})
|
||||
|
||||
HTMLfit = (
|
||||
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" '
|
||||
@@ -234,8 +238,8 @@ class exportHtmlThread(threading.Thread):
|
||||
pyfalog.warning("Failed to export line")
|
||||
continue
|
||||
finally:
|
||||
if self.callback:
|
||||
wx.CallAfter(self.callback, count)
|
||||
if self.progress:
|
||||
self.progress.current = count
|
||||
count += 1
|
||||
HTMLgroup += HTMLship + (' </ul>\n'
|
||||
' </li>\n')
|
||||
@@ -254,6 +258,68 @@ class exportHtmlThread(threading.Thread):
|
||||
HTML += """
|
||||
</ul>
|
||||
</div>
|
||||
<div data-role="header">
|
||||
<h1>Pyfa fits by Name</h1>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
"""
|
||||
HTML += ' <ul data-role="listview" class="ui-listview-outer" data-inset="true" data-filter="true">\n'
|
||||
categoryList = list(sMkt.getShipRoot())
|
||||
categoryList.sort(key=lambda _ship: _ship.name)
|
||||
|
||||
count = 0
|
||||
|
||||
for group in categoryList:
|
||||
# init market group string to give ships something to attach to
|
||||
HTMLgroup = ''
|
||||
|
||||
ships = list(sMkt.getShipList(group.ID))
|
||||
ships.sort(key=lambda _ship: _ship.name)
|
||||
|
||||
# Keep track of how many ships per group
|
||||
groupFits = 0
|
||||
for ship in ships:
|
||||
fits = sFit.getFitsWithShip(ship.ID)
|
||||
|
||||
if len(fits) > 0:
|
||||
groupFits += len(fits)
|
||||
|
||||
for fit in fits:
|
||||
if self.stopRunning:
|
||||
return
|
||||
try:
|
||||
eftFit = Port.exportEft(getFit(fit[0]), options={
|
||||
PortEftOptions.IMPLANTS: True,
|
||||
PortEftOptions.MUTATIONS: True,
|
||||
PortEftOptions.LOADED_CHARGES: True,
|
||||
PortEftOptions.BOOSTERS: True,
|
||||
PortEftOptions.CARGO: True})
|
||||
|
||||
HTMLfit = (
|
||||
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" '
|
||||
'data-corners="false">\n'
|
||||
' <h2>' + ship.name + " - " + fit[1] + '</h2>\n'
|
||||
' <ul data-role="listview" data-shadow="false" data-inset="true" '
|
||||
'data-corners="false">\n'
|
||||
)
|
||||
|
||||
HTMLfit += ' <li><pre>' + eftFit + '\n </pre></li>\n'
|
||||
|
||||
HTMLfit += ' </ul>\n </li>\n'
|
||||
HTML += HTMLfit
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
pyfalog.warning("Failed to export line")
|
||||
continue
|
||||
finally:
|
||||
if self.progress:
|
||||
self.progress.current = count
|
||||
count += 1
|
||||
|
||||
HTML += """
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
@@ -291,7 +357,7 @@ class exportHtmlThread(threading.Thread):
|
||||
pyfalog.error("Failed to export line")
|
||||
continue
|
||||
finally:
|
||||
if self.callback:
|
||||
wx.CallAfter(self.callback, count)
|
||||
if self.progress:
|
||||
self.progress.current = count
|
||||
count += 1
|
||||
return HTML
|
||||
|
||||
@@ -58,6 +58,9 @@ class InputValidator(metaclass=ABCMeta):
|
||||
class FloatBox(wx.TextCtrl):
|
||||
|
||||
def __init__(self, parent, value, id=wx.ID_ANY, style=0, validator=None, **kwargs):
|
||||
# Workaround for #2591
|
||||
if 'wxMac' in wx.PlatformInfo and 'size' not in kwargs:
|
||||
kwargs['size'] = wx.Size(97, 26)
|
||||
super().__init__(parent=parent, id=id, style=style, **kwargs)
|
||||
self.Bind(wx.EVT_TEXT, self.OnText)
|
||||
self._storedValue = ''
|
||||
@@ -107,6 +110,9 @@ class FloatBox(wx.TextCtrl):
|
||||
class FloatRangeBox(wx.TextCtrl):
|
||||
|
||||
def __init__(self, parent, value, id=wx.ID_ANY, style=0, **kwargs):
|
||||
# Workaround for #2591
|
||||
if 'wxMac' in wx.PlatformInfo and 'size' not in kwargs:
|
||||
kwargs['size'] = wx.Size(97, 26)
|
||||
super().__init__(parent=parent, id=id, style=style, **kwargs)
|
||||
self.Bind(wx.EVT_TEXT, self.OnText)
|
||||
self._storedValue = ''
|
||||
|
||||
19
gui/utils/progressHelper.py
Normal file
@@ -0,0 +1,19 @@
|
||||
class ProgressHelper:
|
||||
|
||||
def __init__(self, message, maximum=None, callback=None):
|
||||
self.message = message
|
||||
self.current = 0
|
||||
self.maximum = maximum
|
||||
self.workerWorking = True
|
||||
self.dlgWorking = True
|
||||
self.error = None
|
||||
self.callback = callback
|
||||
self.cbArgs = []
|
||||
|
||||
@property
|
||||
def working(self):
|
||||
return self.workerWorking and self.dlgWorking and not self.error
|
||||
|
||||
@property
|
||||
def userCancelled(self):
|
||||
return not self.dlgWorking
|
||||
BIN
imgs/icons/10155@1x.png
Normal file
|
After Width: | Height: | Size: 828 B |
BIN
imgs/icons/10155@2x.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
imgs/icons/10848@1x.png
Normal file
|
After Width: | Height: | Size: 673 B |
BIN
imgs/icons/10848@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/24603@1x.png
Normal file
|
After Width: | Height: | Size: 915 B |
BIN
imgs/icons/24603@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
imgs/icons/25994@1x.png
Normal file
|
After Width: | Height: | Size: 918 B |
BIN
imgs/icons/25994@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
imgs/icons/26002@1x.png
Normal file
|
After Width: | Height: | Size: 910 B |
BIN
imgs/icons/26002@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
imgs/icons/26004@1x.png
Normal file
|
After Width: | Height: | Size: 761 B |
BIN
imgs/icons/26004@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
imgs/icons/26005@1x.png
Normal file
|
After Width: | Height: | Size: 767 B |
BIN
imgs/icons/26005@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
imgs/icons/26016@1x.png
Normal file
|
After Width: | Height: | Size: 821 B |
BIN
imgs/icons/26016@2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
imgs/icons/26017@1x.png
Normal file
|
After Width: | Height: | Size: 832 B |
BIN
imgs/icons/26017@2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
imgs/icons/26018@1x.png
Normal file
|
After Width: | Height: | Size: 822 B |
BIN
imgs/icons/26018@2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
imgs/icons/26019@1x.png
Normal file
|
After Width: | Height: | Size: 832 B |
BIN
imgs/icons/26019@2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
imgs/icons/26038@1x.png
Normal file
|
After Width: | Height: | Size: 980 B |
BIN
imgs/icons/26038@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
imgs/icons/26039@1x.png
Normal file
|
After Width: | Height: | Size: 945 B |
BIN
imgs/icons/26039@2x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
imgs/icons/26040@1x.png
Normal file
|
After Width: | Height: | Size: 947 B |
BIN
imgs/icons/26040@2x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
imgs/icons/26041@1x.png
Normal file
|
After Width: | Height: | Size: 951 B |
BIN
imgs/icons/26041@2x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
imgs/icons/26042@1x.png
Normal file
|
After Width: | Height: | Size: 983 B |
BIN
imgs/icons/26042@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
imgs/icons/26043@1x.png
Normal file
|
After Width: | Height: | Size: 961 B |
BIN
imgs/icons/26043@2x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
imgs/icons/26044@1x.png
Normal file
|
After Width: | Height: | Size: 988 B |
BIN
imgs/icons/26044@2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
imgs/icons/26048@1x.png
Normal file
|
After Width: | Height: | Size: 680 B |
BIN
imgs/icons/26048@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/26049@1x.png
Normal file
|
After Width: | Height: | Size: 689 B |
BIN
imgs/icons/26049@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/26050@1x.png
Normal file
|
After Width: | Height: | Size: 697 B |
BIN
imgs/icons/26050@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/26051@1x.png
Normal file
|
After Width: | Height: | Size: 725 B |
BIN
imgs/icons/26051@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/26052@1x.png
Normal file
|
After Width: | Height: | Size: 805 B |
BIN
imgs/icons/26052@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/26053@1x.png
Normal file
|
After Width: | Height: | Size: 832 B |
BIN
imgs/icons/26053@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/26054@1x.png
Normal file
|
After Width: | Height: | Size: 772 B |
BIN
imgs/icons/26054@2x.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
imgs/icons/26055@1x.png
Normal file
|
After Width: | Height: | Size: 725 B |
BIN
imgs/icons/26055@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/26056@1x.png
Normal file
|
After Width: | Height: | Size: 692 B |
BIN
imgs/icons/26056@2x.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
imgs/icons/26062@1x.png
Normal file
|
After Width: | Height: | Size: 315 B |
BIN
imgs/icons/26062@2x.png
Normal file
|
After Width: | Height: | Size: 158 B |
BIN
imgs/icons/26065@1x.png
Normal file
|
After Width: | Height: | Size: 442 B |
BIN
imgs/icons/26065@2x.png
Normal file
|
After Width: | Height: | Size: 498 B |
BIN
imgs/icons/26069@1x.png
Normal file
|
After Width: | Height: | Size: 868 B |
BIN
imgs/icons/26069@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
imgs/icons/26070@1x.png
Normal file
|
After Width: | Height: | Size: 821 B |
BIN
imgs/icons/26070@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
imgs/icons/26071@1x.png
Normal file
|
After Width: | Height: | Size: 759 B |
BIN
imgs/icons/26071@2x.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
imgs/icons/26072@1x.png
Normal file
|
After Width: | Height: | Size: 783 B |
BIN
imgs/icons/26072@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
imgs/icons/26073@1x.png
Normal file
|
After Width: | Height: | Size: 807 B |
BIN
imgs/icons/26073@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |