diff --git a/level/scripts/player.gd b/level/scripts/player.gd index 72b7633..e04f494 100644 --- a/level/scripts/player.gd +++ b/level/scripts/player.gd @@ -213,17 +213,6 @@ func _process(delta): _check_fall_and_respawn() - # Handle mouse capture toggle (Escape to release, click to recapture) - if Input.is_action_just_pressed("quit"): - if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: - Input.mouse_mode = Input.MOUSE_MODE_VISIBLE - else: - Input.mouse_mode = Input.MOUSE_MODE_CAPTURED - - # Recapture mouse on click if it was released - if Input.is_action_just_pressed("attack") and Input.mouse_mode != Input.MOUSE_MODE_CAPTURED: - Input.mouse_mode = Input.MOUSE_MODE_CAPTURED - # Update attack cooldown if _attack_timer > 0: _attack_timer -= delta diff --git a/level/ui/scenes/escape_menu.tscn b/level/ui/scenes/escape_menu.tscn new file mode 100644 index 0000000..400c502 --- /dev/null +++ b/level/ui/scenes/escape_menu.tscn @@ -0,0 +1,106 @@ +[gd_scene load_steps=4 format=3 uid="uid://cepm3qkjr8h1h"] + +[ext_resource type="Script" path="res://level/ui/scripts/escape_menu.gd" id="1_script"] +[ext_resource type="Theme" uid="uid://dvsh7tuhulnfm" path="res://level/ui/theme/wow_style.tres" id="2_theme"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_menu"] +bg_color = Color(0.12, 0.1, 0.08, 0.95) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +border_color = Color(0.6, 0.5, 0.2, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[node name="EscapeMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("2_theme") +script = ExtResource("1_script") + +[node name="DarkBackground" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.7) + +[node name="Panel" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -150.0 +offset_right = 200.0 +offset_bottom = 150.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_menu") + +[node name="MarginContainer" type="MarginContainer" parent="Panel"] +layout_mode = 2 +theme_override_constants/margin_left = 30 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_right = 30 +theme_override_constants/margin_bottom = 30 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 20 + +[node name="TitleLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(1, 0.8, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 32 +text = "MENU" +horizontal_alignment = 1 + +[node name="Spacer" type="Control" parent="Panel/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 20) +layout_mode = 2 + +[node name="ResumeButton" type="Button" parent="Panel/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 1, 1) +theme_override_colors/font_hover_color = Color(1, 0.8, 0, 1) +theme_override_font_sizes/font_size = 20 +text = "Resume" + +[node name="ExitButton" type="Button" parent="Panel/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 1, 1) +theme_override_colors/font_hover_color = Color(1, 0.8, 0, 1) +theme_override_font_sizes/font_size = 20 +text = "Exit Game" + +[node name="Spacer2" type="Control" parent="Panel/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="HintLabel" type="Label" parent="Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.7, 0.7, 0.7, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 1 +theme_override_font_sizes/font_size = 14 +text = "Press ESC to close" +horizontal_alignment = 1 + +[connection signal="pressed" from="Panel/MarginContainer/VBoxContainer/ResumeButton" to="." method="_on_resume_pressed"] +[connection signal="pressed" from="Panel/MarginContainer/VBoxContainer/ExitButton" to="." method="_on_exit_pressed"] diff --git a/level/ui/scenes/weapon_slot.tscn b/level/ui/scenes/weapon_slot.tscn new file mode 100644 index 0000000..78e7c01 --- /dev/null +++ b/level/ui/scenes/weapon_slot.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=4 format=3 uid="uid://dnwg8b7xmh5qa"] + +[ext_resource type="Script" path="res://level/ui/scripts/weapon_slot.gd" id="1_script"] +[ext_resource type="Theme" uid="uid://dvsh7tuhulnfm" path="res://level/ui/theme/wow_style.tres" id="2_theme"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_slot"] +bg_color = Color(0.15, 0.15, 0.15, 0.9) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.6, 0.5, 0.2, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[node name="WeaponSlot" type="Control"] +custom_minimum_size = Vector2(80, 80) +mouse_filter = 0 +theme = ExtResource("2_theme") +script = ExtResource("1_script") + +[node name="SlotPanel" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_slot") + +[node name="MarginContainer" type="MarginContainer" parent="SlotPanel"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="IconRect" type="TextureRect" parent="SlotPanel/MarginContainer"] +layout_mode = 2 +expand_mode = 1 +stretch_mode = 5 diff --git a/level/ui/scenes/weapon_tooltip.tscn b/level/ui/scenes/weapon_tooltip.tscn new file mode 100644 index 0000000..f101053 --- /dev/null +++ b/level/ui/scenes/weapon_tooltip.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=3 format=3 uid="uid://ck5y3rmj8vx2p"] + +[ext_resource type="Theme" uid="uid://dvsh7tuhulnfm" path="res://level/ui/theme/wow_style.tres" id="1_theme"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tooltip"] +bg_color = Color(0.15, 0.15, 0.15, 0.95) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.6, 0.5, 0.2, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[node name="WeaponTooltip" type="PanelContainer"] +z_index = 1000 +offset_right = 200.0 +offset_bottom = 150.0 +theme = ExtResource("1_theme") +theme_override_styles/panel = SubResource("StyleBoxFlat_tooltip") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="StatsLabel" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 1, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 1 +theme_override_font_sizes/font_size = 14 +text = "Weapon Name +--- +Damage: 10 +Range: 3 +Cooldown: 0.5s" diff --git a/level/ui/scripts/character_sheet.gd b/level/ui/scripts/character_sheet.gd index a9b0fe6..6b03813 100644 --- a/level/ui/scripts/character_sheet.gd +++ b/level/ui/scripts/character_sheet.gd @@ -20,6 +20,12 @@ func _ready(): is_visible = false func _input(event): + # Close on Escape press if open + if event.is_action_pressed("quit") and is_visible: + toggle_sheet() + get_viewport().set_input_as_handled() + return + # Toggle on Tab press if event.is_action_pressed("toggle_character_sheet"): toggle_sheet() @@ -126,38 +132,34 @@ func _refresh_weapons(): spacer.custom_minimum_size = Vector2(0, 10) weapons_container.add_child(spacer) - # Main hand weapon - _add_stat_label("--- MAIN HAND ---", Color(0.8, 0.8, 0.8)) + # Create horizontal container for weapon slots + var slots_container = HBoxContainer.new() + slots_container.add_theme_constant_override("separation", 15) + weapons_container.add_child(slots_container) + + # Load weapon slot scene + var weapon_slot_scene = load("res://level/ui/scenes/weapon_slot.tscn") + if not weapon_slot_scene: + push_error("[CharacterSheet] Failed to load weapon_slot.tscn") + return + + # Create main hand slot + var main_hand_slot = weapon_slot_scene.instantiate() + slots_container.add_child(main_hand_slot) + if player.equipped_weapon and player.equipped_weapon.weapon_data: - var wd = player.equipped_weapon.weapon_data - _add_stat_label("Name: " + wd.weapon_name, Color(0.0, 1.0, 0.5)) - _add_stat_label("Damage: " + str(wd.damage)) - _add_stat_label("Range: " + str(wd.attack_range)) - _add_stat_label("Cooldown: " + str(wd.attack_cooldown) + "s") - _add_stat_label("Knockback: " + str(wd.knockback_force)) - if wd.can_block: - _add_stat_label("Block: " + str(int(wd.block_reduction * 100)) + "%") + main_hand_slot.set_weapon(player.equipped_weapon.weapon_data, "Main Hand") else: - _add_stat_label("No weapon equipped", Color(0.7, 0.7, 0.7)) + main_hand_slot.clear_weapon() - # Spacer - var spacer2 = Control.new() - spacer2.custom_minimum_size = Vector2(0, 10) - weapons_container.add_child(spacer2) + # Create off-hand slot + var off_hand_slot = weapon_slot_scene.instantiate() + slots_container.add_child(off_hand_slot) - # Off-hand weapon - _add_stat_label("--- OFF-HAND ---", Color(0.8, 0.8, 0.8)) if player.equipped_offhand and player.equipped_offhand.weapon_data: - var wd = player.equipped_offhand.weapon_data - _add_stat_label("Name: " + wd.weapon_name, Color(0.0, 1.0, 0.5)) - _add_stat_label("Damage: " + str(wd.damage)) - _add_stat_label("Range: " + str(wd.attack_range)) - _add_stat_label("Cooldown: " + str(wd.attack_cooldown) + "s") - _add_stat_label("Knockback: " + str(wd.knockback_force)) - if wd.can_block: - _add_stat_label("Block: " + str(int(wd.block_reduction * 100)) + "%") + off_hand_slot.set_weapon(player.equipped_offhand.weapon_data, "Off-Hand") else: - _add_stat_label("No weapon equipped", Color(0.7, 0.7, 0.7)) + off_hand_slot.clear_weapon() ## Refresh abilities list func _refresh_abilities(): diff --git a/level/ui/scripts/escape_menu.gd b/level/ui/scripts/escape_menu.gd new file mode 100644 index 0000000..11ef55b --- /dev/null +++ b/level/ui/scripts/escape_menu.gd @@ -0,0 +1,46 @@ +extends Control +class_name EscapeMenu +## Escape menu that opens when Escape is pressed +## Allows player to exit the game or resume playing + +# Visibility +var is_visible: bool = false + +# Player reference (to control mouse mode) +var player: Character = null + +func _ready(): + # Start hidden + hide() + is_visible = false + +func _input(event): + # Toggle on Escape press + if event.is_action_pressed("quit"): + toggle_menu() + get_viewport().set_input_as_handled() + +## Set the player reference +func set_player(p: Character): + player = p + +## Toggle menu visibility +func toggle_menu(): + is_visible = !is_visible + if is_visible: + show() + # Release mouse when opening menu + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + else: + hide() + # Recapture mouse when closing menu + if player and player.is_multiplayer_authority(): + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + +## Called when Resume button is clicked +func _on_resume_pressed(): + toggle_menu() + +## Called when Exit button is clicked +func _on_exit_pressed(): + get_tree().quit() diff --git a/level/ui/scripts/escape_menu.gd.uid b/level/ui/scripts/escape_menu.gd.uid new file mode 100644 index 0000000..7bc5205 --- /dev/null +++ b/level/ui/scripts/escape_menu.gd.uid @@ -0,0 +1 @@ +uid://brfqjw57ypd3g diff --git a/level/ui/scripts/hud_manager.gd b/level/ui/scripts/hud_manager.gd index 84c7b20..4b3118c 100644 --- a/level/ui/scripts/hud_manager.gd +++ b/level/ui/scripts/hud_manager.gd @@ -8,6 +8,7 @@ var unit_frame: Control = null var target_frame: Control = null var character_sheet: Control = null var tab_hint: Control = null +var escape_menu: Control = null # Player reference var local_player: Character = null @@ -76,6 +77,7 @@ func _create_ui_components(): _create_unit_frame() _create_character_sheet() _create_tab_hint() + _create_escape_menu() ## Create action bar at bottom of screen func _create_action_bar(): @@ -123,6 +125,18 @@ func _create_tab_hint(): else: push_error("[HUD] Failed to load tab_hint.tscn") +## Create escape menu (toggle with Escape) +func _create_escape_menu(): + var escape_menu_scene = load("res://level/ui/scenes/escape_menu.tscn") + if escape_menu_scene: + escape_menu = escape_menu_scene.instantiate() + add_child(escape_menu) + if escape_menu and local_player: + escape_menu.set_player(local_player) + print("[HUD] Escape menu created") + else: + push_error("[HUD] Failed to load escape_menu.tscn") + ## Show all UI components func show_hud(): show() diff --git a/level/ui/scripts/weapon_slot.gd b/level/ui/scripts/weapon_slot.gd new file mode 100644 index 0000000..69fc243 --- /dev/null +++ b/level/ui/scripts/weapon_slot.gd @@ -0,0 +1,98 @@ +extends Control +class_name WeaponSlot +## Weapon slot that displays weapon icon and shows tooltip on hover + +# References +@onready var icon_rect: TextureRect = $SlotPanel/MarginContainer/IconRect +var tooltip: PanelContainer = null + +# Weapon data +var weapon_data: WeaponData = null +var slot_name: String = "Main Hand" + +func _ready(): + # Create tooltip as a top-level CanvasLayer child so it's not clipped + _create_tooltip() + + # Connect mouse events + mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) + +## Create tooltip as a top-level node to avoid clipping +func _create_tooltip(): + # Load tooltip scene template + var tooltip_scene = load("res://level/ui/scenes/weapon_tooltip.tscn") + if tooltip_scene: + tooltip = tooltip_scene.instantiate() + # Add to the root CanvasLayer (HUD) so it's not clipped + var hud = get_node_or_null("/root/HUD") + if hud: + hud.add_child(tooltip) + tooltip.hide() + else: + push_error("[WeaponSlot] Could not find HUD node") + else: + push_error("[WeaponSlot] Failed to load weapon_tooltip.tscn") + +## Set weapon data and update display +func set_weapon(data: WeaponData, name: String = "Main Hand"): + weapon_data = data + slot_name = name + _update_display() + +## Clear the weapon slot +func clear_weapon(): + weapon_data = null + _update_display() + +## Update the icon display +func _update_display(): + if not icon_rect: + return + + if weapon_data and weapon_data.icon: + icon_rect.texture = weapon_data.icon + icon_rect.modulate = Color.WHITE + else: + icon_rect.texture = null + icon_rect.modulate = Color(0.3, 0.3, 0.3, 0.5) # Dim empty slot + +## Show tooltip on mouse enter +func _on_mouse_entered(): + if weapon_data and tooltip: + _update_tooltip() + # Position tooltip to the right of the slot + var global_pos = global_position + tooltip.position = Vector2(global_pos.x + size.x + 10, global_pos.y) + tooltip.show() + +## Hide tooltip on mouse exit +func _on_mouse_exited(): + if tooltip: + tooltip.hide() + +## Update tooltip content +func _update_tooltip(): + if not tooltip or not weapon_data: + return + + var tooltip_label = tooltip.get_node_or_null("MarginContainer/VBoxContainer/StatsLabel") + if not tooltip_label: + return + + var text = "" + text += slot_name.to_upper() + "\n" + text += weapon_data.weapon_name + "\n" + text += "---\n" + text += "Damage: " + str(weapon_data.damage) + "\n" + text += "Range: " + str(weapon_data.attack_range) + "\n" + text += "Cooldown: " + str(weapon_data.attack_cooldown) + "s\n" + text += "Knockback: " + str(weapon_data.knockback_force) + "\n" + + if weapon_data.can_block: + text += "Block: " + str(int(weapon_data.block_reduction * 100)) + "%\n" + + if weapon_data.description: + text += "\n" + weapon_data.description + + tooltip_label.text = text diff --git a/level/ui/scripts/weapon_slot.gd.uid b/level/ui/scripts/weapon_slot.gd.uid new file mode 100644 index 0000000..608a40b --- /dev/null +++ b/level/ui/scripts/weapon_slot.gd.uid @@ -0,0 +1 @@ +uid://ev146dh8du3y