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:
@@ -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