From c0e4aaeac55b3d7f615cbb278c66183d30e8240c Mon Sep 17 00:00:00 2001 From: Aodhan Collins Date: Mon, 26 Jan 2026 03:31:45 +0000 Subject: [PATCH] Basic UI --- scenes/main.tscn | 177 ++++++++++++++++++++++++++++------------- scripts/GameManager.gd | 2 +- scripts/Minimap.gd | 69 ++++++++++------ scripts/WorldData.gd | 85 ++++++++++++++++++++ scripts/main.gd | 22 ++--- 5 files changed, 263 insertions(+), 92 deletions(-) diff --git a/scenes/main.tscn b/scenes/main.tscn index cb33e0b..7c78ceb 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=3 uid="uid://c8j7k6l5m4n3"] +[gd_scene load_steps=8 format=3 uid="uid://c8j7k6l5m4n3"] [ext_resource type="Script" path="res://scripts/main.gd" id="1_main"] [ext_resource type="Script" path="res://scripts/GameManager.gd" id="2_gm"] @@ -6,6 +6,43 @@ [ext_resource type="Texture2D" path="res://assets/placeholder_room.svg" id="4_bg"] [ext_resource type="PackedScene" uid="uid://combat_ui_scene" path="res://scenes/combat_ui.tscn" id="5_combat"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_parchment"] +bg_color = Color(0.96, 0.93, 0.86, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.36, 0.25, 0.2, 1) +corner_radius_top_left = 5 +corner_radius_top_right = 5 +corner_radius_bottom_right = 5 +corner_radius_bottom_left = 5 +content_margin_left = 10.0 +content_margin_top = 10.0 +content_margin_right = 10.0 +content_margin_bottom = 10.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_frame"] +bg_color = Color(0.1, 0.1, 0.1, 1) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.6, 0.5, 0.3, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wood"] +bg_color = Color(0.27, 0.17, 0.13, 1) +border_width_top = 4 +border_color = Color(0.18, 0.11, 0.08, 1) +content_margin_left = 20.0 +content_margin_top = 20.0 +content_margin_right = 20.0 +content_margin_bottom = 20.0 + [node name="Main" type="Node2D"] script = ExtResource("1_main") @@ -17,91 +54,123 @@ script = ExtResource("2_gm") [node name="CombatUI" parent="UI" instance=ExtResource("5_combat")] visible = false -[node name="MainLayout" type="HBoxContainer" parent="UI"] +[node name="MainLayout" type="VBoxContainer" parent="UI"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +theme_override_constants/separation = 0 -[node name="GameView" type="VBoxContainer" parent="UI/MainLayout"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_stretch_ratio = 0.7 - -[node name="RoomImage" type="TextureRect" parent="UI/MainLayout/GameView"] +[node name="TopPanel" type="HBoxContainer" parent="UI/MainLayout"] layout_mode = 2 size_flags_vertical = 3 +size_flags_stretch_ratio = 0.7 +theme_override_constants/separation = 0 + +[node name="NarrativePanel" type="PanelContainer" parent="UI/MainLayout/TopPanel"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.4 +theme_override_styles/panel = SubResource("StyleBoxFlat_parchment") + +[node name="VBox" type="VBoxContainer" parent="UI/MainLayout/TopPanel/NarrativePanel"] +layout_mode = 2 + +[node name="RoomLabel" type="Label" parent="UI/MainLayout/TopPanel/NarrativePanel/VBox"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.2, 0.1, 0.05, 1) +theme_override_font_sizes/font_size = 24 +text = "Room Name" +horizontal_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="UI/MainLayout/TopPanel/NarrativePanel/VBox"] +layout_mode = 2 + +[node name="Log" type="RichTextLabel" parent="UI/MainLayout/TopPanel/NarrativePanel/VBox"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_colors/default_color = Color(0.15, 0.1, 0.05, 1) +text = "Log..." +scroll_following = true + +[node name="Input" type="LineEdit" parent="UI/MainLayout/TopPanel/NarrativePanel/VBox"] +layout_mode = 2 +placeholder_text = "What do you want to do?" + +[node name="VisualPanel" type="PanelContainer" parent="UI/MainLayout/TopPanel"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.6 +theme_override_styles/panel = SubResource("StyleBoxFlat_frame") + +[node name="RoomImage" type="TextureRect" parent="UI/MainLayout/TopPanel/VisualPanel"] +layout_mode = 2 texture = ExtResource("4_bg") expand_mode = 1 stretch_mode = 5 -[node name="Log" type="RichTextLabel" parent="UI/MainLayout/GameView"] +[node name="ControlDeck" type="PanelContainer" parent="UI/MainLayout"] layout_mode = 2 size_flags_vertical = 3 -size_flags_stretch_ratio = 0.4 -text = "Log..." -scroll_following = true - -[node name="Input" type="LineEdit" parent="UI/MainLayout/GameView"] -layout_mode = 2 -placeholder_text = "What do you want to do?" - -[node name="Sidebar" type="VBoxContainer" parent="UI/MainLayout"] -layout_mode = 2 -size_flags_horizontal = 3 size_flags_stretch_ratio = 0.3 +theme_override_styles/panel = SubResource("StyleBoxFlat_wood") -[node name="StatsLabel" type="Label" parent="UI/MainLayout/Sidebar"] +[node name="HBox" type="HBoxContainer" parent="UI/MainLayout/ControlDeck"] layout_mode = 2 -text = "HP: 100/100" -horizontal_alignment = 1 +alignment = 1 +theme_override_constants/separation = 40 -[node name="MinimapLabel" type="Label" parent="UI/MainLayout/Sidebar"] -layout_mode = 2 -text = "Map" -horizontal_alignment = 1 - -[node name="Minimap" type="GridContainer" parent="UI/MainLayout/Sidebar"] -layout_mode = 2 -size_flags_horizontal = 4 -script = ExtResource("3_minimap") - -[node name="RoomLabel" type="Label" parent="UI/MainLayout/Sidebar"] -layout_mode = 2 -text = "Room Name" -horizontal_alignment = 1 - -[node name="Controls" type="VBoxContainer" parent="UI/MainLayout/Sidebar"] -layout_mode = 2 - -[node name="BtnNorth" type="Button" parent="UI/MainLayout/Sidebar/Controls"] -layout_mode = 2 -text = "North" - -[node name="HBox" type="HBoxContainer" parent="UI/MainLayout/Sidebar/Controls"] +[node name="StatsPanel" type="VBoxContainer" parent="UI/MainLayout/ControlDeck/HBox"] layout_mode = 2 alignment = 1 -[node name="BtnWest" type="Button" parent="UI/MainLayout/Sidebar/Controls/HBox"] +[node name="StatsLabel" type="Label" parent="UI/MainLayout/ControlDeck/HBox/StatsPanel"] +layout_mode = 2 +text = "HP: 100/100" + +[node name="MinimapPanel" type="CenterContainer" parent="UI/MainLayout/ControlDeck/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Minimap" type="GridContainer" parent="UI/MainLayout/ControlDeck/HBox/MinimapPanel"] +layout_mode = 2 +script = ExtResource("3_minimap") + +[node name="ActionsPanel" type="VBoxContainer" parent="UI/MainLayout/ControlDeck/HBox"] +layout_mode = 2 +alignment = 1 + +[node name="BtnNorth" type="Button" parent="UI/MainLayout/ControlDeck/HBox/ActionsPanel"] +layout_mode = 2 +size_flags_horizontal = 4 +text = "North" + +[node name="HBox" type="HBoxContainer" parent="UI/MainLayout/ControlDeck/HBox/ActionsPanel"] +layout_mode = 2 +alignment = 1 + +[node name="BtnWest" type="Button" parent="UI/MainLayout/ControlDeck/HBox/ActionsPanel/HBox"] layout_mode = 2 text = "West" -[node name="BtnEast" type="Button" parent="UI/MainLayout/Sidebar/Controls/HBox"] +[node name="BtnEast" type="Button" parent="UI/MainLayout/ControlDeck/HBox/ActionsPanel/HBox"] layout_mode = 2 text = "East" -[node name="BtnSouth" type="Button" parent="UI/MainLayout/Sidebar/Controls"] +[node name="BtnSouth" type="Button" parent="UI/MainLayout/ControlDeck/HBox/ActionsPanel"] layout_mode = 2 +size_flags_horizontal = 4 text = "South" -[node name="HSeparator" type="HSeparator" parent="UI/MainLayout/Sidebar/Controls"] +[node name="SystemPanel" type="VBoxContainer" parent="UI/MainLayout/ControlDeck/HBox"] layout_mode = 2 +alignment = 1 -[node name="BtnSave" type="Button" parent="UI/MainLayout/Sidebar/Controls"] +[node name="BtnSave" type="Button" parent="UI/MainLayout/ControlDeck/HBox/SystemPanel"] layout_mode = 2 -text = "Save Game" +text = "Save" -[node name="BtnLoad" type="Button" parent="UI/MainLayout/Sidebar/Controls"] +[node name="BtnLoad" type="Button" parent="UI/MainLayout/ControlDeck/HBox/SystemPanel"] layout_mode = 2 -text = "Load Game" +text = "Load" diff --git a/scripts/GameManager.gd b/scripts/GameManager.gd index 98dc140..9b53e3a 100644 --- a/scripts/GameManager.gd +++ b/scripts/GameManager.gd @@ -23,7 +23,7 @@ func _ready(): audio_manager = load("res://scripts/AudioManager.gd").new() add_child(audio_manager) - rooms = WorldData.generate_test_world() + rooms = WorldData.generate_procedural_world(20) start_new_game() func start_new_game(): diff --git a/scripts/Minimap.gd b/scripts/Minimap.gd index 5a815d0..d53118d 100644 --- a/scripts/Minimap.gd +++ b/scripts/Minimap.gd @@ -1,32 +1,49 @@ -extends GridContainer +extends Control -var cells: Array[ColorRect] = [] +var explored_rooms: Array = [] +var current_room_id: String = "" +var cell_size: Vector2 = Vector2(20, 20) +var padding: float = 5.0 func _ready(): - columns = 4 - for i in range(16): - var cell = ColorRect.new() - cell.custom_minimum_size = Vector2(40, 40) - cell.color = Color.DARK_GRAY - add_child(cell) - cells.append(cell) + # Remove any children if they exist (cleanup from previous implementation) + for child in get_children(): + child.queue_free() -func update_map(current_room_id: String, explored_rooms: Array): - # Parse "room_x_y" - var parts = current_room_id.split("_") - if parts.size() == 3: - var x = int(parts[1]) - var y = int(parts[2]) - var index = y * 4 + x +func update_map(new_room_id: String, explored: Array): + current_room_id = new_room_id + explored_rooms = explored + queue_redraw() + +func _draw(): + if current_room_id == "": + return - for i in range(cells.size()): - var cell_x = i % 4 - var cell_y = i / 4 - var cell_id = "room_%d_%d" % [cell_x, cell_y] + var center = size / 2 + + # Parse current room coordinates to center the map + var current_coords = _parse_coords(current_room_id) + + for room_id in explored_rooms: + var coords = _parse_coords(room_id) + var rel_x = coords.x - current_coords.x + var rel_y = coords.y - current_coords.y + + # Calculate position relative to center + # (rel_x, rel_y) * (cell_size + padding) gives the offset + var offset = Vector2(rel_x, rel_y) * (cell_size + Vector2(padding, padding)) + var rect_pos = center + offset - cell_size / 2 + var rect = Rect2(rect_pos, cell_size) + + var color = Color.LIGHT_GRAY + if room_id == current_room_id: + color = Color.GREEN - if i == index: - cells[i].color = Color.GREEN # Player - elif cell_id in explored_rooms: - cells[i].color = Color.LIGHT_GRAY # Explored - else: - cells[i].color = Color.DARK_GRAY # Unexplored + draw_rect(rect, color, true) + draw_rect(rect, Color.BLACK, false, 1.0) # Border + +func _parse_coords(id: String) -> Vector2: + var parts = id.split("_") + if parts.size() >= 3: + return Vector2(int(parts[1]), int(parts[2])) + return Vector2.ZERO diff --git a/scripts/WorldData.gd b/scripts/WorldData.gd index 8cc6f3d..fe53c59 100644 --- a/scripts/WorldData.gd +++ b/scripts/WorldData.gd @@ -1,5 +1,90 @@ class_name WorldData +static func generate_procedural_world(num_rooms: int = 15) -> Dictionary: + var rooms = {} + var occupied_coords = {} # Vector2 -> RoomData + + # Start at 0,0 + var current_pos = Vector2(0, 0) + var positions = [current_pos] + + # Create first room + var start_room = _create_room(0, 0) + rooms[start_room.id] = start_room + occupied_coords[current_pos] = start_room + + # Random Walk + while rooms.size() < num_rooms: + # Pick a random existing position to branch from + var base_pos = positions[randi() % positions.size()] + + var directions = [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT] + var dir = directions[randi() % directions.size()] + var new_pos = base_pos + dir + + if not occupied_coords.has(new_pos): + var new_room = _create_room(int(new_pos.x), int(new_pos.y)) + rooms[new_room.id] = new_room + occupied_coords[new_pos] = new_room + positions.append(new_pos) + + # Second pass: Link exits + for pos in occupied_coords: + var room = occupied_coords[pos] + var x = int(pos.x) + var y = int(pos.y) + + if occupied_coords.has(Vector2(x, y - 1)): room.exits["north"] = "room_%d_%d" % [x, y - 1] + if occupied_coords.has(Vector2(x, y + 1)): room.exits["south"] = "room_%d_%d" % [x, y + 1] + if occupied_coords.has(Vector2(x + 1, y)): room.exits["east"] = "room_%d_%d" % [x + 1, y] + if occupied_coords.has(Vector2(x - 1, y)): room.exits["west"] = "room_%d_%d" % [x - 1, y] + + # Populate items/enemies + _populate_world(rooms) + + return rooms + +static func _create_room(x: int, y: int) -> RoomData: + var room = RoomData.new() + room.id = "room_%d_%d" % [x, y] + room.room_name = "Room %d,%d" % [x, y] + room.description = "A dark, stone-walled room at coordinates %d, %d." % [x, y] + room.exits = {} + return room + +static func _populate_world(rooms: Dictionary): + var room_ids = rooms.keys() + + # Add a sword to a random room (excluding 0,0 if possible, but random is fine) + var sword_room_id = room_ids[randi() % room_ids.size()] + var sword_room = rooms[sword_room_id] + + var sword = ItemData.new() + sword.id = "sword" + sword.name = "Rusty Sword" + sword.description = "A rusty old sword." + sword.effect_type = "DAMAGE" + sword.effect_value = 10 + sword_room.items.append(sword) + + # Add some enemies + var num_enemies = rooms.size() / 3 + for i in range(num_enemies): + var enemy_room_id = room_ids[randi() % room_ids.size()] + # Avoid putting enemy in start room (0,0) + if enemy_room_id == "room_0_0": + continue + + var enemy_room = rooms[enemy_room_id] + if enemy_room.enemies.size() == 0: # One enemy per room for now + var goblin = EnemyData.new() + goblin.id = "goblin" + goblin.name = "Goblin" + goblin.hp = 30 + goblin.max_hp = 30 + goblin.damage = 5 + enemy_room.enemies.append(goblin) + static func generate_test_world() -> Dictionary: var rooms = {} for x in range(4): diff --git a/scripts/main.gd b/scripts/main.gd index a274f8e..c81ccef 100644 --- a/scripts/main.gd +++ b/scripts/main.gd @@ -1,18 +1,18 @@ extends Node2D @onready var game_manager = $GameManager -@onready var room_label = $UI/MainLayout/Sidebar/RoomLabel -@onready var log_label = $UI/MainLayout/GameView/Log -@onready var input_field = $UI/MainLayout/GameView/Input -@onready var minimap = $UI/MainLayout/Sidebar/Minimap -@onready var stats_label = $UI/MainLayout/Sidebar/StatsLabel +@onready var room_label = $UI/MainLayout/TopPanel/NarrativePanel/VBox/RoomLabel +@onready var log_label = $UI/MainLayout/TopPanel/NarrativePanel/VBox/Log +@onready var input_field = $UI/MainLayout/TopPanel/NarrativePanel/VBox/Input +@onready var minimap = $UI/MainLayout/ControlDeck/HBox/MinimapPanel/Minimap +@onready var stats_label = $UI/MainLayout/ControlDeck/HBox/StatsPanel/StatsLabel -@onready var btn_north = $UI/MainLayout/Sidebar/Controls/BtnNorth -@onready var btn_south = $UI/MainLayout/Sidebar/Controls/BtnSouth -@onready var btn_east = $UI/MainLayout/Sidebar/Controls/HBox/BtnEast -@onready var btn_west = $UI/MainLayout/Sidebar/Controls/HBox/BtnWest -@onready var btn_save = $UI/MainLayout/Sidebar/Controls/BtnSave -@onready var btn_load = $UI/MainLayout/Sidebar/Controls/BtnLoad +@onready var btn_north = $UI/MainLayout/ControlDeck/HBox/ActionsPanel/BtnNorth +@onready var btn_south = $UI/MainLayout/ControlDeck/HBox/ActionsPanel/BtnSouth +@onready var btn_east = $UI/MainLayout/ControlDeck/HBox/ActionsPanel/HBox/BtnEast +@onready var btn_west = $UI/MainLayout/ControlDeck/HBox/ActionsPanel/HBox/BtnWest +@onready var btn_save = $UI/MainLayout/ControlDeck/HBox/SystemPanel/BtnSave +@onready var btn_load = $UI/MainLayout/ControlDeck/HBox/SystemPanel/BtnLoad @onready var combat_ui = $UI/CombatUI @onready var main_layout = $UI/MainLayout