示例#1
0
    /// <summary>
    ///   Grabs data from the type to initialize this
    /// </summary>
    /// <remarks>
    ///   <para>
    ///     Doesn't initialize the graphics scene which needs to be set separately
    ///   </para>
    /// </remarks>
    public void Init(ChunkConfiguration chunkType, CompoundCloudSystem compoundClouds,
                     string modelPath)
    {
        this.compoundClouds = compoundClouds;

        // Grab data
        VentPerSecond = chunkType.VentAmount;
        Dissolves     = chunkType.Dissolves;
        Size          = chunkType.Size;
        Damages       = chunkType.Damages;
        DeleteOnTouch = chunkType.DeleteOnTouch;

        Mass = chunkType.Mass;

        // These are stored for saves to work
        Radius     = chunkType.Radius;
        ChunkScale = chunkType.ChunkScale;

        ModelNodePath = modelPath;

        // Copy compounds to vent
        if (chunkType.Compounds?.Count > 0)
        {
            // Capacity is set to 0 so that no compounds can be added
            // the normal way to the chunk
            ContainedCompounds = new CompoundBag(0);

            foreach (var entry in chunkType.Compounds)
            {
                ContainedCompounds.Compounds.Add(entry.Key, entry.Value.Amount);
            }
        }
    }
示例#2
0
    /// <summary>
    ///   Absorbs compounds from this cloud
    /// </summary>
    public void AbsorbCompounds(int localX, int localY, CompoundBag storage,
                                Dictionary <Compound, float> totals, float delta, float rate)
    {
        var fractionToTake = 1.0f - (float)Math.Pow(0.5f, delta / Constants.CLOUD_ABSORPTION_HALF_LIFE);

        for (int i = 0; i < Constants.CLOUDS_IN_ONE; i++)
        {
            if (Compounds[i] == null)
            {
                break;
            }

            // Skip if compound is non-useful
            if (!storage.IsUseful(Compounds[i]))
            {
                continue;
            }

            // Overestimate of how much compounds we get
            float generousAmount = HackyAddress(Density[localX, localY], i) *
                                   Constants.SKIP_TRYING_TO_ABSORB_RATIO;

            // Skip if there isn't enough to absorb
            if (generousAmount < MathUtils.EPSILON)
            {
                continue;
            }

            float freeSpace = storage.Capacity - storage.GetCompoundAmount(Compounds[i]);

            float multiplier = 1.0f * rate;

            if (freeSpace < generousAmount)
            {
                // Allow partial absorption to allow cells to take from high density clouds
                multiplier = freeSpace / generousAmount;
            }

            float taken = TakeCompound(Compounds[i], localX, localY, fractionToTake * multiplier) *
                          Constants.ABSORPTION_RATIO;

            storage.AddCompound(Compounds[i], taken);

            // Keep track of total compounds absorbed for the cell
            if (!totals.ContainsKey(Compounds[i]))
            {
                totals.Add(Compounds[i], taken);
            }
            else
            {
                totals[Compounds[i]] += taken;
            }
        }
    }
示例#3
0
    /// <summary>
    ///   Gives organelles more compounds to grow
    /// </summary>
    public void GrowOrganelle(CompoundBag compounds)
    {
        float totalTaken = 0;
        var   keys       = new List <string>(compoundsLeft.Keys);

        foreach (var key in keys)
        {
            var amountNeeded = compoundsLeft[key];

            if (amountNeeded <= 0.0f)
            {
                continue;
            }

            // Take compounds if the cell has what we need
            // ORGANELLE_GROW_STORAGE_MUST_HAVE_AT_LEAST controls how
            // much of a certain compound must exist before we take
            // some
            var amountAvailable = compounds.GetCompoundAmount(key)
                                  - Constants.ORGANELLE_GROW_STORAGE_MUST_HAVE_AT_LEAST;

            if (amountAvailable <= 0.0f)
            {
                continue;
            }

            // We can take some
            var amountToTake = Mathf.Min(amountNeeded, amountAvailable);

            var amount = compounds.TakeCompound(key, amountToTake);
            var left   = amountNeeded - amount;

            if (left < 0.0001)
            {
                left = 0;
            }

            compoundsLeft[key] = left;

            totalTaken += amount;
        }

        if (totalTaken > 0)
        {
            growthValueDirty = true;

            ApplyScale();
        }
    }
示例#4
0
    /// <summary>
    ///   Grabs data from the type to initialize this
    /// </summary>
    /// <remarks>
    ///   <para>
    ///     Doesn't initialize the graphics scene which needs to be set separately
    ///   </para>
    /// </remarks>
    public void Init(ChunkConfiguration chunkType, CompoundCloudSystem compoundClouds,
                     string modelPath)
    {
        this.compoundClouds = compoundClouds;

        // Grab data
        VentPerSecond = chunkType.VentAmount;
        Dissolves     = chunkType.Dissolves;
        Size          = chunkType.Size;
        Damages       = chunkType.Damages;
        DeleteOnTouch = chunkType.DeleteOnTouch;

        Mass = chunkType.Mass;

        // These are stored for saves to work
        Radius     = chunkType.Radius;
        ChunkScale = chunkType.ChunkScale;

        ModelNodePath = modelPath;

        // Apply physics shape
        var shape = GetNode <CollisionShape>("CollisionShape");

        // This only works as long as the sphere shape type is not changed in the editor
        ((SphereShape)shape.Shape).Radius = chunkType.Radius;

        // Copy compounds to vent
        if (chunkType.Compounds != null && chunkType.Compounds.Count > 0)
        {
            // Capacity is set to 0 so that no compounds can be added
            // the normal way to the chunk
            ContainedCompounds = new CompoundBag(0);

            foreach (var entry in chunkType.Compounds)
            {
                ContainedCompounds.Compounds.Add(entry.Key, entry.Value.Amount);
            }
        }

        // Needs physics callback when this is engulfable or damaging
        if (Damages > 0 || DeleteOnTouch || Size > 0)
        {
            ContactsReported = Constants.DEFAULT_STORE_CONTACTS_COUNT;
            Connect("body_shape_entered", this, "OnContactBegin");
            Connect("body_shape_exited", this, "OnContactEnd");
        }
    }
示例#5
0
    /// <summary>
    ///   Absorbs compounds from this cloud
    /// </summary>
    public void AbsorbCompounds(int localX, int localY, CompoundBag storage,
                                Dictionary <string, float> totals, float delta)
    {
        var fractionToTake = 1.0f - (float)Math.Pow(0.5f, delta / Constants.CLOUD_ABSORPTION_HALF_LIFE);

        foreach (var slot in slots)
        {
            // Overestimate of how much compounds we get
            float generousAmount = slot.Density[localX, localY] *
                                   Constants.SKIP_TRYING_TO_ABSORB_RATIO;

            // Skip if there isn't enough to absorb
            if (generousAmount < MathUtils.EPSILON)
            {
                continue;
            }

            var compound = slot.Compound.InternalName;

            float freeSpace = storage.Capacity - storage.GetCompoundAmount(compound);

            float multiplier = 1.0f;

            if (freeSpace < generousAmount)
            {
                // Allow partial absorption to allow cells to take from high density clouds
                multiplier = freeSpace / generousAmount;
            }

            float taken = slot.TakeCompound(localX, localY, fractionToTake * multiplier) *
                          Constants.ABSORPTION_RATIO;

            storage.AddCompound(compound, taken);

            // Keep track of total compounds absorbed for the cell
            if (!totals.ContainsKey(compound))
            {
                totals.Add(compound, taken);
            }
            else
            {
                totals[compound] += taken;
            }
        }
    }
示例#6
0
 public CompoundBagEventArgs(CompoundBag compounds)
 {
     Compounds = compounds;
 }
    private void RunProcess(float delta, BioProcess processData, CompoundBag bag, TweakedProcess process,
                            SingleProcessStatistics currentProcessStatistics, float inverseDelta)
    {
        // Can your cell do the process
        bool canDoProcess = true;

        // Loop through to make sure you can follow through with your
        // whole process so nothing gets wasted as that would be
        // frustrating.
        float environmentModifier = 1.0f;

        // First check the environmental compounds so that we can build the right environment modifier for accurate
        // check of normal compound input amounts
        foreach (var entry in processData.Inputs)
        {
            // Set used compounds to be useful, we dont want to purge
            // those
            bag.SetUseful(entry.Key);

            if (!entry.Key.IsEnvironmental)
            {
                continue;
            }

            var dissolved = GetDissolved(entry.Key);

            // currentProcessStatistics?.AddInputAmount(entry.Key, entry.Value * inverseDelta);
            currentProcessStatistics?.AddInputAmount(entry.Key, dissolved);

            // do environmental modifier here, and save it for later
            environmentModifier *= dissolved / entry.Value;

            if (environmentModifier <= MathUtils.EPSILON)
            {
                currentProcessStatistics?.AddLimitingFactor(entry.Key);
            }
        }

        if (environmentModifier <= MathUtils.EPSILON)
        {
            canDoProcess = false;
        }

        foreach (var entry in processData.Inputs)
        {
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            var inputRemoved = entry.Value * process.Rate * delta;

            currentProcessStatistics?.AddInputAmount(entry.Key, inputRemoved * inverseDelta);

            // currentProcessStatistics?.AddInputAmount(entry.Key, 0);

            // If not enough compound we can't do the process
            if (bag.GetCompoundAmount(entry.Key) < inputRemoved)
            {
                canDoProcess = false;
                currentProcessStatistics?.AddLimitingFactor(entry.Key);
            }
        }

        // Output
        // This is now always looped (even when we can't do the process)
        // because the is useful part is needs to be always be done
        foreach (var entry in processData.Outputs)
        {
            // For now lets assume compounds we produce are also
            // useful
            bag.SetUseful(entry.Key);

            // Apply the general modifiers and
            // apply the environmental modifier
            var outputAdded = entry.Value * process.Rate * delta * environmentModifier;

            currentProcessStatistics?.AddOutputAmount(entry.Key, outputAdded * inverseDelta);

            // currentProcessStatistics?.AddOutputAmount(entry.Key, 0);

            // If no space we can't do the process, and if environmental
            // right now this isn't released anywhere
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            if (bag.GetCompoundAmount(entry.Key) + outputAdded > bag.Capacity)
            {
                canDoProcess = false;
                currentProcessStatistics?.AddCapacityProblem(entry.Key);
            }
        }

        // Only carry out this process if you have all the required
        // ingredients and enough space for the outputs
        if (!canDoProcess)
        {
            return;
        }

        if (currentProcessStatistics != null)
        {
            currentProcessStatistics.CurrentSpeed = process.Rate * environmentModifier;
        }

        // Consume inputs
        foreach (var entry in processData.Inputs)
        {
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            var inputRemoved = entry.Value * process.Rate * delta *
                               environmentModifier;

            currentProcessStatistics?.AddInputAmount(entry.Key, inputRemoved * inverseDelta);

            // This should always succeed (due to the earlier check) so
            // it is always assumed here that the process succeeded
            bag.TakeCompound(entry.Key, inputRemoved);
        }

        // Add outputs
        foreach (var entry in processData.Outputs)
        {
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            var outputGenerated = entry.Value * process.Rate * delta *
                                  environmentModifier;

            currentProcessStatistics?.AddOutputAmount(entry.Key, outputGenerated * inverseDelta);

            bag.AddCompound(entry.Key, outputGenerated);
        }
    }
示例#8
0
    /// <summary>
    ///   Absorbs compounds from clouds into a bag
    /// </summary>
    /// <remarks>
    ///   <para>
    ///     TODO: finding a way to add threading here probably helps quite a bit
    ///   </para>
    /// </remarks>
    public void AbsorbCompounds(Vector3 position, float radius, CompoundBag storage,
                                Dictionary <Compound, float> totals, float delta, float rate)
    {
        // It might be fine to remove this check but this was in the old code
        if (radius < 1.0f)
        {
            GD.PrintErr("Grab radius < 1 is not allowed");
            return;
        }

        int resolution = Resolution;

        // This version is used when working with cloud local coordinates
        float localGrabRadius = radius / resolution;

        float localGrabRadiusSquared = Mathf.Pow(radius / resolution, 2);

        // Find clouds that are in range for absorbing
        foreach (var cloud in clouds)
        {
            // Skip clouds that are out of range
            if (!cloud.ContainsPositionWithRadius(position, radius))
            {
                continue;
            }

            int cloudRelativeX, cloudRelativeY;
            cloud.ConvertToCloudLocal(position, out cloudRelativeX, out cloudRelativeY);

            // Calculate all circle positions and grab from all the valid
            // positions

            // For simplicity all points within a bounding box around the
            // relative origin point is calculated and that is restricted by
            // checking if the point is within the circle before grabbing
            int xEnd = (int)Mathf.Round(cloudRelativeX + localGrabRadius);
            int yEnd = (int)Mathf.Round(cloudRelativeY + localGrabRadius);

            for (int x = (int)Mathf.Round(cloudRelativeX - localGrabRadius);
                 x <= xEnd;
                 x += 1)
            {
                for (int y = (int)Mathf.Round(cloudRelativeY - localGrabRadius);
                     y <= yEnd;
                     y += 1)
                {
                    // Negative coordinates are always outside the cloud area
                    if (x < 0 || y < 0)
                    {
                        continue;
                    }

                    // Circle check
                    if (Mathf.Pow(x - cloudRelativeX, 2) +
                        Mathf.Pow(y - cloudRelativeY, 2) >
                        localGrabRadiusSquared)
                    {
                        // Not in it
                        continue;
                    }

                    // Then just need to check that it is within the cloud simulation array
                    if (x < cloud.Size && y < cloud.Size)
                    {
                        // Absorb all compounds in the cloud
                        cloud.AbsorbCompounds(x, y, storage, totals, delta, rate);
                    }
                }
            }
        }
    }
示例#9
0
    private void RunProcess(float delta, BioProcess processData, CompoundBag bag, TweakedProcess process,
                            SingleProcessStatistics currentProcessStatistics, float inverseDelta)
    {
        // Can your cell do the process
        bool canDoProcess = true;

        float environmentModifier = 1.0f;

        // This modifies the process overall speed to allow really fast processes to run, for example if there are
        // a ton of one organelle it might consume 100 glucose per go, which might be unlikely for the cell to have
        // so if there is *some* but not enough space for results (and also inputs) this can run the process as
        // fraction of the speed to allow the cell to still function well
        float spaceConstraintModifier = 1.0f;

        // First check the environmental compounds so that we can build the right environment modifier for accurate
        // check of normal compound input amounts
        foreach (var entry in processData.Inputs)
        {
            // Set used compounds to be useful, we dont want to purge those
            bag.SetUseful(entry.Key);

            if (!entry.Key.IsEnvironmental)
            {
                continue;
            }

            var dissolved = GetDissolved(entry.Key);

            // currentProcessStatistics?.AddInputAmount(entry.Key, entry.Value * inverseDelta);
            currentProcessStatistics?.AddInputAmount(entry.Key, dissolved);

            // do environmental modifier here, and save it for later
            environmentModifier *= dissolved / entry.Value;

            if (environmentModifier <= MathUtils.EPSILON)
            {
                currentProcessStatistics?.AddLimitingFactor(entry.Key);
            }
        }

        if (environmentModifier <= MathUtils.EPSILON)
        {
            canDoProcess = false;
        }

        // Compute spaceConstraintModifier before updating the final use and input amounts
        foreach (var entry in processData.Inputs)
        {
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            var inputRemoved = entry.Value * process.Rate * environmentModifier;

            // currentProcessStatistics?.AddInputAmount(entry.Key, 0);
            // We don't multiply by delta here because we report the per-second values anyway. In the actual process
            // output numbers (computed after testing the speed), we need to multiply by inverse delta
            currentProcessStatistics?.AddInputAmount(entry.Key, inputRemoved);

            inputRemoved = inputRemoved * delta * spaceConstraintModifier;

            // If not enough we can't run the process unless we can lower spaceConstraintModifier enough
            var availableAmount = bag.GetCompoundAmount(entry.Key);
            if (availableAmount < inputRemoved)
            {
                bool canRun = false;

                if (availableAmount > MathUtils.EPSILON)
                {
                    var neededModifier = availableAmount / inputRemoved;

                    if (neededModifier > Constants.MINIMUM_RUNNABLE_PROCESS_FRACTION)
                    {
                        spaceConstraintModifier = neededModifier;
                        canRun = true;

                        // Due to rounding errors there can be very small disparity here between the amount available
                        // and what we will take with the modifiers. See the comment in outputs for more details
                    }
                }

                if (!canRun)
                {
                    canDoProcess = false;
                    currentProcessStatistics?.AddLimitingFactor(entry.Key);
                }
            }
        }

        foreach (var entry in processData.Outputs)
        {
            // For now lets assume compounds we produce are also useful
            bag.SetUseful(entry.Key);

            var outputAdded = entry.Value * process.Rate * environmentModifier;

            // currentProcessStatistics?.AddOutputAmount(entry.Key, 0);
            currentProcessStatistics?.AddOutputAmount(entry.Key, outputAdded);

            outputAdded = outputAdded * delta * spaceConstraintModifier;

            // if environmental right now this isn't released anywhere
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            // If no space we can't do the process, if we can't adjust the space constraint modifier enough
            var remainingSpace = bag.Capacity - bag.GetCompoundAmount(entry.Key);
            if (outputAdded > remainingSpace)
            {
                bool canRun = false;

                if (remainingSpace > MathUtils.EPSILON)
                {
                    var neededModifier = remainingSpace / outputAdded;

                    if (neededModifier > Constants.MINIMUM_RUNNABLE_PROCESS_FRACTION)
                    {
                        spaceConstraintModifier = neededModifier;
                        canRun = true;
                    }

                    // With all of the modifiers we can lose a tiny bit of compound that won't fit due to rounding
                    // errors, but we ignore that here
                }

                if (!canRun)
                {
                    canDoProcess = false;
                    currentProcessStatistics?.AddCapacityProblem(entry.Key);
                }
            }
        }

        // Only carry out this process if you have all the required ingredients and enough space for the outputs
        if (!canDoProcess)
        {
            if (currentProcessStatistics != null)
            {
                currentProcessStatistics.CurrentSpeed = 0;
            }
            return;
        }

        float totalModifier = process.Rate * delta * environmentModifier * spaceConstraintModifier;

        if (currentProcessStatistics != null)
        {
            currentProcessStatistics.CurrentSpeed = process.Rate * environmentModifier * spaceConstraintModifier;
        }

        // Consume inputs
        foreach (var entry in processData.Inputs)
        {
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            var inputRemoved = entry.Value * totalModifier;

            currentProcessStatistics?.AddInputAmount(entry.Key, inputRemoved * inverseDelta);

            // This should always succeed (due to the earlier check) so it is always assumed here that this succeeded
            bag.TakeCompound(entry.Key, inputRemoved);
        }

        // Add outputs
        foreach (var entry in processData.Outputs)
        {
            if (entry.Key.IsEnvironmental)
            {
                continue;
            }

            var outputGenerated = entry.Value * totalModifier;

            currentProcessStatistics?.AddOutputAmount(entry.Key, outputGenerated * inverseDelta);

            bag.AddCompound(entry.Key, outputGenerated);
        }
    }