Refactor how large screen quadrants are stored in grids
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import copy
|
||||
import logging
|
||||
import RaceRandom as random
|
||||
import random as _random
|
||||
@@ -8,7 +7,7 @@ from OverworldShuffle import connect_two_way, validate_layout
|
||||
|
||||
ENABLE_KEEP_SIMILAR_SPECIAL_HANDLING = False
|
||||
PREVENT_WRAPPED_LARGE_SCREENS = False
|
||||
DRAW_IMAGE = False
|
||||
DRAW_IMAGE = True
|
||||
|
||||
large_screen_ids = [0x00, 0x03, 0x05, 0x18, 0x1B, 0x1E, 0x30, 0x35] + [0x40, 0x43, 0x45, 0x58, 0x5B, 0x5E, 0x70, 0x75]
|
||||
|
||||
@@ -49,7 +48,7 @@ class WorldPiece:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
screens: List[List[Optional[Screen]]],
|
||||
screens: Optional[List[List[Optional[Screen]]]] = None,
|
||||
grid: Optional[List[List[int]]] = None,
|
||||
width: int = 0,
|
||||
height: int = 0,
|
||||
@@ -62,7 +61,7 @@ class WorldPiece:
|
||||
west_edges_water: Optional[List[List[List[OWEdge]]]] = None,
|
||||
east_edges_water: Optional[List[List[List[OWEdge]]]] = None
|
||||
):
|
||||
self.screens = screens
|
||||
self.screens = screens if screens is not None else []
|
||||
self.grid = grid if grid is not None else []
|
||||
self.width = width
|
||||
self.height = height
|
||||
@@ -154,7 +153,7 @@ class LayoutGeneratorOptions:
|
||||
"""
|
||||
Configuration options for layout generation.
|
||||
"""
|
||||
__slots__ = ('horizontal_wrap', 'vertical_wrap',
|
||||
__slots__ = ('horizontal_wrap', 'vertical_wrap', 'split_large_screens',
|
||||
'large_screen_pool', 'distortion_chance', 'random_order',
|
||||
'multi_choice', 'max_delay', 'first_ignore_bonus_points',
|
||||
'penalty_full_edge_mismatch', 'penalty_partial_edge_mismatch', 'bonus_partial_edge_match',
|
||||
@@ -168,6 +167,7 @@ class LayoutGeneratorOptions:
|
||||
self,
|
||||
horizontal_wrap: bool = True,
|
||||
vertical_wrap: bool = True,
|
||||
split_large_screens = False,
|
||||
large_screen_pool: bool = False,
|
||||
distortion_chance: float = 0.0,
|
||||
random_order: int = 0,
|
||||
@@ -194,6 +194,7 @@ class LayoutGeneratorOptions:
|
||||
):
|
||||
self.horizontal_wrap = horizontal_wrap
|
||||
self.vertical_wrap = vertical_wrap
|
||||
self.split_large_screens = split_large_screens
|
||||
self.large_screen_pool = large_screen_pool
|
||||
self.distortion_chance = distortion_chance
|
||||
self.random_order = random_order
|
||||
@@ -418,8 +419,8 @@ def create_piece_list(world: World, player: int, options: LayoutGeneratorOptions
|
||||
|
||||
if castle_screen and central_bonk_screen and links_house_screen:
|
||||
piece = create_piece(world, player, [
|
||||
[0x1B, 0x1B],
|
||||
[0x1B, 0x1B],
|
||||
[0x1B, 0x1C],
|
||||
[0x23, 0x24],
|
||||
[0x2B, 0x2C]
|
||||
], overworld_screens)
|
||||
|
||||
@@ -439,10 +440,18 @@ def create_piece_list(world: World, player: int, options: LayoutGeneratorOptions
|
||||
# Add large screens
|
||||
for screen in all_large_screens:
|
||||
if screen not in used_screens_set:
|
||||
piece = create_piece(world, player, [[screen.id, screen.id], [screen.id, screen.id]], overworld_screens)
|
||||
if options.large_screen_pool:
|
||||
piece.restriction = [0x00, 0x03, 0x05, 0x18, 0x1B, 0x1E, 0x30, 0x35]
|
||||
piece_list.append(piece)
|
||||
base_id = screen.id
|
||||
if options.split_large_screens:
|
||||
for quadrant_offset in [0x00, 0x01, 0x08, 0x09]:
|
||||
piece = create_piece(world, player, [[base_id + quadrant_offset]], overworld_screens)
|
||||
if options.large_screen_pool:
|
||||
piece.restriction = [large_screen_id + quadrant_offset for large_screen_id in [0x00, 0x03, 0x05, 0x18, 0x1B, 0x1E, 0x30, 0x35]]
|
||||
piece_list.append(piece)
|
||||
else:
|
||||
piece = create_piece(world, player, [[base_id, base_id + 0x01], [base_id + 0x08, base_id + 0x09]], overworld_screens)
|
||||
if options.large_screen_pool:
|
||||
piece.restriction = [0x00, 0x03, 0x05, 0x18, 0x1B, 0x1E, 0x30, 0x35]
|
||||
piece_list.append(piece)
|
||||
used_screens_set.add(screen)
|
||||
if world.owParallel[player]:
|
||||
used_screens_set.add(screen.parallel)
|
||||
@@ -478,43 +487,50 @@ def create_piece_list(world: World, player: int, options: LayoutGeneratorOptions
|
||||
for k in range(piece.height):
|
||||
for l in range(piece.width):
|
||||
piece.crossed_groups[k].append(-1)
|
||||
screen_id = piece.main.grid[k][l]
|
||||
if screen_id != -1:
|
||||
piece.crossed_groups[k][l] = 1 if screen_id in crossed_group_b else 0
|
||||
screen = piece.main.screens[k][l]
|
||||
if screen:
|
||||
piece.crossed_groups[k][l] = 1 if screen.id in crossed_group_b else 0
|
||||
else:
|
||||
if piece.parallel and piece.parallel.screens[k][l]:
|
||||
piece.crossed_groups[k][l] = 1 if piece.parallel.grid[k][l] in crossed_group_b else 0
|
||||
piece.crossed_groups[k][l] = 1 if piece.parallel.screens[k][l].id in crossed_group_b else 0
|
||||
|
||||
return piece_list
|
||||
|
||||
def create_piece(world: World, player: int, grid: List[List[int]], overworld_screens: Dict[int, Screen]) -> Piece:
|
||||
"""
|
||||
Create piece from grid of screen IDs
|
||||
Takes 2D array of screen IDs and creates main and parallel pieces
|
||||
Create piece from grid of cell IDs
|
||||
Takes 2D array of cell IDs and creates main and parallel pieces
|
||||
"""
|
||||
piece = Piece(
|
||||
main=WorldPiece(screens=[]),
|
||||
main=WorldPiece(),
|
||||
width=len(grid[0]),
|
||||
height=len(grid)
|
||||
)
|
||||
|
||||
if world.owParallel[player]:
|
||||
piece.parallel = WorldPiece(screens=[])
|
||||
piece.parallel = WorldPiece()
|
||||
|
||||
found_screens = set()
|
||||
|
||||
for i in range(piece.height):
|
||||
new_row = []
|
||||
new_screen_row = []
|
||||
new_row_parallel = []
|
||||
piece.main.screens.append(new_row)
|
||||
new_screen_row_parallel = []
|
||||
piece.main.grid.append(new_row)
|
||||
piece.main.screens.append(new_screen_row)
|
||||
if world.owParallel[player]:
|
||||
piece.parallel.screens.append(new_row_parallel)
|
||||
piece.parallel.grid.append(new_row_parallel)
|
||||
piece.parallel.screens.append(new_screen_row_parallel)
|
||||
|
||||
for j in range(piece.width):
|
||||
screen = overworld_screens.get(grid[i][j])
|
||||
new_row.append(screen)
|
||||
if world.owParallel[player]:
|
||||
new_row_parallel.append(screen.parallel if screen else None)
|
||||
cell_id = grid[i][j]
|
||||
new_row.append(cell_id)
|
||||
screen = overworld_screens.get(get_screen_id_from_cell(cell_id))
|
||||
new_screen_row.append(screen)
|
||||
if world.owParallel[player] and screen:
|
||||
new_row_parallel.append(cell_id - screen.id + screen.parallel.id)
|
||||
new_screen_row_parallel.append(screen.parallel)
|
||||
|
||||
if screen and screen not in found_screens:
|
||||
found_screens.add(screen)
|
||||
@@ -532,14 +548,14 @@ def create_piece(world: World, player: int, grid: List[List[int]], overworld_scr
|
||||
def add_piece_data(world: World, player: int, piece: Piece, large_screen_quadrant_info: Dict[int, Dict], large_screen_quadrant_info_land: Dict[int, Dict], large_screen_quadrant_info_water: Dict[int, Dict]) -> None:
|
||||
"""
|
||||
Add computed data to piece
|
||||
Calls add_piece_grid_info for main and parallel pieces
|
||||
Calls add_world_piece_edge_info for main and parallel pieces
|
||||
"""
|
||||
num_pieces = 2 if piece.parallel else 1
|
||||
for p in range(num_pieces):
|
||||
world_piece = piece.main if p == 0 else piece.parallel
|
||||
world_piece.width = len(world_piece.screens[0])
|
||||
world_piece.height = len(world_piece.screens)
|
||||
add_piece_grid_info(world, player, world_piece, large_screen_quadrant_info, large_screen_quadrant_info_land, large_screen_quadrant_info_water)
|
||||
world_piece.width = piece.width
|
||||
world_piece.height = piece.height
|
||||
add_world_piece_edge_info(world, player, world_piece, large_screen_quadrant_info, large_screen_quadrant_info_land, large_screen_quadrant_info_water)
|
||||
|
||||
piece.width = piece.main.width
|
||||
piece.height = piece.main.height
|
||||
@@ -572,12 +588,11 @@ def add_piece_data(world: World, player: int, piece: Piece, large_screen_quadran
|
||||
piece.edge_sides = 0
|
||||
piece.max_edges_per_side = 0
|
||||
|
||||
def add_piece_grid_info(world: World, player: int, piece: WorldPiece, large_screen_quadrant_info: Dict[int, Dict], large_screen_quadrant_info_land: Dict[int, Dict], large_screen_quadrant_info_water: Dict[int, Dict]) -> None:
|
||||
def add_world_piece_edge_info(world: World, player: int, piece: WorldPiece, large_screen_quadrant_info: Dict[int, Dict], large_screen_quadrant_info_land: Dict[int, Dict], large_screen_quadrant_info_water: Dict[int, Dict]) -> None:
|
||||
"""
|
||||
Populate piece edge information
|
||||
Initializes 8x8 edge arrays and extracts edges from screens
|
||||
"""
|
||||
piece.grid = [[] for _ in range(8)]
|
||||
piece.north_edges = [[] for _ in range(8)]
|
||||
piece.south_edges = [[] for _ in range(8)]
|
||||
piece.west_edges = [[] for _ in range(8)]
|
||||
@@ -591,7 +606,6 @@ def add_piece_grid_info(world: World, player: int, piece: WorldPiece, large_scre
|
||||
|
||||
for k in range(piece.height):
|
||||
for l in range(piece.width):
|
||||
piece.grid[k].append(piece.screens[k][l].id if piece.screens[k][l] else -1)
|
||||
piece.north_edges[k].append([])
|
||||
piece.south_edges[k].append([])
|
||||
piece.west_edges[k].append([])
|
||||
@@ -603,38 +617,41 @@ def add_piece_grid_info(world: World, player: int, piece: WorldPiece, large_scre
|
||||
piece.west_edges_water[k].append([])
|
||||
piece.east_edges_water[k].append([])
|
||||
|
||||
done_large = set()
|
||||
for k in range(piece.height):
|
||||
for l in range(piece.width):
|
||||
screen = piece.screens[k][l]
|
||||
if not screen:
|
||||
continue
|
||||
|
||||
cell_id = piece.grid[k][l]
|
||||
|
||||
if screen.big:
|
||||
if screen.id not in done_large:
|
||||
done_large.add(screen.id)
|
||||
quadrant_info = (large_screen_quadrant_info[screen.id] if world.owTerrain[player]
|
||||
else large_screen_quadrant_info_land[screen.id])
|
||||
# Determine quadrant by subtracting cell ID from screen ID
|
||||
# 0x00 = NW (top-left), 0x01 = NE (top-right), 0x08 = SW (bottom-left), 0x09 = SE (bottom-right)
|
||||
quadrant_offset = cell_id - screen.id
|
||||
if quadrant_offset == 0x00:
|
||||
quadrant_name = "NW"
|
||||
elif quadrant_offset == 0x01:
|
||||
quadrant_name = "NE"
|
||||
elif quadrant_offset == 0x08:
|
||||
quadrant_name = "SW"
|
||||
else:
|
||||
quadrant_name = "SE"
|
||||
|
||||
piece.north_edges[k][l] = [e for e in quadrant_info["NW"][Direction.North] if not e.dest]
|
||||
piece.north_edges[k][l + 1] = [e for e in quadrant_info["NE"][Direction.North] if not e.dest]
|
||||
piece.south_edges[k + 1][l] = [e for e in quadrant_info["SW"][Direction.South] if not e.dest]
|
||||
piece.south_edges[k + 1][l + 1] = [e for e in quadrant_info["SE"][Direction.South] if not e.dest]
|
||||
piece.west_edges[k][l] = [e for e in quadrant_info["NW"][Direction.West] if not e.dest]
|
||||
piece.west_edges[k + 1][l] = [e for e in quadrant_info["SW"][Direction.West] if not e.dest]
|
||||
piece.east_edges[k][l + 1] = [e for e in quadrant_info["NE"][Direction.East] if not e.dest]
|
||||
piece.east_edges[k + 1][l + 1] = [e for e in quadrant_info["SE"][Direction.East] if not e.dest]
|
||||
quadrant_info = (large_screen_quadrant_info[screen.id] if world.owTerrain[player]
|
||||
else large_screen_quadrant_info_land[screen.id])
|
||||
|
||||
if not world.owTerrain[player]:
|
||||
quadrant_info = large_screen_quadrant_info_water[screen.id]
|
||||
piece.north_edges_water[k][l] = [e for e in quadrant_info["NW"][Direction.North] if not e.dest]
|
||||
piece.north_edges_water[k][l + 1] = [e for e in quadrant_info["NE"][Direction.North] if not e.dest]
|
||||
piece.south_edges_water[k + 1][l] = [e for e in quadrant_info["SW"][Direction.South] if not e.dest]
|
||||
piece.south_edges_water[k + 1][l + 1] = [e for e in quadrant_info["SE"][Direction.South] if not e.dest]
|
||||
piece.west_edges_water[k][l] = [e for e in quadrant_info["NW"][Direction.West] if not e.dest]
|
||||
piece.west_edges_water[k + 1][l] = [e for e in quadrant_info["SW"][Direction.West] if not e.dest]
|
||||
piece.east_edges_water[k][l + 1] = [e for e in quadrant_info["NE"][Direction.East] if not e.dest]
|
||||
piece.east_edges_water[k + 1][l + 1] = [e for e in quadrant_info["SE"][Direction.East] if not e.dest]
|
||||
piece.north_edges[k][l] = [e for e in quadrant_info[quadrant_name][Direction.North] if not e.dest]
|
||||
piece.south_edges[k][l] = [e for e in quadrant_info[quadrant_name][Direction.South] if not e.dest]
|
||||
piece.west_edges[k][l] = [e for e in quadrant_info[quadrant_name][Direction.West] if not e.dest]
|
||||
piece.east_edges[k][l] = [e for e in quadrant_info[quadrant_name][Direction.East] if not e.dest]
|
||||
|
||||
if not world.owTerrain[player]:
|
||||
quadrant_info_water = large_screen_quadrant_info_water[screen.id]
|
||||
piece.north_edges_water[k][l] = [e for e in quadrant_info_water[quadrant_name][Direction.North] if not e.dest]
|
||||
piece.south_edges_water[k][l] = [e for e in quadrant_info_water[quadrant_name][Direction.South] if not e.dest]
|
||||
piece.west_edges_water[k][l] = [e for e in quadrant_info_water[quadrant_name][Direction.West] if not e.dest]
|
||||
piece.east_edges_water[k][l] = [e for e in quadrant_info_water[quadrant_name][Direction.East] if not e.dest]
|
||||
else:
|
||||
for edge in sorted(screen.edges.values(), key=lambda e: e.midpoint):
|
||||
if not edge.dest:
|
||||
@@ -651,6 +668,19 @@ def add_piece_grid_info(world: World, player: int, piece: WorldPiece, large_scre
|
||||
target = piece.east_edges[k][l] if world.owTerrain[player] or edge.terrain != Terrain.Water else piece.east_edges_water[k][l]
|
||||
target.append(edge)
|
||||
|
||||
def get_screen_id_from_cell(cell_id: int) -> int:
|
||||
"""Get the base screen ID from a cell ID.
|
||||
|
||||
For large screens, returns the top-left corner ID.
|
||||
For small screens, returns the cell ID unchanged.
|
||||
"""
|
||||
base_id = cell_id & 0xBF # Remove world bit if present
|
||||
# Check if this is a quadrant of a large screen
|
||||
for large_id in large_screen_ids:
|
||||
if base_id in [large_id, large_id + 0x01, large_id + 0x08, large_id + 0x09]:
|
||||
return large_id | (cell_id & 0x40) # Preserve world bit
|
||||
return cell_id
|
||||
|
||||
# ============================================================================
|
||||
# PLACEMENT ALGORITHM
|
||||
# ============================================================================
|
||||
@@ -1316,11 +1346,22 @@ def format_grid_for_spoiler(grid: List[List[int]]) -> str:
|
||||
return "\n".join(lines)
|
||||
|
||||
def is_same_large_screen(grid: List[List[int]], row1: int, col1: int, row2: int, col2: int) -> bool:
|
||||
id1 = grid[row1 % 8][col1 % 8]
|
||||
id2 = grid[row2 % 8][col2 % 8]
|
||||
"""Checks if two adjacent cells belong to the same large screen with correct quadrant positions."""
|
||||
id1, id2 = grid[row1 % 8][col1 % 8], grid[row2 % 8][col2 % 8]
|
||||
if id1 == -1 or id2 == -1:
|
||||
return False
|
||||
return id1 == id2 and id1 in large_screen_ids
|
||||
base1, base2 = get_screen_id_from_cell(id1), get_screen_id_from_cell(id2)
|
||||
if base1 != base2 or base1 not in large_screen_ids:
|
||||
return False
|
||||
# Get quadrant offsets (0x00=NW, 0x01=NE, 0x08=SW, 0x09=SE)
|
||||
q1, q2 = (id1 & 0xBF) - (base1 & 0xBF), (id2 & 0xBF) - (base2 & 0xBF)
|
||||
# Swap if cell2 is before cell1
|
||||
if col1 > col2 or row1 > row2:
|
||||
q1, q2 = q2, q1
|
||||
# Check valid adjacency: east (0x00->0x01, 0x08->0x09) or south (0x00->0x08, 0x01->0x09)
|
||||
if col1 != col2:
|
||||
return (q1, q2) in [(0x00, 0x01), (0x08, 0x09)]
|
||||
return (q1, q2) in [(0x00, 0x08), (0x01, 0x09)]
|
||||
|
||||
# ============================================================================
|
||||
# MAIN EXECUTION
|
||||
@@ -1347,6 +1388,7 @@ def generate_random_grid_layout(world: World, player: int, connected_edges: List
|
||||
options = LayoutGeneratorOptions(
|
||||
horizontal_wrap=horizontal_wrap,
|
||||
vertical_wrap=vertical_wrap,
|
||||
split_large_screens=False,
|
||||
large_screen_pool=False,
|
||||
distortion_chance=0.0,
|
||||
random_order=6 if world.owParallel[player] else 12,
|
||||
@@ -1385,19 +1427,9 @@ def generate_random_grid_layout(world: World, player: int, connected_edges: List
|
||||
connect_edges_for_screen_layout(world, player, result.grid_info, options, connected_edges, prio_edges, overworld_screens, True)
|
||||
grid = result.grid_info.grid
|
||||
|
||||
# Make new grid containing cell IDs for the overworld map
|
||||
map_grid = copy.deepcopy(grid)
|
||||
for w in range(2):
|
||||
for i in range(8):
|
||||
for j in range(8):
|
||||
screen_id = map_grid[w][i][j]
|
||||
if screen_id in large_screen_ids and map_grid[w][i][(j + 1) % 8] == screen_id and map_grid[w][(i + 1) % 8][j] == screen_id and map_grid[w][(i + 1) % 8][(j + 1) % 8] == screen_id:
|
||||
map_grid[w][i][(j + 1) % 8] = screen_id + 0x01
|
||||
map_grid[w][(i + 1) % 8][j] = screen_id + 0x08
|
||||
map_grid[w][(i + 1) % 8][(j + 1) % 8] = screen_id + 0x09
|
||||
world.owgrid[player] = map_grid
|
||||
world.owlayoutmap_lw[player] = {id & 0xBF: i for i, id in enumerate(sum(map_grid[0], []))}
|
||||
world.owlayoutmap_dw[player] = {id & 0xBF: i for i, id in enumerate(sum(map_grid[1], []))}
|
||||
world.owgrid[player] = grid
|
||||
world.owlayoutmap_lw[player] = {id & 0xBF: i for i, id in enumerate(sum(grid[0], []))}
|
||||
world.owlayoutmap_dw[player] = {id & 0xBF: i for i, id in enumerate(sum(grid[1], []))}
|
||||
|
||||
world.spoiler.set_map('layout_grid_lw', format_grid_for_spoiler(grid[0]), grid[0], player)
|
||||
if not world.owParallel[player]:
|
||||
|
||||
@@ -4,7 +4,18 @@ from datetime import datetime
|
||||
from typing import Dict, List
|
||||
from PIL import Image, ImageDraw
|
||||
from BaseClasses import Direction, OWEdge
|
||||
from source.overworld.LayoutGenerator import Screen
|
||||
from source.overworld.LayoutGenerator import Screen, get_screen_id_from_cell
|
||||
|
||||
def get_quadrant_from_cell_id(cell_id: int, screen_id: int) -> str:
|
||||
offset = (cell_id & 0xBF) - (screen_id & 0xBF)
|
||||
if offset == 0x00:
|
||||
return "NW"
|
||||
elif offset == 0x01:
|
||||
return "NE"
|
||||
elif offset == 0x08:
|
||||
return "SW"
|
||||
else:
|
||||
return "SE"
|
||||
|
||||
def get_edge_lists(grid: List[List[List[int]]],
|
||||
overworld_screens: Dict[int, Screen],
|
||||
@@ -13,7 +24,7 @@ def get_edge_lists(grid: List[List[List[int]]],
|
||||
Get list of edges for each cell and direction.
|
||||
|
||||
Args:
|
||||
grid: 3D list [world][row][col] containing screen IDs
|
||||
grid: 3D list [world][row][col] containing cell IDs
|
||||
overworld_screens: Dict of screen_id -> Screen objects
|
||||
large_screen_quadrant_info: Dict of screen_id -> quadrant info for large screens
|
||||
|
||||
@@ -24,47 +35,27 @@ def get_edge_lists(grid: List[List[List[int]]],
|
||||
GRID_SIZE = 8
|
||||
edge_lists = {}
|
||||
|
||||
# Large screen base IDs
|
||||
large_screen_base_ids = [0x00, 0x03, 0x05, 0x18, 0x1B, 0x1E, 0x30, 0x35,
|
||||
0x40, 0x43, 0x45, 0x58, 0x5B, 0x5E, 0x70, 0x75]
|
||||
|
||||
for world_idx in range(2):
|
||||
# Build a map of screen_id -> list of (row, col) positions for large screens
|
||||
large_screen_positions = {}
|
||||
for row in range(GRID_SIZE):
|
||||
for col in range(GRID_SIZE):
|
||||
screen_id = grid[world_idx][row][col]
|
||||
if screen_id != -1 and screen_id in large_screen_base_ids:
|
||||
if screen_id not in large_screen_positions:
|
||||
large_screen_positions[screen_id] = []
|
||||
large_screen_positions[screen_id].append((row, col))
|
||||
cell_id = grid[world_idx][row][col]
|
||||
|
||||
for row in range(GRID_SIZE):
|
||||
for col in range(GRID_SIZE):
|
||||
screen_id = grid[world_idx][row][col]
|
||||
|
||||
if screen_id == -1:
|
||||
if cell_id == -1:
|
||||
# Empty cell - no edges
|
||||
for direction in [Direction.North, Direction.South, Direction.East, Direction.West]:
|
||||
edge_lists[(world_idx, row, col, direction)] = []
|
||||
continue
|
||||
|
||||
screen_id = get_screen_id_from_cell(cell_id)
|
||||
screen = overworld_screens.get(screen_id)
|
||||
if not screen:
|
||||
for direction in [Direction.North, Direction.South, Direction.East, Direction.West]:
|
||||
edge_lists[(world_idx, row, col, direction)] = []
|
||||
continue
|
||||
|
||||
is_large = screen_id in large_screen_base_ids
|
||||
|
||||
if is_large:
|
||||
# For large screens, determine which quadrant this cell is
|
||||
# Find all positions of this large screen and determine quadrant
|
||||
positions = large_screen_positions.get(screen_id, [(row, col)])
|
||||
|
||||
# Determine quadrant by finding relative position
|
||||
# The quadrant is determined by which cells are adjacent
|
||||
quadrant = determine_large_screen_quadrant(row, col, positions, GRID_SIZE)
|
||||
if screen.big:
|
||||
# For large screens, determine quadrant from cell ID
|
||||
quadrant = get_quadrant_from_cell_id(cell_id, screen_id)
|
||||
|
||||
# Get edges for this quadrant
|
||||
if screen_id in large_screen_quadrant_info:
|
||||
@@ -85,45 +76,6 @@ def get_edge_lists(grid: List[List[List[int]]],
|
||||
|
||||
return edge_lists
|
||||
|
||||
def determine_large_screen_quadrant(row: int, col: int, positions: List[tuple], grid_size: int) -> str:
|
||||
"""
|
||||
Determine which quadrant (NW, NE, SW, SE) a cell is in for a large screen.
|
||||
Handles wrapping correctly by checking adjacency patterns.
|
||||
|
||||
Args:
|
||||
row: Current cell row
|
||||
col: Current cell column
|
||||
positions: List of all (row, col) positions for this large screen
|
||||
grid_size: Size of the grid (8)
|
||||
|
||||
Returns:
|
||||
Quadrant string: "NW", "NE", "SW", or "SE"
|
||||
"""
|
||||
positions_set = set(positions)
|
||||
|
||||
# Check which adjacent cells also belong to this large screen
|
||||
has_right = ((row, (col + 1) % grid_size) in positions_set)
|
||||
has_below = (((row + 1) % grid_size, col) in positions_set)
|
||||
has_left = ((row, (col - 1) % grid_size) in positions_set)
|
||||
has_above = (((row - 1) % grid_size, col) in positions_set)
|
||||
|
||||
# Determine quadrant based on adjacency
|
||||
# NW: has right and below neighbors
|
||||
# NE: has left and below neighbors
|
||||
# SW: has right and above neighbors
|
||||
# SE: has left and above neighbors
|
||||
|
||||
if has_right and has_below:
|
||||
return "NW"
|
||||
elif has_left and has_below:
|
||||
return "NE"
|
||||
elif has_right and has_above:
|
||||
return "SW"
|
||||
elif has_left and has_above:
|
||||
return "SE"
|
||||
else:
|
||||
raise Exception("?")
|
||||
|
||||
def is_crossed_edge(edge: OWEdge, overworld_screens: Dict[int, Screen]) -> bool:
|
||||
if edge.dest is None:
|
||||
return False
|
||||
@@ -132,6 +84,40 @@ def is_crossed_edge(edge: OWEdge, overworld_screens: Dict[int, Screen]) -> bool:
|
||||
dest_screen = overworld_screens.get(edge.dest.owIndex)
|
||||
return source_screen.dark_world != dest_screen.dark_world
|
||||
|
||||
def are_large_screen_cells_connected(cell_id1: int, cell_id2: int, quadrant1: str, quadrant2: str, direction: str) -> bool:
|
||||
"""
|
||||
Check if two cells of a large screen are connected (should have no border between them).
|
||||
|
||||
For cells to be connected:
|
||||
1. They must be from the same large screen (same base screen ID)
|
||||
2. Their quadrants must be adjacent in the expected direction
|
||||
|
||||
Args:
|
||||
cell_id1: Cell ID of the first cell
|
||||
cell_id2: Cell ID of the second cell
|
||||
quadrant1: Quadrant of the first cell ("NW", "NE", "SW", "SE")
|
||||
quadrant2: Quadrant of the second cell
|
||||
direction: Direction from cell1 to cell2 ("east", "south")
|
||||
|
||||
Returns:
|
||||
True if the cells should have no border between them
|
||||
"""
|
||||
# Must be from the same large screen
|
||||
screen_id1 = get_screen_id_from_cell(cell_id1)
|
||||
screen_id2 = get_screen_id_from_cell(cell_id2)
|
||||
if screen_id1 != screen_id2:
|
||||
return False
|
||||
|
||||
# Check if quadrants are properly adjacent
|
||||
if direction == "east":
|
||||
# For east connection: NW->NE or SW->SE
|
||||
return (quadrant1 == "NW" and quadrant2 == "NE") or (quadrant1 == "SW" and quadrant2 == "SE")
|
||||
elif direction == "south":
|
||||
# For south connection: NW->SW or NE->SE
|
||||
return (quadrant1 == "NW" and quadrant2 == "SW") or (quadrant1 == "NE" and quadrant2 == "SE")
|
||||
|
||||
return False
|
||||
|
||||
def visualize_layout(grid: List[List[List[int]]], output_dir: str,
|
||||
overworld_screens: Dict[int, Screen],
|
||||
large_screen_quadrant_info: Dict[int, Dict]) -> None:
|
||||
@@ -162,78 +148,42 @@ def visualize_layout(grid: List[List[List[int]]], output_dir: str,
|
||||
output_height = world_height
|
||||
output_img = Image.new('RGB', (output_width, output_height), color='black')
|
||||
|
||||
# Large screen base IDs (defined once for reuse)
|
||||
large_screen_base_ids = [0x00, 0x03, 0x05, 0x18, 0x1B, 0x1E, 0x30, 0x35,
|
||||
0x40, 0x43, 0x45, 0x58, 0x5B, 0x5E, 0x70, 0x75]
|
||||
|
||||
# Process both worlds
|
||||
for world_idx in range(2):
|
||||
x_offset = 0 if world_idx == 0 else (world_width + gap)
|
||||
|
||||
# Build a map of screen_id -> list of (row, col) positions for large screens
|
||||
large_screen_positions = {}
|
||||
for row in range(GRID_SIZE):
|
||||
for col in range(GRID_SIZE):
|
||||
screen_id = grid[world_idx][row][col]
|
||||
if screen_id != -1 and screen_id in large_screen_base_ids:
|
||||
if screen_id not in large_screen_positions:
|
||||
large_screen_positions[screen_id] = []
|
||||
large_screen_positions[screen_id].append((row, col))
|
||||
|
||||
# Process each cell in the grid individually
|
||||
# This handles wrapped large screens correctly by drawing each quadrant separately
|
||||
for row in range(GRID_SIZE):
|
||||
for col in range(GRID_SIZE):
|
||||
screen_id = grid[world_idx][row][col]
|
||||
cell_id = grid[world_idx][row][col]
|
||||
|
||||
if screen_id == -1:
|
||||
if cell_id == -1:
|
||||
# Empty cell - fill with black (already black from initialization)
|
||||
continue
|
||||
|
||||
is_large = screen_id in large_screen_base_ids
|
||||
screen_id = get_screen_id_from_cell(cell_id)
|
||||
screen = overworld_screens.get(screen_id)
|
||||
if not screen:
|
||||
continue
|
||||
|
||||
# Calculate source position in the world image
|
||||
source_row = (screen_id % 0x40) >> 3
|
||||
source_col = screen_id % 0x08
|
||||
world_img = lightworld_img if screen_id < 0x40 else darkworld_img
|
||||
is_large = screen.big
|
||||
|
||||
if is_large:
|
||||
# For large screens, determine which quadrant this cell represents
|
||||
positions = large_screen_positions.get(screen_id, [(row, col)])
|
||||
quadrant = determine_large_screen_quadrant(row, col, positions, GRID_SIZE)
|
||||
# Calculate source position in the world image based on cell_id
|
||||
# For large screens, cell_id already encodes the quadrant position
|
||||
source_row = (cell_id % 0x40) >> 3
|
||||
source_col = cell_id % 0x08
|
||||
world_img = lightworld_img if cell_id < 0x40 else darkworld_img
|
||||
|
||||
# Map quadrant to source offset within the 2x2 large screen
|
||||
quadrant_offsets = {
|
||||
"NW": (0, 0),
|
||||
"NE": (1, 0),
|
||||
"SW": (0, 1),
|
||||
"SE": (1, 1)
|
||||
}
|
||||
q_col_offset, q_row_offset = quadrant_offsets[quadrant]
|
||||
source_x = source_col * SOURCE_CELL_SIZE
|
||||
source_y = source_row * SOURCE_CELL_SIZE
|
||||
|
||||
# Calculate source position for this quadrant
|
||||
source_x = (source_col + q_col_offset) * SOURCE_CELL_SIZE
|
||||
source_y = (source_row + q_row_offset) * SOURCE_CELL_SIZE
|
||||
|
||||
# Crop single cell from source (the specific quadrant)
|
||||
cropped = world_img.crop((
|
||||
source_x,
|
||||
source_y,
|
||||
source_x + SOURCE_CELL_SIZE,
|
||||
source_y + SOURCE_CELL_SIZE
|
||||
))
|
||||
else:
|
||||
# Small screen (1x1)
|
||||
source_x = source_col * SOURCE_CELL_SIZE
|
||||
source_y = source_row * SOURCE_CELL_SIZE
|
||||
|
||||
# Crop single cell from source
|
||||
cropped = world_img.crop((
|
||||
source_x,
|
||||
source_y,
|
||||
source_x + SOURCE_CELL_SIZE,
|
||||
source_y + SOURCE_CELL_SIZE
|
||||
))
|
||||
# Crop single cell from source
|
||||
cropped = world_img.crop((
|
||||
source_x,
|
||||
source_y,
|
||||
source_x + SOURCE_CELL_SIZE,
|
||||
source_y + SOURCE_CELL_SIZE
|
||||
))
|
||||
|
||||
# Resize to output size (64x64 pixels)
|
||||
resized = cropped.resize(
|
||||
@@ -257,52 +207,93 @@ def visualize_layout(grid: List[List[List[int]]], output_dir: str,
|
||||
for world_idx in range(2):
|
||||
x_offset = 0 if world_idx == 0 else (world_width + gap)
|
||||
|
||||
# Build large screen positions map for this world
|
||||
large_screen_positions = {}
|
||||
for row in range(GRID_SIZE):
|
||||
for col in range(GRID_SIZE):
|
||||
screen_id = grid[world_idx][row][col]
|
||||
if screen_id != -1 and screen_id in large_screen_base_ids:
|
||||
if screen_id not in large_screen_positions:
|
||||
large_screen_positions[screen_id] = []
|
||||
large_screen_positions[screen_id].append((row, col))
|
||||
|
||||
# Draw borders for each cell
|
||||
# For large screens, only draw borders where cells are not connected
|
||||
for row in range(GRID_SIZE):
|
||||
for col in range(GRID_SIZE):
|
||||
screen_id = grid[world_idx][row][col]
|
||||
cell_id = grid[world_idx][row][col]
|
||||
|
||||
if screen_id == -1:
|
||||
if cell_id == -1:
|
||||
continue
|
||||
|
||||
is_large = screen_id in large_screen_base_ids
|
||||
screen_id = get_screen_id_from_cell(cell_id)
|
||||
screen = overworld_screens.get(screen_id)
|
||||
if not screen:
|
||||
continue
|
||||
|
||||
is_large = screen.big
|
||||
|
||||
dest_x = x_offset + col * OUTPUT_CELL_SIZE
|
||||
dest_y = row * OUTPUT_CELL_SIZE
|
||||
|
||||
if is_large:
|
||||
# For large screens, determine which quadrant this cell is
|
||||
positions = large_screen_positions.get(screen_id, [(row, col)])
|
||||
quadrant = determine_large_screen_quadrant(row, col, positions, GRID_SIZE)
|
||||
quadrant = get_quadrant_from_cell_id(cell_id, screen_id)
|
||||
|
||||
# Draw border only on the outer edges of the large screen
|
||||
# (not on internal edges between quadrants)
|
||||
# NW: draw top and left borders
|
||||
# NE: draw top and right borders
|
||||
# SW: draw bottom and left borders
|
||||
# SE: draw bottom and right borders
|
||||
|
||||
if quadrant in ["NW", "NE"]:
|
||||
# Draw top border
|
||||
draw.line([(dest_x, dest_y), (dest_x + OUTPUT_CELL_SIZE - 1, dest_y)], fill='black', width=BORDER_WIDTH)
|
||||
# Check each border direction
|
||||
# Top border: draw if this is a north quadrant OR if the cell above is not connected
|
||||
draw_top = True
|
||||
if quadrant in ["SW", "SE"]:
|
||||
# Draw bottom border
|
||||
draw.line([(dest_x, dest_y + OUTPUT_CELL_SIZE - 1), (dest_x + OUTPUT_CELL_SIZE - 1, dest_y + OUTPUT_CELL_SIZE - 1)], fill='black', width=BORDER_WIDTH)
|
||||
if quadrant in ["NW", "SW"]:
|
||||
# Draw left border
|
||||
draw.line([(dest_x, dest_y), (dest_x, dest_y + OUTPUT_CELL_SIZE - 1)], fill='black', width=BORDER_WIDTH)
|
||||
# Check if cell above is connected
|
||||
above_row = (row - 1) % GRID_SIZE
|
||||
above_cell_id = grid[world_idx][above_row][col]
|
||||
if above_cell_id != -1:
|
||||
above_screen_id = get_screen_id_from_cell(above_cell_id)
|
||||
above_screen = overworld_screens.get(above_screen_id)
|
||||
if above_screen and above_screen.big:
|
||||
above_quadrant = get_quadrant_from_cell_id(above_cell_id, above_screen_id)
|
||||
if are_large_screen_cells_connected(above_cell_id, cell_id, above_quadrant, quadrant, "south"):
|
||||
draw_top = False
|
||||
|
||||
# Bottom border: draw if this is a south quadrant OR if the cell below is not connected
|
||||
draw_bottom = True
|
||||
if quadrant in ["NW", "NE"]:
|
||||
# Check if cell below is connected
|
||||
below_row = (row + 1) % GRID_SIZE
|
||||
below_cell_id = grid[world_idx][below_row][col]
|
||||
if below_cell_id != -1:
|
||||
below_screen_id = get_screen_id_from_cell(below_cell_id)
|
||||
below_screen = overworld_screens.get(below_screen_id)
|
||||
if below_screen and below_screen.big:
|
||||
below_quadrant = get_quadrant_from_cell_id(below_cell_id, below_screen_id)
|
||||
if are_large_screen_cells_connected(cell_id, below_cell_id, quadrant, below_quadrant, "south"):
|
||||
draw_bottom = False
|
||||
|
||||
# Left border: draw if this is a west quadrant OR if the cell to the left is not connected
|
||||
draw_left = True
|
||||
if quadrant in ["NE", "SE"]:
|
||||
# Draw right border
|
||||
# Check if cell to the left is connected
|
||||
left_col = (col - 1) % GRID_SIZE
|
||||
left_cell_id = grid[world_idx][row][left_col]
|
||||
if left_cell_id != -1:
|
||||
left_screen_id = get_screen_id_from_cell(left_cell_id)
|
||||
left_screen = overworld_screens.get(left_screen_id)
|
||||
if left_screen and left_screen.big:
|
||||
left_quadrant = get_quadrant_from_cell_id(left_cell_id, left_screen_id)
|
||||
if are_large_screen_cells_connected(left_cell_id, cell_id, left_quadrant, quadrant, "east"):
|
||||
draw_left = False
|
||||
|
||||
# Right border: draw if this is an east quadrant OR if the cell to the right is not connected
|
||||
draw_right = True
|
||||
if quadrant in ["NW", "SW"]:
|
||||
# Check if cell to the right is connected
|
||||
right_col = (col + 1) % GRID_SIZE
|
||||
right_cell_id = grid[world_idx][row][right_col]
|
||||
if right_cell_id != -1:
|
||||
right_screen_id = get_screen_id_from_cell(right_cell_id)
|
||||
right_screen = overworld_screens.get(right_screen_id)
|
||||
if right_screen and right_screen.big:
|
||||
right_quadrant = get_quadrant_from_cell_id(right_cell_id, right_screen_id)
|
||||
if are_large_screen_cells_connected(cell_id, right_cell_id, quadrant, right_quadrant, "east"):
|
||||
draw_right = False
|
||||
|
||||
# Draw the borders
|
||||
if draw_top:
|
||||
draw.line([(dest_x, dest_y), (dest_x + OUTPUT_CELL_SIZE - 1, dest_y)], fill='black', width=BORDER_WIDTH)
|
||||
if draw_bottom:
|
||||
draw.line([(dest_x, dest_y + OUTPUT_CELL_SIZE - 1), (dest_x + OUTPUT_CELL_SIZE - 1, dest_y + OUTPUT_CELL_SIZE - 1)], fill='black', width=BORDER_WIDTH)
|
||||
if draw_left:
|
||||
draw.line([(dest_x, dest_y), (dest_x, dest_y + OUTPUT_CELL_SIZE - 1)], fill='black', width=BORDER_WIDTH)
|
||||
if draw_right:
|
||||
draw.line([(dest_x + OUTPUT_CELL_SIZE - 1, dest_y), (dest_x + OUTPUT_CELL_SIZE - 1, dest_y + OUTPUT_CELL_SIZE - 1)], fill='black', width=BORDER_WIDTH)
|
||||
else:
|
||||
# Small screen - draw border around single cell
|
||||
@@ -315,8 +306,8 @@ def visualize_layout(grid: List[List[List[int]]], output_dir: str,
|
||||
# Draw edge connection indicators for each cell
|
||||
for row in range(GRID_SIZE):
|
||||
for col in range(GRID_SIZE):
|
||||
screen_id = grid[world_idx][row][col]
|
||||
if screen_id == -1:
|
||||
cell_id = grid[world_idx][row][col]
|
||||
if cell_id == -1:
|
||||
continue
|
||||
|
||||
dest_x = x_offset + col * OUTPUT_CELL_SIZE
|
||||
|
||||
Reference in New Issue
Block a user