Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using UnityEngine;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Text;
- public class GameGrid : YetiBehavior
- {
- #region :: Class Members ::
- private Transform _transform;
- private Vector3 _lowerLeftPosition;
- private Gem[,] _activeGrid;
- private Gem[,] _upNextGrid;
- private Vector3[,] _activeGridPosition;
- private Vector3[,] _upNextGridPosition;
- private SpriteManager _spriteManagerComponent;
- private const int _numRows = 8;
- private const int _numColumns = 8;
- private float _gemSize = 80.0f;
- private Vector2 _size;
- private Point2i _selectedTileCurrent;
- private Point2i _selectedTilePrevious;
- private Point2i[] _swappedTiles;
- private List<GemMatch> _scoreQueue;
- private List<Point2i> _deathQueue;
- private List<Point2i> _treasureQueue;
- private HintList _hintList;
- private int _cascadeCount;
- private GameObject _input;
- private UIButton _inputComponent;
- private bool _pauseActiveGridUpdates = false;
- private bool _pauseMatchingGems = false;
- private int _validBoardLoopCounter = 0;
- public SelectionBox SelectionBox;
- public HintArrow HintArrow1;
- public HintArrow HintArrow2;
- public float HintArrowMoveSpeed = 35.0f;
- public float HintArrowBobDistance = 5.0f;
- #endregion
- public override void HandleAwake()
- {
- base.HandleAwake();
- //Cache a few components for faster runtime access
- _transform = this.gameObject.transform;
- _activeGrid = new Gem[_numColumns, _numRows];
- _upNextGrid = new Gem[_numColumns, _numRows];
- _activeGridPosition = new Vector3[_numColumns, _numRows];
- _upNextGridPosition = new Vector3[_numColumns, _numRows];
- //Keep a permanent reference to the PackedSprite component
- _spriteManagerComponent = this.GetComponent("SpriteManager") as SpriteManager;
- _selectedTileCurrent = new Point2i();
- _selectedTilePrevious = new Point2i();
- _swappedTiles = new Point2i[2] { new Point2i(), new Point2i() };
- _scoreQueue = new List<GemMatch>();
- _deathQueue = new List<Point2i>();
- _treasureQueue = new List<Point2i>();
- _hintList = new HintList();
- /*this.HintArrow1.gameObject.active = false;
- this.HintArrow2.gameObject.active = false;
- this.SelectionBox.active = false;*/
- }
- #region :: Initialization ::
- public override void Initialize()
- {
- base.Initialize();
- //Configure the speed of swapping and the gem size based on what type of device we're running on
- InitializeBasedOnDeviceResolution();
- //How big is the game grid?
- _size.x = _numColumns * _gemSize;
- _size.y = _numRows * _gemSize;
- //Make sure that the gamegrid is at the right Z-position
- //_transform.position = new Vector3(_transform.position.x, _transform.position.y, GameLayer.Gems);
- //What is the position of the lower left point of the game grid?
- _lowerLeftPosition = new Vector3(_transform.position.x - _size.x/2, _transform.position.y - _size.y/2, _transform.position.z);
- //Create the object that will give us gameplay input
- InitializeInput();
- //Create all the gem objects on the game board
- LoadGems();
- this.DefineObjectTypeInPool("GemDeathEffect", "Prefabs/Particle Effects/GemDeathEffect", 8);
- //Initialize Child Controls
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- if(_upNextGrid[x,y] != null)
- _upNextGrid[x,y].Initialize();
- if(_activeGrid[x,y] != null)
- _activeGrid[x,y].Initialize();
- }
- }
- this.HintArrow1.Initialize();
- this.HintArrow2.Initialize();
- this.SelectionBox.Initialize();
- }
- private void InitializeBasedOnDeviceResolution()
- {
- if(GraphicsManager.Instance.IsDeviceIpad)
- {
- _gemSize = 80.0f;
- this.HintArrowMoveSpeed = 36.0f;
- this.HintArrowBobDistance = 6.0f;
- }
- else // iPhone
- {
- _gemSize = 40.0f;
- this.HintArrowMoveSpeed = 18.0f;
- this.HintArrowBobDistance = 3.0f;
- }
- //If we're using a retina display, let's double all the values
- if(GraphicsManager.Instance.IsRetinaDisplay)
- {
- _gemSize *= 2.0f;
- this.HintArrowMoveSpeed *= 2.0f;
- this.HintArrowBobDistance *= 2.0f;
- }
- }
- private void InitializeInput()
- {
- //Create our input object and position it relative to the parent object
- _input = new GameObject("InputObject");
- _input.transform.parent = this.transform;
- _input.transform.localPosition = new Vector3(0.0f, 0.0f, -1.0f);
- //Create the actual UI Button component and attach it to the input object
- _inputComponent = (UIButton)_input.AddComponent(typeof(UIButton));
- //Configure the input parameters
- _inputComponent.width = _size.x;
- _inputComponent.height = _size.y;
- _inputComponent.RenderCamera = Camera.main;
- _inputComponent.scriptWithMethodToInvoke = this;
- _inputComponent.whenToInvoke = POINTER_INFO.INPUT_EVENT.PRESS;
- _inputComponent.methodToInvoke = "HandleInputPress";
- _inputComponent.whenToInvokeSecondary = POINTER_INFO.INPUT_EVENT.DRAG;
- _inputComponent.methodToInvokeSecondary = "HandleInputDrag";
- //Remove the mesh renderer components that are automatically added
- GameObject.Destroy(_input.GetComponent("MeshRenderer"));
- GameObject.Destroy(_input.GetComponent("MeshFilter"));
- }
- private void LoadGems()
- {
- //Load all the gems on the board
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- //Load the prefab and cache the prefab object / gem component
- string prefabLocation = "Prefabs/Game/iPad/Gem0Prefab";
- if(!GraphicsManager.Instance.IsDeviceIpad)
- prefabLocation = "Prefabs/Game/iPhone/Gem0Prefab-iPhone";
- GameObject gemPrefab = Instantiate(Resources.Load(prefabLocation)) as GameObject;
- Gem gemComponent = gemPrefab.GetComponent("Gem") as Gem;
- _activeGrid[x,y] = null;
- _upNextGrid[x,y] = gemComponent;
- //Ensure that the gem type we're about to create is not one of the 4 neighbors, left/right/up/down. This will ensure no instant matches when the game starts
- gemComponent.GemID = 0;
- //Set the size of the gem in the data model
- GemDataModel.Instance.SetSize(_gemSize);
- //Add the Gem to the SpriteManager
- _spriteManagerComponent.AddSprite(gemPrefab);
- //Calculate the position that the gems will reside on the game board. The upNext grid is offset by the board height
- _activeGridPosition[x,y] = GetCenterPointFromGridCoords(new Point2i(x, y), GameLayer.Gems);
- _upNextGridPosition[x,y] = _activeGridPosition[x,y];
- _upNextGridPosition[x,y].y += _numRows * _gemSize;
- gemPrefab.transform.position = _upNextGridPosition[x,y];
- gemPrefab.transform.parent = this.transform;
- gemPrefab.name = string.Format("Gem [{0},{1}]", x, y);
- //We'll want to know when a gem starts or stops moving so we can look for matches
- gemComponent.Kinematic.IsMovingChanged += new PropertyChangedEventHandler(HandleGemIsMovingChanged);
- }
- }
- }
- #endregion
- #region :: Game Pump / Event Detection ::
- public override void HandleUpdate(float frameStartTime, float deltaTime)
- {
- base.HandleUpdate(frameStartTime, deltaTime);
- //Make sure our queues are flushed
- if(_scoreQueue.Count != 0)
- {
- CommitScoreQueue();
- }
- if(_deathQueue.Count != 0)
- {
- CommitDeathQueue();
- UpdateActiveGrid();
- }
- //Update the gems -- we do this manually because there is a performance hit if they use Unity's Update method
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- if(_activeGrid[x,y] != null)
- _activeGrid[x,y].HandleUpdate(frameStartTime, deltaTime);
- if(_upNextGrid[x,y] != null)
- _upNextGrid[x,y].HandleUpdate(frameStartTime, deltaTime);
- }
- }
- this.HintArrow1.HandleUpdate(frameStartTime, deltaTime);
- this.HintArrow2.HandleUpdate(frameStartTime, deltaTime);
- if(GameManager.Instance.AutoPlay)
- AutoPlay();
- //Every 500 game updates, let's ensure we have a valid board -- Just in case some logic got screwed up somewhere. This is better than having a user with
- //an unplayable game board
- _validBoardLoopCounter++;
- if(_validBoardLoopCounter > 500)
- {
- _validBoardLoopCounter = 0;
- ConfirmValidBoardExists();
- }
- }
- #endregion
- #region :: Internal Implementation ::
- public Vector3 GetCenterPointFromGridCoords(Point2i tileIndex, float positionZ)
- {
- //We'll offset the position with the GameGrid object's position. This is the lower left hand side of the game board
- //Positions are centered on the object so we need to place them by dividing height & width by 2
- //Get the center point for the object that lives on the grid
- Vector3 centerPoint = new Vector3(_lowerLeftPosition.x + tileIndex.x*_gemSize + _gemSize/2,
- _lowerLeftPosition.y + tileIndex.y*_gemSize + _gemSize/2,
- positionZ);
- return centerPoint;
- }
- private void SelectGridPosition(Point2i tileIndex)
- {
- _selectedTilePrevious.Set(_selectedTileCurrent.x, _selectedTileCurrent.y);
- _selectedTileCurrent.Set(tileIndex.x, tileIndex.y);
- //Show the SelectionBox around the gem that we clicked on
- this.SelectionBox.gameObject.active = true;
- this.SelectionBox.SpriteGraphics.Hide(false);
- this.SelectionBox.gameObject.transform.position = GetCenterPointFromGridCoords(tileIndex, GameLayer.SelectionBox);
- //Was there a tile previously selected?
- if(_selectedTilePrevious.IsValid)
- {
- //Check to see if we can swap these two tiles
- if(CheckIfTilesShareEdge(_selectedTilePrevious, _selectedTileCurrent))
- {
- //Swap Tiles
- SwapTiles(_selectedTileCurrent, _selectedTilePrevious, true);
- }
- }
- }
- public void ClearGridSelection()
- {
- _selectedTileCurrent.Reset();
- _selectedTilePrevious.Reset();
- this.SelectionBox.gameObject.active = false;
- }
- private void HideHintArrows()
- {
- this.HintArrow1.gameObject.active = false;
- this.HintArrow2.gameObject.active = false;
- }
- private bool CheckIfTilesShareEdge(Point2i tile1Index, Point2i tile2Index)
- {
- bool doTilesShareEdge = false;
- if(tile1Index.x == tile2Index.x)
- {
- if(Math.Abs(tile1Index.y - tile2Index.y) == 1)
- doTilesShareEdge = true;
- }
- else if(tile1Index.y == tile2Index.y)
- {
- if(Math.Abs(tile1Index.x - tile2Index.x) == 1)
- doTilesShareEdge = true;
- }
- return doTilesShareEdge;
- }
- public int[] GetListOfNeighborIDs(int gridX, int gridY)
- {
- return GetListOfNeighborIDs(_activeGrid, gridX, gridY);
- }
- private int[] GetListOfNeighborIDs(Gem[,] grid, int gridX, int gridY)
- {
- List<int> neighborIDs = new List<int>();
- //Left
- if(gridX-1 >= 0 && grid[gridX-1, gridY] != null)
- {
- int id = grid[gridX-1, gridY].GemID;
- if(!neighborIDs.Contains(id))
- neighborIDs.Add(id);
- }
- //Bottom
- if(gridY-1 >= 0 && grid[gridX, gridY-1] != null)
- {
- int id = grid[gridX, gridY-1].GemID;
- if(!neighborIDs.Contains(id))
- neighborIDs.Add(id);
- }
- //Right
- if(gridX+1 < _numColumns && grid[gridX+1, gridY] != null)
- {
- int id = grid[gridX+1, gridY].GemID;
- if(!neighborIDs.Contains(id))
- neighborIDs.Add(id);
- }
- //Top
- if(gridY+1 < _numRows && grid[gridX, gridY+1] != null)
- {
- int id = grid[gridX, gridY+1].GemID;
- if(!neighborIDs.Contains(id))
- neighborIDs.Add(id);
- }
- return neighborIDs.ToArray();
- }
- private void SwapTiles(Point2i tile1Index, Point2i tile2Index, bool isSwapUserInitiated)
- {
- //Swap the gems in our array
- Gem tempGemComponent = _activeGrid[tile1Index.x, tile1Index.y];
- _activeGrid[tile1Index.x, tile1Index.y] = _activeGrid[tile2Index.x, tile2Index.y];
- _activeGrid[tile2Index.x, tile2Index.y] = tempGemComponent;
- //Move them to their new locations
- //_activeGrid[tile1Index.x, tile1Index.y].Kinematic.MoveTo(_activeGrid[tile2Index.x, tile2Index.y].transform.position, this.GemSwapSpeed);
- //_activeGrid[tile2Index.x, tile2Index.y].Kinematic.MoveTo(_activeGrid[tile1Index.x, tile1Index.y].transform.position, this.GemSwapSpeed);
- float movementTime = 0.30f;
- _activeGrid[tile1Index.x, tile1Index.y].Kinematic.MoveToWithTween(_activeGrid[tile2Index.x, tile2Index.y].transform.position, movementTime);
- _activeGrid[tile2Index.x, tile2Index.y].Kinematic.MoveToWithTween(_activeGrid[tile1Index.x, tile1Index.y].transform.position, movementTime);
- //Remember what gems we swapped. We do this incase there is not a match and we need to move the gems back to their proper location
- if(isSwapUserInitiated)
- {
- _swappedTiles[0].Set(tile1Index);
- _swappedTiles[1].Set(tile2Index);
- }
- else
- {
- _swappedTiles[0].Reset();
- _swappedTiles[1].Reset();
- }
- ClearGridSelection();
- HideHintArrows();
- }
- public bool GetAreTilesMoving()
- {
- //Checks the Gem array to see if any of the tiles are currently moving to a destination
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- if(_activeGrid[x, y] == null || _activeGrid[x, y].Kinematic.IsMoving)
- return true;
- }
- }
- return false;
- }
- private void CheckForMatches(Gem[,] grid)
- {
- _treasureQueue.Clear();
- _deathQueue.Clear();
- _scoreQueue.Clear();
- CheckForHorizontalMatches(grid);
- CheckForVerticalMatches(grid);
- }
- private void CheckForHorizontalMatches(Gem[,] grid)
- {
- //Check for Horizontal Matches
- for(int y=0; y<_numRows; y++)
- {
- //We can safely ignore the 2 right columns since they can't form matches with only two gems
- for(int x=0; x<_numColumns-2; x++)
- {
- int nextGemIndex = x + 1;
- int numGemsMatched = 1; //The tile found at currentIndexToCheck
- bool didNextGemMatch = true;
- //Look for matches
- while(didNextGemMatch)
- {
- //Does the next gem to look at match our current [x,y] position?
- if(grid[nextGemIndex,y] != null && grid[x,y] != null &&
- grid[nextGemIndex,y].GemID == grid[x,y].GemID)
- {
- numGemsMatched++;
- nextGemIndex++;
- //Break out of the loop if we get to the edge of the game board
- if(nextGemIndex >= _numColumns)
- didNextGemMatch = false;
- }
- else
- {
- didNextGemMatch = false;
- }
- }
- //Done searching for matches on this row. Let's see if we have enough tiles matched to score.
- if(numGemsMatched >= 3)
- {
- if(grid == _activeGrid)
- {
- //Add the matched gems to our score queue so that the player gets points
- AddGemsToScoreQueue(new Point2i(x, y), new Point2i(x+numGemsMatched-1, y), numGemsMatched);
- _cascadeCount++;
- }
- //Add the gems that matched to the death queue to be removed after all matches are found
- for(int i=x; i<(x+numGemsMatched); i++)
- {
- AddGemToDeathQueue(new Point2i(i, y));
- }
- //Increment our iterator to skip the gems we've already matched
- x += (numGemsMatched - 1);
- }
- }
- }
- }
- private void CheckForVerticalMatches(Gem[,] grid)
- {
- //Check for Vertical Matches
- for(int x=0; x<_numColumns; x++)
- {
- //We can safely ignore the 2 right columns since they can't form matches with only two gems
- for(int y=0; y<_numRows-2; y++)
- {
- int nextGemIndex = y + 1;
- int numGemsMatched = 1; //The tile found at currentIndexToCheck
- bool didNextGemMatch = true;
- //Look for matches
- while(didNextGemMatch)
- {
- //Does the next gem to look at match our current [x,y] position?
- if(grid[x, nextGemIndex] != null && grid[x,y] != null &&
- grid[x, nextGemIndex].GemID == grid[x,y].GemID)
- {
- numGemsMatched++;
- nextGemIndex++;
- //Break out of the loop if we get to the edge of the game board
- if(nextGemIndex >= _numRows)
- didNextGemMatch = false;
- }
- else
- {
- didNextGemMatch = false;
- }
- }
- //Done searching for matches on this row. Let's see if we have enough tiles matched to score.
- if(numGemsMatched >= 3)
- {
- if(grid == _activeGrid)
- {
- //Add the matched gems to our score queue so that the player gets points
- AddGemsToScoreQueue(new Point2i(x, y), new Point2i(x, y+numGemsMatched-1), numGemsMatched);
- _cascadeCount++;
- }
- //Add the gems that matched to the death queue to be removed after all matches are found
- for(int i=y; i<(y+numGemsMatched); i++)
- {
- AddGemToDeathQueue(new Point2i(x, i));
- }
- //Increment our iterator to skip the gems we've already matched
- y += (numGemsMatched - 1);
- }
- }
- }
- }
- private void CheckForMoves(Gem[,] grid)
- {
- _hintList.Clear();
- CheckForHorizontalMoves(grid);
- CheckForVerticalMoves(grid);
- }
- private void CheckForHorizontalMoves(Gem[,] grid)
- {
- for(int y=0; y<_numRows; y++)
- {
- //Check for Horizontal Hints of typed "JOINED"
- //We can safely ignore the right most column since they can't form matches with only two gems
- for(int x=0; x<_numColumns-1; x++)
- {
- int nextGemIndex = x + 1;
- int numGemsMatched = 1; //The tile found at currentIndexToCheck
- bool didNextGemMatch = true;
- //Look for matches
- while(didNextGemMatch)
- {
- //Does the next gem to look at match our current [x,y] position?
- if(grid[nextGemIndex,y] != null && grid[x,y] != null &&
- grid[nextGemIndex,y].GemID == grid[x,y].GemID)
- {
- numGemsMatched++;
- nextGemIndex++;
- //Break out of the loop if we get to the edge of the game board or we have our required number matched to search for hints
- if(nextGemIndex >= _numColumns || numGemsMatched == 2)
- didNextGemMatch = false;
- }
- else
- {
- didNextGemMatch = false;
- }
- }
- //Done searching for matches on this row. Let's see if we have enough tiles matched to create a hint
- if(numGemsMatched == 2)
- {
- //We found two tiles that match horizontally, so let's see if there is a tile that could match it around the endpoints
- //.2..5.
- //1.xx.4
- //.3..6.
- //Check the left most for match, #1
- if(x-2 >= 0)
- {
- if(grid[x-2,y].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x-2, y, x-1, y, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the left-upper for match, #2
- if(x-1 >= 0 && y+1 < _numRows)
- {
- if(grid[x-1,y+1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x-1, y+1, x-1, y, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the left-lower for match, #3
- if(x-1 >= 0 && y-1 >= 0)
- {
- if(grid[x-1,y-1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x-1, y-1, x-1, y, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the right most for match, #4
- if(x+3 < _numColumns)
- {
- if(grid[x+3,y].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+3, y, x+2, y, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the right-upper for match, #5
- if(x+2 < _numColumns && y+1 < _numRows)
- {
- if(grid[x+2,y+1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+2, y+1, x+2, y, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the right-lower for match, #6
- if(x+2 < _numColumns && y-1 >= 0)
- {
- if(grid[x+2,y-1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+2, y-1, x+2, y, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Increment our iterator to skip the gems we've already matched
- x += (numGemsMatched - 1);
- }
- }
- //Check for Horizontal Hints of typed "SPLIT"
- for(int x=0; x<_numColumns-2; x++)
- {
- //We're looking for gems that are the same type but split apart. We'll add a hint if we can swap a gem from top or bottom with the middle gem to form a match
- //.1.
- //x.x
- //.2.
- if(grid[x+2,y].GemID == grid[x,y].GemID)
- {
- //Check the upper for match, #1
- if(y+1 < _numRows)
- {
- if(grid[x+1,y+1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+1, y+1, x+1, y, grid[x,y].GemID, HintType.GEMS_SPLIT));
- }
- }
- //Check the lower for match, #2
- if(y-1 >= 0)
- {
- if(grid[x+1,y-1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+1, y-1, x+1, y, grid[x,y].GemID, HintType.GEMS_SPLIT));
- }
- }
- }
- }
- }
- }
- private void CheckForVerticalMoves(Gem[,] grid)
- {
- for(int x=0; x<_numColumns; x++)
- {
- //Check for Horizontal Hints of typed "JOINED"
- //We can safely ignore the right most column since they can't form matches with only two gems
- for(int y=0; y<_numRows-1; y++)
- {
- int nextGemIndex = y + 1;
- int numGemsMatched = 1; //The tile found at currentIndexToCheck
- bool didNextGemMatch = true;
- //Look for matches
- while(didNextGemMatch)
- {
- //Does the next gem to look at match our current [x,y] position?
- if(grid[x,nextGemIndex] != null && grid[x,y] != null &&
- grid[x,nextGemIndex].GemID == grid[x,y].GemID)
- {
- numGemsMatched++;
- nextGemIndex++;
- //Break out of the loop if we get to the edge of the game board or we have our required number matched to search for hints
- if(nextGemIndex >= _numRows || numGemsMatched == 2)
- didNextGemMatch = false;
- }
- else
- {
- didNextGemMatch = false;
- }
- }
- //Done searching for matches on this row. Let's see if we have enough tiles matched to create a hint
- if(numGemsMatched == 2)
- {
- //We found two tiles that match vertically, so let's see if there is a tile that could match it around the endpoints
- //.4.
- //5.6
- //.x.
- //.x.
- //2.3
- //.1.
- //Check the bottom most match, #1
- if(y-2 >= 0)
- {
- if(grid[x,y-2].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x, y-2, x, y-1, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the lower-left for match, #2
- if(x-1 >= 0 && y-1 >= 0)
- {
- if(grid[x-1,y-1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x-1, y-1, x, y-1, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the lower-right for match #3
- if(x+1 < _numColumns && y-1 >= 0)
- {
- if(grid[x+1,y-1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+1, y-1, x, y-1, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the top most for match, #4
- if(y+3 < _numRows)
- {
- if(grid[x,y+3].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x, y+3, x, y+2, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the upper-left for match, #5
- if(x-1 >=0 && y+2 < _numRows)
- {
- if(grid[x-1,y+2].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x-1, y+2, x, y+2, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Check the upper-right for match, #6
- if(x+1 < _numColumns && y+2 < _numRows)
- {
- if(grid[x+1,y+2].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+1, y+2, x, y+2, grid[x,y].GemID, HintType.GEMS_JOINED));
- }
- }
- //Increment our iterator to skip the gems we've already matched
- y += (numGemsMatched - 1);
- }
- }
- //Check for Vertical Hints of typed "SPLIT"
- for(int y=0; y<_numRows-2; y++)
- {
- //We're looking for gems that are the same type but split apart. We'll add a hint if we can swap a gem from left or right with the middle gem to form a match
- //.x.
- //1.2
- //.x.
- if(grid[x,y+2].GemID == grid[x,y].GemID)
- {
- //Check the left for match, #1
- if(x-1 >= 0)
- {
- if(grid[x-1,y+1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x-1, y+1, x, y+1, grid[x,y].GemID, HintType.GEMS_SPLIT));
- }
- }
- //Check the right for match, #2
- if(x+1 < _numColumns)
- {
- if(grid[x+1,y+1].GemID == grid[x,y].GemID)
- {
- _hintList.AddHint(new Hint(x+1, y+1, x, y+1, grid[x,y].GemID, HintType.GEMS_SPLIT));
- }
- }
- }
- }
- }
- }
- private void UpdateActiveGrid()
- {
- if(this.PauseActiveGridUpdates)
- return;
- List<Gem> droppingGems = new List<Gem>();
- float tweenSpeed = 0.40f;
- for(int x=0; x<_numColumns; x++)
- {
- for(int y=0; y<_numRows; y++)
- {
- //Check for a hole in the column that needs to be filled
- if(_activeGrid[x,y] == null)
- {
- //We found a hole in the column, find the next closest gem above this hole
- for(int j=y; j<_numRows; j++)
- {
- if(_activeGrid[x,j] != null)
- {
- //Move the gem down to the empty spot
- //_activeGrid[x,j].Kinematic.MoveTo(_activeGridPosition[x,y], this.GemFallSpeed);
- _activeGrid[x,j].Kinematic.MoveToWithTween(_activeGridPosition[x,y], tweenSpeed);
- //Assign the moving gem to it's new spot and designate the other as empty
- _activeGrid[x,y] = _activeGrid[x,j];
- _activeGrid[x,j] = null;
- break;
- }
- }
- }
- //Check to see if we still have a hole in the board after looking above us. If there is still a hole, we need to pull a gem out of the upNext board
- if(_activeGrid[x,y] == null)
- {
- //We found a hole in the column, find the next closest gem above this hole
- for(int j=0; j<_numRows; j++)
- {
- if(_upNextGrid[x,j] != null)
- {
- //Move the gem down to the empty spot
- _upNextGrid[x,j].Visible = true;
- //_upNextGrid[x,j].Kinematic.MoveTo(_activeGridPosition[x,y], this.GemFallSpeed);
- _upNextGrid[x,j].Kinematic.MoveToWithTween(_activeGridPosition[x,y], tweenSpeed);
- //Assign the moving gem to it's new spot and designate the other as empty
- _activeGrid[x,y] = _upNextGrid[x,j];
- _upNextGrid[x,j] = null;
- //Keep a list of the gems we're dropping in case there are no more moves and we need to reassign gems
- droppingGems.Add(_activeGrid[x,y]);
- //Tell everything above this gem in upNext to move down a spot
- for(int z=j+1; j<_numRows; j++)
- {
- if(_upNextGrid[x,z] != null)
- {
- //_upNextGrid[x,z].Kinematic.MoveTo(_upNextGridPosition[x,z-1], this.GemFallSpeed);
- _upNextGrid[x,z].Kinematic.MoveToWithTween(_upNextGridPosition[x,z-1], tweenSpeed);
- _upNextGrid[x,z-1] = _upNextGrid[x,z];
- _upNextGrid[x,z] = null;
- }
- }
- break;
- }
- }
- }
- }
- }
- //Play a drop gem sound if we moved any gems
- if(droppingGems.Count > 0)
- {
- //this.ScheduleTimer("", tweenSpeed, PlayGemsFallSound);
- this.ScheduleTimer("", 0.0f, PlayGemsFallSound);
- }
- //The active grid changed, check for new moves
- CheckForMoves(_activeGrid);
- //Did the gems we just added result in no new moves?
- int boardRegenCount = 0;
- while(!_hintList.HasHints && droppingGems.Count > 0)
- {
- foreach(Gem droppingGem in droppingGems)
- {
- droppingGem.Randomize();
- }
- boardRegenCount++;
- //Check for moves now that we've randomized the gems that were about to drop
- CheckForMoves(_activeGrid);
- if(boardRegenCount > 30)
- break;
- }
- if(boardRegenCount > 30)
- {
- Debug.Log("No More Moves. Board Regen Count Over 30.. Resetting board");
- }
- //If no hints are available, reset the board
- if(!_hintList.HasHints)
- ResetGameBoard();
- //Save the game board
- GameWindow.Instance.SaveGameBoard();
- }
- private void CommitDeathQueue()
- {
- for(int i=0; i<_deathQueue.Count; i++)
- {
- if(_activeGrid[_deathQueue[i].x, _deathQueue[i].y] != null)
- {
- _activeGrid[_deathQueue[i].x, _deathQueue[i].y].Die();
- }
- ClearGridSpace(_deathQueue[i]);
- }
- _deathQueue.Clear();
- }
- private void CommitScoreQueue()
- {
- for(int i=0; i<_scoreQueue.Count; i++)
- {
- GameWindow.Instance.ScoreGemMatch(_scoreQueue[i]);
- }
- _scoreQueue.Clear();
- }
- private void CommitTreasureQueue()
- {
- for(int i=0; i<_treasureQueue.Count; i++)
- {
- //Remove the spot that we'll use for the treasure chest from the death queue.
- for(int j=0; j<_deathQueue.Count; j++)
- {
- if(_deathQueue[j].Equals(_treasureQueue[i]))
- {
- //Debug.Log(String.Format("Removing gem from death queue at {0},{1}", _deathQueue[j].x, _deathQueue[j].y));
- _deathQueue.RemoveAt(j);
- break;
- }
- }
- //Reassign the gem that we are removing to become a treasure chest
- //Debug.Log("Created Treasure Chest");
- _activeGrid[_treasureQueue[i].x, _treasureQueue[i].y].IsTreasure = true;
- //Award the treasure chest in a few moments -- Give the timer a unique name just in case there are two chests made in the queue
- ScheduleTimer(String.Format("AwardTreasureChest{0}{1}",_treasureQueue[i].x, _treasureQueue[i].y),
- 0.5f,
- GameWindow.Instance.AwardTreasureChest,
- _activeGrid[_treasureQueue[i].x, _treasureQueue[i].y]);
- }
- _treasureQueue.Clear();
- }
- private void ClearGridSpace(Point2i tileIndex)
- {
- //Make sure the activeGrid hasn't already cleared that space
- if(_activeGrid[tileIndex.x, tileIndex.y] == null)
- return;
- //Find the lowest available item in UpNext Grid.
- for(int y=0; y<_numRows; y++)
- {
- if(_upNextGrid[tileIndex.x,y] == null)
- {
- //Move the reference to the gem to the upNextGrid
- _upNextGrid[tileIndex.x,y] = _activeGrid[tileIndex.x, tileIndex.y];
- _activeGrid[tileIndex.x, tileIndex.y] = null;
- //Disable the gem in the upNextGrid so we don't see it rendered
- _upNextGrid[tileIndex.x,y].Visible = false;
- //Update it's position
- _upNextGrid[tileIndex.x,y].transform.position = _upNextGridPosition[tileIndex.x,y];
- //Change the gem
- _upNextGrid[tileIndex.x,y].Randomize();
- break;
- }
- }
- }
- public void AddGemsToScoreQueue(Point2i tile1Index, Point2i tile2Index, int numGemsMatched)
- {
- int gemID = _activeGrid[tile1Index.x, tile1Index.y].GemID;
- Vector3 tile1Position = GetCenterPointFromGridCoords(tile1Index, GameLayer.Gems);
- Vector3 tile2Position = GetCenterPointFromGridCoords(tile2Index, GameLayer.Gems);
- Vector3 centerPosition = new Vector3((tile1Position.x + tile2Position.x) / 2,
- (tile1Position.y + tile2Position.y) / 2,
- GameLayer.Text);
- _scoreQueue.Add(new GemMatch(centerPosition, gemID, numGemsMatched));
- //If we matched 5 gems, store the location where we want to put the treasure chest once the scoring is done
- if(numGemsMatched >= 5)
- {
- AddGemToTreasureQueue(tile1Index, tile2Index);
- }
- }
- public void AddGemToScoreQueue(Point2i tileIndex, bool wasDestroyedByMagic)
- {
- //Make sure this gem hasn't been recently destroyed
- if(_activeGrid[tileIndex.x, tileIndex.y] == null)
- return;
- int gemID = _activeGrid[tileIndex.x, tileIndex.y].GemID;
- Vector3 centerPosition = GetCenterPointFromGridCoords(tileIndex, GameLayer.Text);
- //Do they get a bonus for destroying this with magic?
- int scoreMultiplier = 1;
- if(wasDestroyedByMagic && GameManager.Instance.GameState.HasUnlockedGnarledWoodenStaff)
- scoreMultiplier = 2;
- _scoreQueue.Add(new GemMatch(centerPosition, gemID, 1, scoreMultiplier));
- }
- public void AddGemToDeathQueue(Point2i tileIndex)
- {
- bool isAlreadyAdded = false;
- //Make sure this tileIndex is not already added.
- foreach(Point2i tile in _deathQueue)
- {
- if(tile.x == tileIndex.x && tile.y == tileIndex.y)
- {
- isAlreadyAdded = true;
- break;
- }
- }
- if(!isAlreadyAdded)
- _deathQueue.Add(tileIndex);
- }
- public void DestroyGem(Gem gemToDestroy)
- {
- //Find the gem in the active grid and destroy it
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- if(_activeGrid[x,y] == gemToDestroy)
- {
- ClearGridSpace(new Point2i(x,y));
- gemToDestroy.Die();
- }
- }
- }
- }
- private void AddGemToTreasureQueue(Point2i tile1Index, Point2i tile2Index)
- {
- int treasureTileX = (tile1Index.x + tile2Index.x) / 2;
- int treasureTileY = (tile1Index.y + tile2Index.y) / 2;
- _treasureQueue.Add(new Point2i(treasureTileX, treasureTileY));
- }
- private void MoveAllGemsToUpNext()
- {
- Gem[,] temporaryGrid = new Gem[_numColumns, _numRows];
- int temporaryGridIndex = 0;
- //Get a reference to every Gem because they could be spread out between the active or upNext grid
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- if(_upNextGrid[x,y] != null)
- {
- temporaryGrid[temporaryGridIndex % _numColumns, temporaryGridIndex / _numColumns] = _upNextGrid[x,y];
- temporaryGridIndex++;
- _upNextGrid[x,y] = null;
- }
- if(_activeGrid[x,y] != null)
- {
- temporaryGrid[temporaryGridIndex % _numColumns, temporaryGridIndex / _numColumns] = _activeGrid[x,y];
- temporaryGridIndex++;
- _activeGrid[x,y] = null;
- }
- }
- }
- //Move all the gems to the upNext grid & set their position
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- _upNextGrid[x,y] = temporaryGrid[x,y];
- _upNextGrid[x,y].transform.position = _upNextGridPosition[x,y];
- }
- }
- }
- private void ConfirmValidBoardExists()
- {
- if(GetAreTilesMoving())
- return;
- //Checks the board to make sure there is a valid game board with at least one move. If there isnt, which should be very rare, the entire board will be reset.
- CheckForMoves(_activeGrid);
- //If no hints are available, reset the board
- if(!_hintList.HasHints)
- {
- Debug.Log("There were no valid moves on the board. Resetting.");
- ResetGameBoard();
- GameWindow.Instance.SaveGameBoard();
- }
- }
- #endregion
- private void AutoPlay()
- {
- if(GetAreTilesMoving() || !_hintList.HasHints)
- return;
- //Get one of the best hints and use it
- Hint hint = _hintList.GetBestHint();
- SwapTiles(hint.Gem1Position, hint.Gem2Position, true);
- }
- #region :: Event Handlers ::
- private void HandleGemIsMovingChanged(object kinematicComponent, EventArgs e)
- {
- if(((Kinematic2D)kinematicComponent).IsMoving == false)
- {
- //If all the tiles on the board are done moving, check for matches
- if(GetAreTilesMoving() == false)
- {
- CheckForMatches(_activeGrid);
- //If we found no matches but swapped a tile, reverse the swapping
- if(_deathQueue.Count == 0 && _swappedTiles[0].IsValid)
- {
- SwapTiles(_swappedTiles[0], _swappedTiles[1], false);
- return;
- }
- else if(_deathQueue.Count == 0)
- {
- return;
- }
- _swappedTiles[0].Reset();
- _swappedTiles[1].Reset();
- //We must commit the treasure queue before the death queue because this method will remove a gem from the death queue for use by the treasure icon
- CommitTreasureQueue();
- CommitDeathQueue();
- CommitScoreQueue();
- //Move the gems from the up next grid to the active grid
- UpdateActiveGrid();
- }
- }
- }
- public void HandleInputPress(POINTER_INFO pointer)
- {
- //Ignore touches if we are moving tiles around
- if(GetAreTilesMoving() || this.PauseActiveGridUpdates)
- return;
- //Our pointer is in screen coordinates, convert this into world coordinates using our camera
- Vector3 mouseWorldPosition = _inputComponent.RenderCamera.ScreenToWorldPoint(pointer.devicePos);
- //We need to adjust our mouse click to be relative to the lower left corner of the game grid so that it's easier to determine which grid x,y we clicked on.
- //We cached the lower left point of the grid during initialization in _lowerLeftPosition
- Vector3 mouseLowerLeftPosition = mouseWorldPosition - _lowerLeftPosition;
- int gridX = (int)(Math.Floor(mouseLowerLeftPosition.x) / _gemSize);
- int gridY = (int)(Math.Floor(mouseLowerLeftPosition.y) / _gemSize);
- Point2i tileIndex = new Point2i(gridX, gridY);
- //Check to see if the mouse input needs to be handled by the GameWindow because we have an active spell
- if(GameWindow.Instance.GetDoesActiveSpellExecuteOnUserInput())
- {
- GameWindow.Instance.ExecuteSpellsOnUserInput(tileIndex);
- }
- else if(_selectedTileCurrent.x != gridX || _selectedTileCurrent.y != gridY)
- {
- //No tiles are moving on the board, did we select a new tile?s
- SelectGridPosition(tileIndex);
- }
- }
- public void HandleInputDrag(POINTER_INFO pointer)
- {
- //Ignore touches if we are moving tiles around
- if(GetAreTilesMoving() || this.PauseActiveGridUpdates)
- return;
- //Our pointer is in screen coordinates, convert this into world coordinates using our camera
- Vector3 mouseWorldPositionStart = _inputComponent.RenderCamera.ScreenToWorldPoint(pointer.origPos);
- Vector3 mouseWorldPositionEnd = _inputComponent.RenderCamera.ScreenToWorldPoint(pointer.devicePos);
- //We need to adjust our mouse click to be relative to the lower left corner of the game grid so that it's easier to determine which grid x,y we clicked on.
- //We cached the lower left point of the grid during initialization in _lowerLeftPosition
- Vector3 mouseLowerLeftPositionStart = mouseWorldPositionStart - _lowerLeftPosition; //The position of the mouse relative to the lower left point on the game grid
- int gridXStart = (int)(Math.Floor(mouseLowerLeftPositionStart.x) / _gemSize);
- int gridYStart = (int)(Math.Floor(mouseLowerLeftPositionStart.y) / _gemSize);
- Vector3 mouseLowerLeftPositionEnd = mouseWorldPositionEnd - _lowerLeftPosition; //The position of the mouse relative to the lower left point on the game grid
- int gridXEnd = (int)(Math.Floor(mouseLowerLeftPositionEnd.x) / _gemSize);
- int gridYEnd = (int)(Math.Floor(mouseLowerLeftPositionEnd.y) / _gemSize);
- //Did the user drag the mouse far enough to be over another column? If so, try to swap them if it's a valid move
- if(gridXStart != gridXEnd && gridXEnd >= 0 && gridXEnd < _numColumns)
- {
- //Swap horizontally. We'll keep the original Y position just in case the user dragged diagonally
- if(gridXEnd > gridXStart)
- SelectGridPosition(new Point2i(gridXStart + 1, gridYStart));
- else if(gridXEnd < gridXStart)
- SelectGridPosition(new Point2i(gridXStart - 1, gridYStart));
- }
- else if(gridYStart != gridYEnd && gridYEnd >= 0 && gridYEnd < _numRows)
- {
- //Swap vertically. We'll keep the original X position just in case the user dragged diagonally
- if(gridYEnd > gridYStart)
- SelectGridPosition(new Point2i(gridXStart, gridYStart + 1));
- else if(gridYEnd < gridYStart)
- SelectGridPosition(new Point2i(gridXStart, gridYStart - 1));
- }
- }
- #endregion
- #region :: Public Methods ::
- public void ResetGameBoard()
- {
- //Ensure all our gems are in the UpNext grid
- MoveAllGemsToUpNext();
- //Guarantee that there's at least 5 matches in the upNextGrid so that we'll always have a valid board when the game starts
- _hintList.Clear();
- while(_hintList.NumHints < 4)
- {
- for(int y=0; y<_numRows; y++)
- {
- for(int x=0; x<_numColumns; x++)
- {
- _upNextGrid[x,y].GemID = GemDataModel.Instance.GetRandomGemID();
- }
- }
- //Check to see if there are any matches -- we want none when it starts
- CheckForMatches(_upNextGrid);
- if(_deathQueue.Count > 0)
- {
- continue;
- }
- else
- {
- CheckForMoves(_upNextGrid);
- }
- }
- //Tell the game to fill in the empty holes in the game grid with those in the upNext grid
- UpdateActiveGrid();
- }
- public void ShowHint()
- {
- Hint hint = _hintList.GetBestHint();
- if(hint != null)
- {
- //Put the hint arrows to show what to swap
- this.HintArrow1.gameObject.active = true;
- this.HintArrow2.gameObject.active = true;
- this.HintArrow1.SpriteGraphics.Hide(false);
- this.HintArrow2.SpriteGraphics.Hide(false);
- //Find the position for the arrows that will place them at the top of the gems
- Vector3 hintArrow1Position = GetCenterPointFromGridCoords(hint.Gem1Position, GameLayer.HintArrows) + new Vector3(0, _gemSize/2, 0);
- Vector3 hintArrow2Position = GetCenterPointFromGridCoords(hint.Gem2Position, GameLayer.HintArrows) + new Vector3(0, _gemSize/2, 0);
- //Tell the hint arrows to animate up and down
- this.HintArrow1.Kinematic.MoveBetween(hintArrow1Position + new Vector3(0, this.HintArrowBobDistance, 0), hintArrow1Position - new Vector3(0, this.HintArrowBobDistance, 0), this.HintArrowMoveSpeed);
- this.HintArrow2.Kinematic.MoveBetween(hintArrow2Position + new Vector3(0, this.HintArrowBobDistance, 0), hintArrow2Position - new Vector3(0, this.HintArrowBobDistance, 0), this.HintArrowMoveSpeed);
- ScheduleTimer("HideHintArrows", 2.0f, HideHintArrows);
- }
- AudioManager.Instance.Play(AudioClips.UI_TAP);
- }
- public Gem GetGemAtPosition(Point2i tileIndex)
- {
- return _activeGrid[tileIndex.x, tileIndex.y];
- }
- public void PlayGemsFallSound()
- {
- //GameWindow.Instance.SoundGemsFall.PlayOneShot(GameWindow.Instance.SoundGemsFall.clip);
- }
- #endregion
- #region :: Public Properties ::
- public int NumRows
- {
- get
- {
- return _numRows;
- }
- }
- public int NumColumns
- {
- get
- {
- return _numColumns;
- }
- }
- public float GemSize
- {
- get
- {
- return _gemSize;
- }
- }
- public float Width
- {
- get
- {
- return _numColumns * _gemSize;
- }
- }
- public float Height
- {
- get
- {
- return _numRows * _gemSize;
- }
- }
- public SpriteManager SpriteManager
- {
- get
- {
- return _spriteManagerComponent;
- }
- }
- #endregion
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement