extends Node3D ## Main - Root scene that coordinates the game # Components var table_setup: TableSetup var game_ui: GameUI var hand_display: HandDisplay var hand_layer: CanvasLayer # Player damage displays var damage_displays: Array[DamageDisplay] = [] func _ready() -> void: _setup_table() _setup_ui() _connect_signals() # Start game when ready if GameManager.is_initialized: _start_game() else: GameManager.game_ready.connect(_start_game) func _setup_table() -> void: table_setup = TableSetup.new() add_child(table_setup) # Connect table signals table_setup.card_clicked.connect(_on_table_card_clicked) func _setup_ui() -> void: # Main game UI overlay (has its own CanvasLayer) game_ui = GameUI.new() game_ui.layer = 10 # Base UI layer add_child(game_ui) # Hand display needs its own CanvasLayer to render on top of 3D hand_layer = CanvasLayer.new() hand_layer.layer = 11 # Above the game UI add_child(hand_layer) # Container for hand at bottom of screen - must fill the viewport var hand_container = Control.new() hand_layer.add_child(hand_container) hand_container.set_anchors_preset(Control.PRESET_FULL_RECT) hand_container.mouse_filter = Control.MOUSE_FILTER_IGNORE # Hand display positioned at bottom - use explicit positioning hand_display = HandDisplay.new() hand_container.add_child(hand_display) # Position at bottom of screen with explicit coordinates # We'll update position in _process or use a deferred call after container is sized call_deferred("_position_hand_display") hand_display.card_selected.connect(_on_hand_card_selected) hand_display.card_hovered.connect(_on_hand_card_hovered) hand_display.card_unhovered.connect(_on_hand_card_unhovered) # Create damage displays (positioned by 3D camera overlay later) for i in range(2): var damage_display = DamageDisplay.new() damage_displays.append(damage_display) func _position_hand_display() -> void: # Get viewport size and position hand at bottom var viewport = get_viewport() if viewport: var vp_size = viewport.get_visible_rect().size hand_display.position = Vector2(50, vp_size.y - 180) hand_display.size = Vector2(vp_size.x - 100, 170) func _connect_signals() -> void: # GameManager signals GameManager.game_started.connect(_on_game_started) GameManager.game_ended.connect(_on_game_ended) GameManager.turn_changed.connect(_on_turn_changed) GameManager.phase_changed.connect(_on_phase_changed) GameManager.damage_dealt.connect(_on_damage_dealt) func _start_game() -> void: GameManager.start_new_game() # Force an update of visuals after a frame to ensure everything is ready call_deferred("_force_initial_update") func _force_initial_update() -> void: _sync_visuals() _update_hand_display() _update_cp_display() func _on_game_started() -> void: _sync_visuals() _update_hand_display() func _on_game_ended(winner_name: String) -> void: game_ui.show_message(winner_name + " wins the game!") func _on_turn_changed(_player_name: String, _turn_number: int) -> void: _sync_visuals() _update_hand_display() _update_cp_display() func _on_phase_changed(_phase_name: String) -> void: _update_playable_highlights() _update_cp_display() func _on_damage_dealt(player_name: String, _amount: int) -> void: # Find player index if GameManager.game_state: for i in range(2): var player = GameManager.game_state.get_player(i) if player and player.player_name == player_name: if i < damage_displays.size(): damage_displays[i].set_damage(player.damage_zone.get_count()) func _sync_visuals() -> void: if GameManager.game_state and table_setup: table_setup.sync_with_game_state(GameManager.game_state) func _update_hand_display() -> void: if not GameManager.game_state: return # Show current player's hand var current_player = GameManager.get_current_player() if current_player: var cards = current_player.hand.get_cards() hand_display.update_hand(cards) func _update_cp_display() -> void: if not GameManager.game_state: return var current_player = GameManager.get_current_player() if current_player: game_ui.update_cp_display(current_player.cp_pool) func _update_playable_highlights() -> void: if not GameManager.game_state: return var phase = GameManager.get_current_phase() var player = GameManager.get_current_player() if not player: hand_display.clear_highlights() return match phase: Enums.TurnPhase.MAIN_1, Enums.TurnPhase.MAIN_2: # Highlight cards that can be played hand_display.highlight_playable(func(card: CardInstance) -> bool: return player.cp_pool.can_afford_card(card.card_data) ) _: hand_display.clear_highlights() func _on_hand_card_selected(card: CardInstance) -> void: print("Main: Card selected: ", card.card_data.name, " input_mode=", GameManager.input_mode) var input_mode = GameManager.input_mode match input_mode: GameManager.InputMode.SELECT_CARD_TO_PLAY: print("Main: Trying to play card") GameManager.try_play_card(card) _sync_visuals() _update_hand_display() _update_cp_display() GameManager.InputMode.SELECT_CP_SOURCE: # Discard for CP print("Main: Discarding for CP") GameManager.discard_card_for_cp(card) _update_hand_display() _update_cp_display() _: print("Main: Input mode not handled: ", input_mode) func _on_hand_card_hovered(card: CardInstance) -> void: game_ui.show_card_detail(card) func _on_hand_card_unhovered() -> void: game_ui.hide_card_detail() func _on_table_card_clicked(card: CardInstance, zone_type: Enums.ZoneType, player_index: int) -> void: var input_mode = GameManager.input_mode match input_mode: GameManager.InputMode.SELECT_CP_SOURCE: # Check if it's a backup we can dull if zone_type == Enums.ZoneType.FIELD_BACKUPS: if player_index == GameManager.game_state.turn_manager.current_player_index: GameManager.dull_backup_for_cp(card) _sync_visuals() _update_cp_display() GameManager.InputMode.SELECT_ATTACKER: # Select attacker if zone_type == Enums.ZoneType.FIELD_FORWARDS: if player_index == GameManager.game_state.turn_manager.current_player_index: GameManager.declare_attack(card) _sync_visuals() GameManager.InputMode.SELECT_BLOCKER: # Select blocker if zone_type == Enums.ZoneType.FIELD_FORWARDS: var opponent_index = 1 - GameManager.game_state.turn_manager.current_player_index if player_index == opponent_index: GameManager.declare_block(card) _sync_visuals() # Show card detail on any click game_ui.show_card_detail(card) func _input(event: InputEvent) -> void: # Keyboard shortcuts if event is InputEventKey and event.pressed: match event.keycode: KEY_SPACE: # Pass priority / end phase GameManager.pass_priority() _sync_visuals() _update_hand_display() _update_cp_display() KEY_ESCAPE: # Cancel current selection GameManager.clear_selection() table_setup.clear_all_highlights() hand_display.clear_highlights()