mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-01-30 02:44:26 +00:00
* Initial * Remove corrupt dmis * Fixup maps in TGM format4e5a32721f: maps/_templates_and_guidance/Public Event Templates/Maze_Reward_-_Copy.dmm4e5a32721f: maps/_templates_and_guidance/Templates/shelter_Medical.dmm4e5a32721f: maps/expedition_vr/aerostat/aerostat.dmm4e5a32721f: maps/expedition_vr/aerostat/aerostat_science_outpost.dmm4e5a32721f: maps/expedition_vr/beach/submaps/deadBeacon.dmm4e5a32721f: maps/expedition_vr/wild/tether_wild-crash-alt.dmm4e5a32721f: maps/expedition_vr/wild/tether_wild-crash.dmm4e5a32721f: maps/expedition_vr/wild/tether_wild-surface.dmm4e5a32721f: maps/expedition_vr/wild/tether_wild-temple.dmm4e5a32721f: maps/gateway_vr/lucky_7.dmm4e5a32721f: maps/gateway_vr/snow_outpost.dmm4e5a32721f: maps/overmap/_map.dmm4e5a32721f: maps/overmap/bearcat/bearcat.dmm4e5a32721f: maps/overmap/example_sector1.dmm4e5a32721f: maps/overmap/example_sector2.dmm4e5a32721f: maps/redgate/falls/falls.dmm4e5a32721f: maps/submaps/pois_vr/aerostat/CaveS.dmm4e5a32721f: maps/submaps/pois_vr/aerostat/DeadSettlers1.dmm4e5a32721f: maps/submaps/pois_vr/aerostat/DeadSettlers2.dmm4e5a32721f: maps/submaps/pois_vr/aerostat/DoomP.dmm4e5a32721f: maps/submaps/pois_vr/aerostat/Lab1.dmm4e5a32721f: maps/submaps/pois_vr/aerostat/Rockybase.dmm4e5a32721f: maps/submaps/pois_vr/debris_field/debris14.dmm4e5a32721f: maps/submaps/pois_vr/debris_field/derelict.dmm4e5a32721f: maps/submaps/pois_vr/debris_field/new_escapepod_xeno.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/BlastMine1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/CaveTrench.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/Cavelake.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/Cliff1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/CrashedMedShuttle1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/Geyser1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/Geyser2.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/Geyser3.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/Mineshaft1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/Scave1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/SupplyDrop1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/crashed_ufo.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/crashed_ufo_frigate.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/crashedcontainmentshuttle.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/crystal1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/crystal2.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/crystal3.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/deadBeacon.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/deadly_rabbit_vr.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/deadspy.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/digsite.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/excavation1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/lava_trench.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/prepper1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/ritual.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/spatial_anomaly.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/speakeasy_vr.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/vault1.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/vault2.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/vault3.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/vault4.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/vault5.dmm4e5a32721f: maps/submaps/surface_submaps/mountains/vault6.dmm4e5a32721f: maps/submaps/surface_submaps/plains/Boathouse.dmm4e5a32721f: maps/submaps/surface_submaps/plains/BuriedTreasure.dmm4e5a32721f: maps/submaps/surface_submaps/plains/BuriedTreasure2.dmm4e5a32721f: maps/submaps/surface_submaps/plains/BuriedTreasure3.dmm4e5a32721f: maps/submaps/surface_submaps/plains/Oldhouse.dmm4e5a32721f: maps/submaps/surface_submaps/plains/PooledR.dmm4e5a32721f: maps/submaps/surface_submaps/plains/Rocky5.dmm4e5a32721f: maps/submaps/surface_submaps/plains/Shakden.dmm4e5a32721f: maps/submaps/surface_submaps/plains/Thiefc.dmm4e5a32721f: maps/submaps/surface_submaps/plains/beacons.dmm4e5a32721f: maps/submaps/surface_submaps/plains/chemspill1.dmm4e5a32721f: maps/submaps/surface_submaps/plains/farm1.dmm4e5a32721f: maps/submaps/surface_submaps/plains/house1.dmm4e5a32721f: maps/submaps/surface_submaps/plains/lonehome.dmm4e5a32721f: maps/submaps/surface_submaps/plains/smol2.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Blackshuttledown.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Blueshuttledown.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Boombase.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/CaveS.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Chapel.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Cragzone1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/DJOutpost1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/DJOutpost2.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/DJOutpost3.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/DJOutpost4.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/DecoupledEngine.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/DoomP.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Drugden.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Epod3.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Epod4.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Flake.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/FrostflyNest.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/MCamp1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/MHR.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Manor1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Mudpit.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Rocky1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Rocky3.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Rocky4.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Rockybase.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Shack1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Shelter.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Smol1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/Snowrock1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/borglab.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/butchershack.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/chasm.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/chemspill2.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/deathden.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/derelictengine.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/frostoasis.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/kururakden.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/spider1.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/wolfden.dmm4e5a32721f: maps/submaps/surface_submaps/wilderness/xenohive.dmm4e5a32721f: maps/tether/tether-02-surface2.dmm4e5a32721f: maps/virgo_minitest/virgo_minitest-sector-2.dmm Automatically commited by: tools\mapmerge2\fixup.py * Remove unnecessary whitespace edits from mapmerger * Cable dirs update path * Fix area var edits * Put the area over there * Ignore archive maps folder * Forgot to port multivar support too * A few changes I forgot about for hook install * restore multivar support that chomp doesn't have yet * ban those * Forgot to add code for the marker too * Couple more of these invalid cables were added in master * Update multiple_blood_effects.yml * Update multiple_blood_effects.yml * Fixup maps in TGM format612ca9cbb9: maps/tether/submaps/tether_misc.dmm Automatically commited by: tools\mapmerge2\fixup.py * Fixup now logs the map its currently checking * Final fixes? * Fixup maps in TGM format3078e5cd0a: maps/expedition_vr/beach/submaps/crashedcontainmentshuttle.dmm3078e5cd0a: maps/redgate/fantasy_dungeon.dmm3078e5cd0a: maps/submaps/pois_vr/aerostat/Rockybase.dmm3078e5cd0a: maps/submaps/surface_submaps/mountains/crashedcontainmentshuttle_vr.dmm3078e5cd0a: maps/submaps/surface_submaps/plains/Oldhouse_vr.dmm3078e5cd0a: maps/submaps/surface_submaps/plains/dogbase.dmm3078e5cd0a: maps/submaps/surface_submaps/plains/greatwolfden.dmm3078e5cd0a: maps/submaps/surface_submaps/plains/lonehome_vr.dmm3078e5cd0a: maps/submaps/surface_submaps/plains/methlab.dmm3078e5cd0a: maps/submaps/surface_submaps/plains/oldhotel.dmm3078e5cd0a: maps/submaps/surface_submaps/plains/priderock.dmm3078e5cd0a: maps/submaps/surface_submaps/wilderness/Rockybase.dmm3078e5cd0a: maps/submaps/surface_submaps/wilderness/demonpool.dmm3078e5cd0a: maps/submaps/surface_submaps/wilderness/dogbase.dmm3078e5cd0a: maps/submaps/surface_submaps/wilderness/greatwolfden.dmm3078e5cd0a: maps/tether/submaps/underdark_pois/abandonded_outpost.dmm3078e5cd0a: maps/tether/submaps/underdark_pois/phoron_rat_den.dmm Automatically commited by: tools\mapmerge2\fixup.py * Fix tether_misc error * Remap reused solar farm area * Fix erroneous bearcat entries * Fix weird whitespace (most archive maps also affected but didn't bother) * misc mdb cleanup * moar * grr --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> Co-authored-by: Cameron Lennox <killer65311@gmail.com>
202 lines
8.3 KiB
Python
202 lines
8.3 KiB
Python
# A script and syntax for applying path updates to maps.
|
|
import re
|
|
import os
|
|
import sys
|
|
import argparse
|
|
from mapmerge2 import frontend
|
|
from mapmerge2.dmm import *
|
|
|
|
desc = """
|
|
Update dmm files given update file/string.
|
|
Replacement syntax example:
|
|
/turf/open/floor/iron/warningline : /obj/effect/turf_decal {dir = @OLD ;tag = @SKIP;icon_state = @SKIP}
|
|
/turf/open/floor/iron/warningline : /obj/effect/turf_decal {@OLD} , /obj/thing {icon_state = @OLD:name; name = "meme"}
|
|
/turf/open/floor/iron/warningline{dir=2} : /obj/thing
|
|
/obj/effect/landmark/start/virologist : @DELETE
|
|
/mob/living{resize = @ANY} : /mob/living{@OLD; resize = @SKIP}
|
|
Syntax for subtypes also exist, to update a path's type but maintain subtypes:
|
|
/obj/structure/closet/crate/@SUBTYPES : /obj/structure/new_box/@SUBTYPES {@OLD}
|
|
New paths properties:
|
|
@DELETE - if used as new path name the old path will be deleted
|
|
@OLD - if used as property name copies all modified properties from original path to this one
|
|
property = @SKIP - will not copy this property through when global @OLD is used.
|
|
property = @OLD - will copy this modified property from original object even if global @OLD is not used
|
|
property = @OLD:name - will copy [name] property from original object even if global @OLD is not used
|
|
Anything else is copied as written.
|
|
Old paths properties:
|
|
Will be used as a filter.
|
|
property = @UNSET - will apply the rule only if the property is not mapedited
|
|
property = @ANY - will apply the rule when the property is mapedited, regardless of its value.
|
|
"""
|
|
|
|
replacement_re = re.compile(r'\s*(?P<path>[^{]*)\s*(\{(?P<props>.*)\})?')
|
|
|
|
#urgent todo: replace with actual parser, this is slow as janitor in crit
|
|
split_re = re.compile(r'((?:[A-Za-z0-9_\-$]+)\s*=\s*(?:"(?:.+?)"|[^";][^;]*)|@OLD);?')
|
|
|
|
|
|
def props_to_string(props):
|
|
return "{{{}}}".format(";".join([f"{k} = {v}" for k, v in props.items()]))
|
|
|
|
|
|
def string_to_props(propstring, verbose = False):
|
|
props = dict()
|
|
for raw_prop in re.split(split_re, propstring):
|
|
if not raw_prop or raw_prop.strip() == ';':
|
|
continue
|
|
prop = raw_prop.split('=', maxsplit=1)
|
|
props[prop[0].strip()] = prop[1].strip() if len(prop) > 1 else None
|
|
if verbose:
|
|
print("{0} to {1}".format(propstring, props))
|
|
return props
|
|
|
|
|
|
def parse_rep_string(replacement_string, verbose = False):
|
|
# translates /blah/blah {meme = "test",} into path,prop dictionary tuple
|
|
match = re.match(replacement_re, replacement_string)
|
|
path = match['path']
|
|
props = match['props']
|
|
if props:
|
|
prop_dict = string_to_props(props, verbose)
|
|
else:
|
|
prop_dict = dict()
|
|
return path.strip(), prop_dict
|
|
|
|
|
|
def update_path(dmm_data, replacement_string, verbose=False):
|
|
old_path_part, new_path_part = replacement_string.split(':', maxsplit=1)
|
|
old_path, old_path_props = parse_rep_string(old_path_part, verbose)
|
|
new_paths = list()
|
|
for replacement_def in new_path_part.split(','):
|
|
new_path, new_path_props = parse_rep_string(replacement_def, verbose)
|
|
new_paths.append((new_path, new_path_props))
|
|
|
|
subtypes = ""
|
|
if old_path.endswith("/@SUBTYPES"):
|
|
old_path = old_path[:-len("/@SUBTYPES")]
|
|
if verbose:
|
|
print("Looking for subtypes of", old_path)
|
|
subtypes = r"(?:/\w+)*"
|
|
|
|
replacement_pattern = re.compile(rf"(?P<path>{re.escape(old_path)}(?P<subtype>{subtypes}))\s*(:?{{(?P<props>.*)}})?$")
|
|
|
|
def replace_def(match):
|
|
if match['props']:
|
|
old_props = string_to_props(match['props'], verbose)
|
|
else:
|
|
old_props = dict()
|
|
for filter_prop in old_path_props:
|
|
if filter_prop not in old_props:
|
|
if old_path_props[filter_prop] == "@UNSET":
|
|
continue
|
|
else:
|
|
return [match.group(0)]
|
|
else:
|
|
if old_path_props[filter_prop] == "@ANY":
|
|
continue
|
|
elif old_props[filter_prop] != old_path_props[filter_prop] or old_path_props[filter_prop] == "@UNSET":
|
|
return [match.group(0)] #does not match current filter, skip the change.
|
|
if verbose:
|
|
print("Found match : {0}".format(match.group(0)))
|
|
out_paths = []
|
|
for new_path, new_props in new_paths:
|
|
if new_path == "@OLD":
|
|
out = match.group('path')
|
|
elif new_path == "@DELETE":
|
|
if verbose:
|
|
print("Deleting match : {0}".format(match.group(0)))
|
|
return [None]
|
|
elif new_path.endswith("/@SUBTYPES"):
|
|
path_start = new_path[:-len("/@SUBTYPES")]
|
|
out = path_start + match.group('subtype')
|
|
else:
|
|
out = new_path
|
|
|
|
out_props = dict()
|
|
for prop_name, prop_text in new_props.items():
|
|
prop_value = str(prop_text)
|
|
if prop_name == "@OLD":
|
|
out_props = dict(old_props)
|
|
continue
|
|
if prop_value == "@SKIP":
|
|
out_props.pop(prop_name, None)
|
|
continue
|
|
if prop_value.startswith("@OLD"):
|
|
params = prop_value.split(":")
|
|
out_props[prop_name] = old_props[params[1]] if len(params) > 1 else old_props[prop_name]
|
|
continue
|
|
out_props[prop_name] = prop_value
|
|
if out_props:
|
|
out += props_to_string(out_props)
|
|
out_paths.append(out)
|
|
if verbose:
|
|
print("Replacing with: {0}".format(out_paths))
|
|
return out_paths
|
|
|
|
def get_result(element):
|
|
match = replacement_pattern.match(element)
|
|
if match:
|
|
return replace_def(match)
|
|
else:
|
|
return [element]
|
|
|
|
bad_keys = {}
|
|
modified_keys = []
|
|
keys = list(dmm_data.dictionary.keys())
|
|
for definition_key in keys:
|
|
def_value = dmm_data.dictionary[definition_key]
|
|
new_value = tuple(y for x in def_value for y in get_result(x) if y != None)
|
|
if new_value != def_value:
|
|
dmm_data.overwrite_key(definition_key, new_value, bad_keys)
|
|
modified_keys.append(definition_key)
|
|
dmm_data.reassign_bad_keys(bad_keys)
|
|
return modified_keys
|
|
|
|
|
|
def update_map(map_filepath, updates, verbose=False):
|
|
print("Updating: {0}".format(map_filepath))
|
|
dmm_data = DMM.from_file(map_filepath)
|
|
modified_keys = []
|
|
for update_string in updates:
|
|
modified_keys.extend(update_path(dmm_data, update_string, verbose))
|
|
dmm_data.remove_unused_keys(modified_keys)
|
|
dmm_data.to_file(map_filepath)
|
|
|
|
|
|
def update_all_maps(map_directory, updates, verbose=False):
|
|
for root, _, files in os.walk(map_directory):
|
|
for filepath in files:
|
|
if filepath.endswith(".dmm"):
|
|
path = os.path.join(root, filepath)
|
|
update_map(path, updates, verbose)
|
|
|
|
|
|
def main(args):
|
|
if args.inline:
|
|
print("Using replacement:", args.update_source)
|
|
updates = [args.update_source]
|
|
else:
|
|
with open(args.update_source) as f:
|
|
updates = [line for line in f if line and not line.startswith("#") and not line.isspace()]
|
|
print(f"Using {len(updates)} replacements from file:", args.update_source)
|
|
|
|
if args.map:
|
|
update_map(args.map, updates, verbose=args.verbose)
|
|
else:
|
|
map_directory = args.directory or frontend.read_settings().map_folder
|
|
update_all_maps(map_directory, updates, verbose=args.verbose)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
prog = __spec__.name.replace('.__main__', '')
|
|
if os.name == 'nt' and len(sys.argv) <= 1:
|
|
print("usage: drag-and-drop a path script .txt onto `Update Paths.bat`\n or")
|
|
|
|
parser = argparse.ArgumentParser(prog=prog, description=desc, formatter_class=argparse.RawTextHelpFormatter)
|
|
parser.add_argument("update_source", help="update file path / line of update notation")
|
|
parser.add_argument("--map", "-m", help="path to update, defaults to all maps in directory")
|
|
parser.add_argument("--directory", "-d", help="path to maps directory, defaults to maps/")
|
|
parser.add_argument("--inline", "-i", help="treat update source as update string instead of path", action="store_true")
|
|
parser.add_argument("--verbose", "-v", help="toggle detailed update information", action="store_true")
|
|
main(parser.parse_args())
|