254 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			254 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | from typing import Dict, Set | ||
|  | from ..roomEditor import RoomEditor | ||
|  | 
 | ||
|  | 
 | ||
|  | animated_tiles = {0x0E, 0x1B, 0x1E, 0x1F, 0x44, 0x91, 0xCF, 0xD0, 0xD1, 0xD2, 0xD9, 0xDC, 0xE9, 0xEB, 0xEC, 0xED, 0xEE, 0xEF} | ||
|  | entrance_tiles = {0xE1, 0xE2, 0xE3, 0xBA, 0xC6} | ||
|  | 
 | ||
|  | solid_tiles = set() | ||
|  | open_tiles = set() | ||
|  | walkable_tiles = set() | ||
|  | vertical_edge_tiles = set() | ||
|  | horizontal_edge_tiles = set() | ||
|  | 
 | ||
|  | 
 | ||
|  | class TileInfo: | ||
|  |     def __init__(self, key): | ||
|  |         self.key = key | ||
|  |         self.up = set() | ||
|  |         self.right = set() | ||
|  |         self.down = set() | ||
|  |         self.left = set() | ||
|  |         self.up_freq = {} | ||
|  |         self.right_freq = {} | ||
|  |         self.down_freq = {} | ||
|  |         self.left_freq = {} | ||
|  |         self.frequency = 0 | ||
|  | 
 | ||
|  |     def copy(self): | ||
|  |         result = TileInfo(self.key) | ||
|  |         result.up = self.up.copy() | ||
|  |         result.right = self.right.copy() | ||
|  |         result.down = self.down.copy() | ||
|  |         result.left = self.left.copy() | ||
|  |         result.up_freq = self.up_freq.copy() | ||
|  |         result.right_freq = self.right_freq.copy() | ||
|  |         result.down_freq = self.down_freq.copy() | ||
|  |         result.left_freq = self.left_freq.copy() | ||
|  |         result.frequency = self.frequency | ||
|  |         return result | ||
|  | 
 | ||
|  |     def remove(self, tile_id): | ||
|  |         if tile_id in self.up: | ||
|  |             self.up.remove(tile_id) | ||
|  |             del self.up_freq[tile_id] | ||
|  |         if tile_id in self.down: | ||
|  |             self.down.remove(tile_id) | ||
|  |             del self.down_freq[tile_id] | ||
|  |         if tile_id in self.left: | ||
|  |             self.left.remove(tile_id) | ||
|  |             del self.left_freq[tile_id] | ||
|  |         if tile_id in self.right: | ||
|  |             self.right.remove(tile_id) | ||
|  |             del self.right_freq[tile_id] | ||
|  | 
 | ||
|  |     def update(self, other: "TileInfo", tile_filter: Set[int]): | ||
|  |         self.frequency += other.frequency | ||
|  |         self.up.update(other.up.intersection(tile_filter)) | ||
|  |         self.down.update(other.down.intersection(tile_filter)) | ||
|  |         self.left.update(other.left.intersection(tile_filter)) | ||
|  |         self.right.update(other.right.intersection(tile_filter)) | ||
|  |         for k, v in other.up_freq.items(): | ||
|  |             if k not in tile_filter: | ||
|  |                 continue | ||
|  |             self.up_freq[k] = self.up_freq.get(k, 0) + v | ||
|  |         for k, v in other.down_freq.items(): | ||
|  |             if k not in tile_filter: | ||
|  |                 continue | ||
|  |             self.down_freq[k] = self.down_freq.get(k, 0) + v | ||
|  |         for k, v in other.left_freq.items(): | ||
|  |             if k not in tile_filter: | ||
|  |                 continue | ||
|  |             self.left_freq[k] = self.left_freq.get(k, 0) + v | ||
|  |         for k, v in other.down_freq.items(): | ||
|  |             if k not in tile_filter: | ||
|  |                 continue | ||
|  |             self.right_freq[k] = self.right_freq.get(k, 0) + v | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return f"<{self.key}>\n U{[f'{n:02x}' for n in self.up]}\n R{[f'{n:02x}' for n in self.right]}\n D{[f'{n:02x}' for n in self.down]}\n L{[f'{n:02x}' for n in self.left]}>" | ||
|  | 
 | ||
|  | 
 | ||
|  | class TileSet: | ||
|  |     def __init__(self, *, main_id=None, animation_id=None): | ||
|  |         self.main_id = main_id | ||
|  |         self.animation_id = animation_id | ||
|  |         self.palette_id = None | ||
|  |         self.attr_bank = None | ||
|  |         self.attr_addr = None | ||
|  |         self.tiles: Dict[int, "TileInfo"] = {} | ||
|  |         self.all: Set[int] = set() | ||
|  | 
 | ||
|  |     def copy(self) -> "TileSet": | ||
|  |         result = TileSet(main_id=self.main_id, animation_id=self.animation_id) | ||
|  |         for k, v in self.tiles.items(): | ||
|  |             result.tiles[k] = v.copy() | ||
|  |         result.all = self.all.copy() | ||
|  |         return result | ||
|  | 
 | ||
|  |     def remove(self, tile_id): | ||
|  |         self.all.remove(tile_id) | ||
|  |         del self.tiles[tile_id] | ||
|  |         for k, v in self.tiles.items(): | ||
|  |             v.remove(tile_id) | ||
|  | 
 | ||
|  |     # Look at the "other" tileset and merge information about tiles known in this tileset | ||
|  |     def learn_from(self, other: "TileSet"): | ||
|  |         for key, other_info in other.tiles.items(): | ||
|  |             if key not in self.all: | ||
|  |                 continue | ||
|  |             self.tiles[key].update(other_info, self.all) | ||
|  | 
 | ||
|  |     def combine(self, other: "TileSet"): | ||
|  |         if other.main_id and not self.main_id: | ||
|  |             self.main_id = other.main_id | ||
|  |         if other.animation_id and not self.animation_id: | ||
|  |             self.animation_id = other.animation_id | ||
|  |         for key, other_info in other.tiles.items(): | ||
|  |             if key not in self.all: | ||
|  |                 self.tiles[key] = other_info.copy() | ||
|  |             else: | ||
|  |                 self.tiles[key].update(other_info, self.all) | ||
|  |         self.all.update(other.all) | ||
|  | 
 | ||
|  | 
 | ||
|  | def loadTileInfo(rom) -> Dict[str, TileSet]: | ||
|  |     for n in range(0x100): | ||
|  |         physics_flag = rom.banks[8][0x0AD4 + n] | ||
|  |         if n == 0xEF: | ||
|  |             physics_flag = 0x01  # One of the sky tiles is marked as a pit instead of solid, which messes with the generation of sky | ||
|  |         if physics_flag in {0x00, 0x05, 0x06, 0x07}: | ||
|  |             open_tiles.add(n) | ||
|  |             walkable_tiles.add(n) | ||
|  |             vertical_edge_tiles.add(n) | ||
|  |             horizontal_edge_tiles.add(n) | ||
|  |         elif physics_flag in {0x01, 0x04, 0x60}: | ||
|  |             solid_tiles.add(n) | ||
|  |             vertical_edge_tiles.add(n) | ||
|  |             horizontal_edge_tiles.add(n) | ||
|  |         elif physics_flag in {0x08}:  # Bridge | ||
|  |             open_tiles.add(n) | ||
|  |             walkable_tiles.add(n) | ||
|  |         elif physics_flag in {0x02}:  # Stairs | ||
|  |             open_tiles.add(n) | ||
|  |             walkable_tiles.add(n) | ||
|  |             horizontal_edge_tiles.add(n) | ||
|  |         elif physics_flag in {0x03}:  # Entrances | ||
|  |             open_tiles.add(n) | ||
|  |         elif physics_flag in {0x30}:  # bushes/rocks | ||
|  |             open_tiles.add(n) | ||
|  |         elif physics_flag in {0x50}:  # pits | ||
|  |             open_tiles.add(n) | ||
|  |     world_tiles = {} | ||
|  |     for ry in range(0, 16): | ||
|  |         for rx in range(0, 16): | ||
|  |             tileset_id = rom.banks[0x3F][0x3F00 + rx + (ry << 4)] | ||
|  |             re = RoomEditor(rom, rx | (ry << 4)) | ||
|  |             tiles = re.getTileArray() | ||
|  |             for y in range(8): | ||
|  |                 for x in range(10): | ||
|  |                     tile_id = tiles[x+y*10] | ||
|  |                     world_tiles[(rx*10+x, ry*8+y)] = (tile_id, tileset_id, re.animation_id | 0x100) | ||
|  | 
 | ||
|  |     # Fix up wrong tiles | ||
|  |     world_tiles[(150, 24)] = (0x2A, world_tiles[(150, 24)][1], world_tiles[(150, 24)][2])  # Left of the raft house, a tree has the wrong tile. | ||
|  | 
 | ||
|  |     rom_tilesets: Dict[int, TileSet] = {} | ||
|  |     for (x, y), (key, tileset_id, animation_id) in world_tiles.items(): | ||
|  |         if key in animated_tiles: | ||
|  |             if animation_id not in rom_tilesets: | ||
|  |                 rom_tilesets[animation_id] = TileSet(animation_id=animation_id&0xFF) | ||
|  |             tileset = rom_tilesets[animation_id] | ||
|  |         else: | ||
|  |             if tileset_id not in rom_tilesets: | ||
|  |                 rom_tilesets[tileset_id] = TileSet(main_id=tileset_id) | ||
|  |             tileset = rom_tilesets[tileset_id] | ||
|  |         tileset.all.add(key) | ||
|  |         if key not in tileset.tiles: | ||
|  |             tileset.tiles[key] = TileInfo(key) | ||
|  |         ti = tileset.tiles[key] | ||
|  |         ti.frequency += 1 | ||
|  |         if (x, y - 1) in world_tiles: | ||
|  |             tile_id = world_tiles[(x, y - 1)][0] | ||
|  |             ti.up.add(tile_id) | ||
|  |             ti.up_freq[tile_id] = ti.up_freq.get(tile_id, 0) + 1 | ||
|  |         if (x + 1, y) in world_tiles: | ||
|  |             tile_id = world_tiles[(x + 1, y)][0] | ||
|  |             ti.right.add(tile_id) | ||
|  |             ti.right_freq[tile_id] = ti.right_freq.get(tile_id, 0) + 1 | ||
|  |         if (x, y + 1) in world_tiles: | ||
|  |             tile_id = world_tiles[(x, y + 1)][0] | ||
|  |             ti.down.add(tile_id) | ||
|  |             ti.down_freq[tile_id] = ti.down_freq.get(tile_id, 0) + 1 | ||
|  |         if (x - 1, y) in world_tiles: | ||
|  |             tile_id = world_tiles[(x - 1, y)][0] | ||
|  |             ti.left.add(tile_id) | ||
|  |             ti.left_freq[tile_id] = ti.left_freq.get(tile_id, 0) + 1 | ||
|  | 
 | ||
|  |     tilesets = { | ||
|  |         "basic": rom_tilesets[0x0F].copy() | ||
|  |     } | ||
|  |     for key, tileset in rom_tilesets.items(): | ||
|  |         tilesets["basic"].learn_from(tileset) | ||
|  |     tilesets["mountains"] = rom_tilesets[0x3E].copy() | ||
|  |     tilesets["mountains"].combine(rom_tilesets[0x10B]) | ||
|  |     tilesets["mountains"].remove(0xB6)  # Remove the raft house roof | ||
|  |     tilesets["mountains"].remove(0xB7)  # Remove the raft house roof | ||
|  |     tilesets["mountains"].remove(0x66)  # Remove the raft house roof | ||
|  |     tilesets["mountains"].learn_from(rom_tilesets[0x1C]) | ||
|  |     tilesets["mountains"].learn_from(rom_tilesets[0x3C]) | ||
|  |     tilesets["mountains"].learn_from(rom_tilesets[0x30]) | ||
|  |     tilesets["mountains"].palette_id = 0x15 | ||
|  |     tilesets["mountains"].attr_bank = 0x27 | ||
|  |     tilesets["mountains"].attr_addr = 0x5A20 | ||
|  | 
 | ||
|  |     tilesets["egg"] = rom_tilesets[0x3C].copy() | ||
|  |     tilesets["egg"].combine(tilesets["mountains"]) | ||
|  |     tilesets["egg"].palette_id = 0x13 | ||
|  |     tilesets["egg"].attr_bank = 0x27 | ||
|  |     tilesets["egg"].attr_addr = 0x5620 | ||
|  | 
 | ||
|  |     tilesets["forest"] = rom_tilesets[0x20].copy() | ||
|  |     tilesets["forest"].palette_id = 0x00 | ||
|  |     tilesets["forest"].attr_bank = 0x25 | ||
|  |     tilesets["forest"].attr_addr = 0x4000 | ||
|  | 
 | ||
|  |     tilesets["town"] = rom_tilesets[0x26].copy() | ||
|  |     tilesets["town"].combine(rom_tilesets[0x103]) | ||
|  |     tilesets["town"].palette_id = 0x03 | ||
|  |     tilesets["town"].attr_bank = 0x25 | ||
|  |     tilesets["town"].attr_addr = 0x4C00 | ||
|  | 
 | ||
|  |     tilesets["swamp"] = rom_tilesets[0x36].copy() | ||
|  |     tilesets["swamp"].combine(rom_tilesets[0x103]) | ||
|  |     tilesets["swamp"].palette_id = 0x0E | ||
|  |     tilesets["swamp"].attr_bank = 0x22 | ||
|  |     tilesets["swamp"].attr_addr = 0x7400 | ||
|  | 
 | ||
|  |     tilesets["beach"] = rom_tilesets[0x22].copy() | ||
|  |     tilesets["beach"].combine(rom_tilesets[0x102]) | ||
|  |     tilesets["beach"].palette_id = 0x01 | ||
|  |     tilesets["beach"].attr_bank = 0x22 | ||
|  |     tilesets["beach"].attr_addr = 0x5000 | ||
|  | 
 | ||
|  |     tilesets["water"] = rom_tilesets[0x3E].copy() | ||
|  |     tilesets["water"].combine(rom_tilesets[0x103]) | ||
|  |     tilesets["water"].learn_from(tilesets["basic"]) | ||
|  |     tilesets["water"].remove(0x7A) | ||
|  |     tilesets["water"].remove(0xC8) | ||
|  |     tilesets["water"].palette_id = 0x09 | ||
|  |     tilesets["water"].attr_bank = 0x22 | ||
|  |     tilesets["water"].attr_addr = 0x6400 | ||
|  | 
 | ||
|  |     return tilesets |