using System; using UnityEngine; using System.Collections.Generic; using System.Linq; /// /// Handles the logic of a single tile /// public class TileScript : MonoBehaviour { #region Enums /// /// Type of tile (bomb or mine) /// public enum EType { Safe, Mine } /// /// Number of the tile (can be correctly casted to an integer) /// public enum ENumber { Empty, One, Two, Three, Four, Five, Six, Seven, Eight } #endregion #region Fields /// /// References to unrevealed tiles /// public Sprite TileUnknown, TilePressed; /// /// References to revealed empty tiles /// public Sprite Tile0, Tile1, Tile2, Tile3, Tile4, Tile5, Tile6, Tile7, Tile8; /// /// References to mine-related tiles /// public Sprite TileMine, TileFlag, TileQuestion, TileQuestionPressed; /// /// References to revealed or wrongly placed flag tiles /// public Sprite TileDead, TileWrong; public float LocalScale = 1; /// /// What type is this /// private EType _type = EType.Safe; private ENumber _num = ENumber.Empty; private bool _flagged; private bool _revealed; private Sprite _sprRevealed; private SpriteRenderer _sprRend; private bool _enabled = true; // Tile container private ControlScript _parent; private GameObject[,] _container; private Vector2i _gridPos; private List _neighbours = new List(); // Tile mouse fields private bool _leftDown; private bool _rightDown; private bool _middleDown; #endregion #region Properties /// /// What type is this tile (safe or not)? /// public EType Type { get { return _type; } set { _type = value; AssignTexture(); } } /// /// What number does this tile have? Only applicable if not a mine /// public ENumber Number { get { return _num; } set { _num = value; AssignTexture(); } } /// /// Reference to the control script /// public ControlScript Parent { get { return _parent; } set { _parent = value; } } /// /// Reference to the tiles container /// public GameObject[,] Container { get { return _container; } set { _container = value; } } /// /// Position of this tile within the grid /// public Vector2i GridPos { get { return _gridPos; } set { _gridPos = value; } } /// /// Is the tile revealed? Setting it will apply its value. /// public bool Revealed { get { return _revealed; } set { if (_revealed && !value) { Unreveal(); _revealed = false; } else if (!_revealed && value) { Reveal(); } } } /// /// Is the tile flagged? Setting it will apply or remove the flag /// public bool Flagged { get { return _flagged; } set { if (value) Flag(); else Unflag(); } } /// /// All neighbours of this tile /// public List Neighbours { get { return _neighbours; } } /// /// Adds a neighbour. Strange that this is a property... /// public TileScript NewNeighbour { set { _neighbours.Add(value); } } /// /// Should the tile react to user input /// public bool Enabled { get { return _enabled; } set { _enabled = value; } } #endregion #region Unity Methods /// /// First initailization, called before all Start methods /// void Awake() { _sprRevealed = Tile0; // default reveal tile } /// /// Use this for initialization /// void Start() { _sprRend = GetComponent(); _sprRend.sprite = TileUnknown; transform.localScale = new Vector3(LocalScale, LocalScale, LocalScale); } /// /// Update is called once per frame /// void Update() { // Left mouse button released if (_parent.Controls[EControls.TileReleased].IsActive() && _leftDown) { _leftDown = false; if (!_revealed) TileRelease(); } // Right mouse button released if (_parent.Controls[EControls.TileFlagReleased].IsActive() && _rightDown) _rightDown = false; // Middle mouse button released if (_parent.Controls[EControls.TileSquareReleased].IsActive() && _middleDown) { _middleDown = false; foreach (var neighbour in _neighbours.Where(neighbour => !neighbour._flagged && !neighbour._revealed)) neighbour.TileRelease(); } } /// /// Is called every frame that the mouse is over this objects bounding box /// void OnMouseOver() { if (!_enabled) return; // Left mouse button behaviour if (!_revealed && _parent.Controls[EControls.TilePressed].IsActive()) { _leftDown = true; TilePress(); } if (_parent.Controls[EControls.TileReleased].IsActive() && _leftDown) _parent.OnTileClick(gameObject); // Right mouse button behaviour if (!_revealed && _parent.Controls[EControls.TileFlagPressed].IsActive()) _rightDown = true; if (_parent.Controls[EControls.TileFlagReleased].IsActive() && _rightDown) _parent.OnTileRightClick(gameObject); // Middle mouse button behaviour if (_revealed && _parent.Controls[EControls.TileSquarePressed].IsActive()) { _middleDown = true; foreach (var neighbour in _neighbours.Where(neighbour => !neighbour._flagged && !neighbour._revealed)) neighbour.TilePress(); } if (_parent.Controls[EControls.TileSquareReleased].IsActive() && _middleDown) _parent.OnTileMiddleClick(gameObject); } #endregion #region Methods /// /// Removes all neighbours /// public void ClearNeighbours() { _neighbours.Clear(); } /// /// Reveals current tile and nearby ones if ENumber == Empty /// public void Reveal() { // skip if already revealed or flagged if (_revealed || _flagged) return; _revealed = true; _sprRend.sprite = _sprRevealed; if (_type != EType.Mine && _num == ENumber.Empty) Neighbours.ForEach(neighbour => neighbour.Revealed = true); _parent.OnTileReveal(gameObject); } /// /// Used when the user reveals this mine /// public void SetAsDeadMine() { _sprRend.sprite = TileDead; } /// /// Hides the tile again /// public void Unreveal() { // Skip if already hidden if (!_revealed) return; _revealed = false; _sprRend.sprite = TileUnknown; } /// /// Reveals the tile as if the game was lost (only mines are revealed, empty tiles and correctly flagged mines are /// unaffected, incorrectly flagged mines are tagged) /// public void RevealAsLoseState() { if (_type == EType.Mine && !_flagged && !_revealed) { _sprRend.sprite = TileMine; } else if (_type != EType.Mine && _flagged) { _sprRend.sprite = TileWrong; } } /// /// Swap the flag state /// public void FlagSwap() { if (_flagged) Unflag(); else Flag(); } /// /// Forces a flag on the tile /// public void Flag() { // skip if already flagged or revealed if (_flagged || _revealed) return; _flagged = true; _sprRend.sprite = TileFlag; } /// /// Forces to remove the flag /// public void Unflag() { // skip if already unflagged if (!_flagged) return; _flagged = false; _sprRend.sprite = TileUnknown; } #endregion #region Helper functions /// /// Visual feedback when the user clicks a mine /// private void TilePress() { if (!_revealed && !_flagged) _sprRend.sprite = TilePressed; } /// /// Visual feedback for when the user releases the click /// private void TileRelease() { if (!_revealed && !_flagged) _sprRend.sprite = TileUnknown; } /// /// Assigns the correct texture for when the tile is revealed /// private void AssignTexture() { switch (_type) { case EType.Safe: switch (_num) { case ENumber.Empty: _sprRevealed = Tile0; break; case ENumber.One: _sprRevealed = Tile1; break; case ENumber.Two: _sprRevealed = Tile2; break; case ENumber.Three: _sprRevealed = Tile3; break; case ENumber.Four: _sprRevealed = Tile4; break; case ENumber.Five: _sprRevealed = Tile5; break; case ENumber.Six: _sprRevealed = Tile6; break; case ENumber.Seven: _sprRevealed = Tile7; break; case ENumber.Eight: _sprRevealed = Tile8; break; default: throw new ArgumentOutOfRangeException(); } break; case EType.Mine: _sprRevealed = TileMine; break; default: throw new ArgumentOutOfRangeException(); } } #endregion }