class_name GameUI extends CanvasLayer ## GameUI - Main UI overlay for game information and controls signal end_phase_pressed signal pass_priority_pressed # UI Components var turn_panel: PanelContainer var phase_panel: PanelContainer var cp_panel: PanelContainer var message_panel: PanelContainer var card_detail_panel: PanelContainer var action_buttons: HBoxContainer # Labels var turn_label: Label var phase_label: Label var cp_label: Label var message_label: Label # Card detail labels var detail_name_label: Label var detail_type_label: Label var detail_cost_label: Label var detail_power_label: Label var detail_element_label: Label var detail_ability_label: Label # Buttons var end_phase_button: Button var pass_button: Button # Message queue var message_queue: Array[String] = [] var message_timer: Timer const MESSAGE_DISPLAY_TIME: float = 3.0 func _ready() -> void: _create_ui() _connect_signals() func _create_ui() -> void: # Root control that fills the screen var root = Control.new() add_child(root) root.set_anchors_preset(Control.PRESET_FULL_RECT) root.mouse_filter = Control.MOUSE_FILTER_IGNORE # === TOP BAR === var top_bar = HBoxContainer.new() root.add_child(top_bar) top_bar.set_anchors_preset(Control.PRESET_TOP_WIDE) top_bar.offset_left = 10 top_bar.offset_right = -10 top_bar.offset_top = 10 top_bar.offset_bottom = 70 top_bar.add_theme_constant_override("separation", 20) # Turn panel (top left) turn_panel = _create_panel() top_bar.add_child(turn_panel) var turn_vbox = VBoxContainer.new() turn_panel.add_child(turn_vbox) var turn_header = Label.new() turn_header.text = "Turn" turn_header.add_theme_font_size_override("font_size", 12) turn_vbox.add_child(turn_header) turn_label = Label.new() turn_label.text = "Player 1 - Turn 1" turn_label.add_theme_font_size_override("font_size", 16) turn_vbox.add_child(turn_label) # Phase panel (top center-left) phase_panel = _create_panel() top_bar.add_child(phase_panel) var phase_vbox = VBoxContainer.new() phase_panel.add_child(phase_vbox) var phase_header = Label.new() phase_header.text = "Phase" phase_header.add_theme_font_size_override("font_size", 12) phase_vbox.add_child(phase_header) phase_label = Label.new() phase_label.text = "Active Phase" phase_label.add_theme_font_size_override("font_size", 16) phase_vbox.add_child(phase_label) # CP panel cp_panel = _create_panel() top_bar.add_child(cp_panel) var cp_vbox = VBoxContainer.new() cp_panel.add_child(cp_vbox) var cp_header = Label.new() cp_header.text = "CP Pool" cp_header.add_theme_font_size_override("font_size", 12) cp_vbox.add_child(cp_header) cp_label = Label.new() cp_label.text = "0 CP" cp_label.add_theme_font_size_override("font_size", 16) cp_vbox.add_child(cp_label) # Spacer to push buttons to the right var spacer = Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL top_bar.add_child(spacer) # Action buttons in top bar (right side) action_buttons = HBoxContainer.new() top_bar.add_child(action_buttons) action_buttons.add_theme_constant_override("separation", 10) pass_button = Button.new() pass_button.text = "Pass" pass_button.custom_minimum_size = Vector2(80, 40) action_buttons.add_child(pass_button) end_phase_button = Button.new() end_phase_button.text = "End Phase" end_phase_button.custom_minimum_size = Vector2(100, 40) action_buttons.add_child(end_phase_button) # === MESSAGE PANEL (upper center of screen, below top bar) === # Use a container to properly center the message var message_container = Control.new() root.add_child(message_container) message_container.set_anchors_preset(Control.PRESET_TOP_WIDE) message_container.offset_top = 90 message_container.offset_bottom = 160 message_container.mouse_filter = Control.MOUSE_FILTER_IGNORE var message_center = CenterContainer.new() message_container.add_child(message_center) message_center.set_anchors_preset(Control.PRESET_FULL_RECT) message_center.mouse_filter = Control.MOUSE_FILTER_IGNORE message_panel = _create_panel() message_center.add_child(message_panel) message_panel.custom_minimum_size = Vector2(350, 50) message_label = Label.new() message_label.text = "" message_label.add_theme_font_size_override("font_size", 20) message_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER message_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER message_panel.add_child(message_label) message_panel.visible = false # === CARD DETAIL PANEL (right side) === card_detail_panel = _create_panel() root.add_child(card_detail_panel) card_detail_panel.set_anchors_preset(Control.PRESET_CENTER_RIGHT) card_detail_panel.offset_left = -220 card_detail_panel.offset_right = -10 card_detail_panel.offset_top = -150 card_detail_panel.offset_bottom = 150 card_detail_panel.visible = false var detail_vbox = VBoxContainer.new() card_detail_panel.add_child(detail_vbox) detail_vbox.add_theme_constant_override("separation", 5) detail_name_label = Label.new() detail_name_label.text = "Card Name" detail_name_label.add_theme_font_size_override("font_size", 18) detail_vbox.add_child(detail_name_label) var separator = HSeparator.new() detail_vbox.add_child(separator) detail_type_label = Label.new() detail_type_label.text = "Type: Forward" detail_vbox.add_child(detail_type_label) detail_element_label = Label.new() detail_element_label.text = "Element: Fire" detail_vbox.add_child(detail_element_label) detail_cost_label = Label.new() detail_cost_label.text = "Cost: 3" detail_vbox.add_child(detail_cost_label) detail_power_label = Label.new() detail_power_label.text = "Power: 7000" detail_vbox.add_child(detail_power_label) var separator2 = HSeparator.new() detail_vbox.add_child(separator2) var ability_header = Label.new() ability_header.text = "Abilities:" ability_header.add_theme_font_size_override("font_size", 12) detail_vbox.add_child(ability_header) detail_ability_label = Label.new() detail_ability_label.text = "" detail_ability_label.autowrap_mode = TextServer.AUTOWRAP_WORD detail_ability_label.custom_minimum_size.x = 180 detail_vbox.add_child(detail_ability_label) # Message timer message_timer = Timer.new() add_child(message_timer) message_timer.one_shot = true message_timer.timeout.connect(_on_message_timer_timeout) func _create_panel() -> PanelContainer: var panel = PanelContainer.new() # Create stylebox var style = StyleBoxFlat.new() style.bg_color = Color(0.1, 0.1, 0.15, 0.9) 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) return panel func _connect_signals() -> void: end_phase_button.pressed.connect(_on_end_phase_pressed) pass_button.pressed.connect(_on_pass_priority_pressed) # Connect to GameManager signals if GameManager: GameManager.turn_changed.connect(_on_turn_changed) GameManager.phase_changed.connect(_on_phase_changed) GameManager.message.connect(show_message) func _on_end_phase_pressed() -> void: end_phase_pressed.emit() if GameManager: GameManager.pass_priority() func _on_pass_priority_pressed() -> void: pass_priority_pressed.emit() if GameManager: GameManager.pass_priority() func _on_turn_changed(player_name: String, turn_number: int) -> void: turn_label.text = player_name + " - Turn " + str(turn_number) func _on_phase_changed(phase_name: String) -> void: phase_label.text = phase_name ## Update CP display func update_cp_display(cp_pool: CPPool) -> void: if not cp_pool: cp_label.text = "0 CP" return var total = cp_pool.get_total_cp() var text = str(total) + " CP" # Show element breakdown if any specific element CP var elements_text = [] for element in Enums.Element.values(): var amount = cp_pool.get_cp(element) if amount > 0: var elem_name = Enums.element_to_string(element) elements_text.append(elem_name + ":" + str(amount)) if elements_text.size() > 0: text += "\n" + ", ".join(elements_text) cp_label.text = text ## Show a message func show_message(text: String) -> void: message_queue.append(text) if not message_timer.is_stopped(): return _show_next_message() func _show_next_message() -> void: if message_queue.is_empty(): message_panel.visible = false return var text = message_queue.pop_front() message_label.text = text message_panel.visible = true message_timer.start(MESSAGE_DISPLAY_TIME) func _on_message_timer_timeout() -> void: _show_next_message() ## Show card detail panel func show_card_detail(card: CardInstance) -> void: if not card or not card.card_data: card_detail_panel.visible = false return var data = card.card_data detail_name_label.text = data.name detail_type_label.text = "Type: " + Enums.card_type_to_string(data.type) detail_cost_label.text = "Cost: " + str(data.cost) # Element var element_strs = [] for elem in data.elements: element_strs.append(Enums.element_to_string(elem)) detail_element_label.text = "Element: " + "/".join(element_strs) # Power if data.type == Enums.CardType.FORWARD or data.power > 0: detail_power_label.text = "Power: " + str(card.get_power()) detail_power_label.visible = true else: detail_power_label.visible = false # Abilities var ability_text = "" for ability in data.abilities: if ability_text != "": ability_text += "\n\n" var type_str = "" match ability.type: Enums.AbilityType.FIELD: type_str = "[Field]" Enums.AbilityType.AUTO: type_str = "[Auto]" Enums.AbilityType.ACTION: type_str = "[Action]" Enums.AbilityType.SPECIAL: type_str = "[Special]" ability_text += type_str if ability.name != "": ability_text += " " + ability.name ability_text += "\n" + ability.effect detail_ability_label.text = ability_text if ability_text != "" else "No abilities" card_detail_panel.visible = true ## Hide card detail panel func hide_card_detail() -> void: print("DEBUG: GameUI.hide_card_detail() called") card_detail_panel.visible = false ## Update button states based on game phase func update_button_states(can_end_phase: bool, can_pass: bool) -> void: end_phase_button.disabled = not can_end_phase pass_button.disabled = not can_pass