/// <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); }
/// <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); } } }
/// <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); } }
/// <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()); }