User:JdavisBro/Maps
Full Maps of Picnic based on the ingame map.
Layer 0[edit | edit source]
Layer 1[edit | edit source]
Layer 2[edit | edit source]
Code[edit | edit source]
Python code to generate the maps. Requires pillow. Extract the sprMap_outline_thick, sprMap_diag, sprMap_vert, and sprMap_water sprites with UndertaleModTool to script dir/sprites/ Move level_data from the game to script dir/ Run script
import base64 import json import zlib from pathlib import Path from PIL import Image sprdir = Path("sprites/") def get_msquare_ims(name: str) -> list[Image.Image]: return [Image.open(sprdir / name.format(i)) for i in range(15)] # outline = get_msquare_ims("sprMap_outline_{}.png") outline_thick = get_msquare_ims("sprMap_outline_thick_{}.png") # outline_thick2 = get_msquare_ims("sprMap_outline_thick1_{}.png") # not used i guess # outline_thick3 = get_msquare_ims("sprMap_outline_thick11_{}.png") diag = get_msquare_ims("sprMap_diag_{}.png") vert = get_msquare_ims("sprMap_vert_{}.png") water = get_msquare_ims("sprMap_water_{}.png") with Path("level_data").open() as f: level_data = json.load(f) GEO_SIZE = (81,46) def geo(data: str) -> list[list[int]]: hex = zlib.decompress(base64.b64decode(data)).hex() data = [int(v + hex[i+1], base=16) for i, v in enumerate(hex) if i % 2 == 0] assert(len(data) == GEO_SIZE[0] * GEO_SIZE[1]) data_list = [] i = 0 for y in range(GEO_SIZE[1]): data_list.append([]) for x in range(GEO_SIZE[0]): data_list[-1].append(data[i]) i += 1 return data_list # Map Making def render_tag(im: Image, geo: list[list[int]], tag: int, msquare: list[Image.Image]) -> Image: size = msquare[0].size for y in range(len(geo)-1): for x in range(len(geo[y])-1): a, b, c, d = geo[y][x], geo[y][x+1], geo[y+1][x+1], geo[y+1][x] val = int(a == tag) + (int(b == tag) << 1) + (int(c == tag) << 2) + (int(d == tag) << 3) if val > 0: im.paste(msquare[val-1], (x*size[0], y*size[1]), msquare[val-1]) TILE_SIZE = (128, 72) def do_level(screen: str) -> Image: im = Image.new("RGBA", (1920, 1080), (0,0,0,0)) level = geo(level_data[screen]["geo"]) minn = 0 for y in range(len(level)): for x in range(len(level[y])): minn = min(minn, level[y][x]) if level[y][x] > 128: minn = min(minn, level[y][x] - 255) if level[y][x] == 3: level[y][x] = 2 render_tag(im, level, 5, diag) # unwalkable render_tag(im, level, 6, water) # water render_tag(im, level, 2, vert) # non swim render_tag(im, level, 2, outline_thick) render_tag(im, level, 4, outline_thick) t = 0 while t >= minn: render_tag(im, level, t, outline_thick) t -= 1 t = 255 while t >= 255 + minn: render_tag(im, level, t, outline_thick) t -= 1 return im def main(): min_x = {0: 0, 1: 0, 2: 0} max_x = {0: 0, 1: 0, 2: 0} min_y = {0: 0, 1: 0, 2: 0} max_y = {0: 0, 1: 0, 2: 0} tiles = {0: {}, 1: {}, 2: {}} for level in level_data: print(f"Making {level}") z, x, y = [int(i) for i in level.split("_")] if z > 3: continue if y == -99: continue tiles[z][f"{x}_{y}"] = do_level(level).resize(TILE_SIZE, Image.Resampling.NEAREST) min_x[z] = min(min_x[z], x) max_x[z] = max(max_x[z], x) min_y[z] = min(min_y[z], y) max_y[z] = max(max_y[z], y) for z in range(3): print(f"Saving Layer {z}") im_size = ((max_x[z] - min_x[z] + 1) * TILE_SIZE[0], (max_y[z] - min_y[z] + 1) * TILE_SIZE[1]) tile_offset = (min_x[z] * -1 * TILE_SIZE[0], min_y[z] * -1 * TILE_SIZE[1]) im = Image.new("RGBA", im_size) for level in tiles[z]: x, y = [int(i) for i in level.split("_")] im.paste(tiles[z][level], (tile_offset[0] + x * TILE_SIZE[0], tile_offset[1] + y * TILE_SIZE[1])) im.save(f"map{z}.png") if __name__ == "__main__": main()