From 3a8884a9e1d7b445a6ca625aa41f8a903e30433a Mon Sep 17 00:00:00 2001 From: dP Date: Sun, 29 May 2022 00:29:38 +0300 Subject: [PATCH] alpine: Update to latest (external) grf-py version --- grf/alpine/alpine.py | 106 +++---- grf/alpine/grf.py | 637 ------------------------------------------- 2 files changed, 55 insertions(+), 688 deletions(-) delete mode 100644 grf/alpine/grf.py diff --git a/grf/alpine/alpine.py b/grf/alpine/alpine.py index b21951152c..68546a24dc 100644 --- a/grf/alpine/alpine.py +++ b/grf/alpine/alpine.py @@ -4,9 +4,9 @@ import grf gen = grf.NewGRF( - b'CMAL', - 'CityMania Alpine Landscape', - 'Modified OpenGFX sprites for alpine climate.', + grfid=b'CMAL', + name='CityMania Alpine Landscape', + description='Modified OpenGFX sprites for alpine climate.', ) @@ -90,15 +90,15 @@ def tmpl_temperate_road_tunnels_grid(func, **kw): def replace_ground_sprites(sprite_id, file, x, y, **kw): png = grf.ImageFile(file) - gen.add_sprite(grf.ReplaceSprites([(sprite_id, 19)])) - sprite = lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)) + gen.add(grf.ReplaceOldSprites([(sprite_id, 19)])) + sprite = lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)) tmpl_ground_sprites(sprite, x, y, **kw) def replace_shore_sprites(sprite_id, file, x, y, **kw): png = grf.ImageFile(file) - gen.add_sprite(grf.ReplaceSprites([(sprite_id, 8)])) - sprite = lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)) + gen.add(grf.ReplaceOldSprites([(sprite_id, 8)])) + sprite = lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)) sprite(320+x, y, 64, 31, xofs=-31, yofs= 0, **kw) sprite( 80+x, y, 64, 31, xofs=-31, yofs= 0, **kw) sprite(160+x, y, 64, 23, xofs=-31, yofs= 0, **kw) @@ -111,8 +111,8 @@ def replace_shore_sprites(sprite_id, file, x, y, **kw): def replace_additional_rough_sprites(sprite_id, file, x, y, **kw): png = grf.ImageFile(file) - gen.add_sprite(grf.ReplaceSprites([(sprite_id, 4)])) - sprite = lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)) + gen.add(grf.ReplaceOldSprites([(sprite_id, 4)])) + sprite = lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)) sprite( x, y, 64, 31, xofs=-31, yofs=0, **kw) sprite( 80+x, y, 64, 31, xofs=-31, yofs=0, **kw) sprite(160+x, y, 64, 31, xofs=-31, yofs=0, **kw) @@ -121,8 +121,8 @@ def replace_additional_rough_sprites(sprite_id, file, x, y, **kw): def replace_sprites_template(sprite_id, amount, file, func, **kw): png = grf.ImageFile(file) - gen.add_sprite(grf.ReplaceSprites([(sprite_id, amount)])) - func(lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)), **kw) + gen.add(grf.ReplaceOldSprites([(sprite_id, amount)])) + func(lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)), **kw) # Normal land @@ -196,8 +196,8 @@ replace_sprites_template(2603, 29, 'gfx/miscellaneous/hq.png', tmpl_hq) def replace_coastal_sprites(file, x, y, **kw): png = grf.ImageFile(file) - gen.add_sprite(grf.ReplaceNewSprites(0x0d, 16)) - sprite = lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)) + gen.add(grf.ReplaceNewSprites(0x0d, 16)) + sprite = lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)) sprite(1276+x, y, 64, 15, xofs=-31, yofs= 0, **kw) sprite( 80+x, y, 64, 31, xofs=-31, yofs= 0, **kw) sprite( 160+x, y, 64, 23, xofs=-31, yofs= 0, **kw) @@ -281,9 +281,9 @@ TREES = [ for sprite_id, file, is_wide in TREES: - gen.add_sprite(grf.ReplaceSprites([(sprite_id, 7)])) + gen.add(grf.ReplaceOldSprites([(sprite_id, 7)])) png = grf.ImageFile('gfx/trees/' + file) - sprite = lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)) + sprite = lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)) if is_wide: tmpl_tree_wide(sprite) else: @@ -293,29 +293,27 @@ for sprite_id, file, is_wide in TREES: # Tile slope to sprite offset get_tile_slope_offset = grf.VarAction2( feature=grf.OBJECT, - use_related=False, - set_id=0, + ref_id=0, ranges={0: 0, 1: 1, 2: 2, 4: 4, 8: 8, 9: 9, 3: 3, 6: 6, 12: 12, 5: 5, 10: 10, 11: 11, 7: 7, 14: 14, 13: 13, 27: 17, 23: 16, 30: 18, 29: 15}, default=0, code='tile_slope' ) -gen.add_sprite(get_tile_slope_offset) +gen.add(get_tile_slope_offset) # Ground sprite get_ground_sprite = grf.VarAction2( feature=grf.OBJECT, - use_related=False, - set_id=1, + ref_id=1, ranges={-2: 4550 , -1: 4550 - 19, 0: 4550 - 19 * 2, 1: 4550 - 19 * 3}, default=3981, code='max(snowline_height - tile_height, -2)' ) -gen.add_sprite(get_ground_sprite) +gen.add(get_ground_sprite) # png = grf.ImageFile("gfx/meadow_grid_temperate.png") -# gen.add_sprite(grf.SpriteSet(grf.OBJECT, 19)) -# tmpl_ground_sprites(lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)), 1, 1) -# gen.add_sprite(grf.AdvancedSpriteLayout( +# gen.add(grf.SpriteSet(grf.OBJECT, 19)) +# tmpl_ground_sprites(lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)), 1, 1) +# gen.add(grf.AdvancedSpriteLayout( # grf.OBJECT, 255, # ground={ # 'sprite': 0, @@ -325,7 +323,7 @@ gen.add_sprite(get_ground_sprite) # } # )) -# gen.add_sprite(grf.Object(0, +# gen.add(grf.Object(0, # label=b'FLMA', # size=0x11, # climate=0xf, @@ -333,61 +331,67 @@ gen.add_sprite(get_ground_sprite) # flags=grf.Object.Flags.HAS_NO_FOUNDATION | grf.Object.Flags.ALLOW_UNDER_BRIDGE, # )) -# gen.add_sprite(grf.VarAction2( +# gen.add(grf.VarAction2( # feature=grf.OBJECT, -# use_related=False, -# set_id=255, +# ref_id=255, # ranges={0: grf.Set(255)}, # default=grf.Set(255), # code='TEMP[0] = call(0)' # )) -# gen.add_sprite(grf.Action3(grf.OBJECT, [0], [[255, 255]], 255)) +# gen.add(grf.Action3(grf.OBJECT, [0], [[255, 255]], 255)) # CREEKS -gen.add_sprite(grf.Action1(grf.OBJECT, 81, 19)) +gen.add(grf.Action1(grf.OBJECT, 81, 19)) png = grf.ImageFile("gfx/rivers.png") for i in range(81): - tmpl_ground_sprites(lambda *args, **kw: gen.add_sprite(grf.FileSprite(png, *args, **kw)), 1, i * 64 + 1) + tmpl_ground_sprites(lambda *args, **kw: gen.add(grf.FileSprite(png, *args, **kw)), 1, i * 64 + 1) for i in range(81): - gen.add_sprite(grf.AdvancedSpriteLayout( - grf.OBJECT, 255, + gen.add(grf.AdvancedSpriteLayout( + feature=grf.OBJECT, + ref_id=255, ground={ - 'sprite': 0, - 'pal': 0, + # 'sprite': grf.SpriteRef(0, is_global=True), + 'sprite': grf.SpriteRef(4550, is_global=True), 'flags': 2, - 'add': grf.Temp(1), + # 'add': grf.Temp(1), }, - sprites=[{ - 'sprite': i, - 'pal': (1 << 15), + buildings=[{ + 'sprite': grf.SpriteRef(i, is_global=False), 'flags': 2, 'add': grf.Temp(0), }] )) - gen.add_sprite(grf.VarAction2( + gen.add(grf.VarAction2( feature=grf.OBJECT, - use_related=False, - set_id=255, - ranges={0: grf.Set(255)}, - default=grf.Set(255), + ref_id=255, + ranges={0: grf.Ref(255)}, + default=grf.Ref(255), code=f''' TEMP[0] = call({get_tile_slope_offset}) TEMP[1] = call({get_ground_sprite}) + TEMP[0] ''' )) - gen.add_sprite(creek_obj := grf.Object(i, - label=b'CREE', - size=(1, 1), - climate=0xf, - eol_date=0, - flags=grf.Object.Flags.HAS_NO_FOUNDATION | grf.Object.Flags.ALLOW_UNDER_BRIDGE | grf.Object.Flags.AUTOREMOVE, + gen.add(creek_obj := grf.Define( + feature=grf.OBJECT, + id=i, + props={ + 'label' : b'CREE', + 'size' : (1, 1), + 'climate' : 0xf, + 'eol_date' : 0, + 'flags' : grf.Object.Flags.HAS_NO_FOUNDATION | grf.Object.Flags.ALLOW_UNDER_BRIDGE | grf.Object.Flags.AUTOREMOVE, + } )) - gen.add_sprite(grf.Map(creek_obj, [[255, 255]], 255)) + gen.add(grf.Map( + object=creek_obj, + maps={255: grf.Ref(255)}, + default=grf.Ref(255), + )) gen.write('alpine.grf') diff --git a/grf/alpine/grf.py b/grf/alpine/grf.py deleted file mode 100644 index 586d4d5012..0000000000 --- a/grf/alpine/grf.py +++ /dev/null @@ -1,637 +0,0 @@ -import math - -from PIL import Image, ImageDraw -from nml.spriteencoder import SpriteEncoder -import spectra -import struct -import numpy as np - -from parser import Node, Expr, Value, Var, Temp, Perm, Call, parse_code, OP_INIT - - -to_spectra = lambda r, g, b: spectra.rgb(float(r) / 255., float(g) / 255., float(b) / 255.) -# working with DOS palette only -PALETTE = (0, 0, 255, 16, 16, 16, 32, 32, 32, 48, 48, 48, 64, 64, 64, 80, 80, 80, 100, 100, 100, 116, 116, 116, 132, 132, 132, 148, 148, 148, 168, 168, 168, 184, 184, 184, 200, 200, 200, 216, 216, 216, 232, 232, 232, 252, 252, 252, 52, 60, 72, 68, 76, 92, 88, 96, 112, 108, 116, 132, 132, 140, 152, 156, 160, 172, 176, 184, 196, 204, 208, 220, 48, 44, 4, 64, 60, 12, 80, 76, 20, 96, 92, 28, 120, 120, 64, 148, 148, 100, 176, 176, 132, 204, 204, 168, 72, 44, 4, 88, 60, 20, 104, 80, 44, 124, 104, 72, 152, 132, 92, 184, 160, 120, 212, 188, 148, 244, 220, 176, 64, 0, 4, 88, 4, 16, 112, 16, 32, 136, 32, 52, 160, 56, 76, 188, 84, 108, 204, 104, 124, 220, 132, 144, 236, 156, 164, 252, 188, 192, 252, 208, 0, 252, 232, 60, 252, 252, 128, 76, 40, 0, 96, 60, 8, 116, 88, 28, 136, 116, 56, 156, 136, 80, 176, 156, 108, 196, 180, 136, 68, 24, 0, 96, 44, 4, 128, 68, 8, 156, 96, 16, 184, 120, 24, 212, 156, 32, 232, 184, 16, 252, 212, 0, 252, 248, 128, 252, 252, 192, 32, 4, 0, 64, 20, 8, 84, 28, 16, 108, 44, 28, 128, 56, 40, 148, 72, 56, 168, 92, 76, 184, 108, 88, 196, 128, 108, 212, 148, 128, 8, 52, 0, 16, 64, 0, 32, 80, 4, 48, 96, 4, 64, 112, 12, 84, 132, 20, 104, 148, 28, 128, 168, 44, 28, 52, 24, 44, 68, 32, 60, 88, 48, 80, 104, 60, 104, 124, 76, 128, 148, 92, 152, 176, 108, 180, 204, 124, 16, 52, 24, 32, 72, 44, 56, 96, 72, 76, 116, 88, 96, 136, 108, 120, 164, 136, 152, 192, 168, 184, 220, 200, 32, 24, 0, 56, 28, 0, 72, 40, 4, 88, 52, 12, 104, 64, 24, 124, 84, 44, 140, 108, 64, 160, 128, 88, 76, 40, 16, 96, 52, 24, 116, 68, 40, 136, 84, 56, 164, 96, 64, 184, 112, 80, 204, 128, 96, 212, 148, 112, 224, 168, 128, 236, 188, 148, 80, 28, 4, 100, 40, 20, 120, 56, 40, 140, 76, 64, 160, 100, 96, 184, 136, 136, 36, 40, 68, 48, 52, 84, 64, 64, 100, 80, 80, 116, 100, 100, 136, 132, 132, 164, 172, 172, 192, 212, 212, 224, 40, 20, 112, 64, 44, 144, 88, 64, 172, 104, 76, 196, 120, 88, 224, 140, 104, 252, 160, 136, 252, 188, 168, 252, 0, 24, 108, 0, 36, 132, 0, 52, 160, 0, 72, 184, 0, 96, 212, 24, 120, 220, 56, 144, 232, 88, 168, 240, 128, 196, 252, 188, 224, 252, 16, 64, 96, 24, 80, 108, 40, 96, 120, 52, 112, 132, 80, 140, 160, 116, 172, 192, 156, 204, 220, 204, 240, 252, 172, 52, 52, 212, 52, 52, 252, 52, 52, 252, 100, 88, 252, 144, 124, 252, 184, 160, 252, 216, 200, 252, 244, 236, 72, 20, 112, 92, 44, 140, 112, 68, 168, 140, 100, 196, 168, 136, 224, 200, 176, 248, 208, 184, 255, 232, 208, 252, 60, 0, 0, 92, 0, 0, 128, 0, 0, 160, 0, 0, 196, 0, 0, 224, 0, 0, 252, 0, 0, 252, 80, 0, 252, 108, 0, 252, 136, 0, 252, 164, 0, 252, 192, 0, 252, 220, 0, 252, 252, 0, 204, 136, 8, 228, 144, 4, 252, 156, 0, 252, 176, 48, 252, 196, 100, 252, 216, 152, 8, 24, 88, 12, 36, 104, 20, 52, 124, 28, 68, 140, 40, 92, 164, 56, 120, 188, 72, 152, 216, 100, 172, 224, 92, 156, 52, 108, 176, 64, 124, 200, 76, 144, 224, 92, 224, 244, 252, 200, 236, 248, 180, 220, 236, 132, 188, 216, 88, 152, 172, 244, 0, 244, 245, 0, 245, 246, 0, 246, 247, 0, 247, 248, 0, 248, 249, 0, 249, 250, 0, 250, 251, 0, 251, 252, 0, 252, 253, 0, 253, 254, 0, 254, 255, 0, 255, 76, 24, 8, 108, 44, 24, 144, 72, 52, 176, 108, 84, 210, 146, 126, 252, 60, 0, 252, 84, 0, 252, 104, 0, 252, 124, 0, 252, 148, 0, 252, 172, 0, 252, 196, 0, 64, 0, 0, 255, 0, 0, 48, 48, 0, 64, 64, 0, 80, 80, 0, 255, 255, 0, 32, 68, 112, 36, 72, 116, 40, 76, 120, 44, 80, 124, 48, 84, 128, 72, 100, 144, 100, 132, 168, 216, 244, 252, 96, 128, 164, 68, 96, 140, 255, 255, 255) -SAFE_COLORS = set(range(1, 0xD7)) -ALL_COLORS = set(range(256)) -SPECTRA_PALETTE = {i:to_spectra(PALETTE[i * 3], PALETTE[i * 3 + 1], PALETTE[i * 3 + 2]) for i in range(256)} -WATER_COLORS = set(range(0xF5, 0xFF)) - -# ZOOM_OUT_4X, ZOOM_NORMAL, ZOOM_OUT_2X, ZOOM_OUT_8X, ZOOM_OUT_16X, ZOOM_OUT_32X = range(6) -ZOOM_4X, ZOOM_NORMAL, ZOOM_2X, ZOOM_8X, ZOOM_16X, ZOOM_32X = range(6) -BPP_8, BPP_32 = range(2) - -OBJECT = 0x0f - - -def hex_str(s): - if isinstance(s, (bytes, memoryview)): - return ':'.join('{:02x}'.format(b) for b in s) - return ':'.join('{:02x}'.format(ord(c)) for c in s) - - -def color_distance(c1, c2): - rmean = (c1.rgb[0] + c2.rgb[0]) / 2. - r = c1.rgb[0] - c2.rgb[0] - g = c1.rgb[1] - c2.rgb[1] - b = c1.rgb[2] - c2.rgb[2] - return math.sqrt( - ((2 + rmean) * r * r) + - 4 * g * g + - (3 - rmean) * b * b) - - -def find_best_color(x, in_range=SAFE_COLORS): - mj, md = 0, 1e100 - for j in in_range: - c = SPECTRA_PALETTE[j] - d = color_distance(x, c) - if d < md: - mj, md = j, d - return mj - - -def fix_palette(img): - assert (img.mode == 'P') # TODO - pal = tuple(img.getpalette()) - if pal == PALETTE: return img - print(f'Custom palette in file {img.filename}, converting...') - # for i in range(256): - # if tuple(pal[i * 3: i*3 + 3]) != PALETTE[i * 3: i*3 + 3]: - # print(i, pal[i * 3: i*3 + 3], PALETTE[i * 3: i*3 + 3]) - remap = PaletteRemap() - for i in ALL_COLORS: - remap.remap[i] = find_best_color(to_spectra(pal[3 * i], pal[3 * i + 1], pal[3 * i + 2]), in_range=ALL_COLORS) - return remap.remap_image(img) - - -def open_image(filename, *args, **kw): - return fix_palette(Image.open(filename, *args, **kw)) - -# def map_rgb_image(self, im): -# assert im.mode == 'RGB', im.mode -# data = np.array(im) - -class BaseSprite: - def get_data(self): - raise NotImplemented - - def get_data_size(self): - raise NotImplemented - - -class PaletteRemap(BaseSprite): - def __init__(self, ranges=None): - self.remap = np.arange(256, dtype=np.uint8) - if ranges: - self.set_ranges(ranges) - - def get_data(self): - return b'\x00' + self.remap.tobytes() - - def get_data_size(self): - return 257 - - @classmethod - def from_function(cls, color_func, remap_water=False): - res = cls() - for i in SAFE_COLORS: - res.remap[i] = find_best_color(color_func(SPECTRA_PALETTE[i])) - if remap_water: - for i in WATER_COLORS: - res.remap[i] = find_best_color(color_func(SPECTRA_PALETTE[i])) - return res - - def set_ranges(self, ranges): - for r in ranges: - f, t, v = r - self.remap[f: t + 1] = v - - def remap_image(self, im): - assert im.mode == 'P', im.mode - data = np.array(im) - data = self.remap[data] - res = Image.fromarray(data) - res.putpalette(PALETTE) - return res - - -class RealSprite(BaseSprite): - def __init__(self, w, h, *, xofs=0, yofs=0, zoom=ZOOM_4X): - self.sprite_id = None - self.w = w - self.h = h - # self.file = None - # self.x = None - # self.y = None - self.xofs = xofs - self.yofs = yofs - self.zoom = zoom - - def get_data_size(self): - return 4 - - def get_data(self): - return struct.pack(' {self.set}' - return f'{self.low}..{self.high} -> {self.set}' - - __repr__ = __str__ - - -class VarAction2(LazyBaseSprite): - def __init__(self, feature, set_id, use_related, ranges, default, code): - super().__init__() - self.feature = feature - assert feature == OBJECT, feature - self.set_id = set_id - self.use_related = use_related - self.ranges = ranges - self.default = default - self.code = parse_code(code) - - def __str__(self): - return str(self.set_id) - - def _get_set_value(self, set_obj): - if isinstance(set_obj, Set): - return set_obj.value - assert isinstance(set_obj, int) - return set_obj | 0x8000 - - def _encode(self): - res = bytes((0x02, self.feature, self.set_id, 0x8a if self.use_related else 0x89)) - code = self.code[0].compile(register=0x80)[1] - for c in self.code[1:]: - code += bytes((OP_INIT,)) - code += c.compile(register=0x80)[1] - # print('CODE', hex_str(code)) - res += code[:-5] - res += bytes((code[-5] & ~0x20,)) # mark the end of a chain - res += code[-4:] - res += bytes((len(self.ranges),)) - # print('RES', hex_str(res)) - ranges = self.ranges - if isinstance(ranges, dict): - ranges = self.ranges.items() - for r in ranges: - if isinstance(r, Range): - set_obj = r.set - low = r.low - high = r.high - else: - set_obj = r[1] - low = r[0] - high = r[0] - # TODO split (or validate) negative-positive ranges - res += struct.pack(' 0) - if isinstance(sprites[0], RealSprite): - assert(all(isinstance(s, RealSprite) for s in sprites)) - assert(len(set(s.zoom for s in sprites)) == len(sprites)) - for s in sprites: - s.sprite_id = self._next_sprite_id - self._next_sprite_id += 1 - - for s in sprites: - self.sprites.append(s) - - self.pseudo_sprites.append(sprites[0]) - else: - assert(len(sprites) == 1) - self.pseudo_sprites.append(sprites[0]) - - def _write_pseudo_sprite(self, f, data, grf_type=0xff): - f.write(struct.pack(' recoloursprites() (257 each) - self._write_pseudo_sprite(f, b'\x02\x00\x00\x00') - - for s in self.pseudo_sprites: - self._write_pseudo_sprite(f, s.get_data(), grf_type=0xfd if isinstance(s, RealSprite) else 0xff) - f.write(b'\x00\x00\x00\x00') - for s in self.sprites: - f.write(s.get_real_data()) - - f.write(b'\x00\x00\x00\x00') \ No newline at end of file