232 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			232 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | import random | ||
|  | from .tileset import solid_tiles, open_tiles | ||
|  | from ..locations.items import * | ||
|  | 
 | ||
|  | 
 | ||
|  | PRIMARY_ITEMS = [POWER_BRACELET, SHIELD, BOW, HOOKSHOT, MAGIC_ROD, PEGASUS_BOOTS, OCARINA, FEATHER, SHOVEL, MAGIC_POWDER, BOMB, SWORD, FLIPPERS, SONG1] | ||
|  | SECONDARY_ITEMS = [BOOMERANG, RED_TUNIC, BLUE_TUNIC, MAX_POWDER_UPGRADE, MAX_BOMBS_UPGRADE, MAX_ARROWS_UPGRADE, GEL] | ||
|  | 
 | ||
|  | HORIZONTAL = 0 | ||
|  | VERTICAL = 1 | ||
|  | 
 | ||
|  | 
 | ||
|  | class RoomEdge: | ||
|  |     def __init__(self, direction): | ||
|  |         self.__solid = False | ||
|  |         self.__open_range = None | ||
|  |         self.direction = direction | ||
|  |         self.__open_min = 2 if direction == HORIZONTAL else 1 | ||
|  |         self.__open_max = 8 if direction == HORIZONTAL else 7 | ||
|  | 
 | ||
|  |     def force_solid(self): | ||
|  |         self.__open_min = -1 | ||
|  |         self.__open_max = -1 | ||
|  |         self.__open_range = None | ||
|  |         self.__solid = True | ||
|  | 
 | ||
|  |     def set_open_min(self, value): | ||
|  |         if self.__open_min < 0: | ||
|  |             return | ||
|  |         self.__open_min = max(self.__open_min, value) | ||
|  | 
 | ||
|  |     def set_open_max(self, value): | ||
|  |         if self.__open_max < 0: | ||
|  |             return | ||
|  |         self.__open_max = min(self.__open_max, value) | ||
|  | 
 | ||
|  |     def set_solid(self): | ||
|  |         self.__open_range = None | ||
|  |         self.__solid = True | ||
|  | 
 | ||
|  |     def can_open(self): | ||
|  |         return self.__open_min > -1 | ||
|  | 
 | ||
|  |     def set_open(self): | ||
|  |         cnt = random.randint(1, self.__open_max - self.__open_min) | ||
|  |         if random.randint(1, 100) < 50: | ||
|  |             cnt = 1 | ||
|  |         offset = random.randint(self.__open_min, self.__open_max - cnt) | ||
|  |         self.__open_range = (offset, offset + cnt) | ||
|  |         self.__solid = False | ||
|  | 
 | ||
|  |     def is_solid(self): | ||
|  |         return self.__solid | ||
|  | 
 | ||
|  |     def get_open_range(self): | ||
|  |         return self.__open_range | ||
|  | 
 | ||
|  |     def seed(self, wfc, x, y): | ||
|  |         for offset, cell in self.__cells(wfc, x, y): | ||
|  |             if self.__open_range and self.__open_range[0] <= offset < self.__open_range[1]: | ||
|  |                 cell.init_options.intersection_update(open_tiles) | ||
|  |             elif self.__solid: | ||
|  |                 cell.init_options.intersection_update(solid_tiles) | ||
|  | 
 | ||
|  |     def __cells(self, wfc, x, y): | ||
|  |         if self.direction == HORIZONTAL: | ||
|  |             for n in range(1, 9): | ||
|  |                 yield n, wfc.cell_data[(x + n, y)] | ||
|  |         else: | ||
|  |             for n in range(1, 7): | ||
|  |                 yield n, wfc.cell_data[(x, y + n)] | ||
|  | 
 | ||
|  | 
 | ||
|  | class RoomInfo: | ||
|  |     def __init__(self, x, y): | ||
|  |         self.x = x | ||
|  |         self.y = y | ||
|  |         self.tileset_id = "basic" | ||
|  |         self.room_type = None | ||
|  |         self.tiles = None | ||
|  |         self.edge_left = None | ||
|  |         self.edge_up = None | ||
|  |         self.edge_right = RoomEdge(VERTICAL) | ||
|  |         self.edge_down = RoomEdge(HORIZONTAL) | ||
|  |         self.room_left = None | ||
|  |         self.room_up = None | ||
|  |         self.room_right = None | ||
|  |         self.room_down = None | ||
|  |         self.locations = [] | ||
|  |         self.entities = [] | ||
|  | 
 | ||
|  |     def __repr__(self): | ||
|  |         return f"Room<{self.x} {self.y}>" | ||
|  | 
 | ||
|  | 
 | ||
|  | class Map: | ||
|  |     def __init__(self, w, h, tilesets): | ||
|  |         self.w = w | ||
|  |         self.h = h | ||
|  |         self.tilesets = tilesets | ||
|  |         self.__rooms = [RoomInfo(x, y) for y in range(h) for x in range(w)] | ||
|  |         for x in range(w): | ||
|  |             for y in range(h): | ||
|  |                 room = self.get(x, y) | ||
|  |                 if x == 0: | ||
|  |                     room.edge_left = RoomEdge(VERTICAL) | ||
|  |                 else: | ||
|  |                     room.edge_left = self.get(x - 1, y).edge_right | ||
|  |                 if y == 0: | ||
|  |                     room.edge_up = RoomEdge(HORIZONTAL) | ||
|  |                 else: | ||
|  |                     room.edge_up = self.get(x, y - 1).edge_down | ||
|  |                 if x > 0: | ||
|  |                     room.room_left = self.get(x - 1, y) | ||
|  |                 if x < w - 1: | ||
|  |                     room.room_right = self.get(x + 1, y) | ||
|  |                 if y > 0: | ||
|  |                     room.room_up = self.get(x, y - 1) | ||
|  |                 if y < h - 1: | ||
|  |                     room.room_down = self.get(x, y + 1) | ||
|  |         for x in range(w): | ||
|  |             self.get(x, 0).edge_up.set_solid() | ||
|  |             self.get(x, h-1).edge_down.set_solid() | ||
|  |         for y in range(h): | ||
|  |             self.get(0, y).edge_left.set_solid() | ||
|  |             self.get(w-1, y).edge_right.set_solid() | ||
|  | 
 | ||
|  |     def __iter__(self): | ||
|  |         return iter(self.__rooms) | ||
|  | 
 | ||
|  |     def get(self, x, y) -> RoomInfo: | ||
|  |         assert 0 <= x < self.w and 0 <= y < self.h, f"{x} {y}" | ||
|  |         return self.__rooms[x + y * self.w] | ||
|  | 
 | ||
|  |     def get_tile(self, x, y): | ||
|  |         return self.get(x // 10, y // 8).tiles[(x % 10) + (y % 8) * 10] | ||
|  | 
 | ||
|  |     def get_item_pool(self): | ||
|  |         item_pool = {} | ||
|  |         for room in self.__rooms: | ||
|  |             for location in room.locations: | ||
|  |                 print(room, location.get_item_pool(), location.__class__.__name__) | ||
|  |                 for k, v in location.get_item_pool().items(): | ||
|  |                     item_pool[k] = item_pool.get(k, 0) + v | ||
|  |         unmapped_count = item_pool.get(None, 0) | ||
|  |         del item_pool[None] | ||
|  |         for item in PRIMARY_ITEMS: | ||
|  |             if item not in item_pool: | ||
|  |                 item_pool[item] = 1 | ||
|  |                 unmapped_count -= 1 | ||
|  |         while item_pool[POWER_BRACELET] < 2: | ||
|  |             item_pool[POWER_BRACELET] = item_pool.get(POWER_BRACELET, 0) + 1 | ||
|  |             unmapped_count -= 1 | ||
|  |         while item_pool[SHIELD] < 2: | ||
|  |             item_pool[SHIELD] = item_pool.get(SHIELD, 0) + 1 | ||
|  |             unmapped_count -= 1 | ||
|  |         assert unmapped_count >= 0 | ||
|  | 
 | ||
|  |         for item in SECONDARY_ITEMS: | ||
|  |             if unmapped_count > 0: | ||
|  |                 item_pool[item] = item_pool.get(item, 0) + 1 | ||
|  |                 unmapped_count -= 1 | ||
|  | 
 | ||
|  |         # Add a heart container per 10 items "spots" left. | ||
|  |         heart_piece_count = unmapped_count // 10 | ||
|  |         unmapped_count -= heart_piece_count * 4 | ||
|  |         item_pool[HEART_PIECE] = item_pool.get(HEART_PIECE, 0) + heart_piece_count * 4 | ||
|  | 
 | ||
|  |         # Add the rest as rupees | ||
|  |         item_pool[RUPEES_50] = item_pool.get(RUPEES_50, 0) + unmapped_count | ||
|  |         return item_pool | ||
|  | 
 | ||
|  |     def dump(self): | ||
|  |         for y in range(self.h): | ||
|  |             for x in range(self.w): | ||
|  |                 if self.get(x, y).edge_right.is_solid(): | ||
|  |                     print(" |", end="") | ||
|  |                 elif self.get(x, y).edge_right.get_open_range(): | ||
|  |                     print("  ", end="") | ||
|  |                 else: | ||
|  |                     print(" ?", end="") | ||
|  |             print() | ||
|  |             for x in range(self.w): | ||
|  |                 if self.get(x, y).edge_down.is_solid(): | ||
|  |                     print("-+", end="") | ||
|  |                 elif self.get(x, y).edge_down.get_open_range(): | ||
|  |                     print(" +", end="") | ||
|  |                 else: | ||
|  |                     print("?+", end="") | ||
|  |             print() | ||
|  |         print() | ||
|  | 
 | ||
|  | 
 | ||
|  | class MazeGen: | ||
|  |     UP = 0x01 | ||
|  |     DOWN = 0x02 | ||
|  |     LEFT = 0x04 | ||
|  |     RIGHT = 0x08 | ||
|  | 
 | ||
|  |     def __init__(self, the_map: Map): | ||
|  |         self.map = the_map | ||
|  |         self.visited = set() | ||
|  |         self.visit(0, 0) | ||
|  | 
 | ||
|  |     def visit(self, x, y): | ||
|  |         self.visited.add((x, y)) | ||
|  |         neighbours = self.get_neighbours(x, y) | ||
|  |         while any((x, y) not in self.visited for x, y, d in neighbours): | ||
|  |             x, y, d = random.choice(neighbours) | ||
|  |             if (x, y) not in self.visited: | ||
|  |                 if d == self.RIGHT and self.map.get(x, y).edge_left.can_open(): | ||
|  |                     self.map.get(x, y).edge_left.set_open() | ||
|  |                 elif d == self.LEFT and self.map.get(x, y).edge_right.can_open(): | ||
|  |                     self.map.get(x, y).edge_right.set_open() | ||
|  |                 elif d == self.DOWN and self.map.get(x, y).edge_up.can_open(): | ||
|  |                     self.map.get(x, y).edge_up.set_open() | ||
|  |                 elif d == self.UP and self.map.get(x, y).edge_down.can_open(): | ||
|  |                     self.map.get(x, y).edge_down.set_open() | ||
|  |                 self.visit(x, y) | ||
|  | 
 | ||
|  |     def get_neighbours(self, x, y): | ||
|  |         neighbours = [] | ||
|  |         if x > 0: | ||
|  |             neighbours.append((x - 1, y, self.LEFT)) | ||
|  |         if x < self.map.w - 1: | ||
|  |             neighbours.append((x + 1, y, self.RIGHT)) | ||
|  |         if y > 0: | ||
|  |             neighbours.append((x, y - 1, self.UP)) | ||
|  |         if y < self.map.h - 1: | ||
|  |             neighbours.append((x, y + 1, self.DOWN)) | ||
|  |         return neighbours |