304 lines
9.1 KiB
GDScript
304 lines
9.1 KiB
GDScript
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
|
|
var action_log: ActionLog
|
|
|
|
# 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)
|
|
|
|
# Action log (collapsible panel on right side)
|
|
var log_layer = CanvasLayer.new()
|
|
log_layer.layer = 12 # Above other UI
|
|
add_child(log_layer)
|
|
|
|
action_log = ActionLog.new()
|
|
log_layer.add_child(action_log)
|
|
# Position on right side of screen
|
|
action_log.set_anchors_preset(Control.PRESET_CENTER_RIGHT)
|
|
action_log.offset_left = -290
|
|
action_log.offset_right = -10
|
|
action_log.offset_top = -200
|
|
action_log.offset_bottom = 200
|
|
|
|
# Connect undo signal
|
|
action_log.undo_requested.connect(_on_undo_requested)
|
|
|
|
# Connect to GameManager undo signals
|
|
GameManager.undo_available_changed.connect(_on_undo_available_changed)
|
|
|
|
# Ensure ActionLog connects to GameManager after it's in the tree
|
|
action_log.call_deferred("_connect_game_manager_signals")
|
|
|
|
# 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_action_requested.connect(_on_hand_card_action)
|
|
hand_display.card_hovered.connect(_on_hand_card_hovered)
|
|
hand_display.card_unhovered.connect(_on_hand_card_unhovered)
|
|
hand_display.card_selected.connect(_on_hand_card_selected)
|
|
|
|
# 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 cards are 195x273px (triple original), positioned at y=0 in container
|
|
var card_height = 273.0
|
|
var hand_height = card_height + 25.0 # Extra space for hover lift
|
|
|
|
# Position so card bottom is 100px above viewport bottom
|
|
var bottom_offset = 100.0
|
|
hand_display.position = Vector2(10, vp_size.y - bottom_offset - card_height)
|
|
hand_display.size = Vector2(vp_size.x - 20, hand_height)
|
|
|
|
if not viewport.size_changed.is_connected(_on_viewport_resized):
|
|
viewport.size_changed.connect(_on_viewport_resized)
|
|
|
|
func _on_viewport_resized() -> void:
|
|
var viewport = get_viewport()
|
|
if viewport and hand_display:
|
|
var vp_size = viewport.get_visible_rect().size
|
|
var card_height = 273.0 # Triple size hand cards
|
|
var hand_height = card_height + 25.0
|
|
var bottom_offset = 100.0
|
|
hand_display.position = Vector2(10, vp_size.y - bottom_offset - card_height)
|
|
hand_display.size = Vector2(vp_size.x - 20, hand_height)
|
|
|
|
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_action(card: CardInstance, action: String) -> void:
|
|
match action:
|
|
"play":
|
|
# Try to play the card
|
|
GameManager.try_play_card(card)
|
|
_sync_visuals()
|
|
_update_hand_display()
|
|
_update_cp_display()
|
|
|
|
"discard_cp":
|
|
# Discard for CP
|
|
GameManager.discard_card_for_cp(card)
|
|
_sync_visuals()
|
|
_update_hand_display()
|
|
_update_cp_display()
|
|
|
|
"view":
|
|
# Show detailed card view
|
|
game_ui.show_card_detail(card)
|
|
|
|
func _on_hand_card_hovered(_card: CardInstance) -> void:
|
|
# Hand cards use the selection panel for detail view, not the GameUI hover preview
|
|
# So we intentionally do nothing here - no hover preview for hand cards
|
|
pass
|
|
|
|
func _on_hand_card_unhovered() -> void:
|
|
game_ui.hide_card_detail()
|
|
|
|
func _on_hand_card_selected(_card: CardInstance) -> void:
|
|
# Selection panel is now visible - ensure any stale hover preview is hidden
|
|
game_ui.hide_card_detail()
|
|
|
|
func _on_undo_requested() -> void:
|
|
if GameManager.undo_last_action():
|
|
_sync_visuals()
|
|
_update_hand_display()
|
|
_update_cp_display()
|
|
_update_playable_highlights()
|
|
# Restore input mode based on current phase
|
|
GameManager.restore_input_mode_for_phase()
|
|
|
|
func _on_undo_available_changed(available: bool) -> void:
|
|
if action_log:
|
|
action_log.set_undo_available(available)
|
|
|
|
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:
|
|
# Ctrl+Z for undo
|
|
if event.keycode == KEY_Z and event.ctrl_pressed:
|
|
_on_undo_requested()
|
|
return
|
|
|
|
# L key to toggle action log
|
|
if event.keycode == KEY_L and not event.ctrl_pressed:
|
|
if action_log:
|
|
action_log.toggle_panel()
|
|
return
|
|
|
|
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()
|