Add escape menu and weapon slot tooltips to character sheet
- Escape Menu: - Opens with Escape key from base game screen - Resume button to return to game - Exit Game button to quit application - Character sheet closes with Escape if open (takes priority) - Mouse capture/release handled by UI components - Weapon Slot System: - Visual weapon boxes (80x80) in character sheet - Display weapon icons from WeaponData - Empty slots appear dimmed - Hover tooltips show weapon stats (not clipped by containers) - Tooltips display: slot name, weapon name, damage, range, cooldown, knockback, block percentage - Tooltips positioned globally to avoid ScrollContainer clipping - Bug Fixes: - Fixed typo in lilguy_body.gd (dwextends -> extends) - Removed manual mouse toggle from player (now handled by UI) - Updated character_sheet.gd to use weapon slot components instead of text labels - Added weapon_tooltip.tscn as separate scene for unclipped tooltips
This commit is contained in:
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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():
|
||||
|
||||
@@ -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()
|
||||
@@ -0,0 +1 @@
|
||||
uid://brfqjw57ypd3g
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://ev146dh8du3y
|
||||
Reference in New Issue
Block a user