1#!/usr/bin/env python3 2"""Translates map coordinate to map file and folder and vice-versa 3Run this script with -h for full usage information. 4 5Info about output: 6 Map: In game map coordinate number 7 File: Where coordinate is found on the maps folder 8 Folder: Range of submaps the folder can have 9Overmaps: Number of overmaps the folder contains 10 11Examples with map format: 12 13 %(prog)s 6.1.-3.map 14 %(prog)s -4.8.1.map 15 %(prog)s -9.-9.17.map 16 17Examples using folder format: 18 19 %(prog)s 1.0.0 20 %(prog)s -7.3.5 21 %(prog)s 8.-9.-1 22 23Examples with map coordinate format: 24 25 %(prog)s "5'107 3'146" 26 %(prog)s "7'98 -7'137 5" 27 %(prog)s "-1'0 0'4 -8" 28 29Examples of INVALID map coordinate format: 30 31 %(prog)s "3'180 3'17" -> Number after the ' must be between 0 and 179 32 %(prog)s "7'31 5'-17 1" -> Number after the ' must be positive 33 %(prog)s 1'13 3'17 1 -> Quotation marks are needed (") 34 to prevent string escaping 35 36Examples with range: 37 38 %(prog)s "0'31 0'0" --range "0'32 0'0" 39 %(prog)s "1'5 4'3 5" --range "-7'1 -9'179 -7" 40 %(prog)s 7.3.0.map --range -1.73.1.map 41""" 42 43import sys 44import argparse 45import re 46 47 48def print_info() -> None: 49 print( 50 """ 51 The game map is divided in overmaps 52 Each overmap is divided in overmap terrain tiles (OTT) in a 180x180 grid 53 Each OTT is 24x24 tiles divided into 4 submaps ( terrain ) 54 Each OTT is a `.map` file 55 Each map folder is 32x32 OTTs 56 """ 57 ) 58 59 60def info_map(format: str) -> list: 61 mx, cx = [int(n) for n in format.split()[0].split("'")] 62 my, cy = [int(n) for n in format.split()[1].split("'")] 63 64 dx = mx * 180 + cx 65 dy = my * 180 + cy 66 dz = 0 67 68 if len(format.split()) == 3: 69 dz = int(format.split()[2]) 70 71 return (dx, dy, dz) 72 73 74def info_file(format: str) -> list: 75 cx, cy, cz = [int(n) for n in format.split(".")[:3]] 76 return (cx, cy, cz) 77 78 79def info_folder(format: str) -> None: 80 cx, cy, cz = [int(n) for n in format.split(".")] 81 min_cx, max_cx = cx * 32, cx * 32 + 31 82 min_cy, max_cy = cy * 32, cy * 32 + 31 83 84 o1 = f"{min_cx//180} {min_cy//180}" 85 o2 = f"{min_cx//180} {max_cy//180}" 86 o3 = f"{max_cx//180} {min_cy//180}" 87 o4 = f"{max_cx//180} {max_cy//180}" 88 ol = [o1] 89 if o2 not in ol: 90 ol.append(o2) 91 if o3 not in ol: 92 ol.append(o3) 93 if o4 not in ol: 94 ol.append(o4) 95 96 print(f" Folder: {min_cx}.{min_cy}.{cz}.map -> " 97 f"{max_cx}.{max_cy}.{cz}.map") 98 print("Overmaps: ", end="") 99 for k, v in enumerate(ol): 100 if k == len(ol) - 1: 101 print(v) 102 else: 103 print(v, end=", ") 104 105 106def get_range_list(value1: int, value2: int) -> list: 107 min_value = min(value1, value2) 108 max_value = max(value1, value2) 109 110 if min_value == max_value: 111 return [min_value] 112 else: 113 return list(range(min_value, max_value + 1)) 114 115 116def info_range(r1: tuple, r2: tuple) -> None: 117 118 lvx = get_range_list(r1[0], r2[0]) 119 lvy = get_range_list(r1[1], r2[1]) 120 lvz = get_range_list(r1[2], r2[2]) 121 122 for z in lvz: 123 for y in lvy: 124 for x in lvx: 125 print(f"{x//32}.{y//32}.{z}/{x}.{y}.{z}.map", end=" ") 126 print("") 127 128 129if __name__ == "__main__": 130 131 if len(sys.argv) > 1: 132 for key, arg in enumerate(sys.argv[1:]): 133 if arg[0] == "-" and arg[1].isdigit(): 134 argvCopy = " " + arg 135 sys.argv[key + 1] = argvCopy 136 137 parser = argparse.ArgumentParser( 138 description=__doc__, 139 formatter_class=argparse.RawTextHelpFormatter, 140 ) 141 parser.add_argument( 142 "format", 143 type=str, 144 nargs="?", 145 help="""coordinate format 146Accepted formats: 147Folders: x.y.z 148Maps: x.y.z.map 149Coordinate: \"x'(0->179) y'(0->179)\" or \"x'(0->179) y'(0->179) z\" 150 Default for z is 0 151 """, 152 ) 153 parser.add_argument("--info", action="store_true", 154 help="Print info about maps") 155 parser.add_argument( 156 "--range", help="Print maps in range using maps or coordinate format" 157 ) 158 args = parser.parse_args(sys.argv[1:]) 159 160 if not args.format: 161 print("Use --help to view usage") 162 sys.exit(1) 163 164 args.format = args.format.strip() 165 166 if args.info: 167 print_info() 168 169 elif args.range: 170 args.range = args.range.strip() 171 matchFile = re.compile(r"^-?\d+\.-?\d+\.-?\d+\.map$") 172 matchCoord = re.compile(r"^-?\d'-?\d+\s+-?\d+'-?\d+(\s+-?\d+)?$") 173 174 if re.match(matchFile, args.format): 175 if re.match(matchFile, args.range): 176 retInfoR = info_file(args.range) 177 178 elif re.match(matchCoord, args.range): 179 retInfoR = info_map(args.range) 180 181 else: 182 print("Invalid format for range, use --help to view usage") 183 sys.exit(1) 184 185 retInfoF = info_file(args.format) 186 187 elif re.match(matchCoord, args.format): 188 if re.match(matchFile, args.range): 189 retInfoR = info_file(args.range) 190 191 elif re.match(matchCoord, args.range): 192 retInfoR = info_map(args.range) 193 194 else: 195 print("Invalid format for range, use --help to view usage") 196 sys.exit(1) 197 198 retInfoF = info_map(args.format) 199 200 else: 201 print("Invalid format, print help to view usage") 202 sys.exit(1) 203 204 info_range(retInfoF, retInfoR) 205 206 else: 207 args.format = args.format.strip() 208 209 retInfo = False 210 211 # .map file pattern 212 if re.match(r"^-?\d+\.-?\d+\.-?\d+\.map$", args.format): 213 retInfo = info_file(args.format) 214 215 # .map folder pattern 216 elif re.match(r"^-?\d+\.-?\d+\.-?\d+$", args.format): 217 info_folder(args.format) 218 219 # map coordinate pattern 220 elif re.match( 221 r"^-?\d'(\d{0,2}|1[0-7]\d)\s+-?\d+'(\d{0,2}|1[0-7]\d)(\s+-?\d+)?$", 222 args.format, 223 ): 224 retInfo = info_map(args.format) 225 elif re.match(r"^-?\d'-?\d+\s+-?\d+'-?\d+(\s+-?\d+)?$", args.format): 226 print( 227 "Invalid range for map coordination," 228 " a'b where b is positive from 0 to 179" 229 ) 230 else: 231 print("Invalid format, print help to view usage") 232 233 if retInfo: 234 cx, cy, cz = retInfo 235 236 print( 237 f" Map: {cx//180}'{cx - 180 * (cx//180)}" 238 f" {cy//180}'{cy- 180 * (cy//180)} {cz}" 239 ) 240 241 print(f" File: {cx//32}.{cy//32}.{cz}/{cx}.{cy}.{cz}.map") 242 243 info_folder(f"{cx//32}.{cy//32}.{cz}") 244