protected bool CheckForOverlap(Tetromino sim) { // This try catch block is here as a dirty hack! // This dirty hack returns true (overlap) if the simulated piece is out of bounds. // In my defense, this function was previously called "CheckForBounds" try { // Create a new simulated field with a simulated piece to check for overlaps char[,] simulatedField = UpdateSimulation(sim); // For each point in the simulated field... for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { // ...check only for the active blocks(non - frozen, non - empty) if (simulatedField[j, i] != 'e' && simulatedField[j, i] != 'f') { // If any active block in the simulated field overlaps with a frozen block in the real field, then we have found a overlap. if (field[j, i] == 'f') { return(true); } } } } // If we find no overlaps between the simulated and the real field, return false, no overlaps. return(false); } catch (Exception) { return(true); } }
// Collision and Bounds Checking protected char[,] UpdateSimulation(Tetromino sim) { // Takes a tetromino as input and generates a simulated matrix that overlaps the tetromino on the game matrix. char[,] simulatedField = new char[w, h]; // Copy the game matrix as the simulated one. for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { simulatedField[j, i] = field[j, i]; } } // Put the tetromino in the simulated matrix. for (int i = 0; i < sim.Orientations[sim.Phase].Matrix.GetLength(0); i++) { for (int j = 0; j < sim.Orientations[sim.Phase].Matrix.GetLength(1); j++) { if (sim.Orientations[sim.Phase].Matrix[i, j] != 'e') { simulatedField[sim.X + j, sim.Y + i] = sim.Orientations[sim.Phase].Matrix[i, j]; } } } return(simulatedField); }
protected char[,] GenerateTetrominoInfoArray(Tetromino renderTetromino) { // This function generates 4x4 arrays of tetrominos. // It is used to display the next and saved tetrominos. // It is 4x4 to be universal and accomidate any tetromino. // Initialize the matrix with 'h'idden characters. char[,] temp = new char[4, 4] { { 'h', 'h', 'h', 'h' }, { 'h', 'h', 'h', 'h' }, { 'h', 'h', 'h', 'h' }, { 'h', 'h', 'h', 'h' } }; // These loops copy over the matrix of the saved or next tetromnio over to the 4x4 matrix. // The if statement ensures that we only copy the parts we need, basically, ignore the empty spaces. for (int i = 0; i < renderTetromino.Orientations[renderTetromino.Phase].Matrix.GetLength(0); i++) { for (int j = 0; j < renderTetromino.Orientations[renderTetromino.Phase].Matrix.GetLength(1); j++) { if (renderTetromino.Orientations[renderTetromino.Phase].Matrix[i, j] != 'e') { temp[i, j] = renderTetromino.Orientations[renderTetromino.Phase].Matrix[i, j]; } } } // We return the matrix to be displayed. return(temp); }
public Quadris(ConsoleHardwareConfig config) : base(config) { World = new Composition(new byte[FieldHeight * FieldWidth / 8], FieldWidth, FieldHeight); ResetPlayfield(); Tetromino = new Tetromino(this); RenderFrame(); TimeToFall = false; _timeToFallTimer = new Timer(OnTimerTick, null, TimeSpan.Zero, TimeSpan.FromTicks(TetrominoFallInterval)); DisplayDelay = 0; }
public static Tetromino Copy(Tetromino source) { Tetromino copy = new Tetromino(); copy.phase = source.phase; copy.x = source.x; copy.y = source.y; foreach (var item in source.orientations) { copy.orientations.Add(item); } return(copy); }
public void Land(Tetromino piece) { int t = 0; if (piece is I) { t = 1; } if (piece is J) { t = 2; } if (piece is L) { t = 3; } if (piece is O) { t = 4; } if (piece is S) { t = 5; } if (piece is T) { t = 6; } if (piece is Z) { t = 7; } for (int py = 0; py < Constants.PieceTiles; ++py) { for (int px = 0; px < Constants.PieceTiles; ++px) { if (piece.Tiles[py, px] == 0) { continue; } int wellX = ((int)piece.X - Constants.PieceTiles / 2) + px; int wellY = ((int)piece.Y - Constants.PieceTiles / 2) + py; wellTiles[wellY * Constants.WellWidth + wellX] = t; } } }
private void SpawnPiece(bool first = false) { // Reset keys newpiece = true; keyDownTime[Keys.Left] = 0; keyDownTime[Keys.Right] = 0; keyDownTime[Keys.Down] = 0; // Set current piece piece = (first) ? GenerateRandomPiece() : preview; piece.X = Constants.WellWidth / 2; piece.Y = 0; // Calculate vertical clearance space for (int y = 0; y <= Constants.PieceTiles / 2; ++y) { // if sum of row > 0 int sum = 0; for (int x = 0; x < Constants.PieceTiles; ++x) { sum += piece.Tiles[y, x]; if (sum > 0) { break; } } if (sum > 0) { piece.Y += Math.Abs((Constants.PieceTiles / 2) - y); break; } } // Fail state occurs when there is no space to spawn next piece if (well.Collision(piece, 0, 0)) { gameState = GameState.GameOver; SaveHighScores(); } // Generate next piece preview = GenerateRandomPiece(); preview.X = Constants.WellWidth + 5; preview.Y = 5; }
protected void SaveTetromino() { // Saves the currentTetromino and spawns a new one. // If there is one already saved, swaps the saved one and the current one. if (savedTetromino == null) { savedTetromino = Tetromino.Copy(currentTetromino); SpawnNewTetromino(); } else { Tetromino temp = Tetromino.Copy(currentTetromino); currentTetromino = Tetromino.Copy(savedTetromino); savedTetromino = Tetromino.Copy(temp); currentTetromino.X = 4; currentTetromino.Y = 0; //currentTetromino.Phase = 0; } }
public bool Collision(Tetromino piece, int offsetX = 0, int offsetY = 0) { // Loop over the 5x5 tiles of a piece on the board (i1,j1 = position in well; i2,j2 = position in piece tile grid) for (int py = 0; py < Constants.PieceTiles; ++py) { for (int px = 0; px < Constants.PieceTiles; ++px) { if (piece.Tiles[py, px] == 0) { continue; } int wellX = ((int)piece.X + offsetX - Constants.PieceTiles / 2) + px; int wellY = ((int)piece.Y + offsetY - Constants.PieceTiles / 2) + py; // Check if the piece is inside horizontal bounds of the well if (wellX < 0 || wellX > Constants.WellWidth - 1) { return(true); } // Check if the piece is inside vertical bounds of the well if (wellY > Constants.WellHeight - 1) { return(true); } // Check if piece has collided with a previously stored tile if (wellY >= 0 && wellTiles[wellY * Constants.WellWidth + wellX] != 0) { return(true); } } } // No collision return(false); }
protected void Rotate(int direction) { int oldX = currentTetromino.X; // Save old X position, in case the simulated Tetromino is overlaping a frozen piece. int newX = currentTetromino.X; // Shifted X position, in case the new orientation is out of bounds. int oldOrientation = currentTetromino.Phase; // Save old orientation, in case the new one is overlaping a frozen piece. int newOrientation = currentTetromino.Phase; // New orientation // Simulated Tetromino, to see if after rotation, the current tetromino is overlaping or out of bounds. Tetromino sim = Tetromino.Copy(currentTetromino); //If direction is 1, it's clockwise, if it's -1 it's counter clock wise, so we do bounds checking. newOrientation = newOrientation + direction; if (newOrientation > currentTetromino.PhaseCount - 1) // -1 because if there are 4 orientations, the last one will have position 3 in the List. { newOrientation = 0; } else if (newOrientation < 0) { newOrientation = currentTetromino.PhaseCount - 1; } //If the oriented piece is out of bounds, push it back. if (newX > w - 1 - currentTetromino.Orientations[newOrientation].Padding) { newX = w - 1 - currentTetromino.Orientations[newOrientation].Padding; } //Assign our simulated Tetromino it's new position and orientation. sim.X = newX; sim.Phase = newOrientation; //Check if the simulated piece overlaps a frozen piece, if it's not overlaping, make the current piece, same as the simulated one. if (CheckForOverlap(sim) == false) { currentTetromino.X = newX; currentTetromino.Phase = newOrientation; } }
protected void RandomNextTetromino() { // "Randomly" generates nextTetromino. Random rng = new Random(); switch (rng.Next(6)) { case 0: { nextTetromino = Tetromino.Copy(sc); break; } case 1: { nextTetromino = Tetromino.Copy(si); break; } case 2: { nextTetromino = Tetromino.Copy(sz); break; } case 3: { nextTetromino = Tetromino.Copy(sn); break; } case 4: { nextTetromino = Tetromino.Copy(st); break; } case 5: { nextTetromino = Tetromino.Copy(sl); break; } case 6: { nextTetromino = Tetromino.Copy(sj); break; } default: { break; } } }
private void UpdateInput(KeyboardState keyState, GameTime gameTime) { if (keyState.IsKeyDown(Keys.Escape)) { gameState = GameState.Menu; Reset(); return; } if (keyState.IsKeyDown(Keys.Left)) { if (!prevKeyState.IsKeyDown(Keys.Left)) { newpiece = false; keyDownTime[Keys.Left] = 0; keyDownTime[Keys.Right] = 0; if (!well.Collision(piece, -1, 0)) { piece.X--; } } else if (!newpiece) { // key was down on last tick as well if (++keyDownTime[Keys.Left] % Math.Max(4, 20 - keyDownTime[Keys.Left] / 2) == 0) { if (!well.Collision(piece, -1, 0)) { piece.X--; } } } } else if (keyState.IsKeyDown(Keys.Right)) { if (!prevKeyState.IsKeyDown(Keys.Right)) { newpiece = false; keyDownTime[Keys.Left] = 0; keyDownTime[Keys.Right] = 0; if (!well.Collision(piece, 1, 0)) { piece.X++; } } else if (!newpiece) { if (++keyDownTime[Keys.Right] % Math.Max(4, 20 - keyDownTime[Keys.Right] / 2) == 0) { if (!well.Collision(piece, 1, 0)) { piece.X++; } } } } // Soft drop else if (keyState.IsKeyDown(Keys.Down)) { if (!prevKeyState.IsKeyDown(Keys.Down)) { newpiece = false; keyDownTime[Keys.Down] = 0; if (!well.Collision(piece, 0, 1)) { piece.Y++; } else { gravityTime = gravityTimer; } } else if (!newpiece) { if (++keyDownTime[Keys.Down] % Math.Max(4, 20 - keyDownTime[Keys.Down] / 2) == 0) { if (!well.Collision(piece, 0, 1)) { piece.Y++; } else { gravityTime = gravityTimer; } } } } if (keyState.IsKeyDown(Keys.Z) && !prevKeyState.IsKeyDown(Keys.Z)) { Tetromino rotated = piece.Copy(); rotated.Tiles = piece.Rotate(); // attempt rotation as-is if (!well.Collision(rotated)) { piece.Tiles = rotated.Tiles; } // attempt wall-kick-right before rotation else if (!well.Collision(rotated, 1, 0)) { piece.X++; piece.Tiles = rotated.Tiles; } // attempt wall-kick-left before rotation else if (!well.Collision(rotated, -1, 0)) { piece.X--; piece.Tiles = rotated.Tiles; } } // Hard drop if (keyState.IsKeyDown(Keys.X) && !prevKeyState.IsKeyDown(Keys.X)) { while (!well.Collision(piece)) { piece.Y++; } piece.Y--; gravityTime = gravityTimer; } }
public override void Loop() { var leftStick = Hardware.JoystickLeft.XDirection; if (leftStick != _previousLeftDirection) { switch (leftStick) { case AnalogJoystick.Direction.Negative: Tetromino.MoveLeft(); break; case AnalogJoystick.Direction.Positive: Tetromino.MoveRight(); break; } _previousLeftDirection = leftStick; } var rightStick = Hardware.JoystickRight.XDirection; if (rightStick != _previousRightDirection) { switch (rightStick) { case AnalogJoystick.Direction.Negative: Tetromino.RotateLeft(); break; case AnalogJoystick.Direction.Positive: Tetromino.RotateRight(); break; } _previousRightDirection = rightStick; } var stickDown = Hardware.JoystickLeft.YDirection == AnalogJoystick.Direction.Positive; if (TimeToFall || (stickDown && !_rightJoystickPreviouslyDown)) { TimeToFall = false; if (!Tetromino.MoveDown()) { // The tetromino hit the bottom // We need to copy the tetromino onto the playfield CopyTetrominoOntoPlayfield(); // Did it have enough space to render entirely? if (Tetromino.Y < 0) { // Nope -> Game over RenderFrame(); MakeExplosionSound(); Stop(); } else { MakeHitSound(); // Check for and remove full lines RemoveFullLines(); // Spawn a new tetromino Tetromino = new Tetromino(this); // Accelerate the game _currentTimeToFallTicks = (long)(_currentTimeToFallTicks * 100 / AccelarationRate); _timeToFallTimer.Change(TimeSpan.Zero, TimeSpan.FromTicks(_currentTimeToFallTicks)); } } } _rightJoystickPreviouslyDown = stickDown; RenderFrame(); }
protected void SpawnNewTetromino() { // Makes currentTetromino to be the Next one and generates a new nextTetromino. currentTetromino = Tetromino.Copy(nextTetromino); RandomNextTetromino(); }