Implements mini map and updates door lights

This commit is contained in:
2026-03-10 11:24:36 -04:00
parent b709b64a52
commit db44a1a318
5 changed files with 206 additions and 7 deletions
+2
View File
@@ -15,6 +15,8 @@ signal floor_cleared(floor_number: int)
signal room_entered(room_id: String) signal room_entered(room_id: String)
signal room_cleared(room_id: String) signal room_cleared(room_id: String)
signal room_revealed signal room_revealed
signal room_entered_at(grid_pos: Vector2i)
signal floor_map_ready(rooms_data: Dictionary)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Items & Loot # Items & Loot
+16 -7
View File
@@ -8,15 +8,20 @@ const DOOR_THICKNESS := 12.0
const INDICATOR_SIZE := 10.0 const INDICATOR_SIZE := 10.0
const INDICATOR_OFFSET := 20.0 # distance above door const INDICATOR_OFFSET := 20.0 # distance above door
# Indicator light colors (brighter versions of room floor colors) # Indicator light colors - only for special rooms, matching floor colors but brighter
const INDICATOR_COLORS := { const INDICATOR_COLORS := {
RoomData.RoomType.START: Color(0.3, 0.3, 0.5), # blue-gray RoomData.RoomType.ITEM: Color(0.2, 0.45, 0.2), # green (brighter version of 0.10, 0.16, 0.10)
RoomData.RoomType.COMBAT: Color(0.35, 0.35, 0.55), # blue-gray RoomData.RoomType.SHOP: Color(0.9, 0.75, 0.1), # yellow
RoomData.RoomType.ITEM: Color(0.2, 0.5, 0.2), # green RoomData.RoomType.BOSS: Color(0.7, 0.15, 0.15), # red (brighter version of 0.20, 0.08, 0.08)
RoomData.RoomType.SHOP: Color(0.6, 0.45, 0.2), # gold/amber
RoomData.RoomType.BOSS: Color(0.7, 0.15, 0.15), # red
} }
# Room types that should show indicator lights
const SPECIAL_ROOM_TYPES := [
RoomData.RoomType.ITEM,
RoomData.RoomType.SHOP,
RoomData.RoomType.BOSS,
]
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# State # State
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -71,6 +76,10 @@ func _build_barrier() -> void:
func _build_indicator() -> void: func _build_indicator() -> void:
# Only show indicators for special rooms (ITEM, SHOP, BOSS)
if adjacent_room_type not in SPECIAL_ROOM_TYPES:
return
_indicator = Polygon2D.new() _indicator = Polygon2D.new()
# Small diamond shape for indicator # Small diamond shape for indicator
@@ -89,7 +98,7 @@ func _build_indicator() -> void:
"right": offset = Vector2(door_size.x / 2.0 + INDICATOR_OFFSET, 0) "right": offset = Vector2(door_size.x / 2.0 + INDICATOR_OFFSET, 0)
_indicator.position = offset _indicator.position = offset
_indicator.color = INDICATOR_COLORS.get(adjacent_room_type, INDICATOR_COLORS[RoomData.RoomType.COMBAT]) _indicator.color = INDICATOR_COLORS[adjacent_room_type]
add_child(_indicator) add_child(_indicator)
+7
View File
@@ -20,6 +20,7 @@ const ENTRY_INSET := 60.0 # how far from wall edge the player appears
# State # State
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
var _rooms: Dictionary = {} # Vector2i -> Room var _rooms: Dictionary = {} # Vector2i -> Room
var _rooms_data: Dictionary = {} # Vector2i -> RoomData (for mini map)
var _current_grid: Vector2i var _current_grid: Vector2i
var _transitioning: bool = false var _transitioning: bool = false
@@ -40,6 +41,7 @@ func _build_floor(floor_data: Array) -> void:
rooms_container.add_child(room_node) rooms_container.add_child(room_node)
room_node.setup(room_data, self) room_node.setup(room_data, self)
_rooms[room_data.grid_pos] = room_node _rooms[room_data.grid_pos] = room_node
_rooms_data[room_data.grid_pos] = room_data
if room_data.type == RoomData.RoomType.START: if room_data.type == RoomData.RoomType.START:
start_pos = room_data.grid_pos start_pos = room_data.grid_pos
@@ -51,6 +53,10 @@ func _build_floor(floor_data: Array) -> void:
_current_grid = start_pos _current_grid = start_pos
_rooms[start_pos].activate() _rooms[start_pos].activate()
# Emit signals for mini map
EventBus.floor_map_ready.emit(_rooms_data)
EventBus.room_entered_at.emit(start_pos)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Transitions # Transitions
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -59,6 +65,7 @@ func transition_to(target_grid: Vector2i, from_direction: String) -> void:
return return
_transitioning = true _transitioning = true
_current_grid = target_grid _current_grid = target_grid
EventBus.room_entered_at.emit(target_grid)
var new_world := _grid_to_world(target_grid) var new_world := _grid_to_world(target_grid)
+10
View File
@@ -1,5 +1,7 @@
extends CanvasLayer extends CanvasLayer
const MiniMapScript = preload("res://scripts/ui/mini_map.gd")
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Heart geometry — two polygons per slot: background (dark) + fill (red) # Heart geometry — two polygons per slot: background (dark) + fill (red)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -39,6 +41,7 @@ const ORIGIN := Vector2(32, 32) # center of first heart (screen px)
# Each entry: { "bg": Polygon2D, "fill": Polygon2D } # Each entry: { "bg": Polygon2D, "fill": Polygon2D }
var _slots: Array = [] var _slots: Array = []
var _credits_label: Label var _credits_label: Label
var _mini_map: Control
# DEPRECATED — Boss bar nodes (commented out) # DEPRECATED — Boss bar nodes (commented out)
#var _boss_bar_root: Node2D = null #var _boss_bar_root: Node2D = null
@@ -56,6 +59,7 @@ func _ready() -> void:
#EventBus.boss_health_changed.connect(_on_boss_health_changed) #EventBus.boss_health_changed.connect(_on_boss_health_changed)
#EventBus.boss_died.connect(_on_boss_died) #EventBus.boss_died.connect(_on_boss_died)
_build_credits_label() _build_credits_label()
_build_mini_map()
# Initialise once the scene is fully set up # Initialise once the scene is fully set up
call_deferred("_init_from_player") call_deferred("_init_from_player")
@@ -126,6 +130,12 @@ func _build_credits_label() -> void:
_credits_label.text = "SCRAP: %d" % amount _credits_label.text = "SCRAP: %d" % amount
add_child(_credits_label) add_child(_credits_label)
func _build_mini_map() -> void:
_mini_map = Control.new()
_mini_map.set_script(MiniMapScript)
add_child(_mini_map)
func _on_credits_changed(new_total: int) -> void: func _on_credits_changed(new_total: int) -> void:
_credits_label.text = "SCRAP: %d" % new_total _credits_label.text = "SCRAP: %d" % new_total
+171
View File
@@ -0,0 +1,171 @@
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)