Merge branch 'master' into ammo_graph

This commit is contained in:
DarkPhoenix
2019-10-31 11:29:31 +03:00
48 changed files with 4020292 additions and 91 deletions

View File

@@ -65,6 +65,8 @@ before_build:
build_script:
- ECHO "Build pyfa:"
# Build gamedata DB
- "python db_update.py"
##########
# PyInstaller - create binaries for pyfa
##########

View File

@@ -13,6 +13,7 @@ matrix:
before_install:
- bash scripts/setup-osx.sh
install:
- python3 db_update.py
- export PYFA_VERSION="$(python3 scripts/dump_version.py)"
- bash scripts/package-osx.sh
before_deploy:

View File

@@ -12,7 +12,7 @@ Virtual environment will be created in *PyfaEnv* folder. Project will be cloned
## Setting up the project manually
Clone the repo
Clone the repository
```
git clone <repo> PyfaDEV
```
@@ -36,7 +36,7 @@ pip install -r PyfaDEV\requirements.txt
```
> For some Linux distributions, you may need to install separate wxPython bindings, such as `python-matplotlib-wx`
Check that libs from *requirements.txt* are installed
Check that the libs from *requirements.txt* are installed
```
pip list
```

View File

@@ -39,6 +39,7 @@ loggingLevel = None
logging_setup = None
cipher = None
clientHash = None
experimentalFeatures = None
ESI_CACHE = 'esi_cache'
@@ -103,6 +104,7 @@ def defPaths(customSavePath=None):
global cipher
global clientHash
global version
global experimentalFeatures
pyfalog.debug("Configuring Pyfa")
@@ -168,6 +170,10 @@ def defPaths(customSavePath=None):
logPath = os.path.join(savePath, logFile)
experimentalFeatures = getattr(configforced, "experimentalFeatures", experimentalFeatures)
if experimentalFeatures is None:
experimentalFeatures = False
# DON'T MODIFY ANYTHING BELOW
import eos.config

107
scripts/jsonToSql.py → db_update.py Executable file → Normal file
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
#======================================================================
# Copyright (C) 2012 Diego Duclos
#
@@ -18,35 +18,70 @@
# License along with eos. If not, see <http://www.gnu.org/licenses/>.
#======================================================================
import functools
import itertools
import json
import os
import sqlite3
import sys
# Add eos root path to sys.path so we can import ourselves
path = os.path.dirname(__file__)
sys.path.insert(0, os.path.realpath(os.path.join(path, '..')))
import json
import argparse
import itertools
ROOT_DIR = os.path.realpath(os.path.dirname(__file__))
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
CATEGORIES_TO_REMOVE = [
30 # Apparel
]
def db_needs_update():
"""True if needs, false if it does not, none if we cannot check it."""
try:
with open(os.path.join(JSON_DIR, 'phobos', 'metadata.json')) as f:
data_version = next((r['field_value'] for r in json.load(f) if r['field_name'] == 'client_build'))
except KeyboardInterrupt:
raise
# If we have no source data - return None; should not update in this case
except:
return None
if not os.path.isfile(DB_PATH):
print('Gamedata DB not found')
return True
db_data_version = None
db_schema_version = None
try:
db = sqlite3.connect(DB_PATH)
cursor = db.cursor()
cursor.execute('SELECT field_value FROM metadata WHERE field_name = \'client_build\'')
for row in cursor:
db_data_version = int(row[0])
cursor.execute('SELECT field_value FROM metadata WHERE field_name = \'schema_version\'')
for row in cursor:
db_schema_version = int(row[0])
cursor.close()
db.close()
except KeyboardInterrupt:
raise
except:
print('Error when fetching gamedata DB metadata')
return True
if data_version != db_data_version:
print('Gamedata DB data version mismatch: needed {}, DB has {}'.format(data_version, db_data_version))
return True
if GAMEDATA_SCHEMA_VERSION != db_schema_version:
print('Gamedata DB schema version mismatch: needed {}, DB has {}'.format(GAMEDATA_SCHEMA_VERSION, db_schema_version))
return True
return False
def main(db, json_path):
if os.path.isfile(db):
os.remove(db)
jsonPath = os.path.expanduser(json_path)
def update_db():
# Import eos.config first and change it
import eos.config
eos.config.gamedata_connectionstring = db
eos.config.debug = False
print('Building gamedata DB...')
if os.path.isfile(DB_PATH):
os.remove(DB_PATH)
# Now thats done, we can import the eos modules using the config
import eos.db
import eos.gamedata
@@ -274,7 +309,7 @@ def main(db, json_path):
# 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(jsonPath, minerName, '{}.json'.format(jsonName)), encoding='utf-8') as f:
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 = []
@@ -373,7 +408,7 @@ def main(db, json_path):
eos.db.gamedata_session.add(instance)
# quick and dirty hack to get this data in
with open(os.path.join(jsonPath, 'fsd_binary', 'dynamicitemattributes.json'), encoding='utf-8') as f:
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()
@@ -395,20 +430,25 @@ def main(db, json_path):
attr.max = attrData['max']
eos.db.gamedata_session.add(attr)
# Add schema version to prevent further updates
metadata_schema_version = eos.gamedata.MetaData()
metadata_schema_version.field_name = 'schema_version'
metadata_schema_version.field_value = GAMEDATA_SCHEMA_VERSION
eos.db.gamedata_session.add(metadata_schema_version)
eos.db.gamedata_session.commit()
# 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
# pyfa, we can do it here as a post-processing step
eos.db.gamedata_engine.execute('UPDATE dgmtypeattribs SET value = 4.0 WHERE attributeID = ?', (1367,))
for attr in eos.db.gamedata_session.query(eos.gamedata.Attribute).filter(eos.gamedata.Attribute.ID == 1367).all():
attr.value = 4.0
for item in eos.db.gamedata_session.query(eos.gamedata.Item).filter(eos.gamedata.Item.name.like('%abyssal%')).all():
item.published = False
eos.db.gamedata_engine.execute('UPDATE invtypes SET published = 0 WHERE typeName LIKE \'%abyssal%\'')
# fix for #1722 until CCP gets their shit together
eos.db.gamedata_engine.execute('UPDATE invtypes SET typeName = \'Small Abyssal Energy Nosferatu\' WHERE typeID = ? AND typeName = ?', (48419, ''))
print()
for x in CATEGORIES_TO_REMOVE:
for x in [
30 # Apparel
]:
cat = eos.db.gamedata_session.query(eos.gamedata.Category).filter(eos.gamedata.Category.ID == x).first()
print ('Removing Category: {}'.format(cat.name))
eos.db.gamedata_session.delete(cat)
@@ -418,11 +458,6 @@ def main(db, json_path):
print('done')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='This scripts dumps effects from an sqlite cache dump to mongo')
parser.add_argument('-d', '--db', required=True, type=str, help='The sqlalchemy connectionstring, example: sqlite:///c:/tq.db')
parser.add_argument('-j', '--json', required=True, type=str, help='The path to the json dump')
args = parser.parse_args()
main(args.db, args.json)
update_db()

View File

@@ -18,6 +18,38 @@
# =============================================================================
import math
# Just copy-paste penalization chain calculation code (with some modifications,
# as multipliers arrive in different form) in here to not make actual attribute
# calculations slower than they already are due to extra function calls
def calculateMultiplier(multipliers):
"""
multipliers: dictionary in format:
{stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]}
"""
val = 1
for penalizedMultipliers in multipliers.values():
# A quick explanation of how this works:
# 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them
l1 = [v[0] for v in penalizedMultipliers if v[0] > 1]
l2 = [v[0] for v in penalizedMultipliers if v[0] < 1]
# 2: The most significant bonuses take the smallest penalty,
# This means we'll have to sort
abssort = lambda _val: -abs(_val - 1)
l1.sort(key=abssort)
l2.sort(key=abssort)
# 3: The first module doesn't get penalized at all
# Any module after the first takes penalties according to:
# 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289)
for l in (l1, l2):
for i in range(len(l)):
bonus = l[i]
val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289)
return val
def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedRange=True):
"""Range strength/chance factor, applicable to guns, ewar, RRs, etc."""
if distance is None:
@@ -31,3 +63,9 @@ def calculateRangeFactor(srcOptimalRange, srcFalloffRange, distance, restrictedR
return 1
else:
return 0
def calculateLockTime(srcScanRes, tgtSigRadius):
if not srcScanRes or not tgtSigRadius:
return None
return min(40000 / srcScanRes / math.asinh(tgtSigRadius) ** 2, 30 * 60)

View File

@@ -92,7 +92,8 @@ def getItem(lookfor, eager=None):
else:
# Item names are unique, so we can use first() instead of one()
item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.name == lookfor).first()
itemNameMap[lookfor] = item.ID
if item is not None:
itemNameMap[lookfor] = item.ID
else:
raise TypeError("Need integer or string as argument")
return item
@@ -195,7 +196,8 @@ def getGroup(lookfor, eager=None):
else:
# Group names are unique, so we can use first() instead of one()
group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.name == lookfor).first()
groupNameMap[lookfor] = group.ID
if group is not None:
groupNameMap[lookfor] = group.ID
else:
raise TypeError("Need integer or string as argument")
return group
@@ -224,7 +226,8 @@ def getCategory(lookfor, eager=None):
# Category names are unique, so we can use first() instead of one()
category = gamedata_session.query(Category).options(*processEager(eager)).filter(
Category.name == lookfor).first()
categoryNameMap[lookfor] = category.ID
if category is not None:
categoryNameMap[lookfor] = category.ID
else:
raise TypeError("Need integer or string as argument")
return category
@@ -253,7 +256,8 @@ def getMetaGroup(lookfor, eager=None):
# MetaGroup names are unique, so we can use first() instead of one()
metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter(
MetaGroup.name == lookfor).first()
metaGroupNameMap[lookfor] = metaGroup.ID
if metaGroup is not None:
metaGroupNameMap[lookfor] = metaGroup.ID
else:
raise TypeError("Need integer or string as argument")
return metaGroup

View File

@@ -78,6 +78,15 @@ class DamagePattern:
"exp" : "explosive"
}
@classmethod
def oneType(cls, damageType, amount=100):
pattern = DamagePattern()
pattern.update(amount if damageType == "em" else 0,
amount if damageType == "thermal" else 0,
amount if damageType == "kinetic" else 0,
amount if damageType == "explosive" else 0)
return pattern
@classmethod
def importPatterns(cls, text):
lines = re.split('[\n\r]+', text)

View File

@@ -21,13 +21,14 @@ import datetime
import time
from copy import deepcopy
from itertools import chain
from math import asinh, log, sqrt
from math import log, sqrt
from logbook import Logger
from sqlalchemy.orm import reconstructor, validates
import eos.db
from eos import capSim
from eos.calc import calculateMultiplier, calculateLockTime
from eos.const import CalcType, FitSystemSecurity, FittingHardpoint, FittingModuleState, FittingSlot, ImplantLocation
from eos.effectHandlerHelpers import (
HandledBoosterList, HandledDroneCargoList, HandledImplantList,
@@ -1529,9 +1530,7 @@ class Fit:
def calculateLockTime(self, radius):
scanRes = self.ship.getModifiedItemAttr("scanResolution")
if scanRes is not None and scanRes > 0:
# Yes, this function returns time in seconds, not miliseconds.
# 40,000 is indeed the correct constant here.
return min(40000 / scanRes / asinh(radius) ** 2, 30 * 60)
return calculateLockTime(srcScanRes=scanRes, tgtSigRadius=radius)
else:
return self.ship.getModifiedItemAttr("scanSpeed") / 1000.0
@@ -1626,6 +1625,22 @@ class Fit:
if ability.active:
yield fighter, ability
def getDampMultScanRes(self):
damps = []
for mod in self.activeModulesIter():
for effectName in ('remoteSensorDampFalloff', 'structureModuleEffectRemoteSensorDampener'):
if effectName in mod.item.effects:
damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default'))
if 'doomsdayAOEDamp' in mod.item.effects:
damps.append((mod.getModifiedItemAttr('scanResolutionBonus'), 'default'))
for drone in self.activeDronesIter():
if 'remoteSensorDampEntity' in drone.item.effects:
damps.extend(drone.amountActive * ((drone.getModifiedItemAttr('scanResolutionBonus'), 'default'),))
mults = {}
for strength, stackingGroup in damps:
mults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
return calculateMultiplier(mults)
def __deepcopy__(self, memo=None):
fitCopy = Fit()
# Character and owner are not copied

BIN
eve.db

Binary file not shown.

View File

@@ -18,40 +18,9 @@
# =============================================================================
import math
from service.settings import GraphSettings
# Just copy-paste penalization chain calculation code (with some modifications,
# as multipliers arrive in different form) in here to not make actual attribute
# calculations slower than they already are due to extra function calls
def calculateMultiplier(multipliers):
"""
multipliers: dictionary in format:
{stacking group name: [(mult, resist attr ID), (mult, resist attr ID)]}
"""
val = 1
for penalizedMultipliers in multipliers.values():
# A quick explanation of how this works:
# 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them
l1 = [v[0] for v in penalizedMultipliers if v[0] > 1]
l2 = [v[0] for v in penalizedMultipliers if v[0] < 1]
# 2: The most significant bonuses take the smallest penalty,
# This means we'll have to sort
abssort = lambda _val: -abs(_val - 1)
l1.sort(key=abssort)
l2.sort(key=abssort)
# 3: The first module doesn't get penalized at all
# Any module after the first takes penalties according to:
# 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289)
for l in (l1, l2):
for i in range(len(l)):
bonus = l[i]
val *= 1 + (bonus - 1) * math.exp(- i ** 2 / 7.1289)
return val
def checkLockRange(src, distance):
if distance is None:
return True

View File

@@ -26,3 +26,5 @@ from . import fitCapacitor
from . import fitMobility
from . import fitWarpTime
from . import fitLockTime
# Hidden graphs, available via ctrl-alt-g
from . import fitEcmBurstScanresDamps

View File

@@ -29,6 +29,7 @@ from service.const import GraphCacheCleanupReason
class FitGraph(metaclass=ABCMeta):
# UI stuff
hidden = False
views = []
viewMap = {}

View File

@@ -0,0 +1,24 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
from .graph import FitEcmBurstScanresDampsGraph
FitEcmBurstScanresDampsGraph.register()

View File

@@ -0,0 +1,117 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
from eos.calc import calculateLockTime
from graphs.data.base import SmoothPointGetter
ECM_BURST_DURATION = 30
DRONE_LOCK_TIME = 2
class TgtScanRes2TgtLockTimeGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, src, tgt):
if miscParams['applyDamps']:
tgtScanResMult = src.item.getDampMultScanRes()
else:
tgtScanResMult = 1
return {
'tgtScanResMult': tgtScanResMult,
'sigRadius': src.item.ship.getModifiedItemAttr('signatureRadius')}
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
scanRes = x
time = calculateLockTime(
srcScanRes=scanRes * commonData['tgtScanResMult'],
tgtSigRadius=commonData['sigRadius'])
return time
class TgtScanRes2TgtLockUptimeGetter(TgtScanRes2TgtLockTimeGetter):
def _calculatePoint(self, *args, **kwargs):
# Assuming you ECM burst every 30 seconds, find out how long you
# will be locked before you burst another time
lockTime = super()._calculatePoint(*args, **kwargs)
lockedTime = max(0, ECM_BURST_DURATION - lockTime)
return lockedTime
class SrcDmgBaseGetter(SmoothPointGetter):
def _getCommonData(self, miscParams, src, tgt):
if miscParams['applyDamps']:
tgtScanResMult = src.item.getDampMultScanRes()
else:
tgtScanResMult = 1
return {
'tgtScanResMult': tgtScanResMult,
'srcSigRadius': src.item.ship.getModifiedItemAttr('signatureRadius'),
'srcEhp': sum(src.item.ehp.values()),
'srcDpsWeapon': src.item.getWeaponDps().total,
'srcDpsDrone': src.item.getDroneDps().total if miscParams['applyDrones'] else 0}
@staticmethod
def _calculateInflictedDamage(srcSigRadius, srcWeaponDps, srcDroneDps, srcEhp, tgtScanRes, tgtDps, uptimeAdjustment, uptimeAmountLimit):
lockTime = calculateLockTime(srcScanRes=tgtScanRes, tgtSigRadius=srcSigRadius)
lockUptime = max(0, ECM_BURST_DURATION - lockTime - uptimeAdjustment)
lockDowntime = ECM_BURST_DURATION - lockUptime
inflictedDmg = 0
remainingEhp = srcEhp
for i in range(int(uptimeAmountLimit)):
timeAliveUnderFire = min(lockUptime, remainingEhp / tgtDps)
timeAlive = lockDowntime + timeAliveUnderFire
remainingEhp -= lockUptime * tgtDps
inflictedDmg += timeAlive * srcWeaponDps
inflictedDmg += max(0, timeAlive - DRONE_LOCK_TIME - 1) * srcDroneDps
if remainingEhp <= 0:
break
return inflictedDmg
class TgtScanRes2SrcDmgGetter(SrcDmgBaseGetter):
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
damage = self._calculateInflictedDamage(
srcSigRadius=commonData['srcSigRadius'],
srcWeaponDps=commonData['srcDpsWeapon'],
srcDroneDps=commonData['srcDpsDrone'],
srcEhp=commonData['srcEhp'],
tgtScanRes=x * commonData['tgtScanResMult'],
tgtDps=miscParams['tgtDps'],
uptimeAdjustment=miscParams['uptimeAdj'],
uptimeAmountLimit=miscParams['uptimeAmtLimit'])
return damage
class TgtDps2SrcDmgGetter(SrcDmgBaseGetter):
def _calculatePoint(self, x, miscParams, src, tgt, commonData):
damage = self._calculateInflictedDamage(
srcSigRadius=commonData['srcSigRadius'],
srcWeaponDps=commonData['srcDpsWeapon'],
srcDroneDps=commonData['srcDpsDrone'],
srcEhp=commonData['srcEhp'],
tgtScanRes=miscParams['tgtScanRes'] * commonData['tgtScanResMult'],
tgtDps=x,
uptimeAdjustment=miscParams['uptimeAdj'],
uptimeAmountLimit=miscParams['uptimeAmtLimit'])
return damage

View File

@@ -0,0 +1,65 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
"""
Disclaimer by kadesh: this graph was made to analyze my ECM burst + damp frig
concept. I do not think it is useful for regular player, so it is disabled.
Enable by setting config.experimentalFeatures = True.
"""
import math
from graphs.data.base import FitGraph, XDef, YDef, Input, InputCheckbox
from .getter import (
TgtScanRes2TgtLockTimeGetter, TgtScanRes2TgtLockUptimeGetter,
TgtScanRes2SrcDmgGetter, TgtDps2SrcDmgGetter)
class FitEcmBurstScanresDampsGraph(FitGraph):
# UI stuff
hidden = True
internalName = 'ecmBurstScanresDamps'
name = 'ECM Burst + Scanres Damps'
xDefs = [
XDef(handle='tgtDps', unit=None, label='Enemy DPS', mainInput=('tgtDps', None)),
XDef(handle='tgtScanRes', unit='mm', label='Enemy scanres', mainInput=('tgtScanRes', 'mm'))]
yDefs = [
YDef(handle='srcDmg', unit=None, label='Damage inflicted'),
YDef(handle='tgtLockTime', unit='s', label='Lock time'),
YDef(handle='tgtLockUptime', unit='s', label='Lock uptime')]
inputs = [
Input(handle='tgtScanRes', unit='mm', label='Enemy scanres', iconID=74, defaultValue=700, defaultRange=(100, 1000)),
Input(handle='tgtDps', unit=None, label='Enemy DPS', iconID=1432, defaultValue=200, defaultRange=(100, 600)),
Input(handle='uptimeAdj', unit='s', label='Uptime adjustment', iconID=1392, defaultValue=1, defaultRange=(None, None), conditions=[(None, ('srcDmg', None))]),
Input(handle='uptimeAmtLimit', unit=None, label='Max amount of uptimes', iconID=1397, defaultValue=3, defaultRange=(None, None), conditions=[(None, ('srcDmg', None))])]
checkboxes = [
InputCheckbox(handle='applyDamps', label='Apply sensor dampeners', defaultValue=True),
InputCheckbox(handle='applyDrones', label='Use drones', defaultValue=True, conditions=[(None, ('srcDmg', None))])]
srcExtraCols = ('SigRadius', 'Damp ScanRes')
# Calculation stuff
_limiters = {'tgtScanRes': lambda src, tgt: (1, math.inf)}
_getters = {
('tgtScanRes', 'tgtLockTime'): TgtScanRes2TgtLockTimeGetter,
('tgtScanRes', 'tgtLockUptime'): TgtScanRes2TgtLockUptimeGetter,
('tgtScanRes', 'srcDmg'): TgtScanRes2SrcDmgGetter,
('tgtDps', 'srcDmg'): TgtDps2SrcDmgGetter}

View File

@@ -20,8 +20,8 @@
import math
from eos.calc import calculateRangeFactor
from graphs.calc import calculateMultiplier, checkLockRange, checkDroneControlRange
from eos.calc import calculateMultiplier, calculateRangeFactor
from graphs.calc import checkLockRange, checkDroneControlRange
from graphs.data.base import SmoothPointGetter

View File

@@ -20,7 +20,7 @@
import math
from graphs.data.base import FitGraph, Input, XDef, YDef
from graphs.data.base import FitGraph, XDef, YDef, Input
from .getter import TgtSigRadius2LockTimeGetter

View File

@@ -43,7 +43,7 @@ REDRAW_DELAY = 500
class GraphFrame(AuxiliaryFrame):
def __init__(self, parent):
def __init__(self, parent, includeHidden=False):
if not canvasPanel.graphFrame_enabled:
pyfalog.warning('Matplotlib is not enabled. Skipping initialization.')
return
@@ -74,6 +74,8 @@ class GraphFrame(AuxiliaryFrame):
# Setup - graph selector
for view in FitGraph.views:
if view.hidden and not includeHidden:
continue
self.graphSelection.Append(view.name, view())
self.graphSelection.SetSelection(0)
self.ctrlPanel.updateControls(layout=False)
@@ -101,9 +103,9 @@ class GraphFrame(AuxiliaryFrame):
self.draw()
@classmethod
def openOne(cls, parent):
def openOne(cls, parent, *args, **kwargs):
if canvasPanel.graphFrame_enabled:
super().openOne(parent)
super().openOne(parent, *args, **kwargs)
def UpdateWindowSize(self):
curW, curH = self.GetSize()

View File

@@ -18,11 +18,11 @@
# =============================================================================
from eos.calc import calculateMultiplier
from eos.saveddata.damagePattern import DamagePattern
from eos.saveddata.fit import Fit
from eos.saveddata.targetProfile import TargetProfile
from service.const import TargetResistMode
from .calc import calculateMultiplier
class BaseWrapper:

View File

@@ -53,10 +53,10 @@ class AuxiliaryFrame(wx.Frame):
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
@classmethod
def openOne(cls, parent):
def openOne(cls, parent, *args, **kwargs):
"""If window is open and alive - raise it, open otherwise"""
if not cls._instance:
frame = cls(parent)
frame = cls(parent, *args, **kwargs)
cls._instance = frame
frame.Show()
else:

View File

@@ -0,0 +1,60 @@
# =============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
# noinspection PyPackageRequirements
import wx
from eos.saveddata.fit import Fit
from graphs.wrapper import BaseWrapper
from gui.bitmap_loader import BitmapLoader
from eos.utils.float import floatUnerr
from gui.utils.numberFormatter import formatAmount
from gui.viewColumn import ViewColumn
class DampScanResColumn(ViewColumn):
name = 'Damp ScanRes'
def __init__(self, fittingView, params):
ViewColumn.__init__(self, fittingView)
self.imageId = fittingView.imageList.GetImageIndex(74, 'icons')
self.bitmap = BitmapLoader.getBitmap(74, 'icons')
self.mask = wx.LIST_MASK_IMAGE
def getText(self, stuff):
if isinstance(stuff, BaseWrapper):
stuff = stuff.item
mult = 1
if isinstance(stuff, Fit):
mult = floatUnerr(stuff.getDampMultScanRes())
if mult == 1:
text = ''
else:
text = '{}%'.format(formatAmount((mult - 1) * 100, 3, 0, 0, forceSign=True))
return text
def getImageId(self, stuff):
return -1
def getToolTip(self, stuff):
return 'Scan resolution dampening'
DampScanResColumn.register()

View File

@@ -468,6 +468,8 @@ class FittingView(d.Display):
elif item.isModule:
if mstate.GetModifiers() == wx.MOD_ALT:
self.mainFrame.command.Submit(cmd.GuiFillWithNewLocalModulesCommand(fitID=fitID, itemID=itemID))
elif dstPos is not None:
self.mainFrame.command.Submit(cmd.GuiReplaceLocalModuleCommand(fitID=fitID, itemID=itemID, positions=[dstPos]))
else:
self.mainFrame.command.Submit(cmd.GuiAddLocalModuleCommand(fitID=fitID, itemID=itemID))
elif item.isSubsystem:

View File

@@ -40,6 +40,7 @@ class CopySelectDialog(wx.Dialog):
copyFormatEsi = 3
copyFormatMultiBuy = 4
copyFormatEfs = 5
copyFormatFitStats = 6
def __init__(self, parent):
super().__init__(parent, id=wx.ID_ANY, title="Select a format", size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE)
@@ -50,7 +51,8 @@ class CopySelectDialog(wx.Dialog):
CopySelectDialog.copyFormatDna : self.exportDna,
CopySelectDialog.copyFormatEsi : self.exportEsi,
CopySelectDialog.copyFormatMultiBuy: self.exportMultiBuy,
CopySelectDialog.copyFormatEfs : self.exportEfs
CopySelectDialog.copyFormatEfs : self.exportEfs,
CopySelectDialog.copyFormatFitStats: self.exportFitStats
}
self.mainFrame = parent
@@ -62,6 +64,7 @@ class CopySelectDialog(wx.Dialog):
("ESI", (CopySelectDialog.copyFormatEsi, None)),
("DNA", (CopySelectDialog.copyFormatDna, DNA_OPTIONS)),
("EFS", (CopySelectDialog.copyFormatEfs, None)),
("Stats", (CopySelectDialog.copyFormatFitStats, None)),
# ("XML", (CopySelectDialog.copyFormatXml, None)),
))
@@ -117,7 +120,8 @@ class CopySelectDialog(wx.Dialog):
self.Center()
def Validate(self):
# Since this dialog is shown through aa ShowModal(), we hook into the Validate function to veto the closing of the dialog until we're ready.
# Since this dialog is shown through as ShowModal(),
# we hook into the Validate function to veto the closing of the dialog until we're ready.
# This always returns False, and when we're ready will EndModal()
selected = self.GetSelected()
options = self.GetOptions()
@@ -185,3 +189,10 @@ class CopySelectDialog(wx.Dialog):
def exportEfs(self, options, callback):
fit = getFit(self.mainFrame.getActiveFit())
EfsPort.exportEfs(fit, 0, callback)
# noinspection PyUnusedLocal
def exportFitStats(self, options, callback):
""" Puts fit stats in textual format into the clipboard """
fit = getFit(self.mainFrame.getActiveFit())
Port.exportFitStats(fit, callback)

View File

@@ -204,6 +204,7 @@ class MainFrame(wx.Frame):
self.addPageId = wx.NewId()
self.closePageId = wx.NewId()
self.closeAllPagesId = wx.NewId()
self.hiddenGraphsId = wx.NewId()
self.widgetInspectMenuID = wx.NewId()
self.SetMenuBar(MainMenuBar(self))
@@ -423,6 +424,9 @@ class MainFrame(wx.Frame):
def OnShowGraphFrame(self, event):
GraphFrame.openOne(self)
def OnShowGraphFrameHidden(self, event):
GraphFrame.openOne(self, includeHidden=True)
def OnShowDevTools(self, event):
DevTools.openOne(parent=self)
@@ -551,6 +555,7 @@ class MainFrame(wx.Frame):
# Graphs
self.Bind(wx.EVT_MENU, self.OnShowGraphFrame, id=menuBar.graphFrameId)
self.Bind(wx.EVT_MENU, self.OnShowGraphFrameHidden, id=self.hiddenGraphsId)
toggleSearchBoxId = wx.NewId()
toggleShipMarketId = wx.NewId()
@@ -576,6 +581,9 @@ class MainFrame(wx.Frame):
(wx.ACCEL_CTRL, wx.WXK_F4, self.closePageId),
(wx.ACCEL_CMD, ord("W"), self.closePageId),
(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("G"), self.hiddenGraphsId),
(wx.ACCEL_CMD | wx.ACCEL_ALT, ord("G"), self.hiddenGraphsId),
(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("W"), self.closeAllPagesId),
(wx.ACCEL_CTRL | wx.ACCEL_ALT, wx.WXK_F4, self.closeAllPagesId),
(wx.ACCEL_CMD | wx.ACCEL_ALT, ord("W"), self.closeAllPagesId),

View File

@@ -77,6 +77,7 @@ from gui.builtinViewColumns import ( # noqa: E402, F401
baseIcon,
baseName,
capacitorUse,
dampScanRes,
graphColor,
graphLightness,
graphLineStyle,

View File

@@ -26,6 +26,7 @@ from optparse import AmbiguousOptionError, BadOptionError, OptionParser
import config
from service.prereqsCheck import PreCheckException, PreCheckMessage, version_block, version_precheck
from db_update import db_needs_update, update_db
# ascii_text = '''
@@ -75,6 +76,7 @@ parser.add_option("-p", "--profile", action="store", dest="profile_path", help="
(options, args) = parser.parse_args()
if __name__ == "__main__":
try:
@@ -116,6 +118,9 @@ if __name__ == "__main__":
else:
pyfalog.info("Running in a thawed state.")
if db_needs_update() is True:
update_db()
# Lets get to the good stuff, shall we?
import eos.db
import eos.events # todo: move this to eos initialization?

View File

@@ -112,11 +112,14 @@ class Fit:
ship = eos.db.getItem(shipID)
if ship is not None:
shipMap[shipID] = (ship.name, ship.getShortName())
fitsToPurge = set()
for fit in fits:
try:
fit.shipName, fit.shipNameShort = shipMap[fit.shipID]
except KeyError:
pass
except (KeyError, TypeError):
fitsToPurge.add(fit)
for fit in fitsToPurge:
fits.remove(fit)
return fits
@staticmethod

View File

@@ -39,6 +39,7 @@ from service.port.eft import (
from service.port.esi import exportESI, importESI
from service.port.multibuy import exportMultiBuy
from service.port.shared import IPortUser, UserCancelException, processing_notify
from service.port.shipstats import exportFitStats
from service.port.xml import importXml, exportXml
from service.port.muta import parseMutant
@@ -317,3 +318,7 @@ class Port:
@staticmethod
def exportMultiBuy(fit, options, callback=None):
return exportMultiBuy(fit, options, callback=callback)
@staticmethod
def exportFitStats(fit, callback=None):
return exportFitStats(fit, callback=callback)

191
service/port/shipstats.py Normal file
View File

@@ -0,0 +1,191 @@
from functools import reduce
from eos.saveddata.damagePattern import DamagePattern
from gui.utils.numberFormatter import formatAmount
tankTypes = ("shield", "armor", "hull")
damageTypes = ("em", "thermal", "kinetic", "explosive")
damagePatterns = [DamagePattern.oneType(damageType) for damageType in damageTypes]
damageTypeResonanceNames = [damageType.capitalize() + "DamageResonance" for damageType in damageTypes]
resonanceNames = {"shield": ["shield" + s for s in damageTypeResonanceNames],
"armor": ["armor" + s for s in damageTypeResonanceNames],
"hull": [s[0].lower() + s[1:] for s in damageTypeResonanceNames]}
def firepowerSection(fit):
""" Returns the text of the firepower section"""
totalDps = fit.getTotalDps().total
weaponDps = fit.getWeaponDps().total
droneDps = fit.getDroneDps().total
totalVolley = fit.getTotalVolley().total
firepower = [totalDps, weaponDps, droneDps, totalVolley]
firepowerStr = [formatAmount(dps, 3, 0, 0) for dps in firepower]
# showWeaponAndDroneDps = (weaponDps > 0) and (droneDps > 0)
if sum(firepower) == 0:
return ""
return "DPS: {} (".format(firepowerStr[0]) + \
("Weapon: {}, Drone: {}, ".format(*firepowerStr[1:3])) + \
("Volley: {})\n".format(firepowerStr[3]))
def tankSection(fit):
""" Returns the text of the tank section"""
ehp = [fit.ehp[tank] for tank in tankTypes] if fit.ehp is not None else [0, 0, 0]
ehp.append(sum(ehp))
ehpStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehp]
resists = {tankType: [1 - fit.ship.getModifiedItemAttr(s) for s in resonanceNames[tankType]] for tankType in tankTypes}
ehpAgainstDamageType = [sum(pattern.calculateEhp(fit).values()) for pattern in damagePatterns]
ehpAgainstDamageTypeStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehpAgainstDamageType]
# not used for now. maybe will be improved later
# def formattedOutput():
# return \
# " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \
# "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \
# "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \
# "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \
# "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"])
def generalOutput():
return \
"EHP: {:>} (Em: {:>}, Th: {:>}, Kin: {:>}, Exp: {:>})\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \
"Shield: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%})\n".format(ehpStr[0], *resists["shield"]) + \
"Armor: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%})\n".format(ehpStr[1], *resists["armor"]) + \
"Hull: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%})\n".format(ehpStr[2], *resists["hull"])
return generalOutput()
def repsSection(fit):
""" Returns the text of the repairs section"""
selfRep = [fit.effectiveTank[tankType + "Repair"] for tankType in tankTypes]
sustainRep = [fit.effectiveSustainableTank[tankType + "Repair"] for tankType in tankTypes]
remoteRepObj = fit.getRemoteReps()
remoteRep = [remoteRepObj.shield, remoteRepObj.armor, remoteRepObj.hull]
shieldRegen = [fit.effectiveSustainableTank["passiveShield"], 0, 0]
shieldRechargeModuleMultipliers = [module.item.attributes["shieldRechargeRateMultiplier"].value for module in
fit.modules if
module.item and "shieldRechargeRateMultiplier" in module.item.attributes]
shieldRechargeMultiplierByModules = reduce(lambda x, y: x * y, shieldRechargeModuleMultipliers, 1)
if shieldRechargeMultiplierByModules >= 0.9: # If the total affect of modules on the shield recharge is negative or insignificant, we don't care about it
shieldRegen[0] = 0
totalRep = list(zip(selfRep, remoteRep, shieldRegen))
totalRep = list(map(sum, totalRep))
selfRep.append(sum(selfRep))
sustainRep.append(sum(sustainRep))
remoteRep.append(sum(remoteRep))
shieldRegen.append(sum(shieldRegen))
totalRep.append(sum(totalRep))
totalSelfRep = selfRep[-1]
totalRemoteRep = remoteRep[-1]
totalShieldRegen = shieldRegen[-1]
text = ""
if sum(totalRep) > 0: # Most commonly, there are no reps at all; then we skip this section
singleTypeRep = None
singleTypeRepName = None
if totalRemoteRep == 0 and totalShieldRegen == 0: # Only self rep
singleTypeRep = selfRep[:-1]
singleTypeRepName = "Self"
if totalSelfRep == 0 and totalShieldRegen == 0: # Only remote rep
singleTypeRep = remoteRep[:-1]
singleTypeRepName = "Remote"
if totalSelfRep == 0 and totalRemoteRep == 0: # Only shield regen
singleTypeRep = shieldRegen[:-1]
singleTypeRepName = "Regen"
if singleTypeRep and sum(
x > 0 for x in singleTypeRep) == 1: # Only one type of reps and only one tank type is repaired
index = next(i for i, v in enumerate(singleTypeRep) if v > 0)
if singleTypeRepName == "Regen":
text += "Shield regeneration: {} EHP/s".format(formatAmount(singleTypeRep[index], 3, 0, 9))
else:
text += "{} {} repair: {} EHP/s".format(singleTypeRepName, tankTypes[index],
formatAmount(singleTypeRep[index], 3, 0, 9))
if (singleTypeRepName == "Self") and (sustainRep[index] != singleTypeRep[index]):
text += " (Sustained: {} EHP/s)".format(formatAmount(sustainRep[index], 3, 0, 9))
text += "\n"
else: # Otherwise show a table
selfRepStr = [formatAmount(rep, 3, 0, 9) for rep in selfRep]
sustainRepStr = [formatAmount(rep, 3, 0, 9) for rep in sustainRep]
remoteRepStr = [formatAmount(rep, 3, 0, 9) for rep in remoteRep]
shieldRegenStr = [formatAmount(rep, 3, 0, 9) if rep != 0 else "" for rep in shieldRegen]
totalRepStr = [formatAmount(rep, 3, 0, 9) for rep in totalRep]
header = "REPS "
lines = [
"Shield ",
"Armor ",
"Hull ",
"Total "
]
showSelfRepColumn = totalSelfRep > 0
showSustainRepColumn = sustainRep != selfRep
showRemoteRepColumn = totalRemoteRep > 0
showShieldRegenColumn = totalShieldRegen > 0
if showSelfRepColumn + showSustainRepColumn + showRemoteRepColumn + showShieldRegenColumn > 1:
header += "{:>7} ".format("TOTAL")
lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, totalRepStr)]
if showSelfRepColumn:
header += "{:>7} ".format("SELF")
lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, selfRepStr)]
if showSustainRepColumn:
header += "{:>7} ".format("SUST")
lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, sustainRepStr)]
if showRemoteRepColumn:
header += "{:>7} ".format("REMOTE")
lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, remoteRepStr)]
if showShieldRegenColumn:
header += "{:>7} ".format("REGEN")
lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, shieldRegenStr)]
text += header + "\n"
repsByTank = zip(totalRep, selfRep, sustainRep, remoteRep, shieldRegen)
for line in lines:
reps = next(repsByTank)
if sum(reps) > 0:
text += line + "\n"
return text
def miscSection(fit):
text = ""
text += "Speed: {} m/s\n".format(formatAmount(fit.maxSpeed, 3, 0, 0))
text += "Signature: {} m\n".format(formatAmount(fit.ship.getModifiedItemAttr("signatureRadius"), 3, 0, 9))
text += "Capacitor: {} GJ".format(formatAmount(fit.ship.getModifiedItemAttr("capacitorCapacity"), 3, 0, 9))
capState = fit.capState
if fit.capStable:
text += " (Stable at {0:.0f}%)".format(capState)
else:
text += " (Lasts {})".format("%ds" % capState if capState <= 60 else "%dm%ds" % divmod(capState, 60))
text += "\n"
text += "Targeting range: {} km\n".format(formatAmount(fit.maxTargetRange / 1000, 3, 0, 0))
text += "Scan resolution: {0:.0f} mm\n".format(fit.ship.getModifiedItemAttr("scanResolution"))
text += "Sensor strength: {}\n".format(formatAmount(fit.scanStrength, 3, 0, 0))
return text
def exportFitStats(fit, callback):
"""
Returns the text of the stats export of the given fit
"""
sections = filter(None, (firepowerSection(fit), # Prune empty sections
tankSection(fit),
repsSection(fit),
miscSection(fit)))
text = "{} ({})\n".format(fit.name, fit.ship.name) + "\n"
text += "\n".join(sections)
if callback:
callback(text)
else:
return text

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

@@ -0,0 +1,524 @@
[
{
"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

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
{
"1": {
"name": "Tech I",
"nameID": 66672
},
"2": {
"iconID": 24150,
"iconSuffix": "tech2",
"name": "Tech II",
"nameID": 66673
},
"3": {
"iconID": 24147,
"iconSuffix": "storyline",
"name": "Storyline",
"nameID": 66674
},
"4": {
"iconID": 24146,
"iconSuffix": "faction",
"name": "Faction",
"nameID": 66675
},
"5": {
"iconID": 24149,
"iconSuffix": "officer",
"name": "Officer",
"nameID": 66676
},
"6": {
"description": "Modules found in deadspace.",
"descriptionID": 66671,
"iconID": 24148,
"iconSuffix": "deadspace",
"name": "Deadspace",
"nameID": 66677
},
"7": {
"name": "Frigates",
"nameID": 66678
},
"8": {
"name": "Elite Frigates",
"nameID": 66679
},
"9": {
"name": "Commander Frigates",
"nameID": 66680
},
"10": {
"name": "Destroyer",
"nameID": 66681
},
"11": {
"name": "Cruiser",
"nameID": 66682
},
"12": {
"name": "Elite Cruiser",
"nameID": 66683
},
"13": {
"name": "Commander Cruiser",
"nameID": 66684
},
"14": {
"iconID": 24151,
"iconSuffix": "tech3",
"name": "Tech III",
"nameID": 66685
},
"15": {
"iconID": 24152,
"iconSuffix": "abyssal",
"name": "Abyssal",
"nameID": 317771
},
"17": {
"description": "This item is only available through the New Eden Store or exclusive offers.",
"descriptionID": 317894,
"iconID": 24153,
"iconSuffix": "premium",
"name": "Premium",
"nameID": 317893
},
"19": {
"description": "This item is only available for a limited time.",
"descriptionID": 317896,
"iconID": 24154,
"iconSuffix": "limited",
"name": "Limited Time",
"nameID": 317895
},
"52": {
"iconID": 24155,
"iconSuffix": "structureFaction",
"name": "Structure Faction",
"nameID": 550638
},
"53": {
"iconID": 24156,
"iconSuffix": "structureTech2",
"name": "Structure Tech II",
"nameID": 550639
},
"54": {
"iconID": 24157,
"iconSuffix": "structureTech1",
"name": "Structure Tech I",
"nameID": 550644
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,273 @@
{
"0": {
"categoryID": 0,
"categoryName": "#System",
"categoryNameID": 63539,
"published": false
},
"1": {
"categoryID": 1,
"categoryName": "Owner",
"categoryNameID": 63540,
"published": false
},
"10": {
"categoryID": 10,
"categoryName": "Trading",
"categoryNameID": 63548,
"published": false
},
"11": {
"categoryID": 11,
"categoryName": "Entity",
"categoryNameID": 63549,
"published": false
},
"14": {
"categoryID": 14,
"categoryName": "Bonus",
"categoryNameID": 63550,
"iconID": 0,
"published": false
},
"16": {
"categoryID": 16,
"categoryName": "Skill",
"categoryNameID": 63551,
"iconID": 33,
"published": true
},
"17": {
"categoryID": 17,
"categoryName": "Commodity",
"categoryNameID": 63552,
"iconID": 0,
"published": true
},
"18": {
"categoryID": 18,
"categoryName": "Drone",
"categoryNameID": 63553,
"iconID": 0,
"published": true,
"sofBuildClass": "ship"
},
"2": {
"categoryID": 2,
"categoryName": "Celestial",
"categoryNameID": 63541,
"published": true
},
"20": {
"categoryID": 20,
"categoryName": "Implant",
"categoryNameID": 63554,
"iconID": 0,
"published": true
},
"22": {
"categoryID": 22,
"categoryName": "Deployable",
"categoryNameID": 63555,
"iconID": 0,
"published": true
},
"23": {
"categoryID": 23,
"categoryName": "Starbase",
"categoryNameID": 63556,
"iconID": 0,
"published": true
},
"24": {
"categoryID": 24,
"categoryName": "Reaction",
"categoryNameID": 63557,
"iconID": 0,
"published": true
},
"25": {
"categoryID": 25,
"categoryName": "Asteroid",
"categoryNameID": 63558,
"published": true
},
"26": {
"categoryID": 26,
"categoryName": "WorldSpace",
"categoryNameID": 63568,
"published": false
},
"29": {
"categoryID": 29,
"categoryName": "Abstract",
"categoryNameID": 63559,
"published": false
},
"3": {
"categoryID": 3,
"categoryName": "Station",
"categoryNameID": 63542,
"published": false
},
"30": {
"categoryID": 30,
"categoryName": "Apparel",
"categoryNameID": 63572,
"published": true
},
"32": {
"categoryID": 32,
"categoryName": "Subsystem",
"categoryNameID": 63562,
"published": true
},
"34": {
"categoryID": 34,
"categoryName": "Ancient Relics",
"categoryNameID": 63561,
"published": true
},
"35": {
"categoryID": 35,
"categoryName": "Decryptors",
"categoryNameID": 63563,
"published": true
},
"350001": {
"categoryID": 350001,
"categoryName": "Infantry",
"categoryNameID": 267649,
"published": false
},
"39": {
"categoryID": 39,
"categoryName": "Infrastructure Upgrades",
"categoryNameID": 63565,
"published": true
},
"4": {
"categoryID": 4,
"categoryName": "Material",
"categoryNameID": 63543,
"iconID": 22,
"published": true
},
"40": {
"categoryID": 40,
"categoryName": "Sovereignty Structures",
"categoryNameID": 63564,
"published": true
},
"41": {
"categoryID": 41,
"categoryName": "Planetary Interaction",
"categoryNameID": 63569,
"published": true
},
"42": {
"categoryID": 42,
"categoryName": "Planetary Resources",
"categoryNameID": 63566,
"published": true
},
"43": {
"categoryID": 43,
"categoryName": "Planetary Commodities",
"categoryNameID": 63567,
"published": true
},
"46": {
"categoryID": 46,
"categoryName": "Orbitals",
"categoryNameID": 63570,
"published": true
},
"49": {
"categoryID": 49,
"categoryName": "Placeables",
"categoryNameID": 63571,
"published": false
},
"5": {
"categoryID": 5,
"categoryName": "Accessories",
"categoryNameID": 63560,
"iconID": 33,
"published": true
},
"53": {
"categoryID": 53,
"categoryName": "Effects",
"categoryNameID": 63573,
"published": false
},
"54": {
"categoryID": 54,
"categoryName": "Lights",
"categoryNameID": 63574,
"published": false
},
"59": {
"categoryID": 59,
"categoryName": "Cells",
"categoryNameID": 235965,
"published": false
},
"6": {
"categoryID": 6,
"categoryName": "Ship",
"categoryNameID": 63544,
"published": true
},
"63": {
"categoryID": 63,
"categoryName": "Special Edition Assets",
"categoryNameID": 285070,
"published": true
},
"65": {
"categoryID": 65,
"categoryName": "Structure",
"categoryNameID": 308338,
"published": true
},
"66": {
"categoryID": 66,
"categoryName": "Structure Module",
"categoryNameID": 308340,
"published": true
},
"7": {
"categoryID": 7,
"categoryName": "Module",
"categoryNameID": 63545,
"iconID": 67,
"published": true
},
"8": {
"categoryID": 8,
"categoryName": "Charge",
"categoryNameID": 63546,
"published": true
},
"87": {
"categoryID": 87,
"categoryName": "Fighter",
"categoryNameID": 510368,
"published": true
},
"9": {
"categoryID": 9,
"categoryName": "Blueprint",
"categoryNameID": 63547,
"iconID": 21,
"published": true
},
"91": {
"categoryID": 91,
"categoryName": "SKINs",
"categoryNameID": 531338,
"published": true
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
[
{
"field_name": "client_build",
"field_value": 1595463
},
{
"field_name": "dump_time",
"field_value": 1572435719
}
]

19443
staticdata/phobos/traits.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
version: v2.13.0
version: v2.14.0