/// <summary> /// Заполнение пустых ячеек в массиве модели. /// </summary> /// <returns>Список новых камешков на обогащение спрайтами</returns> public GemList FillBlanks() { GemList newGems = new GemList(); Random rnd = new Random(); for (int row = 0; row < RowsNumber; row++) { for (int column = 0; column < ColumnsNumber; column++) { if (GemArray[row, column] == null) { bool foundApropriateGemType = false; // Создаем камешек, записываем его в массив, проверяем не создал ли он цепочку // если создал - повторяем до те пор, пока не найдем камешек, который не создаст цепочку while (!foundApropriateGemType) { GemType newGemType = (GemType)rnd.Next(Enum.GetNames(typeof(GemType)).Length); GemArray[row, column] = new Gem(newGemType, row, column); foundApropriateGemType = GetChainAt(row, column).Count < 3; } // Добавляем новый камешек в список камешков, которым позже добавят спрайты newGems.Add(GemArray[row, column]); } } } //______ДЛЯ ДЕБАГА______ PrintOutGemArrayToConsole(false); return(newGems); }
/// <summary> /// Анимация счета за уничтожение цепочки /// </summary> /// <param name="chain">Уничтожаемая цепочка.</param> private void AnimateScore(GemList chain) { SKSpriteNode firstGem = chain.GetFirstGem().Sprite; SKSpriteNode lastGem = chain.GetLastGem().Sprite; CGPoint centerPoint = new CGPoint( (firstGem.Position.X + lastGem.Position.X) / 2, (firstGem.Position.Y + lastGem.Position.Y) / 2 - 8); SKLabelNode scoreLabel = new SKLabelNode("GillSans-BoldItalic") { FontSize = 16, Text = chain.GetScore() + "", Position = centerPoint, ZPosition = 300 }; gemLayer.AddChild(scoreLabel); SKAction moveAction = SKAction.MoveBy(0, 3, 0.7); //.Move(by: CGVector(dx: 0, dy: 3), duration: 0.7) moveAction.TimingMode = SKActionTimingMode.EaseOut; scoreLabel.RunAction(SKAction.Sequence(moveAction, SKAction.RemoveFromParent())); }
/// <summary> /// Прикрепление спрайтов к камешкам в списке /// </summary> /// <param name="gems">Список камешков на обогащение спрайтами</param> public void AttachSpritesTo(GemList gems) { // маркер появления на игровом поле разрушителя, для проигрывания // звука появления разрушителя bool hasDestroyers = false; bool hasBomb = false; foreach (Gem gem in gems) { AttachSpriteTo(gem); hasDestroyers |= gem.IsALineDestroyer; hasBomb |= gem.IsABomb; } // проигрывам звук появления разрушителя if (hasBomb) { RunAction(newBombSound); } else if (hasDestroyers) { RunAction(newDestroyerSound); } }
/// <summary> /// Проверяет цепочку на наличие бонусов и возвращает список бонусов /// </summary> /// <returns>Список бонусов из цепочки.</returns> public GemList GetAllBonuses() { GemList bonuses = new GemList(); foreach (Gem gem in this) { if (gem.IsALineDestroyer || gem.IsABomb) { bonuses.Add(gem); } } return(bonuses); }
/// <summary> /// Падение камешков на пустые места. Сканирует колонку на пустоту, /// найдя пустоту сканирует снова, на нличие камешка сверху. При наличии - /// "роняет" его на пустое место. Упавшие амешки заносятся в колонки, /// Колонки заносятся в список на обновление спрайтов /// </summary> /// <returns>Список колонок камешков, которые переместились, /// для обновления позиции спрайтов</returns> public List <GemList> DropGems() { List <GemList> columns = new List <GemList>(); // пробегаем по столбцам for (int column = 0; column < ColumnsNumber; column++) { GemList columnOfFallenGems = new GemList(); for (int row = 0; row < RowsNumber; row++) { // Если текущая ячейка пустая - сканируем текущий столбец вверх if (GemArray[row, column] == null) { bool foundNewGemAbove = false; for (int aboveRow = row + 1; aboveRow < RowsNumber && !foundNewGemAbove; aboveRow++) { // находим непустую ячейку и переносим камешек оттуда в текущую ячейку if (GemArray[aboveRow, column] != null) { GemArray[row, column] = GemArray[aboveRow, column]; // ячейку из которой спустили камешек зануляем GemArray[aboveRow, column] = null; // Обновляем координату ряда у камешка GemArray[row, column].Row = row; //запоминаем камешек для анимации спрайтов columnOfFallenGems.Add(GemArray[row, column]); foundNewGemAbove = true; } } } } // столбец из перенесенных камешков запоминаем в списке таких столбцов // чтобы анимировать их спрайты if (columnOfFallenGems.Count > 0) { columns.Add(columnOfFallenGems); } } //_______ДЛЯ ДЕБАГА______ PrintOutGemArrayToConsole(false); return(columns); }
/// <summary> /// Сканирование на наличие цепочки /// </summary> /// <returns><c>true</c>, если цепочка найдена, <c>false</c> если не найдена.</returns> public GemList RetriveChain() { for (int row = 0; row < RowsNumber; row++) { for (int column = 0; column < ColumnsNumber; column++) { GemList chain = GetChainAt(row, column); if (chain != null && chain.Count >= 3) { return(chain); } } } return(null); }
/// <summary> /// Обмена местами в массиве двух камешков. /// </summary> /// <param name="swap">Объект с камешками на обмен</param> public void Perform(Swap swap) { int columnA = swap.GemA.Column; int rowA = swap.GemA.Row; int columnB = swap.GemB.Column; int rowB = swap.GemB.Row; // Меняем камешки местами GemArray[rowA, columnA] = swap.GemB; swap.GemB.Column = columnA; swap.GemB.Row = rowA; GemArray[rowB, columnB] = swap.GemA; swap.GemA.Column = columnB; swap.GemA.Row = rowB; GemList chain = GetChainAt(rowB, columnB); // проверяем получившюся цепоку на длину. если цепочка больше трех на // месте премещенного камешка ставим бонус // записываем бонус в список на добавление спрайтов для сцены if (chain.Count == 4) { bool isHorizontal = chain.ToArray()[0].Row == chain.ToArray()[1].Row; BonusesToAddSpritesTo.Add(new Gem(true, isHorizontal, GemArray[rowB, columnB].GemType, rowB, columnB)); } if (chain.Count >= 5) { BonusesToAddSpritesTo.Add(new Gem(true, GemArray[rowB, columnB].GemType, rowB, columnB)); } chain = GetChainAt(rowA, columnA); if (chain.Count == 4) { bool isHorizontal = chain.ToArray()[0].Row == chain.ToArray()[1].Row; BonusesToAddSpritesTo.Add(new Gem(true, isHorizontal, GemArray[rowA, columnA].GemType, rowA, columnA)); } if (chain.Count >= 5) { BonusesToAddSpritesTo.Add(new Gem(true, GemArray[rowA, columnA].GemType, rowA, columnA)); } }
public List <GemList> RetriveAllChainsOnLevel() { List <GemList> chains = new List <GemList>(); for (int row = 0; row < RowsNumber; row++) { for (int column = 0; column < ColumnsNumber; column++) { GemList chain = GetChainAt(row, column); if (chain != null && chain.Count >= 3 && !chains.Contains(chain)) { chains.Add(chain); } } } return(chains); }
/// <summary> /// Уничтожение цепей в массиве камешков: /// метод сканирует массив на цепочки до тех пор, пока они есть, /// найдя цепочку заносит ее в список удаляемых цепочек, после чего /// уничтожает соответствующие камешки в массиве. Так же проверяет цепочки /// в них бонусов, заполняет список с бонусами. /// </summary> /// <returns><c>true</c> если нашелся хотя бы один активируемый бонус</returns> public void DestroyChains() { GemList chain = RetriveChain(); // Повторяем процесс пока находим цепочку на уровне while (chain != null) { GemList bonuses; bool needToReiterrate = true; while (needToReiterrate) { needToReiterrate = false; bonuses = chain.GetAllBonuses(); foreach (Gem bonus in bonuses) { // Запоминаем разрушитель в списке разрушителей на анимацию if (!BonusesToAnimate.Contains(bonus)) { BonusesToAnimate.Add(bonus); } // Заносим в цепочку весь ряд илил столбец, // в зависимости от напрваленности разрушителя if (bonus.IsALineDestroyer) { if (bonus.IsHorizontal) { for (int column = 0; column < ColumnsNumber; column++) { if (GemArray[bonus.Row, column] != null && !chain.Contains(GemArray[bonus.Row, column])) { chain.Add(GemArray[bonus.Row, column]); } } } else { for (int row = 0; row < RowsNumber; row++) { if (GemArray[row, bonus.Column] != null && !chain.Contains(GemArray[row, bonus.Column])) { chain.Add(GemArray[row, bonus.Column]); } } } } if (bonus.IsABomb) { int i; int j; if (bonus.Row == 0) { i = 0; } else { if (bonus.Row - Properties.BombBlastRadius < 0) { i = 0; } else { i = bonus.Row - Properties.BombBlastRadius; } } if (bonus.Column == 0) { j = 0; } else { if (bonus.Column - Properties.BombBlastRadius < 0) { j = 0; } else { j = bonus.Column - Properties.BombBlastRadius; } } for (int row = i; row <= bonus.Row + Properties.BombBlastRadius && row <= RowsNumber - 1; row++) { for (int column = j; column <= bonus.Column + Properties.BombBlastRadius && column <= ColumnsNumber - 1; column++) { if (GemArray[row, column] != null && !chain.Contains(GemArray[row, column])) { chain.Add(GemArray[row, column]); } } } } } needToReiterrate = bonuses.Count < chain.GetAllBonuses().Count; } // подсчитываем очки за цепочку, добавляем их к общему счету // int chainScore = (chain.Count - 2) * 10; Score += chain.GetScore(); // заносим цепочку в список на анимацию DestroyedChains.Add(chain); // удаляем из массива камешки в соответствии с цепью chain.ForEach(gem => GemArray[gem.Row, gem.Column] = null); // Получаем новую цепочку chain = RetriveChain(); } //______ДЛЯ ДЕБАГА______ PrintOutGemArrayToConsole(false); }
/// <summary> /// Проверка нахождения камешка в цепочке одинаковых камешков /// В этом проекте приоритет отдается горизонтальным цепочкам. /// Иерархия появилась т.к. Т- и Х- образные цепочки не проверяются и не просчитываются. /// </summary> /// <returns>Цепочка камешков</returns> /// <param name="gemRow">Ряд</param> /// <param name="gemColumn">Колонка</param> private GemList GetChainAt(int gemRow, int gemColumn) { GemList possibleChain = new GemList(); // Если камешка в данной позиции вообще нет - возвращаем пустой список if (GemArray[gemRow, gemColumn] == null) { return(possibleChain); } possibleChain.Add(GemArray[gemRow, gemColumn]); GemType gemTypeToCheck = GemArray[gemRow, gemColumn].GemType; // Флаг разпыва цепочки bool chainIsNotBroken = true; //Проверка влево for (int column = gemColumn - 1; column >= 0 && chainIsNotBroken; column--) { if (GemArray[gemRow, column] != null && gemTypeToCheck == GemArray[gemRow, column].GemType) { possibleChain.Add(GemArray[gemRow, column]); } else { chainIsNotBroken = false; } } chainIsNotBroken = true; // Проверка вправо for (int column = gemColumn + 1; column < ColumnsNumber && chainIsNotBroken; column++) { if (GemArray[gemRow, column] != null && gemTypeToCheck == GemArray[gemRow, column].GemType) { possibleChain.Add(GemArray[gemRow, column]); } else { chainIsNotBroken = false; } } // Если в цепочке три и более элементов - цепочка возвращается if (possibleChain.Count >= 3) { return(possibleChain); } // Сбрасываем цепочку, если не нашлось цепочки по горизонтали possibleChain.Clear(); possibleChain.Add(GemArray[gemRow, gemColumn]); chainIsNotBroken = true; // Проверка вниз for (int row = gemRow - 1; row >= 0 && chainIsNotBroken; row--) { if (GemArray[row, gemColumn] != null && gemTypeToCheck == GemArray[row, gemColumn].GemType) { possibleChain.Add(GemArray[row, gemColumn]); } else { chainIsNotBroken = false; } } chainIsNotBroken = true; // Проверка вверх for (int row = gemRow + 1; row < RowsNumber && chainIsNotBroken; row++) { if (GemArray[row, gemColumn] != null && gemTypeToCheck == GemArray[row, gemColumn].GemType) { possibleChain.Add(GemArray[row, gemColumn]); } else { chainIsNotBroken = false; } } return(possibleChain); }