Skip to content

Commit

Permalink
Add minSubjectsToComplete attribute to CollectScience parameter. (#2070)
Browse files Browse the repository at this point in the history
Now also updates the widget with biome completion percentages.
  • Loading branch information
siimav authored Jun 29, 2023
1 parent 29ffe5f commit f570511
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 27 deletions.
170 changes: 145 additions & 25 deletions Source/CC_RP0/RP1CollectScience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
using ContractConfigurator.Util;
using Contracts;
using KSP.Localization;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UniLinq;

namespace ContractConfigurator.RP0
{
Expand All @@ -14,11 +16,17 @@ public class RP1CollectScience : ContractConfiguratorParameter
protected BodyLocation? location { get; set; }
protected List<string> experiment { get; set; }
protected double fractionComplete { get; set; }
protected int? minSubjectsToComplete { get; set; }

protected bool _expandedBiomes = false;
protected List<ScienceSubject> subjects;
protected Dictionary<string, string> paramIdToTitleDict = new Dictionary<string, string>();
protected Dictionary<string, float> paramIdProgressDict = new Dictionary<string, float>();

private float lastUpdate = 0.0f;
private const float UPDATE_FREQUENCY = 2.5f;
private const float FractionErrorMargin = 0.00025f;
private const double MinFractionDiffForTitleUpdate = 0.001;

public RP1CollectScience()
: base(null)
Expand All @@ -27,7 +35,7 @@ public RP1CollectScience()
}

public RP1CollectScience(CelestialBody targetBody, string biome, ExperimentSituations? situation,
BodyLocation? location, List<string> experiment, double fractionComplete, string title)
BodyLocation? location, List<string> experiment, double fractionComplete, int? minSubjectsToComplete, string title)
: base(title)
{
lastUpdate = UnityEngine.Time.fixedTime;
Expand All @@ -38,10 +46,12 @@ public RP1CollectScience(CelestialBody targetBody, string biome, ExperimentSitua
this.location = location;
this.experiment = experiment;
this.fractionComplete = fractionComplete;
this.minSubjectsToComplete = minSubjectsToComplete;

disableOnStateChange = true;

LoadScienceSubjects();
CreateDelegates();
}

protected override void OnParameterSave(ConfigNode node)
Expand All @@ -68,6 +78,11 @@ protected override void OnParameterSave(ConfigNode node)

node.AddValue("fractionComplete", fractionComplete);

if (minSubjectsToComplete.HasValue)
{
node.AddValue("minSubjectsToComplete", minSubjectsToComplete);
}

foreach (string exp in experiment)
{
if (!string.IsNullOrEmpty(exp))
Expand All @@ -79,16 +94,25 @@ protected override void OnParameterSave(ConfigNode node)

protected override void OnParameterLoad(ConfigNode node)
{
targetBody = ConfigNodeUtil.ParseValue(node, "targetBody", (CelestialBody)null);
biome = ConfigNodeUtil.ParseValue(node, "biome", "").Replace(" ", "");
situation = ConfigNodeUtil.ParseValue(node, "situation", (ExperimentSituations?)null);
location = ConfigNodeUtil.ParseValue(node, "location", (BodyLocation?)null);
experiment = ConfigNodeUtil.ParseValue(node, "experiment", new List<string>(0));
fractionComplete = ConfigNodeUtil.ParseValue(node, "fractionComplete", 1d);
try
{
targetBody = ConfigNodeUtil.ParseValue(node, "targetBody", (CelestialBody)null);
biome = ConfigNodeUtil.ParseValue(node, "biome", "").Replace(" ", "");
situation = ConfigNodeUtil.ParseValue(node, "situation", (ExperimentSituations?)null);
location = ConfigNodeUtil.ParseValue(node, "location", (BodyLocation?)null);
experiment = ConfigNodeUtil.ParseValue(node, "experiment", new List<string>(0));
fractionComplete = ConfigNodeUtil.ParseValue(node, "fractionComplete", 1d);
minSubjectsToComplete = ConfigNodeUtil.ParseValue(node, "minSubjectsToComplete", (int?)null);

if (State == ParameterState.Incomplete)
if (State == ParameterState.Incomplete)
{
LoadScienceSubjects();
CreateDelegates();
}
}
finally
{
LoadScienceSubjects();
ParameterDelegate<Vessel>.OnDelegateContainerLoad(node);
}
}

Expand All @@ -107,18 +131,33 @@ protected override string GetParameterTitle()
string biomeStr = string.IsNullOrEmpty(biome) ? (targetBody?.displayName) : new Biome(targetBody, biome).ToString();
string situationStr = situation != null ? situation.Value.Print().ToLower() :
location != null ? Localizer.GetStringByTag(location.Value == BodyLocation.Surface ? "#cc.science.location.Surface" : "#cc.science.location.Space") : null;
string fractionStr = $"{Math.Round(fractionComplete):P0} ";

if (biomeStr == null)
{
output = Localizer.Format("#cc.param.CollectScience.1", experimentStr);
}
else if (situationStr == null)
if (_expandedBiomes)
{
output = Localizer.Format("#cc.param.CollectScience.2", experimentStr, biomeStr);
if (minSubjectsToComplete > 1)
{
output = $"Collect science: {fractionStr} of {experimentStr} from at least {minSubjectsToComplete} biomes while {situationStr}";
}
else
{
output = $"Collect science: {fractionStr} of {experimentStr} from any biome while {situationStr}";
}
}
else
{
output = Localizer.Format("#cc.param.CollectScience.3", experimentStr, biomeStr, situationStr);
if (biomeStr == null)
{
output = $"Collect science: {fractionStr} of {experimentStr}";
}
else if (situationStr == null)
{
output = $"Collect science: {fractionStr} of {experimentStr} from {biomeStr}";
}
else
{
output = $"Collect science: {fractionStr} of {experimentStr} from {biomeStr} while {situationStr}";
}
}
}
}
Expand All @@ -136,17 +175,81 @@ protected override void OnUpdate()
if (UnityEngine.Time.fixedTime - lastUpdate > UPDATE_FREQUENCY)
{
lastUpdate = UnityEngine.Time.fixedTime;
int numToComplete = minSubjectsToComplete ?? 1;
int completeSubjCount = 0;
foreach (ScienceSubject subj in subjects)
{
float curFraction = subj.science / subj.scienceCap;
if (curFraction >= fractionComplete)
if (curFraction >= fractionComplete && ++completeSubjCount >= numToComplete)
{
SetState(ParameterState.Complete);
}
}

UpdateDelegates();
}
}

protected void CreateDelegates()
{
if (subjects.Count > 1)
{
foreach (var subj in subjects)
{
float curFraction = GetCompletedFraction(subj);
string title = ConstructDelegateTitle(subj, curFraction);
AddParameter(new ParameterDelegate<ScienceSubject>(title, s => false), id: subj.id);
paramIdProgressDict[subj.id] = curFraction;
}
}
}

protected void UpdateDelegates()
{
if (subjects.Count < 2) return;

foreach (ContractParameter genericParam in this.GetAllDescendents())
{
var param = genericParam as ParameterDelegate<ScienceSubject>;
if (param == null || param.State == ParameterState.Complete)
{
continue;
}

string oldTitle = param.Title;
ScienceSubject subj = subjects.FirstOrDefault(s => s.id == param.ID);
if (subj != null)
{
float curFraction = GetCompletedFraction(subj);
float prevFraction = paramIdProgressDict[subj.id];
if (curFraction - prevFraction > MinFractionDiffForTitleUpdate)
{
paramIdProgressDict[subj.id] = curFraction;
param.SetTitle(ConstructDelegateTitle(subj, curFraction));
ContractsWindow.SetParameterTitle(param, param.Title);
}

if (curFraction >= fractionComplete)
{
param.SetState(ParameterState.Complete);
}
}
}
}

protected string ConstructDelegateTitle(ScienceSubject subj, float curFraction)
{
string title = paramIdToTitleDict[subj.id];
return $"{title}: {curFraction:P1}";
}

protected static float GetCompletedFraction(ScienceSubject subj)
{
float curFraction = subj.science / subj.scienceCap;
curFraction += FractionErrorMargin;
return curFraction;
}

protected void LoadScienceSubjects()
{
subjects = new List<ScienceSubject>(experiment.Count);
Expand All @@ -156,35 +259,52 @@ protected void LoadScienceSubjects()

if (situation.HasValue)
{
subjects.Add(GetSubjectForSituation(se, situation.Value));
AddSubjectsForSituation(subjects, se, situation.Value);
}
else
{
if (location.Value == BodyLocation.Surface)
{
subjects.Add(GetSubjectForSituation(se, ExperimentSituations.SrfLanded));
subjects.Add(GetSubjectForSituation(se,ExperimentSituations.SrfSplashed));
AddSubjectsForSituation(subjects, se, ExperimentSituations.SrfLanded);
AddSubjectsForSituation(subjects, se, ExperimentSituations.SrfSplashed);
}
else
{
subjects.Add(GetSubjectForSituation(se, ExperimentSituations.InSpaceLow));
subjects.Add(GetSubjectForSituation(se, ExperimentSituations.InSpaceHigh));
AddSubjectsForSituation(subjects, se, ExperimentSituations.InSpaceLow);
AddSubjectsForSituation(subjects, se, ExperimentSituations.InSpaceHigh);
}
}
}
}

protected ScienceSubject GetSubjectForSituation(ScienceExperiment se, ExperimentSituations situation)
protected void AddSubjectsForSituation(List<ScienceSubject> subjects, ScienceExperiment se, ExperimentSituations situation)
{
string biomeName = string.Empty;
string biomeTitle = string.Empty;
if (se.BiomeIsRelevantWhile(situation))
bool hasBiomes = se.BiomeIsRelevantWhile(situation);
if (hasBiomes && string.IsNullOrEmpty(biome))
{
List<string> allBiomes = ResearchAndDevelopment.GetBiomeTags(targetBody, false);
foreach (string biomeName2 in allBiomes)
{
string biomeTitle2 = ScienceUtil.GetBiomedisplayName(targetBody, biomeName2);
var subj2 = ResearchAndDevelopment.GetExperimentSubject(se, situation, targetBody, biomeName2, biomeTitle2);
paramIdToTitleDict[subj2.id] = biomeTitle2;
subjects.Add(subj2);
}
_expandedBiomes = true;
return;
}

if (hasBiomes)
{
biomeName = biome;
biomeTitle = ScienceUtil.GetBiomedisplayName(targetBody, biome);
}

return ResearchAndDevelopment.GetExperimentSubject(se, situation, targetBody, biomeName, biomeTitle);
var subj = ResearchAndDevelopment.GetExperimentSubject(se, situation, targetBody, biomeName, biomeTitle);
paramIdToTitleDict[subj.id] = biomeTitle;
subjects.Add(subj);
}

protected string ExperimentName(string experiment)
Expand Down
6 changes: 4 additions & 2 deletions Source/CC_RP0/RP1CollectScienceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class RP1CollectScienceFactory : ParameterFactory
protected List<ScienceExperiment> experiment { get; set; }
protected List<ScienceSubject> subjects { get; set; }
protected double fractionComplete { get; set; }
protected int? minSubjectsToComplete { get; set; }

public override bool Load(ConfigNode configNode)
{
Expand All @@ -24,6 +25,7 @@ public override bool Load(ConfigNode configNode)
valid &= ConfigNodeUtil.ParseValue(configNode, "experiment", x => experiment = x, this, new List<ScienceExperiment>(), x =>
x.All(Validation.NotNull));
valid &= ConfigNodeUtil.ParseValue(configNode, "fractionComplete", x => fractionComplete = x, this, 1d);
valid &= ConfigNodeUtil.ParseValue(configNode, "minSubjectsToComplete", x => minSubjectsToComplete = x, this, (int?)null);

valid &= ConfigNodeUtil.ParseValue(configNode, "subject", x => subjects = x, this, new List<ScienceSubject>());

Expand Down Expand Up @@ -58,12 +60,12 @@ public override ContractParameter Generate(Contract contract)
ExperimentSituations es = Util.Science.GetSituation(subjects[0]);

return new RP1CollectScience(b == null ? targetBody : b.body, b == null ? "" : b.biome, es, location,
subjects.Select(s => Util.Science.GetExperiment(s).id).ToList(), fractionComplete, title);
subjects.Select(s => Util.Science.GetExperiment(s).id).ToList(), fractionComplete, minSubjectsToComplete, title);
}
else
{
return new RP1CollectScience(biome == null ? targetBody : biome.body, biome == null ? "" : biome.biome, situation, location,
experiment.Select(e => e.id).ToList(), fractionComplete, title);
experiment.Select(e => e.id).ToList(), fractionComplete, minSubjectsToComplete, title);
}
}
}
Expand Down

0 comments on commit f570511

Please sign in to comment.