Merge branch 'master' into ammo_graph

This commit is contained in:
DarkPhoenix
2020-01-21 15:25:39 +03:00
64 changed files with 2392149 additions and 3277152 deletions

View File

@@ -32,7 +32,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 = 1
GAMEDATA_SCHEMA_VERSION = 3
def db_needs_update():
@@ -88,52 +88,164 @@ def update_db():
# Create the database tables
eos.db.gamedata_meta.create_all()
# Config dict
tables = {
'clonegrades': ('fsd_lite', eos.gamedata.AlphaCloneSkill),
'dogmaattributes': ('bulkdata', eos.gamedata.AttributeInfo),
'dogmaeffects': ('bulkdata', eos.gamedata.Effect),
'dogmatypeattributes': ('bulkdata', eos.gamedata.Attribute),
'dogmatypeeffects': ('bulkdata', eos.gamedata.ItemEffect),
'dogmaunits': ('bulkdata', eos.gamedata.Unit),
'evecategories': ('fsd_lite', eos.gamedata.Category),
'evegroups': ('fsd_lite', eos.gamedata.Group),
'metagroups': ('fsd_binary', eos.gamedata.MetaGroup),
'evetypes': ('fsd_lite', eos.gamedata.Item),
'traits': ('phobos', eos.gamedata.Traits),
'metadata': ('phobos', eos.gamedata.MetaData),
'marketgroups': ('fsd_binary', eos.gamedata.MarketGroup)}
def _readData(minerName, jsonName, keyIdName=None):
with open(os.path.join(JSON_DIR, minerName, '{}.json'.format(jsonName)), encoding='utf-8') as f:
rawData = json.load(f)
if not keyIdName:
return rawData
# IDs in keys, rows in values
data = []
for k, v in rawData.items():
row = {}
row.update(v)
if keyIdName not in row:
row[keyIdName] = int(k)
data.append(row)
return data
fieldMapping = {
'marketgroups': {
'id': 'marketGroupID',
'name': 'marketGroupName'},
'metagroups': {
'id': 'metaGroupID'}}
def _addRows(data, cls, fieldMap=None):
if fieldMap is None:
fieldMap = {}
for row in data:
instance = cls()
for k, v in row.items():
if isinstance(v, str):
v = v.strip()
setattr(instance, fieldMap.get(k, k), v)
eos.db.gamedata_session.add(instance)
rowsInValues = (
'evetypes',
'evegroups',
'evecategories',
'marketgroups',
'metagroups')
def processEveTypes():
print('processing evetypes')
data = _readData('fsd_lite', 'evetypes', keyIdName='typeID')
for row in data:
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['published'] = True
def convertIcons(data):
new = []
for k, v in list(data.items()):
v['iconID'] = k
new.append(v)
return new
def convertClones(data):
newData = []
for row in data:
if (
row['published'] or
# group Ship Modifiers, for items like tactical t3 ship modes
row['groupID'] == 1306 or
# Micro Bombs (Fighters)
row['typeID'] in (41549, 41548, 41551, 41550) or
# Abyssal weather (environment)
row['groupID'] in (
1882,
1975,
1971,
# the "container" for the abyssal environments
1983)
):
newData.append(row)
_addRows(newData, eos.gamedata.Item)
return newData
def processEveGroups():
print('processing evegroups')
data = _readData('fsd_lite', 'evegroups', keyIdName='groupID')
_addRows(data, eos.gamedata.Group)
return data
def processEveCategories():
print('processing evecategories')
data = _readData('fsd_lite', 'evecategories', keyIdName='categoryID')
_addRows(data, eos.gamedata.Category)
def processDogmaAttributes():
print('processing dogmaattributes')
data = _readData('fsd_binary', 'dogmaattributes', keyIdName='attributeID')
_addRows(data, eos.gamedata.AttributeInfo)
def processDogmaTypeAttributes(eveTypesData):
print('processing dogmatypeattributes')
data = _readData('fsd_binary', 'typedogma', keyIdName='typeID')
eveTypeIds = set(r['typeID'] for r in eveTypesData)
newData = []
for row in eveTypesData:
for attrId, attrName in {4: 'mass', 38: 'capacity', 161: 'volume', 162: 'radius'}.items():
if attrName in row:
newData.append({'typeID': row['typeID'], 'attributeID': attrId, 'value': row[attrName]})
for typeData in data:
if typeData['typeID'] not in eveTypeIds:
continue
for row in typeData.get('dogmaAttributes', ()):
row['typeID'] = typeData['typeID']
newData.append(row)
_addRows(newData, eos.gamedata.Attribute)
return newData
def processDynamicItemAttributes():
print('processing dynamicitemattributes')
data = _readData('fsd_binary', 'dynamicitemattributes')
for mutaID, mutaData in data.items():
muta = eos.gamedata.DynamicItem()
muta.typeID = mutaID
muta.resultingTypeID = mutaData['inputOutputMapping'][0]['resultingType']
eos.db.gamedata_session.add(muta)
for x in mutaData['inputOutputMapping'][0]['applicableTypes']:
item = eos.gamedata.DynamicItemItem()
item.typeID = mutaID
item.applicableTypeID = x
eos.db.gamedata_session.add(item)
for attrID, attrData in mutaData['attributeIDs'].items():
attr = eos.gamedata.DynamicItemAttribute()
attr.typeID = mutaID
attr.attributeID = attrID
attr.min = attrData['min']
attr.max = attrData['max']
eos.db.gamedata_session.add(attr)
def processDogmaEffects():
print('processing dogmaeffects')
data = _readData('fsd_binary', 'dogmaeffects', keyIdName='effectID')
_addRows(data, eos.gamedata.Effect, fieldMap={'resistanceAttributeID': 'resistanceID'})
def processDogmaTypeEffects(eveTypesData):
print('processing dogmatypeeffects')
data = _readData('fsd_binary', 'typedogma', keyIdName='typeID')
eveTypeIds = set(r['typeID'] for r in eveTypesData)
newData = []
for typeData in data:
if typeData['typeID'] not in eveTypeIds:
continue
for row in typeData.get('dogmaEffects', ()):
row['typeID'] = typeData['typeID']
newData.append(row)
_addRows(newData, eos.gamedata.ItemEffect)
return newData
def processDogmaUnits():
print('processing dogmaunits')
data = _readData('fsd_binary', 'dogmaunits', keyIdName='unitID')
_addRows(data, eos.gamedata.Unit, fieldMap={'name': 'unitName'})
def processMarketGroups():
print('processing marketgroups')
data = _readData('fsd_binary', 'marketgroups', keyIdName='marketGroupID')
_addRows(data, eos.gamedata.MarketGroup, fieldMap={'name': 'marketGroupName'})
def processMetaGroups():
print('processing metagroups')
data = _readData('fsd_binary', 'metagroups', keyIdName='metaGroupID')
_addRows(data, eos.gamedata.MetaGroup)
def processCloneGrades():
print('processing clonegrades')
data = _readData('fsd_lite', 'clonegrades')
newData = []
# December, 2017 - CCP decided to use only one set of skill levels for alpha clones. However, this is still
# represented in the data as a skillset per race. To ensure that all skills are the same, we store them in a way
# that we can check to make sure all races have the same skills, as well as skill levels
check = {}
for ID in data:
for skill in data[ID]['skills']:
newData.append({
@@ -144,18 +256,25 @@ def update_db():
if ID not in check:
check[ID] = {}
check[ID][int(skill['typeID'])] = int(skill['level'])
if not functools.reduce(lambda a, b: a if a == b else False, [v for _, v in check.items()]):
raise Exception('Alpha Clones not all equal')
newData = [x for x in newData if x['alphaCloneID'] == 1]
if len(newData) == 0:
raise Exception('Alpha Clone processing failed')
return newData
tmp = []
for row in newData:
if row['alphaCloneID'] not in tmp:
cloneParent = eos.gamedata.AlphaClone()
setattr(cloneParent, 'alphaCloneID', row['alphaCloneID'])
setattr(cloneParent, 'alphaCloneName', row['alphaCloneName'])
eos.db.gamedata_session.add(cloneParent)
tmp.append(row['alphaCloneID'])
_addRows(newData, eos.gamedata.AlphaCloneSkill)
def convertTraits(data):
def processTraits():
print('processing traits')
data = _readData('phobos', 'traits')
def convertSection(sectionData):
sectionLines = []
@@ -182,9 +301,42 @@ def update_db():
traitLine = '<br />\n<br />\n'.join(typeLines)
newRow = {'typeID': typeId, 'traitText': traitLine}
newData.append(newRow)
return newData
def fillReplacements(tables):
_addRows(newData, eos.gamedata.Traits)
def processMetadata():
print('processing metadata')
data = _readData('phobos', 'metadata')
_addRows(data, eos.gamedata.MetaData)
def processReqSkills(eveTypesData):
print('processing requiredskillsfortypes')
def composeReqSkills(raw):
reqSkills = {}
for skillTypeID, skillLevels in raw.items():
reqSkills[int(skillTypeID)] = skillLevels[0]
return reqSkills
eveTypeIds = set(r['typeID'] for r in eveTypesData)
data = _readData('fsd_binary', 'requiredskillsfortypes')
reqsByItem = {}
itemsByReq = {}
for typeID, skillreqData in data.items():
typeID = int(typeID)
if typeID not in eveTypeIds:
continue
for skillTypeID, skillLevel in composeReqSkills(skillreqData).items():
reqsByItem.setdefault(typeID, {})[skillTypeID] = skillLevel
itemsByReq.setdefault(skillTypeID, {})[typeID] = skillLevel
for item in eos.db.gamedata_session.query(eos.gamedata.Item).all():
if item.typeID in reqsByItem:
item.reqskills = json.dumps(reqsByItem[item.typeID])
if item.typeID in itemsByReq:
item.requiredfor = json.dumps(itemsByReq[item.typeID])
def processReplacements(eveTypesData, eveGroupsData, dogmaTypeAttributesData, dogmaTypeEffectsData):
print('finding item replacements')
def compareAttrs(attrs1, attrs2):
# Consider items as different if they have no attrs
@@ -196,7 +348,6 @@ def update_db():
return True
return False
print('finding replacements')
skillReqAttribs = {
182: 277,
183: 278,
@@ -208,18 +359,18 @@ def update_db():
# Get data on type groups
# Format: {type ID: group ID}
typesGroups = {}
for row in tables['evetypes']:
for row in eveTypesData:
typesGroups[row['typeID']] = row['groupID']
# Get data on item effects
# Format: {type ID: set(effect, IDs)}
typesEffects = {}
for row in tables['dogmatypeeffects']:
for row in dogmaTypeEffectsData:
typesEffects.setdefault(row['typeID'], set()).add(row['effectID'])
# Get data on type attributes
# Format: {type ID: {attribute ID: attribute value}}
typesNormalAttribs = {}
typesSkillAttribs = {}
for row in tables['dogmatypeattributes']:
for row in dogmaTypeAttributesData:
attributeID = row['attributeID']
if attributeID in skillReqAttribsFlat:
typeSkillAttribs = typesSkillAttribs.setdefault(row['typeID'], {})
@@ -258,13 +409,13 @@ def update_db():
typeSkillReqs[skillType] = skillLevel
# Format: {group ID: category ID}
groupCategories = {}
for row in tables['evegroups']:
for row in eveGroupsData:
groupCategories[row['groupID']] = row['categoryID']
# As EVE affects various types mostly depending on their group or skill requirements,
# we're going to group various types up this way
# Format: {(group ID, frozenset(skillreq, type, IDs), frozenset(type, effect, IDs): [type ID, {attribute ID: attribute value}]}
groupedData = {}
for row in tables['evetypes']:
for row in eveTypesData:
typeID = row['typeID']
# Ignore items outside of categories we need
if groupCategories[typesGroups[typeID]] not in (
@@ -301,134 +452,30 @@ def update_db():
if compareAttrs(type1[1], type2[1]):
replacements.setdefault(type1[0], set()).add(type2[0])
replacements.setdefault(type2[0], set()).add(type1[0])
# Put this data into types table so that normal process hooks it up
for row in tables['evetypes']:
row['replacements'] = ','.join('{}'.format(tid) for tid in sorted(replacements.get(row['typeID'], ())))
# Update DB session with data we generated
for item in eos.db.gamedata_session.query(eos.gamedata.Item).all():
itemReplacements = replacements.get(item.typeID)
if itemReplacements is not None:
item.replacements = ','.join('{}'.format(tid) for tid in sorted(itemReplacements))
data = {}
eveTypesData = processEveTypes()
eveGroupsData = processEveGroups()
processEveCategories()
processDogmaAttributes()
dogmaTypeAttributesData = processDogmaTypeAttributes(eveTypesData)
processDynamicItemAttributes()
processDogmaEffects()
dogmaTypeEffectsData = processDogmaTypeEffects(eveTypesData)
processDogmaUnits()
processMarketGroups()
processMetaGroups()
processCloneGrades()
processTraits()
processMetadata()
# Dump all data to memory so we can easely cross check ignored rows
for jsonName, (minerName, cls) in tables.items():
with open(os.path.join(JSON_DIR, minerName, '{}.json'.format(jsonName)), encoding='utf-8') as f:
tableData = json.load(f)
if jsonName in rowsInValues:
newTableData = []
for k, v in tableData.items():
row = {}
row.update(v)
if 'id' not in row:
row['id'] = int(k)
newTableData.append(row)
tableData = newTableData
if jsonName == 'icons':
tableData = convertIcons(tableData)
if jsonName == 'traits':
tableData = convertTraits(tableData)
if jsonName == 'clonegrades':
tableData = convertClones(tableData)
data[jsonName] = tableData
fillReplacements(data)
# Set with typeIDs which we will have in our database
# Sometimes CCP unpublishes some items we want to have published, we
# can do it here - just add them to initial set
eveTypes = set()
for row in data['evetypes']:
if (
row['published'] or
row['typeName'] == 'Capsule' or
# group Ship Modifiers, for items like tactical t3 ship modes
row['groupID'] == 1306 or
# Civilian weapons
(row['typeName'].startswith('Civilian') and "Shuttle" not in row['typeName']) or
# Micro Bombs (Fighters)
row['typeID'] in (41549, 41548, 41551, 41550) or
# Abyssal weather (environment)
row['groupID'] in (
1882,
1975,
1971,
# the "container" for the abyssal environments
1983) or
# Dark Blood Tracking Disruptor (drops, but rarely)
row['typeID'] == 32416
):
eveTypes.add(row['typeID'])
# ignore checker
def isIgnored(file, row):
if file in ('evetypes', 'dogmatypeeffects', 'dogmatypeattributes') and row['typeID'] not in eveTypes:
return True
return False
# Loop through each json file and write it away, checking ignored rows
for jsonName, table in data.items():
fieldMap = fieldMapping.get(jsonName, {})
tmp = []
print('processing {}'.format(jsonName))
for row in table:
# We don't care about some kind of rows, filter it out if so
if not isIgnored(jsonName, row):
if (
jsonName == 'evetypes' and (
# 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['published'] = True
instance = tables[jsonName][1]()
# fix for issue 80
if jsonName is 'icons' and 'res:/ui/texture/icons/' in str(row['iconFile']).lower():
row['iconFile'] = row['iconFile'].lower().replace('res:/ui/texture/icons/', '').replace('.png', '')
# with res:/ui... references, it points to the actual icon file (including it's size variation of #_size_#)
# strip this info out and get the identifying info
split = row['iconFile'].split('_')
if len(split) == 3:
row['iconFile'] = '{}_{}'.format(split[0], split[2])
if jsonName is 'icons' and 'modules/' in str(row['iconFile']).lower():
row['iconFile'] = row['iconFile'].lower().replace('modules/', '').replace('.png', '')
if jsonName is 'clonegrades':
if row['alphaCloneID'] not in tmp:
cloneParent = eos.gamedata.AlphaClone()
setattr(cloneParent, 'alphaCloneID', row['alphaCloneID'])
setattr(cloneParent, 'alphaCloneName', row['alphaCloneName'])
eos.db.gamedata_session.add(cloneParent)
tmp.append(row['alphaCloneID'])
for k, v in row.items():
if isinstance(v, str):
v = v.strip()
setattr(instance, fieldMap.get(k, k), v)
eos.db.gamedata_session.add(instance)
# quick and dirty hack to get this data in
with open(os.path.join(JSON_DIR, 'fsd_binary', 'dynamicitemattributes.json'), encoding='utf-8') as f:
bulkdata = json.load(f)
for mutaID, data in bulkdata.items():
muta = eos.gamedata.DynamicItem()
muta.typeID = mutaID
muta.resultingTypeID = data['inputOutputMapping'][0]['resultingType']
eos.db.gamedata_session.add(muta)
for x in data['inputOutputMapping'][0]['applicableTypes']:
item = eos.gamedata.DynamicItemItem()
item.typeID = mutaID
item.applicableTypeID = x
eos.db.gamedata_session.add(item)
for attrID, attrData in data['attributeIDs'].items():
attr = eos.gamedata.DynamicItemAttribute()
attr.typeID = mutaID
attr.attributeID = attrID
attr.min = attrData['min']
attr.max = attrData['max']
eos.db.gamedata_session.add(attr)
eos.db.gamedata_session.flush()
processReqSkills(eveTypesData)
processReplacements(eveTypesData, eveGroupsData, dogmaTypeAttributesData, dogmaTypeEffectsData)
# Add schema version to prevent further updates
metadata_schema_version = eos.gamedata.MetaData()
@@ -436,7 +483,7 @@ def update_db():
metadata_schema_version.field_value = GAMEDATA_SCHEMA_VERSION
eos.db.gamedata_session.add(metadata_schema_version)
eos.db.gamedata_session.commit()
eos.db.gamedata_session.flush()
# CCP still has 5 subsystems assigned to T3Cs, even though only 4 are available / usable. They probably have some
# old legacy requirement or assumption that makes it difficult for them to change this value in the data. But for

View File

@@ -1,78 +0,0 @@
# This apes hook-matplotlib.backends.py, but REMOVES backends, all but
# the ones in the list below.
# Courtesy of https://github.com/bpteague/cytoflow/blob/70f9291/packaging/hook-matplotlib.backends.py
KEEP = ["WXAgg", "WX", "agg"]
from PyInstaller.compat import is_darwin
from PyInstaller.utils.hooks import (
eval_statement, exec_statement, logger)
def get_matplotlib_backend_module_names():
"""
List the names of all matplotlib backend modules importable under the
current Python installation.
Returns
----------
list
List of the fully-qualified names of all such modules.
"""
# Statement safely importing a single backend module.
import_statement = """
import os, sys
# Preserve stdout.
sys_stdout = sys.stdout
try:
# Redirect output printed by this importation to "/dev/null", preventing
# such output from being erroneously interpreted as an error.
with open(os.devnull, 'w') as dev_null:
sys.stdout = dev_null
__import__('%s')
# If this is an ImportError, print this exception's message without a traceback.
# ImportError messages are human-readable and require no additional context.
except ImportError as exc:
sys.stdout = sys_stdout
print(exc)
# Else, print this exception preceded by a traceback. traceback.print_exc()
# prints to stderr rather than stdout and must not be called here!
except Exception:
sys.stdout = sys_stdout
import traceback
print(traceback.format_exc())
"""
# List of the human-readable names of all available backends.
backend_names = eval_statement(
'import matplotlib; print(matplotlib.rcsetup.all_backends)')
# List of the fully-qualified names of all importable backend modules.
module_names = []
# If the current system is not OS X and the "CocoaAgg" backend is available,
# remove this backend from consideration. Attempting to import this backend
# on non-OS X systems halts the current subprocess without printing output
# or raising exceptions, preventing its reliable detection.
if not is_darwin and 'CocoaAgg' in backend_names:
backend_names.remove('CocoaAgg')
# For safety, attempt to import each backend in a unique subprocess.
for backend_name in backend_names:
if backend_name in KEEP:
continue
module_name = 'matplotlib.backends.backend_%s' % backend_name.lower()
stdout = exec_statement(import_statement % module_name)
# If no output was printed, this backend is importable.
if not stdout:
module_names.append(module_name)
logger.info(' Matplotlib backend "%s": removed' % backend_name)
return module_names
# Freeze all importable backends, as PyInstaller is unable to determine exactly
# which backends are required by the current program.
e=get_matplotlib_backend_module_names()
print(e)
excludedimports = e

View File

@@ -44,7 +44,9 @@ items_table = Table("invtypes", gamedata_meta,
Column("metaLevel", Integer),
Column("metaGroupID", Integer, ForeignKey("invmetagroups.metaGroupID"), index=True),
Column("variationParentTypeID", Integer, ForeignKey("invtypes.typeID"), index=True),
Column("replacements", String))
Column("replacements", String),
Column("reqskills", String),
Column("requiredfor", String))
from .traits import traits_table # noqa

View File

@@ -424,25 +424,3 @@ def getDynamicItem(itemID, eager=None):
except exc.NoResultFound:
result = None
return result
def getRequiredFor(itemID, attrMapping):
Attribute1 = aliased(Attribute)
Attribute2 = aliased(Attribute)
skillToLevelClauses = []
for attrSkill, attrLevel in attrMapping.items():
skillToLevelClauses.append(and_(Attribute1.attributeID == attrSkill, Attribute2.attributeID == attrLevel))
queryOr = or_(*skillToLevelClauses)
q = select((Attribute2.typeID, Attribute2.value),
and_(Attribute1.value == itemID, queryOr),
from_obj=[
join(Attribute1, Attribute2, Attribute1.typeID == Attribute2.typeID)
])
result = gamedata_session.execute(q).fetchall()
return result

View File

@@ -8,43 +8,25 @@ many upgrade files as there are database versions (version 5 would include
upgrade files 1-5)
"""
import pkgutil
import re
from eos.utils.pyinst_support import iterNamespace
updates = {}
appVersion = 0
prefix = __name__ + "."
# load modules to work based with and without pyinstaller
# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py
# see: https://github.com/pyinstaller/pyinstaller/issues/1905
# load modules using iter_modules()
# (should find all filters in normal build, but not pyinstaller)
module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)]
# special handling for PyInstaller
importers = map(pkgutil.get_importer, __path__)
toc = set()
for i in importers:
if hasattr(i, 'toc'):
toc |= i.toc
for elm in toc:
if elm.startswith(prefix):
module_names.append(elm)
for modname in module_names:
for modName in iterNamespace(__name__, __path__):
# loop through python files, extracting update number and function, and
# adding it to a list
modname_tail = modname.rsplit('.', 1)[-1]
module = __import__(modname, fromlist=True)
modname_tail = modName.rsplit('.', 1)[-1]
m = re.match("^upgrade(?P<index>\d+)$", modname_tail)
if not m:
continue
index = int(m.group("index"))
appVersion = max(appVersion, index)
module = __import__(modName, fromlist=True)
upgrade = getattr(module, "upgrade", False)
if upgrade:
updates[index] = upgrade

View File

@@ -280,6 +280,17 @@ class Effect51(BaseEffect):
fit.ship.multiplyItemAttr('rechargeRate', module.getModifiedItemAttr('capacitorRechargeRateMultiplier'), **kwargs)
class Effect54(BaseEffect):
"""
targetPassively
Used by:
Modules from group: Passive Targeting System (6 of 6)
"""
type = 'active'
class Effect55(BaseEffect):
"""
targetHostiles
@@ -1751,9 +1762,10 @@ class Effect598(BaseEffect):
ammoSpeedMultiplier
Used by:
Charges from group: Festival Charges (28 of 28)
Charges from group: Festival Charges (27 of 28)
Charges from group: Interdiction Probe (2 of 2)
Items from market group: Special Edition Assets > Special Edition Festival Assets (32 of 35)
Charges from group: Structure Festival Charges (2 of 2)
Special Edition Assetss from group: Festival Charges Expired (4 of 4)
"""
type = 'passive'
@@ -6808,7 +6820,10 @@ class Effect2298(BaseEffect):
scanStrengthBonusPercentPassive
Used by:
Implants named like: High grade (20 of 66)
Implants named like: High grade Grail (5 of 6)
Implants named like: High grade Jackal (5 of 6)
Implants named like: High grade Spur (5 of 6)
Implants named like: High grade Talon (5 of 6)
"""
type = 'passive'
@@ -9163,7 +9178,7 @@ class Effect3001(BaseEffect):
Used by:
Modules from group: Missile Launcher Torpedo (22 of 22)
Items from market group: Ship Equipment > Turrets & Bays (429 of 889)
Items from market group: Ship Equipment > Turrets & Launchers (429 of 889)
Module: Interdiction Sphere Launcher I
"""
@@ -12759,8 +12774,8 @@ class Effect4038(BaseEffect):
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
fit.modules.filteredItemMultiply(lambda mod: 'overloadECMStrenghtBonus' in mod.itemModifiedAttributes,
'overloadECMStrenghtBonus', module.getModifiedItemAttr('overloadBonusMultiplier'), **kwargs)
fit.modules.filteredItemMultiply(lambda mod: 'overloadECMStrengthBonus' in mod.itemModifiedAttributes,
'overloadECMStrengthBonus', module.getModifiedItemAttr('overloadBonusMultiplier'), **kwargs)
class Effect4039(BaseEffect):
@@ -27507,12 +27522,32 @@ class Effect6439(BaseEffect):
stackingPenalties=True, **kwargs)
class Effect6440(BaseEffect):
"""
fighterAbilityAfterburner
Used by:
Fighters named like: Shadow (2 of 2)
Fighters named like: Siren (4 of 4)
"""
displayName = 'Afterburner'
grouped = True
runTime = 'late'
type = 'active'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
module.boostItemAttr('maxVelocity', module.getModifiedItemAttr('fighterAbilityAfterburnerSpeedBonus'),
stackingPenalties=True, **kwargs)
class Effect6441(BaseEffect):
"""
fighterAbilityMicroWarpDrive
Used by:
Items from category: Fighter (48 of 82)
Items from category: Fighter (44 of 82)
"""
displayName = 'Microwarpdrive'
@@ -27913,7 +27948,7 @@ class Effect6488(BaseEffect):
Charges from group: Sensor Booster Script (3 of 3)
"""
type = 'active'
type = 'passive'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
@@ -36185,3 +36220,35 @@ class Effect7248(BaseEffect):
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill('Small Projectile Turret'),
'speed', ship.getModifiedItemAttr('shipBonusMF'), skill='Minmatar Frigate', **kwargs)
class Effect8011(BaseEffect):
"""
shieldHpBonusPostPercentHpLocationShip
Used by:
Implants named like: grade Nirvana (10 of 12)
"""
type = 'passive'
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
fit.ship.boostItemAttr('shieldCapacity', container.getModifiedItemAttr('shieldHpBonus'), **kwargs)
class Effect8013(BaseEffect):
"""
setBonusNirvana
Used by:
Implants named like: grade Nirvana (12 of 12)
"""
runTime = 'early'
type = 'passive'
@staticmethod
def handler(fit, implant, context, projectionRange, **kwargs):
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill('Cybernetics'),
'shieldHpBonus', implant.getModifiedItemAttr('ImplantSetNirvana') or 1, **kwargs)

View File

@@ -18,6 +18,7 @@
# ===============================================================================
import json
from collections import OrderedDict
from logbook import Logger
@@ -324,50 +325,26 @@ class Item(EqBase):
eos.db.saveddata_session.delete(override)
eos.db.commit()
srqIDMap = {182: 277, 183: 278, 184: 279, 1285: 1286, 1289: 1287, 1290: 1288}
@property
def requiredSkills(self):
if self.__requiredSkills is None:
requiredSkills = OrderedDict()
self.__requiredSkills = requiredSkills
# Map containing attribute IDs we may need for required skills
# { requiredSkillX : requiredSkillXLevel }
combinedAttrIDs = set(self.srqIDMap.keys()).union(set(self.srqIDMap.values()))
# Map containing result of the request
# { attributeID : attributeValue }
skillAttrs = {}
# Get relevant attribute values from db (required skill IDs and levels) for our item
for attrInfo in eos.db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
attrID = attrInfo[1]
attrVal = attrInfo[2]
skillAttrs[attrID] = attrVal
# Go through all attributeID pairs
for srqIDAtrr, srqLvlAttr in self.srqIDMap.items():
# Check if we have both in returned result
if srqIDAtrr in skillAttrs and srqLvlAttr in skillAttrs:
skillID = int(skillAttrs[srqIDAtrr])
skillLvl = skillAttrs[srqLvlAttr]
# Fetch item from database and fill map
item = eos.db.getItem(skillID)
requiredSkills[item] = skillLvl
self.__requiredSkills = {}
if self.reqskills:
for skillTypeID, skillLevel in json.loads(self.reqskills).items():
skillItem = eos.db.getItem(int(skillTypeID))
if skillItem:
self.__requiredSkills[skillItem] = skillLevel
return self.__requiredSkills
@property
def requiredFor(self):
if self.__requiredFor is None:
self.__requiredFor = dict()
# Map containing attribute IDs we may need for required skills
# Get relevant attribute values from db (required skill IDs and levels) for our item
q = eos.db.getRequiredFor(self.ID, self.srqIDMap)
for itemID, lvl in q:
# Fetch item from database and fill map
item = eos.db.getItem(itemID)
self.__requiredFor[item] = lvl
self.__requiredFor = {}
if self.requiredfor:
for typeID, skillLevel in json.loads(self.requiredfor).items():
requiredForItem = eos.db.getItem(int(typeID))
if requiredForItem:
self.__requiredFor[requiredForItem] = skillLevel
return self.__requiredFor
factionMap = {
@@ -410,6 +387,7 @@ class Item(EqBase):
9 : "guristas", # Caldari + Gallente
10 : "angelserp", # Minmatar + Gallente, final race depends on the order of skills
12 : "sisters", # Amarr + Gallente
15 : "concord",
16 : "jove",
32 : "sansha", # Incrusion Sansha
128: "ore",

View File

@@ -25,106 +25,118 @@ from sqlalchemy.orm import reconstructor
import eos.db
# Order is significant here - UI uses order as-is for built-in patterns
BUILTINS = OrderedDict([
(-1, ('Uniform', 25, 25, 25, 25)),
(-2, ('[Generic]EM', 1, 0, 0, 0)),
(-3, ('[Generic]Thermal', 0, 1, 0, 0)),
(-4, ('[Generic]Kinetic', 0, 0, 1, 0)),
(-5, ('[Generic]Explosive', 0, 0, 0, 1)),
(-6, ('[Bombs]Electron Bomb', 6400, 0, 0, 0)),
(-7, ('[Bombs]Scorch Bomb', 0, 6400, 0, 0)),
(-8, ('[Bombs]Concussion Bomb', 0, 0, 6400, 0)),
(-9, ('[Bombs]Shrapnel Bomb', 0, 0, 0, 6400)),
(-10, ('[Frequency Crystals]|[T2] Conflagration', 7.7, 7.7, 0, 0)),
(-11, ('[Frequency Crystals]|[T2] Scorch', 9, 2, 0, 0)),
(-12, ('[Frequency Crystals]|[T2] Gleam', 7, 7, 0, 0)),
(-13, ('[Frequency Crystals]|[T2] Aurora', 5, 3, 0, 0)),
(-14, ('[Frequency Crystals]Multifrequency', 7, 5, 0, 0)),
(-15, ('[Frequency Crystals]Gamma', 7, 4, 0, 0)),
(-16, ('[Frequency Crystals]Xray', 6, 4, 0, 0)),
(-17, ('[Frequency Crystals]Ultraviolet', 6, 3, 0, 0)),
(-18, ('[Frequency Crystals]Standard', 5, 3, 0, 0)),
(-19, ('[Frequency Crystals]Infrared', 5, 2, 0, 0)),
(-20, ('[Frequency Crystals]Microwave', 4, 2, 0, 0)),
(-21, ('[Frequency Crystals]Radio', 5, 0, 0, 0)),
(-22, ('[Hybrid Charges]|[T2] Void', 0, 7.7, 7.7, 0)),
(-23, ('[Hybrid Charges]|[T2] Null', 0, 6, 5, 0)),
(-24, ('[Hybrid Charges]|[T2] Javelin', 0, 8, 6, 0)),
(-25, ('[Hybrid Charges]|[T2] Spike', 0, 4, 4, 0)),
(-26, ('[Hybrid Charges]Antimatter', 0, 5, 7, 0)),
(-27, ('[Hybrid Charges]Plutonium', 0, 5, 6, 0)),
(-28, ('[Hybrid Charges]Uranium', 0, 4, 6, 0)),
(-29, ('[Hybrid Charges]Thorium', 0, 4, 5, 0)),
(-30, ('[Hybrid Charges]Lead', 0, 3, 5, 0)),
(-31, ('[Hybrid Charges]Iridium', 0, 3, 4, 0)),
(-32, ('[Hybrid Charges]Tungsten', 0, 2, 4, 0)),
(-33, ('[Hybrid Charges]Iron', 0, 2, 3, 0)),
(-34, ('[Missiles]Mjolnir', 1, 0, 0, 0)),
(-35, ('[Missiles]Inferno', 0, 1, 0, 0)),
(-36, ('[Missiles]Scourge', 0, 0, 1, 0)),
(-37, ('[Missiles]Nova', 0, 0, 0, 1)),
(-38, ('[Missiles]|[Structure] Standup Missile', 1, 1, 1, 1)),
(-39, ('[Projectile Ammo]|[T2] Hail', 0, 0, 3.3, 12.1)),
(-40, ('[Projectile Ammo]|[T2] Barrage', 0, 0, 5, 6)),
(-41, ('[Projectile Ammo]|[T2] Quake', 0, 0, 5, 9)),
(-42, ('[Projectile Ammo]|[T2] Tremor', 0, 0, 3, 5)),
(-43, ('[Projectile Ammo]EMP', 9, 0, 1, 2)),
(-44, ('[Projectile Ammo]Phased Plasma', 0, 10, 2, 0)),
(-45, ('[Projectile Ammo]Fusion', 0, 0, 2, 10)),
(-46, ('[Projectile Ammo]Depleted Uranium', 0, 3, 2, 3)),
(-47, ('[Projectile Ammo]Titanium Sabot', 0, 0, 6, 2)),
(-48, ('[Projectile Ammo]Proton', 3, 0, 2, 0)),
(-49, ('[Projectile Ammo]Carbonized Lead', 0, 0, 4, 1)),
(-50, ('[Projectile Ammo]Nuclear', 0, 0, 1, 4)),
(-6, ('[Frequency Crystals]|[T2] Aurora', 5, 3, 0, 0)),
(-7, ('[Frequency Crystals]|[T2] Scorch', 9, 2, 0, 0)),
(-8, ('[Frequency Crystals]Radio', 5, 0, 0, 0)),
(-9, ('[Frequency Crystals]Microwave', 4, 2, 0, 0)),
(-10, ('[Frequency Crystals]Infrared', 5, 2, 0, 0)),
(-11, ('[Frequency Crystals]Standard', 5, 3, 0, 0)),
(-12, ('[Frequency Crystals]Ultraviolet', 6, 3, 0, 0)),
(-13, ('[Frequency Crystals]Xray', 6, 4, 0, 0)),
(-14, ('[Frequency Crystals]Gamma', 7, 4, 0, 0)),
(-15, ('[Frequency Crystals]Multifrequency', 7, 5, 0, 0)),
(-16, ('[Frequency Crystals]|[T2] Gleam', 7, 7, 0, 0)),
(-17, ('[Frequency Crystals]|[T2] Conflagration', 7.7, 7.7, 0, 0)),
# Different sizes of plasma do different damage ratios, the values here
# are average of ratios across sizes
(-51, ('[Exotic Plasma]|[T2] Occult', 0, 55863, 0, 44137)),
(-52, ('[Exotic Plasma]|[T2] Mystic', 0, 66319, 0, 33681)),
(-53, ('[Exotic Plasma]Tetryon', 0, 69208, 0, 30792)),
(-54, ('[Exotic Plasma]Baryon', 0, 59737, 0, 40263)),
(-55, ('[Exotic Plasma]Meson', 0, 60519, 0, 39481)),
(-56, ('[NPC][Asteroid]Angel Cartel', 1838, 562, 2215, 3838)),
(-57, ('[NPC][Asteroid]Blood Raiders', 5067, 4214, 0, 0)),
(-58, ('[NPC][Asteroid]Guristas', 0, 1828, 7413, 0)),
(-59, ('[NPC][Asteroid]Rogue Drone', 394, 666, 1090, 1687)),
(-60, ('[NPC][Asteroid]Sanshas Nation', 5586, 4112, 0, 0)),
(-61, ('[NPC][Asteroid]Serpentis', 0, 5373, 4813, 0)),
(-62, ('[NPC][Deadspace]Angel Cartel', 369, 533, 1395, 3302)),
(-63, ('[NPC][Deadspace]Blood Raiders', 6040, 5052, 10, 15)),
(-64, ('[NPC][Deadspace]Guristas', 0, 1531, 9680, 0)),
(-65, ('[NPC][Deadspace]Rogue Drone', 276, 1071, 1069, 871)),
(-66, ('[NPC][Deadspace]Sanshas Nation', 3009, 2237, 0, 0)),
(-67, ('[NPC][Deadspace]Serpentis', 0, 3110, 1929, 0)),
(-68, ('[NPC][Mission]Amarr Empire', 4464, 3546, 97, 0)),
(-69, ('[NPC][Mission]Caldari State', 0, 2139, 4867, 0)),
(-70, ('[NPC][Mission]CONCORD', 336, 134, 212, 412)),
(-71, ('[NPC][Mission]Gallente Federation', 9, 3712, 2758, 0)),
(-72, ('[NPC][Mission]Khanid', 612, 483, 43, 6)),
(-73, ('[NPC][Mission]Minmatar Republic', 1024, 388, 1655, 4285)),
(-74, ('[NPC][Mission]Mordus Legion', 25, 262, 625, 0)),
(-75, ('[NPC][Mission]Thukker', 0, 52, 10, 79)),
(-76, ('[NPC][Burner]Cruor (Blood Raiders)', 90, 90, 0, 0)),
(-77, ('[NPC][Burner]Dramiel (Angel)', 55, 0, 20, 96)),
(-78, ('[NPC][Burner]Daredevil (Serpentis)', 0, 110, 154, 0)),
(-79, ('[NPC][Burner]Succubus (Sanshas Nation)', 135, 30, 0, 0)),
(-80, ('[NPC][Burner]Worm (Guristas)', 0, 0, 228, 0)),
(-81, ('[NPC][Burner]Enyo', 0, 147, 147, 0)),
(-82, ('[NPC][Burner]Hawk', 0, 0, 247, 0)),
(-83, ('[NPC][Burner]Jaguar', 36, 0, 50, 182)),
(-84, ('[NPC][Burner]Vengeance', 232, 0, 0, 0)),
(-85, ('[NPC][Burner]Ashimmu (Blood Raiders)', 260, 100, 0, 0)),
(-86, ('[NPC][Burner]Talos', 0, 413, 413, 0)),
(-87, ('[NPC][Burner]Sentinel', 0, 75, 0, 90)),
(-88, ('[NPC][Other]Sleepers', 1472, 1472, 1384, 1384)),
(-89, ('[NPC][Other]Sansha Incursion', 1682, 1347, 3678, 3678)),
(-18, ('[Exotic Plasma]|[T2] Mystic', 0, 66319, 0, 33681)),
(-19, ('[Exotic Plasma]Meson', 0, 60519, 0, 39481)),
(-20, ('[Exotic Plasma]Baryon', 0, 59737, 0, 40263)),
(-21, ('[Exotic Plasma]Tetryon', 0, 69208, 0, 30792)),
(-22, ('[Exotic Plasma]|[T2] Occult', 0, 55863, 0, 44137)),
(-23, ('[Hybrid Charges]|[T2] Spike', 0, 4, 4, 0)),
(-24, ('[Hybrid Charges]|[T2] Null', 0, 6, 5, 0)),
(-25, ('[Hybrid Charges]Iron', 0, 2, 3, 0)),
(-26, ('[Hybrid Charges]Tungsten', 0, 2, 4, 0)),
(-27, ('[Hybrid Charges]Iridium', 0, 3, 4, 0)),
(-28, ('[Hybrid Charges]Lead', 0, 3, 5, 0)),
(-29, ('[Hybrid Charges]Thorium', 0, 4, 5, 0)),
(-30, ('[Hybrid Charges]Uranium', 0, 4, 6, 0)),
(-31, ('[Hybrid Charges]Plutonium', 0, 5, 6, 0)),
(-32, ('[Hybrid Charges]Antimatter', 0, 5, 7, 0)),
(-33, ('[Hybrid Charges]|[T2] Javelin', 0, 8, 6, 0)),
(-34, ('[Hybrid Charges]|[T2] Void', 0, 7.7, 7.7, 0)),
(-35, ('[Projectile Ammo]|[T2] Tremor', 0, 0, 3, 5)),
(-36, ('[Projectile Ammo]|[T2] Barrage', 0, 0, 5, 6)),
(-37, ('[Projectile Ammo]Carbonized Lead', 0, 0, 4, 1)),
(-38, ('[Projectile Ammo]Nuclear', 0, 0, 1, 4)),
(-39, ('[Projectile Ammo]Proton', 3, 0, 2, 0)),
(-40, ('[Projectile Ammo]Depleted Uranium', 0, 3, 2, 3)),
(-41, ('[Projectile Ammo]Titanium Sabot', 0, 0, 6, 2)),
(-42, ('[Projectile Ammo]EMP', 9, 0, 1, 2)),
(-43, ('[Projectile Ammo]Phased Plasma', 0, 10, 2, 0)),
(-44, ('[Projectile Ammo]Fusion', 0, 0, 2, 10)),
(-45, ('[Projectile Ammo]|[T2] Quake', 0, 0, 5, 9)),
(-46, ('[Projectile Ammo]|[T2] Hail', 0, 0, 3.3, 12.1)),
(-47, ('[Missiles]Mjolnir', 1, 0, 0, 0)),
(-48, ('[Missiles]Inferno', 0, 1, 0, 0)),
(-49, ('[Missiles]Scourge', 0, 0, 1, 0)),
(-50, ('[Missiles]Nova', 0, 0, 0, 1)),
(-51, ('[Bombs]Electron Bomb', 6400, 0, 0, 0)),
(-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
(-90, ('[NPC][Invasion][Invading Precursor Entities]0% spool up', 31, 29, 24, 16)),
(-91, ('[NPC][Invasion][Invading Precursor Entities]50% spool up', 29, 37, 15, 19)),
(-92, ('[NPC][Invasion][Invading Precursor Entities]100% spool up', 28, 41, 11, 20)),
(-93, ('[NPC][Invasion]Retaliating Amarr Entities', 58, 42, 0, 0)),
(-94, ('[NPC][Invasion]Retaliating Caldari Entities', 30, 43, 2, 25)),
(-95, ('[NPC][Invasion]Retaliating Gallente Entities', 0, 42, 58, 0)),
(-96, ('[NPC][Invasion]Retaliating Minmatar Entities', 17, 8, 44, 31))])
(-55, ('[NPC][Abyssal]All', 130, 396, 258, 216)),
(-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)),
(-60, ('[NPC][Abyssal]Sleeper', 313, 313, 187, 187)),
(-61, ('[NPC][Abyssal]Triglavian', 0, 610, 0, 390)),
(-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)),
(-65, ('[NPC][Asteroid]Rogue Drone', 394, 666, 1090, 1687)),
(-66, ('[NPC][Asteroid]Sanshas Nation', 5586, 4112, 0, 0)),
(-67, ('[NPC][Asteroid]Serpentis', 0, 5373, 4813, 0)),
(-68, ('[NPC][Burner]Cruor (Blood Raiders)', 90, 90, 0, 0)),
(-69, ('[NPC][Burner]Dramiel (Angel)', 55, 0, 20, 96)),
(-70, ('[NPC][Burner]Daredevil (Serpentis)', 0, 110, 154, 0)),
(-71, ('[NPC][Burner]Succubus (Sanshas Nation)', 135, 30, 0, 0)),
(-72, ('[NPC][Burner]Worm (Guristas)', 0, 0, 228, 0)),
(-73, ('[NPC][Burner]Enyo', 0, 147, 147, 0)),
(-74, ('[NPC][Burner]Hawk', 0, 0, 247, 0)),
(-75, ('[NPC][Burner]Jaguar', 36, 0, 50, 182)),
(-76, ('[NPC][Burner]Vengeance', 232, 0, 0, 0)),
(-77, ('[NPC][Burner]Ashimmu (Blood Raiders)', 260, 100, 0, 0)),
(-78, ('[NPC][Burner]Talos', 0, 413, 413, 0)),
(-79, ('[NPC][Burner]Sentinel', 0, 75, 0, 90)),
(-80, ('[NPC][Deadspace]Angel Cartel', 369, 533, 1395, 3302)),
(-81, ('[NPC][Deadspace]Blood Raiders', 6040, 5052, 10, 15)),
(-82, ('[NPC][Deadspace]Guristas', 0, 1531, 9680, 0)),
(-83, ('[NPC][Deadspace]Rogue Drone', 276, 1071, 1069, 871)),
(-84, ('[NPC][Deadspace]Sanshas Nation', 3009, 2237, 0, 0)),
(-85, ('[NPC][Deadspace]Serpentis', 0, 3110, 1929, 0)),
# Source: ticket #2067
(-86, ('[NPC][Invasion][Invading Precursor Entities]Dread', 0, 417, 0, 583)),
(-87, ('[NPC][Invasion][Invading Precursor Entities]Normal Subcaps', 0, 610, 0, 390)),
(-88, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 0% spool up', 363, 160, 363, 115)),
(-89, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 50% spool up', 286, 249, 286, 179)),
(-90, ('[NPC][Invasion][Invading Precursor Entities]Subcaps w/missiles 100% spool up', 236, 307, 236, 221)),
(-91, ('[NPC][Invasion][Retaliating Amarr Entities]Dread/Subcaps', 583, 417, 0, 0)),
(-92, ('[NPC][Invasion][Retaliating Caldari Entities]Dread', 1000, 0, 0, 0)),
(-93, ('[NPC][Invasion][Retaliating Caldari Entities]Subcaps', 511, 21, 29, 439)),
(-94, ('[NPC][Invasion][Retaliating Gallente Entities]Dread/Subcaps', 0, 417, 583, 0)),
(-95, ('[NPC][Invasion][Retaliating Minmatar Entities]Dread', 0, 0, 583, 417)),
(-96, ('[NPC][Invasion][Retaliating Minmatar Entities]Subcaps', 302, 132, 330, 236)),
(-97, ('[NPC][Mission]Amarr Empire', 4464, 3546, 97, 0)),
(-98, ('[NPC][Mission]Caldari State', 0, 2139, 4867, 0)),
(-99, ('[NPC][Mission]CONCORD', 336, 134, 212, 412)),
(-100, ('[NPC][Mission]Gallente Federation', 9, 3712, 2758, 0)),
(-101, ('[NPC][Mission]Khanid', 612, 483, 43, 6)),
(-102, ('[NPC][Mission]Minmatar Republic', 1024, 388, 1655, 4285)),
(-103, ('[NPC][Mission]Mordus Legion', 25, 262, 625, 0)),
(-104, ('[NPC][Mission]Thukker', 0, 52, 10, 79)),
(-105, ('[NPC]Sansha Incursion', 1682, 1347, 3678, 3678)),
(-106, ('[NPC]Sleepers', 1472, 1472, 1384, 1384))])
class DamagePattern:

View File

@@ -99,7 +99,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__chargeModifiedAttributes = ModifiedAttributeDict()
if len(self.abilities) != len(self.item.effects):
if {a.effectID for a in self.abilities} != {e.ID for e in self.item.effects.values()}:
self.__abilities = []
for ability in self.__getAbilities():
self.__abilities.append(ability)

View File

@@ -165,6 +165,9 @@ class Fit:
self.__capUsed = None
self.__capRecharge = None
self.__savedCapSimData.clear()
# Ancillary tank modules affect this
self.__sustainableTank = None
self.__effectiveSustainableTank = None
@property
def targetProfile(self):

View File

@@ -84,11 +84,102 @@ BUILTINS = OrderedDict([
(-50, ('[NPC][Burner]Talos', 0.68, 0.59, 0.59, 0.43)),
(-51, ('[NPC][Burner]Sentinel', 0.58, 0.45, 0.52, 0.66)),
# Source: ticket #2067
(-52, ('[NPC][Invasion]Invading Precursor Entities', 0.46, 0.39, 0.48, 0.42)),
(-53, ('[NPC][Invasion]Retaliating Amarr Entities', 0.36, 0.31, 0.44, 0.60)),
(-54, ('[NPC][Invasion]Retaliating Caldari Entities', 0.28, 0.61, 0.48, 0.39)),
(-55, ('[NPC][Invasion]Retaliating Gallente Entities', 0.36, 0.39, 0.56, 0.50)),
(-56, ('[NPC][Invasion]Retaliating Minmatar Entities', 0.62, 0.42, 0.35, 0.40))])
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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)),
(-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))])
class TargetProfile:

View File

@@ -0,0 +1,39 @@
"""
Slightly modified version of function taken from here:
https://github.com/pyinstaller/pyinstaller/issues/1905#issuecomment-525221546
"""
import pkgutil
def iterNamespace(name, path):
"""Pyinstaller-compatible namespace iteration.
Yields the name of all modules found at a given Fully-qualified path.
To have it running with pyinstaller, it requires to ensure a hook inject the
"hidden" modules from your plugins folder inside the executable:
- if your plugins are under the ``myappname/pluginfolder`` module
- create a file ``specs/hook-<myappname.pluginfolder>.py``
- content of this file should be:
.. code-block:: python
from PyInstaller.utils.hooks import collect_submodules
hiddenimports = collect_submodules('<myappname.pluginfolder>')
"""
prefix = name + "."
for p in pkgutil.iter_modules(path, prefix):
yield p[1]
# special handling when the package is bundled with PyInstaller 3.5
# See https://github.com/pyinstaller/pyinstaller/issues/1905#issuecomment-445787510
toc = set()
for importer in pkgutil.iter_importers(name.partition(".")[0]):
if hasattr(importer, 'toc'):
toc |= importer.toc
for name in toc:
if name.startswith(prefix):
yield name

View File

@@ -299,7 +299,10 @@ class ImplantDisplay(d.Display):
sourceContext1 = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar"
sourceContext2 = "implantItemMisc" if fit.implantSource == ImplantLocation.FIT else "implantItemMiscChar"
itemContext = None if mainImplant is None else Market.getInstance().getCategoryByItem(mainImplant.item).name
menu = ContextMenu.getMenu(self, mainImplant, selection, (sourceContext1, itemContext), (sourceContext2, itemContext))
menu = ContextMenu.getMenu(self, mainImplant, selection,
(sourceContext1, itemContext),
(sourceContext2, itemContext)
)
if menu:
self.PopupMenu(menu)

View File

@@ -37,7 +37,8 @@ from gui.builtinContextMenus import cargoAdd
from gui.builtinContextMenus import cargoAddAmmo
from gui.builtinContextMenus import itemProject
from gui.builtinContextMenus import ammoToDmgPattern
from gui.builtinContextMenus import implantSetAdd
from gui.builtinContextMenus import implantSetApply
from gui.builtinContextMenus import implantSetSave
# Price
from gui.builtinContextMenus import priceOptions
# Resistance panel

View File

@@ -3,7 +3,6 @@ from gui.contextMenu import ContextMenuUnconditional
from gui.utils.clipboard import toClipboard
from service.fit import Fit
from service.port.eft import exportDrones, exportFighters, exportCargo, exportImplants, exportBoosters
from service.settings import ContextMenuSettings
viewSpecMap = {
@@ -17,12 +16,12 @@ viewSpecMap = {
class AdditionsExportAll(ContextMenuUnconditional):
visibilitySetting = 'additionsCopyPaste'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, callingWindow, srcContext):
if not ContextMenuSettings.getInstance().get('additionsCopyPaste'):
return False
if srcContext not in viewSpecMap:
return False
fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit())

View File

@@ -3,7 +3,6 @@ from gui.contextMenu import ContextMenuSelection
from gui.utils.clipboard import toClipboard
from service.fit import Fit
from service.port.eft import exportDrones, exportFighters, exportCargo, exportImplants, exportBoosters
from service.settings import ContextMenuSettings
viewSpecMap = {
@@ -17,12 +16,12 @@ viewSpecMap = {
class AdditionsExportAll(ContextMenuSelection):
visibilitySetting = 'additionsCopyPaste'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, callingWindow, srcContext, selection):
if not ContextMenuSettings.getInstance().get('additionsCopyPaste'):
return False
if srcContext not in viewSpecMap:
return False
if not selection:

View File

@@ -4,7 +4,6 @@ from gui.contextMenu import ContextMenuUnconditional
from gui.utils.clipboard import fromClipboard
from service.fit import Fit
from service.port.eft import parseAdditions
from service.settings import ContextMenuSettings
viewSpecMap = {
@@ -18,12 +17,12 @@ viewSpecMap = {
class AdditionsImport(ContextMenuUnconditional):
visibilitySetting = 'additionsCopyPaste'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, callingWindow, srcContext):
if not ContextMenuSettings.getInstance().get('additionsCopyPaste'):
return False
if srcContext not in viewSpecMap:
return False
fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit())

View File

@@ -5,19 +5,16 @@ import gui.globalEvents as GE
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class AmmoToDmgPattern(ContextMenuSingle):
visibilitySetting = 'ammoPattern'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, callingWindow, srcContext, mainItem):
if not self.settings.get('ammoPattern'):
return False
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False

View File

@@ -19,8 +19,8 @@ class AddToCargo(ContextMenuSingle):
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
# Make sure context menu registers in the correct view
if not fit or fit.isStructure:
if not fit or (fit.isStructure and mainItem.category.ID != 8):
return False
return True

View File

@@ -30,13 +30,15 @@ class ChangeDamagePattern(ContextMenuUnconditional):
fitID = self.mainFrame.getActiveFit()
self.fit = sFit.getFit(fitID)
builtinPatterns = sDP.getBuiltinDamagePatternList()
userPatterns = sorted(sDP.getUserDamagePatternList(), key=lambda p: smartSort(p.fullName))
# Order here is important: patterns with duplicate names from the latter will overwrite
# patterns from the former
self.patterns = list(chain(sDP.getBuiltinDamagePatternList(), sDP.getUserDamagePatternList()))
self.patterns.sort(key=lambda p: (p.fullName not in ["Uniform", "Selected Ammo"], smartSort(p.fullName)))
self.patterns = sorted(
chain(builtinPatterns, userPatterns),
key=lambda p: p.fullName not in ["Uniform", "Selected Ammo"])
self.patternEventMap = {}
self.items = (OrderedDict(), OrderedDict())
for pattern in self.patterns:
container = self.items

View File

@@ -5,7 +5,7 @@ from gui.contextMenu import ContextMenuUnconditional
from service.implantSet import ImplantSets as s_ImplantSets
class AddImplantSet(ContextMenuUnconditional):
class ImplantSetApply(ContextMenuUnconditional):
def display(self, callingWindow, srcContext):
@@ -14,10 +14,11 @@ class AddImplantSet(ContextMenuUnconditional):
if len(implantSets) == 0:
return False
return srcContext in ("implantItemMisc", "implantEditor")
def getText(self, callingWindow, itmContext):
return "Add Implant Set"
def getText(self, callingWindow, context):
return "Apply Implant Set"
def getSubMenu(self, callingWindow, context, rootMenu, i, pitem):
m = wx.Menu()
@@ -49,4 +50,4 @@ class AddImplantSet(ContextMenuUnconditional):
self.callingWindow.addImplantSet(impSet)
AddImplantSet.register()
ImplantSetApply.register()

View File

@@ -0,0 +1,77 @@
import wx
import gui.mainFrame
from gui.contextMenu import ContextMenuUnconditional
from service.fit import Fit
class ImplantSetSave(ContextMenuUnconditional):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, callingWindow, srcContext):
if srcContext not in ('implantItemMisc', 'implantItemMiscChar'):
return False
fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit())
self.implants = fit.appliedImplants[:]
if not self.implants:
return False
return True
def getText(self, callingWindow, context):
return 'Save as New Implant Set'
def activate(self, callingWindow, fullContext, i):
with NameDialog(self.mainFrame, '') as dlg:
if dlg.ShowModal() == wx.ID_OK:
name = dlg.input.GetLineText(0).strip()
if name == '':
return
from gui.setEditor import ImplantSetEditor
ImplantSetEditor.openOne(parent=self.mainFrame, dataToAdd=(name, self.implants))
ImplantSetSave.register()
class NameDialog(wx.Dialog):
def __init__(self, parent, value):
super().__init__(parent, title='New Implant Set', style=wx.DEFAULT_DIALOG_STYLE)
self.SetMinSize((346, 156))
bSizer1 = wx.BoxSizer(wx.VERTICAL)
bSizer2 = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, wx.ID_ANY, 'Enter a name for your new Implant Set:')
bSizer2.Add(text, 0)
bSizer1.Add(bSizer2, 0, wx.ALL, 10)
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
if value is None:
value = ''
else:
value = str(value)
self.input.SetValue(value)
self.input.SelectAll()
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
bSizer3 = wx.BoxSizer(wx.VERTICAL)
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 15)
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
self.input.SetFocus()
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
self.SetSizer(bSizer1)
self.CenterOnParent()
self.Fit()
def processEnter(self, evt):
self.EndModal(wx.ID_OK)

View File

@@ -1,19 +1,16 @@
import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
from service.settings import ContextMenuSettings
class FillWithItem(ContextMenuSingle):
visibilitySetting = 'moduleFill'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, callingWindow, srcContext, mainItem):
if not self.settings.get('moduleFill'):
return False
if srcContext not in ('marketItemGroup', 'marketItemMisc'):
return False

View File

@@ -2,19 +2,16 @@ import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class ProjectItem(ContextMenuSingle):
visibilitySetting = 'project'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, callingWindow, srcContext, mainItem):
if not self.settings.get('project'):
return False
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False

View File

@@ -7,19 +7,16 @@ from gui.contextMenu import ContextMenuCombined
from gui.fitCommands.helpers import getSimilarModPositions, getSimilarFighters
from service.fit import Fit
from service.market import Market
from service.settings import ContextMenuSettings
class ChangeItemToVariation(ContextMenuCombined):
visibilitySetting = 'metaSwap'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, callingWindow, srcContext, mainItem, selection):
if not self.settings.get('metaSwap'):
return False
if self.mainFrame.getActiveFit() is None or srcContext not in (
'fittingModule',
'droneItem',
@@ -59,8 +56,11 @@ class ChangeItemToVariation(ContextMenuCombined):
return x.metaLevel or 0
def get_metagroup(x):
# We want deadspace before officer mods
remap = {5: 6, 6: 5}
remap = {
# We want deadspace before officer mods
5: 6, 6: 5,
# For structures we want t1-t2-faction
54: 52, 52: 54}
metaGroup = sMkt.getMetaGroupByItem(x)
return remap.get(metaGroup.ID, metaGroup.ID) if metaGroup is not None else 0

View File

@@ -2,20 +2,16 @@ import gui.fitCommands as cmd
import gui.mainFrame
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class FillWithModule(ContextMenuSingle):
visibilitySetting = 'moduleFill'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, callingWindow, srcContext, mainItem):
if not self.settings.get('moduleFill'):
return False
if mainItem is None or getattr(mainItem, 'isEmpty', False):
return False

View File

@@ -9,20 +9,17 @@ import gui.mainFrame
from eos.utils.spoolSupport import SpoolType, SpoolOptions
from gui.contextMenu import ContextMenuSingle
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeModuleSpool(ContextMenuSingle):
visibilitySetting = 'spoolup'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
self.resetId = None
def display(self, callingWindow, srcContext, mainItem):
if not self.settings.get('spoolup'):
return False
if srcContext not in ('fittingModule', 'projectedModule') or self.mainFrame.getActiveFit() is None:
return False

View File

@@ -8,19 +8,16 @@ from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenuSingle
from service.character import Character
from service.fit import Fit
from service.settings import ContextMenuSettings
class ChangeAffectingSkills(ContextMenuSingle):
visibilitySetting = 'changeAffectingSkills'
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, callingWindow, srcContext, mainItem):
if not self.settings.get('changeAffectingSkills'):
return False
if srcContext not in (
"fittingModule", "fittingCharge",
"fittingShip", "droneItem",

View File

@@ -10,12 +10,13 @@ _ValueChanged, EVT_VALUE_CHANGED = wx.lib.newevent.NewEvent()
class AttributeSliderChangeEvent:
def __init__(self, obj, old_value, new_value, old_percentage, new_percentage):
def __init__(self, obj, old_value, new_value, old_percentage, new_percentage, affect_modified_flag=True):
self.__obj = obj
self.__old = old_value
self.__new = new_value
self.__old_percent = old_percentage
self.__new_percent = new_percentage
self.__affect_modified_flag = affect_modified_flag
def GetObj(self):
return self.__obj
@@ -32,6 +33,10 @@ class AttributeSliderChangeEvent:
def GetPercentage(self):
return self.__new_percent
@property
def AffectsModifiedFlag(self):
return self.__affect_modified_flag
Object = property(GetObj)
OldValue = property(GetOldValue)
Value = property(GetValue)
@@ -40,9 +45,9 @@ class AttributeSliderChangeEvent:
class ValueChanged(_ValueChanged, AttributeSliderChangeEvent):
def __init__(self, obj, old_value, new_value, old_percentage, new_percentage):
def __init__(self, obj, old_value, new_value, old_percentage, new_percentage, affect_modified_flag=True):
_ValueChanged.__init__(self)
AttributeSliderChangeEvent.__init__(self, obj, old_value, new_value, old_percentage, new_percentage)
AttributeSliderChangeEvent.__init__(self, obj, old_value, new_value, old_percentage, new_percentage, affect_modified_flag=affect_modified_flag)
class AttributeSlider(wx.Panel):
@@ -118,7 +123,7 @@ class AttributeSlider(wx.Panel):
self.SetValue(self.GetValue())
evt.Skip()
def SetValue(self, value, post_event=True):
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:
@@ -127,7 +132,7 @@ class AttributeSlider(wx.Panel):
slider_percentage = (value - self.base_value) / (self.base_value - self.UserMinValue) * 100 * invert_factor
self.slider.SetValue(slider_percentage)
if post_event:
wx.PostEvent(self, ValueChanged(self, None, value, None, slider_percentage))
wx.PostEvent(self, ValueChanged(self, None, value, None, slider_percentage, affect_modified_flag=affect_modified_flag))
def OnMouseWheel(self, evt):
if evt.GetWheelRotation() > 0 and evt.GetWheelAxis() == wx.MOUSE_WHEEL_VERTICAL:

View File

@@ -76,6 +76,7 @@ class ItemMutatorList(wx.ScrolledWindow):
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
self.mod = mod
self.timer = None
self.isModified = False
goodColor = wx.Colour(96, 191, 0)
badColor = wx.Colour(255, 64, 0)
@@ -171,6 +172,8 @@ class ItemMutatorList(wx.ScrolledWindow):
self.SetSizer(sizer)
def changeMutatedValue(self, evt):
if evt.AffectsModifiedFlag:
self.isModified = True
m = self.event_mapping[evt.Object]
value = evt.Value
value = m.attribute.unit.ComplicateValue(value)
@@ -188,29 +191,32 @@ class ItemMutatorList(wx.ScrolledWindow):
self.timer = wx.CallLater(1000, self.callLater)
def resetMutatedValues(self, evt):
self.isModified = True
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
value = sFit.changeMutatedValuePrelim(m, m.baseValue)
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
def randomMutatedValues(self, evt):
self.isModified = True
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
value = random.uniform(m.minValue, m.maxValue)
value = sFit.changeMutatedValuePrelim(m, value)
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
def revertChanges(self, evt):
self.isModified = False
sFit = Fit.getInstance()
for slider, m in self.event_mapping.items():
if m.attrID in self.initialMutations:
value = sFit.changeMutatedValuePrelim(m, self.initialMutations[m.attrID])
value = m.attribute.unit.SimplifyValue(value)
slider.SetValue(value)
slider.SetValue(value, affect_modified_flag=False)
evt.Skip()
def OnWindowClose(self):
@@ -218,15 +224,18 @@ class ItemMutatorList(wx.ScrolledWindow):
sFit = Fit.getInstance()
fit = sFit.getFit(self.carryingFitID)
if self.mod in fit.modules:
currentMutation = {}
for slider, m in self.event_mapping.items():
# Sliders may have more up-to-date info than mutator in case we changed
# value in slider and without confirming it, decided to close window
value = slider.GetValue()
value = m.attribute.unit.ComplicateValue(value)
if value != m.value:
value = sFit.changeMutatedValuePrelim(m, value)
currentMutation[m.attrID] = value
if self.isModified:
currentMutation = {}
for slider, m in self.event_mapping.items():
# Sliders may have more up-to-date info than mutator in case we changed
# value in slider and without confirming it, decided to close window
value = slider.GetValue()
value = m.attribute.unit.ComplicateValue(value)
if value != m.value:
value = sFit.changeMutatedValuePrelim(m, value)
currentMutation[m.attrID] = value
else:
currentMutation = self.initialMutations
mainFrame = gui.mainFrame.MainFrame.getInstance()
mainFrame.getCommandForFit(self.carryingFitID).Submit(cmd.GuiChangeLocalModuleMutationCommand(
fitID=self.carryingFitID,

View File

@@ -21,9 +21,12 @@ class PFContextMenuPref(PreferenceView):
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.EXPAND | wx.ALL, 5)
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY,
"Disabling context menus can improve responsiveness.",
wx.DefaultPosition, wx.DefaultSize, 0)
self.stSubTitle = wx.StaticText(
panel, wx.ID_ANY,
'Disabling context menus can improve responsiveness.\n'
'You can hold {} key + right-click to show all menu items regardless of these settings.'.format(
'Command' if 'wxMac' in wx.PlatformInfo else 'Control'),
wx.DefaultPosition, wx.DefaultSize, 0)
self.stSubTitle.Wrap(-1)
mainSizer.Add(self.stSubTitle, 0, wx.ALL, 5)

View File

@@ -21,7 +21,7 @@
import wx
from gui.statsView import StatsView
from gui.bitmap_loader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
from gui.utils.numberFormatter import formatAmount, roundToPrec
class CapacitorViewFull(StatsView):
@@ -139,14 +139,14 @@ class CapacitorViewFull(StatsView):
lines.append(' +{} GJ/s'.format(formatAmount(cap_recharge, 3, 0, 3)))
lines.append(' -{} GJ/s'.format(formatAmount(cap_use, 3, 0, 3)))
delta = round(cap_recharge - cap_use, 3)
if delta > 0 and neut_res < 1:
if delta > 0 and 0 < round(neut_res, 4) < 1:
lines.append('')
lines.append('Effective excessive gain:')
lines.append(' +{} GJ/s'.format(formatAmount(delta / neut_res, 3, 0, 3)))
label.SetToolTip(wx.ToolTip('\n'.join(lines)))
if labelName == 'label%sCapacitorResist':
texts = ['Neutralizer resistance']
if cap_amount > 0 and neut_res < 1:
if cap_amount > 0 and 0 < round(neut_res, 4) < 1:
texts.append('Effective capacity: {} GJ'.format(formatAmount(cap_amount / neut_res, 3, 0, 9)))
label.SetToolTip(wx.ToolTip('\n'.join(texts)))

View File

@@ -339,23 +339,25 @@ class Miscellanea(ViewColumn):
formatAmount(radar, 3, 0, 3),
)
return text, tooltip
elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier"):
elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier", "Structure Signal Amplifier"):
textLines = []
tooltipLines = []
scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus")
if scanResBonus:
textLines.append("{}%".format(formatAmount(scanResBonus, 3, 0, 3)))
tooltipLines.append("{}% scan resolution".format(formatAmount(scanResBonus, 3, 0, 3)))
lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus")
if lockRangeBonus:
textLines.append("{}%".format(formatAmount(lockRangeBonus, 3, 0, 3)))
tooltipLines.append("{}% lock range".format(formatAmount(lockRangeBonus, 3, 0, 3)))
gravBonus = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent")
if scanResBonus is None or lockRangeBonus is None or gravBonus is None:
if gravBonus:
textLines.append("{}%".format(formatAmount(gravBonus, 3, 0, 3)))
tooltipLines.append("{}% sensor strength".format(formatAmount(gravBonus, 3, 0, 3)))
if not textLines:
return "", None
text = "{0}% | {1}% | {2}%".format(
formatAmount(scanResBonus, 3, 0, 3),
formatAmount(lockRangeBonus, 3, 0, 3),
formatAmount(gravBonus, 3, 0, 3),
)
tooltip = "Applied bonuses:\n{0}% scan resolution | {1}% lock range | {2}% sensor strength".format(
formatAmount(scanResBonus, 3, 0, 3),
formatAmount(lockRangeBonus, 3, 0, 3),
formatAmount(gravBonus, 3, 0, 3),
)
text = " | ".join(textLines)
tooltip = "Applied bonuses:\n{}".format(" | ".join(tooltipLines))
return text, tooltip
elif itemGroup in ("Projected ECCM", "ECCM", "Sensor Backup Array"):
grav = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent")
@@ -588,7 +590,7 @@ class Miscellanea(ViewColumn):
):
if "Armor" in itemGroup or "Shield" in itemGroup:
boosted_attribute = "HP"
reload_time = item.getAttribute("reloadTime", 0) / 1000
reload_time = stuff.getModifiedItemAttr("reloadTime", 0) / 1000
elif "Capacitor" in itemGroup:
boosted_attribute = "Cap"
reload_time = 10

View File

@@ -178,10 +178,17 @@ class EntityEditor(wx.Panel):
return True
def checkEntitiesExist(self):
if len(self.choices) == 0:
self.Parent.Hide()
if self.OnNew(None) is False:
return False
self.Parent.Show()
if len(self.choices) > 0:
return True
else:
return self.enterNewEntity()
def enterNewEntity(self):
self.Parent.Hide()
if self.OnNew(None) is False:
return False
self.Parent.Show()
return True

View File

@@ -23,6 +23,8 @@ from abc import ABCMeta, abstractmethod
import wx
from logbook import Logger
from service.settings import ContextMenuSettings
pyfalog = Logger(__name__)
@@ -32,6 +34,7 @@ class ContextMenu(metaclass=ABCMeta):
menus = []
_ids = [] # [wx.NewId() for x in xrange(200)] # init with decent amount
_idxid = -1
visibilitySetting = None
@classmethod
def register(cls):
@@ -72,6 +75,10 @@ class ContextMenu(metaclass=ABCMeta):
ContextMenu._idxid = -1
debug_start = len(ContextMenu._ids)
# Control being pressed forces all hidden menu items to be shown
visibilitySettingOverride = wx.GetMouseState().GetModifiers() == wx.MOD_CONTROL
cmSettings = ContextMenuSettings.getInstance()
rootMenu = wx.Menu()
rootMenu.info = {}
rootMenu.selection = (selection,) if not hasattr(selection, "__iter__") else selection
@@ -87,7 +94,11 @@ class ContextMenu(metaclass=ABCMeta):
for menuHandler in cls.menus:
# loop through registered menus
m = menuHandler()
if m._baseDisplay(callingWindow, srcContext, mainItem, selection):
if m.visibilitySetting:
visible = visibilitySettingOverride or cmSettings.get(m.visibilitySetting)
else:
visible = True
if visible and m._baseDisplay(callingWindow, srcContext, mainItem, selection):
display_amount += 1
texts = m._baseGetText(callingWindow, itemContext, mainItem, selection)

View File

@@ -321,7 +321,10 @@ def activeStateLimit(itemIdentity):
item = Market.getInstance().getItem(itemIdentity)
if {
'moduleBonusAssaultDamageControl', 'moduleBonusIndustrialInvulnerability',
'microJumpDrive', 'microJumpPortalDrive', 'emergencyHullEnergizer'
'microJumpDrive', 'microJumpPortalDrive', 'emergencyHullEnergizer',
'cynosuralGeneration', 'jumpPortalGeneration', 'jumpPortalGenerationBO',
'cloneJumpAccepting', 'cloakingWarpSafe', 'cloakingPrototype', 'cloaking',
'massEntanglerEffect5', 'electronicAttributeModifyOnline', 'targetPassively'
}.intersection(item.effects):
return FittingModuleState.ONLINE
return FittingModuleState.ACTIVE

View File

@@ -47,7 +47,7 @@ class ImplantTextValidor(BaseValidator):
if len(text) == 0:
raise ValueError("You must supply a name for the Implant Set!")
elif text in [x.name for x in entityEditor.choices]:
raise ValueError("Imlplant Set name already in use, please choose another.")
raise ValueError("Implant Set name already in use, please choose another.")
return True
except ValueError as e:
@@ -106,7 +106,7 @@ class ImplantSetEditorView(BaseImplantEditorView):
sIS = ImplantSets.getInstance()
set_ = self.Parent.entityEditor.getActiveEntity()
sIS.addImplant(set_.ID, item.ID)
sIS.addImplants(set_.ID, item.ID)
def removeImplantFromContext(self, implant):
sIS = ImplantSets.getInstance()
@@ -117,7 +117,7 @@ class ImplantSetEditorView(BaseImplantEditorView):
class ImplantSetEditor(AuxiliaryFrame):
def __init__(self, parent):
def __init__(self, parent, dataToAdd=None):
super().__init__(
parent, id=wx.ID_ANY, title="Implant Set Editor", resizeable=True,
size=wx.Size(950, 500) if "wxGTK" in wx.PlatformInfo else wx.Size(850, 420))
@@ -166,7 +166,13 @@ class ImplantSetEditor(AuxiliaryFrame):
self.SetSizer(mainSizer)
self.Layout()
if not self.entityEditor.checkEntitiesExist():
if dataToAdd:
name, implants = dataToAdd
newSet = self.entityEditor.DoNew(name)
ImplantSets.getInstance().addImplants(newSet.ID, *[i.item.ID for i in implants])
self.entityEditor.refreshEntityList(newSet)
wx.PostEvent(self.entityEditor.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))
elif not self.entityEditor.checkEntitiesExist():
self.Close()
return

View File

@@ -189,7 +189,7 @@ class ShipBrowser(wx.Panel):
RACE_ORDER = [
"amarr", "caldari", "gallente", "minmatar",
"sisters", "ore",
"sisters", "ore", "concord",
"serpentis", "angel", "blood", "sansha", "guristas", "mordu",
"jove", "upwell", "triglavian", None
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,4 +1,4 @@
wxPython == 4.0.6
wxPython == 4.0.7.post2
logbook >= 1.0.0
matplotlib == 3.0.3
numpy==1.16.4
@@ -11,4 +11,3 @@ packaging == 16.8
roman == 2.0.0
beautifulsoup4 == 4.6.0
pyyaml >= 5.1
PyInstaller == 3.3

View File

@@ -5,3 +5,4 @@ sudo python3 -m ensurepip --upgrade
# A manual check that the correct version of Python is running.
python3 --version
python3 -m pip install -r requirements.txt
python3 -m pip install PyInstaller

View File

@@ -7,37 +7,17 @@ item's name. The name of the file is usually arbitrary unless it's used in logic
elsewhere (in which case can be accessed with packs[name])
"""
import pkgutil
from eos.utils.pyinst_support import iterNamespace
# init parent dict
all = {}
# init container to store the separate conversion packs in case we need them
packs = {}
prefix = __name__ + "."
# load modules to work based with and without pyinstaller
# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py
# see: https://github.com/pyinstaller/pyinstaller/issues/1905
# load modules using iter_modules()
# (should find all filters in normal build, but not pyinstaller)
module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)]
# special handling for PyInstaller
importers = map(pkgutil.get_importer, __path__)
toc = set()
for i in importers:
if hasattr(i, 'toc'):
toc |= i.toc
for elm in toc:
if elm.startswith(prefix):
module_names.append(elm)
for modname in module_names:
conversionPack = __import__(modname, fromlist="dummy")
for modName in iterNamespace(__name__, __path__):
conversionPack = __import__(modName, fromlist="dummy")
all.update(conversionPack.CONVERSIONS)
modname_tail = modname.rsplit('.', 1)[-1]
modname_tail = modName.rsplit('.', 1)[-1]
packs[modname_tail] = conversionPack.CONVERSIONS

View File

@@ -4,5 +4,23 @@ Conversion pack for August 2019 release
CONVERSIONS = {
# Renamed items
"Gravid 1MN Afterburner MutaPlasmid": "Gravid 1MN Afterburner Mutaplasmid",
'Gravid 1MN Afterburner MutaPlasmid': 'Gravid 1MN Afterburner Mutaplasmid',
'Low-grade Slave Alpha': 'Low-grade Amulet Alpha',
'Low-grade Slave Beta': 'Low-grade Amulet Beta',
'Low-grade Slave Gamma': 'Low-grade Amulet Gamma',
'Low-grade Slave Delta': 'Low-grade Amulet Delta',
'Low-grade Slave Epsilon': 'Low-grade Amulet Epsilon',
'Low-grade Slave Omega': 'Low-grade Amulet Omega',
'Mid-grade Slave Alpha': 'Mid-grade Amulet Alpha',
'Mid-grade Slave Beta': 'Mid-grade Amulet Beta',
'Mid-grade Slave Gamma': 'Mid-grade Amulet Gamma',
'Mid-grade Slave Delta': 'Mid-grade Amulet Delta',
'Mid-grade Slave Epsilon': 'Mid-grade Amulet Epsilon',
'Mid-grade Slave Omega': 'Mid-grade Amulet Omega',
'High-grade Slave Alpha': 'High-grade Amulet Alpha',
'High-grade Slave Beta': 'High-grade Amulet Beta',
'High-grade Slave Gamma': 'High-grade Amulet Gamma',
'High-grade Slave Delta': 'High-grade Amulet Delta',
'High-grade Slave Epsilon': 'High-grade Amulet Epsilon',
'High-grade Slave Omega': 'High-grade Amulet Omega'
}

View File

@@ -0,0 +1,14 @@
"""
Conversion pack for Januray 2020 release
"""
CONVERSIONS = {
'Small C5-L Emergency Shield Overload I': 'Small C5-L Compact Shield Booster',
'Small Clarity Ward Booster I': 'Small Clarity Ward Enduring Shield Booster',
'Medium Clarity Ward Booster I': 'Medium Clarity Ward Enduring Shield Booster',
'Medium C5-L Emergency Shield Overload I': 'Medium C5-L Compact Shield Booster',
'Large Clarity Ward Booster I': 'Large Clarity Ward Enduring Shield Booster',
'Large C5-L Emergency Shield Overload I': 'Large C5-L Compact Shield Booster',
'X-Large Clarity Ward Booster I': 'X-Large Clarity Ward Enduring Shield Booster',
'X-Large C5-L Emergency Shield Overload I': 'X-Large C5-L Compact Shield Booster'
}

View File

@@ -53,13 +53,13 @@ class DamagePattern:
@staticmethod
def newPattern(name):
p = es_DamagePattern(0, 0, 0, 0)
p.name = name
p.rawName = name
eos.db.save(p)
return p
@staticmethod
def renamePattern(p, newName):
p.name = newName
p.rawName = newName
eos.db.save(p)
@staticmethod
@@ -91,5 +91,5 @@ class DamagePattern:
if patterns[i].name in ("Uniform", "Selected Ammo"):
del patterns[i]
patterns.sort(key=lambda p: p.name)
patterns.sort(key=lambda p: p.fullName)
return es_DamagePattern.exportPatterns(*patterns)

View File

@@ -52,11 +52,12 @@ class ImplantSets:
return eos.db.getImplantSet(setID).implants
@staticmethod
def addImplant(setID, itemID):
def addImplants(setID, *itemIDs):
implant_set = eos.db.getImplantSet(setID)
implant = es_Implant(eos.db.getItem(itemID))
implant_set.implants.makeRoom(implant)
implant_set.implants.append(implant)
for itemID in itemIDs:
implant = es_Implant(eos.db.getItem(itemID))
implant_set.implants.makeRoom(implant)
implant_set.implants.append(implant)
eos.db.commit()
@staticmethod

View File

@@ -306,3 +306,4 @@ cdfe: Core Defense Field Extender
lg: Low-grade
mg: Mid-grade
hg: High-grade
slave: amulet

View File

@@ -267,7 +267,9 @@ class Market:
"Medium Nano Armor Repair Unit I": ("Tech I", "Medium Armor Repairer I"),
"Large 'Reprieve' Vestment Reconstructer I": ("Storyline", "Large Armor Repairer I"),
"Khanid Navy Torpedo Launcher": ("Faction", "Torpedo Launcher I"),
"Dark Blood Tracking Disruptor": ("Faction", "Tracking Disruptor I")}
"Dark Blood Tracking Disruptor": ("Faction", "Tracking Disruptor I"),
"Dread Guristas Standup Variable Spectrum ECM": ("Structure Faction", "Standup Variable Spectrum ECM I"),
"Dark Blood Standup Heavy Energy Neutralizer": ("Structure Faction", "Standup Heavy Energy Neutralizer I")}
# Parent type name: set(item names)
self.ITEMS_FORCEDMETAGROUP_R = {}
for item, value in list(self.ITEMS_FORCEDMETAGROUP.items()):
@@ -280,27 +282,18 @@ class Market:
self.ITEMS_FORCEDMARKETGROUP = {
"Advanced Cerebral Accelerator" : 2487, # Implants & Boosters > Booster > Cerebral Accelerators
"Civilian Hobgoblin" : 837, # Drones > Combat Drones > Light Scout Drones
"Civilian Light Missile Launcher" : 640,
# Ship Equipment > Turrets & Bays > Missile Launchers > Light Missile Launchers
"Civilian Scourge Light Missile" : 920,
# Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles
"Civilian Small Remote Armor Repairer" : 1059,
# Ship Equipment > Hull & Armor > Remote Armor Repairers > Small
"Civilian Light Missile Launcher" : 640, # Ship Equipment > Turrets & Launchers > Missile Launchers > Light Missile Launchers
"Civilian Scourge Light Missile" : 920, # Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles
"Civilian Small Remote Armor Repairer" : 1059, # Ship Equipment > Hull & Armor > Remote Armor Repairers > Small
"Civilian Small Remote Shield Booster" : 603, # Ship Equipment > Shield > Remote Shield Boosters > Small
"Hardwiring - Zainou 'Sharpshooter' ZMX10" : 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX100" : 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX1000": 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX11" : 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX110" : 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493,
# Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX10" : 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX100" : 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX1000": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX11" : 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX110" : 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Prototype Cerebral Accelerator" : 2487, # Implants & Boosters > Booster > Cerebral Accelerators
"Prototype Iris Probe Launcher" : 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers
"Prototype Iris Probe Launcher" : 712, # Ship Equipment > Scanning Equipment > Scan Probe Launchers
"Standard Cerebral Accelerator" : 2487, # Implants & Boosters > Booster > Cerebral Accelerators
}
@@ -808,7 +801,7 @@ class Market:
def getReplacements(self, identity):
item = self.getItem(identity)
# We already store needed type IDs in database
replTypeIDs = {int(i) for i in item.replacements.split(",") if i}
replTypeIDs = {int(i) for i in item.replacements.split(",") if i} if item.replacements is not None else {}
if not replTypeIDs:
return ()
# As replacements were generated without keeping track which items were published,

View File

@@ -48,13 +48,13 @@ class TargetProfile:
@staticmethod
def newPattern(name):
p = es_TargetProfile()
p.name = name
p.rawName = name
db.save(p)
return p
@staticmethod
def renamePattern(p, newName):
p.name = newName
p.rawName = newName
db.save(p)
@staticmethod
@@ -82,5 +82,5 @@ class TargetProfile:
def exportPatterns(self):
patterns = self.getUserTargetProfileList()
patterns.sort(key=lambda p: p.name)
patterns.sort(key=lambda p: p.fullName)
return es_TargetProfile.exportPatterns(*patterns)

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,524 +0,0 @@
[
{
"dataID": 16545434,
"description": "Meter",
"descriptionID": 77965,
"displayName": "m",
"displayNameID": 78005,
"unitID": 1,
"unitName": "Length"
},
{
"dataID": 16545435,
"description": "Kilogram",
"descriptionID": 77966,
"displayName": "kg",
"displayNameID": 78006,
"unitID": 2,
"unitName": "Mass"
},
{
"dataID": 16545436,
"description": "Second",
"descriptionID": 77967,
"displayName": "sec",
"displayNameID": 78007,
"unitID": 3,
"unitName": "Time"
},
{
"dataID": 16545437,
"description": "Ampere",
"descriptionID": 77968,
"displayName": "A",
"displayNameID": 78008,
"unitID": 4,
"unitName": "Electric Current"
},
{
"dataID": 16545438,
"description": "Kelvin",
"descriptionID": 77969,
"displayName": "K",
"displayNameID": 78009,
"unitID": 5,
"unitName": "Temperature"
},
{
"dataID": 16545439,
"description": "Mole",
"descriptionID": 77970,
"displayName": "mol",
"displayNameID": 78010,
"unitID": 6,
"unitName": "Amount Of Substance"
},
{
"dataID": 16545440,
"description": "Candela",
"descriptionID": 77971,
"displayName": "cd",
"displayNameID": 78011,
"unitID": 7,
"unitName": "Luminous Intensity"
},
{
"dataID": 16545441,
"description": "Square meter",
"descriptionID": 77972,
"displayName": "m2",
"displayNameID": 78012,
"unitID": 8,
"unitName": "Area"
},
{
"dataID": 16545442,
"description": "Cubic meter",
"descriptionID": 77973,
"displayName": "m3",
"displayNameID": 78013,
"unitID": 9,
"unitName": "Volume"
},
{
"dataID": 16545443,
"description": "Meter per second",
"descriptionID": 77974,
"displayName": "m/sec",
"displayNameID": 78014,
"unitID": 10,
"unitName": "Speed"
},
{
"dataID": 16545444,
"description": "Meter per second squared",
"descriptionID": 77975,
"displayName": "m/sec",
"displayNameID": 78015,
"unitID": 11,
"unitName": "Acceleration"
},
{
"dataID": 16545445,
"description": "Reciprocal meter",
"descriptionID": 77976,
"displayName": "m-1",
"displayNameID": 78016,
"unitID": 12,
"unitName": "Wave Number"
},
{
"dataID": 16545446,
"description": "Kilogram per cubic meter",
"descriptionID": 77977,
"displayName": "kg/m3",
"displayNameID": 78017,
"unitID": 13,
"unitName": "Mass Density"
},
{
"dataID": 16545447,
"description": "Cubic meter per kilogram",
"descriptionID": 77978,
"displayName": "m3/kg",
"displayNameID": 78018,
"unitID": 14,
"unitName": "Specific Volume"
},
{
"dataID": 16545448,
"description": "Ampere per square meter",
"descriptionID": 77979,
"displayName": "A/m2",
"displayNameID": 78019,
"unitID": 15,
"unitName": "Current Density"
},
{
"dataID": 16545449,
"description": "Ampere per meter",
"descriptionID": 77980,
"displayName": "A/m",
"displayNameID": 78020,
"unitID": 16,
"unitName": "Magnetic Field Strength"
},
{
"dataID": 16545450,
"description": "Mole per cubic meter",
"descriptionID": 77981,
"displayName": "mol/m3",
"displayNameID": 78021,
"unitID": 17,
"unitName": "Amount-Of-Substance Concentration"
},
{
"dataID": 16545451,
"description": "Candela per square meter",
"descriptionID": 77982,
"displayName": "cd/m2",
"displayNameID": 78022,
"unitID": 18,
"unitName": "Luminance"
},
{
"dataID": 16545452,
"description": "Kilogram per kilogram, which may be represented by the number 1",
"descriptionID": 77983,
"displayName": "kg/kg = 1",
"displayNameID": 78023,
"unitID": 19,
"unitName": "Mass Fraction"
},
{
"dataID": 16545453,
"description": "",
"descriptionID": null,
"displayName": "s",
"displayNameID": 78024,
"unitID": 101,
"unitName": "Milliseconds"
},
{
"dataID": 16545454,
"description": "",
"descriptionID": null,
"displayName": "mm",
"displayNameID": 78025,
"unitID": 102,
"unitName": "Millimeters"
},
{
"dataID": 13353825,
"description": "",
"descriptionID": null,
"displayName": "",
"displayNameID": null,
"unitID": 103,
"unitName": "MegaPascals"
},
{
"dataID": 16545455,
"description": "Indicates that the unit is a multiplier.",
"descriptionID": 77984,
"displayName": "x",
"displayNameID": 78026,
"unitID": 104,
"unitName": "Multiplier"
},
{
"dataID": 16545456,
"description": "",
"descriptionID": null,
"displayName": "%",
"displayNameID": 78027,
"unitID": 105,
"unitName": "Percentage"
},
{
"dataID": 16545457,
"description": "",
"descriptionID": null,
"displayName": "tf",
"displayNameID": 78028,
"unitID": 106,
"unitName": "Teraflops"
},
{
"dataID": 16545458,
"description": "",
"descriptionID": null,
"displayName": "MW",
"displayNameID": 78029,
"unitID": 107,
"unitName": "MegaWatts"
},
{
"dataID": 16545459,
"description": "Used for resistance.\r\n0.0 = 100% 1.0 = 0%\r\n",
"descriptionID": 77985,
"displayName": "%",
"displayNameID": 78030,
"unitID": 108,
"unitName": "Inverse Absolute Percent"
},
{
"dataID": 16545460,
"description": "Used for multipliers displayed as %\r\n1.1 = +10%\r\n0.9 = -10%",
"descriptionID": 77986,
"displayName": "%",
"displayNameID": 78031,
"unitID": 109,
"unitName": "Modifier Percent"
},
{
"dataID": 16545461,
"description": "Used to modify damage resistance. Damage resistance bonus.\r\n0.1 = 90%\r\n0.9 = 10%",
"descriptionID": 77987,
"displayName": "%",
"displayNameID": 78032,
"unitID": 111,
"unitName": "Inversed Modifier Percent"
},
{
"dataID": 16545462,
"description": "Rotation speed.",
"descriptionID": 77988,
"displayName": "rad/sec",
"displayNameID": 78033,
"unitID": 112,
"unitName": "Radians/Second"
},
{
"dataID": 16545463,
"description": "",
"descriptionID": null,
"displayName": "HP",
"displayNameID": 78034,
"unitID": 113,
"unitName": "Hitpoints"
},
{
"dataID": 16545464,
"description": "Giga Joule",
"descriptionID": 77989,
"displayName": "GJ",
"displayNameID": 78035,
"unitID": 114,
"unitName": "capacitor units"
},
{
"dataID": 16545465,
"description": "",
"descriptionID": null,
"displayName": "groupID",
"displayNameID": 78036,
"unitID": 115,
"unitName": "groupID"
},
{
"dataID": 16545466,
"description": "",
"descriptionID": null,
"displayName": "typeID",
"displayNameID": 78037,
"unitID": 116,
"unitName": "typeID"
},
{
"dataID": 100671817,
"description": "1=small 2=medium 3=large 4=x-large",
"descriptionID": 318074,
"displayName": "1=small 2=medium 3=l",
"displayNameID": 78038,
"unitID": 117,
"unitName": "Sizeclass"
},
{
"dataID": 16545468,
"description": "",
"descriptionID": null,
"displayName": "Ore units",
"displayNameID": 78039,
"unitID": 118,
"unitName": "Ore units"
},
{
"dataID": 16545469,
"description": "",
"descriptionID": null,
"displayName": "attributeID",
"displayNameID": 78040,
"unitID": 119,
"unitName": "attributeID"
},
{
"dataID": 16545470,
"description": "",
"descriptionID": null,
"displayName": "points",
"displayNameID": 78041,
"unitID": 120,
"unitName": "attributePoints"
},
{
"dataID": 16545471,
"description": "Used for real percentages, i.e. the number 5 is 5%",
"descriptionID": 77990,
"displayName": "%",
"displayNameID": 78042,
"unitID": 121,
"unitName": "realPercent"
},
{
"dataID": 13353933,
"description": "",
"descriptionID": null,
"displayName": "",
"displayNameID": null,
"unitID": 122,
"unitName": "Fitting slots"
},
{
"dataID": 16545472,
"description": "Shows seconds directly",
"descriptionID": 77991,
"displayName": "sec",
"displayNameID": 78043,
"unitID": 123,
"unitName": "trueTime"
},
{
"dataID": 16545473,
"description": "Used for relative percentages displayed as %",
"descriptionID": 77992,
"displayName": "%",
"displayNameID": 78044,
"unitID": 124,
"unitName": "Modifier Relative Percent"
},
{
"dataID": 16545474,
"description": "",
"descriptionID": null,
"displayName": "N",
"displayNameID": 78045,
"unitID": 125,
"unitName": "Newton"
},
{
"dataID": 16545475,
"description": "",
"descriptionID": null,
"displayName": "ly",
"displayNameID": 78046,
"unitID": 126,
"unitName": "Light Year"
},
{
"dataID": 16545476,
"description": "0.0 = 0% 1.0 = 100%",
"descriptionID": 77993,
"displayName": "%",
"displayNameID": 78047,
"unitID": 127,
"unitName": "Absolute Percent"
},
{
"dataID": 16545477,
"description": "Mega bits per second",
"descriptionID": 77994,
"displayName": "Mbit/sec",
"displayNameID": 78048,
"unitID": 128,
"unitName": "Drone bandwidth"
},
{
"dataID": 16545488,
"description": "Hours",
"descriptionID": 77995,
"displayName": "",
"displayNameID": null,
"unitID": 129,
"unitName": "Hours"
},
{
"dataID": 16545478,
"description": "ISK",
"descriptionID": 77996,
"displayName": "ISK",
"displayNameID": 78049,
"unitID": 133,
"unitName": "Money"
},
{
"dataID": 16545479,
"description": "Bandwidth for PI",
"descriptionID": 77997,
"displayName": "m3/hour",
"displayNameID": 78050,
"unitID": 134,
"unitName": "Logistical Capacity"
},
{
"dataID": 16545480,
"description": "Used to denote distance, 1AU = The distance from the Earth to the Sun.",
"descriptionID": 77998,
"displayName": "AU",
"displayNameID": 78051,
"unitID": 135,
"unitName": "Astronomical Unit"
},
{
"dataID": 16545481,
"description": "Slot number prefix for various purposes",
"descriptionID": 77999,
"displayName": "Slot",
"displayNameID": 78052,
"unitID": 136,
"unitName": "Slot"
},
{
"dataID": 16545482,
"description": "For displaying boolean flags 1=True 0=False",
"descriptionID": 78000,
"displayName": "1=True 0=False",
"displayNameID": 78053,
"unitID": 137,
"unitName": "Boolean"
},
{
"dataID": 16545483,
"description": "Units of something, for example fuel",
"descriptionID": 78001,
"displayName": "units",
"displayNameID": 78054,
"unitID": 138,
"unitName": "Units"
},
{
"dataID": 16545484,
"description": "Forces a plus sign for positive values",
"descriptionID": 78002,
"displayName": "+",
"displayNameID": 78055,
"unitID": 139,
"unitName": "Bonus"
},
{
"dataID": 16545485,
"description": "For anything which is divided by levels",
"descriptionID": 78003,
"displayName": "Level",
"displayNameID": 78056,
"unitID": 140,
"unitName": "Level"
},
{
"dataID": 16545486,
"description": "For various counts to do with turret, launcher and rig hardpoints",
"descriptionID": 78004,
"displayName": "hardpoints",
"displayNameID": 78057,
"unitID": 141,
"unitName": "Hardpoints"
},
{
"dataID": 16545487,
"description": "",
"descriptionID": null,
"displayName": "1=Male 2=Unisex 3=Female",
"displayNameID": 78058,
"unitID": 142,
"unitName": "Sex"
},
{
"dataID": 97574714,
"description": "Date and time",
"descriptionID": 312106,
"displayName": "",
"displayNameID": null,
"unitID": 143,
"unitName": "Datetime"
}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -64,7 +64,7 @@
"descriptionID": 64453,
"hasTypes": 0,
"iconID": 365,
"name": "Turrets & Bays",
"name": "Turrets & Launchers",
"nameID": 65527,
"parentGroupID": 9
},
@@ -605,7 +605,7 @@
"nameID": 65587
},
"157": {
"description": "Drones are semi-autonomous robotic devices used for military and industrial purposes throughout space.",
"description": "Drones are semi-autonomous robotic devices used for military and industrial purposes throughout space",
"descriptionID": 64514,
"hasTypes": 0,
"iconID": 1084,
@@ -16002,7 +16002,7 @@
"description": "Capital ship entropic disintegrators, for use on dreadnoughts and titans.",
"descriptionID": 553053,
"hasTypes": 1,
"iconID": 21921,
"iconID": 24237,
"name": "Extra Large",
"nameID": 553052,
"parentGroupID": 2432
@@ -16022,5 +16022,41 @@
"name": "Triglavian",
"nameID": 553059,
"parentGroupID": 1883
},
"2701": {
"description": "Items used in the HyperNet Relay",
"descriptionID": 554068,
"hasTypes": 1,
"iconID": 24205,
"name": "HyperNet Relay",
"nameID": 554066,
"parentGroupID": 1922
},
"2702": {
"description": "Corvettes that have been offered to capsuleers on occasion for limited periods.",
"descriptionID": 554136,
"hasTypes": 1,
"iconID": 1443,
"name": "Special Edition Corvettes",
"nameID": 554135,
"parentGroupID": 1612
},
"2703": {
"description": "Precursor Dreadnoughts",
"descriptionID": 555352,
"hasTypes": 0,
"iconID": 21420,
"name": "Precursor Dreadnoughts",
"nameID": 555351,
"parentGroupID": 1971
},
"2704": {
"description": "Triglavian Dreadnoughts",
"descriptionID": 555354,
"hasTypes": 1,
"iconID": 21420,
"name": "Triglavian",
"nameID": 555353,
"parentGroupID": 2703
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -9544,6 +9544,17 @@
"published": true,
"useBasePrice": false
},
"4041": {
"anchorable": false,
"anchored": false,
"categoryID": 17,
"fittableNonSingleton": false,
"groupID": 4041,
"groupName": "Jump Filaments",
"groupNameID": 553695,
"published": true,
"useBasePrice": false
},
"405": {
"anchorable": false,
"anchored": false,

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
[
{
"field_name": "client_build",
"field_value": 1618828
"field_value": 1651039
},
{
"field_name": "dump_time",
"field_value": 1574856401
"field_value": 1579607729
}
]

View File

@@ -3945,11 +3945,11 @@
"bonuses": [
{
"number": "5%",
"text": "bonus to Light Missile and Rocket Launcher explosion velocity"
"text": "bonus to Light Missile and Rocket explosion velocity"
},
{
"number": "5%",
"text": "bonus to Light Missile and Rocket Launcher damage"
"text": "bonus to Light Missile and Rocket damage"
}
],
"header": "Assault Frigates bonuses (per skill level):"
@@ -17728,7 +17728,7 @@
"text": "bonus to Ultratidal Entropic Disintegrator rate of fire"
}
],
"header": "Zirnitra bonuses (per skill level):"
"header": "Precursor Dreadnought bonuses (per skill level):"
}
]
},

View File

@@ -1 +1 @@
version: v2.15.1
version: v2.16.0