Private
Public Access
1
0
Files
OrekiWoofsBeehives/OrekiWoofsBeehives/BlockEntities/BeehiveStats.cs

201 lines
8.2 KiB
C#

using System;
using Vintagestory.API.Common;
namespace OrekiWoofsBeehives.BlockEntities;
public readonly record struct BeehiveStats
{
public required int DailyNetPopulationChange { get; init; }
public required double FramesPerDay { get; init; }
public required double FeedConsumedPerDay { get; init; }
public required double FlowerProductionMultiplier { get; init; }
public required double BeeProductionMultiplier { get; init; }
public required BeehiveStatsComponents Components { get; init; }
public static BeehiveStats Create(BlockEntityReusableBeehive beehive, bool isGreenhouse, double? totalDaysDate = null)
{
ClimateCondition climate = totalDaysDate.HasValue
? beehive.Api.World.BlockAccessor.GetClimateAt(beehive.Pos, EnumGetClimateMode.ForSuppliedDateValues, totalDaysDate.Value)
: beehive.Api.World.BlockAccessor.GetClimateAt(beehive.Pos, EnumGetClimateMode.NowValues);
var cfg = Config.Instance;
float currentTemperature = GetTemperature(climate, isGreenhouse && cfg.GreenhouseAffectsBeehive);
int dailyDeaths = CalculateDailyDeaths(beehive, currentTemperature);
double dailyGrowthRate = CalculateDailyGrowthRate(beehive);
int filledFramesCount = beehive.CountFilledFrames();
double emptyFrameMultiplier = GetEmptyFrameMultiplier(beehive.CountEmptyFrames());
int dailyFilledFrameBonus = (int)(filledFramesCount * cfg.BonusGrowthPerFilledFrame * emptyFrameMultiplier);
int dailyPercentageGrowth = (int)(beehive.BeePopulation * dailyGrowthRate);
double temperatureMultiplier = GetTemperatureMultiplier(currentTemperature);
int dailyGrowth = (int)((dailyPercentageGrowth + dailyFilledFrameBonus) * temperatureMultiplier);
double framesPerDay = CalculateFramesPerDay(beehive, temperatureMultiplier);
double feedConsumedPerDay = CalculateFeedConsumptionPerDay(beehive, currentTemperature, temperatureMultiplier);
int emptyFrames = beehive.CountEmptyFrames();
int totalFrames = beehive.CountTotalFrames();
double beeProductionMultiplier = beehive.BeePopulation / cfg.ReferenceBees;
double effectiveFlowers = GetEffectiveFlowers(beehive);
double flowerProductionMultiplier = effectiveFlowers / cfg.ReferenceFlowers;
int dailyNetPopulationChange = dailyGrowth - dailyDeaths;
return new BeehiveStats
{
DailyNetPopulationChange = dailyNetPopulationChange,
BeeProductionMultiplier = beeProductionMultiplier,
FlowerProductionMultiplier = flowerProductionMultiplier,
FramesPerDay = framesPerDay,
FeedConsumedPerDay = feedConsumedPerDay,
Components = new(
dailyDeaths,
dailyGrowthRate,
dailyPercentageGrowth,
filledFramesCount,
emptyFrames,
totalFrames,
dailyFilledFrameBonus,
dailyGrowth,
temperatureMultiplier,
effectiveFlowers,
isGreenhouse,
currentTemperature
)
};
}
private static int CalculateDailyDeaths(BlockEntityReusableBeehive beehive, float currentTemperature)
{
var cfg = Config.Instance;
var missingFlowers = Math.Max(0, cfg.FlowerThreshold - GetEffectiveFlowers(beehive));
var winterReverseRamp = GetWinterReverseRamp(currentTemperature);
var winterRamp = 1 - winterReverseRamp;
int baseDeaths = (int)(cfg.BaseDeathsPerDay * winterRamp);
int flowerDeaths = (int)(missingFlowers * cfg.DeathPerMissingFlower * winterRamp);
int dailyDeaths = baseDeaths + flowerDeaths;
bool hasFilledOrFeedFrames = beehive.CountFilledFrames() > 0;
if (cfg.WinterHardMode && !hasFilledOrFeedFrames && winterReverseRamp > 0)
dailyDeaths += (int)(cfg.WinterDailyBeeDeathsWithoutFood * winterReverseRamp);
return dailyDeaths;
}
private static double CalculateDailyGrowthRate(BlockEntityReusableBeehive beehive)
{
var cfg = Config.Instance;
double baseGrowthRate = Math.Pow(2, 1.0 / cfg.DoublingTimeDays) - 1;
double emptyFrameMultiplier = GetEmptyFrameMultiplier(beehive.CountEmptyFrames());
double growthModifier = emptyFrameMultiplier;
return baseGrowthRate * growthModifier;
}
private static double GetEmptyFrameMultiplier(int emptyFrames)
{
var hasEmptyFrames = emptyFrames > 0;
return hasEmptyFrames ? 0.5 : 1.0;
}
private static float GetTemperature(ClimateCondition climate, bool isGreenhouse)
{
float currentTemperature = climate?.Temperature ?? 20f;
if (!isGreenhouse)
return currentTemperature;
currentTemperature += 5;
if (OrekiWoofsBeehivesModSystem.IsSteadyGreenhousesLoaded)
return Math.Max(currentTemperature, Config.Instance.TemperatureOptimal);
return currentTemperature;
}
private static double GetTemperatureMultiplier(float currentTemperature)
{
var cfg = Config.Instance;
if (currentTemperature >= cfg.TemperatureOptimal)
return 1.0;
if (currentTemperature <= cfg.TemperatureMinimum)
return 0.0;
float temperatureRange = cfg.TemperatureOptimal - cfg.TemperatureMinimum;
return (currentTemperature - cfg.TemperatureMinimum) / temperatureRange;
}
private static double CalculateFramesPerDay(BlockEntityReusableBeehive beehive, double temperatureMultiplier)
{
if (beehive.BeePopulation <= 0)
return 0;
double effectiveFlowers = GetEffectiveFlowers(beehive);
if (effectiveFlowers <= 0)
return 0;
var cfg = Config.Instance;
double flowerFactor = effectiveFlowers / cfg.ReferenceFlowers;
double beeFactor = beehive.BeePopulation / cfg.ReferenceBees;
return flowerFactor * beeFactor * temperatureMultiplier;
}
private static double CalculateFeedConsumptionPerDay(BlockEntityReusableBeehive beehive, float currentTemperature, double temperatureMultiplier)
{
if (beehive.BeePopulation <= 0)
return 0;
var cfg = Config.Instance;
double beeFactor = beehive.BeePopulation / cfg.ReferenceBees;
double feed_consumption_speed = cfg.MaxFlowersForHoneyProduction / cfg.ReferenceFlowers * 1.5;
double feedTemperatureMultiplier = temperatureMultiplier;
if (cfg.WinterHardMode)
{
double winterReverseRamp = GetWinterReverseRamp(currentTemperature);
double winterFeedFloor = winterReverseRamp * cfg.WinterFoodConsumptionMultiplier;
feedTemperatureMultiplier = Math.Max(feedTemperatureMultiplier, winterFeedFloor);
}
return beeFactor * feed_consumption_speed * feedTemperatureMultiplier;
}
private static double GetWinterReverseRamp(float currentTemperature)
{
var cfg = Config.Instance;
if (currentTemperature >= cfg.TemperatureOptimal)
return 0;
if (currentTemperature <= cfg.TemperatureMinimum)
return 1;
float temperatureRange = cfg.TemperatureOptimal - cfg.TemperatureMinimum;
if (temperatureRange <= 0)
return 1;
return 1.0 - ((currentTemperature - cfg.TemperatureMinimum) / temperatureRange);
}
private static double GetEffectiveFlowers(BlockEntityReusableBeehive beehive)
{
const double cropMultiplier = 0.25;
var cfg = Config.Instance;
if (beehive.FlowersAround is null || beehive.CropsAround is null)
return cfg.ReferenceFlowers;
double effectiveFlowers = beehive.FlowersAround.Value + (beehive.CropsAround.Value * cropMultiplier);
return Math.Min(effectiveFlowers, cfg.MaxFlowersForHoneyProduction);
}
}
public readonly record struct BeehiveStatsComponents(
int DailyDeaths,
double DailyGrowthRate,
int DailyPercentageGrowth,
int FilledFramesCount,
int EmptyFrames,
int TotalFrames,
int DailyFilledFrameBonus,
int DailyGrowth,
double TemperatureMultiplier,
double EffectiveFlowers,
bool IsGreenhouse,
float Temperature
);