extends TileMapLayer # Fog renderer — draws fog overlay on non-visible tiles (D-011) # Three visibility states per tile: # visible = no fog tile (clear) # fog-edge = semi-transparent dark overlay (adjacent to visible) # hidden = opaque black overlay # # Atlas layout: # (0,0) = full fog (opaque black) # (1,0) = fog edge (semi-transparent) const TILE_SIZE: int = 32 var _initialized: bool = false var _all_tile_positions: Dictionary = {} # Vector2i -> true, all known map tiles func _ready() -> void: _setup_tileset() _initialized = true print("FogRenderer: Initialized") func _setup_tileset() -> void: var ts := TileSet.new() ts.tile_size = Vector2i(TILE_SIZE, TILE_SIZE) var source := TileSetAtlasSource.new() var img := Image.create(TILE_SIZE * 2, TILE_SIZE, false, Image.FORMAT_RGBA8) # Full fog (0,0) — opaque black _fill_tile(img, 0, Color(0.02, 0.02, 0.05, 1.0)) # Fog edge (1,0) — semi-transparent dark _fill_tile(img, 1, Color(0.02, 0.02, 0.05, 0.6)) var tex := ImageTexture.create_from_image(img) source.texture = tex source.texture_region_size = Vector2i(TILE_SIZE, TILE_SIZE) source.create_tile(Vector2i(0, 0)) source.create_tile(Vector2i(1, 0)) ts.add_source(source) tile_set = ts # Register all known tile positions (called when tile data arrives) func register_tile_positions(tiles: Array) -> void: _all_tile_positions.clear() for tile_data in tiles: if tile_data.has("x") and tile_data.has("y"): _all_tile_positions[Vector2i(tile_data.x, tile_data.y)] = true # Update fog based on visible positions # visible_positions: Dictionary of Vector2i -> true func update_fog(visible_positions: Dictionary, _player_pos: Vector2) -> void: if not _initialized: return clear() if _all_tile_positions.is_empty() or visible_positions.is_empty(): return # Build set of fog-edge positions (adjacent to visible but not visible themselves) var fog_edge: Dictionary = {} var neighbors := [ Vector2i(-1, 0), Vector2i(1, 0), Vector2i(0, -1), Vector2i(0, 1), Vector2i(-1, -1), Vector2i(1, -1), Vector2i(-1, 1), Vector2i(1, 1), ] for pos in visible_positions: for offset in neighbors: var neighbor_pos: Vector2i = pos + offset if not visible_positions.has(neighbor_pos) and _all_tile_positions.has(neighbor_pos): fog_edge[neighbor_pos] = true # Place fog tiles on all known positions that aren't visible for pos in _all_tile_positions: if visible_positions.has(pos): continue # Visible — no fog elif fog_edge.has(pos): set_cell(pos, 0, Vector2i(1, 0)) # Fog edge — semi-transparent else: set_cell(pos, 0, Vector2i(0, 0)) # Full fog — opaque func _fill_tile(img: Image, tile_index: int, color: Color) -> void: var x_offset := tile_index * TILE_SIZE for x in range(TILE_SIZE): for y in range(TILE_SIZE): img.set_pixel(x_offset + x, y, color)