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:
2025-11-16 23:11:51 +00:00
parent 15865dd15f
commit aba42e2a02
10 changed files with 382 additions and 37 deletions
+28 -26
View File
@@ -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():
+46
View File
@@ -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()
+1
View File
@@ -0,0 +1 @@
uid://brfqjw57ypd3g
+14
View File
@@ -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()
+98
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
uid://ev146dh8du3y