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

Proposal for thermal control + various changes #105

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
25 changes: 16 additions & 9 deletions src/Modules/Comfort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,22 @@ public string tooltip()
{
const string yes = "<b><color=#00ff00>yes</color></b>";
const string no = "<b><color=#ff0000>no</color></b>";
return Lib.BuildString
(
"<align=left />",
"firm ground\t", firm_ground ? yes : no, "\n",
"exercise\t\t", exercise ? yes : no, "\n",
"not alone\t", not_alone ? yes : no, "\n",
"call home\t", call_home ? yes : no, "\n",
"panorama\t", panorama ? yes : no
);

List<string> factors = new List<string>();
factors.Add("<align=left />");
if (Settings.ComfortFirmGround > double.Epsilon) factors.Add(Lib.BuildString("firm ground\t", firm_ground ? yes : no));
if (Settings.ComfortExercise > double.Epsilon) factors.Add(Lib.BuildString("exercise\t\t", exercise ? yes : no));
if (Settings.ComfortNotAlone > double.Epsilon) factors.Add(Lib.BuildString("not alone\t", not_alone ? yes : no));
if (Settings.ComfortCallHome > double.Epsilon) factors.Add(Lib.BuildString("call home\t", call_home ? yes : no));
if (Settings.ComfortPanorama > double.Epsilon) factors.Add(Lib.BuildString("panorama\t", panorama ? yes : no));

string tooltip_text = string.Empty;
for (int i = 0; i < factors.Count; i++)
{
if (i > 1) tooltip_text += "\n";
tooltip_text += factors[i];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string operations generate a lot of temporaries
use StringBuilder directly instead

StringBuilder sb = new StringBuilder();
for(...)
{
  sb.append(factors[i]);
}
return sb.ToString();

}
return tooltip_text;
}

public string summary()
Expand Down
46 changes: 44 additions & 2 deletions src/Profile/Modifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ public static double evaluate(Vessel v, vessel_info vi, vessel_resources resourc
break;

case "temperature":
k *= vi.temp_diff;
k *= Math.Abs(vi.temp_diff);
break;

case "mod_temperature_pos":
k *= vi.temp_diff > 0.0 ? 1.0 : 0.0;
break;

case "mod_temperature_neg":
k *= vi.temp_diff < 0.0 ? 1.0 : 0.0;
break;

case "radiation":
Expand All @@ -47,6 +55,10 @@ public static double evaluate(Vessel v, vessel_info vi, vessel_resources resourc
k /= vi.comforts.factor;
break;

case "mod_comfort_space_limiter":
k *= vi.comforts.factor * ((1 - vi.comforts.factor) + (vi.living_space));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary parenthesis, possible int to double conversion that can be avoided (don't trust the compiler)

k *= vi.comforts.factor * (1.0 - vi.comforts.factor + vi.living_space);

break;

case "pressure":
k *= vi.pressure > Settings.PressureThreshold ? 1.0 : Settings.PressureFactor;
break;
Expand All @@ -59,6 +71,15 @@ public static double evaluate(Vessel v, vessel_info vi, vessel_resources resourc
k /= (double)Math.Max(vi.crew_count, 1);
break;

case "crew_count":
k *= (double)vi.crew_count;
break;

case "inverse":
double i = 1.0 / k;
k = double.IsInfinity(i) || double.IsNaN(i) ? 0.0 : i;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace it with

k = k > double.epsilon ? 1.0 / k : 0.0;

break;

default:
k *= resources.Info(v, mod).amount;
break;
Expand All @@ -80,7 +101,15 @@ public static double evaluate(environment_analyzer env, vessel_analyzer va, reso
break;

case "temperature":
k *= env.temp_diff;
k *= Math.Abs(env.temp_diff);
break;

case "mod_temperature_pos":
k *= env.temp_diff > 0.0 ? 1.0 : 0.0;
break;

case "mod_temperature_neg":
k *= env.temp_diff < 0.0 ? 1.0 : 0.0;
break;

case "radiation":
Expand All @@ -107,6 +136,10 @@ public static double evaluate(environment_analyzer env, vessel_analyzer va, reso
k /= va.comforts.factor;
break;

case "mod_comfort_space_limiter":
k *= va.comforts.factor * ((1 - va.comforts.factor) + (va.living_space));
break;

case "pressure":
k *= va.pressurized ? 1.0 : Settings.PressureFactor;
break;
Expand All @@ -119,6 +152,15 @@ public static double evaluate(environment_analyzer env, vessel_analyzer va, reso
k /= (double)Math.Max(va.crew_count, 1);
break;

case "crew_count":
k *= (double)va.crew_count;
break;

case "inverse":
double i = 1.0 / k;
k = double.IsInfinity(i) || double.IsNaN(i) ? 0.0 : i;
break;

default:
k *= sim.resource(mod).amount;
break;
Expand Down
43 changes: 23 additions & 20 deletions src/Profile/Rule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public Rule(ConfigNode node)
degeneration = Lib.ConfigValue(node, "degeneration", 0.0);
variance = Lib.ConfigValue(node, "variance", 0.0);
modifiers = Lib.Tokenize(Lib.ConfigValue(node, "modifier", string.Empty), ',');
modifiers_degen = Lib.Tokenize(Lib.ConfigValue(node, "modifier_degen", string.Empty), ',');
breakdown = Lib.ConfigValue(node, "breakdown", false);
warning_threshold = Lib.ConfigValue(node, "warning_threshold", 0.33);
danger_threshold = Lib.ConfigValue(node, "danger_threshold", 0.66);
Expand Down Expand Up @@ -60,9 +61,10 @@ public void Execute(Vessel v, vessel_info vi, vessel_resources resources, double

// get product of all environment modifiers
double k = Modifiers.evaluate(v, vi, resources, modifiers);
double k_degen = modifiers_degen.Count > 0 ? Modifiers.evaluate(v, vi, resources, modifiers_degen) : k;

// for each crew
foreach(ProtoCrewMember c in Lib.CrewList(v))
foreach (ProtoCrewMember c in Lib.CrewList(v))
{
// get kerbal data
KerbalData kd = DB.Kerbal(c.name);
Expand Down Expand Up @@ -132,10 +134,10 @@ public void Execute(Vessel v, vessel_info vi, vessel_resources resources, double
// degenerate:
// - if the environment modifier is not telling to reset (by being zero)
// - if this rule is resource-less, or if there was not enough resource in the vessel
if (k > 0.0 && (input.Length == 0 || res.amount <= double.Epsilon))
if (k_degen > 0.0 && (input.Length == 0 || res.amount <= double.Epsilon))
{
rd.problem += degeneration // degeneration rate per-second or per-interval
* k // product of environment modifiers
* k_degen // product of environment modifiers
* step // seconds elapsed or by number of steps
* Variance(c, variance); // kerbal-specific variance
}
Expand Down Expand Up @@ -209,23 +211,24 @@ static double Variance(ProtoCrewMember c, double variance)
}


public string name; // unique name for the rule
public string input; // resource consumed, if any
public string output; // resource produced, if any
public double interval; // if 0 the rule is executed per-second, else it is executed every 'interval' seconds
public double rate; // amount of input resource to consume at each execution
public double ratio; // ratio of output resource in relation to input consumed
public double degeneration; // amount to add to the property at each execution (when we must degenerate)
public double variance; // variance for property rate, unique per-kerbal and in range [1.0-variance, 1.0+variance]
public List<string> modifiers; // if specified, rates are influenced by the product of all environment modifiers
public bool breakdown; // if true, trigger a breakdown instead of killing the kerbal
public double warning_threshold; // threshold of degeneration used to show warning messages and yellow status color
public double danger_threshold; // threshold of degeneration used to show danger messages and red status color
public double fatal_threshold; // threshold of degeneration used to show fatal messages and kill/breakdown the kerbal
public string warning_message; // messages shown on threshold crossings
public string danger_message; // .
public string fatal_message; // .
public string relax_message; // .
public string name; // unique name for the rule
public string input; // resource consumed, if any
public string output; // resource produced, if any
public double interval; // if 0 the rule is executed per-second, else it is executed every 'interval' seconds
public double rate; // amount of input resource to consume at each execution
public double ratio; // ratio of output resource in relation to input consumed
public double degeneration; // amount to add to the property at each execution (when we must degenerate)
public double variance; // variance for property rate, unique per-kerbal and in range [1.0-variance, 1.0+variance]
public List<string> modifiers; // if specified, rates are influenced by the product of all environment modifiers
public List<string> modifiers_degen; // if specified, degeneration rate use this separate set of environment modifiers
public bool breakdown; // if true, trigger a breakdown instead of killing the kerbal
public double warning_threshold; // threshold of degeneration used to show warning messages and yellow status color
public double danger_threshold; // threshold of degeneration used to show danger messages and red status color
public double fatal_threshold; // threshold of degeneration used to show fatal messages and kill/breakdown the kerbal
public string warning_message; // messages shown on threshold crossings
public string danger_message; // .
public string fatal_message; // .
public string relax_message; // .
}


Expand Down
4 changes: 3 additions & 1 deletion src/Sim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,9 @@ public static double SolarFluxAtHome()
public static double TempDiff(double k, CelestialBody body, bool landed)
{
if (body.flightGlobalsIndex == FlightGlobals.GetHomeBodyIndex() && landed) return 0.0;
return Math.Max(Math.Abs(k - Settings.SurvivalTemperature) - Settings.SurvivalRange, 0.0);
return (k - Settings.SurvivalTemperature) > 0 ?
Math.Max(k - Settings.SurvivalTemperature - Settings.SurvivalRange, 0.0):
Math.Min(k - Settings.SurvivalTemperature + Settings.SurvivalRange, 0.0);
}


Expand Down
2 changes: 1 addition & 1 deletion src/System/API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public static void InjectRadiation(string k_name, double amount)
KerbalData kd = DB.Kerbal(k_name);
foreach(Rule rule in Profile.rules)
{
if (rule.modifiers.Contains("radiation"))
if (rule.modifiers.Contains("radiation") || rule.modifiers_degen.Contains("radiation"))
{
RuleData rd = kd.rules[rule.name];
rd.problem = Math.Max(rd.problem + amount, 0.0);
Expand Down
1 change: 1 addition & 0 deletions src/System/Features.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static void detect()
foreach(Rule rule in Profile.rules)
{
foreach(string s in rule.modifiers) modifiers.Add(s);
foreach(string s in rule.modifiers_degen) modifiers.Add(s);
}
foreach(Process process in Profile.processes)
{
Expand Down
63 changes: 45 additions & 18 deletions src/UI/Planner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public Planner()
// special panels
// - stress & radiation panels require that a rule using the living_space/radiation modifier exist (current limitation)
panel_special = new List<string>();
if (Features.LivingSpace && Profile.rules.Find(k => k.modifiers.Contains("living_space")) != null) panel_special.Add("qol");
if (Features.Radiation && Profile.rules.Find(k => k.modifiers.Contains("radiation")) != null) panel_special.Add("radiation");
if (Features.LivingSpace && Profile.rules.Find(k => k.modifiers.Contains("living_space") || k.modifiers_degen.Contains("living_space")) != null) panel_special.Add("qol");
if (Features.Radiation && Profile.rules.Find(k => k.modifiers.Contains("radiation") || k.modifiers_degen.Contains("radiation")) != null) panel_special.Add("radiation");
if (Features.Reliability) panel_special.Add("reliability");
if (Features.Signal) panel_special.Add("signal");

Expand Down Expand Up @@ -256,46 +256,73 @@ void render_stress(Panel p)
// get first living space rule
// - guaranteed to exist, as this panel is not rendered if it doesn't
// - even without crew, it is safe to evaluate the modifiers that use it
Rule rule = Profile.rules.Find(k => k.modifiers.Contains("living_space"));
Rule rule = Profile.rules.Find(k => k.modifiers.Contains("living_space") || k.modifiers_degen.Contains("living_space"));

// render title
p.section("STRESS", string.Empty, () => p.prev(ref special_index, panel_special.Count), () => p.next(ref special_index, panel_special.Count));

bool use_comfort = rule.modifiers.Contains("comfort") || rule.modifiers_degen.Contains("comfort");
bool use_pressure = rule.modifiers.Contains("pressure") || rule.modifiers_degen.Contains("pressure");

// render living space data
// generate details tooltips
string living_space_tooltip = Lib.BuildString
(
"volume per-capita: <b>", Lib.HumanReadableVolume(va.volume / (double)Math.Max(va.crew_count, 1)), "</b>\n",
"ideal living space: <b>", Lib.HumanReadableVolume(Settings.IdealLivingSpace), "</b>"
);
string volume_per_capita = Lib.HumanReadableVolume(va.volume / (double)Math.Max(va.crew_count, 1));
string ideal_living_space = Lib.HumanReadableVolume(Settings.IdealLivingSpace);
string living_space_tooltip = string.Empty;
if (use_comfort && use_pressure)
{
living_space_tooltip = Lib.BuildString
(
"volume per-capita: <b>", volume_per_capita, "</b>\n",
"ideal living space: <b>", ideal_living_space, "</b>"
);
}
else if (use_comfort || use_pressure)
{
living_space_tooltip = Lib.BuildString
(
"ideal living space: <b>", ideal_living_space, "</b>"
);
}
p.content("living space", Habitat.living_space_to_string(va.living_space), living_space_tooltip);

// render comfort data
if (rule.modifiers.Contains("comfort"))
if (use_comfort)
{
p.content("comfort", va.comforts.summary(), va.comforts.tooltip());
}
else
{
p.content("comfort", "n/a");
}

// render pressure data
if (rule.modifiers.Contains("pressure"))
if (use_pressure)
{
string pressure_tooltip = va.pressurized
? "Free roaming in a pressurized environment is\nvastly superior to living in a suit."
: "Being forced inside a suit all the time greatly\nreduce the crew quality of life.\nThe worst part is the diaper.";
p.content("pressurized", va.pressurized ? "yes" : "no", pressure_tooltip);
}
else

// if pressure/comfort are absent, use the empty space to render volume per-capita & ideal living space
if (!use_comfort && !use_pressure)
{
p.content("volume per-capita", volume_per_capita);
p.content("ideal living space", ideal_living_space);
}
else if (!use_comfort || !use_pressure)
{
p.content("pressurized", "n/a");
p.content("volume per-capita", volume_per_capita, living_space_tooltip);
}

// render life estimate
double mod = Modifiers.evaluate(env, va, sim, rule.modifiers);
p.content("duration", Lib.HumanReadableDuration(rule.fatal_threshold / (rule.degeneration * mod)));
double mod = rule.modifiers_degen.Count > 0 ? Modifiers.evaluate(env, va, sim, rule.modifiers_degen) : Modifiers.evaluate(env, va, sim, rule.modifiers);
if (rule.input != string.Empty)
{
simulated_resource res = sim.resource(rule.input);
p.content("time to breakdown", Lib.HumanReadableDuration((rule.fatal_threshold / (rule.degeneration * mod)) + res.lifetime()));
}
else
{
p.content("time to breakdown", Lib.HumanReadableDuration(rule.fatal_threshold / (rule.degeneration * mod)));
}
}


Expand Down