mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-13 07:47:14 +03:00
Add mass and range to E:D Shipyard and Coriolis.
Means that these files can be directly loaded into ETN.
This commit is contained in:
parent
2cd52e8377
commit
c5b2c4b34c
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "coriolis-data"]
|
||||
path = coriolis-data
|
||||
url = git@github.com:cmmcleod/coriolis-data.git
|
@ -5,7 +5,8 @@ This app downloads commodity market and other station data from the game [Elite:
|
||||
|
||||
* sends the data to the [Elite Dangerous Data Network](http://eddn-gateway.elite-markets.net/) (“EDDN”) from where you and others can use it via online trading tools such as [eddb](http://eddb.io/), [Elite Trade Net](http://etn.io/), [Inara](http://inara.cz), [ED-TD](http://ed-td.space/), [Roguey's](http://roguey.co.uk/elite-dangerous/), etc.
|
||||
* saves the data to files on your computer that you can load into trading tools such as [Slopey's BPC Market Tool](https://forums.frontier.co.uk/showthread.php?t=76081), [Trade Dangerous](https://bitbucket.org/kfsone/tradedangerous/wiki/Home), [Thrudd's Trading Tools](http://www.elitetradingtool.co.uk/), [Inara](http://inara.cz), [mEDI's Elite Tools](https://github.com/mEDI-S/mEDI_s-Elite-Tools), etc.
|
||||
* saves a record of your ship loadout and/or flight log.
|
||||
* saves a record of your ship loadout to files on your computer that you can load into [E:D Shipyard](http://www.edshipyard.com), [Coriolis](http://coriolis.io) or [Elite Trade Net](http://etn.io/).
|
||||
* saves your flight log to a file on your computer and/or sends it to [Elite: Dangerous Star Map](http://www.edsm.net/).
|
||||
|
||||
Usage
|
||||
--------
|
||||
@ -61,7 +62,7 @@ This app can save a variety of data in a variety of formats:
|
||||
* CSV format file - saves commodity market data as files that you can upload to [Thrudd's Trading Tools](http://www.elitetradingtool.co.uk/), [Inara](http://inara.cz) or [mEDI's Elite Tools](https://github.com/mEDI-S/mEDI_s-Elite-Tools).
|
||||
|
||||
* Ship loadout
|
||||
* After every outfitting change saves a record of your ship loadout as a file that you can open in a text editor and that you can import into [E:D Shipyard](http://www.edshipyard.com) or [Coriolis](http://coriolis.io).
|
||||
* After every outfitting change saves a record of your ship loadout as a file that you can open in a text editor and that you can import into [E:D Shipyard](http://www.edshipyard.com), [Coriolis](http://coriolis.io) or [Elite Trade Net](http://etn.io/).
|
||||
|
||||
* Flight log
|
||||
* Elite Dangerous Star Map - sends a record of your location to “[EDSM](http://www.edsm.net/)” where you can view your logs under My account → Exploration Logs, and optionally add private comments about a system.
|
||||
|
1
coriolis-data
Submodule
1
coriolis-data
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 618127dbb4effb6ad7b6a9352ef32b162d5560f6
|
34
coriolis.py
34
coriolis.py
@ -1,6 +1,7 @@
|
||||
# Export ship loadout in Coriolis format
|
||||
|
||||
from collections import OrderedDict
|
||||
import cPickle
|
||||
import json
|
||||
import os
|
||||
from os.path import join
|
||||
@ -82,6 +83,10 @@ fixup_map = {
|
||||
}
|
||||
|
||||
|
||||
# Ship masses
|
||||
ships = cPickle.load(open(join(config.respath, 'ships.p'), 'rb'))
|
||||
|
||||
|
||||
def export(data, filename=None):
|
||||
|
||||
querytime = config.getint('querytime') or int(time.time())
|
||||
@ -98,6 +103,8 @@ def export(data, filename=None):
|
||||
])),
|
||||
])
|
||||
maxpri = 0
|
||||
mass = 0.0
|
||||
fsd = None
|
||||
|
||||
# Correct module ordering relies on the fact that "Slots" in the data are correctly ordered alphabetically.
|
||||
# Correct hardpoint ordering additionally relies on the fact that "Huge" < "Large" < "Medium" < "Small"
|
||||
@ -123,6 +130,7 @@ def export(data, filename=None):
|
||||
module = outfitting.lookup(v['module'], ship_map)
|
||||
if not module:
|
||||
raise AssertionError('Unknown module %s' % v) # Shouldn't happen
|
||||
mass += module.get('mass', 0)
|
||||
|
||||
thing = OrderedDict([
|
||||
('class',int(module['class'])),
|
||||
@ -138,6 +146,8 @@ def export(data, filename=None):
|
||||
loadout['components'][category]['bulkheads'] = module['name'] # Bulkheads are just strings
|
||||
else:
|
||||
loadout['components'][category][standard_map[module['name']]] = thing
|
||||
if module['name'] == 'Frame Shift Drive':
|
||||
fsd = module # save for range calculation
|
||||
else:
|
||||
# All other items have a "group" member, some also have a "name"
|
||||
if module['name'] in fixup_map:
|
||||
@ -173,8 +183,30 @@ def export(data, filename=None):
|
||||
('priority', maxpri),
|
||||
])
|
||||
|
||||
# Add mass and range
|
||||
assert data['ship']['name'].lower() in companion.ship_map, data['ship']['name']
|
||||
assert companion.ship_map[data['ship']['name'].lower()] in ships, companion.ship_map[data['ship']['name'].lower()]
|
||||
try:
|
||||
# https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184
|
||||
hullMass = ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass']
|
||||
mass += hullMass
|
||||
multiplier = pow(min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass']
|
||||
|
||||
loadout['stats'] = OrderedDict([
|
||||
('hullMass', hullMass),
|
||||
('fuelCapacity', data['ship']['fuel']['main']['capacity']),
|
||||
('cargoCapacity', data['ship']['cargo']['capacity']),
|
||||
('ladenMass', mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity']),
|
||||
('unladenMass', mass),
|
||||
('unladenRange', round(multiplier / (mass + min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel'])), 2)), # fuel for one jump
|
||||
('fullTankRange', round(multiplier / (mass + data['ship']['fuel']['main']['capacity']), 2)),
|
||||
('ladenRange', round(multiplier / (mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity']), 2)),
|
||||
])
|
||||
except:
|
||||
if __debug__: raise
|
||||
|
||||
# Construct description
|
||||
string = json.dumps(loadout, indent=2)
|
||||
string = json.dumps(loadout, indent=2, separators=(',', ': '))
|
||||
|
||||
if filename:
|
||||
with open(filename, 'wt') as h:
|
||||
|
80
eddb.py
80
eddb.py
@ -31,21 +31,23 @@ class EDDB:
|
||||
return (station_id, bool(flags & EDDB.HAS_MARKET), bool(flags & EDDB.HAS_OUTFITTING), bool(flags & EDDB.HAS_SHIPYARD))
|
||||
|
||||
|
||||
# build system & station database from files systems.json and stations.json from http://eddb.io/api
|
||||
# build databases from files systems.json, stations.json and modules.json from http://eddb.io/api
|
||||
# and from https://github.com/cmmcleod/coriolis-data
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
|
||||
# still send market and outfitting for currently "suspended" stations -
|
||||
# https://community.elitedangerous.com/galnet/uid/568a999f9657ba5e0986a8de
|
||||
suspended = set([659, 39328, 5672, 30402, 30653, 21901, 11335])
|
||||
# https://community.elitedangerous.com/galnet/uid/569f610b9657ba7d3461ba04
|
||||
suspended = set([7, 10035, 9765, 659, 39328, 5672, 30402, 30653, 21901, 11335])
|
||||
|
||||
# system_name by system_id
|
||||
systems = dict([(x['id'], str(x['name'])) for x in json.loads(open('systems.json').read())])
|
||||
systems = dict([(x['id'], str(x['name'])) for x in json.load(open('systems.json'))])
|
||||
|
||||
stations = json.loads(open('stations.json').read())
|
||||
stations = json.load(open('stations.json'))
|
||||
|
||||
# check that all populated systems have known coordinates
|
||||
coords = dict([(x['id'], x['x'] or x['y'] or x['z']) for x in json.loads(open('systems.json').read())])
|
||||
coords = dict([(x['id'], x['x'] or x['y'] or x['z']) for x in json.load(open('systems.json'))])
|
||||
for x in stations:
|
||||
assert x['system_id'] == 17072 or coords[x['system_id']], (x['system_id'], systems[x['system_id']])
|
||||
|
||||
@ -63,3 +65,71 @@ if __name__ == "__main__":
|
||||
for x in stations])
|
||||
cPickle.dump(station_ids, open('stations.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
|
||||
|
||||
|
||||
# Map eddb's names to names displayed in the in-game shipyard
|
||||
eddb_ship_map = {
|
||||
'Sidewinder Mk. I' : 'Sidewinder',
|
||||
'Eagle Mk. II' : 'Eagle',
|
||||
'Cobra Mk. III' : 'Cobra MkIII',
|
||||
'Cobra MK IV' : 'Cobra MkIV',
|
||||
'Viper Mk III' : 'Viper MkIII',
|
||||
'Viper MK IV' : 'Viper MkIV',
|
||||
}
|
||||
|
||||
# PP modules (see weapon-map in outfitting.py)
|
||||
specials = {
|
||||
'Retributor' : 'Retributor Beam Laser',
|
||||
'Pack-Hound' : 'Pack-Hound Missile Rack',
|
||||
'Mining Lance' : 'Mining Lance Beam Laser',
|
||||
'Enforcer' : 'Enforcer Cannon',
|
||||
'Advanced' : 'Advanced Plasma Accelerator',
|
||||
'Distruptor' : 'Pulse Disruptor Laser',
|
||||
'Cytoscrambler' : 'Cytoscrambler Burst Laser',
|
||||
'Imperial Hammer' : 'Imperial Hammer Rail Gun',
|
||||
'Pacifier' : 'Pacifier Frag-Cannon',
|
||||
'Prismatic' : 'Prismatic Shield Generator',
|
||||
}
|
||||
|
||||
# Module masses
|
||||
modules = {}
|
||||
for m in json.load(open('modules.json')):
|
||||
# ignore mount and guidance, and convert strings to ascii to save space
|
||||
key = (specials.get(m['name'], str(m['name'] or m['group']['name'])),
|
||||
m['ship'] and eddb_ship_map.get(m['ship'], str(m['ship'])),
|
||||
str(m['class']),
|
||||
str(m['rating']))
|
||||
if key in modules:
|
||||
# Test our assumption that mount and guidance don't affect mass
|
||||
assert modules[key]['mass'] == m.get('mass', 0), '%s !=\n%s' % (key, m)
|
||||
else:
|
||||
modules[key] = { 'mass': m.get('mass', 0) } # Some modules don't have mass
|
||||
|
||||
# Add FSD data from Coriolis
|
||||
for m in json.load(open('coriolis-data/components/standard/frame_shift_drive.json')).values():
|
||||
key = ('Frame Shift Drive', None, str(m['class']), str(m['rating']))
|
||||
assert key in modules, key
|
||||
modules[key].update({
|
||||
'optmass' : m['optmass'],
|
||||
'maxfuel' : m['maxfuel'],
|
||||
'fuelmul' : m['fuelmul'],
|
||||
'fuelpower' : m['fuelpower'],
|
||||
})
|
||||
cPickle.dump(modules, open('modules.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
|
||||
|
||||
|
||||
# Map Coriolis's names to names displayed in the in-game shipyard
|
||||
coriolis_ship_map = {
|
||||
'Cobra Mk III' : 'Cobra MkIII',
|
||||
'Cobra Mk IV' : 'Cobra MkIV',
|
||||
'Viper' : 'Viper MkIII',
|
||||
'Viper Mk IV' : 'Viper MkIV',
|
||||
}
|
||||
|
||||
# Ship masses
|
||||
ships = {}
|
||||
for f in os.listdir('coriolis-data/ships'):
|
||||
if not f.endswith('.json'): continue
|
||||
for m in json.load(open(join('coriolis-data/ships', f))).values():
|
||||
ships[coriolis_ship_map.get(m['properties']['name'], str(m['properties']['name']))] = { 'hullMass' : m['properties']['hullMass'] }
|
||||
cPickle.dump(ships, open('ships.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
|
||||
|
||||
|
3
eddn.py
3
eddn.py
@ -73,12 +73,13 @@ def export_commodities(data):
|
||||
|
||||
def export_outfitting(data):
|
||||
# *Do* send empty modules list - implies station has no outfitting
|
||||
schemakeys = ['category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating']
|
||||
modules = []
|
||||
for v in data['lastStarport'].get('modules', {}).itervalues():
|
||||
try:
|
||||
module = outfitting.lookup(v, ship_map)
|
||||
if module:
|
||||
modules.append(module)
|
||||
modules.append({ k: module[k] for k in schemakeys if k in module }) # just the relevant keys
|
||||
except AssertionError as e:
|
||||
if __debug__: print 'Outfitting: %s' % e # Silently skip unrecognized modules
|
||||
except:
|
||||
|
26
loadout.py
26
loadout.py
@ -1,6 +1,7 @@
|
||||
# Export ship loadout in E:D Shipyard format
|
||||
|
||||
from collections import defaultdict
|
||||
import cPickle
|
||||
import os
|
||||
from os.path import join
|
||||
import re
|
||||
@ -36,6 +37,11 @@ slot_map = {
|
||||
'fueltank' : 'FS',
|
||||
}
|
||||
|
||||
|
||||
# Ship masses
|
||||
ships = cPickle.load(open(join(config.respath, 'ships.p'), 'rb'))
|
||||
|
||||
|
||||
def export(data, filename=None):
|
||||
|
||||
def class_rating(module):
|
||||
@ -49,6 +55,8 @@ def export(data, filename=None):
|
||||
querytime = config.getint('querytime') or int(time.time())
|
||||
|
||||
loadout = defaultdict(list)
|
||||
mass = 0.0
|
||||
fsd = None
|
||||
|
||||
for slot in sorted(data['ship']['modules']):
|
||||
|
||||
@ -60,6 +68,7 @@ def export(data, filename=None):
|
||||
if not module: continue
|
||||
|
||||
cr = class_rating(module)
|
||||
mass += module.get('mass', 0)
|
||||
|
||||
# Specials
|
||||
if module['name'] in ['Fuel Tank', 'Cargo Rack']:
|
||||
@ -67,6 +76,9 @@ def export(data, filename=None):
|
||||
else:
|
||||
name = module['name']
|
||||
|
||||
if name == 'Frame Shift Drive':
|
||||
fsd = module # save for range calculation
|
||||
|
||||
for s in slot_map:
|
||||
if slot.lower().startswith(s):
|
||||
loadout[slot_map[s]].append(cr + name)
|
||||
@ -92,6 +104,20 @@ def export(data, filename=None):
|
||||
string += '%s: %s\n' % (slot, name)
|
||||
string += '---\nCargo : %d T\nFuel : %d T\n' % (data['ship']['cargo']['capacity'], data['ship']['fuel']['main']['capacity'])
|
||||
|
||||
# Add mass and range
|
||||
assert data['ship']['name'].lower() in companion.ship_map, data['ship']['name']
|
||||
assert companion.ship_map[data['ship']['name'].lower()] in ships, companion.ship_map[data['ship']['name'].lower()]
|
||||
try:
|
||||
# https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184
|
||||
mass += ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass']
|
||||
string += 'Mass : %.1f T empty\n %.1f T full\n' % (mass, mass + data['ship']['fuel']['main']['capacity']+ data['ship']['cargo']['capacity'])
|
||||
multiplier = pow(min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass']
|
||||
string += 'Range : %.2f LY unladen\n %.2f LY laden\n' % (
|
||||
multiplier / (mass + data['ship']['fuel']['main']['capacity']),
|
||||
multiplier / (mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity']))
|
||||
except:
|
||||
if __debug__: raise
|
||||
|
||||
if filename:
|
||||
with open(filename, 'wt') as h:
|
||||
h.write(string)
|
||||
|
@ -1,3 +1,5 @@
|
||||
import cPickle
|
||||
from os.path import join
|
||||
import time
|
||||
|
||||
import companion
|
||||
@ -209,6 +211,10 @@ internal_map = {
|
||||
}
|
||||
|
||||
|
||||
# Module mass, FSD data etc
|
||||
moduledata = cPickle.load(open(join(config.respath, 'modules.p'), 'rb'))
|
||||
|
||||
|
||||
# Given a module description from the Companion API returns a description of the module in the form of a
|
||||
# dict { category, name, [mount], [guidance], [ship], rating, class } using the same terms found in the
|
||||
# English langauge game. For fitted modules, dict also includes { enabled, priority }.
|
||||
@ -319,8 +325,19 @@ def lookup(module, ship_map):
|
||||
if 'on' in module and 'priority' in module:
|
||||
new['enabled'], new['priority'] = module['on'], module['priority'] # priority is zero-based
|
||||
|
||||
# Extra module data
|
||||
key = (new['name'], 'ship' in new and companion.ship_map.get(name[0]) or None, new['class'], new['rating'])
|
||||
if __debug__:
|
||||
assert key in moduledata, key
|
||||
m = moduledata.get(key, {})
|
||||
if new['name'] == 'Frame Shift Drive':
|
||||
assert 'mass' in m and 'optmass' in m and 'maxfuel' in m and 'fuelmul' in m and 'fuelpower' in m, m
|
||||
else:
|
||||
assert 'mass' in m, m
|
||||
new.update(moduledata.get(key, {}))
|
||||
|
||||
# check we've filled out mandatory fields
|
||||
for thing in ['category', 'name', 'class', 'rating']:
|
||||
for thing in ['category', 'name', 'class', 'rating']: # Don't consider mass etc as mandatory
|
||||
if not new.get(thing): raise AssertionError('%s: failed to set %s' % (module['id'], thing))
|
||||
if new['category'] == 'hardpoint' and not new.get('mount'):
|
||||
raise AssertionError('%s: failed to set %s' % (module['id'], 'mount'))
|
||||
|
4
setup.py
4
setup.py
@ -69,7 +69,7 @@ if sys.platform=='darwin':
|
||||
'frameworks': [ 'Sparkle.framework' ],
|
||||
'excludes': [ 'PIL', 'simplejson' ],
|
||||
'iconfile': '%s.icns' % APPNAME,
|
||||
'resources': ['snd_good.wav', 'snd_bad.wav', 'stations.p', 'systems.p'],
|
||||
'resources': ['snd_good.wav', 'snd_bad.wav', 'modules.p', 'ships.p', 'stations.p', 'systems.p'],
|
||||
'semi_standalone': True,
|
||||
'site_packages': False,
|
||||
'plist': {
|
||||
@ -106,6 +106,8 @@ elif sys.platform=='win32':
|
||||
'WinSparkle.pdb', # For debugging - don't include in package
|
||||
'snd_good.wav',
|
||||
'snd_bad.wav',
|
||||
'modules.p',
|
||||
'ships.p',
|
||||
'stations.p',
|
||||
'systems.p',
|
||||
'%s.VisualElementsManifest.xml' % APPNAME,
|
||||
|
5
ships.p
Normal file
5
ships.p
Normal file
@ -0,0 +1,5 @@
|
||||
€}q(UFederal Assault Ship}qUhullMassqMàsU
|
||||
Viper MkIVq}qhK¾sUFederal Gunship}qhMDsUViper MkIIIq}qhK<sUImperial Eagle}q hK2sUImperial Cutter}q
|
||||
hMLsUEagle}qhK2sUPython}qhM^sUType-6 Transporter}q
hK›sUFer-de-Lance}qhKúsUVulture}qhKæsUAdder}qhK#sUType-7 Transporter}qhM¤sUFederal Corvette}qhM„sUDiamondback Scout}qhKªsUHauler}qhKsUDiamondback Explorer}qhM*sUFederal Dropship}qhMDsUCobra MkIIIq}qhK´sUOrca}qhMDsU
|
||||
Sidewinder}qhKsUAsp Explorer}qhMsUType-9 Heavy}qhMèsU
|
||||
Cobra MkIVq}qhKÒsUImperial Courier}qhK#sUAnaconda}q hM<EFBFBD>sUImperial Clipper}q!hM<EFBFBD>sUKeelback}q"hK´sU Asp Scout}q#hK–su.
|
Loading…
x
Reference in New Issue
Block a user