void render_food(food_data food) { render_title("FOOD"); render_content("storage", Lib.ValueOrNone(food.storage)); render_content("consumed", Lib.HumanReadableRate(food.consumed)); render_content("cultivated", Lib.HumanReadableRate(food.cultivated), food.cultivated_tooltip); render_content("life expectancy", Lib.HumanReadableDuration(food.life_expectancy)); render_space(); }
public void render() { // if there is something in the editor if (EditorLogic.RootPart != null) { // store situations and altitude multipliers string[] situations = {"Landed", "Low Orbit", "Orbit", "High Orbit"}; double[] altitude_mults = {0.0, 0.33, 1.0, 3.0}; // get body, situation and altitude multiplier CelestialBody body = FlightGlobals.Bodies[body_index]; string situation = situations[situation_index]; double altitude_mult = altitude_mults[situation_index]; // get parts recursively List<Part> parts = Lib.GetPartsRecursively(EditorLogic.RootPart); // analyze environment_data env = analyze_environment(body, altitude_mult); crew_data crew = analyze_crew(parts); food_data food = analyze_food(parts, env, crew); oxygen_data oxygen = analyze_oxygen(parts, env, crew); signal_data signal = analyze_signal(parts); qol_data qol = analyze_qol(parts, env, crew, signal); radiation_data radiation = analyze_radiation(parts, env, crew); ec_data ec = analyze_ec(parts, env, crew, food, oxygen, signal); reliability_data reliability = analyze_reliability(parts, ec, signal); // render menu GUILayout.BeginHorizontal(row_style); if (GUILayout.Button(body.name, leftmenu_style)) { body_index = (body_index + 1) % FlightGlobals.Bodies.Count; if (body_index == 0) ++body_index; } if (GUILayout.Button("["+ (page + 1) + "/2]", midmenu_style)) { page = (page + 1) % 2; } if (GUILayout.Button(situation, rightmenu_style)) { situation_index = (situation_index + 1) % situations.Length; } GUILayout.EndHorizontal(); // page 1/2 if (page == 0) { // render render_ec(ec); render_food(food); render_oxygen(oxygen); render_qol(qol); } // page 2/2 else { // render render_radiation(radiation, env, crew); render_reliability(reliability, crew); render_signal(signal, env, crew); render_environment(env); } } // if there is nothing in the editor else { // render quote GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(); GUILayout.Label("<i>In preparing for space, I have always found that\nplans are useless but planning is indispensable.\nWernher von Kerman</i>", quote_style); GUILayout.EndHorizontal(); GUILayout.Space(10.0f); } }
public static food_data analyze_food(List<Part> parts, environment_data env, crew_data crew) { // store data food_data food = new food_data(); // calculate food consumed food.consumed = (double)crew.count * Settings.FoodPerMeal / Settings.MealFrequency; // deduce waste produced by the crew per-second double simulated_waste = food.consumed; // scan the parts foreach(Part p in parts) { // accumulate food storage food.storage += Lib.GetResourceAmount(p, "Food"); // for each module foreach(PartModule m in p.Modules) { // greenhouse if (m.moduleName == "Greenhouse") { Greenhouse mm = (Greenhouse)m; // calculate natural lighting double natural_lighting = Greenhouse.NaturalLighting(env.sun_dist); // calculate ec consumed food.greenhouse_cost += mm.ec_rate * mm.lamps; // calculate lighting double lighting = natural_lighting * (mm.door_opened ? 1.0 : 0.0) + mm.lamps * (mm.door_opened ? 1.0 : 1.0 + Settings.GreenhouseDoorBonus); // calculate waste used double waste_used = Math.Min(simulated_waste, mm.waste_rate); double waste_perc = waste_used / mm.waste_rate; simulated_waste -= waste_used; // calculate growth bonus double growth_bonus = 0.0; growth_bonus += Settings.GreenhouseSoilBonus * (env.landed ? 1.0 : 0.0); growth_bonus += Settings.GreenhouseWasteBonus * waste_perc; // calculate growth factor double growth_factor = (mm.growth_rate * (1.0 + growth_bonus)) * lighting; // calculate food cultivated food.cultivated += mm.harvest_size * growth_factor; // calculate time-to-harvest if (growth_factor > double.Epsilon) { food.cultivated_tooltip += (food.cultivated_tooltip.Length > 0 ? "\n" : "") + "Time-to-harvest: <b>" + Lib.HumanReadableDuration(1.0 / growth_factor) + "</b>"; } } } } // calculate life expectancy food.life_expectancy = food.storage / Math.Max(food.consumed - food.cultivated, 0.0); // add formatting to tooltip if (food.cultivated_tooltip.Length > 0) food.cultivated_tooltip = "<i>" + food.cultivated_tooltip + "</i>"; // return data return food; }
public static ec_data analyze_ec(List<Part> parts, environment_data env, crew_data crew, food_data food, oxygen_data oxygen, signal_data signal) { // store data ec_data ec = new ec_data(); // calculate climate cost ec.consumed = (double)crew.count * env.temp_diff * Settings.ElectricChargePerSecond; // scan the parts foreach(Part p in parts) { // accumulate EC storage ec.storage += Lib.GetResourceAmount(p, "ElectricCharge"); // remember if we already considered a resource converter module // rationale: we assume only the first module in a converter is active bool first_converter = true; // for each module foreach(PartModule m in p.Modules) { // command if (m.moduleName == "ModuleCommand") { ModuleCommand mm = (ModuleCommand)m; foreach(ModuleResource res in mm.inputResources) { if (res.name == "ElectricCharge") { ec.consumed += res.rate; } } } // solar panel else if (m.moduleName == "ModuleDeployableSolarPanel") { ModuleDeployableSolarPanel mm = (ModuleDeployableSolarPanel)m; double solar_k = (mm.useCurve ? mm.powerCurve.Evaluate((float)env.sun_dist) : env.sun_flux / Sim.SolarFluxAtHome()); double generated = mm.chargeRate * solar_k * env.atmo_factor; ec.generated_sunlight += generated; ec.best_ec_generator = Math.Max(ec.best_ec_generator, generated); } // generator else if (m.moduleName == "ModuleGenerator") { // skip launch clamps, that include a generator if (p.partInfo.name == "launchClamp1") continue; ModuleGenerator mm = (ModuleGenerator)m; foreach(ModuleResource res in mm.inputList) { if (res.name == "ElectricCharge") { ec.consumed += res.rate; } } foreach(ModuleResource res in mm.outputList) { if (res.name == "ElectricCharge") { ec.generated_shadow += res.rate; ec.generated_sunlight += res.rate; ec.best_ec_generator = Math.Max(ec.best_ec_generator, res.rate); } } } // converter // note: only electric charge is considered for resource converters // note: we only consider the first resource converter in a part, and ignore the rest else if (m.moduleName == "ModuleResourceConverter" && first_converter) { ModuleResourceConverter mm = (ModuleResourceConverter)m; foreach(ResourceRatio rr in mm.inputList) { if (rr.ResourceName == "ElectricCharge") { ec.consumed += rr.Ratio; } } foreach(ResourceRatio rr in mm.outputList) { if (rr.ResourceName == "ElectricCharge") { ec.generated_shadow += rr.Ratio; ec.generated_sunlight += rr.Ratio; ec.best_ec_generator = Math.Max(ec.best_ec_generator, rr.Ratio); } } first_converter = false; } // harvester // note: only electric charge is considered for resource harvesters else if (m.moduleName == "ModuleResourceHarvester") { ModuleResourceHarvester mm = (ModuleResourceHarvester)m; foreach(ResourceRatio rr in mm.inputList) { if (rr.ResourceName == "ElectricCharge") { ec.consumed += rr.Ratio; } } } // active radiators else if (m.moduleName == "ModuleActiveRadiator") { ModuleActiveRadiator mm = (ModuleActiveRadiator)m; if (mm.IsCooling) { foreach(var rr in mm.inputResources) { if (rr.name == "ElectricCharge") { ec.consumed += rr.rate; } } } } // wheels else if (m.moduleName == "ModuleWheelMotor") { ModuleWheelMotor mm = (ModuleWheelMotor)m; if (mm.motorEnabled && mm.inputResource.name == "ElectricCharge") { ec.consumed += mm.inputResource.rate; } } else if (m.moduleName == "ModuleWheelMotorSteering") { ModuleWheelMotorSteering mm = (ModuleWheelMotorSteering)m; if (mm.motorEnabled && mm.inputResource.name == "ElectricCharge") { ec.consumed += mm.inputResource.rate; } } // SCANsat support else if (m.moduleName == "SCANsat" || m.moduleName == "ModuleSCANresourceScanner") { // include it in ec consumption, if deployed if (SCANsat.isDeployed(p, m)) ec.consumed += Lib.ReflectionValue<float>(m, "power"); } // NearFutureSolar support // note: assume half the components are in sunlight, and average inclination is half else if (m.moduleName == "ModuleCurvedSolarPanel") { // get total rate double tot_rate = Lib.ReflectionValue<float>(m, "TotalEnergyRate"); // get number of components int components = p.FindModelTransforms(Lib.ReflectionValue<string>(m, "PanelTransformName")).Length; // approximate output // 0.7071: average clamped cosine ec.generated_sunlight += 0.7071 * tot_rate; } } } // include cost from greenhouses artificial lighting ec.consumed += food.greenhouse_cost; // include cost from scrubbers ec.consumed += oxygen.scrubber_cost; // include relay cost for the best relay antenna ec.consumed += signal.relay_cost; // finally, calculate life expectancy of ec ec.life_expectancy_sunlight = ec.storage / Math.Max(ec.consumed - ec.generated_sunlight, 0.0); ec.life_expectancy_shadow = ec.storage / Math.Max(ec.consumed - ec.generated_shadow, 0.0); // return data return ec; }