/
DecouplingAnalyzer.cs
168 lines (147 loc) · 8.43 KB
/
DecouplingAnalyzer.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
#nullable enable
using System.Collections.Generic;
using MechJebLib.Simulations.PartModules;
using static MechJebLib.Statics;
namespace MechJebLib.Simulations
{
public static class DecouplingAnalyzer
{
public static void Analyze(SimVessel v)
{
// FIXME: this whole algorithm should probably change to better simulate what staging
// actually does to vessels. It should work backwards from the current stage and fire
// decouplers, for everyone one breaking the ship in two and deciding which half is
// "jettisoned" and which half remains. The algorithm for determining that should be
// based on:
// 1. which side has more unfired staged decouplers left in it? (keep that side)
// 2. if tied, which side has seen more fired decouplers in that direction of the tree? (jettison that side)
// The first rule optimizes for keeping as many staged parts around as possible, the
// second rule is at least necessary for the final decoupler to break the 0=0 tie.
// Doing the bookkeeping quickly will likely be challenging. But this would eliminate
// the root part requirement from the algorithm entirely.
SimPart rootPart = FindRootPart(v.Parts);
CalculateDecoupledInStageRecursively(v, rootPart, null, -1);
}
private static SimPart FindRootPart(IReadOnlyList<SimPart> parts)
{
for (int i = 0; i < parts.Count; i++)
if (parts[i].IsRoot)
return parts[i];
return parts[0];
}
// BUG: This should at least be fixed to not pick the payload fairings if they are left in stage 0
// BUG: Does this even do anything? What part has a inverseStage of -1?
// BUG: Even if we change this to p.InverseStage <= 0 below what happens with the last decoupler when
// it should detatch the payload and so the part decoupled from it should be the root, not the part itself?
/*
SimPart? rootPart = null;
for (int i = 0; i < parts.Count; i++)
{
SimPart p = parts[i];
if (p.InverseStage < 0 && rootPart != null)
rootPart = p;
p.DecoupledInStage = int.MinValue;
}
rootPart ??= parts[0];
return rootPart;
*/
private static void CalculateDecoupledInStageRecursively(SimVessel v, SimPart p, SimPart? parent, int inheritedDecoupledInStage)
{
int childDecoupledInStage = CalculateDecoupledInStage(v, p, parent, inheritedDecoupledInStage);
for (int i = 0; i < p.Links.Count; i++)
{
if (p.Links[i] == parent)
continue;
CalculateDecoupledInStageRecursively(v, p.Links[i], p, childDecoupledInStage);
}
}
private static int CalculateDecoupledInStage(SimVessel v, SimPart p, SimPart? parent, int parentDecoupledInStage)
{
// prevent recursion into already-visited nodes
if (p.DecoupledInStage != int.MinValue)
return p.DecoupledInStage;
// don't decouple already decoupled decouplers
if (p.InverseStage >= parentDecoupledInStage)
{
for (int i = 0; i < p.Modules.Count; i++)
{
SimPartModule m = p.Modules[i];
switch (m)
{
case SimModuleDecouple decouple:
if (decouple is { IsDecoupled: false, StagingEnabled: true } && p.StagingOn)
{
if (decouple.IsOmniDecoupler)
{
// We are decoupling our traversalParent. The part and its children are not part of the ship when we decouple
p.DecoupledInStage = p.InverseStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
return p.DecoupledInStage;
}
if (decouple.AttachedPart != null)
{
if (decouple.AttachedPart == parent && decouple.Staged)
{
// We are decoupling our traversalParent. The part and its children are not part of the ship when we decouple
p.DecoupledInStage = p.InverseStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
return p.DecoupledInStage;
}
// We are still attached to our traversalParent. The part we decouple is dropped when we decouple.
// The part and other children are dropped with the traversalParent.
p.DecoupledInStage = parentDecoupledInStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
CalculateDecoupledInStageRecursively(v, decouple.AttachedPart, p, p.InverseStage);
return p.DecoupledInStage;
}
}
break;
case SimModuleDockingNode dockingNode:
if (dockingNode.StagingEnabled && p.StagingOn)
if (dockingNode.AttachedPart != null)
{
if (dockingNode.AttachedPart == parent && dockingNode.Staged)
{
// We are decoupling our traversalParent. The part and its children are not part of the ship when we decouple
p.DecoupledInStage = p.InverseStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
return p.DecoupledInStage;
}
// We are still attached to our traversalParent. The part we decouple is dropped when we decouple.
// The part and other children are dropped with the traversalParent.
p.DecoupledInStage = parentDecoupledInStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
CalculateDecoupledInStageRecursively(v, dockingNode.AttachedPart, p, p.InverseStage);
return p.DecoupledInStage;
}
break;
case SimProceduralFairingDecoupler procFairingDecoupler:
if (procFairingDecoupler is { IsDecoupled: false, StagingEnabled: true } && p.StagingOn)
{
// We are decoupling our traversalParent. The part and its children are not part of the ship when we decouple
p.DecoupledInStage = p.InverseStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
return p.DecoupledInStage;
}
break;
case SimLaunchClamp _:
p.DecoupledInStage = p.InverseStage > parentDecoupledInStage
? p.InverseStage
: parentDecoupledInStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
return p.DecoupledInStage;
}
}
}
// not decoupling this part
p.DecoupledInStage = parentDecoupledInStage;
TrackPartDecoupledInStage(v, p, p.DecoupledInStage);
return p.DecoupledInStage;
}
private static void TrackPartDecoupledInStage(SimVessel v, SimPart part, int stage)
{
for (int i = stage + 1; i <= v.CurrentStage; i++)
v.PartsRemainingInStage[i].Add(part);
}
}
}