/*
     * Build the CrustNode array of lists that the Crust uses
     * Utilises the Object Pooler to avoid needing the GC later
     * on during the simulation.
     */
    public static List <CrustNode>[,] BuildCrustNodesArray(int width, int height)
    {
        List <CrustNode>[,] nodes;

        nodes = new List <CrustNode> [width, height];

        for (int xPos = 0; xPos < width; xPos++)
        {
            for (int zPos = 0; zPos < height; zPos++)
            {
                CrustNode n = ObjectPooler.current.GetPooledNode();

                n.X         = xPos;
                n.Z         = zPos;
                n.Height    = 0f;
                n.Density   = 0.1f;
                n.IsVirtual = false;

                nodes[xPos, zPos] = new List <CrustNode>();
                nodes[xPos, zPos].Add(n);
            }
        }

        return(nodes);
    }
Exemple #2
0
 public void Copy(CrustNode nodeToCopy)
 {
     this.x     = nodeToCopy.X;
     this.z     = nodeToCopy.Z;
     this.plate = nodeToCopy.Plate;
     plate.NodeCount++;
     this.height    = nodeToCopy.Height;
     this.density   = nodeToCopy.Density;
     this.isVirtual = nodeToCopy.IsVirtual;
     this.type      = nodeToCopy.Type;
 }
    void Start()
    {
        pooledNodes = new Stack <CrustNode>(pooledNodeAmount);
        for (int i = 0; i < pooledNodeAmount; i++)
        {
            CrustNode node = new CrustNode(0, 0);
            pooledNodes.Push(node);
        }

        pooledVolcanos = new Stack <Volcano>(pooledVolcanoAmount);
        for (int i = 0; i < pooledVolcanoAmount; i++)
        {
            Volcano volcano = new Volcano(0, 0, null, null);
            pooledVolcanos.Push(volcano);
        }
    }
        public static Color CalculateColor(CrustNode n, float seaLevel, float maxHeight)
        {
            float h = n.Height;
            float normalisedHeight;

            if (n.Type == MaterialType.Oceanic)
            {
                normalisedHeight = h / seaLevel;
                if (normalisedHeight > 1f)
                {
                    return(ColorEx.sandBrownLight);
                }
                else
                {
                    return(Color.Lerp(ColorEx.oceanDeepBlue, ColorEx.oceanLightBlue, normalisedHeight));
                }
            }
            else
            {
                if (h < seaLevel)
                {
                    normalisedHeight = h / seaLevel;
                    return(Color.Lerp(ColorEx.oceanDeepBlue, ColorEx.oceanShallowsBlue, normalisedHeight));
                }
                else
                {
                    h -= seaLevel;
                    if (h < maxHeight * 0.05f) //coast
                    {
                        normalisedHeight = h / maxHeight * 0.05f;
                        return(Color.Lerp(ColorEx.sandBrownLight, ColorEx.sandBrownDark, normalisedHeight));
                    }
                    else if (h < maxHeight * 0.5f) //land
                    {
                        normalisedHeight = Mathf.InverseLerp(0.0f, maxHeight * 0.45f, h - maxHeight * 0.05f);
                        return(Color.Lerp(ColorEx.forestGreenLight, ColorEx.forestGreenDark, normalisedHeight));
                    }
                    else //mountains
                    {
                        normalisedHeight = Mathf.InverseLerp(0.0f, maxHeight * 1f, h - maxHeight * 0.5f);
                        return(Color.Lerp(ColorEx.mountainGrey, Color.white, normalisedHeight));
                    }
                }
            }
        }
    private void MoveNodes()
    {
        for (int p = 0; p < plates.Length; p++)
        {
            plates[p].RegisterMovement();
        }

        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
                //move the nodes
                for (int n_i = 0; n_i < crustNodes[j, i].Count; n_i++)
                {
                    //Get new x and y for prev node
                    CrustNode prevN = crustNodes[j, i][n_i];

                    int newX, newZ;
                    if (prevN.Plate.CheckMoveX())
                    {
                        int dx = prevN.Plate.XSpeed;
                        newX = (prevN.X + dx) % width;
                        if (newX < 0)
                        {
                            newX = width + newX;
                        }
                    }
                    else
                    {
                        newX = prevN.X;
                    }
                    if (prevN.Plate.CheckMoveZ())
                    {
                        int dz = prevN.Plate.ZSpeed;
                        newZ = (prevN.Z + dz) % height;
                        if (newZ < 0)
                        {
                            newZ = height + newZ;
                        }
                    }
                    else
                    {
                        newZ = prevN.Z;
                    }

                    //insert it at it's new position in movedNodes
                    var movedNode = ObjectPooler.current.GetPooledNode();
                    movedNode.Copy(prevN); //dereference
                    movedNode.X = newX;
                    movedNode.Z = newZ;
                    movedCrustNodes[newX, newZ].AddLast(movedNode);
                }
            }
        }

        // update plate speeds
        for (int p = 0; p < plates.Length; p++)
        {
            plates[p].ApplyVectorAffectors();
            plates[p].RecalculateMass();
        }
    }
    private static void CollidePlates(int xPos, int zPos, ref LinkedList <CrustNode>[,] movedNodes, ref Crust crust, ref Dictionary <Plate, int> singlePlateSpacesCounts, float subductionFactor) //TODO: None of these methods properly consider Virtual nodes yet (need to change this!)
    {
        int width  = movedNodes.GetLength(0);
        int height = movedNodes.GetLength(1);

        bool hasOceanic = false, hasContinental = false, hasMultipleOc = false, hasMultipleCo = false;

        var currentNode = movedNodes[xPos, zPos].First;

        for (int k = 0; k < movedNodes[xPos, zPos].Count; k++)
        {
            //count oceanic/continental
            if (currentNode.Value.Type == MaterialType.Oceanic)
            {
                if (hasOceanic != true)
                {
                    hasOceanic = true;
                }
                else
                {
                    hasMultipleOc = true;
                }
            }
            else
            {
                if (hasContinental != true)
                {
                    hasContinental = true;
                }
                else
                {
                    hasMultipleCo = true;
                }
            }


            //also (if non-virtual), cause nodes to affect eachother's plate's speeds
            if (!currentNode.Value.IsVirtual)
            {
                var affectedNode = movedNodes[xPos, zPos].First;
                for (int n = 0; n < movedNodes[xPos, zPos].Count; n++)
                {
                    if (n != k) // don't affect itself
                    {
                        affectedNode.Value.Plate.AffectPlateVector(currentNode.Value.Plate);
                    }
                    affectedNode = affectedNode.Next;
                }
            }

            currentNode = currentNode.Next;
        }

        int listLength;

        //if O-O, Lowest density plate subducts
        if (!hasContinental)
        {
            int   mostDense      = 0;
            float highestDensity = 0.0f;
            currentNode = movedNodes[xPos, zPos].First;
            for (int k = 0; k < movedNodes[xPos, zPos].Count; k++)
            {
                if (currentNode.Value.Density > highestDensity)
                {
                    mostDense      = k;
                    highestDensity = currentNode.Value.Density;
                }
                currentNode = currentNode.Next;
            }

            currentNode = movedNodes[xPos, zPos].First;
            listLength  = movedNodes[xPos, zPos].Count;
            for (int k = 0; k < listLength; k++)
            {
                if (k != mostDense)
                {
                    currentNode.Value.IsVirtual = true;
                    currentNode.Value.Height    = currentNode.Value.Height - subductionFactor; //subduct downwards
                    currentNode = currentNode.Next;
                }
                else
                {
                    currentNode.Value.IsVirtual = false;
                    if (k != 0) //no need to move it to the start if it's already there
                    {
                        movedNodes[xPos, zPos].AddFirst(currentNode.Value);
                        var nodeToDelete = currentNode;
                        currentNode = currentNode.Next;
                        movedNodes[xPos, zPos].Remove(nodeToDelete);
                    }
                }
            }
        }
        //if C-C, crunch
        else if (!hasOceanic)
        {
            //TEMPORARY, JUST USE THE OLD NAIVE CRUNCH
            currentNode = movedNodes[xPos, zPos].First;
            CrustNode fastestPlateNode         = null;
            float     highestAggregateVelocity = 0f;
            for (int k = 0; k < movedNodes[xPos, zPos].Count; k++)
            {
                //find fastest plate
                float aggregateVelocity = Math.Abs(currentNode.Value.Plate.AccurateXSpeed) + Math.Abs(currentNode.Value.Plate.AccurateZSpeed);
                if (aggregateVelocity > highestAggregateVelocity)
                {
                    fastestPlateNode         = currentNode.Value;
                    highestAggregateVelocity = aggregateVelocity;
                }

                //Add all present plates to dict to be used later for plate assignment
                if (!singlePlateSpacesCounts.ContainsKey(currentNode.Value.Plate))
                {
                    singlePlateSpacesCounts.Add(currentNode.Value.Plate, 0);
                }
                currentNode = currentNode.Next;
            }

            currentNode = movedNodes[xPos, zPos].First;
            for (int k = 0; k < movedNodes[xPos, zPos].Count; k++)
            {
                if (currentNode.Value != fastestPlateNode && Random.Range(0.0f, 1.0f) > 0.99f)
                {
                    //add together the speeds of both plates relative to eachother to see how big the impact and resultant crumpling should be
                    float comparativeX = fastestPlateNode.Plate.AccurateXSpeed - currentNode.Value.Plate.AccurateXSpeed; // (we flip the smaller vector for comparison)
                    float comparativeZ = fastestPlateNode.Plate.AccurateZSpeed - currentNode.Value.Plate.AccurateZSpeed;

                    //now get the magnitude of this comparison vector and this will represent the power of the collision
                    float collisionMagnitude = Mathf.Sqrt(comparativeX * comparativeX + comparativeZ * comparativeZ);

                    //Send out a pulse from this node in the opposite direction to the movement of the slower plate (could change all this later to consider weight/density/force etc)
                    int pulseDistance     = Mathf.RoundToInt(collisionMagnitude) * Random.Range(100, 201);
                    int halfPulseDistance = pulseDistance / 2;
                    int xLoc      = xPos;
                    int zLoc      = zPos;
                    int dx        = 1;
                    int dz        = 1;
                    int moveCount = 0;

                    if (currentNode.Value.Plate.AccurateXSpeed < 0)
                    {
                        dx = -1;
                    }
                    if (currentNode.Value.Plate.AccurateZSpeed < 0)
                    {
                        dz = -1;
                    }

                    if (currentNode.Value.Plate.ScaledVectorX == 1f)
                    {
                        for (int p = 0; p < pulseDistance; p++)
                        {
                            moveCount++;

                            xLoc += dx;
                            xLoc  = xLoc % width;
                            if (xLoc < 0)
                            {
                                xLoc = width + xLoc;
                            }

                            if (moveCount >= (1.0f / currentNode.Value.Plate.ScaledVectorZ))
                            {
                                zLoc += dz;
                                zLoc  = zLoc % height;
                                if (zLoc < 0)
                                {
                                    zLoc = height + zLoc;
                                }

                                moveCount = 0;
                            }
                        }
                    }
                    else
                    {
                        for (int p = 0; p < pulseDistance; p++)
                        {
                            moveCount++;

                            zLoc += dz;
                            zLoc  = zLoc % height;
                            if (zLoc < 0)
                            {
                                zLoc = height + zLoc;
                            }

                            if (moveCount >= (1.0f / currentNode.Value.Plate.ScaledVectorX))
                            {
                                xLoc += dx;
                                xLoc  = xLoc % width;
                                if (xLoc < 0)
                                {
                                    xLoc = width + xLoc;
                                }

                                moveCount = 0;
                            }
                        }
                    }

                    if (Random.Range(0.0f, 1.0f) < 0.9f)
                    {
                        Volcano v = ObjectPooler.current.GetPooledVolcano();
                        v.X            = xPos;
                        v.Z            = zPos;
                        v.Plate        = currentNode.Value.Plate;
                        v.MaterialRate = Random.Range(10, 150); //How many rocks get thrown out of the volcano each frame
                        crust.AddStratoVolcano(v);
                    }
                    else //random chance of a huge mountain
                    {
                        Volcano v = ObjectPooler.current.GetPooledVolcano();
                        v.X            = xPos;
                        v.Z            = zPos;
                        v.Plate        = currentNode.Value.Plate;
                        v.MaterialRate = Random.Range(250, 300); //How many rocks get thrown out of the volcano each frame
                        crust.AddStratoVolcano(v);
                    }
                }
            }


            //assign nodes to plates
            int  searchDistance = 0;
            bool found          = false;
            while (!found)
            {
                searchDistance++; // hexagon side length = searchDistance

                //It is best to search in a hexagon outline shape. This algorithm will have to be horizontally flipped if working on an odd z value row
                bool diagnonalMove = true; //Start with a diagonal move when searching from an odd row
                if (zPos % 2 == 0)
                {
                    diagnonalMove = false;
                }
                //start at the left corner and move diagonally up, then across to the right, then diagonally down to the right corner
                //at the same time, do the mirror opposite for the bottom half of the hexagon
                int x, z;
                x = -searchDistance;
                z = 0;
                // up/down from left corner
                for (int c = 0; c < searchDistance; c++)
                {
                    CheckSpaceAndUpdateDict(xPos + x, zPos + z, width, height, ref movedNodes, ref singlePlateSpacesCounts, ref found); //top half
                    CheckSpaceAndUpdateDict(xPos + x, zPos - z, width, height, ref movedNodes, ref singlePlateSpacesCounts, ref found); //bottom half
                    if (diagnonalMove)
                    {
                        x++;
                    }
                    z++;
                    diagnonalMove = !diagnonalMove;
                }
                // across top/bottom
                for (int c = 0; c < searchDistance; c++)
                {
                    CheckSpaceAndUpdateDict(xPos + x, zPos + z, width, height, ref movedNodes, ref singlePlateSpacesCounts, ref found);
                    CheckSpaceAndUpdateDict(xPos + x, zPos - z, width, height, ref movedNodes, ref singlePlateSpacesCounts, ref found);
                    x++;
                }
                // down/up to right corner
                for (int c = 0; c < searchDistance; c++)
                {
                    CheckSpaceAndUpdateDict(xPos + x, zPos + z, width, height, ref movedNodes, ref singlePlateSpacesCounts, ref found);
                    CheckSpaceAndUpdateDict(xPos + x, zPos - z, width, height, ref movedNodes, ref singlePlateSpacesCounts, ref found);
                    if (diagnonalMove)
                    {
                        x++;
                    }
                    z--;
                    diagnonalMove = !diagnonalMove;
                }
            }

            Plate closestPlate = null;
            foreach (Plate plt in singlePlateSpacesCounts.Keys)
            {
                if (closestPlate == null || singlePlateSpacesCounts[plt] > singlePlateSpacesCounts[closestPlate])
                {
                    closestPlate = plt;
                }
            }
            singlePlateSpacesCounts.Clear();

            CrustNode chosenNode = movedNodes[xPos, zPos].First.Value;
            currentNode = movedNodes[xPos, zPos].First;
            listLength  = movedNodes[xPos, zPos].Count;
            for (int k = 0; k < listLength; k++)
            {
                if (currentNode.Value.Plate == closestPlate)
                {
                    movedNodes[xPos, zPos].AddFirst(currentNode.Value);
                    var nodeToDelete = currentNode;
                    currentNode = currentNode.Next;
                    movedNodes[xPos, zPos].Remove(nodeToDelete);
                }
                else
                {
                    //Remove
                    ObjectPooler.current.ReturnNodeToPool(currentNode.Value);
                    var nodeToDelete = currentNode;
                    currentNode = currentNode.Next;
                    movedNodes[xPos, zPos].Remove(nodeToDelete);
                }
            }

            movedNodes[xPos, zPos].First.Value.IsVirtual = false;
        }
        //if C-O, oceanic subducts
        else
        {
            if (hasMultipleCo)
            {
                //not implemented
            }
            else if (hasMultipleOc)
            {
                //not implemented
            }
            else // just one O and one C
            {
                float subductedHeight;
                if (movedNodes[xPos, zPos].First.Value.Type == MaterialType.Oceanic) //O,C
                {
                    movedNodes[xPos, zPos].First.Value.IsVirtual = true;
                    movedNodes[xPos, zPos].First.Value.Height    = movedNodes[xPos, zPos].First.Value.Height - subductionFactor; //subduct downwards
                    subductedHeight = movedNodes[xPos, zPos].First.Value.Height;

                    movedNodes[xPos, zPos].Last.Value.IsVirtual = false;
                    //move to the start of the list
                    movedNodes[xPos, zPos].AddFirst(movedNodes[xPos, zPos].Last.Value);
                    movedNodes[xPos, zPos].Remove(movedNodes[xPos, zPos].Last);
                }
                else //C,O
                {
                    movedNodes[xPos, zPos].First.Value.IsVirtual = false;

                    movedNodes[xPos, zPos].Last.Value.IsVirtual = true;
                    movedNodes[xPos, zPos].Last.Value.Height    = movedNodes[xPos, zPos].Last.Value.Height - subductionFactor; //subduct downwards
                    subductedHeight = movedNodes[xPos, zPos].Last.Value.Height;
                }
            }
        }
    }
 public void ReturnNodeToPool(CrustNode n)
 {
     n.CleanObject();
     pooledNodes.Push(n);
 }