示例#1
0
        public override void HandleLevelComplete(IStateOwner pOwner, GameplayGameState state)
        {
            var completionState = new DrMarioLevelCompleteState(state, () => SetupNextLevel(state, pOwner));

            pOwner.CurrentState = completionState;
        }
示例#2
0
        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;