/// <summary>
        /// <para>Calculates how much of the provided resource is gained or lost (on average) by executing this farm cycle once,
        /// given the provided effective drop rates and resource costs per cycle</para>
        /// <para>If the resource is also spent while executing a cycle, the calculation includes
        /// reducing the drop rate by the safety margin in the logical options.</para>
        /// </summary>
        /// <param name="model">A model that can be used to obtain data about the current game configuration.</param>
        /// <param name="resource">The resource to check</param>
        /// <param name="effectiveDropRates">The effective drop rates, after accounting for unneeded drops.</param>
        /// <param name="costingResources">A dictionary  of resources to their cost per cycle.</param>
        /// <returns></returns>
        private decimal CalculateResourceVariationPerCycle(SuperMetroidModel model, ConsumableResourceEnum resource,
                                                           EnemyDrops effectiveDropRates, IDictionary <ConsumableResourceEnum, int> costingResources)
        {
            // If this farm cycle has to spend some of this resource, do some adaptations:
            // - Reduce the expected drop rate by the logical safety margin
            // - Include the cost when looking at the amount obtained from drops
            decimal dropRateMultiplier;
            int     costPerCycle;

            if (costingResources.TryGetValue(resource, out costPerCycle))
            {
                dropRateMultiplier = (100 - model.LogicalOptions.SpawnerFarmingOptions.SafetyMarginPercent) / 100;
            }
            else
            {
                costPerCycle       = 0;
                dropRateMultiplier = 1M;
            }

            decimal netGainPerCycle = resource.GetRelatedDrops()
                                      .Select(drop => dropRateMultiplier * model.Rules.ConvertDropRateToPercent(effectiveDropRates.GetDropRate(drop))
                                              * FarmCycle.RoomEnemy.Quantity * model.Rules.GetDropResourceCount(drop))
                                      .Sum() - costPerCycle;

            return(netGainPerCycle);
        }
示例#2
0
        /// <summary>
        /// Reduces the provided quantity of the provided consumable resource.
        /// When reducing energy, regular energy is used up first (down to 1) then reserves are used.
        /// </summary>
        /// <param name="resource">The resource to reduce</param>
        /// <param name="quantity">The amount to reduce</param>
        public void ApplyAmountReduction(ConsumableResourceEnum resource, int quantity)
        {
            switch (resource)
            {
            case ConsumableResourceEnum.ENERGY:
                // Consume regular energy first, down to 1
                int regularEnergy          = GetAmount(RechargeableResourceEnum.RegularEnergy);
                int regularEnergyToConsume = regularEnergy > quantity ? quantity : regularEnergy - 1;
                Amounts[RechargeableResourceEnum.RegularEnergy] -= regularEnergyToConsume;
                quantity -= regularEnergyToConsume;
                if (quantity > 0)
                {
                    Amounts[RechargeableResourceEnum.ReserveEnergy] -= quantity;
                }
                break;

            case ConsumableResourceEnum.MISSILE:
                Amounts[RechargeableResourceEnum.Missile] -= quantity;
                break;

            case ConsumableResourceEnum.SUPER:
                Amounts[RechargeableResourceEnum.Super] -= quantity;
                break;

            case ConsumableResourceEnum.POWER_BOMB:
                Amounts[RechargeableResourceEnum.PowerBomb] -= quantity;
                break;
            }
        }
        /// <summary>
        /// <para>Calculates how much of the provided resource is gained or lost (on average) over 60 frames of farming,
        /// given the provided effective drop rates and resource costs per cycle</para>
        /// <para>If the resource is also spent while executing a cycle, the calculation includes
        /// reducing the drop rate by the safety margin in the logical options.</para>
        /// </summary>
        /// <param name="model">A model that can be used to obtain data about the current game configuration.</param>
        /// <param name="resource">The resource to check</param>
        /// <param name="effectiveDropRates">The effective drop rates, after accounting for unneeded drops.</param>
        /// <param name="costingResources">A dictionary  of resources to their cost per cycle.</param>
        /// <returns></returns>
        private decimal CalculateResourceVariationPerSecond(SuperMetroidModel model, ConsumableResourceEnum resource,
                                                            EnemyDrops effectiveDropRates, IDictionary <ConsumableResourceEnum, int> costingResources)
        {
            decimal variationPerCycle = CalculateResourceVariationPerCycle(model, resource, effectiveDropRates, costingResources);

            return(variationPerCycle / FarmCycle.CycleFrames * 60);
        }
        public bool IsResourceAvailable(SuperMetroidModel model, ConsumableResourceEnum resource, int quantity)
        {
            if (quantity == 0)
            {
                return(true);
            }

            // If resource tracking is enabled, look at current resource amounts
            if (model.LogicalOptions.ResourceTrackingEnabled)
            {
                // The other resources can be fully spent, but for energy we don't want to go below 1
                if (resource == ConsumableResourceEnum.ENERGY)
                {
                    return(GetCurrentAmount(resource) > quantity);
                }
                else
                {
                    return(GetCurrentAmount(resource) >= quantity);
                }
            }
            // If resource tracking is not enabled, use max resource amounts instead of current amounts
            else
            {
                // The other resources can be fully spent, but for energy we don't want to go below 1
                if (resource == ConsumableResourceEnum.ENERGY)
                {
                    return(GetMaxAmount(resource) > quantity);
                }
                else
                {
                    return(GetMaxAmount(resource) >= quantity);
                }
            }
        }
 /// <summary>
 /// Consumes the provided quantity of the provided consumable resource. When consuming energy, regular energy is used up first (down to 1)
 /// then reserves are used.
 /// </summary>
 /// <param name="model">A model that can be used to obtain data about the current game configuration.</param>
 /// <param name="resource">The resource to consume</param>
 /// <param name="quantity">The amount to consume</param>
 public void ApplyConsumeResource(SuperMetroidModel model, ConsumableResourceEnum resource, int quantity)
 {
     // Don't bother with current resource count if resource tracking is disabled
     if (model.LogicalOptions.ResourceTrackingEnabled)
     {
         Resources.ApplyAmountReduction(resource, quantity);
     }
 }
 /// <summary>
 /// Returns the enemy drops that this consumable resource can consume.
 /// </summary>
 /// <param name="resource">This resource</param>
 /// <returns>The enemy drops that this can consume</returns>
 public static IEnumerable <EnemyDropEnum> GetRelatedDrops(this ConsumableResourceEnum resource)
 {
     return(resource switch
     {
         ConsumableResourceEnum.ENERGY => new[] { EnemyDropEnum.SMALL_ENERGY, EnemyDropEnum.BIG_ENERGY },
         ConsumableResourceEnum.MISSILE => new[] { EnemyDropEnum.MISSILE },
         ConsumableResourceEnum.SUPER => new[] { EnemyDropEnum.SUPER },
         ConsumableResourceEnum.POWER_BOMB => new[] { EnemyDropEnum.POWER_BOMB },
         _ => throw new Exception($"Unrecognized consumable resource {resource}")
     });
 /// <summary>
 /// Sets current value for the provided consumable resource to the current maximum.
 /// This is almost the same as refilling a rechargeable resource,
 /// except both types of energy are grouped together.
 /// </summary>
 /// <param name="model">A model that can be used to obtain data about the current game configuration.</param>
 /// <param name="resource">The resource to refill</param>
 public void ApplyRefillResource(SuperMetroidModel model, ConsumableResourceEnum resource)
 {
     // Don't bother with current resource count if resource tracking is disabled
     if (model.LogicalOptions.ResourceTrackingEnabled)
     {
         foreach (RechargeableResourceEnum rechargeableResource in resource.ToRechargeableResources())
         {
             ApplyRefillResource(model, rechargeableResource);
         }
     }
 }
示例#8
0
        /// <summary>
        /// Returns whether the amount in this container for the provided resource could be spent without dying.
        /// </summary>
        /// <param name="resource">The resource to check the ability to spend for</param>
        /// <param name="quantity">The amount to check the ability to spend for</param>
        /// <returns></returns>
        public bool IsResourceAvailable(ConsumableResourceEnum resource, int quantity)
        {
            if (quantity == 0)
            {
                return(true);
            }

            // The other resources can be fully spent, but for energy we don't want to go below 1
            if (resource == ConsumableResourceEnum.ENERGY)
            {
                return(GetAmount(resource) > quantity);
            }
            else
            {
                return(GetAmount(resource) >= quantity);
            }
        }
示例#9
0
 /// <summary>
 /// Returns the amount associated to this container for the provided consumable resource.
 /// This is almost the same as getting the current amount of a rechargeable resource,
 /// except both types of energy are grouped together.
 /// </summary>
 /// <param name="resource">Resource to get the amount of.</param>
 /// <returns></returns>
 public int GetAmount(ConsumableResourceEnum resource)
 {
     return(resource.ToRechargeableResources().Select(resource => GetAmount(resource)).Sum());
 }