Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add minSubjectsToComplete attribute to CollectScience parameter #2070

Merged
merged 1 commit into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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