#!/usr/bin/python """rePosition: read a NMS PersistentPlayerBases JSON array entry and add an offset to every Position entry in its Objects[] array. This is to re-align a Corvette ship to the station hangar's grid (which somehow got shifted in an update Sep 2025...?) Python Versions tested: 3.9 Host OS Dependencies: """ #============================================================================== # Imports and definitions from __future__ import print_function import sys import argparse import json #============================================================================== # Args def handle_args(): """Parse args and return the resulting Namespace object.""" parser = argparse.ArgumentParser( description="rePosition items in an Objects array in a JSON file.", formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter( prog, max_help_position=35), # NB: use add_help=False to prevent auto -h/--help epilog=''' This is to re-position an off-center Corvette in the hangar's Build area so that the ship's landing gear is located correctly on the grid it snaps to. It reads from standard input (or -f FILE) and writes to standard output. The Position property in Objects[] is in the order (y,z,x), where positive x is in the direction toward the station entrance portal and (0,0,0) is the yellow central crosshairs on the deck. To use this you need to know the (y,z,x) position of the object you want positioned at exactly the center so it can be subtracted from every object. To find this: 1. First name your ship something besides the procedurally-generated name. 2. Open the Raw JSON using NMSSaveEditor or NomNom. 3. Open BaseContext/PlayerStateData 3. Look in ShipOwnership for the entry with your name. Remember the index. 4. Look in PersistentPlayerBases for the entry with "UserData":. 5. Look for Objects entries with ObjectID = ^B_LND_xx; these are the landing gear. Find the one closest to the center (0,0,0) and remember its Position (y,z,x) to use as parameters to this program. If you don't have one in the center, you can work out the offset: the grid spacing is 4.0, so you can calculate the intended Position of your gear, and subtract that from the actual Position of that gear in the JSON to get the offset you need to use as parameters for this program. 6. Run this program with the -O option and the offset parameters. 7. Replace the Objects[] array in PersistentPlayerBases with the output. ''') # Options parser.add_argument( '-d', '--debug', action='count', help="Enable debug output and DEBUG logging to stderr.") parser.add_argument( '-v', '--verbose', action='count', default=1, help='''Increase verbosity (default=%(default)s/WARN). The effective verbosity is the maximum of this and any defined in the config file. ''') parser.add_argument( '-q', '--quiet', action='count', default=0, help="Decrease verbosity.") parser.add_argument( '-n', '--norun', action='store_true', help="Display what would be done instead of doing it.") parser.add_argument( '-D', '--dump', action='store_true', help="Dump the active args and config and exit.") #parser.add_argument('-h', '--help', action='help') parser.add_argument('-f', '--file', dest='file', default='-', help="Read JSON from FILE instead of stdin.") parser.add_argument( '-O', '--objects', action='store_true', help="Only output the Objects array, ready to paste.") # Required positional args; NB: NMS Position order is (y, z, x) parser.add_argument('y', type=float ) parser.add_argument('z', type=float ) parser.add_argument('x', type=float ) args = parser.parse_args() # fix verbose/quiet args.verbose -= args.quiet return args #============================================================================== def main(): """The main program. Parse args, initialize and run.""" args = handle_args() if args.dump: print("args:") print(json.dumps(vars(args), indent=4)) # sys.exit(0) # Read JSON with open(args.file, 'r') if args.file != "-" else sys.stdin as f: data = json.load(f) if args.dump: print("data:") print(json.dumps(data, indent=4)) sys.exit(0) # Process Objects[] for obj in data['Objects']: pos = obj['Position'] # re-Position it pos[0] = pos[0] - args.y pos[1] = pos[1] - args.z pos[2] = pos[2] - args.x # Write JSON with sys.stdout as f: if args.objects: objstr = json.dumps({"Objects": data['Objects']}, indent=4) objstr = objstr[2:-2] # get rid of enclosing curly braces and nl objstr += ",\n" # ready to paste sys.stdout.write(objstr) else: sys.stdout.write(json.dumps(data, indent=4)) #============================================================================== if __name__ == "__main__": try: main() except KeyboardInterrupt: sys.exit("caught interrupt")