147 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			147 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | from .map import Map | ||
|  | from .locations.entrance import Entrance | ||
|  | from ..logic import * | ||
|  | from .tileset import walkable_tiles, entrance_tiles | ||
|  | 
 | ||
|  | 
 | ||
|  | class LogicGenerator: | ||
|  |     def __init__(self, configuration_options, world_setup, requirements_settings, the_map: Map): | ||
|  |         self.w = the_map.w * 10 | ||
|  |         self.h = the_map.h * 8 | ||
|  |         self.map = the_map | ||
|  |         self.logic_map = [None] * (self.w * self.h) | ||
|  |         self.location_lookup = {} | ||
|  |         self.configuration_options = configuration_options | ||
|  |         self.world_setup = world_setup | ||
|  |         self.requirements_settings = requirements_settings | ||
|  | 
 | ||
|  |         self.entrance_map = {} | ||
|  |         for room in the_map: | ||
|  |             for location in room.locations: | ||
|  |                 self.location_lookup[(room.x * 10 + location.x, room.y * 8 + location.y)] = location | ||
|  |                 if isinstance(location, Entrance): | ||
|  |                     location.prepare_logic(configuration_options, world_setup, requirements_settings) | ||
|  |                     self.entrance_map[location.entrance_name] = location | ||
|  | 
 | ||
|  |         start = self.entrance_map["start_house"] | ||
|  |         self.start = Location() | ||
|  |         self.egg = self.start  # TODO | ||
|  |         self.nightmare = Location() | ||
|  |         self.windfish = Location().connect(self.nightmare, AND(MAGIC_POWDER, SWORD, OR(BOOMERANG, BOW))) | ||
|  |         self.fill_walkable(self.start, start.room.x * 10 + start.x, start.room.y * 8 + start.y) | ||
|  | 
 | ||
|  |         logic_str_map = {None: "."} | ||
|  |         for y in range(self.h): | ||
|  |             line = "" | ||
|  |             for x in range(self.w): | ||
|  |                 if self.logic_map[x + y * self.w] not in logic_str_map: | ||
|  |                     logic_str_map[self.logic_map[x + y * self.w]] = chr(len(logic_str_map)+48) | ||
|  |                 line += logic_str_map[self.logic_map[x + y * self.w]] | ||
|  |             print(line) | ||
|  | 
 | ||
|  |         for room in the_map: | ||
|  |             for location in room.locations: | ||
|  |                 if self.logic_map[(room.x * 10 + location.x) + (room.y * 8 + location.y) * self.w] is None: | ||
|  |                     raise RuntimeError(f"Location not mapped to logic: {room} {location.__class__.__name__} {location.x} {location.y}") | ||
|  | 
 | ||
|  |         tmp = set() | ||
|  |         def r(n): | ||
|  |             if n in tmp: | ||
|  |                 return | ||
|  |             tmp.add(n) | ||
|  |             for item in n.items: | ||
|  |                 print(item) | ||
|  |             for o, req in n.simple_connections: | ||
|  |                 r(o) | ||
|  |             for o, req in n.gated_connections: | ||
|  |                 r(o) | ||
|  |         r(self.start) | ||
|  | 
 | ||
|  |     def fill_walkable(self, location, x, y): | ||
|  |         tile_options = walkable_tiles | entrance_tiles | ||
|  |         for x, y in self.flood_fill_logic(location, tile_options, x, y): | ||
|  |             if self.logic_map[x + y * self.w] is not None: | ||
|  |                 continue | ||
|  |             tile = self.map.get_tile(x, y) | ||
|  |             if tile == 0x5C:  # bush | ||
|  |                 other_location = Location() | ||
|  |                 location.connect(other_location, self.requirements_settings.bush) | ||
|  |                 self.fill_bush(other_location, x, y) | ||
|  |             elif tile == 0x20:  # rock | ||
|  |                 other_location = Location() | ||
|  |                 location.connect(other_location, POWER_BRACELET) | ||
|  |                 self.fill_rock(other_location, x, y) | ||
|  |             elif tile == 0xE8:  # pit | ||
|  |                 if self.map.get_tile(x - 1, y) in tile_options and self.map.get_tile(x + 1, y) in tile_options: | ||
|  |                     if self.logic_map[x - 1 + y * self.w] == location and self.logic_map[x + 1 + y * self.w] is None: | ||
|  |                         other_location = Location().connect(location, FEATHER) | ||
|  |                         self.fill_walkable(other_location, x + 1, y) | ||
|  |                     if self.logic_map[x - 1 + y * self.w] is None and self.logic_map[x + 1 + y * self.w] == location: | ||
|  |                         other_location = Location().connect(location, FEATHER) | ||
|  |                         self.fill_walkable(other_location, x - 1, y) | ||
|  |                 if self.map.get_tile(x, y - 1) in tile_options and self.map.get_tile(x, y + 1) in tile_options: | ||
|  |                     if self.logic_map[x + (y - 1) * self.w] == location and self.logic_map[x + (y + 1) * self.w] is None: | ||
|  |                         other_location = Location().connect(location, FEATHER) | ||
|  |                         self.fill_walkable(other_location, x, y + 1) | ||
|  |                     if self.logic_map[x + (y - 1) * self.w] is None and self.logic_map[x + (y + 1) * self.w] == location: | ||
|  |                         other_location = Location().connect(location, FEATHER) | ||
|  |                         self.fill_walkable(other_location, x, y - 1) | ||
|  | 
 | ||
|  |     def fill_bush(self, location, x, y): | ||
|  |         for x, y in self.flood_fill_logic(location, {0x5C}, x, y): | ||
|  |             if self.logic_map[x + y * self.w] is not None: | ||
|  |                 continue | ||
|  |             tile = self.map.get_tile(x, y) | ||
|  |             if tile in walkable_tiles or tile in entrance_tiles: | ||
|  |                 other_location = Location() | ||
|  |                 location.connect(other_location, self.requirements_settings.bush) | ||
|  |                 self.fill_walkable(other_location, x, y) | ||
|  | 
 | ||
|  |     def fill_rock(self, location, x, y): | ||
|  |         for x, y in self.flood_fill_logic(location, {0x20}, x, y): | ||
|  |             if self.logic_map[x + y * self.w] is not None: | ||
|  |                 continue | ||
|  |             tile = self.map.get_tile(x, y) | ||
|  |             if tile in walkable_tiles or tile in entrance_tiles: | ||
|  |                 other_location = Location() | ||
|  |                 location.connect(other_location, POWER_BRACELET) | ||
|  |                 self.fill_walkable(other_location, x, y) | ||
|  | 
 | ||
|  |     def flood_fill_logic(self, location, tile_types, x, y): | ||
|  |         assert self.map.get_tile(x, y) in tile_types | ||
|  |         todo = [(x, y)] | ||
|  |         entrance_todo = [] | ||
|  | 
 | ||
|  |         edge_set = set() | ||
|  |         while todo: | ||
|  |             x, y = todo.pop() | ||
|  |             if self.map.get_tile(x, y) not in tile_types: | ||
|  |                 edge_set.add((x, y)) | ||
|  |                 continue | ||
|  |             if self.logic_map[x + y * self.w] is not None: | ||
|  |                 continue | ||
|  |             self.logic_map[x + y * self.w] = location | ||
|  |             if (x, y) in self.location_lookup: | ||
|  |                 room_location = self.location_lookup[(x, y)] | ||
|  |                 result = room_location.connect_logic(location) | ||
|  |                 if result: | ||
|  |                     entrance_todo += result | ||
|  | 
 | ||
|  |             if x < self.w - 1 and self.logic_map[x + 1 + y * self.w] is None: | ||
|  |                 todo.append((x + 1, y)) | ||
|  |             if x > 0 and self.logic_map[x - 1 + y * self.w] is None: | ||
|  |                 todo.append((x - 1, y)) | ||
|  |             if y < self.h - 1 and self.logic_map[x + y * self.w + self.w] is None: | ||
|  |                 todo.append((x, y + 1)) | ||
|  |             if y > 0 and self.logic_map[x + y * self.w - self.w] is None: | ||
|  |                 if self.map.get_tile(x, y - 1) == 0xA0:  # Chest, can only be collected from the south | ||
|  |                     self.location_lookup[(x, y - 1)].connect_logic(location) | ||
|  |                     self.logic_map[x + (y - 1) * self.w] = location | ||
|  |                 todo.append((x, y - 1)) | ||
|  | 
 | ||
|  |         for entrance_name, logic_connection in entrance_todo: | ||
|  |             entrance = self.entrance_map[entrance_name] | ||
|  |             entrance.connect_logic(logic_connection) | ||
|  |             self.fill_walkable(logic_connection, entrance.room.x * 10 + entrance.x, entrance.room.y * 8 + entrance.y) | ||
|  |         return edge_set |