Compare commits
217 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a7f5b6092 | ||
|
|
e27ea79bc6 | ||
|
|
adb0609eb8 | ||
|
|
39b08baff0 | ||
|
|
e4388bf835 | ||
|
|
b9cae5165b | ||
|
|
06e6589209 | ||
|
|
9f21b251ff | ||
|
|
7d7a499070 | ||
|
|
e18a3bfab5 | ||
|
|
385215b717 | ||
|
|
d09913d61b | ||
|
|
b9059835ce | ||
|
|
7af28b497c | ||
|
|
5cf555b8ce | ||
|
|
a25bb1aaa3 | ||
|
|
24a54c9cec | ||
|
|
0b9e6bf55d | ||
|
|
41070ce22e | ||
|
|
7b5aab8a35 | ||
|
|
c9fe6c959d | ||
|
|
67cf3a3a26 | ||
|
|
c644d528e9 | ||
|
|
c1302ce7ee | ||
|
|
e4ea9c2ab9 | ||
|
|
cca5781ca8 | ||
|
|
9760d2c4a5 | ||
|
|
4efa9a6961 | ||
|
|
b27d4bcbbd | ||
|
|
7bff295012 | ||
|
|
01249dfd9c | ||
|
|
0cc2668c1c | ||
|
|
dfad734a0a | ||
|
|
66c901a2b0 | ||
|
|
3673e7f39e | ||
|
|
124b35d108 | ||
|
|
5181e8e95a | ||
|
|
48cc4ce9d4 | ||
|
|
07ff43b576 | ||
|
|
70969cc9fb | ||
|
|
d23bf2fa4c | ||
|
|
04c8659922 | ||
|
|
5e3b92699b | ||
|
|
1755ab4c3f | ||
|
|
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 | ||
|
|
7eaeea325d | ||
|
|
9695526352 | ||
|
|
4f35bc38db | ||
|
|
d9c7c1a0fd | ||
|
|
a94e66e5ed | ||
|
|
e9c41c6439 | ||
|
|
b24ceb95f5 | ||
|
|
6ba6e0f69b | ||
|
|
9aca49e539 | ||
|
|
01f1cdb175 | ||
|
|
c1356906bb | ||
|
|
8229537e5f | ||
|
|
6cfa2161ec | ||
|
|
fa3cf46786 | ||
|
|
ac40dfd018 | ||
|
|
4c32d559bb | ||
|
|
02ee12586f | ||
|
|
496f15a83e | ||
|
|
acf6af8f95 | ||
|
|
225b00b9ff | ||
|
|
7f2b7415ea | ||
|
|
d1d9ae4dac | ||
|
|
ac7ccfb6a3 | ||
|
|
05c2c41762 | ||
|
|
10f5133282 | ||
|
|
42658a8167 | ||
|
|
6dbf38dbb4 | ||
|
|
64306e2366 | ||
|
|
f259df85cd | ||
|
|
1090589cb2 | ||
|
|
29e09fcdda | ||
|
|
36fd4aeaff | ||
|
|
c46a59e3b1 | ||
|
|
8a3a21972a | ||
|
|
6839669e04 | ||
|
|
5645e20505 | ||
|
|
15fe8f6261 | ||
|
|
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 |
19
db_update.py
@@ -33,7 +33,7 @@ DB_PATH = os.path.join(ROOT_DIR, 'eve.db')
|
||||
JSON_DIR = os.path.join(ROOT_DIR, 'staticdata')
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
GAMEDATA_SCHEMA_VERSION = 3
|
||||
GAMEDATA_SCHEMA_VERSION = 4
|
||||
|
||||
|
||||
def db_needs_update():
|
||||
@@ -122,9 +122,11 @@ def update_db():
|
||||
if (
|
||||
# Apparently people really want Civilian modules available
|
||||
(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
|
||||
# Nearly useless and clutter search results too much
|
||||
elif row['typeName'].startswith('Limited Synth '):
|
||||
row['published'] = False
|
||||
|
||||
@@ -138,11 +140,10 @@ def update_db():
|
||||
row['typeID'] in (41549, 41548, 41551, 41550) or
|
||||
# Abyssal weather (environment)
|
||||
row['groupID'] in (
|
||||
1882,
|
||||
1975,
|
||||
1971,
|
||||
# the "container" for the abyssal environments
|
||||
1983)
|
||||
1882,
|
||||
1975,
|
||||
1971,
|
||||
1983) # the "container" for the abyssal environments
|
||||
):
|
||||
newData.append(row)
|
||||
|
||||
@@ -327,8 +328,8 @@ def update_db():
|
||||
|
||||
def composeReqSkills(raw):
|
||||
reqSkills = {}
|
||||
for skillTypeID, skillLevels in raw.items():
|
||||
reqSkills[int(skillTypeID)] = skillLevels[0]
|
||||
for skillTypeID, skillLevel in raw.items():
|
||||
reqSkills[int(skillTypeID)] = skillLevel
|
||||
return reqSkills
|
||||
|
||||
eveTypeIds = set(r['typeID'] for r in eveTypesData)
|
||||
|
||||
@@ -119,7 +119,7 @@ function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||
begin
|
||||
if(IsAppRunning( 'pyfa.exe' )) then
|
||||
begin
|
||||
Result := 'Please close pyfa before continuing. When closed, please go back to the previous step and continue.';
|
||||
Result := 'Please close pyfa before continuing. When closed, please go back to the previous step and continue. If you have named this installer pyfa.exe, please rename it and restart installation';
|
||||
end
|
||||
else
|
||||
begin
|
||||
|
||||
42
eos/db/migrations/upgrade38.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
Migration 38
|
||||
|
||||
- Armor hardener tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
16357: ( # Experimental Enduring EM Armor Hardener I
|
||||
16353, # Upgraded Armor EM Hardener I
|
||||
),
|
||||
16365: ( # Experimental Enduring Explosive Armor Hardener I
|
||||
16361, # Upgraded Armor Explosive Hardener I
|
||||
),
|
||||
16373: ( # Experimental Enduring Kinetic Armor Hardener I
|
||||
16369, # Upgraded Armor Kinetic Hardener I
|
||||
),
|
||||
16381: ( # Experimental Enduring Thermal Armor Hardener I
|
||||
16377, # Upgraded Armor Thermal Hardener I
|
||||
),
|
||||
16359: ( # Prototype Compact EM Armor Hardener I
|
||||
16355, # Limited Armor EM Hardener I
|
||||
),
|
||||
16367: ( # Prototype Compact Explosive Armor Hardener I
|
||||
16363, # Limited Armor Explosive Hardener I
|
||||
),
|
||||
16375: ( # Prototype Compact Kinetic Armor Hardener I
|
||||
16371, # Limited Armor Kinetic Hardener I
|
||||
),
|
||||
16383: ( # Prototype Compact Thermal Armor Hardener I
|
||||
16379, # Limited Armor Thermal Hardener 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 "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
34
eos/db/migrations/upgrade39.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Migration 39
|
||||
|
||||
- Shield amplifier tiericide
|
||||
- CCP getting rid of DB TDs due to exploits
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
1798: ( # 'Basic' EM Shield Amplifier
|
||||
9562, # Supplemental EM Ward Amplifier
|
||||
),
|
||||
1804: ( # 'Basic' Explosive Shield Amplifier
|
||||
9574, # Supplemental Explosive Deflection Amplifier
|
||||
),
|
||||
1802: ( # 'Basic' Kinetic Shield Amplifier
|
||||
9570, # Supplemental Kinetic Deflection Amplifier
|
||||
),
|
||||
1800: ( # 'Basic' Thermal Shield Amplifier
|
||||
9566, # Supplemental Thermal Dissipation Amplifier
|
||||
),
|
||||
22933: ( # 'Investor' Tracking Disruptor I
|
||||
32416, # Dark Blood Tracking Disruptor
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
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 "cargo" SET "itemID" = ? WHERE "itemID" = ?',
|
||||
(replacement_item, retired_item))
|
||||
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))
|
||||
26
eos/db/migrations/upgrade43.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Migration 43
|
||||
|
||||
- Shield booster amplifier tiericide
|
||||
"""
|
||||
|
||||
CONVERSIONS = {
|
||||
16533: ( # Stalwart Restrained Shield Boost Amplifier
|
||||
16531, # 5a Prototype Shield Support I
|
||||
),
|
||||
16535: ( # Copasetic Compact Shield Boost Amplifier
|
||||
16529, # Ionic Field Accelerator 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))
|
||||
1327
eos/effects.py
@@ -146,6 +146,12 @@ class Effect(EqBase):
|
||||
|
||||
return self.__effectDef is not None
|
||||
|
||||
@property
|
||||
def dealsDamage(self):
|
||||
if not self.__generated:
|
||||
self.__generateHandler()
|
||||
return self.__dealsDamage
|
||||
|
||||
def isType(self, 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.__runTime = getattr(effectDef, "runTime", "normal")
|
||||
self.__activeByDefault = getattr(effectDef, "activeByDefault", True)
|
||||
self.__dealsDamage = effectDef.dealsDamage
|
||||
effectType = getattr(effectDef, "type", None)
|
||||
effectType = effectType if isinstance(effectType, tuple) or effectType is None else (effectType,)
|
||||
self.__type = effectType
|
||||
@@ -175,6 +182,7 @@ class Effect(EqBase):
|
||||
self.__handler = eos.effects.DummyEffect.handler
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__dealsDamage = False
|
||||
self.__type = None
|
||||
pyfalog.debug("ImportError generating handler: {0}", e)
|
||||
except AttributeError as e:
|
||||
@@ -182,6 +190,7 @@ class Effect(EqBase):
|
||||
self.__handler = eos.effects.DummyEffect.handler
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__dealsDamage = False
|
||||
self.__type = None
|
||||
pyfalog.error("AttributeError generating handler: {0}", e)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
@@ -190,6 +199,7 @@ class Effect(EqBase):
|
||||
self.__handler = eos.effects.DummyEffect.handler
|
||||
self.__runTime = "normal"
|
||||
self.__activeByDefault = True
|
||||
self.__dealsDamage = False
|
||||
self.__type = None
|
||||
pyfalog.critical("Exception generating handler:")
|
||||
pyfalog.critical(e)
|
||||
@@ -333,7 +343,11 @@ class Item(EqBase):
|
||||
if self.__race is None:
|
||||
|
||||
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"
|
||||
else:
|
||||
self.__race = self.factionMap[self.factionID]
|
||||
|
||||
@@ -83,14 +83,17 @@ BUILTINS = OrderedDict([
|
||||
(-52, ('[Bombs]Scorch Bomb', 0, 6400, 0, 0)),
|
||||
(-53, ('[Bombs]Concussion Bomb', 0, 0, 6400, 0)),
|
||||
(-54, ('[Bombs]Shrapnel Bomb', 0, 0, 0, 6400)),
|
||||
# Source: ticket #2067
|
||||
(-55, ('[NPC][Abyssal]All', 130, 396, 258, 216)),
|
||||
# Source: ticket #2067 and #2265
|
||||
(-55, ('[NPC][Abyssal]All', 126, 427, 218, 230)),
|
||||
(-109, ('[NPC][Abyssal]Angel', 450, 72, 80, 398)),
|
||||
(-107, ('[NPC][Abyssal]Concord', 53, 559, 94, 295)),
|
||||
(-56, ('[NPC][Abyssal]Drifter', 250, 250, 250, 250)),
|
||||
(-57, ('[NPC][Abyssal]Drones', 250, 250, 250, 250)),
|
||||
(-58, ('[NPC][Abyssal]Overmind', 0, 408, 592, 0)),
|
||||
(-59, ('[NPC][Abyssal]Seeker', 406, 406, 94, 94)),
|
||||
(-58, ('[NPC][Abyssal]Overmind', 0, 410, 590, 0)),
|
||||
(-108, ('[NPC][Abyssal]Sansha', 569, 431, 0, 0)),
|
||||
(-59, ('[NPC][Abyssal]Seeker', 402, 402, 98, 98)),
|
||||
(-60, ('[NPC][Abyssal]Sleeper', 313, 313, 187, 187)),
|
||||
(-61, ('[NPC][Abyssal]Triglavian', 0, 610, 0, 390)),
|
||||
(-61, ('[NPC][Abyssal]Triglavian', 0, 615, 0, 385)),
|
||||
(-62, ('[NPC][Asteroid]Angel Cartel', 1838, 562, 2215, 3838)),
|
||||
(-63, ('[NPC][Asteroid]Blood Raiders', 5067, 4214, 0, 0)),
|
||||
(-64, ('[NPC][Asteroid]Guristas', 0, 1828, 7413, 0)),
|
||||
|
||||
@@ -364,3 +364,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if self.item.groupID in fitDroneGroupLimits:
|
||||
return True
|
||||
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 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
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
from math import log, sqrt
|
||||
from math import floor, log, sqrt
|
||||
|
||||
from logbook import Logger
|
||||
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.ship import Ship
|
||||
from eos.saveddata.targetProfile import TargetProfile
|
||||
from eos.utils.float import floatUnerr
|
||||
from eos.utils.stats import DmgTypes, RRTypes
|
||||
|
||||
|
||||
@@ -378,8 +379,9 @@ class Fit:
|
||||
|
||||
@property
|
||||
def maxTargets(self):
|
||||
return min(self.extraAttributes["maxTargetsLockedFromSkills"],
|
||||
self.ship.getModifiedItemAttr("maxLockedTargets"))
|
||||
maxTargets = min(self.extraAttributes["maxTargetsLockedFromSkills"],
|
||||
self.ship.getModifiedItemAttr("maxLockedTargets"))
|
||||
return floor(floatUnerr(maxTargets))
|
||||
|
||||
@property
|
||||
def maxTargetRange(self):
|
||||
@@ -750,14 +752,14 @@ class Fit:
|
||||
|
||||
if warfareBuffID == 79: # AOE_Beacon_bioluminescence_cloud
|
||||
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
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Repair Systems"),
|
||||
"armorDamageAmount", value, stackingPenalties=True)
|
||||
if warfareBuffID == 80: # AOE_Beacon_caustic_cloud_inertia
|
||||
self.ship.boostItemAttr("agility", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 81: # AOE_Beacon_caustic_cloud_remote_repair
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"),
|
||||
"armorDamageAmount", value, stackingPenalties=True)
|
||||
if warfareBuffID == 81: # AOE_Beacon_caustic_cloud_velocity
|
||||
self.ship.boostItemAttr("maxVelocity", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 88: # AOE_Beacon_filament_cloud_shield_booster_shield_bonus
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation"),
|
||||
@@ -772,7 +774,11 @@ class Fit:
|
||||
if warfareBuffID == 90: # Weather_electric_storm_EM_resistance_penalty
|
||||
for tankType in ("shield", "armor"):
|
||||
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.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"emDamageResonance", value) #for hull
|
||||
|
||||
if warfareBuffID == 92: # Weather_electric_storm_capacitor_recharge_bonus
|
||||
self.ship.boostItemAttr("rechargeRate", value, stackingPenalties=True)
|
||||
@@ -780,32 +786,54 @@ class Fit:
|
||||
if warfareBuffID == 93: # Weather_xenon_gas_explosive_resistance_penalty
|
||||
for tankType in ("shield", "armor"):
|
||||
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.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"explosiveDamageResonance", value) # for hull
|
||||
|
||||
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
|
||||
for tankType in ("shield", "armor"):
|
||||
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.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"thermalDamageResonance", value) # for hull
|
||||
|
||||
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
|
||||
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"),
|
||||
"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"),
|
||||
"falloff", value, stackingPenalties=True)
|
||||
self.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"falloff", value, stackingPenalties=True)
|
||||
|
||||
if warfareBuffID == 98: # Weather_darkness_velocity_bonus
|
||||
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
|
||||
for tankType in ("shield", "armor"):
|
||||
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.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"),
|
||||
"kineticDamageResonance", value) # for hull
|
||||
|
||||
if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus
|
||||
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
|
||||
@@ -1028,7 +1056,7 @@ class Fit:
|
||||
|
||||
def clearTail(self):
|
||||
tailPositions = {}
|
||||
for mod in self.modules:
|
||||
for mod in reversed(self.modules):
|
||||
if not mod.isEmpty:
|
||||
break
|
||||
tailPositions[self.modules.index(mod)] = mod.slot
|
||||
|
||||
@@ -64,7 +64,9 @@ ProjectedSystem = {
|
||||
class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
"""An instance of this class represents a module together with its charge and modified attributes"""
|
||||
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):
|
||||
"""Initialize a module from the program"""
|
||||
@@ -196,11 +198,18 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
# todo: validate baseItem as well if it's set.
|
||||
if self.isEmpty:
|
||||
return False
|
||||
return (
|
||||
self.__item is None or (
|
||||
self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and
|
||||
self.__item.group.name not in self.SYSTEM_GROUPS) or
|
||||
(self.item.isAbyssal and not self.isMutated))
|
||||
if self.__item is None:
|
||||
return True
|
||||
if (
|
||||
self.__item.category.name not in ("Module", "Subsystem", "Structure Module")
|
||||
and self.__item.group.name not in self.SYSTEM_GROUPS
|
||||
):
|
||||
return True
|
||||
if self.item.isAbyssal and not self.isMutated:
|
||||
return True
|
||||
if self.isMutated and not self.__mutaplasmid:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def isMutated(self):
|
||||
@@ -265,7 +274,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
@property
|
||||
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
|
||||
def isCapitalSize(self):
|
||||
@@ -461,6 +472,20 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return True
|
||||
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):
|
||||
if self.isEmpty or (self.state < FittingModuleState.ACTIVE and not ignoreState):
|
||||
return {0: DmgTypes(0, 0, 0, 0)}
|
||||
@@ -712,6 +737,9 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
return False
|
||||
elif state == FittingModuleState.OVERHEATED and not self.item.isType("overheat"):
|
||||
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:
|
||||
return True
|
||||
|
||||
@@ -1037,7 +1065,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
elif click == "ctrl":
|
||||
state = FittingModuleState.OFFLINE
|
||||
else:
|
||||
state = transitionMap[currState]
|
||||
try:
|
||||
state = transitionMap[currState]
|
||||
except KeyError:
|
||||
state = min(transitionMap)
|
||||
# If passive module tries to transition into online and fails,
|
||||
# put it to passive instead
|
||||
if not mod.isValidState(state) and currState == FittingModuleState.ONLINE:
|
||||
|
||||
@@ -89,12 +89,9 @@ class Mutator(EqBase):
|
||||
# sweet, all good
|
||||
returnVal = val
|
||||
else:
|
||||
# need to fudge the numbers a bit. Go with the value closest to base
|
||||
if val >= 0:
|
||||
returnVal = min(self.maxValue, max(self.minValue, val))
|
||||
else:
|
||||
returnVal = max(self.maxValue, min(self.minValue, val))
|
||||
|
||||
actualMin = min(self.minValue, self.maxValue)
|
||||
actualMax = max(self.minValue, self.maxValue)
|
||||
returnVal = min(actualMax, max(actualMin, val))
|
||||
return returnVal
|
||||
|
||||
@property
|
||||
@@ -120,15 +117,24 @@ class Mutator(EqBase):
|
||||
|
||||
@property
|
||||
def baseValue(self):
|
||||
return self.baseAttribute.value
|
||||
try:
|
||||
return self.baseAttribute.value
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def minValue(self):
|
||||
return self.minMod * self.baseAttribute.value
|
||||
try:
|
||||
return self.minMod * self.baseAttribute.value
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def maxValue(self):
|
||||
return self.maxMod * self.baseAttribute.value
|
||||
try:
|
||||
return self.maxMod * self.baseAttribute.value
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def attribute(self):
|
||||
|
||||
@@ -86,100 +86,140 @@ BUILTINS = OrderedDict([
|
||||
# Source: ticket #2067
|
||||
(-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)),
|
||||
(-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)),
|
||||
(-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)),
|
||||
(-58, ('[NPC][Abyssal][Dark Matter All Tiers]Overmind', 0.626, 0.576, 0.612, 0.624)),
|
||||
(-58, ('[NPC][Abyssal][Dark Matter All Tiers]Overmind', 0.643, 0.593, 0.624, 0.639)),
|
||||
(-59, ('[NPC][Abyssal][Dark Matter All Tiers]Seeker', 0.082, 0.082, 0.082, 0.082)),
|
||||
(-60, ('[NPC][Abyssal][Dark Matter All Tiers]Triglavian', 0.477, 0.401, 0.449, 0.37)),
|
||||
(-61, ('[NPC][Abyssal][Dark Matter All Tiers]Drifter', 0.403, 0.403, 0.403, 0.403)),
|
||||
(-60, ('[NPC][Abyssal][Dark Matter All Tiers]Triglavian', 0.494, 0.41, 0.464, 0.376)),
|
||||
(-61, ('[NPC][Abyssal][Dark Matter All Tiers]Drifter', 0.415, 0.415, 0.415, 0.415)),
|
||||
(-62, ('[NPC][Abyssal][Dark Matter All Tiers]Sleeper', 0.435, 0.435, 0.435, 0.435)),
|
||||
(-63, ('[NPC][Abyssal][Dark Matter All Tiers]All', 0.507, 0.477, 0.502, 0.493)),
|
||||
(-64, ('[NPC][Abyssal][Electrical T1/T2]Drones', 0.323, 0.522, 0.529, 0.435)),
|
||||
(-65, ('[NPC][Abyssal][Electrical T1/T2]Overmind', 0.521, 0.576, 0.612, 0.624)),
|
||||
(-66, ('[NPC][Abyssal][Electrical T1/T2]Seeker', 0, 0.082, 0.082, 0.082)),
|
||||
(-67, ('[NPC][Abyssal][Electrical T1/T2]Triglavian', 0.333, 0.401, 0.449, 0.37)),
|
||||
(-68, ('[NPC][Abyssal][Electrical T1/T2]Drifter', 0.267, 0.403, 0.403, 0.403)),
|
||||
(-69, ('[NPC][Abyssal][Electrical T1/T2]Sleeper', 0.329, 0.435, 0.435, 0.435)),
|
||||
(-70, ('[NPC][Abyssal][Electrical T1/T2]All', 0.385, 0.477, 0.502, 0.493)),
|
||||
(-63, ('[NPC][Abyssal][Dark Matter All Tiers]All', 0.508, 0.474, 0.495, 0.488)),
|
||||
(-64, ('[NPC][Abyssal][Electrical T0/T1/T2]Drones', 0.323, 0.522, 0.529, 0.435)),
|
||||
(-65, ('[NPC][Abyssal][Electrical T0/T1/T2]Overmind', 0.542, 0.593, 0.624, 0.639)),
|
||||
(-66, ('[NPC][Abyssal][Electrical T0/T1/T2]Seeker', 0, 0.082, 0.082, 0.082)),
|
||||
(-67, ('[NPC][Abyssal][Electrical T0/T1/T2]Triglavian', 0.356, 0.41, 0.464, 0.376)),
|
||||
(-68, ('[NPC][Abyssal][Electrical T0/T1/T2]Drifter', 0.277, 0.415, 0.415, 0.415)),
|
||||
(-69, ('[NPC][Abyssal][Electrical T0/T1/T2]Sleeper', 0.329, 0.435, 0.435, 0.435)),
|
||||
(-70, ('[NPC][Abyssal][Electrical T0/T1/T2]All', 0.381, 0.474, 0.495, 0.488)),
|
||||
(-71, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Drones', 0.255, 0.522, 0.529, 0.435)),
|
||||
(-72, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Overmind', 0.457, 0.576, 0.612, 0.624)),
|
||||
(-72, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Overmind', 0.48, 0.593, 0.624, 0.639)),
|
||||
(-73, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Seeker', 0, 0.082, 0.082, 0.082)),
|
||||
(-74, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Triglavian', 0.241, 0.401, 0.449, 0.37)),
|
||||
(-75, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Drifter', 0.184, 0.403, 0.403, 0.403)),
|
||||
(-74, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Triglavian', 0.268, 0.41, 0.464, 0.376)),
|
||||
(-75, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Drifter', 0.191, 0.415, 0.415, 0.415)),
|
||||
(-76, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Sleeper', 0.268, 0.435, 0.435, 0.435)),
|
||||
(-77, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]All', 0.313, 0.477, 0.502, 0.493)),
|
||||
(-78, ('[NPC][Abyssal][Electrical T4/T5]Drones', 0.193, 0.522, 0.529, 0.435)),
|
||||
(-79, ('[NPC][Abyssal][Electrical T4/T5]Overmind', 0.398, 0.576, 0.612, 0.624)),
|
||||
(-80, ('[NPC][Abyssal][Electrical T4/T5]Seeker', 0, 0.082, 0.082, 0.082)),
|
||||
(-81, ('[NPC][Abyssal][Electrical T4/T5]Triglavian', 0.183, 0.401, 0.449, 0.37)),
|
||||
(-82, ('[NPC][Abyssal][Electrical T4/T5]Drifter', 0.107, 0.403, 0.403, 0.403)),
|
||||
(-83, ('[NPC][Abyssal][Electrical T4/T5]Sleeper', 0.215, 0.435, 0.435, 0.435)),
|
||||
(-84, ('[NPC][Abyssal][Electrical T4/T5]All', 0.25, 0.477, 0.502, 0.493)),
|
||||
(-85, ('[NPC][Abyssal][Firestorm T1/T2]Drones', 0.461, 0.425, 0.541, 0.443)),
|
||||
(-86, ('[NPC][Abyssal][Firestorm T1/T2]Overmind', 0.65, 0.469, 0.625, 0.633)),
|
||||
(-87, ('[NPC][Abyssal][Firestorm T1/T2]Seeker', 0.084, 0, 0.084, 0.084)),
|
||||
(-88, ('[NPC][Abyssal][Firestorm T1/T2]Triglavian', 0.534, 0.266, 0.484, 0.366)),
|
||||
(-89, ('[NPC][Abyssal][Firestorm T1/T2]Drifter', 0.422, 0.282, 0.422, 0.422)),
|
||||
(-90, ('[NPC][Abyssal][Firestorm T1/T2]Sleeper', 0.512, 0.402, 0.512, 0.512)),
|
||||
(-91, ('[NPC][Abyssal][Firestorm T1/T2]All', 0.541, 0.365, 0.524, 0.504)),
|
||||
(-77, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]All', 0.308, 0.474, 0.495, 0.488)),
|
||||
(-78, ('[NPC][Abyssal][Electrical T4/T5/T6]Drones', 0.193, 0.522, 0.529, 0.435)),
|
||||
(-79, ('[NPC][Abyssal][Electrical T4/T5/T6]Overmind', 0.423, 0.593, 0.624, 0.639)),
|
||||
(-80, ('[NPC][Abyssal][Electrical T4/T5/T6]Seeker', 0, 0.082, 0.082, 0.082)),
|
||||
(-81, ('[NPC][Abyssal][Electrical T4/T5/T6]Triglavian', 0.206, 0.41, 0.464, 0.376)),
|
||||
(-82, ('[NPC][Abyssal][Electrical T4/T5/T6]Drifter', 0.111, 0.415, 0.415, 0.415)),
|
||||
(-83, ('[NPC][Abyssal][Electrical T4/T5/T6]Sleeper', 0.215, 0.435, 0.435, 0.435)),
|
||||
(-84, ('[NPC][Abyssal][Electrical T4/T5/T6]All', 0.247, 0.474, 0.495, 0.488)),
|
||||
(-85, ('[NPC][Abyssal][Firestorm T0/T1/T2]Drones', 0.461, 0.425, 0.541, 0.443)),
|
||||
(-86, ('[NPC][Abyssal][Firestorm T0/T1/T2]Overmind', 0.666, 0.489, 0.634, 0.646)),
|
||||
(-87, ('[NPC][Abyssal][Firestorm T0/T1/T2]Seeker', 0.084, 0, 0.084, 0.084)),
|
||||
(-88, ('[NPC][Abyssal][Firestorm T0/T1/T2]Triglavian', 0.537, 0.269, 0.489, 0.371)),
|
||||
(-89, ('[NPC][Abyssal][Firestorm T0/T1/T2]Drifter', 0.43, 0.289, 0.43, 0.43)),
|
||||
(-90, ('[NPC][Abyssal][Firestorm T0/T1/T2]Sleeper', 0.512, 0.402, 0.512, 0.512)),
|
||||
(-91, ('[NPC][Abyssal][Firestorm T0/T1/T2]All', 0.537, 0.352, 0.512, 0.495)),
|
||||
(-92, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Drones', 0.461, 0.36, 0.541, 0.443)),
|
||||
(-93, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Overmind', 0.65, 0.391, 0.625, 0.633)),
|
||||
(-93, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Overmind', 0.666, 0.413, 0.634, 0.646)),
|
||||
(-94, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Seeker', 0.084, 0, 0.084, 0.084)),
|
||||
(-95, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Triglavian', 0.534, 0.161, 0.484, 0.366)),
|
||||
(-96, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Drifter', 0.422, 0.196, 0.422, 0.422)),
|
||||
(-95, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Triglavian', 0.537, 0.166, 0.489, 0.371)),
|
||||
(-96, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Drifter', 0.43, 0.201, 0.43, 0.43)),
|
||||
(-97, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Sleeper', 0.512, 0.337, 0.512, 0.512)),
|
||||
(-98, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]All', 0.541, 0.284, 0.524, 0.504)),
|
||||
(-99, ('[NPC][Abyssal][Firestorm T4/T5]Drones', 0.461, 0.305, 0.541, 0.443)),
|
||||
(-100, ('[NPC][Abyssal][Firestorm T4/T5]Overmind', 0.65, 0.323, 0.625, 0.633)),
|
||||
(-101, ('[NPC][Abyssal][Firestorm T4/T5]Seeker', 0.084, 0, 0.084, 0.084)),
|
||||
(-102, ('[NPC][Abyssal][Firestorm T4/T5]Triglavian', 0.534, 0.082, 0.484, 0.366)),
|
||||
(-103, ('[NPC][Abyssal][Firestorm T4/T5]Drifter', 0.422, 0.114, 0.422, 0.422)),
|
||||
(-104, ('[NPC][Abyssal][Firestorm T4/T5]Sleeper', 0.512, 0.276, 0.512, 0.512)),
|
||||
(-105, ('[NPC][Abyssal][Firestorm T4/T5]All', 0.541, 0.214, 0.524, 0.504)),
|
||||
(-106, ('[NPC][Abyssal][Exotic T1/T2]Drones', 0.439, 0.522, 0.417, 0.435)),
|
||||
(-107, ('[NPC][Abyssal][Exotic T1/T2]Overmind', 0.626, 0.576, 0.496, 0.624)),
|
||||
(-108, ('[NPC][Abyssal][Exotic T1/T2]Seeker', 0.082, 0.082, 0, 0.082)),
|
||||
(-109, ('[NPC][Abyssal][Exotic T1/T2]Triglavian', 0.477, 0.401, 0.284, 0.37)),
|
||||
(-110, ('[NPC][Abyssal][Exotic T1/T2]Drifter', 0.403, 0.403, 0.267, 0.403)),
|
||||
(-111, ('[NPC][Abyssal][Exotic T1/T2]Sleeper', 0.435, 0.435, 0.329, 0.435)),
|
||||
(-112, ('[NPC][Abyssal][Exotic T1/T2]All', 0.507, 0.477, 0.373, 0.493)),
|
||||
(-98, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]All', 0.537, 0.269, 0.512, 0.495)),
|
||||
(-99, ('[NPC][Abyssal][Firestorm T4/T5/T6]Drones', 0.461, 0.305, 0.541, 0.443)),
|
||||
(-100, ('[NPC][Abyssal][Firestorm T4/T5/T6]Overmind', 0.666, 0.345, 0.634, 0.646)),
|
||||
(-101, ('[NPC][Abyssal][Firestorm T4/T5/T6]Seeker', 0.084, 0, 0.084, 0.084)),
|
||||
(-102, ('[NPC][Abyssal][Firestorm T4/T5/T6]Triglavian', 0.537, 0.085, 0.489, 0.371)),
|
||||
(-103, ('[NPC][Abyssal][Firestorm T4/T5/T6]Drifter', 0.43, 0.117, 0.43, 0.43)),
|
||||
(-104, ('[NPC][Abyssal][Firestorm T4/T5/T6]Sleeper', 0.512, 0.276, 0.512, 0.512)),
|
||||
(-105, ('[NPC][Abyssal][Firestorm T4/T5/T6]All', 0.537, 0.201, 0.512, 0.495)),
|
||||
(-106, ('[NPC][Abyssal][Exotic T0/T1/T2]Drones', 0.439, 0.522, 0.417, 0.435)),
|
||||
(-107, ('[NPC][Abyssal][Exotic T0/T1/T2]Overmind', 0.643, 0.593, 0.511, 0.639)),
|
||||
(-108, ('[NPC][Abyssal][Exotic T0/T1/T2]Seeker', 0.082, 0.082, 0, 0.082)),
|
||||
(-109, ('[NPC][Abyssal][Exotic T0/T1/T2]Triglavian', 0.494, 0.41, 0.304, 0.376)),
|
||||
(-110, ('[NPC][Abyssal][Exotic T0/T1/T2]Drifter', 0.415, 0.415, 0.277, 0.415)),
|
||||
(-111, ('[NPC][Abyssal][Exotic T0/T1/T2]Sleeper', 0.435, 0.435, 0.329, 0.435)),
|
||||
(-112, ('[NPC][Abyssal][Exotic T0/T1/T2]All', 0.508, 0.474, 0.359, 0.488)),
|
||||
(-113, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Drones', 0.439, 0.522, 0.351, 0.435)),
|
||||
(-114, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Overmind', 0.626, 0.576, 0.419, 0.624)),
|
||||
(-114, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Overmind', 0.643, 0.593, 0.435, 0.639)),
|
||||
(-115, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Seeker', 0.082, 0.082, 0, 0.082)),
|
||||
(-116, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Triglavian', 0.477, 0.401, 0.176, 0.37)),
|
||||
(-117, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Drifter', 0.403, 0.403, 0.184, 0.403)),
|
||||
(-116, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Triglavian', 0.494, 0.41, 0.198, 0.376)),
|
||||
(-117, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Drifter', 0.415, 0.415, 0.191, 0.415)),
|
||||
(-118, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Sleeper', 0.435, 0.435, 0.268, 0.435)),
|
||||
(-119, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]All', 0.507, 0.477, 0.293, 0.493)),
|
||||
(-120, ('[NPC][Abyssal][Exotic T4/T5]Drones', 0.439, 0.522, 0.293, 0.435)),
|
||||
(-121, ('[NPC][Abyssal][Exotic T4/T5]Overmind', 0.626, 0.576, 0.344, 0.624)),
|
||||
(-122, ('[NPC][Abyssal][Exotic T4/T5]Seeker', 0.082, 0.082, 0, 0.082)),
|
||||
(-123, ('[NPC][Abyssal][Exotic T4/T5]Triglavian', 0.477, 0.401, 0.107, 0.37)),
|
||||
(-124, ('[NPC][Abyssal][Exotic T4/T5]Drifter', 0.403, 0.403, 0.107, 0.403)),
|
||||
(-125, ('[NPC][Abyssal][Exotic T4/T5]Sleeper', 0.435, 0.435, 0.215, 0.435)),
|
||||
(-126, ('[NPC][Abyssal][Exotic T4/T5]All', 0.507, 0.477, 0.223, 0.493)),
|
||||
(-127, ('[NPC][Abyssal][Gamma T1/T2]Drones', 0.449, 0.54, 0.549, 0.336)),
|
||||
(-128, ('[NPC][Abyssal][Gamma T1/T2]Overmind', 0.6, 0.557, 0.601, 0.504)),
|
||||
(-129, ('[NPC][Abyssal][Gamma T1/T2]Seeker', 0.085, 0.085, 0.085, 0)),
|
||||
(-130, ('[NPC][Abyssal][Gamma T1/T2]Triglavian', 0.463, 0.392, 0.447, 0.193)),
|
||||
(-131, ('[NPC][Abyssal][Gamma T1/T2]Drifter', 0.428, 0.428, 0.428, 0.287)),
|
||||
(-132, ('[NPC][Abyssal][Gamma T1/T2]Sleeper', 0.435, 0.435, 0.435, 0.329)),
|
||||
(-133, ('[NPC][Abyssal][Gamma T1/T2]All', 0.493, 0.472, 0.5, 0.362)),
|
||||
(-119, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]All', 0.508, 0.474, 0.276, 0.488)),
|
||||
(-120, ('[NPC][Abyssal][Exotic T4/T5/T6]Drones', 0.439, 0.522, 0.293, 0.435)),
|
||||
(-121, ('[NPC][Abyssal][Exotic T4/T5/T6]Overmind', 0.643, 0.593, 0.362, 0.639)),
|
||||
(-122, ('[NPC][Abyssal][Exotic T4/T5/T6]Seeker', 0.082, 0.082, 0, 0.082)),
|
||||
(-123, ('[NPC][Abyssal][Exotic T4/T5/T6]Triglavian', 0.494, 0.41, 0.122, 0.376)),
|
||||
(-124, ('[NPC][Abyssal][Exotic T4/T5/T6]Drifter', 0.415, 0.415, 0.111, 0.415)),
|
||||
(-125, ('[NPC][Abyssal][Exotic T4/T5/T6]Sleeper', 0.435, 0.435, 0.215, 0.435)),
|
||||
(-126, ('[NPC][Abyssal][Exotic T4/T5/T6]All', 0.508, 0.474, 0.208, 0.488)),
|
||||
(-127, ('[NPC][Abyssal][Gamma T0/T1/T2]Drones', 0.449, 0.54, 0.549, 0.336)),
|
||||
(-128, ('[NPC][Abyssal][Gamma T0/T1/T2]Overmind', 0.619, 0.574, 0.612, 0.522)),
|
||||
(-129, ('[NPC][Abyssal][Gamma T0/T1/T2]Seeker', 0.085, 0.085, 0.085, 0)),
|
||||
(-130, ('[NPC][Abyssal][Gamma T0/T1/T2]Triglavian', 0.477, 0.4, 0.461, 0.202)),
|
||||
(-131, ('[NPC][Abyssal][Gamma T0/T1/T2]Drifter', 0.437, 0.437, 0.437, 0.295)),
|
||||
(-132, ('[NPC][Abyssal][Gamma T0/T1/T2]Sleeper', 0.435, 0.435, 0.435, 0.329)),
|
||||
(-133, ('[NPC][Abyssal][Gamma T0/T1/T2]All', 0.493, 0.468, 0.492, 0.35)),
|
||||
(-134, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Drones', 0.449, 0.54, 0.549, 0.264)),
|
||||
(-135, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Overmind', 0.6, 0.557, 0.601, 0.428)),
|
||||
(-135, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Overmind', 0.619, 0.574, 0.612, 0.449)),
|
||||
(-136, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Seeker', 0.085, 0.085, 0.085, 0)),
|
||||
(-137, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Triglavian', 0.463, 0.392, 0.447, 0.071)),
|
||||
(-138, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Drifter', 0.428, 0.428, 0.428, 0.2)),
|
||||
(-137, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Triglavian', 0.477, 0.4, 0.461, 0.081)),
|
||||
(-138, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Drifter', 0.437, 0.437, 0.437, 0.206)),
|
||||
(-139, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Sleeper', 0.435, 0.435, 0.435, 0.268)),
|
||||
(-140, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]All', 0.493, 0.472, 0.5, 0.28)),
|
||||
(-141, ('[NPC][Abyssal][Gamma T4/T5]Drones', 0.449, 0.54, 0.549, 0.197)),
|
||||
(-142, ('[NPC][Abyssal][Gamma T4/T5]Overmind', 0.6, 0.557, 0.601, 0.356)),
|
||||
(-143, ('[NPC][Abyssal][Gamma T4/T5]Seeker', 0.085, 0.085, 0.085, 0)),
|
||||
(-144, ('[NPC][Abyssal][Gamma T4/T5]Triglavian', 0.463, 0.392, 0.447, 0.029)),
|
||||
(-145, ('[NPC][Abyssal][Gamma T4/T5]Drifter', 0.428, 0.428, 0.428, 0.117)),
|
||||
(-146, ('[NPC][Abyssal][Gamma T4/T5]Sleeper', 0.435, 0.435, 0.435, 0.215)),
|
||||
(-147, ('[NPC][Abyssal][Gamma T4/T5]All', 0.493, 0.472, 0.5, 0.21))])
|
||||
(-140, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]All', 0.493, 0.468, 0.492, 0.264)),
|
||||
(-141, ('[NPC][Abyssal][Gamma T4/T5/T6]Drones', 0.449, 0.54, 0.549, 0.197)),
|
||||
(-142, ('[NPC][Abyssal][Gamma T4/T5/T6]Overmind', 0.619, 0.574, 0.612, 0.379)),
|
||||
(-143, ('[NPC][Abyssal][Gamma T4/T5/T6]Seeker', 0.085, 0.085, 0.085, 0)),
|
||||
(-144, ('[NPC][Abyssal][Gamma T4/T5/T6]Triglavian', 0.477, 0.4, 0.461, 0.034)),
|
||||
(-145, ('[NPC][Abyssal][Gamma T4/T5/T6]Drifter', 0.437, 0.437, 0.437, 0.121)),
|
||||
(-146, ('[NPC][Abyssal][Gamma T4/T5/T6]Sleeper', 0.435, 0.435, 0.435, 0.215)),
|
||||
(-147, ('[NPC][Abyssal][Gamma T4/T5/T6]All', 0.493, 0.468, 0.492, 0.196)),
|
||||
# Source: ticket #2265
|
||||
(-148, ('[NPC][Abyssal][Dark Matter All Tiers]Concord', 0.324, 0.318, 0.369, 0.372)),
|
||||
(-149, ('[NPC][Abyssal][Dark Matter All Tiers]Sansha', 0.137, 0.331, 0.332, 0.322)),
|
||||
(-150, ('[NPC][Abyssal][Dark Matter All Tiers]Angel', 0.582, 0.508, 0.457, 0.416)),
|
||||
(-151, ('[NPC][Abyssal][Electrical T0/T1/T2]Concord', 0.121, 0.318, 0.369, 0.372)),
|
||||
(-152, ('[NPC][Abyssal][Electrical T0/T1/T2]Sansha', 0.034, 0.331, 0.332, 0.322)),
|
||||
(-153, ('[NPC][Abyssal][Electrical T0/T1/T2]Angel', 0.456, 0.508, 0.457, 0.416)),
|
||||
(-154, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Concord', 0.025, 0.318, 0.369, 0.372)),
|
||||
(-155, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Sansha', 0.018, 0.331, 0.332, 0.322)),
|
||||
(-156, ('[NPC][Abyssal][Electrical T3 (Some T5 Rooms)]Angel', 0.373, 0.508, 0.457, 0.416)),
|
||||
(-157, ('[NPC][Abyssal][Electrical T4/T5/T6]Concord', 0.008, 0.318, 0.369, 0.372)),
|
||||
(-158, ('[NPC][Abyssal][Electrical T4/T5/T6]Sansha', 0.009, 0.331, 0.332, 0.322)),
|
||||
(-159, ('[NPC][Abyssal][Electrical T4/T5/T6]Angel', 0.3, 0.508, 0.457, 0.416)),
|
||||
(-160, ('[NPC][Abyssal][Firestorm T0/T1/T2]Concord', 0.324, 0.107, 0.369, 0.372)),
|
||||
(-161, ('[NPC][Abyssal][Firestorm T0/T1/T2]Sansha', 0.148, 0.181, 0.329, 0.328)),
|
||||
(-162, ('[NPC][Abyssal][Firestorm T0/T1/T2]Angel', 0.587, 0.342, 0.439, 0.39)),
|
||||
(-163, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Concord', 0.324, 0.016, 0.369, 0.372)),
|
||||
(-164, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Sansha', 0.148, 0.14, 0.329, 0.328)),
|
||||
(-165, ('[NPC][Abyssal][Firestorm T3 (Some T5 Rooms)]Angel', 0.587, 0.241, 0.439, 0.39)),
|
||||
(-166, ('[NPC][Abyssal][Firestorm T4/T5/T6]Concord', 0.324, 0.004, 0.369, 0.372)),
|
||||
(-167, ('[NPC][Abyssal][Firestorm T4/T5/T6]Sansha', 0.148, 0.106, 0.329, 0.328)),
|
||||
(-168, ('[NPC][Abyssal][Firestorm T4/T5/T6]Angel', 0.587, 0.172, 0.439, 0.39)),
|
||||
(-169, ('[NPC][Abyssal][Exotic T0/T1/T2]Concord', 0.324, 0.318, 0.18, 0.372)),
|
||||
(-170, ('[NPC][Abyssal][Exotic T0/T1/T2]Sansha', 0.137, 0.331, 0.166, 0.322)),
|
||||
(-171, ('[NPC][Abyssal][Exotic T0/T1/T2]Angel', 0.582, 0.508, 0.295, 0.416)),
|
||||
(-172, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Concord', 0.324, 0.318, 0.089, 0.372)),
|
||||
(-173, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Sansha', 0.137, 0.331, 0.108, 0.322)),
|
||||
(-174, ('[NPC][Abyssal][Exotic T3 (Some T5 Rooms)]Angel', 0.582, 0.508, 0.203, 0.416)),
|
||||
(-175, ('[NPC][Abyssal][Exotic T4/T5/T6]Concord', 0.324, 0.318, 0.068, 0.372)),
|
||||
(-176, ('[NPC][Abyssal][Exotic T4/T5/T6]Sansha', 0.137, 0.331, 0.073, 0.322)),
|
||||
(-177, ('[NPC][Abyssal][Exotic T4/T5/T6]Angel', 0.582, 0.508, 0.14, 0.416)),
|
||||
(-178, ('[NPC][Abyssal][Gamma T0/T1/T2]Concord', 0.324, 0.318, 0.369, 0.203)),
|
||||
(-179, ('[NPC][Abyssal][Gamma T0/T1/T2]Sansha', 0.137, 0.355, 0.352, 0.16)),
|
||||
(-180, ('[NPC][Abyssal][Gamma T0/T1/T2]Angel', 0.59, 0.528, 0.477, 0.286)),
|
||||
(-181, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Concord', 0.324, 0.318, 0.369, 0.112)),
|
||||
(-182, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Sansha', 0.137, 0.355, 0.352, 0.05)),
|
||||
(-183, ('[NPC][Abyssal][Gamma T3 (Some T5 Rooms)]Angel', 0.59, 0.528, 0.477, 0.197)),
|
||||
(-184, ('[NPC][Abyssal][Gamma T4/T5/T6]Concord', 0.324, 0.318, 0.369, 0.086)),
|
||||
(-185, ('[NPC][Abyssal][Gamma T4/T5/T6]Sansha', 0.137, 0.355, 0.352, 0)),
|
||||
(-186, ('[NPC][Abyssal][Gamma T4/T5/T6]Angel', 0.59, 0.528, 0.477, 0.126))])
|
||||
|
||||
|
||||
class TargetProfile:
|
||||
|
||||
@@ -37,7 +37,14 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
|
||||
for mod in src.item.activeModulesIter():
|
||||
if not mod.isDealingDamage():
|
||||
continue
|
||||
if mod.hardpoint == FittingHardpoint.TURRET:
|
||||
if "ChainLightning" in mod.item.effects:
|
||||
if inLockRange:
|
||||
applicationMap[mod] = getVortonMult(
|
||||
mod=mod,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
elif mod.hardpoint == FittingHardpoint.TURRET:
|
||||
if inLockRange:
|
||||
applicationMap[mod] = getTurretMult(
|
||||
mod=mod,
|
||||
@@ -56,7 +63,6 @@ def getApplicationPerKey(src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAn
|
||||
if inLockRange or (mod.charge is not None and 'fofMissileLaunching' in mod.charge.effects):
|
||||
applicationMap[mod] = getLauncherMult(
|
||||
mod=mod,
|
||||
src=src,
|
||||
distance=distance,
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
@@ -151,7 +157,21 @@ def getTurretMult(mod, src, tgt, atkSpeed, atkAngle, distance, tgtSpeed, tgtAngl
|
||||
return mult
|
||||
|
||||
|
||||
def getLauncherMult(mod, src, distance, tgtSpeed, tgtSigRadius):
|
||||
def getVortonMult(mod, distance, tgtSpeed, tgtSigRadius):
|
||||
rangeFactor = calculateRangeFactor(
|
||||
mod.getModifiedItemAttr('maxRange'),
|
||||
0,
|
||||
distance)
|
||||
applicationFactor = _calcMissileFactor(
|
||||
atkEr=mod.getModifiedItemAttr('aoeCloudSize'),
|
||||
atkEv=mod.getModifiedItemAttr('aoeVelocity'),
|
||||
atkDrf=mod.getModifiedItemAttr('aoeDamageReductionFactor'),
|
||||
tgtSpeed=tgtSpeed,
|
||||
tgtSigRadius=tgtSigRadius)
|
||||
return rangeFactor * applicationFactor
|
||||
|
||||
|
||||
def getLauncherMult(mod, distance, tgtSpeed, tgtSigRadius):
|
||||
missileMaxRangeData = mod.missileMaxRangeData
|
||||
if missileMaxRangeData is None:
|
||||
return 0
|
||||
|
||||
@@ -235,7 +235,7 @@ class GraphControlPanel(wx.Panel):
|
||||
fieldTextBox = FloatBox(self, self._storedConsts.get((inputDef.handle, inputDef.unit), inputDef.defaultValue))
|
||||
fieldTextBox.Bind(wx.EVT_TEXT, self.OnNonMainInputChanged)
|
||||
fieldTextBox.SetToolTip(wx.ToolTip(tooltipText))
|
||||
fieldSizer.Add(fieldTextBox, 0, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
|
||||
fieldSizer.Add(fieldTextBox, 0, wx.EXPAND | wx.RIGHT, 5)
|
||||
fieldIcon = None
|
||||
if inputDef.iconID is not None:
|
||||
icon = BitmapLoader.getBitmap(inputDef.iconID, 'icons')
|
||||
|
||||
@@ -27,7 +27,7 @@ import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from graphs.data.base import FitGraph
|
||||
from graphs.events import RESIST_MODE_CHANGED
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from service.const import GraphCacheCleanupReason
|
||||
from service.settings import GraphSettings
|
||||
|
||||
@@ -36,9 +36,9 @@ class VectorPicker(wx.Window):
|
||||
self._labelpos = int(kwargs.pop('labelpos', 0))
|
||||
self._offset = float(kwargs.pop('offset', 0))
|
||||
self._size = max(0, float(kwargs.pop('size', 50)))
|
||||
self._fontsize = max(1, float(kwargs.pop('fontsize', 8)))
|
||||
self._directionOnly = kwargs.pop('directionOnly', False)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._fontsize = max(1, float(kwargs.pop('fontsize', 8 / self.GetContentScaleFactor())))
|
||||
self._font = wx.Font(self._fontsize, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
|
||||
self._angle = 0
|
||||
self.__length = 1
|
||||
@@ -107,8 +107,11 @@ class VectorPicker(wx.Window):
|
||||
dc = wx.BufferedPaintDC(self)
|
||||
self.Draw(dc)
|
||||
|
||||
def GetScaledClientSize(self):
|
||||
return tuple([dim / self.GetContentScaleFactor() for dim in self.GetClientSize()])
|
||||
|
||||
def Draw(self, dc):
|
||||
width, height = self.GetClientSize()
|
||||
width, height = self.GetScaledClientSize()
|
||||
if not width or not height:
|
||||
return
|
||||
dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID))
|
||||
@@ -122,9 +125,11 @@ class VectorPicker(wx.Window):
|
||||
a = math.radians(self._angle + self._offset)
|
||||
x = math.cos(a) * radius
|
||||
y = math.sin(a) * radius
|
||||
# See PR #2260 on why this is needed
|
||||
pointRadius = 2 / self.GetContentScaleFactor() if 'wxGTK' in wx.PlatformInfo else 2
|
||||
dc.DrawLine(radius + 2, radius + 2, radius + 2 + x * self._length, radius + 2 - y * self._length)
|
||||
dc.SetBrush(wx.BLACK_BRUSH)
|
||||
dc.DrawCircle(radius + 2 + x * self._length, radius + 2 - y * self._length, 2)
|
||||
dc.DrawCircle(radius + 2 + x * self._length, radius + 2 - y * self._length, pointRadius)
|
||||
|
||||
if self._label:
|
||||
labelText = self._label
|
||||
|
||||
@@ -22,12 +22,14 @@
|
||||
import wx
|
||||
|
||||
|
||||
class AuxiliaryFrame(wx.Frame):
|
||||
class AuxiliaryMixin:
|
||||
|
||||
_instance = None
|
||||
|
||||
def __init__(self, parent, id=None, title=None, pos=None, size=None, style=None, name=None, resizeable=False):
|
||||
baseStyle = wx.FRAME_NO_TASKBAR | wx.FRAME_FLOAT_ON_PARENT | wx.CAPTION | wx.CLOSE_BOX | wx.SYSTEM_MENU
|
||||
baseStyle = wx.FRAME_NO_TASKBAR | wx.CAPTION | wx.CLOSE_BOX | wx.SYSTEM_MENU
|
||||
if parent is not None:
|
||||
baseStyle = baseStyle | wx.FRAME_FLOAT_ON_PARENT
|
||||
if resizeable:
|
||||
baseStyle = baseStyle | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX
|
||||
kwargs = {
|
||||
@@ -53,14 +55,26 @@ class AuxiliaryFrame(wx.Frame):
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
|
||||
@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 not cls._instance:
|
||||
if not cls._instance or forceReopen:
|
||||
if cls._instance:
|
||||
cls._instance.Close()
|
||||
frame = cls(parent, *args, **kwargs)
|
||||
cls._instance = frame
|
||||
frame.Show()
|
||||
else:
|
||||
cls._instance.Raise()
|
||||
return cls._instance
|
||||
|
||||
|
||||
def OnSuppressedAction(self, event):
|
||||
return
|
||||
|
||||
|
||||
class AuxiliaryFrame(AuxiliaryMixin, wx.Frame):
|
||||
pass
|
||||
|
||||
|
||||
class AuxiliaryDialog(AuxiliaryMixin, wx.Dialog):
|
||||
pass
|
||||
@@ -24,6 +24,7 @@ import gui.display as d
|
||||
import gui.fitCommands as cmd
|
||||
import gui.globalEvents as GE
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.builtinMarketBrowser.events import ITEM_SELECTED, ItemSelected
|
||||
from gui.utils.staticHelpers import DragDropHelper
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
@@ -58,6 +59,7 @@ class CargoView(d.Display):
|
||||
self.lastFitId = None
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
|
||||
@@ -66,6 +68,31 @@ class CargoView(d.Display):
|
||||
|
||||
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
|
||||
|
||||
def addItem(self, event):
|
||||
item = Market.getInstance().getItem(event.itemID, eager='group')
|
||||
if item is None or not item.isCharge:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
|
||||
if not fit:
|
||||
event.Skip()
|
||||
return
|
||||
modifiers = wx.GetMouseState().GetModifiers()
|
||||
amount = 1
|
||||
if modifiers == wx.MOD_CONTROL:
|
||||
amount = 10
|
||||
elif modifiers == wx.MOD_ALT:
|
||||
amount = 100
|
||||
elif modifiers == wx.MOD_CONTROL | wx.MOD_ALT:
|
||||
amount = 1000
|
||||
self.mainFrame.command.Submit(cmd.GuiAddCargoCommand(
|
||||
fitID=fitID, itemID=item.ID, amount=amount))
|
||||
self.mainFrame.additionsPane.select('Cargo')
|
||||
event.Skip()
|
||||
|
||||
def handleListDrag(self, x, y, data):
|
||||
"""
|
||||
Handles dragging of items from various pyfa displays which support it
|
||||
|
||||
@@ -36,6 +36,11 @@ import gui.fitCommands as cmd
|
||||
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):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(DroneViewDrop, self).__init__(*args, **kwargs)
|
||||
@@ -186,17 +191,13 @@ class DroneView(Display):
|
||||
self.mainFrame.command.Submit(cmd.GuiMergeLocalDroneStacksCommand(
|
||||
fitID=fitID, srcPosition=srcPosition, dstPosition=dstPosition))
|
||||
|
||||
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')
|
||||
|
||||
def droneKey(self, drone):
|
||||
@staticmethod
|
||||
def droneKey(drone):
|
||||
sMkt = Market.getInstance()
|
||||
|
||||
groupName = sMkt.getMarketGroupByItem(drone.item).name
|
||||
|
||||
return (self.DRONE_ORDER.index(groupName),
|
||||
drone.item.name)
|
||||
return (DRONE_ORDER.index(groupName), drone.item.name)
|
||||
|
||||
def fitChanged(self, event):
|
||||
event.Skip()
|
||||
|
||||
@@ -34,6 +34,9 @@ from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
||||
|
||||
|
||||
class FighterViewDrop(wx.DropTarget):
|
||||
def __init__(self, dropFn, *args, **kwargs):
|
||||
super(FighterViewDrop, self).__init__(*args, **kwargs)
|
||||
@@ -250,11 +253,10 @@ class FighterDisplay(d.Display):
|
||||
def _merge(src, dst):
|
||||
return
|
||||
|
||||
FIGHTER_ORDER = ('Light Fighter', 'Heavy Fighter', 'Support Fighter')
|
||||
|
||||
def fighterKey(self, fighter):
|
||||
@staticmethod
|
||||
def fighterKey(fighter):
|
||||
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
|
||||
if groupName == 'Support Fighter':
|
||||
abilityEffectIDs = ()
|
||||
|
||||
@@ -90,8 +90,6 @@ class ProjectedView(d.Display):
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.onLeftDoubleClick)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
|
||||
self.droneView = gui.builtinAdditionPanes.droneView.DroneView
|
||||
|
||||
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
|
||||
|
||||
self.SetDropTarget(ProjectedViewDrop(self.handleListDrag))
|
||||
@@ -119,12 +117,12 @@ class ProjectedView(d.Display):
|
||||
fitID=fitID, itemID=fit.modules[int(data[1])].itemID))
|
||||
elif data[0] == 'market':
|
||||
itemID = int(data[1])
|
||||
category = Market.getInstance().getItem(itemID, eager=('group.category')).category.name
|
||||
if category == 'Module':
|
||||
item = Market.getInstance().getItem(itemID)
|
||||
if item.isModule:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedModuleCommand(fitID=fitID, itemID=itemID))
|
||||
elif category == 'Drone':
|
||||
elif item.isDrone:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedDroneCommand(fitID=fitID, itemID=itemID))
|
||||
elif category == 'Fighter':
|
||||
elif item.isFighter:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddProjectedFighterCommand(fitID=fitID, itemID=itemID))
|
||||
|
||||
def kbEvent(self, event):
|
||||
@@ -162,7 +160,7 @@ class ProjectedView(d.Display):
|
||||
if item.marketGroup is None:
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -6,6 +6,7 @@ from gui.builtinContextMenus import fitAddCurrentlyOpen
|
||||
from gui.builtinContextMenus import envEffectAdd
|
||||
from gui.builtinContextMenus import commandFitAdd
|
||||
from gui.builtinContextMenus.targetProfile import adder
|
||||
from gui.builtinContextMenus import graphFitAmmoPicker
|
||||
# Often-used item manipulations
|
||||
from gui.builtinContextMenus import shipModeChange
|
||||
from gui.builtinContextMenus import moduleAmmoChange
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from itertools import chain
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
@@ -10,6 +11,28 @@ from gui.contextMenu import ContextMenuUnconditional
|
||||
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):
|
||||
|
||||
# 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):
|
||||
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):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
|
||||
# Wormholes
|
||||
|
||||
self.idmap = {}
|
||||
sub = wx.Menu()
|
||||
data = self.getData()
|
||||
msw = "wxMSW" in wx.PlatformInfo
|
||||
|
||||
wormhole_item = wx.MenuItem(sub, wx.ID_ANY, "Wormhole")
|
||||
wormhole_menu = wx.Menu()
|
||||
wormhole_item.SetSubMenu(wormhole_menu)
|
||||
sub.Append(wormhole_item)
|
||||
|
||||
grouped_data, flat_data = self.getEffectBeacons()
|
||||
self.buildMenu(grouped_data, flat_data, wormhole_menu, rootMenu, msw)
|
||||
|
||||
# Incursions
|
||||
|
||||
grouped_data, flat_data = self.getEffectBeacons(incursions=True)
|
||||
self.buildMenu(grouped_data, flat_data, sub, rootMenu, msw)
|
||||
|
||||
# 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)
|
||||
def makeMenu(data, parentMenu):
|
||||
menu = wx.Menu()
|
||||
for group_name in data.groups:
|
||||
menuItem = self._addGroup(rootMenu if msw else parentMenu, group_name)
|
||||
subMenu = makeMenu(data.groups[group_name], menu)
|
||||
menuItem.SetSubMenu(subMenu)
|
||||
menu.Append(menuItem)
|
||||
for entry in data.items:
|
||||
menuItem = self._addEffect(rootMenu if msw else parentMenu, entry.itemID, entry.shortName)
|
||||
menu.Append(menuItem)
|
||||
menu.Bind(wx.EVT_MENU, self.handleSelection)
|
||||
return menu
|
||||
|
||||
sub = makeMenu(data, rootMenu)
|
||||
return sub
|
||||
|
||||
def handleSelection(self, event):
|
||||
# Skip events ids that aren't mapped
|
||||
|
||||
swObj, swName = self.idmap.get(event.Id, (False, False))
|
||||
if not swObj and not swName:
|
||||
swObj = self.idmap.get(event.Id, False)
|
||||
if not swObj:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
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):
|
||||
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):
|
||||
def getEffectBeacons(self, *groups, extra_garbage=()):
|
||||
"""
|
||||
Get dictionary with wormhole system-wide effects
|
||||
Get dictionary with system-wide effects
|
||||
"""
|
||||
compacted = len(groups) <= 1
|
||||
sMkt = Market.getInstance()
|
||||
|
||||
# todo: rework this
|
||||
# Container for system-wide effects
|
||||
grouped = {}
|
||||
|
||||
# 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")
|
||||
data = Group()
|
||||
|
||||
# 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
|
||||
grp = sMkt.getGroup("Effect Beacon")
|
||||
@@ -140,7 +134,7 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||
# Cycle through them
|
||||
for beacon in sMkt.getItemsByGroup(grp):
|
||||
# Check if it belongs to any valid group
|
||||
for group in validgroups:
|
||||
for group in groups:
|
||||
# Check beginning of the name only
|
||||
if re.search(group, beacon.name):
|
||||
# Get full beacon name
|
||||
@@ -159,64 +153,65 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
|
||||
groupname = re.sub(garbage, "", groupname)
|
||||
groupname = re.sub(" {2,}", " ", groupname).strip()
|
||||
# Add stuff to dictionary
|
||||
if groupname not in grouped:
|
||||
grouped[groupname] = set()
|
||||
grouped[groupname].add((beacon, beaconname, shortname))
|
||||
if compacted:
|
||||
container = data.items
|
||||
else:
|
||||
container = data.groups.setdefault(groupname, Group()).items
|
||||
container.append(Entry(beacon.ID, beaconname, shortname))
|
||||
# Break loop on 1st result
|
||||
break
|
||||
|
||||
return grouped, ()
|
||||
data.sort()
|
||||
return data
|
||||
|
||||
def getAbyssalWeather(self):
|
||||
sMkt = Market.getInstance()
|
||||
data = Group()
|
||||
|
||||
environments = {x.ID: x for x in sMkt.getGroup("Abyssal Environment").items}
|
||||
items = chain(sMkt.getGroup("MassiveEnvironments").items, sMkt.getGroup("Non-Interactable Object").items)
|
||||
|
||||
grouped = {}
|
||||
flat = set()
|
||||
|
||||
items = chain(
|
||||
sMkt.getGroup("MassiveEnvironments").items,
|
||||
sMkt.getGroup("Non-Interactable Object").items)
|
||||
for beacon in items:
|
||||
if not beacon.isType('projected'):
|
||||
continue
|
||||
|
||||
type = self.__class__.abyssal_mapping.get(beacon.name[0:-2], None)
|
||||
type = environments.get(type, None)
|
||||
if type is None:
|
||||
continue
|
||||
|
||||
if type.name not in grouped:
|
||||
grouped[type.name] = set()
|
||||
|
||||
subdata = data.groups.setdefault(type.name, Group())
|
||||
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
|
||||
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()
|
||||
|
||||
grp = sMkt.getGroup("Abyssal Hazards")
|
||||
|
||||
grouped = dict()
|
||||
|
||||
for beacon in grp.items:
|
||||
if not beacon.isType('projected'):
|
||||
for item in sMkt.getItemsByGroup(sMkt.getGroup('Destructible Effect Beacon')):
|
||||
if not item.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()
|
||||
if key not in grouped:
|
||||
grouped[key] = set()
|
||||
|
||||
grouped[key].add((beacon, beacon.name, beacon.name))
|
||||
|
||||
return grouped, ()
|
||||
|
||||
data.items.append(Entry(item.ID, item.name, item.name))
|
||||
data.sort()
|
||||
return data
|
||||
|
||||
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.mainFrame
|
||||
from eos.const import FittingHardpoint
|
||||
from eos.saveddata.module import Module
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.contextMenu import ContextMenuCombined
|
||||
from gui.fitCommands.helpers import getSimilarModPositions
|
||||
from service.ammo import Ammo
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
|
||||
class ChangeModuleAmmo(ContextMenuCombined):
|
||||
|
||||
DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal")
|
||||
MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed")
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
# Format: {type ID: set(loadable, charges)}
|
||||
self.loadableCharges = {}
|
||||
self.loadableChargesCache = {}
|
||||
|
||||
def display(self, callingWindow, srcContext, mainItem, selection):
|
||||
if srcContext not in ("fittingModule", "projectedModule"):
|
||||
if srcContext not in ('fittingModule', 'projectedModule'):
|
||||
return False
|
||||
|
||||
if self.mainFrame.getActiveFit() is None:
|
||||
return False
|
||||
|
||||
self.mainCharges = self.getChargesForMod(mainItem)
|
||||
self.mainCharges = self._getAmmo(mainItem)
|
||||
if not self.mainCharges:
|
||||
return False
|
||||
|
||||
@@ -39,186 +34,81 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
||||
return True
|
||||
|
||||
def getText(self, callingWindow, itmContext, mainItem, selection):
|
||||
return "Charge"
|
||||
return 'Charge'
|
||||
|
||||
def getChargesForMod(self, mod):
|
||||
sMkt = Market.getInstance()
|
||||
if mod is None or mod.isEmpty:
|
||||
def _getAmmo(self, mod):
|
||||
if mod.itemID is None:
|
||||
return set()
|
||||
typeID = mod.item.ID
|
||||
if typeID in self.loadableCharges:
|
||||
return self.loadableCharges[typeID]
|
||||
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
|
||||
if mod.itemID not in self.loadableChargesCache:
|
||||
self.loadableChargesCache[mod.itemID] = Ammo.getInstance().getModuleFlatAmmo(mod)
|
||||
return self.loadableChargesCache[mod.itemID]
|
||||
|
||||
def turretSorter(self, 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):
|
||||
def _addCharge(self, menu, charge):
|
||||
id_ = ContextMenuCombined.nextID()
|
||||
name = charge.name if charge is not None else "Empty"
|
||||
self.chargeIds[id_] = charge
|
||||
name = charge.name if charge is not None else 'Empty'
|
||||
self.chargeEventMap[id_] = charge
|
||||
item = wx.MenuItem(menu, id_, name)
|
||||
menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item)
|
||||
item.charge = charge
|
||||
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:
|
||||
item.SetBitmap(bitmap)
|
||||
|
||||
return item
|
||||
|
||||
@staticmethod
|
||||
def addSeperator(m, text):
|
||||
def _addSeparator(m, text):
|
||||
id_ = ContextMenuCombined.nextID()
|
||||
m.Append(id_, '─ %s ─' % text)
|
||||
m.Enable(id_, False)
|
||||
|
||||
def getSubMenu(self, callingWindow, context, mainItem, selection, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
m = wx.Menu()
|
||||
self.chargeIds = {}
|
||||
hardpoint = self.module.hardpoint
|
||||
moduleName = self.module.item.name
|
||||
# Make sure we do not consider mining turrets as combat turrets
|
||||
if hardpoint == FittingHardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount", None) is None:
|
||||
self.addSeperator(m, "Long Range")
|
||||
items = []
|
||||
range_ = None
|
||||
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)
|
||||
msw = True if 'wxMSW' in wx.PlatformInfo else False
|
||||
menu = wx.Menu()
|
||||
self.chargeEventMap = {}
|
||||
modType, chargeDict = Ammo.getInstance().getModuleStructuredAmmo(self.module, ammo=self.mainCharges)
|
||||
if modType == 'ddTurret':
|
||||
self._addSeparator(menu, 'Long Range')
|
||||
menuItems = []
|
||||
for charges in chargeDict.values():
|
||||
if len(charges) == 1:
|
||||
menuItems.append(self._addCharge(rootMenu if msw else menu, charges[0]))
|
||||
else:
|
||||
if sub is None and item and base:
|
||||
sub = wx.Menu()
|
||||
sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
self.addSeperator(sub, "Less Damage")
|
||||
item.SetSubMenu(sub)
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, base))
|
||||
|
||||
sub.Append(self.addCharge(rootMenu if msw else sub, charge))
|
||||
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
|
||||
for item in items:
|
||||
m.Append(item)
|
||||
|
||||
self.addSeperator(m, "Short Range")
|
||||
elif hardpoint == FittingHardpoint.MISSILE and moduleName != 'Festival Launcher':
|
||||
type_ = None
|
||||
sub = None
|
||||
defender = None
|
||||
chargesSorted = sorted(self.mainCharges, key=self.missileSorter)
|
||||
for charge in chargesSorted:
|
||||
currType = self.damageInfo(charge)[0]
|
||||
|
||||
if currType != type_ or type_ is None:
|
||||
if sub is not None:
|
||||
self.addSeperator(sub, "More Damage")
|
||||
|
||||
type_ = currType
|
||||
item = wx.MenuItem(m, wx.ID_ANY, type_.capitalize())
|
||||
bitmap = BitmapLoader.getBitmap("%s_small" % type_, "gui")
|
||||
if bitmap is not None:
|
||||
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
|
||||
baseCharge = charges[0]
|
||||
menuItem = self._addCharge(rootMenu if msw else menu, baseCharge)
|
||||
menuItems.append(menuItem)
|
||||
subMenu = wx.Menu()
|
||||
subMenu.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
menuItem.SetSubMenu(subMenu)
|
||||
self._addSeparator(subMenu, 'Less Damage')
|
||||
for charge in charges:
|
||||
subMenu.Append(self._addCharge(rootMenu if msw else subMenu, charge))
|
||||
self._addSeparator(subMenu, 'More Damage')
|
||||
for menuItem in menuItems:
|
||||
menu.Append(menuItem)
|
||||
self._addSeparator(menu, 'Short Range')
|
||||
elif modType == 'ddMissile':
|
||||
menuItems = []
|
||||
for chargeCatName, charges in chargeDict.items():
|
||||
menuItem = wx.MenuItem(menu, wx.ID_ANY, chargeCatName.capitalize())
|
||||
menuItems.append(menuItem)
|
||||
subMenu = wx.Menu()
|
||||
subMenu.Bind(wx.EVT_MENU, self.handleAmmoSwitch)
|
||||
menuItem.SetSubMenu(subMenu)
|
||||
self._addSeparator(subMenu, 'Less Damage')
|
||||
for charge in charges:
|
||||
subMenu.Append(self._addCharge(rootMenu if msw else subMenu, charge))
|
||||
self._addSeparator(subMenu, 'More Damage')
|
||||
for menuItem in menuItems:
|
||||
menu.Append(menuItem)
|
||||
elif modType == 'general':
|
||||
for charge in chargeDict['general']:
|
||||
menu.Append(self._addCharge(rootMenu if msw else menu, charge))
|
||||
menu.Append(self._addCharge(rootMenu if msw else menu, None))
|
||||
return menu
|
||||
|
||||
def handleAmmoSwitch(self, event):
|
||||
charge = self.chargeIds.get(event.Id, False)
|
||||
charge = self.chargeEventMap.get(event.Id, False)
|
||||
if charge is False:
|
||||
event.Skip()
|
||||
return
|
||||
@@ -254,7 +144,7 @@ class ChangeModuleAmmo(ContextMenuCombined):
|
||||
positions = []
|
||||
for position, mod in enumerate(modContainer):
|
||||
if mod in self.selection:
|
||||
modCharges = self.getChargesForMod(mod)
|
||||
modCharges = self._getAmmo(mod)
|
||||
if modCharges.issubset(self.mainCharges):
|
||||
positions.append(position)
|
||||
self.mainFrame.command.Submit(command(
|
||||
|
||||
@@ -126,10 +126,13 @@ class AttributeSlider(wx.Panel):
|
||||
def SetValue(self, value, post_event=True, affect_modified_flag=True):
|
||||
self.ctrl.SetValue(value)
|
||||
invert_factor = -1 if self.inverse else 1
|
||||
if value >= self.base_value:
|
||||
slider_percentage = (value - self.base_value) / (self.UserMaxValue - self.base_value) * 100 * invert_factor
|
||||
else:
|
||||
slider_percentage = (value - self.base_value) / (self.base_value - self.UserMinValue) * 100 * invert_factor
|
||||
try:
|
||||
if value >= self.base_value:
|
||||
slider_percentage = (value - self.base_value) / (self.UserMaxValue - self.base_value) * 100 * invert_factor
|
||||
else:
|
||||
slider_percentage = (value - self.base_value) / (self.base_value - self.UserMinValue) * 100 * invert_factor
|
||||
except ZeroDivisionError:
|
||||
slider_percentage = 0
|
||||
self.slider.SetValue(slider_percentage)
|
||||
if post_event:
|
||||
wx.PostEvent(self, ValueChanged(self, None, value, None, slider_percentage, affect_modified_flag=affect_modified_flag))
|
||||
|
||||
@@ -35,7 +35,7 @@ class ItemDescription(wx.Panel):
|
||||
self.Layout()
|
||||
|
||||
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()
|
||||
copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
|
||||
@@ -50,7 +50,7 @@ class ItemDescription(wx.Panel):
|
||||
if selectedMenuItem == 1: # Copy was chosen
|
||||
self.copySelectionToClipboard()
|
||||
|
||||
def onKeyDown(self, event):
|
||||
def onKeyUp(self, event):
|
||||
keyCode = event.GetKeyCode()
|
||||
# Ctrl + C
|
||||
if keyCode == 67 and event.ControlDown():
|
||||
|
||||
@@ -89,6 +89,7 @@ class ItemMutatorList(wx.ScrolledWindow):
|
||||
higOverrides = {
|
||||
('Stasis Web', 'speedFactor'): False,
|
||||
('Damage Control', 'duration'): True,
|
||||
('Siege Module', 'siegeLocalLogisticsDurationBonus'): False
|
||||
}
|
||||
|
||||
first = True
|
||||
|
||||
@@ -14,7 +14,7 @@ class ItemTraits(wx.Panel):
|
||||
self.traits.SetPage(item.traits.traitText)
|
||||
|
||||
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)
|
||||
self.Layout()
|
||||
@@ -32,7 +32,7 @@ class ItemTraits(wx.Panel):
|
||||
if selectedMenuItem == 1: # Copy was chosen
|
||||
self.copySelectionToClipboard()
|
||||
|
||||
def onKeyDown(self, event):
|
||||
def onKeyUp(self, event):
|
||||
keyCode = event.GetKeyCode()
|
||||
# Ctrl + C
|
||||
if keyCode == 67 and event.ControlDown():
|
||||
|
||||
@@ -203,22 +203,6 @@ class ItemView(Display):
|
||||
self.setToggles()
|
||||
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):
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
self.ensureSelection(clickedPos)
|
||||
@@ -241,7 +225,7 @@ class ItemView(Display):
|
||||
self.unselectAll()
|
||||
# Perform sorting, using item's meta levels besides other stuff
|
||||
if self.marketBrowser.mode != 'recent':
|
||||
items.sort(key=self.itemSort)
|
||||
items.sort(key=self.sMkt.itemSort)
|
||||
# Mark current item list as active
|
||||
self.active = items
|
||||
# Show them
|
||||
@@ -251,12 +235,10 @@ class ItemView(Display):
|
||||
if len(items) > 1:
|
||||
# Re-sort stuff
|
||||
if self.marketBrowser.mode != 'recent':
|
||||
items.sort(key=self.itemSort)
|
||||
|
||||
items.sort(key=self.sMkt.itemSort)
|
||||
for i, item in enumerate(items[:9]):
|
||||
# set shortcut info for first 9 modules
|
||||
item.marketShortcut = i + 1
|
||||
|
||||
Display.refresh(self, items)
|
||||
|
||||
def columnBackground(self, colItem, item):
|
||||
|
||||
@@ -60,7 +60,8 @@ class MarketTree(wx.TreeCtrl):
|
||||
# If market should have items but it doesn't, do not show it
|
||||
if sMkt.marketGroupValidityCheck(childMktGrp) is False:
|
||||
continue
|
||||
iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp))
|
||||
icon = sMkt.getIconByMarketGroup(childMktGrp)
|
||||
iconId = -1 if icon is None else self.addImage(icon)
|
||||
try:
|
||||
childId = self.AppendItem(root, childMktGrp.name, iconId, data=childMktGrp.ID)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
|
||||
@@ -86,7 +86,7 @@ class PFSearchBox(wx.Window):
|
||||
|
||||
def OnKeyPress(self, event):
|
||||
if event.RawControlDown() and event.GetKeyCode() == wx.WXK_BACK:
|
||||
HandleCtrlBackspace(self.EditBox)
|
||||
HandleCtrlBackspace(self.EditBox)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
|
||||
@@ -35,31 +35,31 @@ class PFGeneralPref(PreferenceView):
|
||||
# Database path
|
||||
self.stSetUserPath = wx.StaticText(panel, wx.ID_ANY, "pyfa User Path:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stSetUserPath.Wrap(-1)
|
||||
mainSizer.Add(self.stSetUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.stSetUserPath, 0, wx.ALL, 5)
|
||||
self.inputUserPath = wx.TextCtrl(panel, wx.ID_ANY, config.savePath, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.inputUserPath.SetEditable(False)
|
||||
self.inputUserPath.SetBackgroundColour((200, 200, 200))
|
||||
mainSizer.Add(self.inputUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
|
||||
mainSizer.Add(self.inputUserPath, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
# Save DB
|
||||
self.stFitDB = wx.StaticText(panel, wx.ID_ANY, "Fitting Database:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stFitDB.Wrap(-1)
|
||||
mainSizer.Add(self.stFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.stFitDB, 0, wx.ALL, 5)
|
||||
|
||||
self.inputFitDB = wx.TextCtrl(panel, wx.ID_ANY, config.saveDB, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.inputFitDB.SetEditable(False)
|
||||
self.inputFitDB.SetBackgroundColour((200, 200, 200))
|
||||
mainSizer.Add(self.inputFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
|
||||
mainSizer.Add(self.inputFitDB, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
# Game Data DB
|
||||
self.stGameDB = wx.StaticText(panel, wx.ID_ANY, "Game Database:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stGameDB.Wrap(-1)
|
||||
mainSizer.Add(self.stGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.stGameDB, 0, wx.ALL, 5)
|
||||
|
||||
self.inputGameDB = wx.TextCtrl(panel, wx.ID_ANY, config.gameDB, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.inputGameDB.SetEditable(False)
|
||||
self.inputGameDB.SetBackgroundColour((200, 200, 200))
|
||||
mainSizer.Add(self.inputGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
|
||||
mainSizer.Add(self.inputGameDB, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.cbsaveInRoot.SetValue(config.saveInRoot)
|
||||
self.cbsaveInRoot.Bind(wx.EVT_CHECKBOX, self.onCBsaveInRoot)
|
||||
|
||||
@@ -50,7 +50,7 @@ class PFHTMLExportPref(PreferenceView):
|
||||
|
||||
self.fileSelectButton = wx.Button(panel, -1, "Set export destination", pos=(0, 0))
|
||||
self.fileSelectButton.Bind(wx.EVT_BUTTON, self.selectHTMLExportFilePath)
|
||||
mainSizer.Add(self.fileSelectButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.fileSelectButton, 0, wx.ALL, 5)
|
||||
|
||||
self.stDesc4 = wx.StaticText(panel, wx.ID_ANY, self.desc4, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stDesc4.Wrap(dlgWidth - 50)
|
||||
|
||||
@@ -36,20 +36,20 @@ class PFGeneralPref(PreferenceView):
|
||||
# Database path
|
||||
self.stLogPath = wx.StaticText(panel, wx.ID_ANY, "Log file location:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.stLogPath.Wrap(-1)
|
||||
mainSizer.Add(self.stLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.stLogPath, 0, wx.ALL, 5)
|
||||
self.inputLogPath = wx.TextCtrl(panel, wx.ID_ANY, config.logPath, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.inputLogPath.SetEditable(False)
|
||||
self.inputLogPath.SetBackgroundColour((200, 200, 200))
|
||||
mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
|
||||
mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
import requests
|
||||
self.certPath = wx.StaticText(panel, wx.ID_ANY, "Cert Path:", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.certPath .Wrap(-1)
|
||||
mainSizer.Add(self.certPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.certPath, 0, wx.ALL, 5)
|
||||
self.certPathCtrl = wx.TextCtrl(panel, wx.ID_ANY, requests.certs.where(), wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.certPathCtrl.SetEditable(False)
|
||||
self.certPathCtrl.SetBackgroundColour((200, 200, 200))
|
||||
mainSizer.Add(self.certPathCtrl, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
|
||||
mainSizer.Add(self.certPathCtrl, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
# Debug Logging
|
||||
self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, "Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
@@ -57,7 +57,7 @@ class PFGeneralPref(PreferenceView):
|
||||
|
||||
self.stDumpLogs = wx.StaticText(panel, wx.ID_ANY, "Pressing this button will cause all logs in memory to write to the log file:",
|
||||
wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
mainSizer.Add(self.stDumpLogs, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.stDumpLogs, 0, wx.ALL, 5)
|
||||
self.btnDumpLogs = wx.Button(panel, wx.ID_ANY, "Dump All Logs", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
self.btnDumpLogs.Bind(wx.EVT_BUTTON, OnDumpLogs)
|
||||
mainSizer.Add(self.btnDumpLogs, 0, wx.ALIGN_LEFT, 5)
|
||||
|
||||
@@ -135,7 +135,7 @@ class PFNetworkPref(PreferenceView):
|
||||
self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, "Auto-detected: ", wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
self.stPSAutoDetected.Wrap(-1)
|
||||
mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL, 5)
|
||||
|
||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnSizer.AddStretchSpacer()
|
||||
|
||||
@@ -51,8 +51,7 @@ class FirepowerViewFull(StatsView):
|
||||
self.headerPanel = headerPanel
|
||||
hsizer = self.headerPanel.Parent.GetHeaderContentSizer()
|
||||
self.stEff = wx.StaticText(self.headerPanel, wx.ID_ANY, "( Effective )")
|
||||
hsizer.Add(self.stEff)
|
||||
# self.headerPanel.GetParent().AddToggleItem(self.stEff)
|
||||
hsizer.Insert(0, self.stEff)
|
||||
|
||||
panel = "full"
|
||||
|
||||
@@ -130,9 +129,12 @@ class FirepowerViewFull(StatsView):
|
||||
self.panel.GetSizer().Layout()
|
||||
|
||||
# Remove effective label
|
||||
hsizer = self.headerPanel.GetSizer()
|
||||
hsizer.Hide(self.stEff)
|
||||
# self.stEff.Destroy()
|
||||
hsizer = self.headerPanel.Parent.GetHeaderContentSizer()
|
||||
for i, c in enumerate(hsizer.Children):
|
||||
if c.GetWindow() is self.stEff:
|
||||
hsizer.Remove(i)
|
||||
self.stEff.Destroy()
|
||||
break
|
||||
|
||||
# Get the new view
|
||||
view = StatsView.getView("miningyieldViewFull")(self.parent)
|
||||
|
||||
@@ -123,6 +123,15 @@ class Miscellanea(ViewColumn):
|
||||
text = ' | '.join(i[0] for i in info)
|
||||
tooltip = ' and '.join(i[1] for i in info).capitalize()
|
||||
return text, tooltip
|
||||
elif itemGroup == "Vorton Projector":
|
||||
cloudSize = stuff.getModifiedItemAttr("aoeCloudSize")
|
||||
aoeVelocity = stuff.getModifiedItemAttr("aoeVelocity")
|
||||
if not cloudSize or not aoeVelocity:
|
||||
return "", None
|
||||
text = "{0}{1} | {2}{3}".format(formatAmount(cloudSize, 3, 0, 3), "m",
|
||||
formatAmount(aoeVelocity, 3, 0, 3), "m/s")
|
||||
tooltip = "Explosion radius and explosion velocity"
|
||||
return text, tooltip
|
||||
elif itemCategory == "Subsystem":
|
||||
slots = ("hi", "med", "low")
|
||||
info = []
|
||||
@@ -133,7 +142,7 @@ class Miscellanea(ViewColumn):
|
||||
return "+ " + ", ".join(info), "Slot Modifiers"
|
||||
elif (
|
||||
itemGroup in ("Energy Neutralizer", "Structure Energy Neutralizer") or
|
||||
(itemGroup == "Structure Burst Projector" and "doomsdayAOENeut" in item.effects)
|
||||
(itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOENeut" in item.effects)
|
||||
):
|
||||
neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount")
|
||||
cycleParams = stuff.getCycleParameters()
|
||||
@@ -182,7 +191,7 @@ class Miscellanea(ViewColumn):
|
||||
return text, tooltip
|
||||
elif (
|
||||
itemGroup in ("Stasis Web", "Stasis Webifying Drone", "Structure Stasis Webifier") or
|
||||
(itemGroup == "Structure Burst Projector" and "doomsdayAOEWeb" in item.effects)
|
||||
(itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEWeb" in item.effects)
|
||||
):
|
||||
speedFactor = stuff.getModifiedItemAttr("speedFactor")
|
||||
if not speedFactor:
|
||||
@@ -193,7 +202,7 @@ class Miscellanea(ViewColumn):
|
||||
elif (
|
||||
itemGroup == "Target Painter" or
|
||||
(itemGroup == "Structure Disruption Battery" and "structureModuleEffectTargetPainter" in item.effects) or
|
||||
(itemGroup == "Structure Burst Projector" and "doomsdayAOEPaint" in item.effects)
|
||||
(itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEPaint" in item.effects)
|
||||
):
|
||||
sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus")
|
||||
if not sigRadBonus:
|
||||
@@ -204,7 +213,7 @@ class Miscellanea(ViewColumn):
|
||||
elif (
|
||||
itemGroup == "Sensor Dampener" or
|
||||
(itemGroup == "Structure Disruption Battery" and "structureModuleEffectRemoteSensorDampener" in item.effects) or
|
||||
(itemGroup == "Structure Burst Projector" and "doomsdayAOEDamp" in item.effects)
|
||||
(itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEDamp" in item.effects)
|
||||
):
|
||||
lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus")
|
||||
scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus")
|
||||
@@ -226,7 +235,7 @@ class Miscellanea(ViewColumn):
|
||||
return text, tooltip
|
||||
elif (
|
||||
itemGroup in ("Weapon Disruptor", "Structure Disruption Battery") or
|
||||
(itemGroup == "Structure Burst Projector" and "doomsdayAOETrack" in item.effects)
|
||||
(itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOETrack" in item.effects)
|
||||
):
|
||||
# Weapon disruption now covers both tracking and guidance (missile) disruptors
|
||||
# First get the attributes for tracking disruptors
|
||||
@@ -279,7 +288,8 @@ class Miscellanea(ViewColumn):
|
||||
"Heat Sink",
|
||||
"Ballistic Control system",
|
||||
"Structure Weapon Upgrade",
|
||||
"Entropic Radiation Sink"
|
||||
"Entropic Radiation Sink",
|
||||
"Vorton Projector Upgrade"
|
||||
):
|
||||
attrMap = {
|
||||
"Gyrostabilizer": ("damageMultiplier", "speedMultiplier", "Projectile weapon"),
|
||||
@@ -287,7 +297,8 @@ class Miscellanea(ViewColumn):
|
||||
"Heat Sink": ("damageMultiplier", "speedMultiplier", "Energy weapon"),
|
||||
"Ballistic Control system": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"),
|
||||
"Structure Weapon Upgrade": ("missileDamageMultiplierBonus", "speedMultiplier", "Missile"),
|
||||
"Entropic Radiation Sink": ("damageMultiplier", "speedMultiplier", "Precursor weapon")}
|
||||
"Entropic Radiation Sink": ("damageMultiplier", "speedMultiplier", "Precursor weapon"),
|
||||
"Vorton Projector Upgrade": ("damageMultiplier", "speedMultiplier", "Vorton projector")}
|
||||
dmgAttr, rofAttr, weaponName = attrMap[itemGroup]
|
||||
dmg = stuff.getModifiedItemAttr(dmgAttr)
|
||||
rof = stuff.getModifiedItemAttr(rofAttr)
|
||||
@@ -311,8 +322,8 @@ class Miscellanea(ViewColumn):
|
||||
tooltip = "Drone DPS boost"
|
||||
return text, tooltip
|
||||
elif (
|
||||
itemGroup in ("ECM", "Burst Jammer", "Burst Projectors", "Structure ECM Battery") or
|
||||
(itemGroup == "Structure Burst Projector" and "doomsdayAOEECM" in item.effects)
|
||||
itemGroup in ("ECM", "Burst Jammer", "Structure ECM Battery") or
|
||||
(itemGroup in ("Structure Burst Projector", "Burst Projectors") and "doomsdayAOEECM" in item.effects)
|
||||
):
|
||||
grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus")
|
||||
ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus")
|
||||
@@ -680,6 +691,13 @@ class Miscellanea(ViewColumn):
|
||||
formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3),
|
||||
)
|
||||
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:
|
||||
chargeGroup = stuff.charge.group.name
|
||||
if chargeGroup.endswith("Rocket") or chargeGroup.endswith("Missile") or chargeGroup.endswith("Torpedo"):
|
||||
|
||||
@@ -166,7 +166,7 @@ class FittingView(d.Display):
|
||||
self.hoveredRow = 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_RIGHT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_MIDDLE_DOWN, self.click)
|
||||
@@ -377,23 +377,7 @@ class FittingView(d.Display):
|
||||
event.Skip()
|
||||
return
|
||||
batchOp = wx.GetMouseState().GetModifiers() == wx.MOD_ALT and getattr(event, 'allowBatch', None) is not False
|
||||
# If we've selected ammo, then apply to the selected module(s)
|
||||
if item.isCharge:
|
||||
positions = []
|
||||
fit = Fit.getInstance().getFit(fitID)
|
||||
if batchOp:
|
||||
for position, mod in enumerate(fit.modules):
|
||||
if isinstance(mod, Module) and not mod.isEmpty:
|
||||
positions.append(position)
|
||||
else:
|
||||
for mod in self.getSelectedMods():
|
||||
if mod.isEmpty or mod not in fit.modules:
|
||||
continue
|
||||
positions.append(fit.modules.index(mod))
|
||||
if len(positions) > 0:
|
||||
self.mainFrame.command.Submit(cmd.GuiChangeLocalModuleChargesCommand(
|
||||
fitID=fitID, positions=positions, chargeItemID=itemID))
|
||||
elif (item.isModule and not batchOp) or item.isSubsystem:
|
||||
if (item.isModule and not batchOp) or item.isSubsystem:
|
||||
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
|
||||
elif item.isModule and batchOp:
|
||||
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
|
||||
@@ -578,7 +562,10 @@ class FittingView(d.Display):
|
||||
|
||||
if sFit.serviceFittingOptions["rackSlots"]:
|
||||
# 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
|
||||
for i, mod in enumerate(self.mods):
|
||||
|
||||
@@ -34,7 +34,7 @@ from wx.lib.agw.floatspin import FloatSpin
|
||||
|
||||
import config
|
||||
import gui.globalEvents as GE
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor, TextEntryValidatedDialog
|
||||
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
||||
@@ -312,6 +312,7 @@ class CharacterEditor(AuxiliaryFrame):
|
||||
|
||||
|
||||
class SkillTreeView(wx.Panel):
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
||||
style=wx.TAB_TRAVERSAL)
|
||||
@@ -402,7 +403,7 @@ class SkillTreeView(wx.Panel):
|
||||
setattr(self, "{}Btn".format(name.lower()), btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTip("%s skills %s clipboard" % (name, direction))
|
||||
bSizerButtons.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT | wx.ALL, 5)
|
||||
bSizerButtons.Add(btn, 0, wx.ALL, 5)
|
||||
btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Skills".format(name.lower())))
|
||||
|
||||
pmainSizer.Add(bSizerButtons, 0, wx.EXPAND, 5)
|
||||
@@ -611,12 +612,16 @@ class SkillTreeView(wx.Panel):
|
||||
|
||||
def spawnMenu(self, event):
|
||||
item = event.GetItem()
|
||||
itemData = self.skillTreeListCtrl.GetItemData(item)
|
||||
if itemData is None:
|
||||
return
|
||||
|
||||
self.skillTreeListCtrl.Select(item)
|
||||
thing = self.skillTreeListCtrl.GetFirstChild(item).IsOk()
|
||||
if thing:
|
||||
return
|
||||
|
||||
id = self.skillTreeListCtrl.GetItemData(item)[1]
|
||||
id = itemData[1]
|
||||
eveItem = Market.getInstance().getItem(id)
|
||||
|
||||
srcContext = "skillItem"
|
||||
|
||||
@@ -367,7 +367,7 @@ class _TabRenderer:
|
||||
width = max(width, self.min_width)
|
||||
height = max(height, self.min_height)
|
||||
|
||||
self.disabled = False
|
||||
self._disabled = False
|
||||
self.baseText = text
|
||||
self.extraText = ''
|
||||
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.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
|
||||
def text(self):
|
||||
return self.baseText + self.extraText
|
||||
@@ -402,6 +414,10 @@ class _TabRenderer:
|
||||
width = max(width, self.min_width)
|
||||
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.InitTab()
|
||||
|
||||
@@ -927,7 +943,6 @@ class _TabsContainer(wx.Panel):
|
||||
tb_renderer = self.tabs[tab]
|
||||
tb_renderer.disabled = disabled
|
||||
|
||||
self.AdjustTabsSize()
|
||||
self.Refresh()
|
||||
|
||||
def GetSelectedTab(self):
|
||||
@@ -975,9 +990,6 @@ class _TabsContainer(wx.Panel):
|
||||
sel_tab = self.tabs.index(tab)
|
||||
self.Parent.SetSelection(sel_tab)
|
||||
|
||||
wx.PostEvent(self.Parent, PageChanged(self.tabs.index(old_sel_tab),
|
||||
self.tabs.index(tab)))
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -177,7 +177,7 @@ class CopySelectDialog(wx.Dialog):
|
||||
|
||||
def exportEsi(self, options, callback):
|
||||
fit = getFit(self.mainFrame.getActiveFit())
|
||||
Port.exportESI(fit, callback)
|
||||
Port.exportESI(fit, True, callback)
|
||||
|
||||
def exportXml(self, options, callback):
|
||||
fit = getFit(self.mainFrame.getActiveFit())
|
||||
|
||||
@@ -26,7 +26,7 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import eos.db
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.builtinShipBrowser.events import FitSelected
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import wx
|
||||
from logbook import Logger
|
||||
|
||||
import config
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from service.prereqsCheck import version_block
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class ErrorFrame(AuxiliaryFrame):
|
||||
self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version + version_block.strip(), wx.DefaultPosition,
|
||||
(-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP)
|
||||
self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
|
||||
mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5)
|
||||
mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL, 5)
|
||||
self.errorTextCtrl.AppendText("\n")
|
||||
self.errorTextCtrl.Layout()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import config
|
||||
import gui.globalEvents as GE
|
||||
from eos.db import getItem
|
||||
from eos.saveddata.cargo import Cargo
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.display import Display
|
||||
from gui.characterEditor import APIView
|
||||
from service.character import Character
|
||||
@@ -18,6 +18,7 @@ from service.esiAccess import APIException
|
||||
from service.fit import Fit
|
||||
from service.port import Port
|
||||
from service.port.esi import ESIExportException
|
||||
from service.settings import EsiSettings
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -61,8 +62,10 @@ class EveFittings(AuxiliaryFrame):
|
||||
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.importBtn = wx.Button(self, wx.ID_ANY, "Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5)
|
||||
self.deleteBtn = wx.Button(self, wx.ID_ANY, "Delete from EVE", wx.DefaultPosition, wx.DefaultSize, 5)
|
||||
self.deleteAllBtn = wx.Button(self, wx.ID_ANY, "Delete all from Eve", wx.DefaultPosition, wx.DefaultSize, 5)
|
||||
btnSizer.Add(self.importBtn, 1, wx.ALL, 5)
|
||||
btnSizer.Add(self.deleteBtn, 1, wx.ALL, 5)
|
||||
btnSizer.Add(self.deleteAllBtn, 1, wx.ALL, 5)
|
||||
fitSizer.Add(btnSizer, 0, wx.EXPAND)
|
||||
|
||||
contentSizer.Add(fitSizer, 1, wx.EXPAND, 0)
|
||||
@@ -71,6 +74,7 @@ class EveFittings(AuxiliaryFrame):
|
||||
self.fetchBtn.Bind(wx.EVT_BUTTON, self.fetchFittings)
|
||||
self.importBtn.Bind(wx.EVT_BUTTON, self.importFitting)
|
||||
self.deleteBtn.Bind(wx.EVT_BUTTON, self.deleteFitting)
|
||||
self.deleteAllBtn.Bind(wx.EVT_BUTTON, self.deleteAllFittings)
|
||||
|
||||
self.Bind(wx.EVT_CHAR_HOOK, self.kbEvent)
|
||||
|
||||
@@ -168,6 +172,37 @@ class EveFittings(AuxiliaryFrame):
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(msg)
|
||||
|
||||
def deleteAllFittings(self, event):
|
||||
sEsi = Esi.getInstance()
|
||||
activeChar = self.getActiveCharacter()
|
||||
if activeChar is None:
|
||||
return
|
||||
charName = sEsi.getSsoCharacter(activeChar).characterName
|
||||
anyDeleted = False
|
||||
with wx.MessageDialog(
|
||||
self, "Do you really want to delete all fits from %s in EVE?"%(charName),
|
||||
"Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
try:
|
||||
for fit in self.fittings:
|
||||
sEsi.delFitting(activeChar, fit['fitting_id'])
|
||||
anyDeleted = True
|
||||
except requests.exceptions.ConnectionError:
|
||||
msg = "Connection error, please check your internet connection"
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(msg)
|
||||
except APIException as ex:
|
||||
if anyDeleted:
|
||||
msg = "Some fits were not deleted: ESI error {} received".format(ex.status_code)
|
||||
else:
|
||||
msg = "Failed to delete fits: ESI error {} received".format(ex.status_code)
|
||||
pyfalog.error(msg)
|
||||
self.statusbar.SetStatusText(msg)
|
||||
# repopulate the fitting list
|
||||
self.fitTree.populateSkillTree(self.fittings)
|
||||
self.fitView.update([])
|
||||
|
||||
|
||||
class ESIServerExceptionHandler:
|
||||
def __init__(self, parentWindow, ex):
|
||||
@@ -207,7 +242,7 @@ class ExportToEve(AuxiliaryFrame):
|
||||
def __init__(self, parent):
|
||||
super().__init__(
|
||||
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
|
||||
|
||||
@@ -224,6 +259,11 @@ class ExportToEve(AuxiliaryFrame):
|
||||
|
||||
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.statusbar = wx.StatusBar(self)
|
||||
@@ -239,6 +279,10 @@ class ExportToEve(AuxiliaryFrame):
|
||||
|
||||
self.Center(wx.BOTH)
|
||||
|
||||
def OnChargeExportChange(self, event):
|
||||
EsiSettings.getInstance().set('exportCharges', self.exportChargesCb.GetValue())
|
||||
event.Skip()
|
||||
|
||||
def updateCharList(self):
|
||||
sEsi = Esi.getInstance()
|
||||
chars = sEsi.getSsoCharacters()
|
||||
@@ -274,8 +318,9 @@ class ExportToEve(AuxiliaryFrame):
|
||||
sEsi = Esi.getInstance()
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
exportCharges = self.exportChargesCb.GetValue()
|
||||
try:
|
||||
data = sPort.exportESI(sFit.getFit(fitID))
|
||||
data = sPort.exportESI(sFit.getFit(fitID), exportCharges)
|
||||
except ESIExportException as e:
|
||||
msg = str(e)
|
||||
if not msg:
|
||||
|
||||
@@ -91,6 +91,8 @@ class CalcReplaceLocalModuleCommand(wx.Command):
|
||||
|
||||
@property
|
||||
def needsGuiRecalc(self):
|
||||
if self.unloadedCharge:
|
||||
return True
|
||||
if self.savedStateCheckChanges is None:
|
||||
return True
|
||||
for container in self.savedStateCheckChanges:
|
||||
|
||||
@@ -63,8 +63,8 @@ class GuiCargoToLocalModuleCommand(wx.Command):
|
||||
dstModSlot = dstMod.slot
|
||||
if self.srcCargoItemID == dstModItemID:
|
||||
return False
|
||||
# To keep all old item properties, copy them over from old module
|
||||
newModInfo = ModuleInfo.fromModule(dstMod)
|
||||
# To keep all old item properties, copy them over from old module, except for mutations
|
||||
newModInfo = ModuleInfo.fromModule(dstMod, unmutate=True)
|
||||
newModInfo.itemID = self.srcCargoItemID
|
||||
if dstMod.isEmpty:
|
||||
newCargoModItemID = None
|
||||
|
||||
@@ -324,7 +324,8 @@ def activeStateLimit(itemIdentity):
|
||||
'microJumpDrive', 'microJumpPortalDrive', 'emergencyHullEnergizer',
|
||||
'cynosuralGeneration', 'jumpPortalGeneration', 'jumpPortalGenerationBO',
|
||||
'cloneJumpAccepting', 'cloakingWarpSafe', 'cloakingPrototype', 'cloaking',
|
||||
'massEntanglerEffect5', 'electronicAttributeModifyOnline', 'targetPassively'
|
||||
'massEntanglerEffect5', 'electronicAttributeModifyOnline', 'targetPassively',
|
||||
'cargoScan', 'shipScan', 'surveyScan'
|
||||
}.intersection(item.effects):
|
||||
return FittingModuleState.ONLINE
|
||||
return FittingModuleState.ACTIVE
|
||||
|
||||
@@ -23,7 +23,7 @@ import wx
|
||||
import config
|
||||
import gui.mainFrame
|
||||
from eos.saveddata.module import Module
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.builtinItemStatsViews.itemAffectedBy import ItemAffectedBy
|
||||
from gui.builtinItemStatsViews.itemAttributes import ItemParams
|
||||
|
||||
@@ -110,8 +110,6 @@ class OpenFitsThread(threading.Thread):
|
||||
self.start()
|
||||
|
||||
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
|
||||
# has 3 values:
|
||||
# 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_PAGEUP, ctabprev),
|
||||
(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
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||
@@ -181,7 +181,7 @@ class DmgPatternEditor(AuxiliaryFrame):
|
||||
setattr(self, name, btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTip("%s patterns %s clipboard" % (name, direction))
|
||||
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
|
||||
footerSizer.Add(btn, 0)
|
||||
btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower())))
|
||||
|
||||
if not self.entityEditor.checkEntitiesExist():
|
||||
|
||||
@@ -10,7 +10,7 @@ import gui.builtinMarketBrowser.pfSearchBox as SBox
|
||||
import gui.display as d
|
||||
import gui.globalEvents as GE
|
||||
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.marketBrowser import SearchBox
|
||||
from service.fit import Fit
|
||||
@@ -213,19 +213,7 @@ class ItemView(d.Display):
|
||||
def itemSort(self, item):
|
||||
sMkt = Market.getInstance()
|
||||
isFittable = item.group.name in sMkt.FIT_GROUPS or item.category.name in sMkt.FIT_CATEGORIES
|
||||
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 not isFittable, catname, mktgrpid, parentname, metatab, metalvl, item.name
|
||||
return (not isFittable, *sMkt.itemSort(item))
|
||||
|
||||
def populateSearch(self, itemIDs):
|
||||
items = Market.getItems(itemIDs)
|
||||
|
||||
@@ -254,7 +254,6 @@ class PyGauge(wx.Window):
|
||||
w = rect.width
|
||||
else:
|
||||
w = rect.width * (float(value) / 100)
|
||||
|
||||
r = copy.copy(rect)
|
||||
r.width = w
|
||||
dc.DrawRectangle(r)
|
||||
@@ -315,7 +314,8 @@ class PyGauge(wx.Window):
|
||||
color,
|
||||
gradient_color
|
||||
)
|
||||
dc.DrawBitmap(gradient_bitmap, r.left, r.top)
|
||||
if gradient_bitmap is not None:
|
||||
dc.DrawBitmap(gradient_bitmap, r.left, r.top)
|
||||
|
||||
# font stuff begins here
|
||||
dc.SetFont(self.font)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
||||
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||
@@ -159,7 +159,7 @@ class ImplantSetEditor(AuxiliaryFrame):
|
||||
setattr(self, name, btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTip("%s implant sets %s clipboard" % (name, direction))
|
||||
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
|
||||
footerSizer.Add(btn, 0)
|
||||
|
||||
mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ class ShipBrowser(wx.Panel):
|
||||
"amarr", "caldari", "gallente", "minmatar",
|
||||
"sisters", "ore", "concord",
|
||||
"serpentis", "angel", "blood", "sansha", "guristas", "mordu",
|
||||
"jove", "upwell", "triglavian", None
|
||||
"jove", "triglavian", "upwell", None
|
||||
]
|
||||
|
||||
def raceNameKey(self, ship):
|
||||
|
||||
@@ -148,7 +148,7 @@ class StatsPane(wx.Panel):
|
||||
sizer.AddStretchSpacer()
|
||||
# Add menu
|
||||
header_menu = wx.StaticText(tp.GetHeaderPanel(), wx.ID_ANY, "\u2630", size=wx.Size((10, -1)))
|
||||
sizer.Add(header_menu , 0, wx.EXPAND | wx.RIGHT | wx.ALIGN_RIGHT, 5)
|
||||
sizer.Add(header_menu , 0, wx.EXPAND | wx.RIGHT, 5)
|
||||
|
||||
header_menu.Bind(wx.EVT_CONTEXT_MENU, handler)
|
||||
header_menu.Bind(wx.EVT_LEFT_UP, handler)
|
||||
|
||||
@@ -27,7 +27,7 @@ from logbook import Logger
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.auxFrame import AuxiliaryFrame
|
||||
from gui.auxWindow import AuxiliaryFrame
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.builtinViews.entityEditor import BaseValidator, EntityEditor
|
||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||
@@ -183,7 +183,7 @@ class TargetProfileEditor(AuxiliaryFrame):
|
||||
ttText, unitText = self.ATTRIBUTES[attr]
|
||||
bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big" % attr, "gui"))
|
||||
bmp.SetToolTip(wx.ToolTip(ttText))
|
||||
miscAttrSizer.Add(bmp, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, leftPad)
|
||||
miscAttrSizer.Add(bmp, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, leftPad)
|
||||
# set text edit
|
||||
editBox = FloatBox(parent=self, id=wx.ID_ANY, value=None, pos=wx.DefaultPosition, size=defSize)
|
||||
editBox.SetToolTip(wx.ToolTip(ttText))
|
||||
@@ -231,7 +231,7 @@ class TargetProfileEditor(AuxiliaryFrame):
|
||||
setattr(self, name, btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTip("%s profiles %s clipboard" % (name, direction))
|
||||
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
|
||||
footerSizer.Add(btn, 0)
|
||||
btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower())))
|
||||
|
||||
if not self.entityEditor.checkEntitiesExist():
|
||||
|
||||
@@ -35,6 +35,8 @@ def DrawFilledBitmap(width, height, color):
|
||||
|
||||
|
||||
def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4):
|
||||
if width == 0 or height == 0:
|
||||
return None
|
||||
canvas = wx.Bitmap(width, height)
|
||||
|
||||
mdc = wx.MemoryDC()
|
||||
|
||||
|
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 452 B |
|
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 699 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 725 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 738 B After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 683 B After Width: | Height: | Size: 781 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 761 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 842 B After Width: | Height: | Size: 915 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 842 B After Width: | Height: | Size: 898 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 834 B After Width: | Height: | Size: 894 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 837 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 856 B After Width: | Height: | Size: 938 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 858 B After Width: | Height: | Size: 944 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 837 B After Width: | Height: | Size: 927 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 962 B |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 696 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 931 B After Width: | Height: | Size: 955 B |