MatchableGrid.cs

mail@pastecode.io avatar
unknown
csharp
5 months ago
15 kB
5
Indexable
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MatchableGrid : GridSystem<Matchable>
{
    private MatchablePool pool;
    private ScoreManager score;

    [SerializeField] private Vector3 offScreenOffset;

    
    private void Start()
    {
        pool = (MatchablePool) MatchablePool.Instance;
        score = (ScoreManager) ScoreManager.Instance;
    }

    // private void Update()
    // {
        // Normally we could have used that since this game is not a big one. However, this would create inefficiency since we run multiple codes for each frame. 
        // 
    // }


    public IEnumerator PopulateGrid(bool allowMatches = false, bool initialPopulation = false) // yield return 0.1 only happens in init population.
    {
        List<Matchable> newMatchables = new List<Matchable>(); // List of new matchables during population.
        Matchable newMatchable;
        Vector3 onScreenPosition;

        for (int y=0; y!= Dimensions.y; ++y)
            for(int x=0; x!=Dimensions.x; ++x)
                if (IsEmpty(x,y))
                {
                    // Get a matchable from pool
                    newMatchable = pool.GetRandomMatchable();
                    // position the matchable on screen
                    newMatchable.transform.position = transform.position + new Vector3(x,y) + offScreenOffset;
                
                    // activate the matchable 
                    newMatchable.gameObject.SetActive(true);
                    // Position of the matchable 
                    newMatchable.position = new Vector2Int(x,y);
                    // place the matchable in the grid-- since we are inheriting from gridsystem
                    PutItemAt(newMatchable,x ,y);
                    newMatchables.Add(newMatchable);

                    int type = newMatchable.Type;

                    while(!allowMatches && IsPartOfAMatch(newMatchable))
                    {   
                        // change the matchables type until it isnt a match anymore.
                        if (pool.NextType(newMatchable) == type)
                        {
                            Debug.LogWarning("Failed to find a matchable type that didn't match at (" + x + "," + y + ")");
                            Debug.Break();
                            break;
                        }
                    }
                // Move the matchable to its own position
//                 yield return new WaitForSeconds(0.1f);
            }

        for (int i =0; i < newMatchables.Count; ++i)
        {
            onScreenPosition = transform.position + new Vector3(newMatchables[i].position.x,newMatchables[i].position.y);
            if (i == newMatchables.Count - 1)
                yield return StartCoroutine(newMatchables[i].MoveToPosition(onScreenPosition));
            else
                StartCoroutine(newMatchables[i].MoveToPosition(onScreenPosition));

            // wait for 0.1 seconds after each. Just a cool effect. 
            if (initialPopulation)
                yield return new WaitForSeconds(0.05f);
        }

    }
// Check if the matchable being populated is part of a match or not -- 
    private bool IsPartOfAMatch(Matchable toMatch)
    {
        int horizontalMatches = 0;
        int verticalMatches = 0;
        // We start checking for left. Will do the same algorithm for each side.
        horizontalMatches += CountMatchesInDirection(toMatch, Vector2Int.left);
        horizontalMatches += CountMatchesInDirection(toMatch, Vector2Int.right);

        if (horizontalMatches>1)
            return true;

        verticalMatches += CountMatchesInDirection(toMatch, Vector2Int.up);
        verticalMatches += CountMatchesInDirection(toMatch, Vector2Int.down);
        if (verticalMatches>1)
            return true;

        return false;
    
    }
// Count the number of matches on the grid starting from the matchable to match moving in the direction indicated

    private int CountMatchesInDirection(Matchable toMatch, Vector2Int direction)
    {
        int matches =0;
        Vector2Int position = toMatch.position + direction;
        while(CheckBounds(position) && !IsEmpty(position) && GetItemAt(position).Type == toMatch.Type)
        {
            ++matches;
            position += direction;
        }
        return matches;
    }


    public IEnumerator TrySwap(Matchable[] selected)
    {

        // To prevent the crashes due to simultaneously mouse-enter while animate, we get a local copy so that function wont depend on 
        // current array, it will depend on the version of the array when function starts.

        Matchable[] copies = new Matchable[2];
        copies[0] = selected[0];
        copies[1] = selected[1]; 
        // yield until matchables animate swapping
        yield return StartCoroutine(Swap(copies));

        // If both are gem, reoslve entire grid
        if (copies[0].IsGem && copies[1].IsGem)
        {   
            MatchEverything();
            yield break;
        }

        else if (copies[0].IsGem)
        {
            MatchEverythingByType(copies[0],copies[1].Type);
            yield break;
        }
        else if (copies[1].IsGem)
        {
            MatchEverythingByType(copies[1],copies[0].Type);
            yield break;
        }

        Match[] matches = new Match[2];
        matches[0] = GetMatch(copies[0]);
        matches[1] = GetMatch(copies[1]);


        if (matches[0] != null)
        {
            // Need to resolve this
            StartCoroutine(score.ResolveMatch(matches[0]));
        }
        if (matches[1] !=null)
        {
            // Resolve also 
            StartCoroutine(score.ResolveMatch(matches[1]));
        }
        if (matches[0] == null && matches[1] == null)
        {
            yield return StartCoroutine(Swap(copies));
            if (ScanForMatches())
                StartCoroutine(FillAndScanGrid());
        }
        else 
            StartCoroutine(FillAndScanGrid());

    }

    private IEnumerator FillAndScanGrid()
    {
            CollapseGrid(); // After the match, matching matchables will leave empty spaces. 
            yield return StartCoroutine(PopulateGrid(true)); // if there are matches 
            // Instead of Update above, we will check whenever a match happens. Here we collapse grid, populate grid and now we will scan the grid for chain reactions.
            if (ScanForMatches())
                // collapse - repopulate - scan again 
                StartCoroutine(FillAndScanGrid());
    }

    private Match GetMatch(Matchable toMatch) 
    {
        Match match = new Match(toMatch);
        Match   horizontalMatch,
                verticalMatch; 

        /* for above 3 line code, explanation
        Below function will not contain the original matchable. However, we add it add first line of this function. Therefore,
        horizontalMatch or verticalMatch needs to contain at least 2 Matchables in the matching scenario.

        */       

        horizontalMatch = GetMatchesInDirection(match,toMatch, Vector2Int.left);
        horizontalMatch.Merge(GetMatchesInDirection(match, toMatch, Vector2Int.right));
        
        horizontalMatch.orientation = Orientation.horizontal;
        
        if (horizontalMatch.Count > 1)
        {
            match.Merge(horizontalMatch);
            // scan for vertical branches 
            GetBranches(match, horizontalMatch, Orientation.vertical);
        }


        verticalMatch = GetMatchesInDirection(match,toMatch, Vector2Int.up);
        verticalMatch.Merge(GetMatchesInDirection(match, toMatch, Vector2Int.down));

        verticalMatch.orientation = Orientation.vertical;
        if (verticalMatch.Count > 1)
        {
            match.Merge(verticalMatch);
            GetBranches(match, verticalMatch, Orientation.horizontal);
        }

        if (match.Count == 1)
            return null; // No matches, only the input. 

        return match;

    }

    private void GetBranches(Match tree, Match branchToSearch, Orientation perpendicular)
    {
        Match branch;

        foreach(Matchable matchable in branchToSearch.Matchables)
        {
            branch = GetMatchesInDirection(tree,matchable, perpendicular == Orientation.horizontal ? Vector2Int.left : Vector2Int.down);
            branch.Merge(GetMatchesInDirection(tree, matchable, perpendicular == Orientation.horizontal ? Vector2Int.right : Vector2Int.up));
        
            branch.orientation  = perpendicular;

            if (branch.Count > 1) // At least 2 and we also have the main matchable.
                tree.Merge(branch);
                GetBranches(tree, branch, perpendicular == Orientation.horizontal ? Orientation.vertical : Orientation.horizontal);
        }
    }

// Add each matching matchable in the directino to a math and return that. 
    private Match GetMatchesInDirection(Match tree, Matchable toMatch, Vector2Int direction)
    {
        Match match = new Match();
        Vector2Int position = toMatch.position + direction;
        Matchable next;

        while(CheckBounds(position) && !IsEmpty(position))
        {
            next = GetItemAt(position);
            if (next.Type == toMatch.Type && next.Idle)
            {
                if(!tree.Contains(next))
                    match.AddMatchable(next);
                else
                {
                    match.AddUnlisted();
                } 
                    

                position += direction;
            }
            else
                break;

        }
        return match;
    }

    private void CollapseGrid()
    {
        for (int x = 0; x != Dimensions.x; ++x)
            for (int yEmpty = 0; yEmpty != Dimensions.y - 1; ++yEmpty)
            {
                if (IsEmpty(x,yEmpty))
                    for (int yNotEmpty = yEmpty + 1; yNotEmpty != Dimensions.y; ++yNotEmpty)
                        if(!IsEmpty(x, yNotEmpty) && GetItemAt(x, yNotEmpty).Idle)
                        {
                            // Move the matchables from nonempty to empty 
                            MoveMatchableToPosition(GetItemAt(x, yNotEmpty), x, yEmpty);
                            break;
                        }
            }   
    }

    private void MoveMatchableToPosition(Matchable toMove, int x, int y)
    {
        // Remove it from the old position 

        // Place it the matchable as its new position 
        MoveItemTo(toMove.position, new Vector2Int(x,y));
        // Update the matchable's internal grid position 
        toMove.position = new Vector2Int(x,y);
        // Start animation to move it on screen  
        StartCoroutine(toMove.MoveToPosition(transform.position + new Vector3(x,y)));
    }

    private bool ScanForMatches()
    {
        bool madeAMatch = false;
        Matchable toMatch;
        Match match;
        for (int y =0; y != Dimensions.y; ++y)
            for (int x= 0; x != Dimensions.x; ++x)
                if(!IsEmpty(x,y))
                {
                    toMatch = GetItemAt(x,y);
                    if(!toMatch.Idle)
                    {
                        continue;
                    }
                    match = GetMatch(toMatch);
                    if (match != null)
                    {
                        madeAMatch = true;
                        StartCoroutine(score.ResolveMatch(match));
                    }
                }

        return madeAMatch;
    }


    public void MatchAllAdjacent(Matchable powerup) // Explode a square 3x3
    {
        Match allAdjacent = new Match();
        for (int y = powerup.position.y -1; y!= powerup.position.y +2; ++y)
            for (int x = powerup.position.x -1; x != powerup.position.x +2; ++x)
                if(CheckBounds(x,y) && !IsEmpty(x,y) && GetItemAt(x,y).Idle)
                {
                    allAdjacent.AddMatchable(GetItemAt(x,y));
                }
        StartCoroutine(score.ResolveMatch(allAdjacent, MatchType.match4));
    }
    
    public void MatchRowAndColumn(Matchable powerup) // match  all row and column, another powerup.
    {
        Match rowAndColumn = new Match();

        for (int y = 0; y!= Dimensions.y; ++y)
            if(CheckBounds(powerup.position.x,y) && !IsEmpty(powerup.position.x,y) && GetItemAt(powerup.position.x,y).Idle)
                rowAndColumn.AddMatchable(GetItemAt(powerup.position.x, y));

        for (int x = 0; x != Dimensions.x; ++x)
            if(CheckBounds(x,powerup.position.y) && !IsEmpty(x,powerup.position.y) && GetItemAt(x,powerup.position.y).Idle)
                    rowAndColumn.AddMatchable(GetItemAt(x,powerup.position.y));

        StartCoroutine(score.ResolveMatch(rowAndColumn,  MatchType.cross));
    }

    private IEnumerator Swap(Matchable[] selected)
    {
        // Swap in data 
        SwapItemsAt(selected[0].position, selected[1].position);
        // assign new positions
        Vector2Int temp = selected[0].position;
        selected[0].position = selected[1].position;
        selected[1].position = temp;

        // get world positions of both 
        Vector3[] worldPosition = new Vector3[2];
        worldPosition[0] = selected[0].transform.position;
        worldPosition[1] = selected[1].transform.position;

        // move them to their new postions 
                     StartCoroutine(selected[0].MoveToPosition(worldPosition[1]));
        yield return StartCoroutine(selected[1].MoveToPosition(worldPosition[0]));
    }

    public void MatchEverything()
    {
        Match everything = new Match();
        for (int y = 0; y != Dimensions.y; ++y)
            for (int x = 0; x != Dimensions.x; ++x)
                if (CheckBounds(x,y) && !IsEmpty(x,y) && GetItemAt(x,y).Idle)
                    everything.AddMatchable(GetItemAt(x,y));

        StartCoroutine(score.ResolveMatch(everything,  MatchType.match5));
        StartCoroutine(FillAndScanGrid());

    }

    public void MatchEverythingByType(Matchable gem, int type)
    {
        Match everythingByType = new Match(gem);
        for (int y = 0; y != Dimensions.y; ++y)
            for (int x = 0; x != Dimensions.x; ++x)
                if (CheckBounds(x,y) && !IsEmpty(x,y) && GetItemAt(x,y).Idle && GetItemAt(x,y).Type == type)
                    everythingByType.AddMatchable(GetItemAt(x,y));

        StartCoroutine(score.ResolveMatch(everythingByType,  MatchType.match5));
        StartCoroutine(FillAndScanGrid());
    }


}
Leave a Comment