public GamePane(GameSession session, GameBlock[] regularBlocks, GameBlock[] hardBlocks, float posX, float posY) { this.session = session; this.regularBlocks = regularBlocks; this.hardBlocks = hardBlocks; this.rnd = new Random(session.Game.Random.Next()); grid = new GameBlockState[Columns, Rows]; position = new Vector2(posX, posY); jitter = 0.0f; scrollY = 0.0f; scrollSpeed = DefaultScrollSpeed; cursorColumn = 0; cursorRow = 1; cursorTexture = regularBlocks[0].Texture; activeAnimations = new HashSet <GameBlockAnimation>(); for (int column = 0; column < Columns; column++) { for (int row = 0; row < Rows; row++) { grid[column, row] = new GameBlockState(this, column, row, (float)rnd.NextDouble()); } } PopulateWithRandomBlocks(7); }
/// <summary> /// Helper for TryClear() that clears column wise. /// </summary> private void TryClearColumns(List <ClearInfo> toClear) { for (int column = 0; column < Columns; column++) { for (int row = 1; row < Rows - 1;) { int rowStart = row; GameBlockState thisState = this[column, row++]; if (thisState.Empty || thisState.Hard) { continue; } Color4 color = thisState.Block.Color; int inTheColumn = 1; for (; row < Rows; row++, inTheColumn++) { GameBlockState otherState = this[column, row]; if (otherState.Empty || color != otherState.Block.Color) { break; } } if (inTheColumn >= 3) { toClear.Add(new ClearInfo(column, rowStart, inTheColumn, ClearInfo.ClearDirection.Column)); } } } }
/// <summary> /// Like MoveUp() but to a lower cell that is empty. /// </summary> public void MoveDown(GameBlockState lowerState) { Debug.Assert(lowerState.Empty, "Can only move down to empty states"); SwapContentsWith(lowerState); lowerState.animationSourceX = BlockX; lowerState.animationSourceY = BlockY; lowerState.animationStep = 0.0f; lowerState.animationSpeedFactor = 50.0f / (Row - lowerState.Row); }
/// <summary> /// Internal helper for swapping. /// </summary> private GameBlockState SwapContentsWith(GameBlockState other) { GameBlockState clone = (GameBlockState)MemberwiseClone(); block = other.block; other.block = clone.block; jiggle = other.jiggle; other.jiggle = clone.jiggle; return(clone); }
/// <summary> /// Exchanges the two blocks under the cursor. /// </summary> public void PerformExchange() { GameBlockState blockState = this[cursorColumn, cursorRow]; if (blockState.Hard) { return; } blockState.ExchangeWith(this[cursorColumn + 1, cursorRow]); TryClear(); }
/// <summary> /// Like ExchangeWith but without the block animation. Used to move /// up blocks when a new row appears. /// </summary> public void SwapWith(GameBlockState other) { GameBlockState clone = SwapContentsWith(other); animationSourceX = other.animationSourceX; other.animationSourceX = clone.animationSourceX; animationSourceY = other.animationSourceY; other.animationSourceY = clone.animationSourceY; animationStep = other.animationStep; other.animationStep = clone.animationStep; }
/// <summary> /// Changes a block state's block with the one from another block state. /// The exchange happens with a little animation and afterwards both /// will also have the jiggle swapped. These two blocks must be on the /// same row though. /// </summary> public void ExchangeWith(GameBlockState other) { Debug.Assert(other.row == row); SwapContentsWith(other); animationStep = other.animationStep = 0.0f; animationSpeedFactor = other.animationSpeedFactor = 7.0f; animationSourceX = other.BlockX; animationSourceY = other.BlockY; other.animationSourceX = BlockX; other.animationSourceY = BlockY; }
/// <summary> /// Implements the logic to break hard rows into soft blocks. Hard blocks are /// broken up into soft ones when something next to them is cleared. /// </summary> private void TryBreakHardBlock(int column, int row) { if (row >= Rows) { return; } GameBlockState blockState = this[column, row]; if (!blockState.Hard) { return; } for (int probeColumn = 0; probeColumn < Columns; probeColumn++) { this[probeColumn, row].MakeSoftBlock(); } }
/// <summary> /// Applies gravity. This is automatically called with TryClear(). /// </summary> private bool ApplyGravity() { bool tryClearAgain = false; for (int row = 1; row < Rows; row++) { for (int column = 0; column < Columns; column++) { GameBlockState thisState = this[column, row]; if (thisState.Empty) { continue; } else if (thisState.Hard) { ApplyGravityOnHardRow(row); continue; } GameBlockState lowestState = null; for (int lowerRow = row - 1; lowerRow > 0; lowerRow--) { GameBlockState otherState = this[column, lowerRow]; if (!otherState.Empty) { break; } lowestState = otherState; } if (lowestState != null) { thisState.MoveDown(lowestState); tryClearAgain = true; } } } return(tryClearAgain); }
public GamePane(GameSession session, GameBlock[] regularBlocks, GameBlock[] hardBlocks, float posX, float posY) { this.session = session; this.regularBlocks = regularBlocks; this.hardBlocks = hardBlocks; this.rnd = new Random(session.Game.Random.Next()); grid = new GameBlockState[Columns, Rows]; position = new Vector2(posX, posY); jitter = 0.0f; scrollY = 0.0f; scrollSpeed = DefaultScrollSpeed; cursorColumn = 0; cursorRow = 1; cursorTexture = regularBlocks[0].Texture; activeAnimations = new HashSet<GameBlockAnimation>(); for (int column = 0; column < Columns; column++) for (int row = 0; row < Rows; row++) grid[column, row] = new GameBlockState(this, column, row, (float)rnd.NextDouble()); PopulateWithRandomBlocks(7); }
public void Draw(DrawContext ctx) { List <GameBlockState> deferredBlocks = new List <GameBlockState>(); float scale = 1.0f + (float)Math.Sin(jitter * 0.5f) * 0.003f; float angle = (float)Math.Sin(jitter * 0.5) * 0.15f; ctx.Push(); ctx.Translate(position.X, position.Y); ctx.ScaleAroundPoint(scale, scale, Columns * BlockSize / 2, Rows * BlockSize / 2); ctx.RotateAroundPoint(angle, Columns * BlockSize / 3, Rows * BlockSize / 3); // cursor if (!gameOver) { ctx.Push(); ctx.Translate(GetBlockX(cursorColumn) - 4, GetBlockY(cursorRow) - 4); ctx.BindTexture(cursorTexture); ctx.SetColor(new Color4(100, 100, 100, 255)); ctx.DrawTexturedRectangle(BlockSize * 2.0f + 8, BlockSize + 8, cursorTexture); ctx.Pop(); // game over logo } else { ctx.Push(); ctx.Translate(40.0f, 240.0f); ctx.BindTexture(session.GameOverTexture); ctx.DrawTexturedRectangle(240.0f, 45.0f, session.GameOverTexture); ctx.Pop(); } // regular blocks for (int column = 0; column < Columns; column++) { for (int row = 0; row < Rows; row++) { GameBlockState blockState = this[column, row]; if (blockState.Empty) { continue; } if (blockState.DrawDeferred) { deferredBlocks.Add(blockState); } else { blockState.Draw(ctx); } } } // block independent block animations foreach (GameBlockAnimation animation in activeAnimations) { animation.Draw(ctx); } // top and bottom bars that hide partial rows if (!gameOver) { ctx.Push(); ctx.BindTexture(session.BarTexture); ctx.SetColor(new Color4(40, 40, 40, 255)); ctx.Translate(-BlockSize / 2, -35.0f); ctx.DrawTexturedRectangle(BlockSize * (Columns + 1), 40.0f, session.BarTexture); ctx.Translate(0.0f, BlockSize * Rows - BlockSize / 2); ctx.DrawTexturedRectangle(BlockSize * (Columns + 1), 50.0f, session.BarTexture); ctx.Pop(); } // selected blocks and other things we want to have on top foreach (GameBlockState blockState in deferredBlocks) { blockState.Draw(ctx); } ctx.Pop(); }
/// <summary> /// Internal helper for swapping. /// </summary> private GameBlockState SwapContentsWith(GameBlockState other) { GameBlockState clone = (GameBlockState)MemberwiseClone(); block = other.block; other.block = clone.block; jiggle = other.jiggle; other.jiggle = clone.jiggle; return clone; }
/// <summary> /// Like ExchangeWith but without the block animation. Used to move /// up blocks when a new row appears. /// </summary> public void SwapWith(GameBlockState other) { GameBlockState clone = SwapContentsWith(other); animationSourceX = other.animationSourceX; other.animationSourceX = clone.animationSourceX; animationSourceY = other.animationSourceY; other.animationSourceY = clone.animationSourceY; animationStep = other.animationStep; other.animationStep = clone.animationStep; }
/// <summary> /// Like MoveUp() but to a lower cell that is empty. /// </summary> public void MoveDown(GameBlockState lowerState) { Debug.Assert(lowerState.Empty, "Can only move down to empty states"); SwapContentsWith(lowerState); lowerState.animationSourceX = BlockX; lowerState.animationSourceY = BlockY; lowerState.animationStep = 0.0f; lowerState.animationSpeedFactor = 50.0f / (Row - lowerState.Row); }
/// <summary> /// Changes a block state's block with the one from another block state. /// The exchange happens with a little animation and afterwards both /// will also have the jiggle swapped. These two blocks must be on the /// same row though. /// </summary> public void ExchangeWith(GameBlockState other) { Debug.Assert(other.row == row); SwapContentsWith(other); animationStep = other.animationStep = 0.0f; animationSpeedFactor = other.animationSpeedFactor = 7.0f; animationSourceX = other.BlockX; animationSourceY = other.BlockY; other.animationSourceX = BlockX; other.animationSourceY = BlockY; }