265 lines
8.1 KiB
GDScript
265 lines
8.1 KiB
GDScript
class_name ActionLog
|
|
extends Control
|
|
|
|
## ActionLog - Collapsible panel showing game actions
|
|
|
|
signal undo_requested
|
|
|
|
# UI Components
|
|
var panel: PanelContainer
|
|
var toggle_button: Button
|
|
var scroll_container: ScrollContainer
|
|
var log_container: VBoxContainer
|
|
var undo_button: Button
|
|
var clear_button: Button
|
|
|
|
# State
|
|
var is_expanded: bool = true
|
|
var action_entries: Array[Control] = []
|
|
|
|
# Layout constants
|
|
const COLLAPSED_WIDTH: float = 40.0
|
|
const EXPANDED_WIDTH: float = 280.0
|
|
const PANEL_HEIGHT: float = 400.0
|
|
|
|
func _ready() -> void:
|
|
_create_ui()
|
|
_connect_signals()
|
|
|
|
func _create_ui() -> void:
|
|
# Set up this control to anchor to right side
|
|
set_anchors_preset(Control.PRESET_CENTER_RIGHT)
|
|
custom_minimum_size = Vector2(EXPANDED_WIDTH, PANEL_HEIGHT)
|
|
size = Vector2(EXPANDED_WIDTH, PANEL_HEIGHT)
|
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
|
|
# Main horizontal container
|
|
var hbox = HBoxContainer.new()
|
|
add_child(hbox)
|
|
hbox.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
hbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
hbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
|
|
# Toggle button on the left edge
|
|
toggle_button = Button.new()
|
|
toggle_button.text = ">"
|
|
toggle_button.custom_minimum_size = Vector2(30, 0)
|
|
toggle_button.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
toggle_button.tooltip_text = "Toggle Action Log (L)"
|
|
hbox.add_child(toggle_button)
|
|
|
|
# Panel container for the log
|
|
panel = PanelContainer.new()
|
|
hbox.add_child(panel)
|
|
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
|
|
var style = StyleBoxFlat.new()
|
|
style.bg_color = Color(0.08, 0.08, 0.12, 0.95)
|
|
style.border_color = Color(0.3, 0.3, 0.4)
|
|
style.set_border_width_all(2)
|
|
style.set_corner_radius_all(5)
|
|
style.set_content_margin_all(8)
|
|
panel.add_theme_stylebox_override("panel", style)
|
|
|
|
# Panel content
|
|
var panel_vbox = VBoxContainer.new()
|
|
panel.add_child(panel_vbox)
|
|
panel_vbox.add_theme_constant_override("separation", 5)
|
|
|
|
# Header
|
|
var header = HBoxContainer.new()
|
|
panel_vbox.add_child(header)
|
|
header.add_theme_constant_override("separation", 5)
|
|
|
|
var title = Label.new()
|
|
title.text = "Action Log"
|
|
title.add_theme_font_size_override("font_size", 14)
|
|
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
header.add_child(title)
|
|
|
|
# Undo button
|
|
undo_button = Button.new()
|
|
undo_button.text = "Undo"
|
|
undo_button.custom_minimum_size = Vector2(50, 25)
|
|
undo_button.add_theme_font_size_override("font_size", 11)
|
|
undo_button.tooltip_text = "Undo last action (Ctrl+Z)"
|
|
undo_button.disabled = true
|
|
header.add_child(undo_button)
|
|
|
|
# Clear button
|
|
clear_button = Button.new()
|
|
clear_button.text = "Clear"
|
|
clear_button.custom_minimum_size = Vector2(45, 25)
|
|
clear_button.add_theme_font_size_override("font_size", 11)
|
|
header.add_child(clear_button)
|
|
|
|
# Separator
|
|
var separator = HSeparator.new()
|
|
panel_vbox.add_child(separator)
|
|
|
|
# Scroll container for log entries
|
|
scroll_container = ScrollContainer.new()
|
|
panel_vbox.add_child(scroll_container)
|
|
scroll_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
scroll_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
scroll_container.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
|
scroll_container.custom_minimum_size = Vector2(200, 250)
|
|
|
|
# Log entries container
|
|
log_container = VBoxContainer.new()
|
|
scroll_container.add_child(log_container)
|
|
log_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
log_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
log_container.add_theme_constant_override("separation", 3)
|
|
|
|
func _connect_signals() -> void:
|
|
toggle_button.pressed.connect(_on_toggle_pressed)
|
|
undo_button.pressed.connect(_on_undo_pressed)
|
|
clear_button.pressed.connect(clear_log)
|
|
|
|
# Defer GameManager connections to ensure it's ready
|
|
call_deferred("_connect_game_manager_signals")
|
|
|
|
func _connect_game_manager_signals() -> void:
|
|
# Connect to GameManager signals
|
|
if GameManager:
|
|
if not GameManager.message.is_connected(_on_game_message):
|
|
GameManager.message.connect(_on_game_message)
|
|
if not GameManager.card_played.is_connected(_on_card_played):
|
|
GameManager.card_played.connect(_on_card_played)
|
|
if not GameManager.turn_changed.is_connected(_on_turn_changed):
|
|
GameManager.turn_changed.connect(_on_turn_changed)
|
|
if not GameManager.phase_changed.is_connected(_on_phase_changed):
|
|
GameManager.phase_changed.connect(_on_phase_changed)
|
|
if not GameManager.damage_dealt.is_connected(_on_damage_dealt):
|
|
GameManager.damage_dealt.connect(_on_damage_dealt)
|
|
if not GameManager.action_undone.is_connected(_on_action_undone):
|
|
GameManager.action_undone.connect(_on_action_undone)
|
|
|
|
func _on_toggle_pressed() -> void:
|
|
toggle_panel()
|
|
|
|
func toggle_panel() -> void:
|
|
is_expanded = not is_expanded
|
|
panel.visible = is_expanded
|
|
toggle_button.text = ">" if is_expanded else "<"
|
|
|
|
if is_expanded:
|
|
custom_minimum_size.x = EXPANDED_WIDTH
|
|
size.x = EXPANDED_WIDTH
|
|
else:
|
|
custom_minimum_size.x = COLLAPSED_WIDTH
|
|
size.x = COLLAPSED_WIDTH
|
|
|
|
func _on_undo_pressed() -> void:
|
|
undo_requested.emit()
|
|
|
|
## Add a log entry with a specific type for styling
|
|
func add_entry(text: String, entry_type: String = "info") -> void:
|
|
if not log_container:
|
|
return
|
|
|
|
var entry = _create_entry(text, entry_type)
|
|
log_container.add_child(entry)
|
|
action_entries.append(entry)
|
|
|
|
# Keep log at reasonable size
|
|
while action_entries.size() > 100:
|
|
var old_entry = action_entries.pop_front()
|
|
old_entry.queue_free()
|
|
|
|
# Scroll to bottom after adding
|
|
call_deferred("_scroll_to_bottom")
|
|
|
|
func _create_entry(text: String, entry_type: String) -> Control:
|
|
var entry_panel = PanelContainer.new()
|
|
|
|
var style = StyleBoxFlat.new()
|
|
style.set_content_margin_all(4)
|
|
style.set_corner_radius_all(3)
|
|
|
|
# Color based on type
|
|
match entry_type:
|
|
"turn":
|
|
style.bg_color = Color(0.2, 0.3, 0.4, 0.8)
|
|
"phase":
|
|
style.bg_color = Color(0.15, 0.25, 0.35, 0.6)
|
|
"action":
|
|
style.bg_color = Color(0.2, 0.2, 0.3, 0.7)
|
|
"damage":
|
|
style.bg_color = Color(0.4, 0.15, 0.15, 0.7)
|
|
"card":
|
|
style.bg_color = Color(0.15, 0.3, 0.2, 0.7)
|
|
_:
|
|
style.bg_color = Color(0.12, 0.12, 0.18, 0.5)
|
|
|
|
entry_panel.add_theme_stylebox_override("panel", style)
|
|
|
|
var label = Label.new()
|
|
label.text = text
|
|
label.add_theme_font_size_override("font_size", 11)
|
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
|
label.custom_minimum_size.x = 200
|
|
entry_panel.add_child(label)
|
|
|
|
return entry_panel
|
|
|
|
func _scroll_to_bottom() -> void:
|
|
await get_tree().process_frame
|
|
scroll_container.scroll_vertical = int(scroll_container.get_v_scroll_bar().max_value)
|
|
|
|
func clear_log() -> void:
|
|
for entry in action_entries:
|
|
entry.queue_free()
|
|
action_entries.clear()
|
|
|
|
## Enable or disable undo button
|
|
func set_undo_available(available: bool) -> void:
|
|
undo_button.disabled = not available
|
|
|
|
## Manually add a message (can be called from Main.gd if signal connection fails)
|
|
func log_message(text: String) -> void:
|
|
_on_game_message(text)
|
|
|
|
## Manually log a turn change
|
|
func log_turn_change(player_name: String, turn_number: int) -> void:
|
|
_on_turn_changed(player_name, turn_number)
|
|
|
|
## Manually log a phase change
|
|
func log_phase_change(phase_name: String) -> void:
|
|
_on_phase_changed(phase_name)
|
|
|
|
## Signal handlers
|
|
func _on_game_message(text: String) -> void:
|
|
# Determine entry type based on message content
|
|
var entry_type = "info"
|
|
if "attacks" in text or "blocks" in text:
|
|
entry_type = "action"
|
|
elif "damage" in text or "broken" in text:
|
|
entry_type = "damage"
|
|
elif "Played" in text or "Discarded" in text or "Dulled" in text:
|
|
entry_type = "card"
|
|
|
|
add_entry(text, entry_type)
|
|
|
|
func _on_card_played(_card_data: Dictionary) -> void:
|
|
# Already handled by message signal
|
|
pass
|
|
|
|
func _on_turn_changed(player_name: String, turn_number: int) -> void:
|
|
add_entry("=== " + player_name + " - Turn " + str(turn_number) + " ===", "turn")
|
|
|
|
func _on_phase_changed(phase_name: String) -> void:
|
|
add_entry(phase_name, "phase")
|
|
|
|
func _on_damage_dealt(_player_name: String, _amount: int) -> void:
|
|
# Already handled by message signal
|
|
pass
|
|
|
|
func _on_action_undone(action_name: String) -> void:
|
|
add_entry("UNDO: " + action_name, "action")
|
|
|
|
# Input is handled by Main.gd to avoid duplicate handling
|