# Export ship loadout in E:D Shipyard plain text format import pickle from collections import defaultdict import os from os.path import join import re import time from config import config import companion import outfitting from typing import Dict, Union, List __Module = Dict[str, Union[str, List[str]]] # Map API ship names to E:D Shipyard ship names ship_map = companion.ship_map.copy() ship_map.update( { 'cobramkiii': 'Cobra Mk III', 'cobramkiv' : 'Cobra Mk IV', 'viper' : 'Viper', 'viper_mkiv': 'Viper Mk IV', } ) # Map API slot names to E:D Shipyard slot names slot_map = { 'hugehardpoint' : 'H', 'largehardpoint' : 'L', 'mediumhardpoint' : 'M', 'smallhardpoint' : 'S', 'tinyhardpoint' : 'U', 'armour' : 'BH', 'powerplant' : 'RB', 'mainengines' : 'TM', 'frameshiftdrive' : 'FH', 'lifesupport' : 'EC', 'powerdistributor' : 'PC', 'radar' : 'SS', 'fueltank' : 'FS', 'military' : 'MC', } # Ship masses # TODO: prefer something other than pickle for this storage (dev readability, security) ships = pickle.load(open(join(config.respath, 'ships.p'), 'rb')) # Export ship loadout in E:D Shipyard plain text format def export(data, filename=None): def class_rating(module: __Module): mod_class = module['class'] mod_rating = module['rating'] mod_mount = module.get('mount') mod_guidance = module.get('guidance') ret = '{mod_class}{rating}'.format(mod_class=mod_class, rating=mod_rating) if 'guidance' in module: # Missiles ret += "/{mount}{guidance}".format( mount=mod_mount[0] if mod_mount is not None else 'F', guidance=mod_guidance[0], ) elif 'mount' in module: # Hardpoints ret += "/{mount}".format(mount=mod_mount) elif 'Cabin' in module['name']: # Passenger cabins ret += "/{name}".format(name=module['name'][0]) return ret + ' ' querytime = config.getint('querytime') or int(time.time()) loadout = defaultdict(list) mass = 0.0 fuel = 0 cargo = 0 fsd = None jumpboost = 0 for slot in sorted(data['ship']['modules']): v = data['ship']['modules'][slot] try: if not v: continue module: __Module = outfitting.lookup(v['module'], ship_map) if not module: continue cr = class_rating(module) mods = v.get('modifications') or v.get('WorkInProgress_modifications') or {} if mods.get('OutfittingFieldType_Mass'): mass += (module.get('mass', 0) * mods['OutfittingFieldType_Mass']['value']) else: mass += module.get('mass', 0) # Specials if 'Fuel Tank' in module['name']: fuel += 2**int(module['class']) name = '{} (Capacity: {})'.format(module['name'], 2**int(module['class'])) elif 'Cargo Rack' in module['name']: cargo += 2**int(module['class']) name = '{} (Capacity: {})'.format(module['name'], 2**int(module['class'])) else: name = module['name'] if name == 'Frame Shift Drive': fsd = module # save for range calculation if mods.get('OutfittingFieldType_FSDOptimalMass'): fsd['optmass'] *= mods['OutfittingFieldType_FSDOptimalMass']['value'] if mods.get('OutfittingFieldType_MaxFuelPerJump'): fsd['maxfuel'] *= mods['OutfittingFieldType_MaxFuelPerJump']['value'] jumpboost += module.get('jumpboost', 0) for s in slot_map: if slot.lower().startswith(s): loadout[slot_map[s]].append(cr + name) break else: if slot.lower().startswith('slot'): loadout[slot[-1]].append(cr + name) elif __debug__ and not slot.lower().startswith('planetaryapproachsuite'): print('EDShipyard: Unknown slot {}'.format(slot)) except AssertionError as e: if __debug__: print('EDShipyard: {}'.format(e)) continue # Silently skip unrecognized modules except Exception: if __debug__: raise # Construct description ship = ship_map.get(data['ship']['name'].lower(), data['ship']['name']) if data['ship'].get('shipName') is not None: _ships = '{}, {}'.format(ship, data['ship']['shipName']) else: _ships = ship string = '[{}]\n'.format(_ships) SLOT_TYPES = ( 'H', 'L', 'M', 'S', 'U', None, 'BH', 'RB', 'TM', 'FH', 'EC', 'PC', 'SS', 'FS', None, 'MC', None, '9', '8', '7', '6', '5', '4', '3', '2', '1' ) for slot in SLOT_TYPES: if not slot: string += '\n' elif slot in loadout: for name in loadout[slot]: string += '{}: {}\n'.format(slot, name) string += '---\nCargo : {} T\nFuel : {} T\n'.format(cargo, fuel) # 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: mass += ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass'] string += 'Mass : {:.2f} T empty\n {:.2f} T full\n'.format(mass, mass + fuel + cargo) multiplier = pow(min(fuel, fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass'] string += 'Range : {:.2f} LY unladen\n {:.2f} LY laden\n'.format( multiplier / (mass + fuel) + jumpboost, multiplier / (mass + fuel + cargo) + jumpboost ) except Exception: if __debug__: raise if filename: with open(filename, 'wt') as h: h.write(string) return # Look for last ship of this type ship = companion.ship_file_name(data['ship'].get('shipName'), data['ship']['name']) regexp = re.compile(re.escape(ship) + r'\.\d{4}-\d\d-\d\dT\d\d\.\d\d\.\d\d\.txt') oldfiles = sorted([x for x in os.listdir(config.get('outdir')) if regexp.match(x)]) if oldfiles: with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h: if h.read() == string: return # same as last time - don't write # Write filename = join(config.get('outdir'), '{}.{}.txt'.format( ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))) ) with open(filename, 'wt') as h: h.write(string)