Compare commits
136 Commits
v2.21.1dev
...
v2.27.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26ec741094 | ||
|
|
530f7510d5 | ||
|
|
37ab704cd7 | ||
|
|
683ad3802f | ||
|
|
c882351886 | ||
|
|
9d71215c52 | ||
|
|
d2a0605abe | ||
|
|
bee6652cc6 | ||
|
|
047ef75960 | ||
|
|
32e57f8138 | ||
|
|
e891804b64 | ||
|
|
7d42b89726 | ||
|
|
6561996327 | ||
|
|
0d2ad6eac2 | ||
|
|
3b2bfd01c5 | ||
|
|
a317dab9a8 | ||
|
|
db6d8b93e9 | ||
|
|
7d585c1a62 | ||
|
|
c45e84470e | ||
|
|
ab3b40e136 | ||
|
|
b566a8bfa6 | ||
|
|
ccb395d592 | ||
|
|
3d039724c9 | ||
|
|
d0e7e7eed5 | ||
|
|
a387bc8d09 | ||
|
|
94344bd432 | ||
|
|
a976fb33f0 | ||
|
|
f850fdf0d5 | ||
|
|
0fcbedba45 | ||
|
|
5707bddacd | ||
|
|
8ed9257dfa | ||
|
|
056ae590cf | ||
|
|
7733fd38c2 | ||
|
|
fef78c971f | ||
|
|
e52ceacdb9 | ||
|
|
85b2d7af8d | ||
|
|
3e658a31bb | ||
|
|
0d2a4d4d44 | ||
|
|
bc2cdcdea7 | ||
|
|
30ad554cc0 | ||
|
|
4a85fd5d1b | ||
|
|
4d8dbe74bd | ||
|
|
016f2b44ff | ||
|
|
676794baed | ||
|
|
ca488089fd | ||
|
|
96f9b9a719 | ||
|
|
508572e08b | ||
|
|
f91e0b2e23 | ||
|
|
93ae9e0891 | ||
|
|
2ed5dbc3c7 | ||
|
|
bca8ba3114 | ||
|
|
174ac97682 | ||
|
|
2d9e873d42 | ||
|
|
33377357f6 | ||
|
|
3ee0ee7e40 | ||
|
|
2f02747b29 | ||
|
|
78b176a135 | ||
|
|
52063beea9 | ||
|
|
03a55d94e9 | ||
|
|
db256f57d1 | ||
|
|
8de6d78be0 | ||
|
|
902a00d37d | ||
|
|
523cb1467e | ||
|
|
f75de70d79 | ||
|
|
4b635f4d21 | ||
|
|
63632e09b3 | ||
|
|
df78eb5781 | ||
|
|
c1a5828d6b | ||
|
|
5377210b89 | ||
|
|
1936255f2c | ||
|
|
b3e5763cfc | ||
|
|
7442f315c9 | ||
|
|
433c9555bf | ||
|
|
d1345fc71e | ||
|
|
31cae0e54b | ||
|
|
b2cc3ae600 | ||
|
|
62d1d6a06d | ||
|
|
83fa567321 | ||
|
|
9b315b5870 | ||
|
|
3094fd32fc | ||
|
|
c3f1824a84 | ||
|
|
b558ae3810 | ||
|
|
c38f05902a | ||
|
|
181e1e1e30 | ||
|
|
3fc26254ae | ||
|
|
ca8822c455 | ||
|
|
4c55d32086 | ||
|
|
c13817708e | ||
|
|
39cab4be72 | ||
|
|
f80473d073 | ||
|
|
0d9be1b174 | ||
|
|
83b0b3ff0e | ||
|
|
c4b490907d | ||
|
|
f89fe4d5d7 | ||
|
|
81dced03bf | ||
|
|
27d3bea8d8 | ||
|
|
aec5a46452 | ||
|
|
588f9a4490 | ||
|
|
4ba948c6f9 | ||
|
|
eacc97160b | ||
|
|
78423f67dd | ||
|
|
61e32d0b1b | ||
|
|
bf04d26a7b | ||
|
|
2217aff5ab | ||
|
|
400d0aaa22 | ||
|
|
bfc928934c | ||
|
|
395c17270e | ||
|
|
1b40467775 | ||
|
|
27e3e53868 | ||
|
|
00b9884c68 | ||
|
|
b05bd04801 | ||
|
|
7ba5585b83 | ||
|
|
9386ba3fb9 | ||
|
|
1321e70035 | ||
|
|
e2d943b0b0 | ||
|
|
424b769ba9 | ||
|
|
3671f10c9c | ||
|
|
935ecd0ea7 | ||
|
|
92e15ece72 | ||
|
|
a5c1875a29 | ||
|
|
9146c0f2c6 | ||
|
|
a917207e07 | ||
|
|
c21b92945f | ||
|
|
049bd10797 | ||
|
|
b486276167 | ||
|
|
e4df215e47 | ||
|
|
33cb332978 | ||
|
|
7c8f4d51bb | ||
|
|
66bf7b3dc6 | ||
|
|
8bb1d43d0c | ||
|
|
3d70ca941c | ||
|
|
04a1e95730 | ||
|
|
9fddb64ef9 | ||
|
|
e883035120 | ||
|
|
4d2da8e52e | ||
|
|
c1df335f08 |
15
db_update.py
@@ -33,7 +33,7 @@ DB_PATH = os.path.join(ROOT_DIR, 'eve.db')
|
|||||||
JSON_DIR = os.path.join(ROOT_DIR, 'staticdata')
|
JSON_DIR = os.path.join(ROOT_DIR, 'staticdata')
|
||||||
if ROOT_DIR not in sys.path:
|
if ROOT_DIR not in sys.path:
|
||||||
sys.path.insert(0, ROOT_DIR)
|
sys.path.insert(0, ROOT_DIR)
|
||||||
GAMEDATA_SCHEMA_VERSION = 3
|
GAMEDATA_SCHEMA_VERSION = 4
|
||||||
|
|
||||||
|
|
||||||
def db_needs_update():
|
def db_needs_update():
|
||||||
@@ -122,9 +122,11 @@ def update_db():
|
|||||||
if (
|
if (
|
||||||
# Apparently people really want Civilian modules available
|
# Apparently people really want Civilian modules available
|
||||||
(row['typeName'].startswith('Civilian') and "Shuttle" not in row['typeName']) or
|
(row['typeName'].startswith('Civilian') and "Shuttle" not in row['typeName']) or
|
||||||
row['typeName'] in ('Capsule', 'Dark Blood Tracking Disruptor')
|
row['typeName'] == 'Capsule' or
|
||||||
|
row['groupID'] == 4033 # destructible effect beacons
|
||||||
):
|
):
|
||||||
row['published'] = True
|
row['published'] = True
|
||||||
|
# Nearly useless and clutter search results too much
|
||||||
elif row['typeName'].startswith('Limited Synth '):
|
elif row['typeName'].startswith('Limited Synth '):
|
||||||
row['published'] = False
|
row['published'] = False
|
||||||
|
|
||||||
@@ -138,11 +140,10 @@ def update_db():
|
|||||||
row['typeID'] in (41549, 41548, 41551, 41550) or
|
row['typeID'] in (41549, 41548, 41551, 41550) or
|
||||||
# Abyssal weather (environment)
|
# Abyssal weather (environment)
|
||||||
row['groupID'] in (
|
row['groupID'] in (
|
||||||
1882,
|
1882,
|
||||||
1975,
|
1975,
|
||||||
1971,
|
1971,
|
||||||
# the "container" for the abyssal environments
|
1983) # the "container" for the abyssal environments
|
||||||
1983)
|
|
||||||
):
|
):
|
||||||
newData.append(row)
|
newData.append(row)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
Migration 39
|
Migration 39
|
||||||
|
|
||||||
- Shield amplifier tiericide
|
- Shield amplifier tiericide
|
||||||
|
- CCP getting rid of DB TDs due to exploits
|
||||||
"""
|
"""
|
||||||
|
|
||||||
CONVERSIONS = {
|
CONVERSIONS = {
|
||||||
@@ -16,6 +17,9 @@ CONVERSIONS = {
|
|||||||
),
|
),
|
||||||
1800: ( # 'Basic' Thermal Shield Amplifier
|
1800: ( # 'Basic' Thermal Shield Amplifier
|
||||||
9566, # Supplemental Thermal Dissipation Amplifier
|
9566, # Supplemental Thermal Dissipation Amplifier
|
||||||
|
),
|
||||||
|
22933: ( # 'Investor' Tracking Disruptor I
|
||||||
|
32416, # Dark Blood Tracking Disruptor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
eos/db/migrations/upgrade40.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""
|
||||||
|
Migration 40
|
||||||
|
|
||||||
|
Imports all item conversions since Migration 28 and runs them against module.baseItemID. This column seems to have been
|
||||||
|
forgotten about since it's been added.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from .upgrade36 import CONVERSIONS as u36
|
||||||
|
from .upgrade37 import CONVERSIONS as u37
|
||||||
|
from .upgrade38 import CONVERSIONS as u38
|
||||||
|
from .upgrade39 import CONVERSIONS as u39
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
for conversions in [u36, u37, u38, u39]:
|
||||||
|
for replacement_item, list in conversions.items():
|
||||||
|
for retired_item in list:
|
||||||
|
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||||
|
(replacement_item, retired_item))
|
||||||
50
eos/db/migrations/upgrade41.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Migration 41
|
||||||
|
|
||||||
|
- Resistance plating tiericide
|
||||||
|
"""
|
||||||
|
|
||||||
|
CONVERSIONS = {
|
||||||
|
16345: ( # Upgraded Layered Coating I
|
||||||
|
16347, # Limited Layered Plating I
|
||||||
|
16349, # 'Scarab' Layered Plating I
|
||||||
|
16351, # 'Grail' Layered Plating I
|
||||||
|
),
|
||||||
|
16305: ( # Upgraded Multispectrum Coating I
|
||||||
|
16307, # Limited Adaptive Nano Plating I
|
||||||
|
16309, # 'Collateral' Adaptive Nano Plating I
|
||||||
|
16311, # 'Refuge' Adaptive Nano Plating I
|
||||||
|
),
|
||||||
|
16329: ( # Upgraded EM Coating I
|
||||||
|
16331, # Limited EM Plating I
|
||||||
|
16333, # 'Contour' EM Plating I
|
||||||
|
16335, # 'Spiegel' EM Plating I
|
||||||
|
),
|
||||||
|
16321: ( # Upgraded Explosive Coating I
|
||||||
|
16323, # Limited Explosive Plating I
|
||||||
|
16325, # Experimental Explosive Plating I
|
||||||
|
16319, # 'Aegis' Explosive Plating I
|
||||||
|
),
|
||||||
|
16313: ( # Upgraded Kinetic Coating I
|
||||||
|
16315, # Limited Kinetic Plating I
|
||||||
|
16317, # Experimental Kinetic Plating I
|
||||||
|
16327, # 'Element' Kinetic Plating I
|
||||||
|
),
|
||||||
|
16337: ( # Upgraded Thermal Coating I
|
||||||
|
16339, # Limited Thermal Plating I
|
||||||
|
16341, # Experimental Thermal Plating I
|
||||||
|
16343, # Prototype Thermal Plating I
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
# Convert modules
|
||||||
|
for replacement_item, list in CONVERSIONS.items():
|
||||||
|
for retired_item in list:
|
||||||
|
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||||
|
(replacement_item, retired_item))
|
||||||
|
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||||
|
(replacement_item, retired_item))
|
||||||
|
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||||
|
(replacement_item, retired_item))
|
||||||
50
eos/db/migrations/upgrade42.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Migration 42
|
||||||
|
|
||||||
|
- Resistance membrane tiericide
|
||||||
|
"""
|
||||||
|
|
||||||
|
CONVERSIONS = {
|
||||||
|
16391: ( # Compact Multispectrum Energized Membrane
|
||||||
|
16389, # Experimental Energized Adaptive Nano Membrane I
|
||||||
|
16387, # Limited Energized Adaptive Nano Membrane I
|
||||||
|
16385, # Upgraded Energized Adaptive Nano Membrane I
|
||||||
|
),
|
||||||
|
16423: ( # Compact Layered Energized Membrane
|
||||||
|
16421, # Experimental Energized Armor Layering Membrane I
|
||||||
|
16419, # Limited Energized Armor Layering Membrane I
|
||||||
|
16417, # Upgraded Energized Armor Layering Membrane I
|
||||||
|
),
|
||||||
|
16415: ( # Compact EM Energized Membrane
|
||||||
|
16413, # Experimental Energized EM Membrane I
|
||||||
|
16411, # Limited Energized EM Membrane I
|
||||||
|
16409, # Upgraded Energized EM Membrane I
|
||||||
|
),
|
||||||
|
16407: ( # Compact Explosive Energized Membrane
|
||||||
|
16405, # Experimental Energized Explosive Membrane I
|
||||||
|
16403, # Limited Energized Explosive Membrane I
|
||||||
|
16401, # Upgraded Energized Explosive Membrane I
|
||||||
|
),
|
||||||
|
16399: ( # Compact Kinetic Energized Membrane
|
||||||
|
16397, # Experimental Energized Kinetic Membrane I
|
||||||
|
16395, # Limited Energized Kinetic Membrane I
|
||||||
|
16393, # Upgraded Energized Kinetic Membrane I
|
||||||
|
),
|
||||||
|
16431: ( # Compact Thermal Energized Membrane
|
||||||
|
16429, # Experimental Energized Thermal Membrane I
|
||||||
|
16427, # Limited Energized Thermal Membrane I
|
||||||
|
16425, # Upgraded Energized Thermal Membrane I
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(saveddata_engine):
|
||||||
|
# Convert modules
|
||||||
|
for replacement_item, list in CONVERSIONS.items():
|
||||||
|
for retired_item in list:
|
||||||
|
saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?',
|
||||||
|
(replacement_item, retired_item))
|
||||||
|
saveddata_engine.execute('UPDATE "modules" SET "baseItemID" = ? WHERE "baseItemID" = ?',
|
||||||
|
(replacement_item, retired_item))
|
||||||
|
saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||||
|
(replacement_item, retired_item))
|
||||||
740
eos/effects.py
@@ -146,6 +146,12 @@ class Effect(EqBase):
|
|||||||
|
|
||||||
return self.__effectDef is not None
|
return self.__effectDef is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dealsDamage(self):
|
||||||
|
if not self.__generated:
|
||||||
|
self.__generateHandler()
|
||||||
|
return self.__dealsDamage
|
||||||
|
|
||||||
def isType(self, type):
|
def isType(self, type):
|
||||||
"""
|
"""
|
||||||
Check if this effect is of the passed type
|
Check if this effect is of the passed type
|
||||||
@@ -167,6 +173,7 @@ class Effect(EqBase):
|
|||||||
self.__handler = getattr(effectDef, "handler", eos.effects.BaseEffect.handler)
|
self.__handler = getattr(effectDef, "handler", eos.effects.BaseEffect.handler)
|
||||||
self.__runTime = getattr(effectDef, "runTime", "normal")
|
self.__runTime = getattr(effectDef, "runTime", "normal")
|
||||||
self.__activeByDefault = getattr(effectDef, "activeByDefault", True)
|
self.__activeByDefault = getattr(effectDef, "activeByDefault", True)
|
||||||
|
self.__dealsDamage = effectDef.dealsDamage
|
||||||
effectType = getattr(effectDef, "type", None)
|
effectType = getattr(effectDef, "type", None)
|
||||||
effectType = effectType if isinstance(effectType, tuple) or effectType is None else (effectType,)
|
effectType = effectType if isinstance(effectType, tuple) or effectType is None else (effectType,)
|
||||||
self.__type = effectType
|
self.__type = effectType
|
||||||
@@ -175,6 +182,7 @@ class Effect(EqBase):
|
|||||||
self.__handler = eos.effects.DummyEffect.handler
|
self.__handler = eos.effects.DummyEffect.handler
|
||||||
self.__runTime = "normal"
|
self.__runTime = "normal"
|
||||||
self.__activeByDefault = True
|
self.__activeByDefault = True
|
||||||
|
self.__dealsDamage = False
|
||||||
self.__type = None
|
self.__type = None
|
||||||
pyfalog.debug("ImportError generating handler: {0}", e)
|
pyfalog.debug("ImportError generating handler: {0}", e)
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
@@ -182,6 +190,7 @@ class Effect(EqBase):
|
|||||||
self.__handler = eos.effects.DummyEffect.handler
|
self.__handler = eos.effects.DummyEffect.handler
|
||||||
self.__runTime = "normal"
|
self.__runTime = "normal"
|
||||||
self.__activeByDefault = True
|
self.__activeByDefault = True
|
||||||
|
self.__dealsDamage = False
|
||||||
self.__type = None
|
self.__type = None
|
||||||
pyfalog.error("AttributeError generating handler: {0}", e)
|
pyfalog.error("AttributeError generating handler: {0}", e)
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
@@ -190,6 +199,7 @@ class Effect(EqBase):
|
|||||||
self.__handler = eos.effects.DummyEffect.handler
|
self.__handler = eos.effects.DummyEffect.handler
|
||||||
self.__runTime = "normal"
|
self.__runTime = "normal"
|
||||||
self.__activeByDefault = True
|
self.__activeByDefault = True
|
||||||
|
self.__dealsDamage = False
|
||||||
self.__type = None
|
self.__type = None
|
||||||
pyfalog.critical("Exception generating handler:")
|
pyfalog.critical("Exception generating handler:")
|
||||||
pyfalog.critical(e)
|
pyfalog.critical(e)
|
||||||
@@ -333,7 +343,11 @@ class Item(EqBase):
|
|||||||
if self.__race is None:
|
if self.__race is None:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.category.categoryName == 'Structure':
|
if (
|
||||||
|
self.category.categoryName == 'Structure' or
|
||||||
|
# Here until CCP puts their shit together
|
||||||
|
self.name in ("Thunderchild", "Stormbringer", "Skybreaker")
|
||||||
|
):
|
||||||
self.__race = "upwell"
|
self.__race = "upwell"
|
||||||
else:
|
else:
|
||||||
self.__race = self.factionMap[self.factionID]
|
self.__race = self.factionMap[self.factionID]
|
||||||
|
|||||||
@@ -364,3 +364,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
if self.item.groupID in fitDroneGroupLimits:
|
if self.item.groupID in fitDroneGroupLimits:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def canDealDamage(self, ignoreState=False):
|
||||||
|
if self.item is None:
|
||||||
|
return False
|
||||||
|
for effect in self.item.effects.values():
|
||||||
|
if effect.dealsDamage and (ignoreState or self.amountActive > 0):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|||||||
@@ -441,3 +441,15 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def canDealDamage(self, ignoreState=False, ignoreAbilityState=False):
|
||||||
|
if self.item is None:
|
||||||
|
return False
|
||||||
|
if not self.active and not ignoreState:
|
||||||
|
return False
|
||||||
|
for ability in self.abilities:
|
||||||
|
if not ability.active and not ignoreAbilityState:
|
||||||
|
continue
|
||||||
|
if ability.effect.dealsDamage:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import datetime
|
|||||||
import time
|
import time
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from math import log, sqrt
|
from math import floor, log, sqrt
|
||||||
|
|
||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
from sqlalchemy.orm import reconstructor, validates
|
from sqlalchemy.orm import reconstructor, validates
|
||||||
@@ -39,6 +39,7 @@ from eos.saveddata.damagePattern import DamagePattern
|
|||||||
from eos.saveddata.module import Module
|
from eos.saveddata.module import Module
|
||||||
from eos.saveddata.ship import Ship
|
from eos.saveddata.ship import Ship
|
||||||
from eos.saveddata.targetProfile import TargetProfile
|
from eos.saveddata.targetProfile import TargetProfile
|
||||||
|
from eos.utils.float import floatUnerr
|
||||||
from eos.utils.stats import DmgTypes, RRTypes
|
from eos.utils.stats import DmgTypes, RRTypes
|
||||||
|
|
||||||
|
|
||||||
@@ -378,8 +379,9 @@ class Fit:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def maxTargets(self):
|
def maxTargets(self):
|
||||||
return min(self.extraAttributes["maxTargetsLockedFromSkills"],
|
maxTargets = min(self.extraAttributes["maxTargetsLockedFromSkills"],
|
||||||
self.ship.getModifiedItemAttr("maxLockedTargets"))
|
self.ship.getModifiedItemAttr("maxLockedTargets"))
|
||||||
|
return floor(floatUnerr(maxTargets))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def maxTargetRange(self):
|
def maxTargetRange(self):
|
||||||
@@ -750,6 +752,8 @@ class Fit:
|
|||||||
|
|
||||||
if warfareBuffID == 79: # AOE_Beacon_bioluminescence_cloud
|
if warfareBuffID == 79: # AOE_Beacon_bioluminescence_cloud
|
||||||
self.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
|
self.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"signatureRadius", value, stackingPenalties=True)
|
||||||
|
|
||||||
if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_local_repair
|
if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_local_repair
|
||||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"),
|
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"),
|
||||||
@@ -772,7 +776,11 @@ class Fit:
|
|||||||
if warfareBuffID == 90: # Weather_electric_storm_EM_resistance_penalty
|
if warfareBuffID == 90: # Weather_electric_storm_EM_resistance_penalty
|
||||||
for tankType in ("shield", "armor"):
|
for tankType in ("shield", "armor"):
|
||||||
self.ship.boostItemAttr("{}EmDamageResonance".format(tankType), value)
|
self.ship.boostItemAttr("{}EmDamageResonance".format(tankType), value)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"{}EmDamageResonance".format(tankType), value)
|
||||||
self.ship.boostItemAttr("emDamageResonance", value) # for hull
|
self.ship.boostItemAttr("emDamageResonance", value) # for hull
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"emDamageResonance", value) #for hull
|
||||||
|
|
||||||
if warfareBuffID == 92: # Weather_electric_storm_capacitor_recharge_bonus
|
if warfareBuffID == 92: # Weather_electric_storm_capacitor_recharge_bonus
|
||||||
self.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True)
|
self.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True)
|
||||||
@@ -780,32 +788,54 @@ class Fit:
|
|||||||
if warfareBuffID == 93: # Weather_xenon_gas_explosive_resistance_penalty
|
if warfareBuffID == 93: # Weather_xenon_gas_explosive_resistance_penalty
|
||||||
for tankType in ("shield", "armor"):
|
for tankType in ("shield", "armor"):
|
||||||
self.ship.boostItemAttr("{}ExplosiveDamageResonance".format(tankType), value)
|
self.ship.boostItemAttr("{}ExplosiveDamageResonance".format(tankType), value)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"{}ExplosiveDamageResonance".format(tankType), value)
|
||||||
self.ship.boostItemAttr("explosiveDamageResonance", value) # for hull
|
self.ship.boostItemAttr("explosiveDamageResonance", value) # for hull
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"explosiveDamageResonance", value) # for hull
|
||||||
|
|
||||||
if warfareBuffID == 94: # Weather_xenon_gas_shield_hp_bonus
|
if warfareBuffID == 94: # Weather_xenon_gas_shield_hp_bonus
|
||||||
self.ship.boostItemAttr("shieldCapacity", value) # for hull
|
self.ship.boostItemAttr("shieldCapacity", value)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"shieldCapacity", value)
|
||||||
|
|
||||||
if warfareBuffID == 95: # Weather_infernal_thermal_resistance_penalty
|
if warfareBuffID == 95: # Weather_infernal_thermal_resistance_penalty
|
||||||
for tankType in ("shield", "armor"):
|
for tankType in ("shield", "armor"):
|
||||||
self.ship.boostItemAttr("{}ThermalDamageResonance".format(tankType), value)
|
self.ship.boostItemAttr("{}ThermalDamageResonance".format(tankType), value)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"{}ThermalDamageResonance".format(tankType), value)
|
||||||
self.ship.boostItemAttr("thermalDamageResonance", value) # for hull
|
self.ship.boostItemAttr("thermalDamageResonance", value) # for hull
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"thermalDamageResonance", value) # for hull
|
||||||
|
|
||||||
if warfareBuffID == 96: # Weather_infernal_armor_hp_bonus
|
if warfareBuffID == 96: # Weather_infernal_armor_hp_bonus
|
||||||
self.ship.boostItemAttr("armorHP", value) # for hull
|
self.ship.boostItemAttr("armorHP", value)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"armorHP", value)
|
||||||
|
|
||||||
if warfareBuffID == 97: # Weather_darkness_turret_range_penalty
|
if warfareBuffID == 97: # Weather_darkness_turret_range_penalty
|
||||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||||
"maxRange", value, stackingPenalties=True)
|
"maxRange", value, stackingPenalties=True)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"maxRange", value, stackingPenalties=True)
|
||||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||||
"falloff", value, stackingPenalties=True)
|
"falloff", value, stackingPenalties=True)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"falloff", value, stackingPenalties=True)
|
||||||
|
|
||||||
if warfareBuffID == 98: # Weather_darkness_velocity_bonus
|
if warfareBuffID == 98: # Weather_darkness_velocity_bonus
|
||||||
self.ship.boostItemAttr("maxVelocity", value)
|
self.ship.boostItemAttr("maxVelocity", value)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"maxVelocity", value)
|
||||||
|
|
||||||
if warfareBuffID == 99: # Weather_caustic_toxin_kinetic_resistance_penalty
|
if warfareBuffID == 99: # Weather_caustic_toxin_kinetic_resistance_penalty
|
||||||
for tankType in ("shield", "armor"):
|
for tankType in ("shield", "armor"):
|
||||||
self.ship.boostItemAttr("{}KineticDamageResonance".format(tankType), value)
|
self.ship.boostItemAttr("{}KineticDamageResonance".format(tankType), value)
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"{}KineticDamageResonance".format(tankType), value)
|
||||||
self.ship.boostItemAttr("kineticDamageResonance", value) # for hull
|
self.ship.boostItemAttr("kineticDamageResonance", value) # for hull
|
||||||
|
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||||
|
"kineticDamageResonance", value) # for hull
|
||||||
|
|
||||||
if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus
|
if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus
|
||||||
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
|
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ ProjectedSystem = {
|
|||||||
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||||
"""An instance of this class represents a module together with its charge and modified attributes"""
|
"""An instance of this class represents a module together with its charge and modified attributes"""
|
||||||
MINING_ATTRIBUTES = ("miningAmount",)
|
MINING_ATTRIBUTES = ("miningAmount",)
|
||||||
SYSTEM_GROUPS = ("Effect Beacon", "MassiveEnvironments", "Abyssal Hazards", "Non-Interactable Object")
|
SYSTEM_GROUPS = (
|
||||||
|
"Effect Beacon", "MassiveEnvironments", "Abyssal Hazards",
|
||||||
|
"Non-Interactable Object", "Destructible Effect Beacon")
|
||||||
|
|
||||||
def __init__(self, item, baseItem=None, mutaplasmid=None):
|
def __init__(self, item, baseItem=None, mutaplasmid=None):
|
||||||
"""Initialize a module from the program"""
|
"""Initialize a module from the program"""
|
||||||
@@ -265,7 +267,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def isExclusiveSystemEffect(self):
|
def isExclusiveSystemEffect(self):
|
||||||
return self.item.group.name in ("Effect Beacon", "Non-Interactable Object", "MassiveEnvironments")
|
# See issue #2258
|
||||||
|
# return self.item.group.name in ("Effect Beacon", "Non-Interactable Object", "MassiveEnvironments")
|
||||||
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def isCapitalSize(self):
|
def isCapitalSize(self):
|
||||||
@@ -461,6 +465,20 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def canDealDamage(self, ignoreState=False):
|
||||||
|
if self.isEmpty:
|
||||||
|
return False
|
||||||
|
for effect in self.item.effects.values():
|
||||||
|
if effect.dealsDamage and (
|
||||||
|
ignoreState or
|
||||||
|
effect.isType('offline') or
|
||||||
|
(effect.isType('passive') and self.state >= FittingModuleState.ONLINE) or
|
||||||
|
(effect.isType('active') and self.state >= FittingModuleState.ACTIVE) or
|
||||||
|
(effect.isType('overheat') and self.state >= FittingModuleState.OVERHEATED)
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def getVolleyParameters(self, spoolOptions=None, targetProfile=None, ignoreState=False):
|
def getVolleyParameters(self, spoolOptions=None, targetProfile=None, ignoreState=False):
|
||||||
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
|
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
|
||||||
return {0: DmgTypes(0, 0, 0, 0)}
|
return {0: DmgTypes(0, 0, 0, 0)}
|
||||||
@@ -712,6 +730,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
return False
|
return False
|
||||||
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
|
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
|
||||||
return False
|
return False
|
||||||
|
# Some destructible effect beacons contain active effects, hardcap those at online state
|
||||||
|
elif state > FittingModuleState.ONLINE and self.slot == FittingSlot.SYSTEM:
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1037,7 +1058,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
|||||||
elif click == "ctrl":
|
elif click == "ctrl":
|
||||||
state = FittingModuleState.OFFLINE
|
state = FittingModuleState.OFFLINE
|
||||||
else:
|
else:
|
||||||
state = transitionMap[currState]
|
try:
|
||||||
|
state = transitionMap[currState]
|
||||||
|
except KeyError:
|
||||||
|
state = min(transitionMap)
|
||||||
# If passive module tries to transition into online and fails,
|
# If passive module tries to transition into online and fails,
|
||||||
# put it to passive instead
|
# put it to passive instead
|
||||||
if not mod.isValidState(state) and currState == FittingModuleState.ONLINE:
|
if not mod.isValidState(state) and currState == FittingModuleState.ONLINE:
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ BUILTINS = OrderedDict([
|
|||||||
# Source: ticket #2067
|
# Source: ticket #2067
|
||||||
(-52, ('[NPC][Invasion]Invading Precursor Entities', 0.422, 0.367, 0.453, 0.411)),
|
(-52, ('[NPC][Invasion]Invading Precursor Entities', 0.422, 0.367, 0.453, 0.411)),
|
||||||
(-53, ('[NPC][Invasion]Retaliating Amarr Entities', 0.360, 0.310, 0.441, 0.602)),
|
(-53, ('[NPC][Invasion]Retaliating Amarr Entities', 0.360, 0.310, 0.441, 0.602)),
|
||||||
(-54, ('[NPC][Invasion]Retaliating Caldari Entities', 0.287, 0.610, 0.487, 0.401)),
|
(-54, ('[NPC][Invasion]Retaliating Caldari Entities', 0.303, 0.610, 0.487, 0.401)),
|
||||||
(-55, ('[NPC][Invasion]Retaliating Gallente Entities', 0.383, 0.414, 0.578, 0.513)),
|
(-55, ('[NPC][Invasion]Retaliating Gallente Entities', 0.383, 0.414, 0.578, 0.513)),
|
||||||
(-56, ('[NPC][Invasion]Retaliating Minmatar Entities', 0.620, 0.422, 0.355, 0.399)),
|
(-56, ('[NPC][Invasion]Retaliating Minmatar Entities', 0.620, 0.422, 0.355, 0.399)),
|
||||||
(-57, ('[NPC][Abyssal][Dark Matter All Tiers]Drones', 0.439, 0.522, 0.529, 0.435)),
|
(-57, ('[NPC][Abyssal][Dark Matter All Tiers]Drones', 0.439, 0.522, 0.529, 0.435)),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import gui.globalEvents as GE
|
|||||||
import gui.mainFrame
|
import gui.mainFrame
|
||||||
from graphs.data.base import FitGraph
|
from graphs.data.base import FitGraph
|
||||||
from graphs.events import RESIST_MODE_CHANGED
|
from graphs.events import RESIST_MODE_CHANGED
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.bitmap_loader import BitmapLoader
|
from gui.bitmap_loader import BitmapLoader
|
||||||
from service.const import GraphCacheCleanupReason
|
from service.const import GraphCacheCleanupReason
|
||||||
from service.settings import GraphSettings
|
from service.settings import GraphSettings
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
import wx
|
import wx
|
||||||
|
|
||||||
|
|
||||||
class AuxiliaryFrame(wx.Frame):
|
class AuxiliaryMixin:
|
||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
|
|
||||||
@@ -55,14 +55,26 @@ class AuxiliaryFrame(wx.Frame):
|
|||||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def openOne(cls, parent, *args, **kwargs):
|
def openOne(cls, parent, *args, forceReopen=False, **kwargs):
|
||||||
"""If window is open and alive - raise it, open otherwise"""
|
"""If window is open and alive - raise it, open otherwise"""
|
||||||
if not cls._instance:
|
if not cls._instance or forceReopen:
|
||||||
|
if cls._instance:
|
||||||
|
cls._instance.Close()
|
||||||
frame = cls(parent, *args, **kwargs)
|
frame = cls(parent, *args, **kwargs)
|
||||||
cls._instance = frame
|
cls._instance = frame
|
||||||
frame.Show()
|
frame.Show()
|
||||||
else:
|
else:
|
||||||
cls._instance.Raise()
|
cls._instance.Raise()
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
|
||||||
def OnSuppressedAction(self, event):
|
def OnSuppressedAction(self, event):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class AuxiliaryFrame(AuxiliaryMixin, wx.Frame):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuxiliaryDialog(AuxiliaryMixin, wx.Dialog):
|
||||||
|
pass
|
||||||
@@ -36,6 +36,11 @@ import gui.fitCommands as cmd
|
|||||||
from gui.fitCommands.helpers import droneStackLimit
|
from gui.fitCommands.helpers import droneStackLimit
|
||||||
|
|
||||||
|
|
||||||
|
DRONE_ORDER = ('Light Scout Drones', 'Medium Scout Drones',
|
||||||
|
'Heavy Attack Drones', 'Sentry Drones', 'Combat Utility Drones',
|
||||||
|
'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones')
|
||||||
|
|
||||||
|
|
||||||
class DroneViewDrop(wx.DropTarget):
|
class DroneViewDrop(wx.DropTarget):
|
||||||
def __init__(self, dropFn, *args, **kwargs):
|
def __init__(self, dropFn, *args, **kwargs):
|
||||||
super(DroneViewDrop, self).__init__(*args, **kwargs)
|
super(DroneViewDrop, self).__init__(*args, **kwargs)
|
||||||
@@ -186,17 +191,13 @@ class DroneView(Display):
|
|||||||
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
|
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
|
||||||
fitID=fitID, srcPosition=srcPosition, dstPosition=dstPosition))
|
fitID=fitID, srcPosition=srcPosition, dstPosition=dstPosition))
|
||||||
|
|
||||||
DRONE_ORDER = ('Light Scout Drones', 'Medium Scout Drones',
|
@staticmethod
|
||||||
'Heavy Attack Drones', 'Sentry Drones', 'Combat Utility Drones',
|
def droneKey(drone):
|
||||||
'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones')
|
|
||||||
|
|
||||||
def droneKey(self, drone):
|
|
||||||
sMkt = Market.getInstance()
|
sMkt = Market.getInstance()
|
||||||
|
|
||||||
groupName = sMkt.getMarketGroupByItem(drone.item).name
|
groupName = sMkt.getMarketGroupByItem(drone.item).name
|
||||||
|
|
||||||
return (self.DRONE_ORDER.index(groupName),
|
return (DRONE_ORDER.index(groupName), drone.item.name)
|
||||||
drone.item.name)
|
|
||||||
|
|
||||||
def fitChanged(self, event):
|
def fitChanged(self, event):
|
||||||
event.Skip()
|
event.Skip()
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ from service.fit import Fit
|
|||||||
from service.market import Market
|
from service.market import Market
|
||||||
|
|
||||||
|
|
||||||
|
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
||||||
|
|
||||||
|
|
||||||
class FighterViewDrop(wx.DropTarget):
|
class FighterViewDrop(wx.DropTarget):
|
||||||
def __init__(self, dropFn, *args, **kwargs):
|
def __init__(self, dropFn, *args, **kwargs):
|
||||||
super(FighterViewDrop, self).__init__(*args, **kwargs)
|
super(FighterViewDrop, self).__init__(*args, **kwargs)
|
||||||
@@ -250,11 +253,10 @@ class FighterDisplay(d.Display):
|
|||||||
def _merge(src, dst):
|
def _merge(src, dst):
|
||||||
return
|
return
|
||||||
|
|
||||||
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
@staticmethod
|
||||||
|
def fighterKey(fighter):
|
||||||
def fighterKey(self, fighter):
|
|
||||||
groupName = Market.getInstance().getGroupByItem(fighter.item).name
|
groupName = Market.getInstance().getGroupByItem(fighter.item).name
|
||||||
orderPos = self.FIGHTER_ORDER.index(groupName)
|
orderPos = FIGHTER_ORDER.index(groupName)
|
||||||
# Sort support fighters by name, ignore their abilities
|
# Sort support fighters by name, ignore their abilities
|
||||||
if groupName == 'Support Fighter':
|
if groupName == 'Support Fighter':
|
||||||
abilityEffectIDs = ()
|
abilityEffectIDs = ()
|
||||||
|
|||||||
@@ -90,8 +90,6 @@ class ProjectedView(d.Display):
|
|||||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||||
|
|
||||||
self.droneView = gui.builtinAdditionPanes.droneView.DroneView
|
|
||||||
|
|
||||||
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
|
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
|
||||||
|
|
||||||
self.SetDropTarget(ProjectedViewDrop(self.handleListDrag))
|
self.SetDropTarget(ProjectedViewDrop(self.handleListDrag))
|
||||||
@@ -162,7 +160,7 @@ class ProjectedView(d.Display):
|
|||||||
if item.marketGroup is None:
|
if item.marketGroup is None:
|
||||||
item = item.metaGroup.parent
|
item = item.metaGroup.parent
|
||||||
|
|
||||||
return (self.droneView.DRONE_ORDER.index(item.marketGroup.name),
|
return (gui.builtinAdditionPanes.droneView.DRONE_ORDER.index(item.marketGroup.name),
|
||||||
drone.item.name)
|
drone.item.name)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from gui.builtinContextMenus import fitAddCurrentlyOpen
|
|||||||
from gui.builtinContextMenus import envEffectAdd
|
from gui.builtinContextMenus import envEffectAdd
|
||||||
from gui.builtinContextMenus import commandFitAdd
|
from gui.builtinContextMenus import commandFitAdd
|
||||||
from gui.builtinContextMenus.targetProfile import adder
|
from gui.builtinContextMenus.targetProfile import adder
|
||||||
|
from gui.builtinContextMenus import graphFitAmmoPicker
|
||||||
# Often-used item manipulations
|
# Often-used item manipulations
|
||||||
from gui.builtinContextMenus import shipModeChange
|
from gui.builtinContextMenus import shipModeChange
|
||||||
from gui.builtinContextMenus import moduleAmmoChange
|
from gui.builtinContextMenus import moduleAmmoChange
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
|
from collections import OrderedDict
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
# noinspection PyPackageRequirements
|
# noinspection PyPackageRequirements
|
||||||
@@ -10,6 +11,28 @@ from gui.contextMenu import ContextMenuUnconditional
|
|||||||
from service.market import Market
|
from service.market import Market
|
||||||
|
|
||||||
|
|
||||||
|
class Group:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.groups = OrderedDict()
|
||||||
|
self.items = []
|
||||||
|
|
||||||
|
def sort(self):
|
||||||
|
self.groups = OrderedDict((k, self.groups[k]) for k in sorted(self.groups))
|
||||||
|
for group in self.groups.values():
|
||||||
|
group.sort()
|
||||||
|
self.items.sort(key=lambda e: e.shortName)
|
||||||
|
|
||||||
|
|
||||||
|
class Entry:
|
||||||
|
|
||||||
|
def __init__(self, itemID, name, shortName):
|
||||||
|
self.itemID = itemID
|
||||||
|
self.name = name
|
||||||
|
self.shortName = shortName
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AddEnvironmentEffect(ContextMenuUnconditional):
|
class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||||
|
|
||||||
# CCP doesn't currently provide a mapping between the general Environment, and the specific environment effect
|
# CCP doesn't currently provide a mapping between the general Environment, and the specific environment effect
|
||||||
@@ -32,107 +55,78 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
|||||||
def getText(self, callingWindow, itmContext):
|
def getText(self, callingWindow, itmContext):
|
||||||
return "Add Environmental Effect"
|
return "Add Environmental Effect"
|
||||||
|
|
||||||
|
def _addGroup(self, parentMenu, name):
|
||||||
|
id = ContextMenuUnconditional.nextID()
|
||||||
|
menuItem = wx.MenuItem(parentMenu, id, name)
|
||||||
|
parentMenu.Bind(wx.EVT_MENU, self.handleSelection, menuItem)
|
||||||
|
return menuItem
|
||||||
|
|
||||||
|
def _addEffect(self, parentMenu, typeID, name):
|
||||||
|
id = ContextMenuUnconditional.nextID()
|
||||||
|
self.idmap[id] = typeID
|
||||||
|
menuItem = wx.MenuItem(parentMenu, id, name)
|
||||||
|
parentMenu.Bind(wx.EVT_MENU, self.handleSelection, menuItem)
|
||||||
|
return menuItem
|
||||||
|
|
||||||
def getSubMenu(self, callingWindow, context, rootMenu, i, pitem):
|
def getSubMenu(self, callingWindow, context, rootMenu, i, pitem):
|
||||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
|
||||||
|
|
||||||
# Wormholes
|
|
||||||
|
|
||||||
self.idmap = {}
|
self.idmap = {}
|
||||||
sub = wx.Menu()
|
data = self.getData()
|
||||||
|
msw = "wxMSW" in wx.PlatformInfo
|
||||||
|
|
||||||
wormhole_item = wx.MenuItem(sub, wx.ID_ANY, "Wormhole")
|
def makeMenu(data, parentMenu):
|
||||||
wormhole_menu = wx.Menu()
|
menu = wx.Menu()
|
||||||
wormhole_item.SetSubMenu(wormhole_menu)
|
for group_name in data.groups:
|
||||||
sub.Append(wormhole_item)
|
menuItem = self._addGroup(rootMenu if msw else parentMenu, group_name)
|
||||||
|
subMenu = makeMenu(data.groups[group_name], menu)
|
||||||
grouped_data, flat_data = self.getEffectBeacons()
|
menuItem.SetSubMenu(subMenu)
|
||||||
self.buildMenu(grouped_data, flat_data, wormhole_menu, rootMenu, msw)
|
menu.Append(menuItem)
|
||||||
|
for entry in data.items:
|
||||||
# Incursions
|
menuItem = self._addEffect(rootMenu if msw else parentMenu, entry.itemID, entry.shortName)
|
||||||
|
menu.Append(menuItem)
|
||||||
grouped_data, flat_data = self.getEffectBeacons(incursions=True)
|
menu.Bind(wx.EVT_MENU, self.handleSelection)
|
||||||
self.buildMenu(grouped_data, flat_data, sub, rootMenu, msw)
|
return menu
|
||||||
|
|
||||||
# Abyssal Weather
|
|
||||||
|
|
||||||
abyssal_item = wx.MenuItem(sub, wx.ID_ANY, "Abyssal Weather")
|
|
||||||
abyssal_menu = wx.Menu()
|
|
||||||
abyssal_item.SetSubMenu(abyssal_menu)
|
|
||||||
sub.Append(abyssal_item)
|
|
||||||
|
|
||||||
grouped_data, flat_data = self.getAbyssalWeather()
|
|
||||||
self.buildMenu(grouped_data, flat_data, abyssal_menu, rootMenu, msw)
|
|
||||||
|
|
||||||
# Localized Weather
|
|
||||||
|
|
||||||
local_item = wx.MenuItem(sub, wx.ID_ANY, "Localized")
|
|
||||||
local_menu = wx.Menu()
|
|
||||||
local_item.SetSubMenu(local_menu)
|
|
||||||
sub.Append(local_item)
|
|
||||||
|
|
||||||
grouped_data, flat_data = self.getLocalizedEnvironments()
|
|
||||||
self.buildMenu(grouped_data, flat_data, local_menu, rootMenu, msw)
|
|
||||||
|
|
||||||
|
sub = makeMenu(data, rootMenu)
|
||||||
return sub
|
return sub
|
||||||
|
|
||||||
def handleSelection(self, event):
|
def handleSelection(self, event):
|
||||||
# Skip events ids that aren't mapped
|
# Skip events ids that aren't mapped
|
||||||
|
|
||||||
swObj, swName = self.idmap.get(event.Id, (False, False))
|
swObj = self.idmap.get(event.Id, False)
|
||||||
if not swObj and not swName:
|
if not swObj:
|
||||||
event.Skip()
|
event.Skip()
|
||||||
return
|
return
|
||||||
|
|
||||||
fitID = self.mainFrame.getActiveFit()
|
fitID = self.mainFrame.getActiveFit()
|
||||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID, swObj.ID))
|
self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID, swObj))
|
||||||
|
|
||||||
def buildMenu(self, grouped_data, flat_data, local_menu, rootMenu, msw):
|
def getData(self):
|
||||||
|
data = Group()
|
||||||
|
data.groups['Metaliminal Storm'] = self.getEffectBeacons(
|
||||||
|
'Electrical', 'Exotic', 'Gamma', 'Plasma',
|
||||||
|
extra_garbage=('Metaliminal', 'Storm', 'Matter', 'Ray', 'Firestorm'))
|
||||||
|
data.groups['Wormhole'] = self.getEffectBeacons(
|
||||||
|
'Black Hole', 'Cataclysmic Variable', 'Magnetar',
|
||||||
|
'Pulsar', 'Red Giant', 'Wolf Rayet')
|
||||||
|
data.groups['Abyssal Weather'] = self.getAbyssalWeather()
|
||||||
|
data.groups['Sansha Incursion'] = self.getEffectBeacons('Sansha Incursion')
|
||||||
|
data.groups['Triglavian Invasion'] = self.getEffectBeacons('Triglavian Invasion')
|
||||||
|
data.groups['Triglavian Invasion'].groups['Destructible Beacons'] = self.getDestructibleBeacons()
|
||||||
|
return data
|
||||||
|
|
||||||
def processFlat(data, root, sub):
|
def getEffectBeacons(self, *groups, extra_garbage=()):
|
||||||
for swData in sorted(data, key=lambda tpl: tpl[2]):
|
|
||||||
wxid = ContextMenuUnconditional.nextID()
|
|
||||||
swObj, swName, swClass = swData
|
|
||||||
self.idmap[wxid] = (swObj, swName)
|
|
||||||
subItem = wx.MenuItem(sub, wxid, swClass)
|
|
||||||
if msw:
|
|
||||||
root.Bind(wx.EVT_MENU, self.handleSelection, subItem)
|
|
||||||
else:
|
|
||||||
sub.Bind(wx.EVT_MENU, self.handleSelection, subItem)
|
|
||||||
sub.Append(subItem)
|
|
||||||
|
|
||||||
for swType in sorted(grouped_data):
|
|
||||||
subItem = wx.MenuItem(local_menu, wx.ID_ANY, swType)
|
|
||||||
grandSub = wx.Menu()
|
|
||||||
subItem.SetSubMenu(grandSub)
|
|
||||||
local_menu.Append(subItem)
|
|
||||||
processFlat(grouped_data[swType], rootMenu, grandSub)
|
|
||||||
|
|
||||||
processFlat(flat_data, rootMenu, local_menu)
|
|
||||||
|
|
||||||
def getEffectBeacons(self, incursions=False):
|
|
||||||
"""
|
"""
|
||||||
Get dictionary with wormhole system-wide effects
|
Get dictionary with system-wide effects
|
||||||
"""
|
"""
|
||||||
|
compacted = len(groups) <= 1
|
||||||
sMkt = Market.getInstance()
|
sMkt = Market.getInstance()
|
||||||
|
|
||||||
# todo: rework this
|
|
||||||
# Container for system-wide effects
|
# Container for system-wide effects
|
||||||
grouped = {}
|
data = Group()
|
||||||
|
|
||||||
# Expressions for matching when detecting effects we're looking for
|
|
||||||
if incursions:
|
|
||||||
validgroups = ("Sansha Incursion",
|
|
||||||
"Triglavian Invasion")
|
|
||||||
else:
|
|
||||||
validgroups = ("Black Hole",
|
|
||||||
"Cataclysmic Variable",
|
|
||||||
"Magnetar",
|
|
||||||
"Pulsar",
|
|
||||||
"Red Giant",
|
|
||||||
"Wolf Rayet")
|
|
||||||
|
|
||||||
# Stuff we don't want to see in names
|
# Stuff we don't want to see in names
|
||||||
garbages = ("System Effects", "Effects")
|
garbages = ["System Effects", "Effects"]
|
||||||
|
garbages.extend(extra_garbage)
|
||||||
|
|
||||||
# Get group with all the system-wide beacons
|
# Get group with all the system-wide beacons
|
||||||
grp = sMkt.getGroup("Effect Beacon")
|
grp = sMkt.getGroup("Effect Beacon")
|
||||||
@@ -140,7 +134,7 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
|||||||
# Cycle through them
|
# Cycle through them
|
||||||
for beacon in sMkt.getItemsByGroup(grp):
|
for beacon in sMkt.getItemsByGroup(grp):
|
||||||
# Check if it belongs to any valid group
|
# Check if it belongs to any valid group
|
||||||
for group in validgroups:
|
for group in groups:
|
||||||
# Check beginning of the name only
|
# Check beginning of the name only
|
||||||
if re.search(group, beacon.name):
|
if re.search(group, beacon.name):
|
||||||
# Get full beacon name
|
# Get full beacon name
|
||||||
@@ -159,64 +153,65 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
|||||||
groupname = re.sub(garbage, "", groupname)
|
groupname = re.sub(garbage, "", groupname)
|
||||||
groupname = re.sub(" {2,}", " ", groupname).strip()
|
groupname = re.sub(" {2,}", " ", groupname).strip()
|
||||||
# Add stuff to dictionary
|
# Add stuff to dictionary
|
||||||
if groupname not in grouped:
|
if compacted:
|
||||||
grouped[groupname] = set()
|
container = data.items
|
||||||
grouped[groupname].add((beacon, beaconname, shortname))
|
else:
|
||||||
|
container = data.groups.setdefault(groupname, Group()).items
|
||||||
|
container.append(Entry(beacon.ID, beaconname, shortname))
|
||||||
# Break loop on 1st result
|
# Break loop on 1st result
|
||||||
break
|
break
|
||||||
|
data.sort()
|
||||||
return grouped, ()
|
return data
|
||||||
|
|
||||||
def getAbyssalWeather(self):
|
def getAbyssalWeather(self):
|
||||||
sMkt = Market.getInstance()
|
sMkt = Market.getInstance()
|
||||||
|
data = Group()
|
||||||
|
|
||||||
environments = {x.ID: x for x in sMkt.getGroup("Abyssal Environment").items}
|
environments = {x.ID: x for x in sMkt.getGroup("Abyssal Environment").items}
|
||||||
items = chain(sMkt.getGroup("MassiveEnvironments").items, sMkt.getGroup("Non-Interactable Object").items)
|
items = chain(
|
||||||
|
sMkt.getGroup("MassiveEnvironments").items,
|
||||||
grouped = {}
|
sMkt.getGroup("Non-Interactable Object").items)
|
||||||
flat = set()
|
|
||||||
|
|
||||||
for beacon in items:
|
for beacon in items:
|
||||||
if not beacon.isType('projected'):
|
if not beacon.isType('projected'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
type = self.__class__.abyssal_mapping.get(beacon.name[0:-2], None)
|
type = self.__class__.abyssal_mapping.get(beacon.name[0:-2], None)
|
||||||
type = environments.get(type, None)
|
type = environments.get(type, None)
|
||||||
if type is None:
|
if type is None:
|
||||||
continue
|
continue
|
||||||
|
subdata = data.groups.setdefault(type.name, Group())
|
||||||
if type.name not in grouped:
|
|
||||||
grouped[type.name] = set()
|
|
||||||
|
|
||||||
display_name = "{} {}".format(type.name, beacon.name[-1:])
|
display_name = "{} {}".format(type.name, beacon.name[-1:])
|
||||||
grouped[type.name].add((beacon, display_name, display_name))
|
subdata.items.append(Entry(beacon.ID, display_name, display_name))
|
||||||
|
data.sort()
|
||||||
|
|
||||||
|
# Localized abyssal hazards
|
||||||
|
items = sMkt.getGroup("Abyssal Hazards").items
|
||||||
|
if items:
|
||||||
|
subdata = data.groups.setdefault('Localized', Group())
|
||||||
|
for beacon in sMkt.getGroup("Abyssal Hazards").items:
|
||||||
|
if not beacon.isType('projected'):
|
||||||
|
continue
|
||||||
|
# Localized effects, currently, have a name like "(size) (type) Cloud"
|
||||||
|
# Until this inevitably changes, do a simple split
|
||||||
|
name_parts = beacon.name.split(" ")
|
||||||
|
|
||||||
|
key = name_parts[1].strip()
|
||||||
|
subsubdata = subdata.groups.setdefault(key, Group())
|
||||||
|
subsubdata.items.append(Entry(beacon.ID, beacon.name, beacon.name))
|
||||||
|
subdata.sort()
|
||||||
|
|
||||||
# PVP weather
|
# PVP weather
|
||||||
flat.add((sMkt.getItem(49766), 'PvP Weather', 'PvP Weather'))
|
data.items.append(Entry(49766, 'PvP Weather', 'PvP Weather'))
|
||||||
|
|
||||||
return grouped, flat
|
return data
|
||||||
|
|
||||||
def getLocalizedEnvironments(self):
|
def getDestructibleBeacons(self):
|
||||||
|
data = Group()
|
||||||
sMkt = Market.getInstance()
|
sMkt = Market.getInstance()
|
||||||
|
for item in sMkt.getItemsByGroup(sMkt.getGroup('Destructible Effect Beacon')):
|
||||||
grp = sMkt.getGroup("Abyssal Hazards")
|
if not item.isType('projected'):
|
||||||
|
|
||||||
grouped = dict()
|
|
||||||
|
|
||||||
for beacon in grp.items:
|
|
||||||
if not beacon.isType('projected'):
|
|
||||||
continue
|
continue
|
||||||
# Localized effects, currently, have a name like "(size) (type) Cloud"
|
data.items.append(Entry(item.ID, item.name, item.name))
|
||||||
# Until this inevitably changes, do a simple split
|
data.sort()
|
||||||
name_parts = beacon.name.split(" ")
|
return data
|
||||||
|
|
||||||
key = name_parts[1].strip()
|
|
||||||
if key not in grouped:
|
|
||||||
grouped[key] = set()
|
|
||||||
|
|
||||||
grouped[key].add((beacon, beacon.name, beacon.name))
|
|
||||||
|
|
||||||
return grouped, ()
|
|
||||||
|
|
||||||
|
|
||||||
AddEnvironmentEffect.register()
|
AddEnvironmentEffect.register()
|
||||||
|
|||||||
241
gui/builtinContextMenus/graphFitAmmoPicker.py
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
# noinspection PyPackageRequirements
|
||||||
|
import wx
|
||||||
|
|
||||||
|
import gui.mainFrame
|
||||||
|
from gui.auxWindow import AuxiliaryDialog
|
||||||
|
from gui.contextMenu import ContextMenuSingle
|
||||||
|
from service.ammo import Ammo
|
||||||
|
from service.market import Market
|
||||||
|
|
||||||
|
|
||||||
|
class GraphFitAmmoPicker(ContextMenuSingle):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||||
|
|
||||||
|
def display(self, callingWindow, srcContext, mainItem):
|
||||||
|
if srcContext != 'graphFitList':
|
||||||
|
return False
|
||||||
|
if mainItem is None or not mainItem.isFit:
|
||||||
|
return False
|
||||||
|
if callingWindow.graphFrame.getView().internalName != 'dmgStatsGraph':
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getText(self, callingWindow, itmContext, mainItem):
|
||||||
|
return 'Plot with Different Ammo...'
|
||||||
|
|
||||||
|
def activate(self, callingWindow, fullContext, mainItem, i):
|
||||||
|
AmmoPickerFrame.openOne(callingWindow, mainItem.item, forceReopen=True)
|
||||||
|
|
||||||
|
|
||||||
|
# GraphFitAmmoPicker.register()
|
||||||
|
|
||||||
|
|
||||||
|
class AmmoPickerFrame(AuxiliaryDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent, fit):
|
||||||
|
super().__init__(parent, title='Choose Different Ammo', style=wx.DEFAULT_DIALOG_STYLE, resizeable=True)
|
||||||
|
padding = 5
|
||||||
|
|
||||||
|
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
|
||||||
|
contents = AmmoPickerContents(self, fit)
|
||||||
|
mainSizer.Add(contents, 1, wx.EXPAND | wx.ALL, padding)
|
||||||
|
|
||||||
|
buttonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)
|
||||||
|
if buttonSizer:
|
||||||
|
mainSizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, padding)
|
||||||
|
|
||||||
|
self.SetSizer(mainSizer)
|
||||||
|
self.Layout()
|
||||||
|
|
||||||
|
contW, contH = contents.GetVirtualSize()
|
||||||
|
bestW = contW + padding * 2
|
||||||
|
bestH = contH + padding * 2
|
||||||
|
if buttonSizer:
|
||||||
|
# Yeah right... whatever
|
||||||
|
buttW, buttH = buttonSizer.GetSize()
|
||||||
|
bestW = max(bestW, buttW + padding * 2)
|
||||||
|
bestH += buttH + padding * 2
|
||||||
|
bestW = min(1000, bestW)
|
||||||
|
bestH = min(700, bestH)
|
||||||
|
self.SetSize(bestW, bestH)
|
||||||
|
self.SetMinSize(wx.Size(int(bestW * 0.7), int(bestH * 0.7)))
|
||||||
|
self.CenterOnParent()
|
||||||
|
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
|
||||||
|
|
||||||
|
def kbEvent(self, event):
|
||||||
|
if event.GetKeyCode() == wx.WXK_ESCAPE and event.GetModifiers() == wx.MOD_NONE:
|
||||||
|
self.Close()
|
||||||
|
return
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
|
|
||||||
|
class AmmoPickerContents(wx.ScrolledCanvas):
|
||||||
|
|
||||||
|
indent = 15
|
||||||
|
|
||||||
|
def __init__(self, parent, fit):
|
||||||
|
wx.ScrolledCanvas.__init__(self, parent)
|
||||||
|
self.SetScrollRate(0, 15)
|
||||||
|
|
||||||
|
mods = self.getMods(fit)
|
||||||
|
drones = self.getDrones(fit)
|
||||||
|
fighters = self.getFighters(fit)
|
||||||
|
self.rbLabelMap = {}
|
||||||
|
self.rbCheckboxMap = {}
|
||||||
|
|
||||||
|
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
|
||||||
|
moduleSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
mainSizer.Add(moduleSizer, 0, wx.ALL, 0)
|
||||||
|
|
||||||
|
self.droneSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
mainSizer.Add(self.droneSizer, 0, wx.ALL, 0)
|
||||||
|
|
||||||
|
fighterSizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
mainSizer.Add(fighterSizer, 0, wx.ALL, 0)
|
||||||
|
|
||||||
|
firstRadio = True
|
||||||
|
|
||||||
|
for modInfo, modAmmo in mods:
|
||||||
|
text = '\n'.join('{}x {}'.format(amount, item.name) for item, amount in modInfo)
|
||||||
|
modRb = self.addRadioButton(moduleSizer, text, firstRadio)
|
||||||
|
firstRadio = False
|
||||||
|
# Get actual module, as ammo getters need it
|
||||||
|
mod = next((m for m in fit.modules if m.itemID == next(iter(modInfo))[0].ID), None)
|
||||||
|
_, ammoTree = Ammo.getInstance().getModuleStructuredAmmo(mod)
|
||||||
|
if len(ammoTree) == 1:
|
||||||
|
for ammoCatName, ammos in ammoTree.items():
|
||||||
|
for ammo in ammos:
|
||||||
|
self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=1)
|
||||||
|
else:
|
||||||
|
for ammoCatName, ammos in ammoTree.items():
|
||||||
|
if len(ammos) == 1:
|
||||||
|
ammo = next(iter(ammos))
|
||||||
|
self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=1)
|
||||||
|
else:
|
||||||
|
self.addLabel(moduleSizer, '{}:'.format(ammoCatName), modRb, indentLvl=1)
|
||||||
|
for ammo in ammos:
|
||||||
|
self.addCheckbox(moduleSizer, ammo.name, modRb, indentLvl=2)
|
||||||
|
if drones:
|
||||||
|
droneRb = self.addRadioButton(self.droneSizer, 'Drones', firstRadio)
|
||||||
|
from gui.builtinAdditionPanes.droneView import DroneView
|
||||||
|
for drone in sorted(drones, key=DroneView.droneKey):
|
||||||
|
self.addCheckbox(self.droneSizer, '{}x {}'.format(drone.amount, drone.item.name), droneRb, indentLvl=1)
|
||||||
|
addBtn = wx.Button(self, wx.ID_ANY, '+', style=wx.BU_EXACTFIT)
|
||||||
|
addBtn.Bind(wx.EVT_BUTTON, self.OnDroneGroupAdd)
|
||||||
|
mainSizer.Add(addBtn, 0, wx.LEFT, self.indent)
|
||||||
|
if fighters:
|
||||||
|
fighterRb = self.addRadioButton(fighterSizer, 'Fighters', firstRadio)
|
||||||
|
from gui.builtinAdditionPanes.fighterView import FighterDisplay
|
||||||
|
for fighter in sorted(fighters, key=FighterDisplay.fighterKey):
|
||||||
|
self.addCheckbox(fighterSizer, '{}x {}'.format(fighter.amount, fighter.item.name), fighterRb, indentLvl=1)
|
||||||
|
|
||||||
|
self.SetSizer(mainSizer)
|
||||||
|
self.refreshStatus()
|
||||||
|
|
||||||
|
def addRadioButton(self, sizer, text, firstRadio=False):
|
||||||
|
if firstRadio:
|
||||||
|
rb = wx.RadioButton(self, wx.ID_ANY, text, style=wx.RB_GROUP)
|
||||||
|
rb.SetValue(True)
|
||||||
|
else:
|
||||||
|
rb = wx.RadioButton(self, wx.ID_ANY, text)
|
||||||
|
rb.SetValue(False)
|
||||||
|
rb.Bind(wx.EVT_RADIOBUTTON, self.rbSelected)
|
||||||
|
sizer.Add(rb, 0, wx.EXPAND | wx.ALL, 0)
|
||||||
|
return rb
|
||||||
|
|
||||||
|
def addCheckbox(self, sizer, text, currentRb, indentLvl=0):
|
||||||
|
cb = wx.CheckBox(self, -1, text)
|
||||||
|
sizer.Add(cb, 0, wx.EXPAND | wx.LEFT, self.indent * indentLvl)
|
||||||
|
if currentRb is not None:
|
||||||
|
self.rbCheckboxMap.setdefault(currentRb, []).append(cb)
|
||||||
|
|
||||||
|
def addLabel(self, sizer, text, currentRb, indentLvl=0):
|
||||||
|
text = text[0].capitalize() + text[1:]
|
||||||
|
label = wx.StaticText(self, wx.ID_ANY, text)
|
||||||
|
sizer.Add(label, 0, wx.EXPAND | wx.LEFT, self.indent * indentLvl)
|
||||||
|
if currentRb is not None:
|
||||||
|
self.rbLabelMap.setdefault(currentRb, []).append(label)
|
||||||
|
|
||||||
|
def getMods(self, fit):
|
||||||
|
sMkt = Market.getInstance()
|
||||||
|
sAmmo = Ammo.getInstance()
|
||||||
|
loadableChargesCache = {}
|
||||||
|
# Modules, format: {frozenset(ammo): {item: count}}
|
||||||
|
modsPrelim = {}
|
||||||
|
if fit is not None:
|
||||||
|
for mod in fit.modules:
|
||||||
|
if not mod.canDealDamage():
|
||||||
|
continue
|
||||||
|
typeID = mod.item.ID
|
||||||
|
if typeID not in loadableChargesCache:
|
||||||
|
loadableChargesCache[typeID] = sAmmo.getModuleFlatAmmo(mod)
|
||||||
|
charges = loadableChargesCache[typeID]
|
||||||
|
# We're not interested in modules which contain no charges
|
||||||
|
if charges:
|
||||||
|
data = modsPrelim.setdefault(frozenset(charges), {})
|
||||||
|
if mod.item not in data:
|
||||||
|
data[mod.item] = 0
|
||||||
|
data[mod.item] += 1
|
||||||
|
# Format: [([(item, count), ...], frozenset(ammo)), ...]
|
||||||
|
modsFinal = []
|
||||||
|
for charges, itemCounts in modsPrelim.items():
|
||||||
|
modsFinal.append((
|
||||||
|
# Sort items within group
|
||||||
|
sorted(itemCounts.items(), key=lambda i: sMkt.itemSort(i[0], reverseMktGrp=True), reverse=True),
|
||||||
|
charges))
|
||||||
|
# Sort item groups
|
||||||
|
modsFinal.sort(key=lambda i: sMkt.itemSort(i[0][0][0], reverseMktGrp=True), reverse=True)
|
||||||
|
return modsFinal
|
||||||
|
|
||||||
|
def getDrones(self, fit):
|
||||||
|
drones = []
|
||||||
|
if fit is not None:
|
||||||
|
for drone in fit.drones:
|
||||||
|
if drone.item is None:
|
||||||
|
continue
|
||||||
|
# Drones are our "ammo", so we want to pick even those which are inactive
|
||||||
|
if drone.canDealDamage(ignoreState=True):
|
||||||
|
drones.append(drone)
|
||||||
|
continue
|
||||||
|
if {'remoteWebifierEntity', 'remoteTargetPaintEntity'}.intersection(drone.item.effects):
|
||||||
|
drones.append(drone)
|
||||||
|
continue
|
||||||
|
return drones
|
||||||
|
|
||||||
|
def getFighters(self, fit):
|
||||||
|
fighters = []
|
||||||
|
if fit is not None:
|
||||||
|
for fighter in fit.fighters:
|
||||||
|
if fighter.item is None:
|
||||||
|
continue
|
||||||
|
# Fighters are our "ammo" as well
|
||||||
|
if fighter.canDealDamage(ignoreState=True):
|
||||||
|
fighters.append(fighter)
|
||||||
|
continue
|
||||||
|
for ability in fighter.abilities:
|
||||||
|
if not ability.active:
|
||||||
|
continue
|
||||||
|
if ability.effect.name == 'fighterAbilityStasisWebifier':
|
||||||
|
fighters.append(fighter)
|
||||||
|
break
|
||||||
|
return fighters
|
||||||
|
|
||||||
|
def OnDroneGroupAdd(self, event):
|
||||||
|
event.Skip()
|
||||||
|
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
label = wx.StaticText()
|
||||||
|
self.droneSizer.Add(sizer, 0, wx.EXPAND | wx.LEFT, self.indent)
|
||||||
|
|
||||||
|
def refreshStatus(self):
|
||||||
|
for map in (self.rbLabelMap, self.rbCheckboxMap):
|
||||||
|
for rb, items in map.items():
|
||||||
|
for item in items:
|
||||||
|
item.Enable(rb.GetValue())
|
||||||
|
|
||||||
|
def rbSelected(self, event):
|
||||||
|
event.Skip()
|
||||||
|
self.refreshStatus()
|
||||||
@@ -3,33 +3,28 @@ import wx
|
|||||||
|
|
||||||
import gui.fitCommands as cmd
|
import gui.fitCommands as cmd
|
||||||
import gui.mainFrame
|
import gui.mainFrame
|
||||||
from eos.const import FittingHardpoint
|
|
||||||
from eos.saveddata.module import Module
|
|
||||||
from gui.bitmap_loader import BitmapLoader
|
from gui.bitmap_loader import BitmapLoader
|
||||||
from gui.contextMenu import ContextMenuCombined
|
from gui.contextMenu import ContextMenuCombined
|
||||||
from gui.fitCommands.helpers import getSimilarModPositions
|
from gui.fitCommands.helpers import getSimilarModPositions
|
||||||
|
from service.ammo import Ammo
|
||||||
from service.fit import Fit
|
from service.fit import Fit
|
||||||
from service.market import Market
|
|
||||||
|
|
||||||
|
|
||||||
class ChangeModuleAmmo(ContextMenuCombined):
|
class ChangeModuleAmmo(ContextMenuCombined):
|
||||||
|
|
||||||
DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal")
|
|
||||||
MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed")
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||||
# Format: {type ID: set(loadable, charges)}
|
# Format: {type ID: set(loadable, charges)}
|
||||||
self.loadableCharges = {}
|
self.loadableChargesCache = {}
|
||||||
|
|
||||||
def display(self, callingWindow, srcContext, mainItem, selection):
|
def display(self, callingWindow, srcContext, mainItem, selection):
|
||||||
if srcContext not in ("fittingModule", "projectedModule"):
|
if srcContext not in ('fittingModule', 'projectedModule'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.mainFrame.getActiveFit() is None:
|
if self.mainFrame.getActiveFit() is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.mainCharges = self.getChargesForMod(mainItem)
|
self.mainCharges = self._getAmmo(mainItem)
|
||||||
if not self.mainCharges:
|
if not self.mainCharges:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -39,186 +34,81 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def getText(self, callingWindow, itmContext, mainItem, selection):
|
def getText(self, callingWindow, itmContext, mainItem, selection):
|
||||||
return "Charge"
|
return 'Charge'
|
||||||
|
|
||||||
def getChargesForMod(self, mod):
|
def _getAmmo(self, mod):
|
||||||
sMkt = Market.getInstance()
|
if mod.itemID is None:
|
||||||
if mod is None or mod.isEmpty:
|
|
||||||
return set()
|
return set()
|
||||||
typeID = mod.item.ID
|
if mod.itemID not in self.loadableChargesCache:
|
||||||
if typeID in self.loadableCharges:
|
self.loadableChargesCache[mod.itemID] = Ammo.getInstance().getModuleFlatAmmo(mod)
|
||||||
return self.loadableCharges[typeID]
|
return self.loadableChargesCache[mod.itemID]
|
||||||
chargeSet = self.loadableCharges.setdefault(typeID, set())
|
|
||||||
# Do not try to grab it for modes which can also be passed as part of selection
|
|
||||||
if isinstance(mod, Module):
|
|
||||||
for charge in mod.getValidCharges():
|
|
||||||
if sMkt.getPublicityByItem(charge):
|
|
||||||
chargeSet.add(charge)
|
|
||||||
return chargeSet
|
|
||||||
|
|
||||||
def turretSorter(self, charge):
|
def _addCharge(self, menu, charge):
|
||||||
damage = 0
|
|
||||||
range_ = (self.module.item.getAttribute("maxRange")) * \
|
|
||||||
(charge.getAttribute("weaponRangeMultiplier") or 1)
|
|
||||||
falloff = (self.module.item.getAttribute("falloff") or 0) * \
|
|
||||||
(charge.getAttribute("fallofMultiplier") or 1)
|
|
||||||
for type_ in self.DAMAGE_TYPES:
|
|
||||||
d = charge.getAttribute("%sDamage" % type_)
|
|
||||||
if d > 0:
|
|
||||||
damage += d
|
|
||||||
|
|
||||||
# Take optimal and falloff as range factor
|
|
||||||
rangeFactor = range_ + falloff
|
|
||||||
|
|
||||||
return - rangeFactor, charge.name.rsplit()[-2:], damage, charge.name
|
|
||||||
|
|
||||||
def missileSorter(self, charge):
|
|
||||||
# Get charge damage type and total damage
|
|
||||||
chargeDamageType, totalDamage = self.damageInfo(charge)
|
|
||||||
# Find its position in sort list
|
|
||||||
position = self.MISSILE_ORDER.index(chargeDamageType)
|
|
||||||
return position, totalDamage, charge.name
|
|
||||||
|
|
||||||
def damageInfo(self, charge):
|
|
||||||
# Set up data storage for missile damage stuff
|
|
||||||
damageMap = {}
|
|
||||||
totalDamage = 0
|
|
||||||
# Fill them with the data about charge
|
|
||||||
for damageType in self.DAMAGE_TYPES:
|
|
||||||
currentDamage = charge.getAttribute("{0}Damage".format(damageType)) or 0
|
|
||||||
damageMap[damageType] = currentDamage
|
|
||||||
totalDamage += currentDamage
|
|
||||||
# Detect type of ammo
|
|
||||||
chargeDamageType = None
|
|
||||||
for damageType in damageMap:
|
|
||||||
# If all damage belongs to certain type purely, set appropriate
|
|
||||||
# ammoType
|
|
||||||
if damageMap[damageType] == totalDamage:
|
|
||||||
chargeDamageType = damageType
|
|
||||||
break
|
|
||||||
# Else consider ammo as mixed damage
|
|
||||||
if chargeDamageType is None:
|
|
||||||
chargeDamageType = "mixed"
|
|
||||||
|
|
||||||
return chargeDamageType, totalDamage
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def numericConverter(string):
|
|
||||||
return int(string) if string.isdigit() else string
|
|
||||||
|
|
||||||
def nameSorter(self, charge):
|
|
||||||
parts = charge.name.split(" ")
|
|
||||||
return list(map(self.numericConverter, parts))
|
|
||||||
|
|
||||||
def addCharge(self, menu, charge):
|
|
||||||
id_ = ContextMenuCombined.nextID()
|
id_ = ContextMenuCombined.nextID()
|
||||||
name = charge.name if charge is not None else "Empty"
|
name = charge.name if charge is not None else 'Empty'
|
||||||
self.chargeIds[id_] = charge
|
self.chargeEventMap[id_] = charge
|
||||||
item = wx.MenuItem(menu, id_, name)
|
item = wx.MenuItem(menu, id_, name)
|
||||||
menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item)
|
menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item)
|
||||||
item.charge = charge
|
item.charge = charge
|
||||||
if charge is not None and charge.iconID is not None:
|
if charge is not None and charge.iconID is not None:
|
||||||
bitmap = BitmapLoader.getBitmap(charge.iconID, "icons")
|
bitmap = BitmapLoader.getBitmap(charge.iconID, 'icons')
|
||||||
if bitmap is not None:
|
if bitmap is not None:
|
||||||
item.SetBitmap(bitmap)
|
item.SetBitmap(bitmap)
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def addSeperator(m, text):
|
def _addSeparator(m, text):
|
||||||
id_ = ContextMenuCombined.nextID()
|
id_ = ContextMenuCombined.nextID()
|
||||||
m.Append(id_, '─ %s ─' % text)
|
m.Append(id_, '─ %s ─' % text)
|
||||||
m.Enable(id_, False)
|
m.Enable(id_, False)
|
||||||
|
|
||||||
def getSubMenu(self, callingWindow, context, mainItem, selection, rootMenu, i, pitem):
|
def getSubMenu(self, callingWindow, context, mainItem, selection, rootMenu, i, pitem):
|
||||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
msw = True if 'wxMSW' in wx.PlatformInfo else False
|
||||||
m = wx.Menu()
|
menu = wx.Menu()
|
||||||
self.chargeIds = {}
|
self.chargeEventMap = {}
|
||||||
hardpoint = self.module.hardpoint
|
modType, chargeDict = Ammo.getInstance().getModuleStructuredAmmo(self.module, ammo=self.mainCharges)
|
||||||
moduleName = self.module.item.name
|
if modType == 'ddTurret':
|
||||||
# Make sure we do not consider mining turrets as combat turrets
|
self._addSeparator(menu, 'Long Range')
|
||||||
if hardpoint == FittingHardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount", None) is None:
|
menuItems = []
|
||||||
self.addSeperator(m, "Long Range")
|
for charges in chargeDict.values():
|
||||||
items = []
|
if len(charges) == 1:
|
||||||
range_ = None
|
menuItems.append(self._addCharge(rootMenu if msw else menu, charges[0]))
|
||||||
nameBase = None
|
|
||||||
sub = None
|
|
||||||
chargesSorted = sorted(self.mainCharges, key=self.turretSorter)
|
|
||||||
for charge in chargesSorted:
|
|
||||||
if "civilian" in charge.name.lower():
|
|
||||||
continue
|
|
||||||
currBase = charge.name.rsplit()[-2:]
|
|
||||||
currRange = charge.getAttribute("weaponRangeMultiplier")
|
|
||||||
if nameBase is None or range_ != currRange or nameBase != currBase:
|
|
||||||
if sub is not None:
|
|
||||||
self.addSeperator(sub, "More Damage")
|
|
||||||
|
|
||||||
sub = None
|
|
||||||
base = charge
|
|
||||||
nameBase = currBase
|
|
||||||
range_ = currRange
|
|
||||||
item = self.addCharge(rootMenu if msw else m, charge)
|
|
||||||
items.append(item)
|
|
||||||
else:
|
else:
|
||||||
if sub is None and item and base:
|
baseCharge = charges[0]
|
||||||
sub = wx.Menu()
|
menuItem = self._addCharge(rootMenu if msw else menu, baseCharge)
|
||||||
sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
menuItems.append(menuItem)
|
||||||
self.addSeperator(sub, "Less Damage")
|
subMenu = wx.Menu()
|
||||||
item.SetSubMenu(sub)
|
subMenu.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||||
sub.Append(self.addCharge(rootMenu if msw else sub, base))
|
menuItem.SetSubMenu(subMenu)
|
||||||
|
self._addSeparator(subMenu, 'Less Damage')
|
||||||
sub.Append(self.addCharge(rootMenu if msw else sub, charge))
|
for charge in charges:
|
||||||
|
subMenu.Append(self._addCharge(rootMenu if msw else subMenu, charge))
|
||||||
if sub is not None:
|
self._addSeparator(subMenu, 'More Damage')
|
||||||
self.addSeperator(sub, "More Damage")
|
for menuItem in menuItems:
|
||||||
|
menu.Append(menuItem)
|
||||||
for item in items:
|
self._addSeparator(menu, 'Short Range')
|
||||||
m.Append(item)
|
elif modType == 'ddMissile':
|
||||||
|
menuItems = []
|
||||||
self.addSeperator(m, "Short Range")
|
for chargeCatName, charges in chargeDict.items():
|
||||||
elif hardpoint == FittingHardpoint.MISSILE and moduleName != 'Festival Launcher':
|
menuItem = wx.MenuItem(menu, wx.ID_ANY, chargeCatName.capitalize())
|
||||||
type_ = None
|
menuItems.append(menuItem)
|
||||||
sub = None
|
subMenu = wx.Menu()
|
||||||
defender = None
|
subMenu.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||||
chargesSorted = sorted(self.mainCharges, key=self.missileSorter)
|
menuItem.SetSubMenu(subMenu)
|
||||||
for charge in chargesSorted:
|
self._addSeparator(subMenu, 'Less Damage')
|
||||||
currType = self.damageInfo(charge)[0]
|
for charge in charges:
|
||||||
|
subMenu.Append(self._addCharge(rootMenu if msw else subMenu, charge))
|
||||||
if currType != type_ or type_ is None:
|
self._addSeparator(subMenu, 'More Damage')
|
||||||
if sub is not None:
|
for menuItem in menuItems:
|
||||||
self.addSeperator(sub, "More Damage")
|
menu.Append(menuItem)
|
||||||
|
elif modType == 'general':
|
||||||
type_ = currType
|
for charge in chargeDict['general']:
|
||||||
item = wx.MenuItem(m, wx.ID_ANY, type_.capitalize())
|
menu.Append(self._addCharge(rootMenu if msw else menu, charge))
|
||||||
bitmap = BitmapLoader.getBitmap("%s_small" % type_, "gui")
|
menu.Append(self._addCharge(rootMenu if msw else menu, None))
|
||||||
if bitmap is not None:
|
return menu
|
||||||
item.SetBitmap(bitmap)
|
|
||||||
|
|
||||||
sub = wx.Menu()
|
|
||||||
sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
|
||||||
self.addSeperator(sub, "Less Damage")
|
|
||||||
item.SetSubMenu(sub)
|
|
||||||
m.Append(item)
|
|
||||||
|
|
||||||
if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"):
|
|
||||||
sub.Append(self.addCharge(rootMenu if msw else sub, charge))
|
|
||||||
else:
|
|
||||||
defender = charge
|
|
||||||
|
|
||||||
if defender is not None:
|
|
||||||
m.Append(self.addCharge(rootMenu if msw else m, defender))
|
|
||||||
if sub is not None:
|
|
||||||
self.addSeperator(sub, "More Damage")
|
|
||||||
else:
|
|
||||||
chargesSorted = sorted(self.mainCharges, key=self.nameSorter)
|
|
||||||
for charge in chargesSorted:
|
|
||||||
m.Append(self.addCharge(rootMenu if msw else m, charge))
|
|
||||||
|
|
||||||
m.Append(self.addCharge(rootMenu if msw else m, None))
|
|
||||||
return m
|
|
||||||
|
|
||||||
def handleAmmoSwitch(self, event):
|
def handleAmmoSwitch(self, event):
|
||||||
charge = self.chargeIds.get(event.Id, False)
|
charge = self.chargeEventMap.get(event.Id, False)
|
||||||
if charge is False:
|
if charge is False:
|
||||||
event.Skip()
|
event.Skip()
|
||||||
return
|
return
|
||||||
@@ -254,7 +144,7 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
|||||||
positions = []
|
positions = []
|
||||||
for position, mod in enumerate(modContainer):
|
for position, mod in enumerate(modContainer):
|
||||||
if mod in self.selection:
|
if mod in self.selection:
|
||||||
modCharges = self.getChargesForMod(mod)
|
modCharges = self._getAmmo(mod)
|
||||||
if modCharges.issubset(self.mainCharges):
|
if modCharges.issubset(self.mainCharges):
|
||||||
positions.append(position)
|
positions.append(position)
|
||||||
self.mainFrame.command.Submit(command(
|
self.mainFrame.command.Submit(command(
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ItemDescription(wx.Panel):
|
|||||||
self.Layout()
|
self.Layout()
|
||||||
|
|
||||||
self.description.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
|
self.description.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
|
||||||
self.description.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
|
self.description.Bind(wx.EVT_KEY_UP, self.onKeyUp)
|
||||||
|
|
||||||
self.popupMenu = wx.Menu()
|
self.popupMenu = wx.Menu()
|
||||||
copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
|
copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
|
||||||
@@ -50,7 +50,7 @@ class ItemDescription(wx.Panel):
|
|||||||
if selectedMenuItem == 1: # Copy was chosen
|
if selectedMenuItem == 1: # Copy was chosen
|
||||||
self.copySelectionToClipboard()
|
self.copySelectionToClipboard()
|
||||||
|
|
||||||
def onKeyDown(self, event):
|
def onKeyUp(self, event):
|
||||||
keyCode = event.GetKeyCode()
|
keyCode = event.GetKeyCode()
|
||||||
# Ctrl + C
|
# Ctrl + C
|
||||||
if keyCode == 67 and event.ControlDown():
|
if keyCode == 67 and event.ControlDown():
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class ItemTraits(wx.Panel):
|
|||||||
self.traits.SetPage(item.traits.traitText)
|
self.traits.SetPage(item.traits.traitText)
|
||||||
|
|
||||||
self.traits.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
|
self.traits.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
|
||||||
self.traits.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
|
self.traits.Bind(wx.EVT_KEY_UP, self.onKeyUp)
|
||||||
|
|
||||||
mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0)
|
mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0)
|
||||||
self.Layout()
|
self.Layout()
|
||||||
@@ -32,7 +32,7 @@ class ItemTraits(wx.Panel):
|
|||||||
if selectedMenuItem == 1: # Copy was chosen
|
if selectedMenuItem == 1: # Copy was chosen
|
||||||
self.copySelectionToClipboard()
|
self.copySelectionToClipboard()
|
||||||
|
|
||||||
def onKeyDown(self, event):
|
def onKeyUp(self, event):
|
||||||
keyCode = event.GetKeyCode()
|
keyCode = event.GetKeyCode()
|
||||||
# Ctrl + C
|
# Ctrl + C
|
||||||
if keyCode == 67 and event.ControlDown():
|
if keyCode == 67 and event.ControlDown():
|
||||||
|
|||||||
@@ -203,22 +203,6 @@ class ItemView(Display):
|
|||||||
self.setToggles()
|
self.setToggles()
|
||||||
self.filterItemStore()
|
self.filterItemStore()
|
||||||
|
|
||||||
def itemSort(self, item):
|
|
||||||
sMkt = self.sMkt
|
|
||||||
catname = sMkt.getCategoryByItem(item).name
|
|
||||||
try:
|
|
||||||
mktgrpid = sMkt.getMarketGroupByItem(item).ID
|
|
||||||
except AttributeError:
|
|
||||||
mktgrpid = -1
|
|
||||||
pyfalog.warning("unable to find market group for {}".format(item.name))
|
|
||||||
parentname = sMkt.getParentItemByItem(item).name
|
|
||||||
# Get position of market group
|
|
||||||
metagrpid = sMkt.getMetaGroupIdByItem(item)
|
|
||||||
metatab = sMkt.META_MAP_REVERSE_INDICES.get(metagrpid)
|
|
||||||
metalvl = item.metaLevel or 0
|
|
||||||
|
|
||||||
return catname, mktgrpid, parentname, metatab, metalvl, item.name
|
|
||||||
|
|
||||||
def contextMenu(self, event):
|
def contextMenu(self, event):
|
||||||
clickedPos = self.getRowByAbs(event.Position)
|
clickedPos = self.getRowByAbs(event.Position)
|
||||||
self.ensureSelection(clickedPos)
|
self.ensureSelection(clickedPos)
|
||||||
@@ -241,7 +225,7 @@ class ItemView(Display):
|
|||||||
self.unselectAll()
|
self.unselectAll()
|
||||||
# Perform sorting, using item's meta levels besides other stuff
|
# Perform sorting, using item's meta levels besides other stuff
|
||||||
if self.marketBrowser.mode != 'recent':
|
if self.marketBrowser.mode != 'recent':
|
||||||
items.sort(key=self.itemSort)
|
items.sort(key=self.sMkt.itemSort)
|
||||||
# Mark current item list as active
|
# Mark current item list as active
|
||||||
self.active = items
|
self.active = items
|
||||||
# Show them
|
# Show them
|
||||||
@@ -251,12 +235,10 @@ class ItemView(Display):
|
|||||||
if len(items) > 1:
|
if len(items) > 1:
|
||||||
# Re-sort stuff
|
# Re-sort stuff
|
||||||
if self.marketBrowser.mode != 'recent':
|
if self.marketBrowser.mode != 'recent':
|
||||||
items.sort(key=self.itemSort)
|
items.sort(key=self.sMkt.itemSort)
|
||||||
|
|
||||||
for i, item in enumerate(items[:9]):
|
for i, item in enumerate(items[:9]):
|
||||||
# set shortcut info for first 9 modules
|
# set shortcut info for first 9 modules
|
||||||
item.marketShortcut = i + 1
|
item.marketShortcut = i + 1
|
||||||
|
|
||||||
Display.refresh(self, items)
|
Display.refresh(self, items)
|
||||||
|
|
||||||
def columnBackground(self, colItem, item):
|
def columnBackground(self, colItem, item):
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class PFSearchBox(wx.Window):
|
|||||||
|
|
||||||
def OnKeyPress(self, event):
|
def OnKeyPress(self, event):
|
||||||
if event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
if event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
||||||
HandleCtrlBackspace(self.EditBox)
|
HandleCtrlBackspace(self.EditBox)
|
||||||
else:
|
else:
|
||||||
event.Skip()
|
event.Skip()
|
||||||
|
|
||||||
|
|||||||
@@ -691,6 +691,13 @@ class Miscellanea(ViewColumn):
|
|||||||
formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3),
|
formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3),
|
||||||
)
|
)
|
||||||
return text, tooltip
|
return text, tooltip
|
||||||
|
elif itemGroup in ("Cargo Scanner", "Ship Scanner", "Survey Scanner"):
|
||||||
|
duration = stuff.getModifiedItemAttr("duration")
|
||||||
|
if not duration:
|
||||||
|
return "", None
|
||||||
|
text = "{}s".format(formatAmount(duration / 1000, 3, 0, 0))
|
||||||
|
tooltip = "Scan duration"
|
||||||
|
return text, tooltip
|
||||||
elif stuff.charge is not None:
|
elif stuff.charge is not None:
|
||||||
chargeGroup = stuff.charge.group.name
|
chargeGroup = stuff.charge.group.name
|
||||||
if chargeGroup.endswith("Rocket") or chargeGroup.endswith("Missile") or chargeGroup.endswith("Torpedo"):
|
if chargeGroup.endswith("Rocket") or chargeGroup.endswith("Missile") or chargeGroup.endswith("Torpedo"):
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class FittingView(d.Display):
|
|||||||
self.hoveredRow = None
|
self.hoveredRow = None
|
||||||
self.hoveredColumn = None
|
self.hoveredColumn = None
|
||||||
|
|
||||||
self.Bind(wx.EVT_KEY_DOWN, self.kbEvent)
|
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||||
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
|
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
|
||||||
self.Bind(wx.EVT_MIDDLE_DOWN, self.click)
|
self.Bind(wx.EVT_MIDDLE_DOWN, self.click)
|
||||||
@@ -562,7 +562,10 @@ class FittingView(d.Display):
|
|||||||
|
|
||||||
if sFit.serviceFittingOptions["rackSlots"]:
|
if sFit.serviceFittingOptions["rackSlots"]:
|
||||||
# flag to know when to add blanks, based on previous slot
|
# flag to know when to add blanks, based on previous slot
|
||||||
slotDivider = None if sFit.serviceFittingOptions["rackLabels"] else self.mods[0].slot
|
if sFit.serviceFittingOptions["rackLabels"] or len(self.mods) == 0:
|
||||||
|
slotDivider = None
|
||||||
|
else:
|
||||||
|
slotDivider = self.mods[0].slot
|
||||||
|
|
||||||
# first loop finds where slot dividers must go before modifying self.mods
|
# first loop finds where slot dividers must go before modifying self.mods
|
||||||
for i, mod in enumerate(self.mods):
|
for i, mod in enumerate(self.mods):
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from wx.lib.agw.floatspin import FloatSpin
|
|||||||
|
|
||||||
import config
|
import config
|
||||||
import gui.globalEvents as GE
|
import gui.globalEvents as GE
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.bitmap_loader import BitmapLoader
|
from gui.bitmap_loader import BitmapLoader
|
||||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor, TextEntryValidatedDialog
|
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor, TextEntryValidatedDialog
|
||||||
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
||||||
@@ -312,6 +312,7 @@ class CharacterEditor(AuxiliaryFrame):
|
|||||||
|
|
||||||
|
|
||||||
class SkillTreeView(wx.Panel):
|
class SkillTreeView(wx.Panel):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
||||||
style=wx.TAB_TRAVERSAL)
|
style=wx.TAB_TRAVERSAL)
|
||||||
@@ -611,12 +612,16 @@ class SkillTreeView(wx.Panel):
|
|||||||
|
|
||||||
def spawnMenu(self, event):
|
def spawnMenu(self, event):
|
||||||
item = event.GetItem()
|
item = event.GetItem()
|
||||||
|
itemData = self.skillTreeListCtrl.GetItemData(item)
|
||||||
|
if itemData is None:
|
||||||
|
return
|
||||||
|
|
||||||
self.skillTreeListCtrl.Select(item)
|
self.skillTreeListCtrl.Select(item)
|
||||||
thing = self.skillTreeListCtrl.GetFirstChild(item).IsOk()
|
thing = self.skillTreeListCtrl.GetFirstChild(item).IsOk()
|
||||||
if thing:
|
if thing:
|
||||||
return
|
return
|
||||||
|
|
||||||
id = self.skillTreeListCtrl.GetItemData(item)[1]
|
id = itemData[1]
|
||||||
eveItem = Market.getInstance().getItem(id)
|
eveItem = Market.getInstance().getItem(id)
|
||||||
|
|
||||||
srcContext = "skillItem"
|
srcContext = "skillItem"
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ class _TabRenderer:
|
|||||||
width = max(width, self.min_width)
|
width = max(width, self.min_width)
|
||||||
height = max(height, self.min_height)
|
height = max(height, self.min_height)
|
||||||
|
|
||||||
self.disabled = False
|
self._disabled = False
|
||||||
self.baseText = text
|
self.baseText = text
|
||||||
self.extraText = ''
|
self.extraText = ''
|
||||||
self.tab_size = (width, height)
|
self.tab_size = (width, height)
|
||||||
@@ -383,6 +383,18 @@ class _TabRenderer:
|
|||||||
self.position = (0, 0) # Not used internally for rendering - helper for tab container
|
self.position = (0, 0) # Not used internally for rendering - helper for tab container
|
||||||
self.InitTab()
|
self.InitTab()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def disabled(self):
|
||||||
|
return self._disabled
|
||||||
|
|
||||||
|
@disabled.setter
|
||||||
|
def disabled(self, value):
|
||||||
|
if value == self._disabled: # Avoid unnecessary re-rendering
|
||||||
|
return
|
||||||
|
|
||||||
|
self._disabled = value
|
||||||
|
self._Render()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
return self.baseText + self.extraText
|
return self.baseText + self.extraText
|
||||||
@@ -402,6 +414,10 @@ class _TabRenderer:
|
|||||||
width = max(width, self.min_width)
|
width = max(width, self.min_width)
|
||||||
height = max(height, self.min_height)
|
height = max(height, self.min_height)
|
||||||
|
|
||||||
|
cur_width, cur_height = self.tab_size
|
||||||
|
if (width == cur_width) and (height == cur_height):
|
||||||
|
return
|
||||||
|
|
||||||
self.tab_size = (width, height)
|
self.tab_size = (width, height)
|
||||||
self.InitTab()
|
self.InitTab()
|
||||||
|
|
||||||
@@ -927,7 +943,6 @@ class _TabsContainer(wx.Panel):
|
|||||||
tb_renderer = self.tabs[tab]
|
tb_renderer = self.tabs[tab]
|
||||||
tb_renderer.disabled = disabled
|
tb_renderer.disabled = disabled
|
||||||
|
|
||||||
self.AdjustTabsSize()
|
|
||||||
self.Refresh()
|
self.Refresh()
|
||||||
|
|
||||||
def GetSelectedTab(self):
|
def GetSelectedTab(self):
|
||||||
@@ -975,9 +990,6 @@ class _TabsContainer(wx.Panel):
|
|||||||
sel_tab = self.tabs.index(tab)
|
sel_tab = self.tabs.index(tab)
|
||||||
self.Parent.SetSelection(sel_tab)
|
self.Parent.SetSelection(sel_tab)
|
||||||
|
|
||||||
wx.PostEvent(self.Parent, PageChanged(self.tabs.index(old_sel_tab),
|
|
||||||
self.tabs.index(tab)))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class CopySelectDialog(wx.Dialog):
|
|||||||
|
|
||||||
def exportEsi(self, options, callback):
|
def exportEsi(self, options, callback):
|
||||||
fit = getFit(self.mainFrame.getActiveFit())
|
fit = getFit(self.mainFrame.getActiveFit())
|
||||||
Port.exportESI(fit, callback)
|
Port.exportESI(fit, True, callback)
|
||||||
|
|
||||||
def exportXml(self, options, callback):
|
def exportXml(self, options, callback):
|
||||||
fit = getFit(self.mainFrame.getActiveFit())
|
fit = getFit(self.mainFrame.getActiveFit())
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import wx
|
|||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
|
|
||||||
import eos.db
|
import eos.db
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.builtinShipBrowser.events import FitSelected
|
from gui.builtinShipBrowser.events import FitSelected
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import wx
|
|||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
|
|
||||||
import config
|
import config
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from service.prereqsCheck import version_block
|
from service.prereqsCheck import version_block
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import config
|
|||||||
import gui.globalEvents as GE
|
import gui.globalEvents as GE
|
||||||
from eos.db import getItem
|
from eos.db import getItem
|
||||||
from eos.saveddata.cargo import Cargo
|
from eos.saveddata.cargo import Cargo
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.display import Display
|
from gui.display import Display
|
||||||
from gui.characterEditor import APIView
|
from gui.characterEditor import APIView
|
||||||
from service.character import Character
|
from service.character import Character
|
||||||
@@ -18,6 +18,7 @@ from service.esiAccess import APIException
|
|||||||
from service.fit import Fit
|
from service.fit import Fit
|
||||||
from service.port import Port
|
from service.port import Port
|
||||||
from service.port.esi import ESIExportException
|
from service.port.esi import ESIExportException
|
||||||
|
from service.settings import EsiSettings
|
||||||
|
|
||||||
|
|
||||||
pyfalog = Logger(__name__)
|
pyfalog = Logger(__name__)
|
||||||
@@ -207,7 +208,7 @@ class ExportToEve(AuxiliaryFrame):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition,
|
parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition,
|
||||||
size=wx.Size(400, 120) if "wxGTK" in wx.PlatformInfo else wx.Size(350, 100), resizeable=True)
|
size=wx.Size(400, 140) if "wxGTK" in wx.PlatformInfo else wx.Size(350, 115), resizeable=True)
|
||||||
|
|
||||||
self.mainFrame = parent
|
self.mainFrame = parent
|
||||||
|
|
||||||
@@ -224,6 +225,11 @@ class ExportToEve(AuxiliaryFrame):
|
|||||||
|
|
||||||
mainSizer.Add(hSizer, 0, wx.EXPAND, 5)
|
mainSizer.Add(hSizer, 0, wx.EXPAND, 5)
|
||||||
|
|
||||||
|
self.exportChargesCb = wx.CheckBox(self, wx.ID_ANY, 'Export Loaded Charges', wx.DefaultPosition, wx.DefaultSize, 0)
|
||||||
|
self.exportChargesCb.SetValue(EsiSettings.getInstance().get('exportCharges'))
|
||||||
|
self.exportChargesCb.Bind(wx.EVT_CHECKBOX, self.OnChargeExportChange)
|
||||||
|
mainSizer.Add(self.exportChargesCb, 0, 0, 5)
|
||||||
|
|
||||||
self.exportBtn.Bind(wx.EVT_BUTTON, self.exportFitting)
|
self.exportBtn.Bind(wx.EVT_BUTTON, self.exportFitting)
|
||||||
|
|
||||||
self.statusbar = wx.StatusBar(self)
|
self.statusbar = wx.StatusBar(self)
|
||||||
@@ -239,6 +245,10 @@ class ExportToEve(AuxiliaryFrame):
|
|||||||
|
|
||||||
self.Center(wx.BOTH)
|
self.Center(wx.BOTH)
|
||||||
|
|
||||||
|
def OnChargeExportChange(self, event):
|
||||||
|
EsiSettings.getInstance().set('exportCharges', self.exportChargesCb.GetValue())
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
def updateCharList(self):
|
def updateCharList(self):
|
||||||
sEsi = Esi.getInstance()
|
sEsi = Esi.getInstance()
|
||||||
chars = sEsi.getSsoCharacters()
|
chars = sEsi.getSsoCharacters()
|
||||||
@@ -274,8 +284,9 @@ class ExportToEve(AuxiliaryFrame):
|
|||||||
sEsi = Esi.getInstance()
|
sEsi = Esi.getInstance()
|
||||||
|
|
||||||
sFit = Fit.getInstance()
|
sFit = Fit.getInstance()
|
||||||
|
exportCharges = self.exportChargesCb.GetValue()
|
||||||
try:
|
try:
|
||||||
data = sPort.exportESI(sFit.getFit(fitID))
|
data = sPort.exportESI(sFit.getFit(fitID), exportCharges)
|
||||||
except ESIExportException as e:
|
except ESIExportException as e:
|
||||||
msg = str(e)
|
msg = str(e)
|
||||||
if not msg:
|
if not msg:
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ class GuiCargoToLocalModuleCommand(wx.Command):
|
|||||||
dstModSlot = dstMod.slot
|
dstModSlot = dstMod.slot
|
||||||
if self.srcCargoItemID == dstModItemID:
|
if self.srcCargoItemID == dstModItemID:
|
||||||
return False
|
return False
|
||||||
# To keep all old item properties, copy them over from old module
|
# To keep all old item properties, copy them over from old module, except for mutations
|
||||||
newModInfo = ModuleInfo.fromModule(dstMod)
|
newModInfo = ModuleInfo.fromModule(dstMod, unmutate=True)
|
||||||
newModInfo.itemID = self.srcCargoItemID
|
newModInfo.itemID = self.srcCargoItemID
|
||||||
if dstMod.isEmpty:
|
if dstMod.isEmpty:
|
||||||
newCargoModItemID = None
|
newCargoModItemID = None
|
||||||
|
|||||||
@@ -324,7 +324,8 @@ def activeStateLimit(itemIdentity):
|
|||||||
'microJumpDrive', 'microJumpPortalDrive', 'emergencyHullEnergizer',
|
'microJumpDrive', 'microJumpPortalDrive', 'emergencyHullEnergizer',
|
||||||
'cynosuralGeneration', 'jumpPortalGeneration', 'jumpPortalGenerationBO',
|
'cynosuralGeneration', 'jumpPortalGeneration', 'jumpPortalGenerationBO',
|
||||||
'cloneJumpAccepting', 'cloakingWarpSafe', 'cloakingPrototype', 'cloaking',
|
'cloneJumpAccepting', 'cloakingWarpSafe', 'cloakingPrototype', 'cloaking',
|
||||||
'massEntanglerEffect5', 'electronicAttributeModifyOnline', 'targetPassively'
|
'massEntanglerEffect5', 'electronicAttributeModifyOnline', 'targetPassively',
|
||||||
|
'cargoScan', 'shipScan', 'surveyScan'
|
||||||
}.intersection(item.effects):
|
}.intersection(item.effects):
|
||||||
return FittingModuleState.ONLINE
|
return FittingModuleState.ONLINE
|
||||||
return FittingModuleState.ACTIVE
|
return FittingModuleState.ACTIVE
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import wx
|
|||||||
import config
|
import config
|
||||||
import gui.mainFrame
|
import gui.mainFrame
|
||||||
from eos.saveddata.module import Module
|
from eos.saveddata.module import Module
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.bitmap_loader import BitmapLoader
|
from gui.bitmap_loader import BitmapLoader
|
||||||
from gui.builtinItemStatsViews.itemAffectedBy import ItemAffectedBy
|
from gui.builtinItemStatsViews.itemAffectedBy import ItemAffectedBy
|
||||||
from gui.builtinItemStatsViews.itemAttributes import ItemParams
|
from gui.builtinItemStatsViews.itemAttributes import ItemParams
|
||||||
|
|||||||
@@ -110,8 +110,6 @@ class OpenFitsThread(threading.Thread):
|
|||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
time.sleep(0.5) # Give GUI some time to finish drawing
|
|
||||||
|
|
||||||
# `startup` tells FitSpawner that we are loading fits are startup, and
|
# `startup` tells FitSpawner that we are loading fits are startup, and
|
||||||
# has 3 values:
|
# has 3 values:
|
||||||
# False = Set as default in FitSpawner itself, never set here
|
# False = Set as default in FitSpawner itself, never set here
|
||||||
@@ -610,7 +608,9 @@ class MainFrame(wx.Frame):
|
|||||||
(wx.ACCEL_CTRL, wx.WXK_PAGEDOWN, ctabnext),
|
(wx.ACCEL_CTRL, wx.WXK_PAGEDOWN, ctabnext),
|
||||||
(wx.ACCEL_CTRL, wx.WXK_PAGEUP, ctabprev),
|
(wx.ACCEL_CTRL, wx.WXK_PAGEUP, ctabprev),
|
||||||
(wx.ACCEL_CMD, wx.WXK_PAGEDOWN, ctabnext),
|
(wx.ACCEL_CMD, wx.WXK_PAGEDOWN, ctabnext),
|
||||||
(wx.ACCEL_CMD, wx.WXK_PAGEUP, ctabprev)
|
(wx.ACCEL_CMD, wx.WXK_PAGEUP, ctabprev),
|
||||||
|
|
||||||
|
(wx.ACCEL_CMD | wx.ACCEL_SHIFT, ord("Z"), wx.ID_REDO)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Ctrl/Cmd+# for addition pane selection
|
# Ctrl/Cmd+# for addition pane selection
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
import wx
|
import wx
|
||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
|
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.bitmap_loader import BitmapLoader
|
from gui.bitmap_loader import BitmapLoader
|
||||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
||||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import gui.builtinMarketBrowser.pfSearchBox as SBox
|
|||||||
import gui.display as d
|
import gui.display as d
|
||||||
import gui.globalEvents as GE
|
import gui.globalEvents as GE
|
||||||
from eos.db.gamedata.queries import getAttributeInfo, getItem
|
from eos.db.gamedata.queries import getAttributeInfo, getItem
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.bitmap_loader import BitmapLoader
|
from gui.bitmap_loader import BitmapLoader
|
||||||
from gui.marketBrowser import SearchBox
|
from gui.marketBrowser import SearchBox
|
||||||
from service.fit import Fit
|
from service.fit import Fit
|
||||||
@@ -213,19 +213,7 @@ class ItemView(d.Display):
|
|||||||
def itemSort(self, item):
|
def itemSort(self, item):
|
||||||
sMkt = Market.getInstance()
|
sMkt = Market.getInstance()
|
||||||
isFittable = item.group.name in sMkt.FIT_GROUPS or item.category.name in sMkt.FIT_CATEGORIES
|
isFittable = item.group.name in sMkt.FIT_GROUPS or item.category.name in sMkt.FIT_CATEGORIES
|
||||||
catname = sMkt.getCategoryByItem(item).name
|
return (not isFittable, *sMkt.itemSort(item))
|
||||||
try:
|
|
||||||
mktgrpid = sMkt.getMarketGroupByItem(item).ID
|
|
||||||
except AttributeError:
|
|
||||||
mktgrpid = -1
|
|
||||||
pyfalog.warning("unable to find market group for {}".format(item.name))
|
|
||||||
parentname = sMkt.getParentItemByItem(item).name
|
|
||||||
# Get position of market group
|
|
||||||
metagrpid = sMkt.getMetaGroupIdByItem(item)
|
|
||||||
metatab = sMkt.META_MAP_REVERSE_INDICES.get(metagrpid)
|
|
||||||
metalvl = item.metaLevel or 0
|
|
||||||
|
|
||||||
return not isFittable, catname, mktgrpid, parentname, metatab, metalvl, item.name
|
|
||||||
|
|
||||||
def populateSearch(self, itemIDs):
|
def populateSearch(self, itemIDs):
|
||||||
items = Market.getItems(itemIDs)
|
items = Market.getItems(itemIDs)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
import wx
|
import wx
|
||||||
from logbook import Logger
|
from logbook import Logger
|
||||||
|
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
||||||
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
||||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ class ShipBrowser(wx.Panel):
|
|||||||
"amarr", "caldari", "gallente", "minmatar",
|
"amarr", "caldari", "gallente", "minmatar",
|
||||||
"sisters", "ore", "concord",
|
"sisters", "ore", "concord",
|
||||||
"serpentis", "angel", "blood", "sansha", "guristas", "mordu",
|
"serpentis", "angel", "blood", "sansha", "guristas", "mordu",
|
||||||
"jove", "upwell", "triglavian", None
|
"jove", "triglavian", "upwell", None
|
||||||
]
|
]
|
||||||
|
|
||||||
def raceNameKey(self, ship):
|
def raceNameKey(self, ship):
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from logbook import Logger
|
|||||||
|
|
||||||
import gui.globalEvents as GE
|
import gui.globalEvents as GE
|
||||||
import gui.mainFrame
|
import gui.mainFrame
|
||||||
from gui.auxFrame import AuxiliaryFrame
|
from gui.auxWindow import AuxiliaryFrame
|
||||||
from gui.bitmap_loader import BitmapLoader
|
from gui.bitmap_loader import BitmapLoader
|
||||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
||||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||||
|
|||||||
BIN
imgs/icons/24252@1x.png
Normal file
|
After Width: | Height: | Size: 728 B |
BIN
imgs/icons/24252@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
imgs/icons/24460@1x.png
Normal file
|
After Width: | Height: | Size: 922 B |
BIN
imgs/icons/24460@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
imgs/icons/24464@1x.png
Normal file
|
After Width: | Height: | Size: 718 B |
BIN
imgs/icons/24464@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
imgs/icons/24465@1x.png
Normal file
|
After Width: | Height: | Size: 736 B |
BIN
imgs/icons/24465@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/24466@1x.png
Normal file
|
After Width: | Height: | Size: 826 B |
BIN
imgs/icons/24466@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
imgs/icons/24467@1x.png
Normal file
|
After Width: | Height: | Size: 855 B |
BIN
imgs/icons/24467@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
imgs/icons/24468@1x.png
Normal file
|
After Width: | Height: | Size: 789 B |
BIN
imgs/icons/24468@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
imgs/icons/24469@1x.png
Normal file
|
After Width: | Height: | Size: 719 B |
BIN
imgs/icons/24469@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/24470@1x.png
Normal file
|
After Width: | Height: | Size: 750 B |
BIN
imgs/icons/24470@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
imgs/icons/24471@1x.png
Normal file
|
After Width: | Height: | Size: 712 B |
BIN
imgs/icons/24471@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/24472@1x.png
Normal file
|
After Width: | Height: | Size: 739 B |
BIN
imgs/icons/24472@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/24473@1x.png
Normal file
|
After Width: | Height: | Size: 719 B |
BIN
imgs/icons/24473@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
imgs/icons/24474@1x.png
Normal file
|
After Width: | Height: | Size: 880 B |
BIN
imgs/icons/24474@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
imgs/icons/24475@1x.png
Normal file
|
After Width: | Height: | Size: 864 B |
BIN
imgs/icons/24475@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
imgs/icons/24476@1x.png
Normal file
|
After Width: | Height: | Size: 882 B |
BIN
imgs/icons/24476@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
imgs/icons/24477@1x.png
Normal file
|
After Width: | Height: | Size: 851 B |
BIN
imgs/icons/24477@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
imgs/icons/24478@1x.png
Normal file
|
After Width: | Height: | Size: 886 B |
BIN
imgs/icons/24478@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
imgs/icons/24479@1x.png
Normal file
|
After Width: | Height: | Size: 874 B |
BIN
imgs/icons/24479@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
imgs/icons/24480@1x.png
Normal file
|
After Width: | Height: | Size: 896 B |
BIN
imgs/icons/24480@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
imgs/icons/24481@1x.png
Normal file
|
After Width: | Height: | Size: 865 B |
BIN
imgs/icons/24481@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
imgs/icons/24482@1x.png
Normal file
|
After Width: | Height: | Size: 889 B |
BIN
imgs/icons/24482@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
imgs/icons/24483@1x.png
Normal file
|
After Width: | Height: | Size: 845 B |
BIN
imgs/icons/24483@2x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
imgs/icons/24484@1x.png
Normal file
|
After Width: | Height: | Size: 902 B |
BIN
imgs/icons/24484@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
imgs/icons/24485@1x.png
Normal file
|
After Width: | Height: | Size: 876 B |
BIN
imgs/icons/24485@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
imgs/icons/24486@1x.png
Normal file
|
After Width: | Height: | Size: 887 B |
BIN
imgs/icons/24486@2x.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
imgs/icons/24487@1x.png
Normal file
|
After Width: | Height: | Size: 746 B |
BIN
imgs/icons/24487@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
imgs/icons/24488@1x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
imgs/icons/24488@2x.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |