/// <summary> /// Resets the compounds to be the ones this species spawns with. Called by spawn helpers /// </summary> public void SetInitialCompounds() { Compounds.ClearCompounds(); foreach (var entry in Species.InitialCompounds) { Compounds.AddCompound(entry.Key, entry.Value); } }
/// <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; } } }
/// <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; } } }
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); } }
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); } }