Fixes room spawning and adds physical doors and indicator lights
This commit is contained in:
@@ -13,4 +13,6 @@ color = Color(0.12, 0.12, 0.18, 1)
|
|||||||
|
|
||||||
[node name="DoorTriggers" type="Node2D" parent="."]
|
[node name="DoorTriggers" type="Node2D" parent="."]
|
||||||
|
|
||||||
|
[node name="Doors" type="Node2D" parent="."]
|
||||||
|
|
||||||
[node name="Contents" type="Node2D" parent="."]
|
[node name="Contents" type="Node2D" parent="."]
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
extends Node2D
|
||||||
|
class_name RoomDoor
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Constants
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
const DOOR_THICKNESS := 12.0
|
||||||
|
const INDICATOR_SIZE := 10.0
|
||||||
|
const INDICATOR_OFFSET := 20.0 # distance above door
|
||||||
|
|
||||||
|
# Indicator light colors (brighter versions of room floor colors)
|
||||||
|
const INDICATOR_COLORS := {
|
||||||
|
RoomData.RoomType.START: Color(0.3, 0.3, 0.5), # blue-gray
|
||||||
|
RoomData.RoomType.COMBAT: Color(0.35, 0.35, 0.55), # blue-gray
|
||||||
|
RoomData.RoomType.ITEM: Color(0.2, 0.5, 0.2), # green
|
||||||
|
RoomData.RoomType.SHOP: Color(0.6, 0.45, 0.2), # gold/amber
|
||||||
|
RoomData.RoomType.BOSS: Color(0.7, 0.15, 0.15), # red
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# State
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
var direction: String = "" # "up", "down", "left", "right"
|
||||||
|
var door_size: Vector2 = Vector2.ZERO
|
||||||
|
var adjacent_room_type: RoomData.RoomType = RoomData.RoomType.COMBAT
|
||||||
|
|
||||||
|
var _barrier: StaticBody2D = null
|
||||||
|
var _barrier_collision: CollisionShape2D = null
|
||||||
|
var _barrier_visual: Polygon2D = null
|
||||||
|
var _indicator: Polygon2D = null
|
||||||
|
var _is_locked: bool = false
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Setup
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
func setup(dir: String, size: Vector2, adj_type: RoomData.RoomType) -> void:
|
||||||
|
direction = dir
|
||||||
|
door_size = size
|
||||||
|
adjacent_room_type = adj_type
|
||||||
|
|
||||||
|
_build_barrier()
|
||||||
|
_build_indicator()
|
||||||
|
|
||||||
|
# Start unlocked (open)
|
||||||
|
unlock()
|
||||||
|
|
||||||
|
|
||||||
|
func _build_barrier() -> void:
|
||||||
|
_barrier = StaticBody2D.new()
|
||||||
|
_barrier.collision_layer = 1 # world layer - blocks player
|
||||||
|
_barrier.collision_mask = 0
|
||||||
|
|
||||||
|
_barrier_collision = CollisionShape2D.new()
|
||||||
|
var shape := RectangleShape2D.new()
|
||||||
|
shape.size = door_size
|
||||||
|
_barrier_collision.shape = shape
|
||||||
|
_barrier.add_child(_barrier_collision)
|
||||||
|
|
||||||
|
# Visual - solid rectangle
|
||||||
|
_barrier_visual = Polygon2D.new()
|
||||||
|
var hx := door_size.x / 2.0
|
||||||
|
var hy := door_size.y / 2.0
|
||||||
|
_barrier_visual.polygon = PackedVector2Array([
|
||||||
|
Vector2(-hx, -hy), Vector2(hx, -hy),
|
||||||
|
Vector2(hx, hy), Vector2(-hx, hy),
|
||||||
|
])
|
||||||
|
_barrier_visual.color = Color(0.4, 0.35, 0.3) # metallic brown
|
||||||
|
_barrier.add_child(_barrier_visual)
|
||||||
|
|
||||||
|
add_child(_barrier)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_indicator() -> void:
|
||||||
|
_indicator = Polygon2D.new()
|
||||||
|
|
||||||
|
# Small diamond shape for indicator
|
||||||
|
var s := INDICATOR_SIZE
|
||||||
|
_indicator.polygon = PackedVector2Array([
|
||||||
|
Vector2(0, -s), Vector2(s, 0),
|
||||||
|
Vector2(0, s), Vector2(-s, 0),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Position based on door direction
|
||||||
|
var offset := Vector2.ZERO
|
||||||
|
match direction:
|
||||||
|
"up": offset = Vector2(0, -door_size.y / 2.0 - INDICATOR_OFFSET)
|
||||||
|
"down": offset = Vector2(0, door_size.y / 2.0 + INDICATOR_OFFSET)
|
||||||
|
"left": 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.color = INDICATOR_COLORS.get(adjacent_room_type, INDICATOR_COLORS[RoomData.RoomType.COMBAT])
|
||||||
|
|
||||||
|
add_child(_indicator)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Lock/Unlock
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
func lock() -> void:
|
||||||
|
_is_locked = true
|
||||||
|
_barrier_collision.disabled = false
|
||||||
|
_barrier_visual.visible = true
|
||||||
|
|
||||||
|
|
||||||
|
func unlock() -> void:
|
||||||
|
_is_locked = false
|
||||||
|
_barrier_collision.disabled = true
|
||||||
|
_barrier_visual.visible = false
|
||||||
|
|
||||||
|
|
||||||
|
func is_locked() -> bool:
|
||||||
|
return _is_locked
|
||||||
@@ -90,3 +90,11 @@ func _entry_offset(from_direction: String) -> Vector2:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
func _grid_to_world(grid_pos: Vector2i) -> Vector2:
|
func _grid_to_world(grid_pos: Vector2i) -> Vector2:
|
||||||
return Vector2(grid_pos.x * ROOM_W, grid_pos.y * ROOM_H)
|
return Vector2(grid_pos.x * ROOM_W, grid_pos.y * ROOM_H)
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the RoomData.RoomType for the room at the given grid position.
|
||||||
|
# Returns -1 if no room exists at that position.
|
||||||
|
func get_room_type_at(grid_pos: Vector2i) -> int:
|
||||||
|
if _rooms.has(grid_pos):
|
||||||
|
return _rooms[grid_pos].data.type
|
||||||
|
return -1
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ class_name FloorGenerator
|
|||||||
const GRID_START := Vector2i(5, 5)
|
const GRID_START := Vector2i(5, 5)
|
||||||
|
|
||||||
const FLOOR_ROOM_COUNTS := {
|
const FLOOR_ROOM_COUNTS := {
|
||||||
1: {"min": 5, "max": 6}, # 4-5 combat + start + boss
|
1: {"min": 8, "max": 9}, # 4-5 combat + start + boss + item + shop
|
||||||
2: {"min": 6, "max": 7}, # 5-6 combat + start + boss
|
2: {"min": 9, "max": 10}, # 5-6 combat + start + boss + item + shop
|
||||||
3: {"min": 3, "max": 4}, # 2-3 combat + start + boss
|
3: {"min": 6, "max": 7}, # 2-3 combat + start + boss + item + shop
|
||||||
}
|
}
|
||||||
|
|
||||||
const DIRS := {
|
const DIRS := {
|
||||||
@@ -29,6 +29,7 @@ static func generate(floor_number: int) -> Array:
|
|||||||
var start := RoomData.new()
|
var start := RoomData.new()
|
||||||
start.type = RoomData.RoomType.START
|
start.type = RoomData.RoomType.START
|
||||||
start.grid_pos = GRID_START
|
start.grid_pos = GRID_START
|
||||||
|
start.id = "%d_%d" % [GRID_START.x, GRID_START.y]
|
||||||
rooms[GRID_START] = start
|
rooms[GRID_START] = start
|
||||||
|
|
||||||
var queue: Array = [GRID_START]
|
var queue: Array = [GRID_START]
|
||||||
@@ -59,6 +60,7 @@ static func generate(floor_number: int) -> Array:
|
|||||||
var r := RoomData.new()
|
var r := RoomData.new()
|
||||||
r.type = RoomData.RoomType.COMBAT
|
r.type = RoomData.RoomType.COMBAT
|
||||||
r.grid_pos = next
|
r.grid_pos = next
|
||||||
|
r.id = "%d_%d" % [next.x, next.y]
|
||||||
rooms[next] = r
|
rooms[next] = r
|
||||||
queue.append(next)
|
queue.append(next)
|
||||||
|
|
||||||
|
|||||||
+43
-2
@@ -55,6 +55,7 @@ var data: RoomData = null
|
|||||||
var _floor: Node = null # reference to floor.gd node
|
var _floor: Node = null # reference to floor.gd node
|
||||||
var _enemy_count: int = 0
|
var _enemy_count: int = 0
|
||||||
var _doors: Dictionary = {} # direction -> Area2D trigger
|
var _doors: Dictionary = {} # direction -> Area2D trigger
|
||||||
|
var _physical_doors: Dictionary = {} # direction -> RoomDoor
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Node refs (set by room_base.tscn)
|
# Node refs (set by room_base.tscn)
|
||||||
@@ -62,6 +63,7 @@ var _doors: Dictionary = {} # direction -> Area2D trigger
|
|||||||
@onready var floor_poly: Polygon2D = $Floor
|
@onready var floor_poly: Polygon2D = $Floor
|
||||||
@onready var walls_node: Node2D = $Walls
|
@onready var walls_node: Node2D = $Walls
|
||||||
@onready var door_triggers: Node2D = $DoorTriggers
|
@onready var door_triggers: Node2D = $DoorTriggers
|
||||||
|
@onready var doors_node: Node2D = $Doors
|
||||||
@onready var contents: Node2D = $Contents
|
@onready var contents: Node2D = $Contents
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -73,12 +75,13 @@ func setup(room_data: RoomData, floor_ref: Node) -> void:
|
|||||||
_apply_floor_color()
|
_apply_floor_color()
|
||||||
_build_walls()
|
_build_walls()
|
||||||
_build_door_triggers()
|
_build_door_triggers()
|
||||||
|
_build_physical_doors()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Activate — called on first (and repeat) entry
|
# Activate — called on first (and repeat) entry
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
func activate() -> void:
|
func activate() -> void:
|
||||||
EventBus.room_entered.emit(data.id if "id" in data else "")
|
EventBus.room_entered.emit(data.id)
|
||||||
if data.visited:
|
if data.visited:
|
||||||
_update_door_locks()
|
_update_door_locks()
|
||||||
return
|
return
|
||||||
@@ -227,6 +230,35 @@ func _on_door_entered(body: Node, direction: String) -> void:
|
|||||||
"transition_to", data.connections[direction], direction
|
"transition_to", data.connections[direction], direction
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Physical doors with lock barriers and indicator lights
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
func _build_physical_doors() -> void:
|
||||||
|
const DOOR_THICKNESS := 12.0
|
||||||
|
var hw := ROOM_W / 2.0
|
||||||
|
var hh := ROOM_H / 2.0
|
||||||
|
|
||||||
|
# Door positions and sizes per direction
|
||||||
|
var configs := {
|
||||||
|
"up": {"pos": Vector2(0, -hh + WALL_T / 2.0), "size": Vector2(DOOR_W, DOOR_THICKNESS)},
|
||||||
|
"down": {"pos": Vector2(0, hh - WALL_T / 2.0), "size": Vector2(DOOR_W, DOOR_THICKNESS)},
|
||||||
|
"left": {"pos": Vector2(-hw + WALL_T / 2.0, 0), "size": Vector2(DOOR_THICKNESS, DOOR_H)},
|
||||||
|
"right": {"pos": Vector2(hw - WALL_T / 2.0, 0), "size": Vector2(DOOR_THICKNESS, DOOR_H)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for dir in data.connections.keys():
|
||||||
|
var cfg = configs[dir]
|
||||||
|
var adj_grid: Vector2i = data.connections[dir]
|
||||||
|
var adj_type: int = _floor.get_room_type_at(adj_grid)
|
||||||
|
if adj_type < 0:
|
||||||
|
adj_type = RoomData.RoomType.COMBAT # fallback
|
||||||
|
|
||||||
|
var door := RoomDoor.new()
|
||||||
|
door.position = cfg["pos"]
|
||||||
|
door.setup(dir, cfg["size"], adj_type as RoomData.RoomType)
|
||||||
|
doors_node.add_child(door)
|
||||||
|
_physical_doors[dir] = door
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Enemy spawning
|
# Enemy spawning
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -284,7 +316,7 @@ func _on_enemy_died() -> void:
|
|||||||
_spawn_body_part_drop()
|
_spawn_body_part_drop()
|
||||||
_spawn_floor_exit()
|
_spawn_floor_exit()
|
||||||
room_cleared.emit()
|
room_cleared.emit()
|
||||||
EventBus.room_cleared.emit(data.id if "id" in data else "")
|
EventBus.room_cleared.emit(data.id)
|
||||||
_update_door_locks()
|
_update_door_locks()
|
||||||
|
|
||||||
|
|
||||||
@@ -512,5 +544,14 @@ func _update_door_locks() -> void:
|
|||||||
var is_combat := data.type == RoomData.RoomType.COMBAT \
|
var is_combat := data.type == RoomData.RoomType.COMBAT \
|
||||||
or data.type == RoomData.RoomType.BOSS
|
or data.type == RoomData.RoomType.BOSS
|
||||||
var locked := data.visited and not data.cleared and is_combat
|
var locked := data.visited and not data.cleared and is_combat
|
||||||
|
|
||||||
|
# Update door triggers (for transitions)
|
||||||
for area in _doors.values():
|
for area in _doors.values():
|
||||||
(area as Area2D).monitoring = not locked
|
(area as Area2D).monitoring = not locked
|
||||||
|
|
||||||
|
# Update physical door barriers
|
||||||
|
for door in _physical_doors.values():
|
||||||
|
if locked:
|
||||||
|
door.lock()
|
||||||
|
else:
|
||||||
|
door.unlock()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ class_name RoomData
|
|||||||
|
|
||||||
enum RoomType { START, COMBAT, ITEM, SHOP, BOSS }
|
enum RoomType { START, COMBAT, ITEM, SHOP, BOSS }
|
||||||
|
|
||||||
|
var id: String = "" # unique identifier (e.g., "5_5" from grid pos)
|
||||||
var type: RoomType = RoomType.COMBAT
|
var type: RoomType = RoomType.COMBAT
|
||||||
var grid_pos: Vector2i = Vector2i.ZERO
|
var grid_pos: Vector2i = Vector2i.ZERO
|
||||||
var connections: Dictionary = {} # "up"/"down"/"left"/"right" -> Vector2i
|
var connections: Dictionary = {} # "up"/"down"/"left"/"right" -> Vector2i
|
||||||
|
|||||||
Reference in New Issue
Block a user