diff --git a/grf/alpine/alpine.nml b/grf/alpine/alpine.nml index b670bfb2ed..e268ddd390 100644 --- a/grf/alpine/alpine.nml +++ b/grf/alpine/alpine.nml @@ -176,7 +176,7 @@ spriteset (meadow_transitions, "gfx/meadow_transitions.png") { tmpl_groundsprites(1, 14 * 64 + 1) tmpl_groundsprites(1, 15 * 64 + 1) } - +/* spritelayout meadow_groundsprites_default { ground { sprite: meadow_transitions( @@ -187,8 +187,15 @@ spritelayout meadow_groundsprites_default { + (nearby_tile_object_type( 1, 0) == meadow && nearby_tile_object_type( 1, -1) == meadow &&nearby_tile_object_type( 0, -1) == meadow ? 0 : 152) ); } +}*/ + +spritelayout meadow_groundsprites_default(n) { + ground { + sprite: meadow_groundsprites(n); + } } + spritelayout meadow_groundsprites_purchase { ground { sprite: meadow_groundsprites; @@ -196,12 +203,35 @@ spritelayout meadow_groundsprites_purchase { } -switch (FEAT_OBJECTS, SELF, switch_meadow_groundsprites_default, [ +/*switch (FEAT_OBJECTS, SELF, switch_meadow_groundsprites_default, [ STORE_TEMP(slope_to_sprite_offset(tile_slope), 0) ]) { meadow_groundsprites_default; } +*/ +switch(FEAT_OBJECTS, SELF, meadow_ground_switch, tile_slope) { + 0: return meadow_groundsprites_default(0); + 1: return meadow_groundsprites_default(1); + 2: return meadow_groundsprites_default(2); + 4: return meadow_groundsprites_default(4); + 8: return meadow_groundsprites_default(8); + 9: return meadow_groundsprites_default(9); + 3: return meadow_groundsprites_default(3); + 6: return meadow_groundsprites_default(6); + 12: return meadow_groundsprites_default(12); + 5: return meadow_groundsprites_default(5); + 10: return meadow_groundsprites_default(10); + 11: return meadow_groundsprites_default(11); + 7: return meadow_groundsprites_default(7); + 14: return meadow_groundsprites_default(14); + 13: return meadow_groundsprites_default(13); + 27: return meadow_groundsprites_default(17); + 23: return meadow_groundsprites_default(16); + 30: return meadow_groundsprites_default(18); + 29: return meadow_groundsprites_default(15); + default: return meadow_groundsprites_default(17); +} item (FEAT_OBJECTS, meadow) { property { @@ -214,12 +244,13 @@ item (FEAT_OBJECTS, meadow) { size: [1,1]; } graphics { - default: meadow_groundsprites_default; - purchase: meadow_groundsprites_purchase; - tile_check: CB_RESULT_LOCATION_ALLOW; + /// default: meadow_groundsprites_default(0); + default: meadow_ground_switch(); + //purchase: meadow_groundsprites_purchase; + //tile_check: CB_RESULT_LOCATION_ALLOW; } } - +/* spriteset (creek_groundsprites, "gfx/rivers.png") { tmpl_groundsprites_anim(1, 0 * 64 + 1) tmpl_groundsprites_anim(1, 1 * 64 + 1) @@ -1705,3 +1736,4 @@ item (FEAT_OBJECTS, rivers_80) { } } +*/ \ No newline at end of file diff --git a/grf/alpine/alpine.py b/grf/alpine/alpine.py index 05f5339dd5..494a790044 100644 --- a/grf/alpine/alpine.py +++ b/grf/alpine/alpine.py @@ -8,29 +8,33 @@ gen = grf.NewGRF( ) +def tmpl_ground_sprites(func, x, y, **kw): + func( 0+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # + func( 80+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # W + func( 160+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # S + func( 240+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # S W + func( 320+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # E + func( 398+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # E W + func( 478+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # E S + func( 558+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # E S W + func( 638+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N + func( 718+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N W + func( 798+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N S + func( 878+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N S W + func( 958+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N E + func(1038+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N E W + func(1118+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N E S + func(1196+x, y, 64, 47, xofs=-31, yofs=-16, **kw) # N E W STEEP + func(1276+x, y, 64, 15, xofs=-31, yofs= 0, **kw) # E S W STEEP + func(1356+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N S W STEEP + func(1436+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N E S STEEP + + 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)) - sprite( 0+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # - sprite( 80+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # W - sprite( 160+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # S - sprite( 240+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # S W - sprite( 320+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # E - sprite( 398+x, y, 64, 31, xofs=-31, yofs= 0, **kw) # E W - sprite( 478+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # E S - sprite( 558+x, y, 64, 23, xofs=-31, yofs= 0, **kw) # E S W - sprite( 638+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N - sprite( 718+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N W - sprite( 798+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N S - sprite( 878+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N S W - sprite( 958+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N E - sprite(1038+x, y, 64, 39, xofs=-31, yofs= -8, **kw) # N E W - sprite(1118+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N E S - sprite(1196+x, y, 64, 47, xofs=-31, yofs=-16, **kw) # N E W STEEP - sprite(1276+x, y, 64, 15, xofs=-31, yofs= 0, **kw) # E S W STEEP - sprite(1356+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N S W STEEP - sprite(1436+x, y, 64, 31, xofs=-31, yofs= -8, **kw) # N E S STEEP + tmpl_ground_sprites(sprite, x, y, **kw) def replace_shore_sprites(sprite_id, file, x, y, **kw): @@ -105,6 +109,25 @@ def replace_coastal_sprites(file, x, y, **kw): replace_coastal_sprites('gfx/water/seashore_grid_temperate.gimp.png', 1, 1) +# spriteset (meadow_groundsprites, "gfx/meadow_grid_temperate.png") { tmpl_groundsprites(1, 1) } + +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.BasicSpriteLayout(grf.OBJECT, 255)) + +gen.add_sprite(grf.Object(0, + label=b'FLMA', + size=0x11, + climate=0xf, + eol_date=0, + flags=grf.Object.Flags.HAS_NO_FOUNDATION | grf.Object.Flags.ALLOW_UNDER_BRIDGE, +)) + +gen.add_sprite(grf.Action3(grf.OBJECT, [0], [[255, 255]], 255)) + +{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} + # CREEKS diff --git a/grf/alpine/grf.py b/grf/alpine/grf.py index 7ebcb34d4f..20fdf4ffdf 100644 --- a/grf/alpine/grf.py +++ b/grf/alpine/grf.py @@ -18,6 +18,9 @@ WATER_COLORS = set(range(0xF5, 0xFF)) ZOOM_4X, ZOOM_NORMAL, ZOOM_2X, ZOOM_8X, ZOOM_16X, ZOOM_32X = range(6) BPP_8, BPP_32 = range(2) +OBJECT = 0x0f + + def color_distance(c1, c2): rmean = (c1.rgb[0] + c2.rgb[0]) / 2. r = c1.rgb[0] - c2.rgb[0] @@ -43,6 +46,81 @@ def find_best_color(x, in_range=SAFE_COLORS): # assert im.mode == 'RGB', im.mode # data = np.array(im) +OP_ADD = 0x00 +OP_SUB = 0x01 +OP_TSTO = 0x0E +OP_PSTO = 0x10 + +OPERATORS = { + OP_ADD: ('{a} + {b}', 2), + OP_SUB: ('{a} - {b}', 2), + OP_TSTO: ('TEMP[{b}] = {a}', 1), + OP_PSTO: ('PERM[{b}] = {a}', 1), +} + +DEFAULT_INDENT_STR = ' ' + + +class Node: + def __init__(self): + pass + + def _make_node(self, value): + if isinstance(value, int): + return Value(value) + assert isinstance(value, Node) + return value + + def __add__(self, other): + return Expr(OP_ADD, self, self._make_node(other)) + + def __sub__(self, other): + return Expr(OP_SUB, self, self._make_node(other)) + + def store_temp(self, register): + return Expr(OP_TSTO, self, self._make_node(register)) + + def store_perm(self, register): + return Expr(OP_PSTO, self, self._make_node(register)) + + def format(self, parent_priority=0, indent=0, indent_str=DEFAULT_INDENT_STR): + raise NotImplementedError + + def __str__(self): + return self.format() + + def __repr__(self): + return self.format() + + +class Expr(Node): + def __init__(self, op, a, b): + self.op = op + self.a = a + self.b = b + + def format(self, parent_priority=0, indent=0, indent_str=DEFAULT_INDENT_STR): + if self.op in OPERATORS: + fmt, prio = OPERATORS[self.op] + else: + fmt, prio = '{a} <{self.op:02x}> {b}', 1 + astr = self.a.format(prio, indent=0) + bstr = self.b.format(prio, indent=0) + res = fmt.format(a=astr, b=bstr) + suffix = indent_str * indent + if prio <= parent_priority: + return suffix + f'({res})' + return suffix + res + + +class Value(Node): + def __init__(self, value): + super().__init__() + self.value = value + + def format(self, parent_priority=0, indent=0, indent_str=DEFAULT_INDENT_STR): + return indent_str * indent + str(self.value) + class BaseSprite: def get_data(self): @@ -253,7 +331,7 @@ class InformationSprite(BaseSprite): # action 14 return len(self._data) -class ReplaceSprites: # action A +class ReplaceSprites(BaseSprite): # action A def __init__(self, sets): assert isinstance(sets, (list, tuple)) assert len(sets) <= 0xff @@ -270,7 +348,7 @@ class ReplaceSprites: # action A return 2 + 3 * len(self.sets) -class ReplaceNewSprites: # action 5 +class ReplaceNewSprites(BaseSprite): # action 5 def __init__(self, set_type, num): # TODO offset assert isinstance(set_type, int) assert isinstance(num, int) @@ -283,6 +361,159 @@ class ReplaceNewSprites: # action 5 def get_data_size(self): return 5 +ACTION0_OBJECT_PROPS = ( + (0x08, 'label', 'L'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Class label, see below + (0x09, 'class_name_id', 'W'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Text ID for class + (0x0A, 'name_id', 'W'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Text ID for this object + (0x0B, 'climate', 'B'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Climate availability + (0x0C, 'size', 'B'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Byte representing size, see below + (0x0D, 'build_cost_factor', 'B'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Object build cost factor (sets object removal cost factor as well) + (0x0E, 'intro_date', 'D'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Introduction date, see below + (0x0F, 'eol_date', 'D'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 End of life date, see below + (0x10, 'flags', 'W'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Object flags, see below + (0x11, 'anim_info', 'W'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Animation information + (0x12, 'anim_speed', 'B'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Animation speed + (0x13, 'anim_trigger', 'W'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Animation triggers + (0x14, 'removal_cost_factor', 'B'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Object removal cost factor (set after object build cost factor) + (0x15, 'cb_flags', 'W'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Callback flags, see below + (0x16, 'building_height', 'B'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Height of the building + (0x17, 'num_views', 'B'), # Supported by OpenTTD 1.1 (r20670)1.1 Supported by TTDPatch 2.6 (r2340)2.6 Number of object views + (0x18, 'num_objects', 'B'), # Supported by OpenTTD 1.4 (r25879)1.4 Not supported by TTDPatch Measure for number of objects placed upon map creation +) + +ACTION0_OBJECT_PROP_DICT = {name: (id, size) for id, name, size in ACTION0_OBJECT_PROPS} + + +class LazyBaseSprite(BaseSprite): + def __init__(self): + super().__init__() + self._data = None + + def _encode(self): + raise NotImplemented + + def get_data(self): + if self._data is None: + self._data = self._encode() + return self._data + + def get_data_size(self): + return len(self.get_data()) + +class Action0(LazyBaseSprite): # action 0 + def __init__(self, feature, first_id, count, props): + super().__init__() + self.feature = feature + self.first_id = first_id + self.count = count + if feature != OBJECT: + raise NotImplemented + self.props = props + assert all(x in ACTION0_OBJECT_PROP_DICT for x in props) + + def _encode_value(self, value, fmt): + if fmt == 'B': return struct.pack(' {self.set}' + return f'{self.low}..{self.high} -> {self.set}' + + __repr__ = __str__ + + def read_sprite_layout(d, num, no_z_position): has_z_position = not no_z_position has_flags = bool((num >> 6) & 1) @@ -302,6 +329,127 @@ def read_sprite_layout(d, num, no_z_position): 'sprites': sprites } + +VA2_GLOBALS = { + 0x00: ('date', 'W'), # 80 W current date (counted as days from 1920)[1] + 0x01: ('year', 'B'), # 81 B 0.6 2.0 Current year (count from 1920, max. 2175 even with eternalgame)[1] + 0x02: ('month', 'B/D'), # 82 B/D 0.6 2.0 current month (0-11) in bits 0-7; the higher bytes contain unusable junk.[1] 0.7  Since OpenTTD r13594 'day of month' (0-30) is stored in bits 8-12, bit 15 is set in leapyears and 'day of year'(0-364 resp. 365) is stored in bits 16-24. All other bits are reserved and should be masked. + 0x03: ('climate', 'B'), # 83 B 0.6 2.0 Current climate: 00 = temp, 01 = arctic, 02 = trop, 03 = toyland + # 84 D 0.6 2.0 GRF loading stage, see below + # 85 B 0.6 2.0 TTDPatch flags: only for bit tests + 0x06: ('drive_side', 'B'), # 86 B 0.6 2.0 Road traffic side: bit 4 clear=left, set=right; other bits are reserved and must be masked. (87) (87) B   No longer used since TTDPatch 2.0. (was width of "€" character) + # 88 4*B 0.6 2.0 Checks specified GRFID (see condition-types)[2] + 0x09: ('date_fract', 'W'), # 89 W 0.6 2.0 date fraction, incremented by 0x375 every engine tick + 0x0A: ('anim_counter', 'W'), # 8A W 0.6 2.0 animation counter, incremented every tick + 0x0B: ('ttdp_version', 'D'), # 8B D  2.0 TTDPatch version, see below [3][4] + 0x0C: ('cur_cb_id', 'W'), # W 0.6 2.5 current callback ID (feature-specific), set to 00 when not in a callback + 0x0D: ('ttd_version', 'B'), # 8D B 0.6 2.5 TTD version, 0=DOS, 1=Windows + 0x0E: ('train_y_ofs', 'B'), # 8E 8E B 0.6 2.5 Y-Offset for train sprites + 0x0F: ('rail_cost', '3*B'), # 8F 8F 3*B 0.6 2.5 Rail track type cost factors + 0x10: ('cb_info1', 'D'), # D 0.6 2.5 Extra callback info 1, see below. + 0x11: ('cur_rail_tool', 'B'), # B  2.5 current rail tool type (for station callbacks) + 0x12: ('game_mode', 'B'), # 92 B 0.6 2.5 Game mode, 0 in title screen, 1 in game and 2 in editor + 0x13: ('tile_refresh_left', 'W'), # 93 93 W  2.5 Tile refresh offset to left [5] + 0x14: ('tile_refresh_right', 'W'), # 94 94 W  2.5 Tile refresh offset to right [5] + 0x15: ('tile_refresh_up', 'W'), # 95 95 W  2.5 Tile refresh offset upwards [5] + 0x16: ('tile_refresh_down', 'W'), # 96 96 W  2.5 Tile refresh offset downwards [5] + # 97 97 B  2.5 Fixed snow line height [6][7] + 0x18: ('cb_info2', 'D'), # D 0.6 2.5 Extra callback info 2, see below. + # 99 99 D  2.5 Global ID offset + 0x1A: ('max_uint32', 'D'), # 9A D 0.6 2.5 Has always all bits set; you can use this to make unconditional jumps + 0x1B: ('display_options', 'B'), # B  2.5 display options; bit 0=town names, 1=station names, 2=signs, 3=animation, 4=transparency, 5=full detail + 0x1C: ('va2_ret', 'D'), # D 0.6 2.5 result from most recent VarAction2 + 0x1D: ('ttd_platform', 'D'), # 9D D 0.6 2.5 TTD Platform, 0=TTDPatch, 1=OpenTTD [4] + 0x1E: ('grf_featuers', 'D'), # 9E 9E D 0.6 2.5 Misc. GRF Features + # 9F D  2.5 writable only: Locale-dependent settings + 0x20: ('snow_line', 'B'), # B 0.6 2.5 Current snow line height, FFh if snow isn't present at all [7] + 0x21: ('openttd_version', 'D'), # A1 D 0.6  OpenTTD version, see below. [4] + 0x22: ('difficulty_level', 'D'), # A2 D 0.7 2.6 Difficulty level: 00= easy, 01=medium, 02=hard, 03=custom + 0x23: ('date_long', 'D'), # A3 D 0.7 2.6 Current date long format + 0x24: ('year_zero', 'D'), # A4 D 0.7 2.6 Current year zero based + 0x25: ('a3_grfid', 'D'), # D 0.7  GRFID of the grf that contains the corresponding Action3. Useful when accessing the "related" object. Currently only supported for vehicles. +} + +V2_OBJECT_VARS = { + 0x40: ('relative_pos', 'D'), # Relative position, like Industry Tile var43 + 0x41: ('tile_info', 'W'), # Tile information, see below + 0x42: ('constructed', 'D'), # Construction date from year 0 + 0x43: ('anim_counter', 'B'), # Animation counter, see below + 0x44: ('founder', 'B'), # Object founder information + 0x45: ('closest_town_info', 'D'), # Get town zone and Manhattan distance of closest town + 0x46: ('closest_town_dist_squared', 'D'), # Get square of Euclidian distance of closest town + 0x47: ('colour', 'B'), # Object colour + 0x48: ('views', 'B'), # Object views + 0x60: ('type_view_ofs', 'W'), # Get object type and view at offset + 0x61: ('random_ofs', 'B'), # Get random bits at offset + 0x62: ('nearby_tile_info', 'D'), # Land info of nearby tiles + 0x63: ('nearby_anim_counter', 'W'), # Animation counter of nearby tile + 0x64: ('object_count', 'D'), # Count of object, distance of closest instance +} + +VA2_OP = { + 0x00: '+', # \2+ result = val1 + val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 + 0x01: '-', # \2- result = val1 - val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 + 0x02: 'min', # \2< result = min(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered signed + 0x03: 'max', # \2> result = max(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 + 0x04: 'min', # \2u< result = min(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered unsigned + 0x05: 'max', # \2u> result = max(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 + 0x06: '/', # \2/ result = val1 / val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered signed + 0x07: '%', # \2% result = val1 mod val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 + 0x08: 'u/', # \2u/ result = val1 / val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered unsigned + 0x09: 'u%', # \2u% result = val1 mod val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 + 0x0A: '*', # \2* result = val1 * val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 result will be truncated to B/W/D (that makes it the same for signed/unsigned operands) + 0x0B: '&', # \2& result = val1 & val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 bitwise AND + 0x0C: '|', # \2| result = val1 | val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 bitwise OR + 0x0D: '^', # \2^ result = val1 ^ val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 bitwise XOR + 0x0E: '(tsto)', # \2s or \2sto [1] var7D[val2] = val1, result = val1 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1246)2.6 Store result. See Temporary storage. + 0x0F: ';', # \2r or \2rst [1] result = val2 [2] Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1246)2.6 + 0x10: '(psto)', # \2psto [3] var7C[val2] = val1, result = val1 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1315)2.6 Store result into persistent storage. See Persistent storage. + 0x11: '(ror)', # \2ror or \2rot [4] result = val1 rotate right val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1651)2.6 Always a 32-bit rotation. + 0x12: '(cmp)', # \2cmp [3] see notes Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1698)2.6 Result is 0 if val1val2. Both values are considered signed. [5] + 0x13: '(ucmp)', # \2ucmp [3] see notes Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1698)2.6 The same as 12, but operands are considered unsigned. [5] + 0x14: '<<', # \2<< [3] result = val1 << val2 Supported by OpenTTD 1.1 (r20332)1.1 Supported by TTDPatch 2.6 (r2335)2.6 shift left; val2 should be in the range 0 to 31. + 0x15: 'u>>', # \2u>> [3] result = val1 >> val2 Supported by OpenTTD 1.1 (r20332)1.1 Supported by TTDPatch 2.6 (r2335)2.6 shift right (unsigned); val2 should be in the range 0 to 31. + 0x16: '>>', # \2>> [3] result = val1 >> val2 Supported by OpenTTD 1.1 (r20332)1.1 Supported by TTDPatch 2.6 (r2335)2.6 shift right (signed); val2 should be in the range 0 to 31. +} + + +def get_va2_var(var): + if var < 0x40: + name, fmt = VA2_GLOBALS[var] + return f'[{name}]', fmt + if var == 0x5f: return '(random)', 'D' + if var == 0x7b: return '(var_eval)', '' + if var == 0x7c: return '(perm)', 'D' + if var == 0x7d: return '(temp)', 'D' + if var == 0x7e: return '(call)', 'D' + if var == 0x7f: return '(param)', 'D' + return V2_OBJECT_VARS[var] + + +class Call(Node): + def __init__(self, subroutine): + self.suroutine = subroutine + + +class Generic(Node): + def __init__(self, var, shift, and_mask, type, add_val, divmod_val): + self.var = var + self.shift = shift + self.and_mask = and_mask + self.type = type + self.add_val = add_val + self.divmod_val = divmod_val + + def format(self, parent_priority=0, indent=0, indent_str=' '): + addstr = '' + if self.type == 1: + addstr = ' +{self.add_val} /{self.divmod_val}' + elif self.type == 2: + addstr = ' +{self.add_val} %{self.divmod_val}' + return (indent_str * indent) + f'(var: {self.var} >>{self.shift} &{self.and_mask}{addstr})' + + def decode_action2(data): feature = data[0] set_id = data[1] @@ -315,50 +463,78 @@ def decode_action2(data): ground_sprite, building_sprite, xofs, yofs, xext, yext, zext = struct.unpack_from('> 2) & 3 + related_scope = bool(num_ent1 & 2) first = True ofs = 3 - adjusts = [] + root = None while True: - res = {} - res['op'] = 0 if first else d.get_byte() - var = res['var'] = d.get_byte() - if var == 0x7e: - res['subroutine'] = d.get_byte() - else: - res['parameter'] = d.get_byte() if 0x60 <= var < 0x80 else 0 + op = 0 if first else d.get_byte() + var = d.get_byte() + if 0x60 <= var < 0x80: + param = d.get_byte() varadj = d.get_byte() - res['shift_num'] = varadj & 0x1f + shift = varadj & 0x1f has_more = bool(varadj & 0x20) - res['type'] = varadj >> 6 - res['and_mask'] = d.get_var(group_size) - if res['type'] != 0: - res['add_val'] = d.get_var(group_size) - res['divmod_val'] = d.get_var(group_size) - adjusts.append(res) + node_type = varadj >> 6 + and_mask = d.get_var(group_size) + if node_type != 0: + # old magic, use advaction2 instead + add_val = d.get_var(group_size) + divmod_val = d.get_var(group_size) + node = Generic(var, shift, and_mask, node_type, add_val, divmod_val) + elif var == 0x1a and shift == 0: + node = Value(and_mask) + else: + node = Generic(var, shift, and_mask, 0, None, None) + if first: + root = node + else: + root = Expr(op, root, node) + + first = False if not has_more: break + # no ranges is special for "do not switch, return the switch value" + # oh, also, the ranges are unsigned + # so if you want to set -5..5 you have to split into two ranges -5..-1, 0..5 n_ranges = d.get_byte() ranges = [] for _ in range(n_ranges): group = d.get_word() low = d.get_var(group_size) high = d.get_var(group_size) - ranges.append((group, low, high)) + ranges.append(Range(low, high, Set(group))) - default_group = d.get_word() - - print(f'default_group: {default_group} adjusts:{adjusts} ranges:{ranges} ') + default_group = Set(d.get_word()) + print(f'VARACT default_group:{default_group} related_scope:{related_scope} ranges:{ranges} ') + # for a in adjusts: + # var = a['var'] + # name, fmt = get_va2_var(var) + # op = VA2_OP[a['op']] + # param_str = '' + # if 0x60 <= var < 0x80: + # if var == 0x7e: + # param_str = ' proc:{:02x}'.format(a['subroutine']) + # else: + # param_str = ' param:{:02x}'.format(a['parameter']) + # type_str = '' + # if a['type'] != 0: + # type_str = '+{add_val} /%{divmod_val}'.format(**a) + # print(f' op<{a["op"]}>:{op} var<{var:02x}>:{name}({fmt}){param_str} type:{a["type"]} >>{a["shift_num"]} &{a["and_mask"]:x}{type_str}') + print() + print(root.format(indent=1)) return layout = read_sprite_layout(d, max(num_ent1, 1), num_ent1 == 0) @@ -378,6 +554,26 @@ def decode_action2(data): print(f'ent1:{ent1} ent2:{ent2}') +def decode_action3(data): + feature = data[0] + idcount = data[1] + print(f' <3>MAP feature:{str_feature(feature)} ', end='') + if data[1] == 0: + _, set_id = struct.unpack_from('