forked from Kerbas-ad-astra/WildBlueTools
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModuleRadiator.cs
More file actions
317 lines (261 loc) · 13 KB
/
ModuleRadiator.cs
File metadata and controls
317 lines (261 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using KSP.IO;
/*
Source code copyright 2015, by Michael Billard (Angel-125)
License: CC BY-NC-SA 4.0
License URL: https://creativecommons.org/licenses/by-nc-sa/4.0/
If you want to use this code, give me a shout on the KSP forums! :)
Wild Blue Industries is trademarked by Michael Billard and may be used for non-commercial purposes. All other rights reserved.
Note that Wild Blue Industries is a ficticious entity created for entertainment purposes. It is in no way meant to represent a real entity.
Any similarity to a real entity is purely coincidental.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace WildBlueIndustries
{
public enum CoolingCycleModes
{
active, //Heat is pulled into the radiator and relies on stock game to cool off. Used when part does not deploy radiators.
passive, //Heat is not pulled into the radiator and it relies upon stock game heat management
closed, //Heat is actively transferred into the radiator, but it relies on stock game to cool off.
open //Heat is actively transferred into the radiator, and coolant is expelled to rapidly cool the radiator.
}
public struct CoolantResource
{
public string name;
public ResourceFlowMode flowMode;
public float ratio;
}
public delegate void RadiatorDestroyed(ModuleRadiator doomed);
public class ModuleRadiator : ModuleDeployableSolarPanel
{
//768k is when object should start glowing red, but we lower it to make the animation glow look right.
private const float kGlowTempOffset = 600f; //My custom Draper Point...
private const string kDefaultCoolant = "Coolant";
//Status text
[KSPField(guiActive = true, guiName = "Temperature")]
public string radiatorTemperature;
//A value between 0 and 1, used to determine how much of the radiator's max
//temperature may be dedicated to heat management.
[KSPField(isPersistant = true)]
public float workingTempFactor;
//How many units of coolant to dump overboard during
//open-cycle cooling.
[KSPField(isPersistant = true)]
public float coolantDumpRate;
//How many units of coolant/sec is lost while the ship is
//under acceleration
[KSPField(isPersistant = true)]
public float lossRateAccelerating;
//Current cooling cycle mode.
[KSPField(isPersistant = true)]
public CoolingCycleModes coolingCycleMode;
//Amount of ec per second required to run the radiator, if any.
[KSPField(isPersistant = true)]
public double ecRequired;
public RadiatorDestroyed radiatorDestroyedDelegate;
protected List<CoolantResource> coolantResources = new List<CoolantResource>();
protected double maxThermalTransfer = 0;
protected double currentThermalTransfer = 0;
PartResourceDefinitionList definitions = PartResourceLibrary.Instance.resourceDefinitions;
PartResourceDefinition electricChargeDef;
#region Overrides and API
[KSPAction("Toggle Cooling Cycle")]
public void ToggleGoolingModeAction(KSPActionParam param)
{
ToggleCoolingMode();
}
[KSPAction("Open Cooling Cycle")]
public void OpenModeAction(KSPActionParam param)
{
coolingCycleMode = CoolingCycleModes.open;
Events["ToggleCoolingMode"].guiName = "Cooling Mode (open)";
}
[KSPAction("Closed Cooling Cycle")]
public void ClosedModeAction(KSPActionParam param)
{
coolingCycleMode = CoolingCycleModes.closed;
Events["ToggleCoolingMode"].guiName = "Cooling Mode (closed)";
}
[KSPEvent(guiActive = true, guiName = "Cooling Mode", active = true, externalToEVAOnly = false, unfocusedRange = 3.0f, guiActiveUnfocused = true)]
public void ToggleCoolingMode()
{
if (coolingCycleMode == CoolingCycleModes.closed)
{
coolingCycleMode = CoolingCycleModes.open;
Events["ToggleCoolingMode"].guiName = "Cooling Mode (open)";
}
else
{
coolingCycleMode = CoolingCycleModes.closed;
Events["ToggleCoolingMode"].guiName = "Cooling Mode (closed)";
}
}
public override void OnLoad(ConfigNode node)
{
base.OnLoad(node);
}
public override void OnStart(StartState state)
{
base.OnStart(state);
//Get the resource definition for electric charge.
electricChargeDef = definitions["ElectricCharge"];
//Since we are based upon the ModuleDeployableSolarPanel, hide its gui
Fields["sunAOA"].guiActive = false;
Fields["flowRate"].guiActive = false;
//Set cooling mode. For now, default is closed.
coolingCycleMode = CoolingCycleModes.closed;
Events["ToggleCoolingMode"].guiName = "Cooling Mode (closed)";
//Dig into the proto part and find the coolant resource nodes.
getCoolantNodes();
this.part.OnJustAboutToBeDestroyed = OnAboutToBeDestroyed;
}
public void OnAboutToBeDestroyed()
{
if (radiatorDestroyedDelegate != null)
radiatorDestroyedDelegate(this);
}
public void UpdateState()
{
//Do we have enough electricity to run the radiator?
if (ecRequired > 0.001 && panelState == panelStates.EXTENDED)
{
double ecPerTimeTick = ecRequired * TimeWarp.fixedDeltaTime;
double ecSupplied = this.part.vessel.rootPart.RequestResource(electricChargeDef.id, ecPerTimeTick, ResourceFlowMode.ALL_VESSEL);
if (ecSupplied < ecPerTimeTick)
return;
}
//For open-cycle cooling, dump coolant resources overboard and adjust thermal energy accordingly.
if (coolingCycleMode == CoolingCycleModes.open || (lossRateAccelerating > 0f && this.part.vessel.acceleration.magnitude > 0f) )
{
//Now go through the list of coolants and dump them overboard, carrying heat with them.
foreach (CoolantResource coolant in coolantResources)
{
if (coolingCycleMode == CoolingCycleModes.open)
dumpCoolant(coolant, coolantDumpRate);
if (lossRateAccelerating > 0f)
dumpCoolant(coolant, lossRateAccelerating);
}
}
//Now set the radiator color
//The game's built-in shader is working but it doesn't look as nice.
SetRadiatorColor();
//If we have heat to transfer in, then do so and reset
//We do this in one chunk for game performance.
if (currentThermalTransfer > 0.001)
{
this.part.AddThermalFlux(currentThermalTransfer);
currentThermalTransfer = 0f;
maxThermalTransfer = 0f;
}
}
public override string GetInfo()
{
string info = string.Format("<b>-Operating Temperature:</b> {0:f1}K\n-Coolant Dump Rate: {1:f1}u/sec",
(part.maxTemp * workingTempFactor), coolantDumpRate);
if (coolantDumpRate > 0.001)
info += "\n\nRight-click the radiator to dump coolant and rapidly cool the radiator.";
return info;
}
public double TransferHeat(double heatToTransfer)
{
//If the panel isn't extended, then we cannot transfer any heat.
if (panelState != panelStates.EXTENDED)
return 0;
//Are we at or exceeding max operating temp?
if (this.part.temperature >= this.part.maxTemp * workingTempFactor)
{
currentThermalTransfer = 0f;
maxThermalTransfer = 0f;
return 0;
}
//Once per time-tick, calculate max thermal transfer
if (maxThermalTransfer < 0.001f)
maxThermalTransfer = (this.part.thermalMass * this.part.maxTemp * workingTempFactor) - (this.part.thermalMass * this.part.temperature);
//If we can take the heat then add it to our bucket.
if (currentThermalTransfer + heatToTransfer > maxThermalTransfer)
currentThermalTransfer = (currentThermalTransfer + heatToTransfer) - maxThermalTransfer;
currentThermalTransfer += heatToTransfer;
return currentThermalTransfer;
}
#endregion
#region Helpers
public void SetRadiatorColor()
{
if (HighLogic.LoadedSceneIsEditor)
return;
//Really, *really* want to use the stock glow shader. I don't know how yet though.
Renderer[] renderers = this.part.FindModelComponents<Renderer>();
//Account for Draper Point
float ratio = (float)(this.part.temperature - kGlowTempOffset) / (float)(this.part.maxTemp - kGlowTempOffset);
if (ratio < 0.0f)
ratio = 0f;
//Set the emissive color
foreach (Renderer renderer in renderers)
renderer.material.SetColor("_EmissiveColor", new Color(ratio, ratio, ratio));
radiatorTemperature = String.Format("{0:#.##}K", this.part.temperature);
}
protected void dumpCoolant(CoolantResource coolant, double dumpRate)
{
PartResourceDefinitionList definitions = PartResourceLibrary.Instance.resourceDefinitions;
PartResourceDefinition resourceDef;
double coolantToDump = dumpRate * TimeWarp.fixedDeltaTime;
double coolantDumped = 0;
double thermalEnergyCoolant = 0;
//The the resource definition
resourceDef = definitions[coolant.name];
//Now calculate the resource amount dumped and the thermal energy of that slug of resource.
coolantDumped = this.part.RequestResource(resourceDef.id, coolantToDump * coolant.ratio, coolant.flowMode);
if (coolantDumped <= 0.001)
return;
thermalEnergyCoolant = this.part.temperature * this.part.resourceThermalMass * coolantDumped;
//Practice conservation of energy...
if (coolantDumped > 0.001)
this.part.AddThermalFlux(-thermalEnergyCoolant);
}
protected void getCoolantNodes()
{
if (this.part.protoPartSnapshot != null)
{
if (this.part.protoPartSnapshot.partInfo != null)
{
//Aha! the part's config file!
//Now go find the MODULE definition for ModuleRadiator
if (this.part.protoPartSnapshot.partInfo.partConfig != null)
{
string value;
ConfigNode[] moduleNodes = this.part.protoPartSnapshot.partInfo.partConfig.GetNodes("MODULE");
if (moduleNodes == null)
return;
//Find our module definition.
foreach (ConfigNode moduleNode in moduleNodes)
{
value = moduleNode.GetValue("name");
if (string.IsNullOrEmpty(value))
continue;
//Aha! found our module definition!
//Now get the coolants
if (value == this.ClassName)
{
CoolantResource coolant;
ConfigNode[] coolantResourceNodes = moduleNode.GetNodes("INPUT_RESOURCE");
foreach (ConfigNode node in coolantResourceNodes)
{
coolant = new CoolantResource();
coolant.name = node.GetValue("name");
coolant.flowMode = (ResourceFlowMode)Enum.Parse(typeof(ResourceFlowMode), node.GetValue("flowMode"));
coolant.ratio = float.Parse(node.GetValue("ratio"));
coolantResources.Add(coolant);
}
}
}
}
}
}
}
#endregion
}
}