mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-18 09:57:40 +03:00
Merge pull request #2098 from HullSeals/enhancement/2051/remove-the-pickle
[2051] Convert Pickle to Json
This commit is contained in:
commit
48644f7978
6
build.py
6
build.py
@ -73,8 +73,10 @@ def generate_data_files(
|
|||||||
"ChangeLog.md",
|
"ChangeLog.md",
|
||||||
"snd_good.wav",
|
"snd_good.wav",
|
||||||
"snd_bad.wav",
|
"snd_bad.wav",
|
||||||
"modules.p",
|
"modules.p", # TODO: Remove in 6.0
|
||||||
"ships.p",
|
"resources/modules.json",
|
||||||
|
"resources/ships.json",
|
||||||
|
"ships.p", # TODO: Remove in 6.0
|
||||||
f"{app_name}.VisualElementsManifest.xml",
|
f"{app_name}.VisualElementsManifest.xml",
|
||||||
f"{app_name}.ico",
|
f"{app_name}.ico",
|
||||||
"EDMarketConnector - TRACE.bat",
|
"EDMarketConnector - TRACE.bat",
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
"""
|
||||||
Build ship and module databases from https://github.com/EDCD/coriolis-data/ .
|
coriolis-update-files.py - Build ship and module databases from https://github.com/EDCD/coriolis-data/.
|
||||||
|
|
||||||
This script also utilise the file outfitting.csv. Due to how collate.py
|
Copyright (c) EDCD, All Rights Reserved
|
||||||
both reads and writes to this file a local copy is used, in the root of the
|
Licensed under the GNU General Public License.
|
||||||
project structure, is used for this purpose. If you want to utilise the
|
See LICENSE file.
|
||||||
|
|
||||||
|
This script also utilizes the file outfitting.csv. Due to how collate.py
|
||||||
|
both reads and writes to this file, a local copy in the root of the
|
||||||
|
project structure is used for this purpose. If you want to utilize the
|
||||||
FDevIDs/ version of the file, copy it over the local one.
|
FDevIDs/ version of the file, copy it over the local one.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import pickle
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
@ -29,7 +31,9 @@ if __name__ == "__main__":
|
|||||||
# Regenerate coriolis-data distribution
|
# Regenerate coriolis-data distribution
|
||||||
subprocess.check_call('npm install', cwd='coriolis-data', shell=True, stdout=sys.stdout, stderr=sys.stderr)
|
subprocess.check_call('npm install', cwd='coriolis-data', shell=True, stdout=sys.stdout, stderr=sys.stderr)
|
||||||
|
|
||||||
data = json.load(open('coriolis-data/dist/index.json'))
|
file_path = 'coriolis-data/dist/index.json'
|
||||||
|
with open(file_path) as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
# Symbolic name from in-game name
|
# Symbolic name from in-game name
|
||||||
reverse_ship_map = {v: k for k, v in list(ship_name_map.items())}
|
reverse_ship_map = {v: k for k, v in list(ship_name_map.items())}
|
||||||
@ -44,11 +48,12 @@ if __name__ == "__main__":
|
|||||||
name = coriolis_ship_map.get(m['properties']['name'], str(m['properties']['name']))
|
name = coriolis_ship_map.get(m['properties']['name'], str(m['properties']['name']))
|
||||||
assert name in reverse_ship_map, name
|
assert name in reverse_ship_map, name
|
||||||
ships[name] = {'hullMass': m['properties']['hullMass']}
|
ships[name] = {'hullMass': m['properties']['hullMass']}
|
||||||
for i in range(len(bulkheads)):
|
for i, bulkhead in enumerate(bulkheads):
|
||||||
modules['_'.join([reverse_ship_map[name], 'armour', bulkheads[i]])] = {'mass': m['bulkheads'][i]['mass']}
|
modules['_'.join([reverse_ship_map[name], 'armour', bulkhead])] = {'mass': m['bulkheads'][i]['mass']}
|
||||||
|
|
||||||
ships = OrderedDict([(k, ships[k]) for k in sorted(ships)]) # sort for easier diffing
|
ships = OrderedDict([(k, ships[k]) for k in sorted(ships)]) # sort for easier diffing
|
||||||
pickle.dump(ships, open('ships.p', 'wb'))
|
with open("resources/ships.json", "w") as ships_file:
|
||||||
|
json.dump(ships, ships_file, indent=4)
|
||||||
|
|
||||||
# Module masses
|
# Module masses
|
||||||
for cat in list(data['Modules'].values()):
|
for cat in list(data['Modules'].values()):
|
||||||
@ -82,4 +87,5 @@ if __name__ == "__main__":
|
|||||||
add(modules, 'hpt_multicannon_fixed_medium_advanced', {'mass': 4})
|
add(modules, 'hpt_multicannon_fixed_medium_advanced', {'mass': 4})
|
||||||
|
|
||||||
modules = OrderedDict([(k, modules[k]) for k in sorted(modules)]) # sort for easier diffing
|
modules = OrderedDict([(k, modules[k]) for k in sorted(modules)]) # sort for easier diffing
|
||||||
pickle.dump(modules, open('modules.p', 'wb'))
|
with open("resources/modules.json", "w") as modules_file:
|
||||||
|
json.dump(modules, modules_file, indent=4)
|
||||||
|
@ -156,7 +156,7 @@ Before you create a new install each time you should:
|
|||||||
1. `cd coriolis-data`
|
1. `cd coriolis-data`
|
||||||
2. `git pull`
|
2. `git pull`
|
||||||
3. `npm install` - to check it's worked.
|
3. `npm install` - to check it's worked.
|
||||||
3. Run `coriolis-update-files.py` to update `modules.p` and `ships.p`. **NB:
|
3. Run `coriolis-update-files.py` to update `modules.json` and `ships.json`. **NB:
|
||||||
The submodule might have been updated by a GitHub workflow/PR/merge, so
|
The submodule might have been updated by a GitHub workflow/PR/merge, so
|
||||||
be sure to perform this step for every build.**
|
be sure to perform this step for every build.**
|
||||||
4. XXX: Test ?
|
4. XXX: Test ?
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
"""Export ship loadout in ED Shipyard plain text format."""
|
"""Export ship loadout in ED Shipyard plain text format."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import pickle
|
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Dict, List, Union
|
from typing import Union
|
||||||
|
|
||||||
import outfitting
|
import outfitting
|
||||||
import util_ships
|
import util_ships
|
||||||
@ -17,14 +18,15 @@ from EDMCLogging import get_main_logger
|
|||||||
|
|
||||||
logger = get_main_logger()
|
logger = get_main_logger()
|
||||||
|
|
||||||
__Module = Dict[str, Union[str, List[str]]]
|
__Module = dict[str, Union[str, list[str]]] # Have to keep old-style here for compatibility
|
||||||
|
|
||||||
# Map API ship names to ED Shipyard names
|
# Map API ship names to ED Shipyard names
|
||||||
ship_map = ship_name_map.copy()
|
ship_map = ship_name_map.copy()
|
||||||
|
|
||||||
# Ship masses
|
# Ship masses
|
||||||
# TODO: prefer something other than pickle for this storage (dev readability, security)
|
ships_file = config.respath_path / "resources" / "ships.json"
|
||||||
ships = pickle.load(open(pathlib.Path(config.respath_path) / 'ships.p', 'rb'))
|
with open(ships_file, encoding="utf-8") as ships_file_handle:
|
||||||
|
ships = json.load(ships_file_handle)
|
||||||
|
|
||||||
|
|
||||||
def export(data, filename=None) -> None: # noqa: C901, CCR001
|
def export(data, filename=None) -> None: # noqa: C901, CCR001
|
||||||
@ -75,7 +77,6 @@ def export(data, filename=None) -> None: # noqa: C901, CCR001
|
|||||||
jumpboost = 0
|
jumpboost = 0
|
||||||
|
|
||||||
for slot in sorted(data['ship']['modules']):
|
for slot in sorted(data['ship']['modules']):
|
||||||
|
|
||||||
v = data['ship']['modules'][slot]
|
v = data['ship']['modules'][slot]
|
||||||
try:
|
try:
|
||||||
if not v:
|
if not v:
|
||||||
@ -116,15 +117,14 @@ def export(data, filename=None) -> None: # noqa: C901, CCR001
|
|||||||
|
|
||||||
jumpboost += module.get('jumpboost', 0) # type: ignore
|
jumpboost += module.get('jumpboost', 0) # type: ignore
|
||||||
|
|
||||||
for s in slot_map:
|
for slot_prefix, index in slot_map.items():
|
||||||
if slot.lower().startswith(s):
|
if slot.lower().startswith(slot_prefix):
|
||||||
loadout[slot_map[s]].append(cr + name)
|
loadout[index].append(cr + name)
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if slot.lower().startswith('slot'):
|
if slot.lower().startswith('slot'):
|
||||||
loadout[slot[-1]].append(cr + name)
|
loadout[slot[-1]].append(cr + name)
|
||||||
|
|
||||||
elif not slot.lower().startswith('planetaryapproachsuite'):
|
elif not slot.lower().startswith('planetaryapproachsuite'):
|
||||||
logger.debug(f'EDShipyard: Unknown slot {slot}')
|
logger.debug(f'EDShipyard: Unknown slot {slot}')
|
||||||
|
|
||||||
@ -138,7 +138,6 @@ def export(data, filename=None) -> None: # noqa: C901, CCR001
|
|||||||
|
|
||||||
# Construct description
|
# Construct description
|
||||||
ship = ship_map.get(data['ship']['name'].lower(), data['ship']['name'])
|
ship = ship_map.get(data['ship']['name'].lower(), data['ship']['name'])
|
||||||
|
|
||||||
if data['ship'].get('shipName') is not None:
|
if data['ship'].get('shipName') is not None:
|
||||||
_ships = f'{ship}, {data["ship"]["shipName"]}'
|
_ships = f'{ship}, {data["ship"]["shipName"]}'
|
||||||
|
|
||||||
@ -185,7 +184,6 @@ def export(data, filename=None) -> None: # noqa: C901, CCR001
|
|||||||
if filename:
|
if filename:
|
||||||
with open(filename, 'wt') as h:
|
with open(filename, 'wt') as h:
|
||||||
h.write(string)
|
h.write(string)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Look for last ship of this type
|
# Look for last ship of this type
|
||||||
@ -193,7 +191,7 @@ def export(data, filename=None) -> None: # noqa: C901, CCR001
|
|||||||
regexp = re.compile(re.escape(ship) + r'\.\d{4}-\d\d-\d\dT\d\d\.\d\d\.\d\d\.txt')
|
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_str('outdir')) if regexp.match(x)])
|
oldfiles = sorted([x for x in os.listdir(config.get_str('outdir')) if regexp.match(x)])
|
||||||
if oldfiles:
|
if oldfiles:
|
||||||
with (pathlib.Path(config.get_str('outdir')) / oldfiles[-1]).open('r') as h:
|
with (pathlib.Path(config.get_str('outdir')) / oldfiles[-1]).open() as h:
|
||||||
if h.read() == string:
|
if h.read() == string:
|
||||||
return # same as last time - don't write
|
return # same as last time - don't write
|
||||||
|
|
||||||
|
@ -1,30 +1,37 @@
|
|||||||
"""Code dealing with ship outfitting."""
|
"""
|
||||||
|
outfitting.py - Code dealing with ship outfitting.
|
||||||
|
|
||||||
import pickle
|
Copyright (c) EDCD, All Rights Reserved
|
||||||
|
Licensed under the GNU General Public License.
|
||||||
|
See LICENSE file.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from os.path import join
|
|
||||||
from typing import Optional
|
|
||||||
from typing import OrderedDict as OrderedDictT
|
from typing import OrderedDict as OrderedDictT
|
||||||
|
|
||||||
from config import config
|
from config import config
|
||||||
from edmc_data import outfitting_armour_map as armour_map
|
from edmc_data import (
|
||||||
from edmc_data import outfitting_cabin_map as cabin_map
|
outfitting_armour_map as armour_map,
|
||||||
from edmc_data import outfitting_corrosion_rating_map as corrosion_rating_map
|
outfitting_cabin_map as cabin_map,
|
||||||
from edmc_data import outfitting_countermeasure_map as countermeasure_map
|
outfitting_corrosion_rating_map as corrosion_rating_map,
|
||||||
from edmc_data import outfitting_fighter_rating_map as fighter_rating_map
|
outfitting_countermeasure_map as countermeasure_map,
|
||||||
from edmc_data import outfitting_internal_map as internal_map
|
outfitting_fighter_rating_map as fighter_rating_map,
|
||||||
from edmc_data import outfitting_misc_internal_map as misc_internal_map
|
outfitting_internal_map as internal_map,
|
||||||
from edmc_data import outfitting_missiletype_map as missiletype_map
|
outfitting_misc_internal_map as misc_internal_map,
|
||||||
from edmc_data import outfitting_planet_rating_map as planet_rating_map
|
outfitting_missiletype_map as missiletype_map,
|
||||||
from edmc_data import outfitting_rating_map as rating_map
|
outfitting_planet_rating_map as planet_rating_map,
|
||||||
from edmc_data import outfitting_standard_map as standard_map
|
outfitting_rating_map as rating_map,
|
||||||
from edmc_data import outfitting_utility_map as utility_map
|
outfitting_standard_map as standard_map,
|
||||||
from edmc_data import outfitting_weapon_map as weapon_map
|
outfitting_utility_map as utility_map,
|
||||||
from edmc_data import outfitting_weaponclass_map as weaponclass_map
|
outfitting_weapon_map as weapon_map,
|
||||||
from edmc_data import outfitting_weaponmount_map as weaponmount_map
|
outfitting_weaponclass_map as weaponclass_map,
|
||||||
from edmc_data import outfitting_weaponoldvariant_map as weaponoldvariant_map
|
outfitting_weaponmount_map as weaponmount_map,
|
||||||
from edmc_data import outfitting_weaponrating_map as weaponrating_map
|
outfitting_weaponoldvariant_map as weaponoldvariant_map,
|
||||||
from edmc_data import ship_name_map
|
outfitting_weaponrating_map as weaponrating_map,
|
||||||
|
ship_name_map,
|
||||||
|
)
|
||||||
from EDMCLogging import get_main_logger
|
from EDMCLogging import get_main_logger
|
||||||
|
|
||||||
logger = get_main_logger()
|
logger = get_main_logger()
|
||||||
@ -33,7 +40,7 @@ logger = get_main_logger()
|
|||||||
moduledata: OrderedDictT = OrderedDict()
|
moduledata: OrderedDictT = OrderedDict()
|
||||||
|
|
||||||
|
|
||||||
def lookup(module, ship_map, entitled=False) -> Optional[dict]: # noqa: C901, CCR001
|
def lookup(module, ship_map, entitled=False) -> dict | None: # noqa: C901, CCR001
|
||||||
"""
|
"""
|
||||||
Produce a standard dict description of the given module.
|
Produce a standard dict description of the given module.
|
||||||
|
|
||||||
@ -51,7 +58,8 @@ def lookup(module, ship_map, entitled=False) -> Optional[dict]: # noqa: C901, C
|
|||||||
"""
|
"""
|
||||||
# Lazily populate
|
# Lazily populate
|
||||||
if not moduledata:
|
if not moduledata:
|
||||||
moduledata.update(pickle.load(open(join(config.respath_path, 'modules.p'), 'rb')))
|
modules_path = config.respath_path / "resources" / "modules.json"
|
||||||
|
moduledata.update(json.loads(modules_path.read_text()))
|
||||||
|
|
||||||
if not module.get('name'):
|
if not module.get('name'):
|
||||||
raise AssertionError(f'{module["id"]}')
|
raise AssertionError(f'{module["id"]}')
|
||||||
@ -61,10 +69,13 @@ def lookup(module, ship_map, entitled=False) -> Optional[dict]: # noqa: C901, C
|
|||||||
|
|
||||||
# Armour - e.g. Federation_Dropship_Armour_Grade2
|
# Armour - e.g. Federation_Dropship_Armour_Grade2
|
||||||
if name[-2] == 'armour':
|
if name[-2] == 'armour':
|
||||||
name = module['name'].lower().rsplit('_', 2) # Armour is ship-specific, and ship names can have underscores
|
# Armour is ship-specific, and ship names can have underscores
|
||||||
|
ship_name, armour_grade = module["name"].lower().rsplit("_", 2)[0:2]
|
||||||
|
if ship_name not in ship_map:
|
||||||
|
raise AssertionError(f"Unknown ship: {ship_name}")
|
||||||
new['category'] = 'standard'
|
new['category'] = 'standard'
|
||||||
new['name'] = armour_map[name[2]]
|
new["name"] = armour_map[armour_grade]
|
||||||
new['ship'] = ship_map[name[0]] # Generate error on unknown ship
|
new["ship"] = ship_map[ship_name]
|
||||||
new['class'] = '1'
|
new['class'] = '1'
|
||||||
new['rating'] = 'I'
|
new['rating'] = 'I'
|
||||||
|
|
||||||
@ -219,10 +230,7 @@ def lookup(module, ship_map, entitled=False) -> Optional[dict]: # noqa: C901, C
|
|||||||
new['enabled'], new['priority'] = module['on'], module['priority'] # priority is zero-based
|
new['enabled'], new['priority'] = module['on'], module['priority'] # priority is zero-based
|
||||||
|
|
||||||
# Entitlements
|
# Entitlements
|
||||||
if not module.get('sku'):
|
if module.get('sku'):
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
new['entitlement'] = module['sku']
|
new['entitlement'] = module['sku']
|
||||||
|
|
||||||
# Extra module data
|
# Extra module data
|
||||||
@ -245,10 +253,11 @@ def lookup(module, ship_map, entitled=False) -> Optional[dict]: # noqa: C901, C
|
|||||||
|
|
||||||
new.update(moduledata.get(module['name'].lower(), {}))
|
new.update(moduledata.get(module['name'].lower(), {}))
|
||||||
|
|
||||||
# check we've filled out mandatory fields
|
# Check we've filled out mandatory fields
|
||||||
for thing in ['id', 'symbol', 'category', 'name', 'class', 'rating']: # Don't consider mass etc as mandatory
|
mandatory_fields = ["id", "symbol", "category", "name", "class", "rating"]
|
||||||
if not new.get(thing):
|
for field in mandatory_fields:
|
||||||
raise AssertionError(f'{module["id"]}: failed to set {thing}')
|
if not new.get(field):
|
||||||
|
raise AssertionError(f'{module["id"]}: failed to set {field}')
|
||||||
|
|
||||||
if new['category'] == 'hardpoint' and not new.get('mount'):
|
if new['category'] == 'hardpoint' and not new.get('mount'):
|
||||||
raise AssertionError(f'{module["id"]}: failed to set mount')
|
raise AssertionError(f'{module["id"]}: failed to set mount')
|
||||||
@ -263,15 +272,15 @@ def export(data, filename) -> None:
|
|||||||
:param data: CAPI data to export.
|
:param data: CAPI data to export.
|
||||||
:param filename: Filename to export into.
|
:param filename: Filename to export into.
|
||||||
"""
|
"""
|
||||||
assert data['lastSystem'].get('name')
|
assert "name" in data["lastSystem"]
|
||||||
assert data['lastStarport'].get('name')
|
assert "name" in data["lastStarport"]
|
||||||
|
|
||||||
header = 'System,Station,Category,Name,Mount,Guidance,Ship,Class,Rating,FDevID,Date\n'
|
header = 'System,Station,Category,Name,Mount,Guidance,Ship,Class,Rating,FDevID,Date\n'
|
||||||
rowheader = f'{data["lastSystem"]["name"]},{data["lastStarport"]["name"]}'
|
rowheader = f'{data["lastSystem"]["name"]},{data["lastStarport"]["name"]}'
|
||||||
|
|
||||||
with open(filename, 'wt') as h:
|
with open(filename, 'wt') as h:
|
||||||
h.write(header)
|
h.write(header)
|
||||||
for v in list(data['lastStarport'].get('modules', {}).values()):
|
for v in data["lastStarport"].get("modules", {}).values():
|
||||||
try:
|
try:
|
||||||
m = lookup(v, ship_name_map)
|
m = lookup(v, ship_name_map)
|
||||||
if m:
|
if m:
|
||||||
|
3306
resources/modules.json
Normal file
3306
resources/modules.json
Normal file
File diff suppressed because it is too large
Load Diff
116
resources/ships.json
Normal file
116
resources/ships.json
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
"Adder": {
|
||||||
|
"hullMass": 35
|
||||||
|
},
|
||||||
|
"Alliance Challenger": {
|
||||||
|
"hullMass": 450
|
||||||
|
},
|
||||||
|
"Alliance Chieftain": {
|
||||||
|
"hullMass": 400
|
||||||
|
},
|
||||||
|
"Alliance Crusader": {
|
||||||
|
"hullMass": 500
|
||||||
|
},
|
||||||
|
"Anaconda": {
|
||||||
|
"hullMass": 400
|
||||||
|
},
|
||||||
|
"Asp Explorer": {
|
||||||
|
"hullMass": 280
|
||||||
|
},
|
||||||
|
"Asp Scout": {
|
||||||
|
"hullMass": 150
|
||||||
|
},
|
||||||
|
"Beluga Liner": {
|
||||||
|
"hullMass": 950
|
||||||
|
},
|
||||||
|
"Cobra MkIII": {
|
||||||
|
"hullMass": 180
|
||||||
|
},
|
||||||
|
"Cobra MkIV": {
|
||||||
|
"hullMass": 210
|
||||||
|
},
|
||||||
|
"Diamondback Explorer": {
|
||||||
|
"hullMass": 260
|
||||||
|
},
|
||||||
|
"Diamondback Scout": {
|
||||||
|
"hullMass": 170
|
||||||
|
},
|
||||||
|
"Dolphin": {
|
||||||
|
"hullMass": 140
|
||||||
|
},
|
||||||
|
"Eagle": {
|
||||||
|
"hullMass": 50
|
||||||
|
},
|
||||||
|
"Federal Assault Ship": {
|
||||||
|
"hullMass": 480
|
||||||
|
},
|
||||||
|
"Federal Corvette": {
|
||||||
|
"hullMass": 900
|
||||||
|
},
|
||||||
|
"Federal Dropship": {
|
||||||
|
"hullMass": 580
|
||||||
|
},
|
||||||
|
"Federal Gunship": {
|
||||||
|
"hullMass": 580
|
||||||
|
},
|
||||||
|
"Fer-de-Lance": {
|
||||||
|
"hullMass": 250
|
||||||
|
},
|
||||||
|
"Hauler": {
|
||||||
|
"hullMass": 14
|
||||||
|
},
|
||||||
|
"Imperial Clipper": {
|
||||||
|
"hullMass": 400
|
||||||
|
},
|
||||||
|
"Imperial Courier": {
|
||||||
|
"hullMass": 35
|
||||||
|
},
|
||||||
|
"Imperial Cutter": {
|
||||||
|
"hullMass": 1100
|
||||||
|
},
|
||||||
|
"Imperial Eagle": {
|
||||||
|
"hullMass": 50
|
||||||
|
},
|
||||||
|
"Keelback": {
|
||||||
|
"hullMass": 180
|
||||||
|
},
|
||||||
|
"Krait MkII": {
|
||||||
|
"hullMass": 320
|
||||||
|
},
|
||||||
|
"Krait Phantom": {
|
||||||
|
"hullMass": 270
|
||||||
|
},
|
||||||
|
"Mamba": {
|
||||||
|
"hullMass": 250
|
||||||
|
},
|
||||||
|
"Orca": {
|
||||||
|
"hullMass": 290
|
||||||
|
},
|
||||||
|
"Python": {
|
||||||
|
"hullMass": 350
|
||||||
|
},
|
||||||
|
"Sidewinder": {
|
||||||
|
"hullMass": 25
|
||||||
|
},
|
||||||
|
"Type-10 Defender": {
|
||||||
|
"hullMass": 1200
|
||||||
|
},
|
||||||
|
"Type-6 Transporter": {
|
||||||
|
"hullMass": 155
|
||||||
|
},
|
||||||
|
"Type-7 Transporter": {
|
||||||
|
"hullMass": 350
|
||||||
|
},
|
||||||
|
"Type-9 Heavy": {
|
||||||
|
"hullMass": 850
|
||||||
|
},
|
||||||
|
"Viper MkIII": {
|
||||||
|
"hullMass": 50
|
||||||
|
},
|
||||||
|
"Viper MkIV": {
|
||||||
|
"hullMass": 190
|
||||||
|
},
|
||||||
|
"Vulture": {
|
||||||
|
"hullMass": 230
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user