505 lines
20 KiB
C#
505 lines
20 KiB
C#
using OrekiWoofsBeehives.BlockEntities;
|
|
using OrekiWoofsBeehives.Behaviors;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using Vintagestory.API.Config;
|
|
|
|
namespace OrekiWoofsBeehives.Helpers;
|
|
|
|
public static class BeehiveInfoStringBuilder
|
|
{
|
|
public static void BuildBeehiveInfo(
|
|
StringBuilder builder,
|
|
BeehiveStats stats,
|
|
BlockEntityReusableBeehive beehive)
|
|
{
|
|
var verbosity = Config.Instance.GetEffectiveInformationVerbosity();
|
|
|
|
if (verbosity == 0)
|
|
return;
|
|
|
|
if (beehive.Api?.World != null && beehive.Api.World.Calendar.TotalDays < beehive.NextSwarmAllowedTotalDays)
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-recently-swarmed", Config.Instance.SwarmCooldownDays));
|
|
|
|
if (beehive.IsSwarmBuildingNearby())
|
|
builder.AppendLine($"<font color=\"#00bb00\">{Lang.Get("orekiwoofsbeehives:beehive-info-source-swarm-forming")}</font>");
|
|
else if (!beehive.IsReadyToStartSwarm() && beehive.IsReadyToStartSwarm(ignoreDayTime: true))
|
|
builder.AppendLine($"<font color=\"#00bb00\">{Lang.Get("orekiwoofsbeehives:beehive-info-ready-waiting-morning")}</font>");
|
|
|
|
if (!beehive.SwarmsDisabled && !beehive.IsSwarmBuildingNearby()
|
|
&& beehive.PreSwarmProgress > 0 && !beehive.IsReadyToStartSwarm(ignoreDayTime: true))
|
|
AppendPreSwarmProgress(builder, beehive, stats, verbosity);
|
|
|
|
if (verbosity >= 4)
|
|
AppendScoutingProgress(builder, beehive);
|
|
|
|
if (beehive.IsReceivingIncomingSwarm)
|
|
builder.AppendLine($"<font color=\"#00bb00\">{Lang.Get("orekiwoofsbeehives:beehive-info-incoming-swarm")}</font>");
|
|
|
|
if (!beehive.IsReceivingIncomingSwarm && beehive.BeePopulation < Config.Instance.BeehiveConsideredEmptyBelowPopulation)
|
|
{
|
|
builder.AppendLine($"<font color=\"#ffff00\">{Lang.GetWithFallback("orekiwoofsbeehives:beehive-info-no-bees-use-skep-or-wait-for-swarm", "orekiwoofsbeehives:beehive-info-no-bees")}</font>");
|
|
return;
|
|
}
|
|
|
|
if (beehive.IsReadyToStartSwarm())
|
|
builder.AppendLine($"<font color=\"#ffaa00\">{Lang.Get("orekiwoofsbeehives:beehive-info-ready-to-swarm")}</font>");
|
|
|
|
AppendPopulationInfo(builder, stats, beehive, verbosity);
|
|
|
|
if (verbosity >= 1)
|
|
AppendFlowerInfo(builder, stats, beehive, verbosity);
|
|
|
|
if (verbosity >= 3)
|
|
AppendFrameInfo(builder, stats);
|
|
|
|
AppendHoneyProgress(builder, stats, beehive, verbosity);
|
|
|
|
if (verbosity >= 3)
|
|
AppendFeedInfo(builder, beehive, stats);
|
|
|
|
AppendFrameStatusMessages(builder, stats, beehive, verbosity);
|
|
|
|
AppendPopulationChange(builder, stats, verbosity);
|
|
|
|
AppendCropBoostEffectiveness(builder, beehive, verbosity);
|
|
|
|
AppendTemperatureInfo(builder, stats, verbosity);
|
|
|
|
if (verbosity >= 3)
|
|
AppendHoneyProduction(builder, stats);
|
|
|
|
if (Config.Instance.EnableSwarms && beehive.SwarmsDisabled)
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-swarms-disabled"));
|
|
}
|
|
|
|
private static void AppendScoutingProgress(StringBuilder builder, BlockEntityReusableBeehive beehive)
|
|
{
|
|
var scoutingProgressString = Lang.Get("orekiwoofsbeehives:beehiveScoutingProgress");
|
|
var scanningProgress = beehive.GetScanningProgress();
|
|
var rescanningProgress = beehive.GetRescanningProgress();
|
|
|
|
builder.AppendLine($"{scoutingProgressString}: {scanningProgress * 100:F1}% (+ {rescanningProgress * 100:F1}%)");
|
|
}
|
|
|
|
private static void AppendPreSwarmProgress(
|
|
StringBuilder builder,
|
|
BlockEntityReusableBeehive beehive,
|
|
BeehiveStats stats,
|
|
int verbosity)
|
|
{
|
|
if (verbosity == 1)
|
|
{
|
|
builder.AppendLine($"<font color=\"#aaffaa\">{Lang.Get("orekiwoofsbeehives:beehive-info-pre-swarm-building")}</font>");
|
|
return;
|
|
}
|
|
|
|
var cfg = Config.Instance;
|
|
var isIncreasing = stats.Components.Temperature >= cfg.TemperatureOptimal;
|
|
|
|
string detail;
|
|
if (isIncreasing)
|
|
{
|
|
var hoursLeft = (1.0 - beehive.PreSwarmProgress) * cfg.PreSwarmDurationHours;
|
|
var hoursPerDay = beehive.Api?.World?.Calendar?.HoursPerDay ?? 24.0;
|
|
|
|
if (hoursLeft < 1)
|
|
{
|
|
detail = Lang.Get("orekiwoofsbeehives:beehive-info-pre-swarm-time-lessthanhourleft");
|
|
}
|
|
else if (hoursLeft < hoursPerDay)
|
|
{
|
|
detail = Lang.Get("orekiwoofsbeehives:beehive-info-pre-swarm-time-hours", $"{hoursLeft:F0}");
|
|
}
|
|
else
|
|
{
|
|
var daysLeft = (int)Math.Round(hoursLeft / hoursPerDay);
|
|
detail = daysLeft == 1
|
|
? Lang.Get("orekiwoofsbeehives:beehive-info-pre-swarm-time-1day")
|
|
: Lang.Get("orekiwoofsbeehives:beehive-info-pre-swarm-time-days", daysLeft);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
detail = Lang.Get("orekiwoofsbeehives:beehive-info-pre-swarm-paused");
|
|
}
|
|
|
|
builder.AppendLine($"<font color=\"#aaffaa\">{Lang.Get("orekiwoofsbeehives:beehive-info-pre-swarm-progress", detail)}</font>");
|
|
}
|
|
|
|
private static void AppendPopulationInfo(
|
|
StringBuilder builder,
|
|
BeehiveStats stats,
|
|
BlockEntityReusableBeehive beehive,
|
|
int verbosity)
|
|
{
|
|
var cfg = Config.Instance;
|
|
|
|
if (verbosity == 1)
|
|
{
|
|
var percentage = beehive.BeePopulation / cfg.MaxBeePopulation;
|
|
|
|
string levelKey = percentage switch
|
|
{
|
|
(<= 0) => "orekiwoofsbeehives:beehive-info-population-none",
|
|
(<= 0.25) => "orekiwoofsbeehives:beehive-info-population-low",
|
|
(<= 0.60) => "orekiwoofsbeehives:beehive-info-population-medium",
|
|
(<= 0.80) => "orekiwoofsbeehives:beehive-info-population-high",
|
|
_ => "orekiwoofsbeehives:beehive-info-population-veryhigh",
|
|
};
|
|
|
|
builder.AppendLine(Lang.Get(levelKey));
|
|
}
|
|
else if (verbosity == 2)
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-population-v2", new Dictionary<string, string>
|
|
{
|
|
["beePopulation"] = $"{beehive.BeePopulation:N0}",
|
|
["maxBeePopulation"] = $"{cfg.MaxBeePopulation:N0}",
|
|
["beeProductionMultiplier"] = $"{stats.BeeProductionMultiplier:F1}"
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-population", new Dictionary<string, string>
|
|
{
|
|
["beePopulation"] = $"{beehive.BeePopulation:N0}",
|
|
["maxBeePopulation"] = $"{cfg.MaxBeePopulation:N0}",
|
|
["beeProductionMultiplier"] = $"{stats.BeeProductionMultiplier:F1}"
|
|
}));
|
|
}
|
|
}
|
|
|
|
private static void AppendFlowerInfo(
|
|
StringBuilder builder,
|
|
BeehiveStats stats,
|
|
BlockEntityReusableBeehive beehive,
|
|
int verbosity)
|
|
{
|
|
var effectiveFlowers = stats.Components.EffectiveFlowers;
|
|
|
|
if (verbosity < 3)
|
|
{
|
|
var scanningProgress = beehive.GetScanningProgress();
|
|
var scoutingSuffix = (!beehive.WasFullyScanned && scanningProgress < 1f)
|
|
? " (" + Lang.Get("orekiwoofsbeehives:beehive-info-flowers-scanning-suffix") + $" - {scanningProgress * 100:F0}%)"
|
|
: string.Empty;
|
|
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-flowers-simple", new Dictionary<string, string>
|
|
{
|
|
["flowersAround"] = $"{beehive.FlowersAround}",
|
|
["cropsAround"] = $"{beehive.CropsAround}"
|
|
}) + scoutingSuffix);
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-flowers", new Dictionary<string, string>
|
|
{
|
|
["flowersAround"] = $"{beehive.FlowersAround}",
|
|
["cropsAround"] = $"{beehive.CropsAround}",
|
|
["effectiveFlowers"] = $"{effectiveFlowers:F1}",
|
|
["flowerProductionMultiplier"] = $"{stats.FlowerProductionMultiplier:F1}"
|
|
}));
|
|
}
|
|
|
|
if (beehive.WasFullyScanned && beehive.FlowersAround.HasValue)
|
|
{
|
|
var cfg = Config.Instance;
|
|
int flowersAround = beehive.FlowersAround.Value;
|
|
|
|
if (flowersAround < cfg.FlowerThreshold)
|
|
{
|
|
int flowersNeeded = cfg.FlowerThreshold - flowersAround;
|
|
builder.AppendLine($"<font color=\"#ff0000\">{FormatLang("orekiwoofsbeehives:beehive-info-flowers-warning-causing-deaths", new Dictionary<string, string>
|
|
{
|
|
["flowersNeeded"] = $"{flowersNeeded}"
|
|
})}</font>");
|
|
}
|
|
else if (effectiveFlowers < cfg.MaxFlowersForHoneyProduction)
|
|
{
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-flowers-warning-suboptimal"));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void AppendFrameInfo(
|
|
StringBuilder builder,
|
|
BeehiveStats stats)
|
|
{
|
|
var cfg = Config.Instance;
|
|
var filledFrames = stats.Components.FilledFramesCount;
|
|
var emptyFrames = stats.Components.EmptyFrames;
|
|
var totalFrames = stats.Components.TotalFrames;
|
|
|
|
if (filledFrames > 0 && emptyFrames > 0)
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-frames-mixed", new Dictionary<string, string>
|
|
{
|
|
["filledFrames"] = $"{filledFrames}",
|
|
["filledFrameBonus"] = $"{filledFrames * cfg.BonusGrowthPerFilledFrame:N0}",
|
|
["emptyFrames"] = $"{emptyFrames}"
|
|
}));
|
|
}
|
|
else if (filledFrames > 0)
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-frames-filled", new Dictionary<string, string>
|
|
{
|
|
["filledFrames"] = $"{filledFrames}",
|
|
["filledFrameBonus"] = $"{filledFrames * cfg.BonusGrowthPerFilledFrame:N0}"
|
|
}));
|
|
}
|
|
else if (emptyFrames > 0)
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-frames-empty", new Dictionary<string, string>
|
|
{
|
|
["emptyFrames"] = $"{emptyFrames}"
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-frames-total", new Dictionary<string, string>
|
|
{
|
|
["totalFrames"] = $"{totalFrames}"
|
|
}));
|
|
}
|
|
}
|
|
|
|
private static void AppendHoneyProgress(
|
|
StringBuilder builder,
|
|
BeehiveStats stats,
|
|
BlockEntityReusableBeehive beehive,
|
|
int verbosity)
|
|
{
|
|
var emptyFrameSlot = beehive.GetFirstEmptyFrameSlot();
|
|
var totalFrames = stats.Components.TotalFrames;
|
|
|
|
if (verbosity == 1)
|
|
{
|
|
if (emptyFrameSlot >= 0)
|
|
{
|
|
if (stats.FramesPerDay <= 0)
|
|
{
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-progress-v1-noproduction"));
|
|
}
|
|
else
|
|
{
|
|
var daysToFill = (1.0 - beehive.HoneyProgress) / stats.FramesPerDay;
|
|
if (daysToFill < 1.0)
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-progress-v1-lessthanday"));
|
|
else
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-progress-v1-days", new Dictionary<string, string>
|
|
{
|
|
["days"] = $"{Math.Ceiling(daysToFill):F0}"
|
|
}));
|
|
}
|
|
}
|
|
else if (totalFrames == 0)
|
|
{
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-no-frames"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (emptyFrameSlot >= 0)
|
|
{
|
|
if (stats.FramesPerDay > 0)
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-progress-filling", new Dictionary<string, string>
|
|
{
|
|
["honeyProgress"] = $"{beehive.HoneyProgress * 100:F1}",
|
|
["daysToFill"] = $"{(1.0 - beehive.HoneyProgress) / stats.FramesPerDay:F1}"
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-progress-noproduction", new Dictionary<string, string>
|
|
{
|
|
["honeyProgress"] = $"{beehive.HoneyProgress * 100:F1}"
|
|
}));
|
|
}
|
|
}
|
|
else if (totalFrames > 0 && verbosity < 3)
|
|
builder.AppendLine($"<font color=\"#00bb00\">{Lang.Get("orekiwoofsbeehives:beehive-info-all-filled")}</font>");
|
|
else if (totalFrames == 0 && verbosity < 3)
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-no-frames"));
|
|
}
|
|
}
|
|
|
|
private static void AppendTemperatureInfo(
|
|
StringBuilder builder,
|
|
BeehiveStats stats,
|
|
int verbosity)
|
|
{
|
|
var temperature = stats.Components.Temperature;
|
|
var temperatureMultiplier = stats.Components.TemperatureMultiplier;
|
|
var cfg = Config.Instance;
|
|
|
|
if (verbosity is >= 1 and < 3)
|
|
{
|
|
if (temperature <= cfg.TemperatureMinimum)
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-temperature-overwintering"));
|
|
else if (temperature < cfg.TemperatureOptimal)
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-temperature-cold"));
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-temperature", new Dictionary<string, string>
|
|
{
|
|
["temperature"] = $"{temperature:F1}",
|
|
["multiplier"] = $"{temperatureMultiplier:F2}"
|
|
}));
|
|
}
|
|
|
|
if (stats.Components.IsGreenhouse && Config.Instance.GreenhouseAffectsBeehive)
|
|
builder.AppendLine(Lang.Get("game:greenhousetempbonus"));
|
|
|
|
if (cfg.WinterHardMode
|
|
&& stats.Components.FilledFramesCount <= 0
|
|
&& temperature <= cfg.TemperatureMinimum)
|
|
builder.AppendLine($"<font color=\"#ff0000\">{Lang.Get("orekiwoofsbeehives:beehive-info-winter-starving")}</font>");
|
|
}
|
|
|
|
private static void AppendFrameStatusMessages(
|
|
StringBuilder builder,
|
|
BeehiveStats stats,
|
|
BlockEntityReusableBeehive beehive,
|
|
int verbosity)
|
|
{
|
|
if (verbosity >= 3)
|
|
return;
|
|
|
|
var cfg = Config.Instance;
|
|
var percentage = beehive.BeePopulation / cfg.MaxBeePopulation;
|
|
var filledFrames = stats.Components.FilledFramesCount;
|
|
var emptyFrames = stats.Components.EmptyFrames;
|
|
|
|
if (filledFrames > 0 && percentage > 0 && percentage <= 0.80)
|
|
{
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-frames-boosting"));
|
|
}
|
|
|
|
if (emptyFrames > 0 && percentage > 0 && percentage <= 0.60)
|
|
{
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:beehive-info-frames-sacrificing"));
|
|
}
|
|
}
|
|
|
|
private static void AppendPopulationChange(StringBuilder builder, BeehiveStats stats, int verbosity)
|
|
{
|
|
if (verbosity == 1)
|
|
{
|
|
var netChange = stats.DailyNetPopulationChange;
|
|
var statusKey =
|
|
netChange < 0 ? "orekiwoofsbeehives:beehive-info-population-status-decreasing"
|
|
: netChange <= 100 ? "orekiwoofsbeehives:beehive-info-population-status-stagnant"
|
|
: netChange <= 300 ? "orekiwoofsbeehives:beehive-info-population-status-slowgrowth"
|
|
: "orekiwoofsbeehives:beehive-info-population-status-growing";
|
|
builder.AppendLine(Lang.Get(statusKey));
|
|
}
|
|
else
|
|
{
|
|
var changeSign = stats.DailyNetPopulationChange >= 0 ? "+" : "";
|
|
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-population-change", new Dictionary<string, string>
|
|
{
|
|
["changeSign"] = changeSign,
|
|
["netChange"] = $"{stats.DailyNetPopulationChange:N0}",
|
|
["dailyGrowth"] = $"{stats.Components.DailyGrowth:N0}",
|
|
["dailyDeaths"] = $"{stats.Components.DailyDeaths:N0}"
|
|
}));
|
|
}
|
|
}
|
|
|
|
private static void AppendCropBoostEffectiveness(StringBuilder builder, BlockEntityReusableBeehive beehive, int verbosity)
|
|
{
|
|
var cfg = Config.Instance;
|
|
if (!cfg.YieldBoost && !cfg.SpeedBoost)
|
|
return;
|
|
|
|
float populationScale = BlockBehaviorBeehiveAffected.GetPopulationBoostScale(beehive.BeePopulation, cfg);
|
|
if (populationScale <= 0f)
|
|
{
|
|
builder.AppendLine($"<font color=\"#ffff00\">{Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-population-too-low")}</font>");
|
|
return;
|
|
}
|
|
|
|
if (verbosity == 1)
|
|
{
|
|
var levelText = populationScale < 0.34f
|
|
? Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-level-low")
|
|
: populationScale < 0.67f
|
|
? Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-level-medium")
|
|
: Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-level-high");
|
|
builder.AppendLine(Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-effectiveness", levelText));
|
|
return;
|
|
}
|
|
|
|
var resultingBoostParts = new List<string>();
|
|
|
|
if (cfg.YieldBoost)
|
|
{
|
|
var yieldBonusFactor = Math.Max(0f, cfg.YieldMultiplier - 1f);
|
|
var yieldPercent = (yieldBonusFactor * populationScale * 100f).ToString("N0");
|
|
resultingBoostParts.Add(Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-result-yield", yieldPercent));
|
|
}
|
|
|
|
if (cfg.SpeedBoost)
|
|
{
|
|
var speedPercent = (cfg.GrowthSpeedBonus * populationScale * 100f).ToString("N0");
|
|
resultingBoostParts.Add(Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-result-speed", speedPercent));
|
|
}
|
|
|
|
var effectivenessPercent = (populationScale * 100f).ToString("N0");
|
|
var cropBoostLine = Lang.Get("orekiwoofsbeehives:blockinfo-crop-boost-effectiveness", effectivenessPercent + "%");
|
|
|
|
if (resultingBoostParts.Count > 0)
|
|
cropBoostLine += " (" + string.Join(", ", resultingBoostParts) + ")";
|
|
|
|
builder.AppendLine(cropBoostLine);
|
|
}
|
|
|
|
private static void AppendHoneyProduction(StringBuilder builder, BeehiveStats stats)
|
|
{
|
|
if (stats.FramesPerDay > 0)
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-honey-production", new Dictionary<string, string>
|
|
{
|
|
["framesPerDay"] = $"{stats.FramesPerDay:F2}",
|
|
["daysPerFrame"] = $"{1.0 / stats.FramesPerDay:F1}"
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-honey-production-simple", new Dictionary<string, string>
|
|
{
|
|
["framesPerDay"] = $"{stats.FramesPerDay:F2}"
|
|
}));
|
|
}
|
|
}
|
|
|
|
private static void AppendFeedInfo(StringBuilder builder, BlockEntityReusableBeehive beehive, BeehiveStats stats)
|
|
{
|
|
if (!beehive.TryGetCurrentFeedStatus(out double remaining))
|
|
return;
|
|
|
|
var daysPerFrame = stats.FeedConsumedPerDay > 0
|
|
? 1.0 / stats.FeedConsumedPerDay
|
|
: 0.0;
|
|
|
|
builder.AppendLine(FormatLang("orekiwoofsbeehives:beehive-info-feed-current", new Dictionary<string, string>
|
|
{
|
|
["feedRemainingPercent"] = $"{remaining * 100:F0}",
|
|
["daysPerFrame"] = $"{daysPerFrame:F1}"
|
|
}));
|
|
}
|
|
|
|
private static string FormatLang(string langKey, Dictionary<string, string> values)
|
|
{
|
|
var result = Lang.Get(langKey);
|
|
foreach (var kvp in values)
|
|
result = result.Replace($">>>{kvp.Key}<<<", kvp.Value);
|
|
return result;
|
|
}
|
|
}
|