/
SSTUSolarPanelDeployable.cs
240 lines (201 loc) · 9.22 KB
/
SSTUSolarPanelDeployable.cs
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
using System;
using System.Collections.Generic;
using UnityEngine;
namespace SSTUTools
{
//Multi-panel solar panel module, each with own suncatcher and pivot and occlusion checks
//Animation code based from stock, Near-Future, and Firespitter code
//Solar panel code based from Near-Future code originally, but has been vastly changed since the original implementation
//solar pivots rotate around localY, to make localZ face the sun
//e.g. y+ should point towards origin, z+ should point towards the panel solar input direction
public class SSTUSolarPanelDeployable : PartModule, IContractObjectiveModule
{
[KSPField]
public String resourceName = "ElectricCharge";
[KSPField]
public bool canDeployShrouded = false;
[KSPField]
public FloatCurve temperatureEfficCurve;
[KSPField]
public int animationLayer = 1;
//[KSPField]
//public bool canLockPanels = true;
//[KSPField(isPersistant = true, guiActive = false, guiActiveEditor = false, guiName = "Panel Rotation"),
// UI_Toggle(suppressEditorShipModified = true, enabledText = "Locked", disabledText = "Tracking")]
//public bool userLock = false;
//[KSPField(isPersistant = true, guiActive = false, guiActiveEditor = false, guiName = "Panel Rotation"),
// UI_FloatEdit(suppressEditorShipModified = true, minValue = -180f, maxValue = 180f, incrementLarge =90f, incrementSmall = 45, incrementSlide = 1f)]
//public float userRotation = 0f;
//BELOW HERE ARE NON-CONFIG EDITABLE FIELDS
//used purely to persist rough estimate of animation state; if it is retracting/extending when reloaded, it will default to the start of that animation transition
//defaults to retracted state for any new/uninitialized parts
//Status displayed for panel state, includes animation state and energy state; Using in place of the three-line output from stock panels
[KSPField(guiName = "S.P.", guiActive = true)]
public String guiStatus = String.Empty;
/// <summary>
/// Public field that can be used by external mods/etc to query for the last updated power output.<para/>
/// This value is given in EC/s, and reflects the total calculated output from the last update tick.
/// </summary>
[KSPField]
public float ECOutput = 0f;
/// <summary>
/// Animation persistent data
/// </summary>
[KSPField(isPersistant = true)]
public String persistentState = string.Empty;
/// <summary>
/// Solar panel rotation persistent data
/// </summary>
[KSPField(isPersistant = true)]
public string solarPersistentData = string.Empty;
/// <summary>
/// Nominal output of the solar panels; 100% thermal efficiency at Kerbin orbit distance from sun (1 KAU). This value is set when
/// the part is initialied and updated any time solar panel layout is changed. Can be queried in the editor or flight scene to
/// determine the current -nominal- EC output of solar panels.
/// </summary>
[KSPField(isPersistant = true)]
public float nominalSolarOutput = 0f;
[Persistent]
public string configNodeData = string.Empty;
private AnimationModule animationModule;
private SolarModule solarModule;
private bool initialized = false;
//KSP Action Group 'Extend Panels' action, will only trigger when panels are actually retracted/ing
[KSPAction("Extend Solar Panels")]
public void extendAction(KSPActionParam param)
{
extendEvent();
}
//KSP Action Group 'Retract Panels' action, will only trigger when panels are actually extended/ing
[KSPAction("Retract Solar Panels")]
public void retractAction(KSPActionParam param)
{
retractEvent();
}
//KSP Action Group 'Toggle Panels' action, will operate regardless of current panel status (except broken)
[KSPAction("Toggle Solar Panels")]
public void toggleAction(KSPActionParam param)
{
animationModule.onToggleAction(param);
}
[KSPEvent(name = "extendEvent", guiName = "Extend Solar Panels", guiActiveUnfocused = true, externalToEVAOnly = true, guiActive = true, unfocusedRange = 4f, guiActiveEditor = true)]
public void extendEvent()
{
animationModule.onDeployEvent();
}
[KSPEvent(name = "retractEvent", guiName = "Retract Solar Panels", guiActiveUnfocused = true, externalToEVAOnly = true, guiActive = true, unfocusedRange = 4f, guiActiveEditor = true)]
public void retractEvent()
{
solarModule.onRetractEvent();
}
public override void OnStart(StartState state)
{
base.OnStart(state);
initialize();
}
public override void OnLoad(ConfigNode node)
{
base.OnLoad(node);
if (string.IsNullOrEmpty(configNodeData)) { configNodeData = node.ToString(); }
initialize();
}
public override void OnSave(ConfigNode node)
{
base.OnSave(node);
//animation persistence is updated on state change
//but rotations are updated every frame, so it is not feasible to update string-based persistence data (without excessive garbage generation)
if (solarModule != null)
{
solarModule.updateSolarPersistence();
node.SetValue(nameof(solarPersistentData), solarPersistentData, true);
}
}
public override string GetInfo()
{
if (moduleIsEnabled)
{
//TODO
MonoBehaviour.print("TODO -- SSTUSolarPanelDeployable - GetInfo()");
}
return base.GetInfo();
}
public void FixedUpdate()
{
ECOutput = 0f;
if (!moduleIsEnabled) { return; }
solarModule.FixedUpdate();
ECOutput = solarModule.totalOutput;
}
public void Update()
{
if (!moduleIsEnabled) { return; }
animationModule.Update();
solarModule.Update();
}
/// <summary>
/// To be called by external modules when the module has been enabled/disabled.
/// Reloads config and/or disables GUI depending on the current moduleIsEnabled status.
/// </summary>
public void reInitialize()
{
initialized = false;
initialize();
}
private void initialize()
{
if (!moduleIsEnabled)
{
//TODO -- UI disabling
return;
}
if (initialized) { return; }
initialized = true;
AnimationData animData = null;
ModelSolarData msd = null;
ConfigNode node = SSTUConfigNodeUtils.parseConfigNode(configNodeData);
if (node.HasValue("modelDefinition"))
{
ModelDefinition def = SSTUModelData.getModelDefinition(node.GetStringValue("modelDefinition"));
if (def == null)
{
MonoBehaviour.print("Could not locate model definition: " + node.GetStringValue("modelDefinition") + " for solar data");
}
else
{
animData = def.animationData;
msd = def.solarModuleData;
}
}
//allow local override of animation data
if (node.HasNode("ANIMATIONDATA"))
{
animData = new AnimationData(node.GetNode("ANIMATIONDATA"));
}
if (node.HasNode("SOLARDATA"))
{
msd = new ModelSolarData(node.GetNode("SOLARDATA"));
}
animationModule = new AnimationModule(part, this, nameof(persistentState), null, nameof(extendEvent), nameof(retractEvent));
animationModule.getSymmetryModule = m => ((SSTUSolarPanelDeployable)m).animationModule;
animationModule.setupAnimations(animData, part.transform.FindRecursive("model"), animationLayer);
solarModule = new SolarModule(part, this, animationModule, Fields[nameof(solarPersistentData)], Fields[nameof(guiStatus)]);
solarModule.getSymmetryModule = m => ((SSTUSolarPanelDeployable)m).solarModule;
solarModule.setupSolarPanelData(new ModelSolarData[] { msd }, new Transform[] { part.transform.FindRecursive("model") });
nominalSolarOutput = solarModule.standardPotentialOutput;
}
//TODO
private void updateGuiData()
{
//Fields[nameof(userLock)].guiActive = Fields[nameof(userLock)].guiActiveEditor = canLockPanels;
//Fields[nameof(userRotation)].guiActive = Fields[nameof(userRotation)].guiActiveEditor = userLock;
}
public string GetContractObjectiveType()
{
return "Generator";
}
public bool CheckContractObjectiveValidity()
{
return moduleIsEnabled;
}
}
}