259 lines
6.2 KiB
C#

using System;
using UnityEngine;
public class Player : BasePlayer, IGamePlayer
{
public event Action<int, int> 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
}