private void PerformRotation(IStateOwner pOwner, Nomino grp, bool ccw) { if (!grp.Controllable) { return; } grp.Rotate(ccw); Sounds.PlaySound(pOwner.AudioThemeMan.BlockGroupRotate.Key, pOwner.Settings.std.EffectVolume); pOwner.Feedback(0.3f, 100); grp.Clamp(PlayField.RowCount, PlayField.ColCount); }
public void Feedback(float Strength, int Length) { GameOwner?.Feedback(Strength, Length); }
public override void HandleGameKey(IStateOwner pOwner, GameKeys g) { if (HandleBlockGroupKey(pOwner, g)) { return; } if (g == GameKeys.GameKey_RotateCW || g == GameKeys.GameKey_RotateCCW) { bool ccw = g == GameKeys.GameKey_RotateCCW; foreach (var activeitem in PlayField.BlockGroups) { if (!activeitem.Controllable) { continue; } if (PlayField.CanRotate(activeitem, ccw)) { PerformRotation(pOwner, activeitem, ccw); } else if (this.GameOptions.AllowWallKicks) { //we will add up to 3 and subtract up to 3 to the X coordinate. if any say we can rotate then we proceed with allowing the rotation. int[] checkoffsets = new int[] { 1, -1, 2, -2, 3, -3 }; int OriginalPos = activeitem.X; Boolean revertpos = true; foreach (int currentoffset in checkoffsets) { if (currentoffset == 0) { continue; } activeitem.X = OriginalPos + currentoffset; if (PlayField.CanRotate(activeitem, ccw)) { PerformRotation(pOwner, activeitem, ccw); revertpos = false; break; } } if (revertpos) { activeitem.X = OriginalPos; } } } } else if (g == GameKeys.GameKey_Down) { HandleActiveGroups(pOwner, true); } else if (g == GameKeys.GameKey_Drop) { //drop all active groups. Nomino FirstGroup = PlayField.BlockGroups.FirstOrDefault(); if (FirstGroup != null) { //store the block positions for each block in the nomino. foreach (var activeitem in PlayField.BlockGroups) { if (!activeitem.Controllable) { continue; } List <Tuple <BCPoint, NominoElement> > StartBlockPositions = new List <Tuple <BCPoint, NominoElement> >(); List <Tuple <BCPoint, NominoElement> > EndBlockPositions = new List <Tuple <BCPoint, NominoElement> >(); foreach (var element in activeitem) { StartBlockPositions.Add(new Tuple <BCPoint, NominoElement>(new BCPoint(activeitem.X + element.X, activeitem.Y + element.Y), element)); } int dropqty = 0; var ghosted = GetGhostDrop(pOwner, activeitem, out dropqty, 0); foreach (var element in ghosted) { EndBlockPositions.Add(new Tuple <BCPoint, NominoElement>(new BCPoint(ghosted.X + element.X, ghosted.Y + element.Y), element)); } GenerateDropParticles(StartBlockPositions, EndBlockPositions); activeitem.X = ghosted.X; activeitem.SetY(pOwner, ghosted.Y); PlayField.SetGroupToField(activeitem); PlayField.RemoveBlockGroup(activeitem); if (GameStats is TetrisStatistics ts) { GameStats.AddScore((dropqty * (5 + (ts.LineCount / 10)))); } } pOwner.Feedback(0.6f, 200); Sounds.PlaySound(pOwner.AudioThemeMan.BlockGroupPlace.Key, pOwner.Settings.std.EffectVolume); GameHandler.ProcessFieldChange(this, pOwner, FirstGroup); //ProcessFieldChangeWithScore(pOwner, FirstGroup); } } else if (g == GameKeys.GameKey_Right || g == GameKeys.GameKey_Left) { int XMove = g == GameKeys.GameKey_Right ? 1 : -1; foreach (var ActiveItem in PlayField.BlockGroups) { if (!ActiveItem.Controllable) { continue; } if (PlayField.CanFit(ActiveItem, ActiveItem.X + XMove, ActiveItem.Y, false) == TetrisField.CanFitResultConstants.CanFit) { lastHorizontalMove = DateTime.Now; ActiveItem.X += XMove; Sounds.PlaySound(pOwner.AudioThemeMan.BlockGroupMove.Key, pOwner.Settings.std.EffectVolume); pOwner.Feedback(0.1f, 50); } else { Sounds.PlaySound(pOwner.AudioThemeMan.BlockStopped.Key, pOwner.Settings.std.EffectVolume); pOwner.Feedback(0.4f, 75); } } } else if (g == GameKeys.GameKey_Pause) { if (g == GameKeys.GameKey_Pause) { pOwner.LastPausedTime = DateTime.Now; pOwner.CurrentState = new PauseGameState(pOwner, this); var playing = Sounds.GetPlayingMusic_Active(); playing?.Pause(); Sounds.PlaySound(pOwner.AudioThemeMan.Pause.Key, pOwner.Settings.std.EffectVolume); } //pOwner.CurrentState = new PauseGameState(this); } else if (g == GameKeys.GameKey_Hold) { if (HoldBlock != null && !BlockHold) { //if there is a holdblock, take it and put it into the gamefield and make the first active blockgroup the new holdblock, //then set BlockHold to block it from being used until the next Tetromino is spawned. Nomino FirstGroup = PlayField.BlockGroups.FirstOrDefault(); if (FirstGroup != null) { if (FirstGroup.Controllable) { PlayField.RemoveBlockGroup(FirstGroup); PlayField.AddBlockGroup(HoldBlock); //We probably should set the speed appropriately here for the level. As is it will retain the speed from whe nthe hold block was //held. PlayField.Theme.ApplyTheme(HoldBlock, GameHandler, PlayField, NominoTheme.ThemeApplicationReason.Normal); HoldBlock.X = (int)(((float)PlayField.ColCount / 2) - ((float)HoldBlock.GroupExtents.Width / 2)); HoldBlock.SetY(pOwner, 0); HoldBlock.HighestHeightValue = 0; //reset the highest height as well, so the falling animation doesn't goof HoldBlock = FirstGroup; Sounds.PlaySound(pOwner.AudioThemeMan.Hold.Key, pOwner.Settings.std.EffectVolume); pOwner.Feedback(0.9f, 40); BlockHold = true; } } } else if (!BlockHold) { Nomino FirstGroup = PlayField.BlockGroups.FirstOrDefault(); if (FirstGroup != null) { if (FirstGroup.Controllable) { PlayField.RemoveBlockGroup(FirstGroup); HoldBlock = FirstGroup; BlockHold = true; Sounds.PlaySound(pOwner.AudioThemeMan.Hold.Key, pOwner.Settings.std.EffectVolume); } } } } else if (g == GameKeys.GameKey_Debug1) { pOwner.CurrentState = new ShowHighScoresState(TetrisGame.ScoreMan["Standard"], pOwner.CurrentState); } else if (g == GameKeys.GameKey_Debug2) { OptionsMenuSettingsSelectorState OptionState = new OptionsMenuSettingsSelectorState(BackgroundDrawers.StandardImageBackgroundGDI.GetStandardBackgroundDrawer(), pOwner, pOwner.CurrentState); pOwner.CurrentState = OptionState; /*if (pOwner.CurrentState is StandardTetrisGameState) * { * ((StandardTetrisGameState) pOwner.CurrentState).GameStats.Score += 1000; * }*/ } else if (g == GameKeys.GameKey_Debug3) { int DesiredFontPixelHeight = (int)(pOwner.GameArea.Height * (23d / 644d)); Font standardFont = new Font(TetrisGame.RetroFont, DesiredFontPixelHeight, FontStyle.Bold, GraphicsUnit.Pixel); Font ItemFont = new Font(TetrisGame.RetroFont, (int)((float)DesiredFontPixelHeight * (3f / 4f)), FontStyle.Bold, GraphicsUnit.Pixel); //set state to a testing menu state. MenuState ms = new MenuState(BackgroundDrawers.StandardImageBackgroundGDI.GetStandardBackgroundDrawer()); ms.StateHeader = "This is a Menu"; ms.HeaderTypeface = standardFont.Name; ms.HeaderTypeSize = standardFont.Size; MenuStateTextMenuItem returnitem = new MenuStateTextMenuItem(); returnitem.FontFace = ItemFont.Name; returnitem.FontSize = ItemFont.Size; returnitem.Text = "Return"; returnitem.BackColor = Color.Transparent; returnitem.ForeColor = Color.DarkBlue; var OriginalState = pOwner.CurrentState; ms.MenuElements.Add(returnitem); var scaleitem = new MenuStateScaleMenuItem(pOwner); scaleitem.FontFace = ItemFont.Name; scaleitem.FontSize = ItemFont.Size; ms.MenuElements.Add(scaleitem); ms.MenuItemActivated += (obj, e) => { if (e.MenuElement == returnitem) { pOwner.CurrentState = OriginalState; } }; for (int i = 0; i < 8; i++) { MenuStateTextMenuItem mts = new MenuStateTextMenuItem(); mts.FontFace = ItemFont.Name; mts.FontSize = ItemFont.Size; mts.BackColor = Color.Transparent; mts.ForeColor = Color.Black; mts.Text = "Item " + i.ToString(); ms.MenuElements.Add(mts); } pOwner.CurrentState = ms; } else if (g == GameKeys.GameKey_Debug4) { if (pOwner is IGamePresenter gp) { var _Present = gp.GetPresenter(); if (_Present.ai == null) { _Present.ai = new StandardNominoAI(pOwner); } else { _Present.ai.AbortAI(); _Present.ai = null; } } } }
public FieldChangeResult ProcessFieldChange(GameplayGameState state, IStateOwner pOwner, Nomino Trigger) { var HotLines = new List <HotLine>(); FieldChangeResult FCR = new FieldChangeResult(); int rowsfound = 0; List <int> CompletedRows = new List <int>(); List <Action> AfterClearActions = new List <Action>(); var PlayField = state.PlayField; var Sounds = state.Sounds; var GameOptions = state.GameOptions; //checks the field contents for lines. If there are lines found, they are removed, and all rows above it are shifted down. for (int r = 0; r < PlayField.RowCount; r++) { if (PlayField.Contents[r].All((d) => d != null)) { Debug.Print("Found completed row at row " + r); if (PlayField.Flags.HasFlag(TetrisField.GameFlags.Flags_Hotline) && PlayField.HotLines.ContainsKey(r)) { Debug.Print("Found hotline row at row " + r); HotLines.Add(PlayField.HotLines[r]); } CompletedRows.Add(r); rowsfound++; //enqueue an action to perform the clear. We'll be replacing the current state with a clear action state, so this should execute AFTER that state returns control. var r1 = r; AfterClearActions.Add (() => { for (int g = r1; g > 0; g--) { Debug.Print("Moving row " + (g - 1).ToString() + " to row " + g); for (int i = 0; i < PlayField.ColCount; i++) { PlayField.Contents[g][i] = PlayField.Contents[g - 1][i]; } } }); } } AfterClearActions.Add(() => { PlayField.HasChanged = true; }); long PreviousLineCount = Statistics.LineCount; if (Trigger != null) { Statistics.AddLineCount(Trigger.GetType(), rowsfound); } if ((PreviousLineCount % 10) > (Statistics.LineCount % 10)) { state.InvokePlayFieldLevelChanged(state, new TetrisField.LevelChangeEventArgs((int)Statistics.LineCount / 10)); Statistics.SetLevelTime(pOwner.GetElapsedTime()); state.Sounds.PlaySound(pOwner.AudioThemeMan.LevelUp.Key, pOwner.Settings.std.EffectVolume); PlayField.SetFieldColors(this); state.f_RedrawStatusBitmap = true; } if (rowsfound > 0 && rowsfound < 4) { Sounds.PlaySound(pOwner.AudioThemeMan.ClearLine.Key, pOwner.Settings.std.EffectVolume * 2); } else if (rowsfound == 4) { Sounds.PlaySound(pOwner.AudioThemeMan.ClearTetris.Key, pOwner.Settings.std.EffectVolume * 2); } int topmost = PlayField.RowCount; //find the topmost row with any blocks. for (int i = 0; i < PlayField.RowCount; i++) { if (PlayField.Contents[i].Any((w) => w != null)) { topmost = i; break; } } topmost = topmost + rowsfound; //subtract the rows that were cleared to get an accurate measurement. if (topmost < 9) { if (state.currenttempo == 1) { state.currenttempo = 68; if (GameOptions.MusicRestartsOnTempoChange) { if (GameOptions.MusicEnabled) { Sounds.PlayMusic(pOwner.AudioThemeMan.BackgroundMusic.Key, pOwner.Settings.std.MusicVolume, true); } } var grabbed = Sounds.GetPlayingMusic_Active(); if (grabbed != null) { Sounds.GetPlayingMusic_Active().Tempo = 75f; } } } else { if (state.currenttempo != 1) { state.currenttempo = 1; if (GameOptions.MusicRestartsOnTempoChange) { if (GameOptions.MusicEnabled) { if (pOwner.Settings.std.MusicOption == "<RANDOM>") { Sounds.PlayMusic(pOwner.AudioThemeMan.BackgroundMusic.Key, pOwner.Settings.std.MusicVolume, true); } else { Sounds.PlayMusic(pOwner.Settings.std.MusicOption, pOwner.Settings.std.MusicVolume, true); } } } var grabbed = Sounds.GetPlayingMusic_Active(); if (grabbed != null) { grabbed.Tempo = 1f; } } } PlayField.HasChanged |= rowsfound > 0; if (rowsfound > 0) { var ClearState = new FieldLineActionGameState(state, CompletedRows.ToArray(), AfterClearActions); ClearState.ClearStyle = TetrisGame.Choose((FieldLineActionGameState.LineClearStyle[])(Enum.GetValues(typeof(FieldLineActionGameState.LineClearStyle)))); pOwner.CurrentState = ClearState; } //if(rowsfound > 0) pOwner.CurrentState = new FieldLineActionDissolve(this,CompletedRows.ToArray(),AfterClearActions); var scoreresult = GetScore(rowsfound, HotLines, state, pOwner, Trigger); pOwner.Feedback(0.9f * (float)scoreresult, scoreresult * 250); FCR.ScoreResult = rowsfound; return(FCR); }