Files
Station-0/scripts/ui/mini_map.gd
T

172 lines
5.8 KiB
GDScript

extends Control
# ---------------------------------------------------------------------------
# Layout constants
# ---------------------------------------------------------------------------
const MAP_SIZE := Vector2(150, 150)
const ROOM_SIZE := Vector2(18, 10) # 16:9 aspect ratio like actual rooms
const ROOM_GAP := 4.0
const PADDING := 10.0
const CONNECTION_WIDTH := 2.0
# ---------------------------------------------------------------------------
# Colors (brightened versions of room.gd floor colors)
# ---------------------------------------------------------------------------
const ROOM_COLORS := {
RoomData.RoomType.START: Color(0.24, 0.24, 0.36), # blue-grey
RoomData.RoomType.COMBAT: Color(0.24, 0.24, 0.36), # blue-grey
RoomData.RoomType.ITEM: Color(0.20, 0.32, 0.20), # green
RoomData.RoomType.SHOP: Color(0.32, 0.24, 0.16), # brown
RoomData.RoomType.BOSS: Color(0.40, 0.16, 0.16), # red
}
const CURRENT_OUTLINE := Color(1.0, 1.0, 0.4) # bright yellow
const CONNECTION_COLOR := Color(0.5, 0.5, 0.6)
const UNVISITED_ALPHA := 0.35
const BACKGROUND_COLOR := Color(0.08, 0.08, 0.10, 0.85)
# ---------------------------------------------------------------------------
# State
# ---------------------------------------------------------------------------
var _rooms_data: Dictionary = {} # Vector2i -> RoomData
var _current_pos: Vector2i = Vector2i.ZERO
var _grid_min: Vector2i = Vector2i.ZERO
var _grid_max: Vector2i = Vector2i.ZERO
# ---------------------------------------------------------------------------
# Setup
# ---------------------------------------------------------------------------
func _ready() -> void:
# Position in top-right corner
anchor_left = 1.0
anchor_right = 1.0
anchor_top = 0.0
anchor_bottom = 0.0
offset_left = -MAP_SIZE.x - PADDING
offset_right = -PADDING
offset_top = PADDING
offset_bottom = MAP_SIZE.y + PADDING
custom_minimum_size = MAP_SIZE
# Connect signals
EventBus.floor_map_ready.connect(_on_floor_map_ready)
EventBus.room_entered_at.connect(_on_room_entered)
EventBus.run_ended.connect(_on_run_ended)
# ---------------------------------------------------------------------------
# Signal handlers
# ---------------------------------------------------------------------------
func _on_floor_map_ready(rooms_data: Dictionary) -> void:
_rooms_data = rooms_data
_compute_grid_bounds()
queue_redraw()
func _on_room_entered(grid_pos: Vector2i) -> void:
_current_pos = grid_pos
queue_redraw()
func _on_run_ended(_reached_hub: bool) -> void:
_rooms_data.clear()
queue_redraw()
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
func _compute_grid_bounds() -> void:
if _rooms_data.is_empty():
_grid_min = Vector2i.ZERO
_grid_max = Vector2i.ZERO
return
var first := true
for pos in _rooms_data.keys():
if first:
_grid_min = pos
_grid_max = pos
first = false
else:
_grid_min.x = mini(_grid_min.x, pos.x)
_grid_min.y = mini(_grid_min.y, pos.y)
_grid_max.x = maxi(_grid_max.x, pos.x)
_grid_max.y = maxi(_grid_max.y, pos.y)
func _get_visible_rooms() -> Dictionary:
# Returns {Vector2i -> {"data": RoomData, "visited": bool}}
var visible := {}
var known_positions: Array[Vector2i] = []
# First pass: collect visited rooms and their adjacent positions
for pos in _rooms_data.keys():
var room: RoomData = _rooms_data[pos]
if room.visited:
visible[pos] = {"data": room, "visited": true}
for conn_pos in room.connections.values():
if conn_pos not in known_positions:
known_positions.append(conn_pos)
# Second pass: add adjacent (known but unvisited) rooms
for pos in known_positions:
if pos not in visible and _rooms_data.has(pos):
visible[pos] = {"data": _rooms_data[pos], "visited": false}
return visible
func _grid_to_map_pos(grid_pos: Vector2i) -> Vector2:
# Convert grid position to local drawing coordinates
var cell_size := ROOM_SIZE + Vector2(ROOM_GAP, ROOM_GAP)
var grid_size := Vector2i(_grid_max.x - _grid_min.x + 1, _grid_max.y - _grid_min.y + 1)
var total_size := Vector2(grid_size.x, grid_size.y) * cell_size - Vector2(ROOM_GAP, ROOM_GAP)
# Center the map within the control
var centering := (MAP_SIZE - total_size) / 2.0
var offset := Vector2(
(grid_pos.x - _grid_min.x) * cell_size.x,
(grid_pos.y - _grid_min.y) * cell_size.y
)
return centering + offset
# ---------------------------------------------------------------------------
# Drawing
# ---------------------------------------------------------------------------
func _draw() -> void:
if _rooms_data.is_empty():
return
# Background panel
draw_rect(Rect2(Vector2.ZERO, MAP_SIZE), BACKGROUND_COLOR)
var visible := _get_visible_rooms()
# Draw connections first (under rooms)
for pos in visible.keys():
var room: RoomData = visible[pos]["data"]
var room_center := _grid_to_map_pos(pos) + ROOM_SIZE / 2.0
for dir in room.connections.keys():
var conn_pos: Vector2i = room.connections[dir]
if visible.has(conn_pos):
var conn_center := _grid_to_map_pos(conn_pos) + ROOM_SIZE / 2.0
# Only draw each connection once (from lower to higher grid pos)
if pos.x < conn_pos.x or (pos.x == conn_pos.x and pos.y < conn_pos.y):
draw_line(room_center, conn_center, CONNECTION_COLOR, CONNECTION_WIDTH)
# Draw rooms
for pos in visible.keys():
var info: Dictionary = visible[pos]
var room: RoomData = info["data"]
var is_visited: bool = info["visited"]
var map_pos := _grid_to_map_pos(pos)
var rect := Rect2(map_pos, ROOM_SIZE)
# Room color with alpha based on visited state
var color: Color = ROOM_COLORS.get(room.type, ROOM_COLORS[RoomData.RoomType.COMBAT])
if not is_visited:
color.a = UNVISITED_ALPHA
draw_rect(rect, color)
# Current room indicator - bright outline
if pos == _current_pos:
draw_rect(rect, CURRENT_OUTLINE, false, 2.0)