using System; using UnityEngine; public class Player : BasePlayer, IGamePlayer { public event Action OnChipDropped; public event Action OnMoveCommitted; public event Action OnThinkingStarted; [Header("Movement Settings")] [SerializeField] private float chipYOffset = 5f; [SerializeField] private float prepareSpeed; [SerializeField] private float moveSpeed; // State private PlayerState state = PlayerState.Sleep; private Chip currentChip; private int[] availableColumns; private int selectedColumnIndex; // Movement targets private Vector2 stickedTargetPosition; private Vector2 dropTargetPosition; private int droppedRow, droppedColumn; private int SelectedColumn => availableColumns[selectedColumnIndex]; public JoystickSide ControlSide { get; set; } = JoystickSide.Left; public bool ControlsEnabled { get; set; } = true; #region Public Methods public void PrepareChip() { SetState(PlayerState.Preparing); CacheAvailableColumns(); CreateChip(); SetInitialChipPosition(); } public void Sleep() { SetState(PlayerState.Sleep); currentChip = null; availableColumns = null; } public void CancelTurn() { if (currentChip != null) { if (state == PlayerState.Dropping) currentChip.transform.position = dropTargetPosition; else ChipPool.Instance.Release(currentChip); } currentChip = null; availableColumns = null; SetState(PlayerState.Sleep); } public void ReturnPreparedChipToPool() { if (currentChip == null || state == PlayerState.Sleep || state == PlayerState.Dropping) return; ChipPool.Instance.Release(currentChip); currentChip = null; availableColumns = null; SetState(PlayerState.Sleep); } public bool ForceDrop() { if (currentChip == null || availableColumns == null || state == PlayerState.Sleep || state == PlayerState.Dropping) return false; BeginDrop(); return true; } public void UpdateControls() { // Always update state machine regardless of ControlsEnabled switch (state) { case PlayerState.Preparing: UpdatePreparing(); break; case PlayerState.Idle: UpdateIdle(); break; case PlayerState.Moving: UpdateMoving(); break; case PlayerState.Dropping: UpdateDropping(); break; case PlayerState.Sleep: // Nothing to do break; } } #endregion #region State Updates private void UpdatePreparing() { if (currentChip.MoveToLinear(stickedTargetPosition, prepareSpeed)) { SetState(PlayerState.Idle); OnThinkingStarted?.Invoke(); } } private void UpdateIdle() { // Only process input if controls are enabled if (!ControlsEnabled) return; // Try horizontal movement first if (TryHorizontalMovement()) return; // Then try drop TryDrop(); } private void UpdateMoving() { // Process horizontal input even while moving if (ControlsEnabled) { TryHorizontalMovement(); } // Wait for chip to reach target if (currentChip.MoveToLinear(stickedTargetPosition, moveSpeed)) { SetState(PlayerState.Idle); } } private void UpdateDropping() { if (currentChip.MoveToAccelerated(dropTargetPosition)) { if (currentChip.MoveToAccelerated(dropTargetPosition)) { SetState(PlayerState.Sleep); OnChipDropped?.Invoke(droppedColumn, droppedRow); } } } #endregion #region Actions private bool TryHorizontalMovement() { int direction = GetHorizontalInput(); if (direction == 0) return false; int newIndex = Mathf.Clamp(selectedColumnIndex + direction, 0, availableColumns.Length - 1); if (newIndex == selectedColumnIndex) return false; selectedColumnIndex = newIndex; SoundManager.instance?.PlayColumnSelectionSound(); stickedTargetPosition = board.GetStickedPosition(SelectedColumn); SetState(PlayerState.Moving); return true; } private void TryDrop() { if (!IsActionPressed()) return; BeginDrop(); } private void BeginDrop() { dropTargetPosition = board.GetTargetPosition(SelectedColumn); droppedColumn = SelectedColumn; droppedRow = board.GetLowestEmptyRow(SelectedColumn); board.DropChip(currentChip, SelectedColumn); OnMoveCommitted?.Invoke(); SetState(PlayerState.Dropping); } #endregion #region Helpers private void CacheAvailableColumns() { availableColumns = board.GetNonFullColumns(); selectedColumnIndex = availableColumns.Length / 2; } private void CreateChip() { currentChip = ChipPool.Instance.GetConfigured(chipSprite, team); } private void SetInitialChipPosition() { stickedTargetPosition = board.GetStickedPosition(SelectedColumn); currentChip.transform.position = stickedTargetPosition + Vector2.up * chipYOffset; } private int GetHorizontalInput() { return In.GetHorizontalAxis(ControlSide, InteractMode.Discrete); } private bool IsActionPressed() { return In.GetAction(ControlSide, InteractMode.Discrete); } private void SetState(PlayerState newState) { state = newState; } #endregion #region Nested Types private enum PlayerState { Sleep, // No input, chip placed on board Preparing, // No input, chip moving to sticked position Idle, // Input enabled, waiting for action Moving, // Input enabled (horizontal only), chip moving horizontally Dropping // No input, chip falling down } #endregion }