201 lines
8.2 KiB
C#
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
|
|
);
|