public GameOverStatistics GetGameOverStatistics(GameplayGameState state, IStateOwner pOwner) { Type[] TetTypes = new Type[] { typeof(Tetrominoes.Tetromino_I), typeof(Tetrominoes.Tetromino_I), typeof(Tetrominoes.Tetromino_O), typeof(Tetrominoes.Tetromino_T), typeof(Tetrominoes.Tetromino_J), typeof(Tetrominoes.Tetromino_L), typeof(Tetrominoes.Tetromino_S), typeof(Tetrominoes.Tetromino_Z) }; GameOverStatistic I_Stat = GetTetrominoStatistic(state, typeof(Tetrominoes.Tetromino_I)); GameOverStatistic O_Stat = GetTetrominoStatistic(state, typeof(Tetrominoes.Tetromino_O)); GameOverStatistic T_Stat = GetTetrominoStatistic(state, typeof(Tetrominoes.Tetromino_T)); GameOverStatistic J_Stat = GetTetrominoStatistic(state, typeof(Tetrominoes.Tetromino_J)); GameOverStatistic L_Stat = GetTetrominoStatistic(state, typeof(Tetrominoes.Tetromino_L)); GameOverStatistic S_Stat = GetTetrominoStatistic(state, typeof(Tetrominoes.Tetromino_S)); GameOverStatistic Z_Stat = GetTetrominoStatistic(state, typeof(Tetrominoes.Tetromino_Z)); GameOverStatistics StatResult = new GameOverStatistics( new GameOverStatistic(new GameOverStatisticColumnData("---Line Clears---", Color.White, Color.Black, GameOverStatisticColumnData.HorizontalAlignment.Middle)), I_Stat, O_Stat, T_Stat, J_Stat, L_Stat, S_Stat, Z_Stat); return(StatResult); }
/// <summary> /// mutates the given GameplayGameState to prepare it for a new level, and returns it to the caller. /// </summary> /// <param name="mutate"></param> /// <returns></returns> public GameState SetupNextLevel(GameplayGameState mutate, IStateOwner pOwner) { Level++; PrepareField(mutate, pOwner); PrimaryBlockAppearanceState levelstarter = new PrimaryBlockAppearanceState(mutate); return(levelstarter); }
public List <Nomino> ProcessBlockDroppage(GameplayGameState state, int Column, int Row, ref HashSet <Nomino> AdditionalSkipBlocks) { List <Nomino> CreateResult = new List <Nomino>(); var currentblock = state.PlayField.Contents[Row][Column]; bool isPopping = false; if (currentblock != null) { if (currentblock is CascadingBlock cb) { if (currentblock is LineSeriesBlock lsb) { isPopping = lsb.Popping; //blocks that are popping shouldn't be resurrected. } if (!isPopping && !cb.IsSupported(cb.Owner, state.PlayField, new[] { cb }.ToList()) && !AdditionalSkipBlocks.Contains(cb.Owner)) { //we initialize the list of recursion blocks to the block we are testing, since it cannot support itself. //resurrect this block and other blocks that are in the same nomino. //since we remove busted blocks from the nomino, we can take the Duomino this //block belongs to and add it back to the Active Groups, then remove all the blocks that are in the nomino from the field. foreach (var iterate in cb.Owner) { var useX = iterate.X + cb.Owner.X; var useY = iterate.Y + cb.Owner.Y; state.PlayField.Contents[useY][useX] = null; } Nomino resurrect = cb.Owner; resurrect.Controllable = false; resurrect.FallSpeed = 250; resurrect.MoveSound = true; resurrect.PlaceSound = false; resurrect.NoGhost = true; AdditionalSkipBlocks.Add(resurrect); CreateResult.Add(resurrect); } } //now recursively process for the block to our left, the block to our right, and the block above. But only if that block is not part of the same nomino as currentblock or currentblock is null. foreach (Point offset in new Point[] { new Point(-1, 0), new Point(0, -1), new Point(1, 0) }) { var checkblock = state.PlayField.Contents[Row + offset.Y][Column + offset.X]; if (checkblock != null && (currentblock == null || !currentblock.Owner.HasBlock(checkblock))) { List <Nomino> CurrResult = ProcessBlockDroppage(state, Column + offset.X, Row + offset.Y, ref AdditionalSkipBlocks); foreach (var iterateresult in CurrResult) { CreateResult.Add(iterateresult); } } } } return(CreateResult); }
/// <summary> /// Creates a Replay State given a State owner and a standard game state. returns the resulting replay state. /// </summary> /// <param name="pOwner"></param> /// <param name="SourceState"></param> /// <returns></returns> public StatefulReplayState CreateReplayState(IStateOwner pOwner, GameplayGameState SourceState) { StatefulReplayState newState = new StatefulReplayState(pOwner, SourceState); if (!ReplayData.ContainsKey(newState.ElapsedGameTime)) { ReplayData.Add(newState.ElapsedGameTime, new List <StatefulReplayState>()); } ReplayData[newState.ElapsedGameTime].Add(newState); return(newState); }
//GeneratePopParticles(pOwner, state, iterate); private void GeneratePopParticles(IStateOwner pOwner, GameplayGameState gstate, SKPointI pt) { var rgen = TetrisGame.rgen; var popItem = gstate.PlayField.Contents[pt.Y][pt.X]; BCColor[] useColor = YellowColors; if (popItem is LineSeriesBlock lsb) { switch (lsb.CombiningIndex) { case LineSeriesBlock.CombiningTypes.Red: useColor = RedColors; break; case LineSeriesBlock.CombiningTypes.Blue: useColor = BlueColors; break; case LineSeriesBlock.CombiningTypes.Yellow: useColor = YellowColors; break; case LineSeriesBlock.CombiningTypes.Orange: useColor = OrangeColors; break; case LineSeriesBlock.CombiningTypes.Magenta: useColor = MagentaColors; break; case LineSeriesBlock.CombiningTypes.Green: useColor = GreenColors; break; } for (int i = 0; i < ParticlesPerPop; i++) { PointF Offset = new PointF((float)rgen.NextDouble(), (float)rgen.NextDouble()); BCColor selColor = TetrisGame.Choose(useColor); BCPoint Velocity = TetrisGame.Choose(CardinalOptions); float Speed = (float)rgen.NextDouble() * (MAX_SPEED - MIN_SPEED) + MIN_SPEED; float Sign = TetrisGame.Choose(new float[] { -1f, 1f }); BCPoint VelocityUse = new BCPoint(Velocity.X * Speed * Sign, Velocity.Y * Speed * Sign); BaseParticle bp = new BaseParticle(new BCPoint(pt.X + Offset.X, pt.Y + Offset.Y), VelocityUse, selColor); gstate.Particles.Add(bp); } } /*for (int i=0;i<ParticlesPerPop;i++) * { * * }*/ }
private void DrawTetrominoStat(GameOverGameState Self, Type TetronimoType, PointF BasePosition, Graphics Target, RectangleF Bounds, Font GameOverFont) { GameplayGameState standardgame = Self.GameOveredState as GameplayGameState; var useStats = standardgame.GameStats as TetrisStatistics; Image I_Tet = standardgame.GetTetrominoImage(TetronimoType); Target.DrawImage(I_Tet, new PointF(BasePosition.X - (float)(I_Tet.Width) / 2, BasePosition.Y)); PointF TextPos = new PointF(BasePosition.X + Bounds.Width / 2, BasePosition.Y - 10); Target.DrawString(useStats.GetLineCount(TetronimoType).ToString(), GameOverFont, Brushes.White, 5 + TextPos.X, 5 + TextPos.Y); Target.DrawString(useStats.GetLineCount(TetronimoType).ToString(), GameOverFont, Brushes.Black, TextPos.X, TextPos.Y); }
/// <summary> /// Searches the Field to find Critical masses. These are cases where the necessary number of blocks are lined up. /// returns the position (X=Column, Y=Row) in the gamefield that these were found. /// </summary> /// <param name="state"></param> /// <param name="pOwner"></param> /// <param name="StartPosition"></param> /// <returns></returns> private HashSet <Point> FindCriticalMasses(GameplayGameState state, IStateOwner pOwner, Point StartPosition) { HashSet <Point> Horizontal = FindCriticalMassesHorizontal(state, pOwner, StartPosition); HashSet <Point> Vertical = FindCriticalMassesVertical(state, pOwner, StartPosition); foreach (Point verticalpoint in Vertical) { if (!Horizontal.Contains(verticalpoint)) { Horizontal.Add(verticalpoint); } } return(Horizontal); }
public override void AIActionFrame() { //do our hard thinking here. //first we only do stuff with the standard game state. if (_Owner == null) { return; } if (_Owner.CurrentState is GameplayGameState) { GameplayGameState stdState = _Owner.CurrentState as GameplayGameState; //next, we only want to do stuff if there is one active blockgroup... if (stdState.PlayField.GetActiveBlockGroups().Count == 1) { //todo: we want to copy the playfield for our inspection here... we'll want to see what happens based on moving the blockgroup left or right up to each side and dropping it and evaluate the result to select the ideal //then slap those keys into the queue. Nomino ActiveGroup = stdState.PlayField.BlockGroups[0]; var PossibleStates = GetPossibleResults(stdState.PlayField.Contents, ActiveGroup, ScoringRules).ToList(); Debug.Print("Found " + PossibleStates.Count + " possible states..."); var Sorted = (ScoringRules.Moronic?PossibleStates.OrderByDescending((w) => TetrisGame.rgen.Next()): PossibleStates.OrderByDescending((w) => w.GetScore(stdState.GameHandler.GetType(), ScoringRules))).ToList(); //var Scores = (from p in PossibleStates orderby p.GetScore(ScoringRules) descending select new Tuple<StoredBoardState, double>(p, p.GetScore(ScoringRules))).ToArray(); /*foreach (var writedebug in Scores) * { * Debug.Print("Possible State: Move " + writedebug.Item1.XOffset + ", Rotate " + writedebug.Item1.RotationCount + " To get score " + writedebug.Item1.GetScore(ScoringRules)); * Debug.Print("What it will look like\n" + writedebug.Item1.GetBoardString()); * Debug.Print("------"); * }*/ var maximumValue = Sorted.FirstOrDefault(); Debug.Print("Best Move: Move " + maximumValue.XOffset + ", Rotate " + maximumValue.RotationCount + " To get score " + maximumValue.GetScore(stdState.GameHandler.GetType(), ScoringRules)); Debug.Print("What it will look like\n" + maximumValue.GetBoardString()); Debug.Print("------"); //int randomint = TetrisGame.rgen.Next(Scores.Length); //int randomint2 = TetrisGame.rgen.Next(Scores.Length); //StoredBoardState FirstState = Scores[randomint2].Item1; StoredBoardState IdealState = maximumValue; PushButtonInputs(IdealState); //if(maximumValue!=null) //{ // PushButtonInputs(maximumValue); //} } } }
private GameOverStatistic GetTetrominoStatistic(GameplayGameState state, Type TetrominoType) { GameplayGameState standardgame = state; var useStats = standardgame.GameStats as TetrisStatistics; SKBitmap I_Tet = standardgame.GetTetrominoSKBitmap(TetrominoType); SKImage ski = SKImage.FromBitmap(I_Tet); GameOverStatistic result = new GameOverStatistic( new GameOverStatisticColumnData(ski, GameOverStatisticColumnData.HorizontalAlignment.Left, GameOverStatisticColumnData.VerticalAlignment.Top), new GameOverStatisticColumnData(Statistics.GetLineCount(TetrominoType).ToString(), SKColors.White, SKColors.Black, GameOverStatisticColumnData.HorizontalAlignment.Right, GameOverStatisticColumnData.VerticalAlignment.Middle)); return(result); }
private void DrawTetrominoStat(GameOverGameState Self, Type TetronimoType, SKPoint BasePosition, SKCanvas Target, SKRect Bounds, SKPaint paint) { GameplayGameState standardgame = Self.GameOveredState as GameplayGameState; var useStats = standardgame.GameStats as TetrisStatistics; SKBitmap I_Tet = standardgame.GetTetrominoSKBitmap(TetronimoType); Target.DrawBitmap(I_Tet, new SKPoint(BasePosition.X - (float)(I_Tet.Width) / 2, BasePosition.Y)); SKPoint TextPos = new SKPoint(BasePosition.X + Bounds.Width / 2, BasePosition.Y - 10); String LineCount = useStats.GetLineCount(TetronimoType).ToString(); paint.Color = SKColors.White; Target.DrawText(LineCount, TextPos.X + 5, TextPos.Y + 5, paint); paint.Color = SKColors.Black; Target.DrawText(LineCount, TextPos.X, TextPos.Y, paint); }
public StatefulReplayState(IStateOwner pOwner, GameplayGameState Source) { var Field = Source.PlayField; Rows = Field.RowCount; Columns = Field.ColCount; ElapsedGameTime = pOwner.GetElapsedTime(); BoardState = new StatefulReplayStateBlockInformation[Field.RowCount][]; for (int y = 0; y < Field.RowCount; y++) { BoardState[y] = new StatefulReplayStateBlockInformation[Field.ColCount]; for (int x = 0; x < BoardState[y].Length; x++) { BoardState[y][x] = new StatefulReplayStateBlockInformation(StatefulReplayStateBlockInformation.BlockInformation.Block_Empty); } } //step one: set the Active Block positions. foreach (var activegroup in Source.PlayField.BlockGroups) { int XGroup = activegroup.X; int YGroup = activegroup.Y; foreach (var iterateblock in activegroup) { int BlockX = iterateblock.X + XGroup; int BlockY = iterateblock.Y + YGroup; BoardState[BlockY][BlockX] = new StatefulReplayStateBlockInformation(StatefulReplayStateBlockInformation.BlockInformation.Block_Active); } } //step two: occupied block positions. for (int CurrentRow = 0; CurrentRow < Source.PlayField.Contents.Length; CurrentRow++) { for (int CurrentCol = 0; CurrentCol < Source.PlayField.Contents[CurrentRow].Length; CurrentCol++) { var thisBlock = Source.PlayField.Contents[CurrentRow][CurrentCol]; if (thisBlock != null) { BoardState[CurrentRow][CurrentCol].State = StatefulReplayStateBlockInformation.BlockInformation.Block_Occupied; } else { BoardState[CurrentRow][CurrentCol].State = StatefulReplayStateBlockInformation.BlockInformation.Block_Empty; } } } }
private int GetScore(int LinesCleared, IList <HotLine> ClearedHotLines, GameplayGameState state, IStateOwner pOwner, Nomino Trigger) { var GameStats = Statistics; int result = LinesCleared; int AddScore = 0; if (result >= 1) { AddScore += ((GameStats.LineCount / 10) + 1) * 15; } if (result >= 2) { AddScore += ((GameStats.LineCount / 10) + 2) * 30; } if (result >= 3) { AddScore += ((GameStats.LineCount / 10) + 3) * 45; } if (result >= 4) { AddScore += AddScore + ((GameStats.LineCount / 10) + 5) * 75; } if (ClearedHotLines != null) { double SumMult = 00; foreach (var iterate in HotLines) { SumMult += iterate.Multiplier; } AddScore = (int)((double)AddScore * SumMult); } LastScoreCalc = AddScore; if (LastScoreLines == result) //getting the same lines in a row gives added score. { AddScore *= 2; } LastScoreLines = result; return(AddScore); }
protected override void OnKeyDown(KeyboardKeyEventArgs e) { _Present.IgnoreController = true; if (e.Key == Key.G) { if (_Present.Game.CurrentState is GameplayGameState) { GameplayGameState gs = _Present.Game.CurrentState as GameplayGameState; NominoBlock[][] inserts = new NominoBlock[4][]; for (int i = 0; i < inserts.Length; i++) { inserts[i] = new NominoBlock[gs.PlayField.ColCount]; for (int c = 1; c < inserts[i].Length; c++) { inserts[i][c] = new StandardColouredBlock() { BlockColor = Color.Red, DisplayStyle = StandardColouredBlock.BlockStyle.Style_CloudBevel }; } } InsertBlockRowsActionGameState irs = new InsertBlockRowsActionGameState(gs, 0, inserts, Enumerable.Empty <Action>()); CurrentState = irs; } } else if (e.Key == Key.C) { if (e.Shift && e.Control) { EnterCheatState cheatstate = new EnterCheatState(CurrentState, _Present.Game, 64); CurrentState = cheatstate; } } Debug.Print("Button pressed:" + e.Key); var translated = _Present.TranslateKey(e.Key); if (translated != null) { _Present.Game.HandleGameKey(this, translated.Value, TetrisGame.KeyInputSource.Input_HID); _Present.GameKeyDown(translated.Value); } }
public void StartGame(GameHandlingConstants option = GameHandlingConstants.Handle_GameThread) { String sDataFolder = TetrisGame.AppDataFolder; String sSettingsFile = Path.Combine(sDataFolder, "Settings.xml"); GameSettings = new SettingsManager(sSettingsFile, _Owner); AudioThemeMan = new AudioThemeManager(AudioTheme.GetDefault(GameSettings.std.SoundScheme)); AudioThemeMan.ResetTheme(); if (GameLoopsRunning) { return; } GameLoopsRunning = true; var standardstate = new GameplayGameState(new StandardTetrisHandler(), null, TetrisGame.Soundman, null); //_Owner.GameStartTime = DateTime.MinValue; Game = new TetrisGame(_Owner, standardstate); //standardstate.Chooser = new MeanChooser(standardstate,Tetromino.StandardTetrominoFunctions); if (GameThread != null) { GameThread.Abort(); GameThread = null; } if (option == GameHandlingConstants.Handle_GameThread) { GameThread = new Thread(GameProc); GameThread.Start(); } if (InputThread != null) { InputThread.Abort(); } InputThread = new Thread(GamepadInputThread); InputThread.Start(); }
protected BCPoint TranslatePosition(IStateOwner pOwner, SKCanvas pRenderTarget, BCPoint Position, GameStateSkiaDrawParameters Element) { BCPoint Result = Position; GameplayGameState foundstandard = null; if (pOwner.CurrentState is GameplayGameState standard) { foundstandard = standard; } else if (pOwner.CurrentState is ICompositeState <GameplayGameState> composite) { foundstandard = composite.GetComposite(); } if (foundstandard != null) { Result.X = foundstandard.PlayField.GetBlockWidth(Element.Bounds) * Position.X; Result.Y = foundstandard.PlayField.GetBlockHeight(Element.Bounds) * Position.Y; } return(Result); }
public void PrepareField(GameplayGameState state, IStateOwner pOwner) { //likely will need to have stats and stuff abstracted to each Handler. state.PlayField.Reset(); HashSet <SKPointI> usedPositions = new HashSet <SKPointI>(); //primary count is based on our level. int numPrimaries = (int)((Level * 1.33f) + 4); for (int i = 0; i < numPrimaries; i++) { //choose a random primary type. var chosentype = TetrisGame.Choose(GetValidPrimaryCombiningTypes()); LineSeriesPrimaryBlock lsmb = new LineSeriesPrimaryBlock() { CombiningIndex = chosentype }; var Dummino = new Nomino() { }; Dummino.AddBlock(new Point[] { new Point(0, 0) }, lsmb); state.PlayField.Theme.ApplyTheme(Dummino, this, state.PlayField, NominoTheme.ThemeApplicationReason.Normal); lsmb.CriticalMass = 4; //TODO: should this be changed? int RandomXPos = TetrisGame.rgen.Next(state.PlayField.ColCount); int RandomYPos = state.PlayField.RowCount - 1 - TetrisGame.rgen.Next(state.PlayField.RowCount / 2); SKPointI randomPos = new SKPointI(RandomXPos, RandomYPos); while (usedPositions.Contains(randomPos)) { int rndXPos = TetrisGame.rgen.Next(state.PlayField.ColCount); int rndYPos = state.PlayField.RowCount - 1 - TetrisGame.rgen.Next(state.PlayField.RowCount / 2); randomPos = new SKPointI(rndXPos, rndYPos); } state.PlayField.Contents[RandomYPos][RandomXPos] = lsmb; PrimaryBlockCount++; } PrimaryBlockAppearanceState appearstate = new PrimaryBlockAppearanceState(state); pOwner.CurrentState = appearstate; }
public void DrawProc(GameplayGameState pState, IStateOwner pOwner, Graphics g, RectangleF Bounds) { if (useBackground == null || !StoredBackground.Equals(Bounds) || pState.DoRefreshBackground) { RefreshBackground(pState, Bounds); } g.DrawImage(useBackground, Bounds); var PlayField = pState.PlayField; if (PlayField != null) { PlayField.Draw(pOwner, g, Bounds); } foreach (var activeblock in PlayField.BlockGroups) { int dl = 0; var GrabGhost = pState.GetGhostDrop(pOwner, activeblock, out dl, 3); if (GrabGhost != null) { var BlockWidth = PlayField.GetBlockWidth(Bounds); var BlockHeight = PlayField.GetBlockHeight(Bounds); foreach (var iterateblock in activeblock) { RectangleF BlockBounds = new RectangleF(BlockWidth * (GrabGhost.X + iterateblock.X), BlockHeight * (GrabGhost.Y + iterateblock.Y - 2), PlayField.GetBlockWidth(Bounds), PlayField.GetBlockHeight(Bounds)); TetrisBlockDrawGDIPlusParameters tbd = new TetrisBlockDrawGDIPlusParameters(g, BlockBounds, GrabGhost, pOwner.Settings); ImageAttributes Shade = new ImageAttributes(); Shade.SetColorMatrix(ColorMatrices.GetFader(0.5f)); tbd.ApplyAttributes = Shade; //tbd.OverrideBrush = GhostBrush; var GetHandler = RenderingProvider.Static.GetHandler(typeof(Graphics), iterateblock.Block.GetType(), typeof(TetrisBlockDrawGDIPlusParameters)); GetHandler.Render(pOwner, tbd.g, iterateblock.Block, tbd); //iterateblock.Block.DrawBlock(tbd); } } } }
private void RefreshBackground(GameplayGameState pState, RectangleF buildSize) { StoredBackground = buildSize; useBackground = new Bitmap((int)buildSize.Width, (int)buildSize.Height, PixelFormat.Format32bppPArgb); using (Graphics bgg = Graphics.FromImage(useBackground)) { var bgdrawdata = pState.PlayField.Theme.GetThemePlayFieldBackground(pState.PlayField, pState.GameHandler); var drawbg = bgdrawdata.BackgroundImage; bgg.CompositingQuality = CompositingQuality.AssumeLinear; bgg.InterpolationMode = InterpolationMode.NearestNeighbor; bgg.SmoothingMode = SmoothingMode.HighSpeed; if (bgdrawdata.TintColor != Color.Transparent) { ImageAttributes useAttributes = new ImageAttributes(); var getmatrix = ColorMatrices.GetColourizer(bgdrawdata.TintColor); useAttributes.SetColorMatrix(getmatrix); bgg.DrawImage(drawbg, new RectangleF(0f, 0f, buildSize.Width, buildSize.Height), useAttributes); } else { bgg.DrawImage(drawbg, new RectangleF(0f, 0f, buildSize.Width, buildSize.Height), new RectangleF(0f, 0f, drawbg.Width, drawbg.Height), GraphicsUnit.Pixel); } } }
public virtual Dictionary <String, String> GetDisplayStatistics(IStateOwner pOwner, GameplayGameState Source) { //stats: Time, Score, Top Score var TopScore = Source.GetLocalScores() == null ? 0 : Source.GetLocalScores().GetScores().First().Score; int MaxScoreLength = Math.Max(TopScore.ToString().Length, Score.ToString().Length); String CurrentScoreStr = Score.ToString().PadLeft(MaxScoreLength); String TopScoreStr = TopScore.ToString().PadLeft(MaxScoreLength); String[] StatLabels = new string[] { "Time:", "Score:", "Top:", "Lines:" }; return(new Dictionary <string, string>() { { "Time", FormatGameTime(pOwner) }, { "Score", CurrentScoreStr }, { "Top", TopScoreStr } }); }
public BaseAIChooser(GameplayGameState _StandardState, Func <Nomino>[] pAvailable) : base(pAvailable) { _State = _StandardState; }
private HashSet <Point> FindCriticalMassesVertical(GameplayGameState state, IStateOwner pOwner, Point StartPosition) { HashSet <Point> FoundPoints = new HashSet <Point>(); var OurPos = state.PlayField.Contents[StartPosition.Y][StartPosition.X] as LineSeriesBlock; if (OurPos == null) { return(new HashSet <Point>()); } else { int FirstRow = StartPosition.Y; //first, we look up until we no longer find a matching block. for (int Y = StartPosition.Y - 1; Y > 0; Y--) { var CheckPos = state.PlayField.Contents[Y][StartPosition.X] as LineSeriesBlock; if (CheckPos == null) { break; } if (CheckPos.CombiningIndex == OurPos.CombiningIndex) { FirstRow = Y; } else { break; } } //starting from FirstCol we will work through and find all matching combining indices.... int VerticalMass = 0; //start at one since we know we have one block. int MaxCriticalVert = 4; List <Point> Verticals = new List <Point>(); Verticals.Add(StartPosition); for (int Y = FirstRow; Y < state.PlayField.RowCount; Y++) { var CheckPos = state.PlayField.Contents[Y][StartPosition.X] as LineSeriesBlock; if (CheckPos == null) { break; } if (CheckPos.CombiningIndex == OurPos.CombiningIndex) { MaxCriticalVert = Math.Max(MaxCriticalVert, CheckPos.CriticalMass); Verticals.Add(new Point(StartPosition.X, Y)); VerticalMass++; } else { break; } } if (VerticalMass >= MaxCriticalVert) { foreach (var iterate in Verticals) { if (!FoundPoints.Contains(iterate)) { FoundPoints.Add(iterate); } } } } return(FoundPoints); }
public GameOverStatistics GetGameOverStatistics(GameplayGameState state, IStateOwner pOwner) { return(null); }
public override void HandleLevelComplete(IStateOwner pOwner, GameplayGameState state) { throw new NotImplementedException(); }
public abstract void HandleLevelComplete(IStateOwner pOwner, GameplayGameState state);
//finds critical mass excesses starting from the given position. //this basically only finds one specific "critical mass" private HashSet <Point> FindCriticalMassesHorizontal(GameplayGameState state, IStateOwner pOwner, Point StartPosition) { HashSet <Point> FoundPoints = new HashSet <Point>(); var OurPos = state.PlayField.Contents[StartPosition.Y][StartPosition.X] as LineSeriesBlock; if (OurPos == null) { return(new HashSet <Point>()); } else { int FirstCol = StartPosition.X; //first, we look to the left until we no longer find a matching block. for (int X = StartPosition.X - 1; X > 0; X--) { var CheckPos = state.PlayField.Contents[StartPosition.Y][X] as LineSeriesBlock; if (CheckPos == null) { break; } if (CheckPos.CombiningIndex == OurPos.CombiningIndex) { FirstCol = X; } else { break; } } //starting from FirstCol we will work through and find all matching combining indices.... int HorizontalMass = 1; //start at one since we know we have one block. int MaxCriticalHorz = 4; List <Point> Horizontals = new List <Point>(); Horizontals.Add(StartPosition); for (int X = FirstCol; X < state.PlayField.ColCount; X++) { var CheckPos = state.PlayField.Contents[StartPosition.Y][X] as LineSeriesBlock; if (CheckPos == null) { break; } if (CheckPos.CombiningIndex == OurPos.CombiningIndex) { MaxCriticalHorz = Math.Max(MaxCriticalHorz, CheckPos.CriticalMass); Horizontals.Add(new Point(X, StartPosition.Y)); HorizontalMass++; } else { break; } } if (HorizontalMass > MaxCriticalHorz) { foreach (var iterate in Horizontals) { if (!FoundPoints.Contains(iterate)) { FoundPoints.Add(iterate); } } } } return(FoundPoints); }
public void PrepareField(GameplayGameState state, IStateOwner pOwner) { //nothing needed here. }
public override Dictionary <string, string> GetDisplayStatistics(IStateOwner pOwner, GameplayGameState Source) { var stats = base.GetDisplayStatistics(pOwner, Source); stats.Add("Level", (Source.GameHandler as DrMarioHandler).Level.ToString()); stats.Add("Virus", (Source.GameHandler as DrMarioHandler).PrimaryBlockCount.ToString()); //we want to show a VIRUS: count too. //implement that into the handler first for us to access here!. return(stats); }
public override void HandleLevelComplete(IStateOwner pOwner, GameplayGameState state) { var completionState = new DrMarioLevelCompleteState(state, () => SetupNextLevel(state, pOwner)); pOwner.CurrentState = completionState; }
public override Dictionary <string, string> GetDisplayStatistics(IStateOwner pOwner, GameplayGameState Source) { var result = base.GetDisplayStatistics(pOwner, Source); int LineCount = Source.GameStats is TetrisStatistics ? (Source.GameStats as TetrisStatistics).LineCount : 0; result.Add("Lines", LineCount.ToString()); return(result); }
public FieldChangeResult ProcessFieldChange(GameplayGameState state, IStateOwner pOwner, Nomino Trigger) { if (state.PlayField.GetActiveBlockGroups().Count() > 0) { return new FieldChangeResult() { ScoreResult = 0 } } ; //here we would go through the field and handle where the blocks line up to more than the required critical mass. //Nomino's have two blocks- usually. But, we should account for more. This handler may be expanded for the Tetris2 handler, (if we ever bother to make one) //in any case we want to check all the positions of the trigger nomino and check for critical masses. int MasterCount = 0; bool LevelCompleted = false; HashSet <Point> CriticalMasses = null; for (int y = 0; y < state.PlayField.RowCount; y++) { var currRow = state.PlayField.Contents[y]; for (int x = 0; x < state.PlayField.ColCount; x++) { if (state.PlayField.Contents[y][x] is LineSeriesPrimaryBlock) { MasterCount++; } if (state.PlayField.Contents[y][x] is LineSeriesBlock) { var foundmasses = FindCriticalMasses(state, pOwner, new Point(x, y)); foreach (var iterate in foundmasses) { if (CriticalMasses == null) { CriticalMasses = new HashSet <Point>(foundmasses); } else if (!CriticalMasses.Contains(iterate)) { CriticalMasses.Add(iterate); } } } } } PrimaryBlockCount = MasterCount; //if MasterCount is 0 then we completed this level. //if there are no primary blocks left, this level is now complete. We need a "Level complete" screen state with an overlay- we would switch to that state. It should //operate similar to the TemporaryInputPauseGameState in that we provide a routine to be called after the user opts to press a button to continue. if (MasterCount == 0) { LevelCompleted = true; } if (CriticalMasses != null && CriticalMasses.Any()) { state.NoTetrominoSpawn = true; //process the critical masses. //first: we need to switch the blocks in question to "pop" them. //then we need to switch to a temporary state that allows them to display as "popped" for a moment or so, without processing drops or other actions. //after the delay expires, the state will then process the critical mass blocks, changing the underlying nomino to remove the deleted block, so that if only part of a nomino is cleared //the other parts are separated from it. //then it will check the full field again, changing unsupported field blocks into active groups and removing them from the field. // Blocks that are part of a nomino will be resurrected with the other blocks that are part of that nomino.) //check the field again and change unsupported field blocks back into active groups. HashSet <Nomino> MassNominoes = new HashSet <Nomino>(); foreach (var iterate in CriticalMasses) { var popItem = state.PlayField.Contents[iterate.Y][iterate.X]; if (popItem is LineSeriesBlock lsb) { lsb.Popping = true; GeneratePopParticles(pOwner, state, new SKPointI(iterate.X, iterate.Y)); if (popItem.Owner != null) { state.PlayField.Theme.ApplyTheme(popItem.Owner, this, state.PlayField, NominoTheme.ThemeApplicationReason.Normal); } else { var Dummino = new Nomino() { }; Dummino.AddBlock(new Point[] { new Point(0, 0) }, popItem); state.PlayField.Theme.ApplyTheme(Dummino, this, state.PlayField, NominoTheme.ThemeApplicationReason.Normal); } } if (popItem.Owner != null) { popItem.Owner.RemoveBlock(popItem); } state.PlayField.HasChanged = true; } var originalstate = state; state.Sounds.PlaySound(pOwner.AudioThemeMan.BlockPop.Key); //need to determine a way to detect chains here, where we create an active block and then it results in another "pop". TemporaryInputPauseGameState tpause = new TemporaryInputPauseGameState(state, 1000, (owner) => { //first, remove the CriticalMasses altogether. foreach (var iterate in CriticalMasses) { //clear out the cell at the appropriate position. var popItem = state.PlayField.Contents[iterate.Y][iterate.X]; state.PlayField.Contents[iterate.Y][iterate.X] = null; //now apply the theme to the specified location if (popItem.Owner != null) { state.PlayField.Theme.ApplyTheme(popItem.Owner, this, state.PlayField, NominoTheme.ThemeApplicationReason.Normal); } else { //create a "dummy" nomino for the application of the "pop" theme animation. var Dummino = new Nomino() { }; Dummino.AddBlock(new Point[] { new Point(0, 0) }, popItem); state.PlayField.Theme.ApplyTheme(Dummino, this, state.PlayField, NominoTheme.ThemeApplicationReason.Normal); } } //algorithm change: instead of going through the entire field, we'll go through all the critical masses. //With Each one: //check the block to the left, to the right, and above. //next, go through the entire field. List <NominoBlock> CheckedBlocks = new List <NominoBlock>(); HashSet <Nomino> ResurrectNominos = new HashSet <Nomino>(); HashSet <CascadingBlock> AddedBlockAlready = new HashSet <CascadingBlock>(); //keep track of the blocks we've examined already. for (int row = 0; row < state.PlayField.RowCount; row++) { for (int column = 0; column < state.PlayField.ColCount; column++) { var currentblock = state.PlayField.Contents[row][column]; bool isPopping = false; if (currentblock != null) { if (currentblock is CascadingBlock cb) { if (currentblock is LineSeriesBlock lsb) { isPopping = lsb.Popping; //blocks that are popping shouldn't be resurrected. } if (row < 5) { ; } if (!isPopping && !cb.IsSupported(cb.Owner, state.PlayField) && !ResurrectNominos.Contains(cb.Owner) && !AddedBlockAlready.Contains(cb)) { //resurrect this block and other blocks that are in the same nomino. //since we remove busted blocks from the nomino, we can take the Duomino this //block belongs to and add it back to the Active Groups, then remove all the blocks that are in the nomino from the field. foreach (var iterate in cb.Owner) { var useX = iterate.X + cb.Owner.X; var useY = iterate.Y + cb.Owner.Y; state.PlayField.Contents[useY][useX] = null; } Nomino resurrect = cb.Owner; resurrect.Controllable = false; resurrect.FallSpeed = 250; resurrect.InitialY = resurrect.Y; resurrect.LastFall = pOwner.GetElapsedTime(); resurrect.MoveSound = true; resurrect.PlaceSound = false; resurrect.NoGhost = true; ResurrectNominos.Add(resurrect); AddedBlockAlready.Add(cb); } //state.PlayField.AddBlockGroup(resurrect); } } //now recursively process for the block to our left, the block to our right, and the block above. But only if that block is not part of the same nomino as currentblock or currentblock is null. } } if (ResurrectNominos.Any()) { HashSet <Point> AddedPoints = new HashSet <Point>(); foreach (var addresurrected in ResurrectNominos) { List <Point> AllPoints = (from b in addresurrected select new Point(b.X + addresurrected.X, b.Y + addresurrected.Y)).ToList(); if (!AllPoints.Any((w) => AddedPoints.Contains(w))) { state.PlayField.AddBlockGroup(addresurrected); foreach (var point in AllPoints) { AddedPoints.Add(point); } } } } originalstate.NoTetrominoSpawn = false; originalstate.PlayField.HasChanged = true; originalstate.SuspendFieldSet = true; //if we determined the level was completed earlier, //we need to switch to the level completion state, and from there will need to resume starting with that new level. if (LevelCompleted) { LevelCompleted = false; HandleLevelComplete(owner, state); } else { owner.CurrentState = originalstate; } }); pOwner.CurrentState = tpause; } if (LevelCompleted) { LevelCompleted = false; var completionState = new DrMarioLevelCompleteState(state, () => SetupNextLevel(state, pOwner)); pOwner.CurrentState = completionState; } //Remove those blocks from the field. //then, reprocess the field: find any unsupported blocks, and generate new ActiveBlockGroups for them. Add them to the list of active block groups. Set the fallspeed appropriately. //if we found any unsupported blocks groups, change the state to the GroupFallState (not defined) which is a composite state that doesn't allow input, and waits for all active block groups to come to rest before //continuing. //once all block groups come to rest, ProcessFieldChange will be called again. //Note: for visual flair eventually we'll want to have a temporary state which does nothing but allow the blocks being destroyed to be indicated for perhaps 250ms, before advancing to the state where blocks //will fall return(new FieldChangeResult() { ScoreResult = 5 }); } const int ParticlesPerPop = 400;