| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- using Content.Shared.Arcade;
- using Robust.Shared.Random;
- using System.Linq;
- namespace Content.Server.Arcade.BlockGame;
- public sealed partial class BlockGame
- {
- // note: field is 10(0 -> 9) wide and 20(0 -> 19) high
- /// <summary>
- /// Whether the given position is above the bottom of the playfield.
- /// </summary>
- private bool LowerBoundCheck(Vector2i position)
- {
- return position.Y < 20;
- }
- /// <summary>
- /// Whether the given position is horizontally positioned within the playfield.
- /// </summary>
- private bool BorderCheck(Vector2i position)
- {
- return position.X >= 0 && position.X < 10;
- }
- /// <summary>
- /// Whether the given position is currently occupied by a piece.
- /// Yes this is on O(n) collision check, it works well enough.
- /// </summary>
- private bool ClearCheck(Vector2i position)
- {
- return _field.All(block => !position.Equals(block.Position));
- }
- /// <summary>
- /// Whether a block can be dropped into the given position.
- /// </summary>
- private bool DropCheck(Vector2i position)
- {
- return LowerBoundCheck(position) && ClearCheck(position);
- }
- /// <summary>
- /// Whether a block can be moved horizontally into the given position.
- /// </summary>
- private bool MoveCheck(Vector2i position)
- {
- return BorderCheck(position) && ClearCheck(position);
- }
- /// <summary>
- /// Whether a block can be rotated into the given position.
- /// </summary>
- private bool RotateCheck(Vector2i position)
- {
- return BorderCheck(position) && LowerBoundCheck(position) && ClearCheck(position);
- }
- /// <summary>
- /// The set of blocks that have landed in the field.
- /// </summary>
- private readonly List<BlockGameBlock> _field = new();
- /// <summary>
- /// The current pool of pickable pieces.
- /// Refreshed when a piece is requested while empty.
- /// Ensures that the player is given an even spread of pieces by making picked pieces unpickable until the rest are picked.
- /// </summary>
- private List<BlockGamePieceType> _blockGamePiecesBuffer = new();
- /// <summary>
- /// Gets a random piece from the pool of pickable pieces. (<see cref="_blockGamePiecesBuffer"/>)
- /// </summary>
- private BlockGamePiece GetRandomBlockGamePiece(IRobustRandom random)
- {
- if (_blockGamePiecesBuffer.Count == 0)
- {
- _blockGamePiecesBuffer = _allBlockGamePieces.ToList();
- }
- var chosenPiece = random.Pick(_blockGamePiecesBuffer);
- _blockGamePiecesBuffer.Remove(chosenPiece);
- return BlockGamePiece.GetPiece(chosenPiece);
- }
- /// <summary>
- /// The piece that is currently falling and controllable by the player.
- /// </summary>
- private BlockGamePiece CurrentPiece
- {
- get => _internalCurrentPiece;
- set
- {
- _internalCurrentPiece = value;
- UpdateFieldUI();
- }
- }
- private BlockGamePiece _internalCurrentPiece = default!;
- /// <summary>
- /// The position of the falling piece.
- /// </summary>
- private Vector2i _currentPiecePosition;
- /// <summary>
- /// The rotation of the falling piece.
- /// </summary>
- private BlockGamePieceRotation _currentRotation;
- /// <summary>
- /// The amount of time (in seconds) between piece steps.
- /// Decreased by a constant amount per level.
- /// Decreased heavily by soft dropping the current piece (holding down).
- /// </summary>
- private float Speed => Math.Max(0.03f, (_softDropPressed ? SoftDropModifier : 1f) - 0.03f * Level);
- /// <summary>
- /// The base amount of time between piece steps while softdropping.
- /// </summary>
- private const float SoftDropModifier = 0.1f;
- /// <summary>
- /// Attempts to rotate the falling piece to a new rotation.
- /// </summary>
- private void TrySetRotation(BlockGamePieceRotation rotation)
- {
- if (!_running)
- return;
- if (!CurrentPiece.CanSpin)
- return;
- if (!CurrentPiece.Positions(_currentPiecePosition, rotation)
- .All(RotateCheck))
- return;
- _currentRotation = rotation;
- UpdateFieldUI();
- }
- /// <summary>
- /// The next piece that will be dispensed.
- /// </summary>
- private BlockGamePiece NextPiece
- {
- get => _internalNextPiece;
- set
- {
- _internalNextPiece = value;
- SendNextPieceUpdate();
- }
- }
- private BlockGamePiece _internalNextPiece = default!;
- /// <summary>
- /// The piece the player has chosen to hold in reserve.
- /// </summary>
- private BlockGamePiece? HeldPiece
- {
- get => _internalHeldPiece;
- set
- {
- _internalHeldPiece = value;
- SendHoldPieceUpdate();
- }
- }
- private BlockGamePiece? _internalHeldPiece = null;
- /// <summary>
- /// Prevents the player from holding the currently falling piece if true.
- /// Set true when a piece is held and set false when a new piece is created.
- /// Exists to prevent the player from swapping between two pieces forever and never actually letting the block fall.
- /// </summary>
- private bool _holdBlock = false;
- /// <summary>
- /// The number of lines that have been cleared in the current level.
- /// Automatically advances the game to the next level if enough lines are cleared.
- /// </summary>
- private int ClearedLines
- {
- get => _clearedLines;
- set
- {
- _clearedLines = value;
- if (_clearedLines < LevelRequirement)
- return;
- _clearedLines -= LevelRequirement;
- Level++;
- }
- }
- private int _clearedLines = 0;
- /// <summary>
- /// The number of lines that must be cleared to advance to the next level.
- /// </summary>
- private int LevelRequirement => Math.Min(100, Math.Max(Level * 10 - 50, 10));
- /// <summary>
- /// The current level of the game.
- /// Effects the movement speed of the active piece.
- /// </summary>
- private int Level
- {
- get => _internalLevel;
- set
- {
- if (_internalLevel == value)
- return;
- _internalLevel = value;
- SendLevelUpdate();
- }
- }
- private int _internalLevel = 0;
- /// <summary>
- /// The total number of points accumulated in the current game.
- /// </summary>
- private int Points
- {
- get => _internalPoints;
- set
- {
- if (_internalPoints == value)
- return;
- _internalPoints = value;
- SendPointsUpdate();
- }
- }
- private int _internalPoints = 0;
- /// <summary>
- /// Setter for the setter for the number of points accumulated in the current game.
- /// </summary>
- private void AddPoints(int amount)
- {
- if (amount == 0)
- return;
- Points += amount;
- }
- /// <summary>
- /// Where the current game has placed amongst the leaderboard.
- /// </summary>
- private ArcadeSystem.HighScorePlacement? _highScorePlacement = null;
- }
|