using Content.Shared.Arcade;
using Robust.Server.Player;
using System.Linq;
namespace Content.Server.Arcade.BlockGame;
public sealed partial class BlockGame
{
///
/// How often to check the currently pressed inputs for whether to move the active piece horizontally.
///
private const float PressCheckSpeed = 0.08f;
///
/// Whether the left button is pressed.
/// Moves the active piece left if true.
///
private bool _leftPressed = false;
///
/// How long the left button has been pressed.
///
private float _accumulatedLeftPressTime = 0f;
///
/// Whether the right button is pressed.
/// Moves the active piece right if true.
///
private bool _rightPressed = false;
///
/// How long the right button has been pressed.
///
private float _accumulatedRightPressTime = 0f;
///
/// Whether the down button is pressed.
/// Speeds up how quickly the active piece falls if true.
///
private bool _softDropPressed = false;
///
/// Handles user input.
///
/// The action to current player has prompted.
public void ProcessInput(BlockGamePlayerAction action)
{
if (_running)
{
switch (action)
{
case BlockGamePlayerAction.StartLeft:
_leftPressed = true;
break;
case BlockGamePlayerAction.StartRight:
_rightPressed = true;
break;
case BlockGamePlayerAction.Rotate:
TrySetRotation(Next(_currentRotation, false));
break;
case BlockGamePlayerAction.CounterRotate:
TrySetRotation(Next(_currentRotation, true));
break;
case BlockGamePlayerAction.SoftdropStart:
_softDropPressed = true;
if (_accumulatedFieldFrameTime > Speed)
_accumulatedFieldFrameTime = Speed; //to prevent jumps
break;
case BlockGamePlayerAction.Harddrop:
PerformHarddrop();
break;
case BlockGamePlayerAction.Hold:
HoldPiece();
break;
}
}
switch (action)
{
case BlockGamePlayerAction.EndLeft:
_leftPressed = false;
break;
case BlockGamePlayerAction.EndRight:
_rightPressed = false;
break;
case BlockGamePlayerAction.SoftdropEnd:
_softDropPressed = false;
break;
case BlockGamePlayerAction.Pause:
_running = false;
SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause, Started));
break;
case BlockGamePlayerAction.Unpause:
if (!_gameOver && Started)
{
_running = true;
SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
}
break;
case BlockGamePlayerAction.ShowHighscores:
_running = false;
SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Highscores, Started));
break;
}
}
///
/// Handle moving the active game piece in response to user input.
///
/// The amount of time the current game tick covers.
private void InputTick(float frameTime)
{
var anythingChanged = false;
if (_leftPressed)
{
_accumulatedLeftPressTime += frameTime;
while (_accumulatedLeftPressTime >= PressCheckSpeed)
{
if (CurrentPiece.Positions(_currentPiecePosition.AddToX(-1), _currentRotation)
.All(MoveCheck))
{
_currentPiecePosition = _currentPiecePosition.AddToX(-1);
anythingChanged = true;
}
_accumulatedLeftPressTime -= PressCheckSpeed;
}
}
if (_rightPressed)
{
_accumulatedRightPressTime += frameTime;
while (_accumulatedRightPressTime >= PressCheckSpeed)
{
if (CurrentPiece.Positions(_currentPiecePosition.AddToX(1), _currentRotation)
.All(MoveCheck))
{
_currentPiecePosition = _currentPiecePosition.AddToX(1);
anythingChanged = true;
}
_accumulatedRightPressTime -= PressCheckSpeed;
}
}
if (anythingChanged)
UpdateFieldUI();
}
///
/// Handles sending a message to all players/spectators.
///
/// The message to broadcase to all players/spectators.
private void SendMessage(BoundUserInterfaceMessage message)
{
if (_uiSystem.TryGetUi(_owner, BlockGameUiKey.Key, out var bui))
_uiSystem.SendUiMessage(bui, message);
}
///
/// Handles sending a message to a specific player/spectator.
///
/// The message to send to a specific player/spectator.
/// The target recipient.
private void SendMessage(BoundUserInterfaceMessage message, IPlayerSession session)
{
if (_uiSystem.TryGetUi(_owner, BlockGameUiKey.Key, out var bui))
_uiSystem.TrySendUiMessage(bui, message, session);
}
///
/// Handles sending the current state of the game to a player that has just opened the UI.
///
/// The target recipient.
public void UpdateNewPlayerUI(IPlayerSession session)
{
if (_gameOver)
{
SendMessage(new BlockGameMessages.BlockGameGameOverScreenMessage(Points, _highScorePlacement?.LocalPlacement, _highScorePlacement?.GlobalPlacement), session);
return;
}
if (Paused)
SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause, Started), session);
else
SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game, Started), session);
FullUpdate(session);
}
///
/// Handles broadcasting the full player-visible game state to everyone who can see the game.
///
private void FullUpdate()
{
UpdateFieldUI();
SendHoldPieceUpdate();
SendNextPieceUpdate();
SendLevelUpdate();
SendPointsUpdate();
SendHighscoreUpdate();
}
///
/// Handles broadcasting the full player-visible game state to a specific player/spectator.
///
/// The target recipient.
private void FullUpdate(IPlayerSession session)
{
UpdateFieldUI(session);
SendNextPieceUpdate(session);
SendHoldPieceUpdate(session);
SendLevelUpdate(session);
SendPointsUpdate(session);
SendHighscoreUpdate(session);
}
///
/// Handles broadcasting the current location of all of the blocks in the playfield + the active piece to all spectators.
///
public void UpdateFieldUI()
{
if (!Started)
return;
var computedField = ComputeField();
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(computedField.ToArray(), BlockGameMessages.BlockGameVisualType.GameField));
}
///
/// Handles broadcasting the current location of all of the blocks in the playfield + the active piece to a specific player/spectator.
///
/// The target recipient.
public void UpdateFieldUI(IPlayerSession session)
{
if (!Started)
return;
var computedField = ComputeField();
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(computedField.ToArray(), BlockGameMessages.BlockGameVisualType.GameField), session);
}
///
/// Generates the set of blocks to send to viewers.
///
public List ComputeField()
{
var result = new List();
result.AddRange(_field);
result.AddRange(CurrentPiece.Blocks(_currentPiecePosition, _currentRotation));
var dropGhostPosition = _currentPiecePosition;
while (CurrentPiece.Positions(dropGhostPosition.AddToY(1), _currentRotation)
.All(DropCheck))
{
dropGhostPosition = dropGhostPosition.AddToY(1);
}
if (dropGhostPosition != _currentPiecePosition)
{
var blox = CurrentPiece.Blocks(dropGhostPosition, _currentRotation);
for (var i = 0; i < blox.Length; i++)
{
result.Add(new BlockGameBlock(blox[i].Position, BlockGameBlock.ToGhostBlockColor(blox[i].GameBlockColor)));
}
}
return result;
}
///
/// Broadcasts the state of the next queued piece to all viewers.
///
private void SendNextPieceUpdate()
{
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(NextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock));
}
///
/// Broadcasts the state of the next queued piece to a specific viewer.
///
/// The target recipient.
private void SendNextPieceUpdate(IPlayerSession session)
{
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(NextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock), session);
}
///
/// Broadcasts the state of the currently held piece to all viewers.
///
private void SendHoldPieceUpdate()
{
if (HeldPiece.HasValue)
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(HeldPiece.Value.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.HoldBlock));
else
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(Array.Empty(), BlockGameMessages.BlockGameVisualType.HoldBlock));
}
///
/// Broadcasts the state of the currently held piece to a specific viewer.
///
/// The target recipient.
private void SendHoldPieceUpdate(IPlayerSession session)
{
if (HeldPiece.HasValue)
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(HeldPiece.Value.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.HoldBlock), session);
else
SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(Array.Empty(), BlockGameMessages.BlockGameVisualType.HoldBlock), session);
}
///
/// Broadcasts the current game level to all viewers.
///
private void SendLevelUpdate()
{
SendMessage(new BlockGameMessages.BlockGameLevelUpdateMessage(Level));
}
///
/// Broadcasts the current game level to a specific viewer.
///
/// The target recipient.
private void SendLevelUpdate(IPlayerSession session)
{
SendMessage(new BlockGameMessages.BlockGameLevelUpdateMessage(Level), session);
}
///
/// Broadcasts the current game score to all viewers.
///
private void SendPointsUpdate()
{
SendMessage(new BlockGameMessages.BlockGameScoreUpdateMessage(Points));
}
///
/// Broadcasts the current game score to a specific viewer.
///
/// The target recipient.
private void SendPointsUpdate(IPlayerSession session)
{
SendMessage(new BlockGameMessages.BlockGameScoreUpdateMessage(Points), session);
}
///
/// Broadcasts the current game high score positions to all viewers.
///
private void SendHighscoreUpdate()
{
SendMessage(new BlockGameMessages.BlockGameHighScoreUpdateMessage(_arcadeSystem.GetLocalHighscores(), _arcadeSystem.GetGlobalHighscores()));
}
///
/// Broadcasts the current game high score positions to a specific viewer.
///
/// The target recipient.
private void SendHighscoreUpdate(IPlayerSession session)
{
SendMessage(new BlockGameMessages.BlockGameHighScoreUpdateMessage(_arcadeSystem.GetLocalHighscores(), _arcadeSystem.GetGlobalHighscores()), session);
}
}