/// <summary> /// Parts of a vessel are first read one after the other, so the first part can only store indices of later parts it is connected to. /// After a complete vessel is read also all parts are read and the indices can be translated in references to now existing KmlParts. /// This is done by this method. Also reverse information (what parts are connected to this one) is then stored in a part. /// </summary> /// <param name="parts">The list of KmlParts, a KmlVessel will have one</param> /// <returns>A list of root parts (not pointing to another parent part). Could be more than one, if some connections are broken.</returns> public static List <KmlPart> BuildAttachmentStructure(List <KmlPart> parts) { List <KmlPart> roots = new List <KmlPart>(); for (int i = 0; i < parts.Count; i++) { // Check parent connection KmlPart part = parts[i]; if (part.ParentPartIndex == i) { // Parent part is itself, so ParentPart property stays null roots.Add(part); } else if (part.ParentPartIndex < 0 || part.ParentPartIndex >= parts.Count) { Syntax.Warning(part, "Part's parent part index [" + part.ParentPartIndex + "] does not point to a valid part."); roots.Add(part); } else { part.ParentPart = parts[part.ParentPartIndex]; if (!part.AttachedToNodeIndices.Contains(part.ParentPartIndex) && part.AttachedToSurfaceIndex != part.ParentPartIndex) { // Part could be docked to parent if ((part is KmlPartDock) && (part.ParentPart is KmlPartDock)) { KmlPartDock docker = (KmlPartDock)part; KmlPartDock dockee = (KmlPartDock)part.ParentPart; // In case of "Docked (same vessel)" there has to be another docker-dockee connection and that would have connection via parent part if (docker.DockState.ToLower() == "docked (docker)") { if (dockee.DockState.ToLower() != "docked (dockee)") { Syntax.Warning(dockee, "Dock part is parent of other docker part. Docking state should be 'Docked (dockee)' but is '" + dockee.DockState + "', other dock: " + docker); docker.NeedsRepair = true; dockee.NeedsRepair = true; } else { KmlPartDock.BuildDockStructure(dockee, docker); KmlPartDock.BuildDockStructure(docker, dockee); } } else if (docker.DockState.ToLower() == "docked (dockee)") { if (dockee.DockState.ToLower() != "docked (docker)") { Syntax.Warning(dockee, "Dock part is parent of other dockee part. Docking state should be 'Docked (docker)' but is '" + dockee.DockState + "', other dock: " + docker); docker.NeedsRepair = true; dockee.NeedsRepair = true; } else { KmlPartDock.BuildDockStructure(dockee, docker); KmlPartDock.BuildDockStructure(docker, dockee); } } else { if (dockee.DockState.ToLower() == "docked (dockee)") { Syntax.Warning(docker, "Dock part is docked to parent dockee part. Docking state should be 'Docked (docker)' but is '" + docker.DockState + "', parent dock: " + dockee); } else if (dockee.DockState.ToLower() == "docked (docker)") { Syntax.Warning(docker, "Dock part is docked to parent docker part. Docking state should be 'Docked (dockee)' but is '" + docker.DockState + "', parent dock: " + dockee); } else { Syntax.Warning(docker, "Dock part is docked to parent dock part. Docking state should be 'Docked (docker)' or 'Docked (dockee)' but is '" + docker.DockState + "', parent dock: " + dockee); Syntax.Warning(dockee, "Dock part is parent of other dock part. Docking state should be 'Docked (dockee)' or 'Docked (docker)' but is '" + dockee.DockState + "', other dock: " + docker); } docker.NeedsRepair = true; dockee.NeedsRepair = true; } } // Part could be grappled by parent else if ((part.ParentPart is KmlPartDock) && (part.ParentPart as KmlPartDock).DockType == KmlPartDock.DockTypes.Grapple) { KmlPartDock grapple = (KmlPartDock)part.ParentPart; if (grapple.DockUid != part.Uid) { Syntax.Warning(part, "Part not attached or grappled by parent grappling part: " + grapple); Syntax.Warning(grapple, "Grappling part is parent of other part, but is not grappled to it: " + part); grapple.NeedsRepair = true; } else if (grapple.DockState.ToLower() != "grappled") { Syntax.Warning(part, "Part grappled by parent part. Docking state should be 'Grappled' but is '" + grapple.DockState + "', parent grapple: " + grapple); Syntax.Warning(grapple, "Grappling part is parent of grappled part. Docking state should be 'Grappled' but is '" + grapple.DockState + "', grappled part: " + part); grapple.NeedsRepair = true; } else { // It's docked but grappling needs a node attachment KmlPartDock.BuildDockStructure(grapple, part); Syntax.Warning(part, "Part is docked but not attached to parent grappling part: " + grapple); Syntax.Warning(grapple, "Grappling part is parent and docked but not attached to grappled part: " + part); grapple.NeedsRepair = true; } } // TODO KmlPart.BuildAttachmentStructure(): How do KAS links work? // Usually you can only attach a new part by a node to the surface of parent // and not attach a part by surface to parents node. But if you have vessels docked // this situation may happen and this leads to this additional check else if (part.ParentPart.AttachedToSurfaceIndex != i) { Syntax.Warning(part, "Part not attached to parent part: " + part.ParentPart); } } } // Check attachments foreach (int p in part.AttachedToNodeIndices) { if (p >= 0 && p < parts.Count) { KmlPart other = parts[p]; // Sort attached part in the corresponding list, identified by position not by node name double diffX = part.Position.X - other.Position.X; double diffY = part.Position.Y - other.Position.Y; double diffZ = part.Position.Z - other.Position.Z; if (Math.Abs(diffX) > Math.Abs(diffY) && Math.Abs(diffX) > Math.Abs(diffZ)) { if (diffX > 0) { other.AttachedPartsRight.Add(part); part.AttachedToPartsLeft.Add(other); } else { other.AttachedPartsLeft.Add(part); part.AttachedToPartsRight.Add(other); } } else if (Math.Abs(diffZ) > Math.Abs(diffX) && Math.Abs(diffZ) > Math.Abs(diffY)) { if (diffZ > 0) { other.AttachedPartsFront.Add(part); part.AttachedToPartsBack.Add(other); } else { other.AttachedPartsBack.Add(part); part.AttachedToPartsFront.Add(other); } } else { if (diffY > 0) { other.AttachedPartsTop.Add(part); part.AttachedToPartsBottom.Add(other); } else { other.AttachedPartsBottom.Add(part); part.AttachedToPartsTop.Add(other); } } if (!other.AttachedToNodeIndices.Contains(parts.IndexOf(part))) { if ((other is KmlPartDock) && (other as KmlPartDock).DockType == KmlPartDock.DockTypes.Grapple) { KmlPartDock grapple = (KmlPartDock)other; if (grapple.DockUid != part.Uid) { Syntax.Warning(part, "Part node attachment not responded from other grappling part: " + grapple); grapple.NeedsRepair = true; } else if (grapple.DockState.ToLower() != "grappled") { Syntax.Warning(part, "Part grappled by other grappling part. Docking state should be 'Grappled' but is '" + grapple.DockState + ", other grapple: " + grapple); grapple.NeedsRepair = true; } else { KmlPartDock.BuildDockStructure(grapple, part); } } else if ((part is KmlPartDock) && (part as KmlPartDock).DockType == KmlPartDock.DockTypes.Grapple) { KmlPartDock grapple = (KmlPartDock)part; if (grapple.DockUid != other.Uid) { Syntax.Warning(grapple, "Grappling part node attachment not responded from other grappled part: " + other); grapple.NeedsRepair = true; } else if (grapple.DockState.ToLower() != "grappled") { Syntax.Warning(grapple, "Grappling part grappled attached part. Docking state should be 'Grappled' but is '" + grapple.DockState + ", attached part: " + other); grapple.NeedsRepair = true; } else { KmlPartDock.BuildDockStructure(grapple, other); } } else { Syntax.Warning(part, "Part node attachment not responded from other part: " + other.ToString()); } } } else { Syntax.Warning(part, "Part supposed to be node attached to part index [" + p + "], which does not point to a valid part"); } } if (part.AttachedToSurfaceIndex >= 0 && part.AttachedToSurfaceIndex < parts.Count) { parts[part.AttachedToSurfaceIndex].AttachedPartsSurface.Add(part); } else if (part.AttachedToSurfaceIndex != -1) { Syntax.Warning(part, "Part supposed to be surface attached to part index [" + part.AttachedToSurfaceIndex + "], which does not point to a valid part"); } // Check docking (with parent involved is already checked above, here needs to e checked a 'Docked (same vessel)' // Need to check one side only, other part will be touched here in another iteration of the loop if (part is KmlPartDock) { KmlPartDock dock = (KmlPartDock)part; KmlPart other = null; foreach (KmlPart p in parts) { if (p.Uid == dock.DockUid) { other = p; break; } } if (other == null) { // This happens a lot, parts show UId 0 or some other UId they have been attached to before if (dock.DockState.ToLower() == "docked (docker)" || dock.DockState.ToLower() == "docked (dockee)" || dock.DockState.ToLower() == "docked (same vessel)" || dock.DockState.ToLower() == "grappled") { Syntax.Warning(dock, "Dock part supposed to be attached to (UId " + dock.DockUid + "), which does not point to a valid part"); dock.NeedsRepair = true; } // If it still says 'Disengage' something went wron on undocking. We can repair if undocked part is now another vessel // so other will be null because not found in this vessel // Also could be 'Disarmed', 'Disabled' etc., so it's not checked to be 'Ready'. else if (dock.DockState.ToLower() == "disengage") { Syntax.Warning(dock, "Dock part state should be 'Ready' but is '" + dock.DockState + "'"); dock.NeedsRepair = true; } } // Docking with parent already checked else if (other != dock.ParentPart && other.ParentPartIndex != i) { if (dock.DockState.ToLower() == "docked (docker)" || dock.DockState.ToLower() == "docked (dockee)" || dock.DockState.ToLower() == "docked (same vessel)" || dock.DockState.ToLower() == "grappled") { KmlPartDock.BuildDockStructure(dock, other); if (other is KmlPartDock) { KmlPartDock otherDock = (KmlPartDock)other; if (otherDock.DockUid != dock.Uid) { Syntax.Warning(dock, "Dock part docked to other dock part, but docking not responded from other side. Other dock: " + otherDock); dock.NeedsRepair = true; otherDock.NeedsRepair = true; } else if (otherDock.DockState.ToLower() == "docked (dockee)") { if (dock.DockState.ToLower() != "docked (same vessel)") { Syntax.Warning(dock, "Dock part is docked to dockee part. Docking state should be 'Docked (same vessel)' but is '" + dock.DockState + "', dockee part: " + otherDock); dock.NeedsRepair = true; otherDock.NeedsRepair = true; } } else if (otherDock.DockState.ToLower() == "docked (same vessel)") { if (dock.DockState.ToLower() != "docked (dockee)") { Syntax.Warning(dock, "Dock part is docked to same vessel docking part. Docking state should be 'Docked (dockee)' but is '" + dock.DockState + "', same vessel docking part: " + otherDock); dock.NeedsRepair = true; otherDock.NeedsRepair = true; } } else { Syntax.Warning(dock, "Dock part is docked to other dock part. Docking state should be 'Docked (same vessel)' or 'Docked (dockee)' but is '" + dock.DockState + "', other dock: " + otherDock); dock.NeedsRepair = true; otherDock.NeedsRepair = true; } } else if (dock.DockType != KmlPartDock.DockTypes.Grapple) { Syntax.Warning(dock, "Dock part is no grappling device, so it should be only docked to other dock parts, but is docked to: " + other); dock.NeedsRepair = true; } } } } } return(roots); }