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
							 |