You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
7.0 KiB
220 lines
7.0 KiB
extends Control |
|
class_name CharacterSheet |
|
## Character sheet / spellbook that displays player stats, weapons, and abilities |
|
## Opens with Tab key |
|
|
|
# References |
|
@onready var stats_container: VBoxContainer = $Panel/MarginContainer/VBoxContainer/HBoxContainer/LeftPage/ScrollContainer/StatsContainer |
|
@onready var weapons_container: VBoxContainer = $Panel/MarginContainer/VBoxContainer/HBoxContainer/RightPage/ScrollContainer/ContentContainer/WeaponsContainer |
|
@onready var abilities_container: VBoxContainer = $Panel/MarginContainer/VBoxContainer/HBoxContainer/RightPage/ScrollContainer/ContentContainer/AbilitiesContainer |
|
|
|
# Player reference |
|
var player: Character = null |
|
|
|
# Visibility |
|
var is_visible: bool = false |
|
|
|
func _ready(): |
|
# Start hidden |
|
hide() |
|
is_visible = false |
|
|
|
func _input(event): |
|
# Toggle on Tab press |
|
if event.is_action_pressed("toggle_character_sheet"): |
|
toggle_sheet() |
|
get_viewport().set_input_as_handled() |
|
|
|
## Set the player reference and update display |
|
func set_player(p: Character): |
|
player = p |
|
if player: |
|
refresh_all() |
|
|
|
## Toggle character sheet visibility |
|
func toggle_sheet(): |
|
is_visible = !is_visible |
|
if is_visible: |
|
show() |
|
refresh_all() |
|
# Release mouse when opening sheet |
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE |
|
else: |
|
hide() |
|
# Recapture mouse when closing sheet |
|
if player and player.is_multiplayer_authority(): |
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED |
|
|
|
## Refresh all data in the sheet |
|
func refresh_all(): |
|
if not player: |
|
return |
|
|
|
_refresh_stats() |
|
_refresh_weapons() |
|
_refresh_abilities() |
|
|
|
## Refresh player stats |
|
func _refresh_stats(): |
|
if not stats_container: |
|
return |
|
|
|
# Clear existing stats |
|
for child in stats_container.get_children(): |
|
child.queue_free() |
|
|
|
# Add title |
|
var title = Label.new() |
|
title.text = "CHARACTER STATS" |
|
title.add_theme_font_size_override("font_size", 24) |
|
title.add_theme_color_override("font_color", Color(1.0, 0.8, 0.0)) |
|
stats_container.add_child(title) |
|
|
|
# Spacer |
|
var spacer1 = Control.new() |
|
spacer1.custom_minimum_size = Vector2(0, 10) |
|
stats_container.add_child(spacer1) |
|
|
|
# Get player name |
|
var player_id = player.name.to_int() |
|
var player_name = "Player" |
|
if Network.players.has(player_id): |
|
player_name = Network.players[player_id]["nick"] |
|
|
|
_add_stat_label("Name: " + player_name) |
|
_add_stat_label("Level: 1") # Hardcoded for now |
|
_add_stat_label("") |
|
|
|
# Health stats |
|
_add_stat_label("=== HEALTH ===", Color(0.8, 0.8, 0.8)) |
|
_add_stat_label("Current HP: " + str(int(player.current_health))) |
|
_add_stat_label("Max HP: " + str(int(player.max_health))) |
|
_add_stat_label("Health: " + str(int(player.get_health_percent() * 100)) + "%") |
|
_add_stat_label("") |
|
|
|
# Movement stats |
|
_add_stat_label("=== MOVEMENT ===", Color(0.8, 0.8, 0.8)) |
|
_add_stat_label("Walk Speed: " + str(player.NORMAL_SPEED)) |
|
_add_stat_label("Sprint Speed: " + str(player.SPRINT_SPEED)) |
|
_add_stat_label("Jump Power: " + str(player.JUMP_VELOCITY)) |
|
_add_stat_label("") |
|
|
|
# Combat stats |
|
_add_stat_label("=== COMBAT ===", Color(0.8, 0.8, 0.8)) |
|
_add_stat_label("Base Damage: " + str(player.attack_damage)) |
|
_add_stat_label("Attack Range: " + str(player.attack_range)) |
|
_add_stat_label("Attack Cooldown: " + str(player.attack_cooldown) + "s") |
|
|
|
## Refresh equipped weapons |
|
func _refresh_weapons(): |
|
if not weapons_container: |
|
return |
|
|
|
# Clear existing |
|
for child in weapons_container.get_children(): |
|
child.queue_free() |
|
|
|
# Add title |
|
var title = Label.new() |
|
title.text = "EQUIPPED WEAPONS" |
|
title.add_theme_font_size_override("font_size", 20) |
|
title.add_theme_color_override("font_color", Color(1.0, 0.8, 0.0)) |
|
weapons_container.add_child(title) |
|
|
|
# Spacer |
|
var spacer = Control.new() |
|
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)) |
|
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)) + "%") |
|
else: |
|
_add_stat_label("No weapon equipped", Color(0.7, 0.7, 0.7)) |
|
|
|
# Spacer |
|
var spacer2 = Control.new() |
|
spacer2.custom_minimum_size = Vector2(0, 10) |
|
weapons_container.add_child(spacer2) |
|
|
|
# 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)) + "%") |
|
else: |
|
_add_stat_label("No weapon equipped", Color(0.7, 0.7, 0.7)) |
|
|
|
## Refresh abilities list |
|
func _refresh_abilities(): |
|
if not abilities_container: |
|
return |
|
|
|
# Clear existing |
|
for child in abilities_container.get_children(): |
|
child.queue_free() |
|
|
|
# Add title |
|
var title = Label.new() |
|
title.text = "ABILITIES" |
|
title.add_theme_font_size_override("font_size", 20) |
|
title.add_theme_color_override("font_color", Color(1.0, 0.8, 0.0)) |
|
abilities_container.add_child(title) |
|
|
|
# Spacer |
|
var spacer = Control.new() |
|
spacer.custom_minimum_size = Vector2(0, 10) |
|
abilities_container.add_child(spacer) |
|
|
|
# Dash ability |
|
_add_stat_label("--- DASH (F) ---", Color(0.8, 0.8, 0.8)) |
|
_add_stat_label("Cooldown: " + str(player.dash_cooldown) + "s") |
|
_add_stat_label("Duration: " + str(player.dash_duration) + "s") |
|
_add_stat_label("Speed: " + str(player.dash_speed_multiplier) + "x") |
|
_add_stat_label("Description: Dash in movement direction") |
|
var dash_remaining = player._dash_cooldown_timer |
|
if dash_remaining > 0: |
|
_add_stat_label("Ready in: " + str(ceil(dash_remaining)) + "s", Color(1.0, 0.5, 0.5)) |
|
else: |
|
_add_stat_label("Status: READY", Color(0.0, 1.0, 0.0)) |
|
|
|
# Spacer |
|
var spacer2 = Control.new() |
|
spacer2.custom_minimum_size = Vector2(0, 10) |
|
abilities_container.add_child(spacer2) |
|
|
|
# Jump ability |
|
_add_stat_label("--- JUMP (Space) ---", Color(0.8, 0.8, 0.8)) |
|
_add_stat_label("Power: " + str(player.JUMP_VELOCITY)) |
|
_add_stat_label("Description: Jump into the air") |
|
_add_stat_label("Status: ALWAYS READY", Color(0.0, 1.0, 0.0)) |
|
|
|
## Helper to add a stat label |
|
func _add_stat_label(text: String, color: Color = Color.WHITE): |
|
var label = Label.new() |
|
label.text = text |
|
label.add_theme_color_override("font_color", color) |
|
label.add_theme_color_override("font_outline_color", Color.BLACK) |
|
label.add_theme_constant_override("outline_size", 1) |
|
label.add_theme_font_size_override("font_size", 16) |
|
|
|
if stats_container: |
|
stats_container.add_child(label) |
|
elif weapons_container: |
|
weapons_container.add_child(label) |
|
elif abilities_container: |
|
abilities_container.add_child(label)
|
|
|